@thomas.f/prnext 0.1.17-alpha → 0.1.18-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/README.md +26 -869
  2. package/build/actions.mjs +1 -1
  3. package/build/analyze.mjs +180 -24
  4. package/build/app-config.mjs +13 -0
  5. package/build/app-static.mjs +34 -12
  6. package/build/app-styles.mjs +79 -0
  7. package/build/app.mjs +81 -26
  8. package/build/bundle-runtime.mjs +22 -9
  9. package/build/cache-components.mjs +1 -1
  10. package/build/cache-handlers.mjs +1 -1
  11. package/build/compact-css.mjs +53 -0
  12. package/build/compilation-cache.mjs +269 -0
  13. package/build/compilation-store.mjs +142 -0
  14. package/build/compiler.mjs +31 -8
  15. package/build/config.mjs +30 -1
  16. package/build/create.mjs +239 -0
  17. package/build/css-modules.mjs +56 -12
  18. package/build/deployment-assets.mjs +89 -0
  19. package/build/deployment-config.mjs +18 -0
  20. package/build/dev-css-update.mjs +4 -2
  21. package/build/dev-dependencies.mjs +1 -1
  22. package/build/dev-discovery.mjs +3 -3
  23. package/build/dev.mjs +15 -8
  24. package/build/dynamic.mjs +1 -1
  25. package/build/edge.mjs +3 -3
  26. package/build/fonts.mjs +1 -1
  27. package/build/framework-imports.mjs +15 -5
  28. package/build/html-only-config.mjs +53 -0
  29. package/build/html-only-graph.mjs +67 -0
  30. package/build/index.mjs +59 -26
  31. package/build/instrumentation.mjs +30 -0
  32. package/build/metadata-routes.mjs +1 -1
  33. package/build/migration-audit.mjs +1 -1
  34. package/build/native-type-config.mjs +37 -0
  35. package/build/persistent-cache.mjs +18 -3
  36. package/build/precompress.mjs +1 -1
  37. package/build/prerender-cache.mjs +70 -0
  38. package/build/prerender-config.mjs +29 -0
  39. package/build/prerender-pages.mjs +19 -9
  40. package/build/prerender-pool.mjs +165 -0
  41. package/build/prerender-process-worker.mjs +58 -0
  42. package/build/prerender-worker.mjs +5 -5
  43. package/build/production-builder.mjs +58 -9
  44. package/build/react-compiler-report.mjs +225 -0
  45. package/build/react-compiler.mjs +139 -0
  46. package/build/runtime-contract.mjs +2 -2
  47. package/build/static-export.mjs +4 -4
  48. package/build/styles.mjs +6 -4
  49. package/build/transform.mjs +1 -1
  50. package/build/typed-routes.mjs +4 -0
  51. package/build/typescript-dev.mjs +35 -8
  52. package/build/typescript-native-worker.mjs +55 -0
  53. package/build/typescript-native.mjs +18 -0
  54. package/build/typescript-toolchain.mjs +19 -0
  55. package/build/typescript.mjs +6 -4
  56. package/build/webpack-graph.mjs +6 -3
  57. package/build/worker-memory.mjs +9 -0
  58. package/cli.mjs +60 -4
  59. package/compat/activity-cache.cjs +26 -0
  60. package/compat/after.cjs +6 -4
  61. package/compat/app-layout-context.cjs +52 -2
  62. package/compat/base64.cjs +10 -0
  63. package/compat/cache-handlers.cjs +12 -2
  64. package/compat/cache-wire.cjs +28 -0
  65. package/compat/cache.cjs +2 -1
  66. package/compat/cache.d.cts +4 -0
  67. package/compat/cookies.cjs +21 -5
  68. package/compat/data-cache.cjs +12 -8
  69. package/compat/flight-delta.cjs +150 -0
  70. package/compat/headers.cjs +4 -2
  71. package/compat/incremental-cache.cjs +20 -7
  72. package/compat/index.d.ts +68 -3
  73. package/compat/link.cjs +12 -9
  74. package/compat/navigation-authorization.cjs +9 -0
  75. package/compat/navigation-stages.cjs +19 -0
  76. package/compat/offline-store.cjs +11 -0
  77. package/compat/offline.cjs +5 -0
  78. package/compat/offline.d.cts +2 -0
  79. package/compat/redis-cache.cjs +201 -0
  80. package/compat/redis-cache.d.cts +37 -0
  81. package/compat/server-primitives.cjs +31 -11
  82. package/compat/server.d.cts +15 -2
  83. package/compat/static-generation.cjs +7 -0
  84. package/compat/use-cache.cjs +3 -0
  85. package/docs/ai/compatibility.md +171 -2
  86. package/docs/ai/data.md +165 -5
  87. package/docs/ai/deployment.md +158 -0
  88. package/docs/ai/quickstart.md +29 -4
  89. package/docs/ai/routing.md +82 -0
  90. package/docs/ai/troubleshooting.md +37 -2
  91. package/package.json +26 -7
  92. package/runtime/action-client.mjs +10 -4
  93. package/runtime/action-server.mjs +2 -0
  94. package/runtime/app-client.mjs +212 -65
  95. package/runtime/app-content.mjs +4 -3
  96. package/runtime/app-partial-client.mjs +3 -0
  97. package/runtime/app-partial.mjs +1 -1
  98. package/runtime/app-prefetch-constants.mjs +4 -0
  99. package/runtime/app-prefetch.mjs +2 -3
  100. package/runtime/app-render-key.mjs +5 -0
  101. package/runtime/app-render.mjs +88 -28
  102. package/runtime/app-routing.mjs +22 -9
  103. package/runtime/app-static.mjs +18 -8
  104. package/runtime/app-stream-html.mjs +10 -53
  105. package/runtime/app-style-selection.mjs +34 -0
  106. package/runtime/app-styles.mjs +154 -0
  107. package/runtime/cache-components-rsc.mjs +4 -1
  108. package/runtime/cache-request.mjs +2 -1
  109. package/runtime/deployment-client.mjs +19 -0
  110. package/runtime/dev-client.mjs +7 -2
  111. package/runtime/dev-compiler-broker.mjs +42 -0
  112. package/runtime/dev-indicator.mjs +8 -3
  113. package/runtime/dev-lazy-tool.mjs +16 -0
  114. package/runtime/development.mjs +29 -12
  115. package/runtime/document.mjs +1 -1
  116. package/runtime/edge.mjs +6 -5
  117. package/runtime/fetch-cache.mjs +10 -8
  118. package/runtime/flight-delta.mjs +50 -0
  119. package/runtime/flight-tap.mjs +28 -0
  120. package/runtime/html-only.mjs +27 -0
  121. package/runtime/http.mjs +2 -0
  122. package/runtime/incremental-cache.mjs +25 -4
  123. package/runtime/instrumentation.mjs +49 -0
  124. package/runtime/navigation-recovery.mjs +36 -0
  125. package/runtime/navigation.mjs +7 -0
  126. package/runtime/node-html-stream.mjs +47 -0
  127. package/runtime/pages-client.mjs +14 -7
  128. package/runtime/pages-navigation.mjs +48 -63
  129. package/runtime/prefetch-scheduler.mjs +110 -0
  130. package/runtime/profile.mjs +14 -0
  131. package/runtime/profiles.json +2 -2
  132. package/runtime/render.mjs +3 -0
  133. package/runtime/route-pattern.mjs +44 -0
  134. package/runtime/rsc-worker.mjs +34 -12
  135. package/runtime/trace.mjs +78 -0
  136. package/runtime/watch-project.mjs +19 -15
  137. package/runtime/worker.mjs +70 -26
@@ -1,3 +1,7 @@
1
+ import {drainTraces} from './trace.mjs';
2
+ import {advancedRoutingSources} from './app-routing-state.mjs';
3
+ import {initializeInstrumentation,withInstrumentation} from './instrumentation.mjs';
4
+ import {deferredCompiler} from './dev-compiler-broker.mjs';
1
5
  // prnext-transport:socket-v2
2
6
  // prnext-concurrency:512
3
7
  import { requestFrames } from './request-transport.mjs';
@@ -38,6 +42,10 @@ loadEnvConfig(projectRoot, { dev: !!manifest.dev });
38
42
  // browser graph and the RSC worker's mode, including dev launched from CI.
39
43
  process.env.NODE_ENV = manifest.dev ? 'development' : 'production';
40
44
  const production = !manifest.dev;
45
+ const compilerDescriptor=process.env.PRNEXT_DEV_COMPILER;
46
+ delete process.env.PRNEXT_DEV_COMPILER;
47
+ process.env.NEXT_RUNTIME='nodejs';
48
+ await initializeInstrumentation(distDir,manifest);
41
49
  let dispatchRequest=handleRequest;
42
50
  if(!production){
43
51
  const {createDevWorkerConsole}=await import('./dev-worker-logs.mjs'),logs=createDevWorkerConsole();globalThis.console=logs.console;
@@ -48,9 +56,9 @@ if(!production){
48
56
  if (production) freezeClientModules(manifest.app?.clientModules);
49
57
  // Load each renderer beside the application only when that request needs it.
50
58
  // Middleware and API workers do not pay for React or the Pages/App renderers.
51
- const runtimeModule = async name => {
59
+ const runtimeModule = async (name,directory=distDir) => {
52
60
  const started = diagnostics.enabled ? performance.now() : 0;
53
- const module = await import(pathToFileURL(path.join(distDir, `runtime/${name}.mjs`)).href);
61
+ const module = await import(pathToFileURL(path.join(directory, `runtime/${name}.mjs`)).href);
54
62
  scheduleCompileCacheFlush();
55
63
  if (diagnostics.enabled) diagnostics.record('wake', { phase: 'module', module: name, durationMs: performance.now() - started });
56
64
  return module;
@@ -60,31 +68,61 @@ try { ({ errorResponse } = await runtimeModule('http')); }
60
68
  catch (error) { console.error('PRNext worker could not load the project runtime:', error.message); process.exit(1); }
61
69
  const routes = new Map(manifest.routes.map(route => [route.id, route]));
62
70
  const prerendered = new Set((manifest.prerendered || []).map(item => item.path));
63
- let appRuntime;
64
- let appStaticRuntime;
65
- let routeStaticRuntime;
66
- let middlewareRuntime;
67
- let pagesRuntime;
68
- let apiRuntime;
69
- let incrementalRuntime;
71
+ const deployment={distDir,manifest,routes,prerendered,modules:new Map()};
72
+ const deployments=new Map([[distDir,deployment]]);
73
+ const compileDeferred=!production&&compilerDescriptor?deferredCompiler(compilerDescriptor,{root:projectRoot,home:path.dirname(distDir)}):undefined;
74
+ async function deferredDeployment(route,signal,sources=[]){
75
+ if(!compileDeferred)throw Error('Deferred development compiler is unavailable');
76
+ const next=await compileDeferred(route.pattern,signal,sources);
77
+ let value=deployments.get(next.distDir);
78
+ if(!value){value={...next,routes:new Map(next.manifest.routes.map(route=>[route.id,route])),prerendered:new Set(),modules:new Map()};deployments.set(next.distDir,value);while(deployments.size>4)deployments.delete(deployments.keys().next().value);}
79
+ const resolved=value.routes.get(route.id);
80
+ if(!resolved||resolved.devDeferred)throw Error('Compiler did not materialize the selected route');
81
+ return value;
82
+ }
83
+
84
+ async function drainRuntime(){
85
+ for(const value of deployments.values())if(value.modules.has('app-render')||value.modules.has('app-static')){
86
+ const runtime=await(value.modules.get('app-render')||import(pathToFileURL(path.join(value.distDir,'runtime/app-render.mjs')).href));
87
+ await runtime.drainAppRuntime();
88
+ }
89
+ await drainTraces();
90
+ }
70
91
 
71
- async function handleRequest(input, output, signalController, responseComplete) {
92
+ async function handleRequest(input, output, signalController, responseComplete, selected=deployment) {
93
+ const {manifest,distDir,routes,prerendered}=selected;
94
+ const renderer=async name=>{
95
+ if(selected.modules.has(name))return selected.modules.get(name);
96
+ // Imports share one pending job, then retain only the namespace. Starting
97
+ // outside the visitor's ALS scope prevents the module cache retaining it.
98
+ const pending=workScope.runInAsyncScope(()=>runtimeModule(name,distDir));
99
+ selected.modules.set(name,pending);
100
+ try{const module=await pending;selected.modules.set(name,module);return module;}
101
+ catch(error){if(selected.modules.get(name)===pending)selected.modules.delete(name);throw error;}
102
+ };
72
103
  let responseStarted = false;
73
104
  const finalizers = new Set();
74
105
  try {
75
- if(input.routeId==='__prnext_preload'&&input.renderMode==='preload'){
76
- const options=manifest.config?.workerPreload;
106
+ if(input.routeId==='__prnext_preload'&&['preload','prewarm'].includes(input.renderMode)){
107
+ const frameworkOnly=input.renderMode==='prewarm';
108
+ const options=manifest.config?.workerPreload||(frameworkOnly?{routes:[],maxRssMb:256,timeoutMs:15000}:undefined);
77
109
  if(!production||!options){await output(input.id,{status:204,body:''},input);return;}
110
+ if(frameworkOnly){
111
+ if(manifest.routes.some(route=>route.router==='app'&&route.kind==='page'))await(await renderer('app-render')).prewarmAppRenderer(options);
112
+ if(manifest.routes.some(route=>route.kind==='page'&&route.router!=='app'))await renderer('render');
113
+ if(manifest.routes.some(route=>route.kind==='api'))await renderer('api');
114
+ scheduleCompileCacheFlush();await output(input.id,{status:204,body:''},input);return;
115
+ }
78
116
  const selected=options.routes.map(pattern=>manifest.routes.find(route=>route.pattern===pattern&&!route.internal)).filter(Boolean);
79
117
  const deadline=performance.now()+options.timeoutMs;
80
118
  for(const route of selected){
81
119
  if(performance.now()>=deadline||process.memoryUsage().rss>=options.maxRssMb*1024*1024)break;
82
120
  const remaining={...options,timeoutMs:Math.max(1,deadline-performance.now())};
83
121
  if(route.router==='app'&&route.kind==='page'){
84
- appRuntime??=await runtimeModule('app-render');
122
+ const appRuntime=await renderer('app-render');
85
123
  await appRuntime.preloadAppRoutes([path.resolve(distDir,route.module)],remaining);
86
124
  }else{
87
- if(route.kind==='page')pagesRuntime??=await runtimeModule('render');else apiRuntime??=await runtimeModule('api');
125
+ if(route.kind==='page')await renderer('render');else await renderer('api');
88
126
  await preloadModules([path.resolve(distDir,route.module)],remaining);
89
127
  }
90
128
  if(process.memoryUsage().rss>=options.maxRssMb*1024*1024)break;
@@ -92,7 +130,7 @@ async function handleRequest(input, output, signalController, responseComplete)
92
130
  scheduleCompileCacheFlush();await output(input.id,{status:204,body:''},input);return;
93
131
  }
94
132
  if (input.renderMode === 'incremental-cache' && input.routeId === '__prnext_incremental_cache') {
95
- incrementalRuntime ??= await runtimeModule('incremental-cache');
133
+ const incrementalRuntime = await renderer('incremental-cache');
96
134
  const result = await incrementalRuntime.runIncrementalCache({ ...input, manifest, distDir, production, signal: signalController.signal });
97
135
  input.body = undefined;
98
136
  responseStarted = true;
@@ -104,31 +142,38 @@ async function handleRequest(input, output, signalController, responseComplete)
104
142
  ? { ...manifest.middleware, id: '__prnext_middleware', kind: 'middleware' }
105
143
  : routes.get(input.routeId);
106
144
  if (!route) { await output(input.id, { status: 404, body: 'Not Found' }, input); return; }
145
+ const sourceModules=!production&&route.router==='app'&&route.kind==='page'?advancedRoutingSources({...input,basePath:manifest.config?.basePath||''},manifest.routes):[];
146
+ const deferredSources=sourceModules.length?manifest.routes.filter(candidate=>candidate.devDeferred&&sourceModules.includes(candidate.module)).map(candidate=>candidate.pattern):[];
147
+ if(route.devDeferred||deferredSources.length){
148
+ const next=await deferredDeployment(route,signalController.signal,deferredSources);
149
+ return await handleRequest(input,output,signalController,responseComplete,next);
150
+ }
107
151
  const requestedPath = new URL(input.url).pathname;
108
152
  if (route.router !== 'app' && !['isr', 'error404', 'error500'].includes(input.renderMode) && route.fallback === false && !prerendered.has(requestedPath) && !prerendered.has(requestedPath.replace(/\/$/, '') || '/')) {
109
153
  await output(input.id, { status: 404, body: 'Not Found' }, input); return;
110
154
  }
111
155
  const options = { ...input, modulePath: path.resolve(distDir, route.module), route, production, manifest, distDir, signal: signalController.signal, deferFinalizer: finalize => finalizers.add(finalize) };
156
+ return await withInstrumentation(options,async()=>{
112
157
  let result;
113
158
  if (isMiddleware) {
114
- middlewareRuntime ??= await import(pathToFileURL(path.join(distDir, 'runtime/middleware.mjs')).href);
159
+ const middlewareRuntime = await renderer('middleware');
115
160
  result = await middlewareRuntime.runMiddleware(options);
116
161
  } else if (route.router === 'app' && route.kind === 'page') {
117
162
  if (input.renderMode === 'isr') {
118
- appStaticRuntime ??= await import(pathToFileURL(path.join(distDir, 'runtime/app-static.mjs')).href);
163
+ const appStaticRuntime = await renderer('app-static');
119
164
  result = await appStaticRuntime.renderAppIsrPage(options);
120
165
  } else {
121
- appRuntime ??= await runtimeModule('app-render');
166
+ const appRuntime = await renderer('app-render');
122
167
  result = await appRuntime.renderAppPage(options);
123
168
  }
124
169
  } else if (route.router === 'app' && route.kind === 'api' && input.renderMode === 'isr') {
125
- routeStaticRuntime ??= await import(pathToFileURL(path.join(distDir, 'runtime/route-static.mjs')).href);
170
+ const routeStaticRuntime = await renderer('route-static');
126
171
  result = await routeStaticRuntime.renderRouteHandlerIsr(options);
127
172
  } else if (route.kind === 'api') {
128
- apiRuntime ??= await runtimeModule('api');
173
+ const apiRuntime = await renderer('api');
129
174
  result = await apiRuntime.runApi(options);
130
175
  } else {
131
- pagesRuntime ??= await runtimeModule('render');
176
+ const pagesRuntime = await renderer('render');
132
177
  result = input.renderMode === 'isr' ? await pagesRuntime.renderIsrPage({ ...options, capturePageFailure: true })
133
178
  : await pagesRuntime.renderPageRequest({ ...options, nativeErrors: true });
134
179
  }
@@ -140,6 +185,7 @@ async function handleRequest(input, output, signalController, responseComplete)
140
185
  if (result.finalizeCache) finalizers.add(result.finalizeCache);
141
186
  await output(input.id, result, { ...input, production, signalController });
142
187
  responseComplete?.();
188
+ });
143
189
  } catch (error) {
144
190
  const cancelled = signalController.signal.aborted || error instanceof ProtocolWriteError;
145
191
  signalController.abort(error);
@@ -189,7 +235,8 @@ if (token && laneLimit > 1) {
189
235
  reportedBusy = true;
190
236
  writeProtocol('busy\n');
191
237
  };
192
- const exitWhenDrained = () => { if (shuttingDown && !invocations.size) process.exit(0); };
238
+ let exiting=false;
239
+ const exitWhenDrained = () => { if (shuttingDown && !invocations.size&&!exiting){exiting=true;void drainRuntime().catch(error=>console.error('[prnext] Runtime drain failed:',error?.message)).finally(()=>process.exit(0));} };
193
240
  const server = net.createServer(socket => {
194
241
  if (sockets.size >= laneLimit) { socket.destroy(); return; }
195
242
  sockets.add(socket); socket.setNoDelay(true);
@@ -316,8 +363,5 @@ if (token && laneLimit > 1) {
316
363
  // The maintenance/ISR transport is serial and awaits its own finalizers.
317
364
  // RSC settlement can still outlive its terminal frame; drain it explicitly
318
365
  // before closing the isolate's MessagePorts after parent EOF.
319
- if (appRuntime || appStaticRuntime) {
320
- const runtime = appRuntime || await runtimeModule('app-render');
321
- await runtime.drainAppRuntime();
322
- }
366
+ await drainRuntime();
323
367
  }