@thomas.f/prnext 0.1.0-alpha.1

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 (211) hide show
  1. package/README.md +54 -0
  2. package/build/action-exports.mjs +107 -0
  3. package/build/actions.mjs +214 -0
  4. package/build/app-config.mjs +138 -0
  5. package/build/app-routing.mjs +119 -0
  6. package/build/app-static.mjs +158 -0
  7. package/build/app.mjs +536 -0
  8. package/build/cache-components.mjs +91 -0
  9. package/build/cache-handlers.mjs +54 -0
  10. package/build/check.mjs +40 -0
  11. package/build/compiler.mjs +222 -0
  12. package/build/config.mjs +193 -0
  13. package/build/css-modules.mjs +114 -0
  14. package/build/custom-routes.mjs +363 -0
  15. package/build/dev-worker.mjs +18 -0
  16. package/build/dev.mjs +76 -0
  17. package/build/directory-cache.mjs +29 -0
  18. package/build/dynamic.mjs +125 -0
  19. package/build/edge.mjs +141 -0
  20. package/build/font-data.LICENSE.txt +25 -0
  21. package/build/font-data.json +1 -0
  22. package/build/font-files.mjs +221 -0
  23. package/build/fonts.mjs +131 -0
  24. package/build/framework-imports.mjs +17 -0
  25. package/build/i18n.mjs +39 -0
  26. package/build/images-config.mjs +62 -0
  27. package/build/images.mjs +86 -0
  28. package/build/incremental.mjs +76 -0
  29. package/build/index.mjs +356 -0
  30. package/build/loader-cache.mjs +69 -0
  31. package/build/metadata-images.mjs +72 -0
  32. package/build/metadata-routes.mjs +77 -0
  33. package/build/middleware.mjs +231 -0
  34. package/build/migrate.mjs +289 -0
  35. package/build/module-resolution.mjs +78 -0
  36. package/build/native-discovery.mjs +134 -0
  37. package/build/native-packages.mjs +105 -0
  38. package/build/npm-packages.mjs +110 -0
  39. package/build/og.mjs +14 -0
  40. package/build/page-extensions.mjs +12 -0
  41. package/build/passthrough-loader.cjs +4 -0
  42. package/build/precompress.mjs +122 -0
  43. package/build/scan.mjs +178 -0
  44. package/build/script-workers.mjs +25 -0
  45. package/build/server-actions-config.mjs +27 -0
  46. package/build/source-maps.mjs +34 -0
  47. package/build/standalone-config.mjs +19 -0
  48. package/build/standalone-runner.mjs +21 -0
  49. package/build/standalone-worker.mjs +5 -0
  50. package/build/standalone.mjs +240 -0
  51. package/build/static-export.mjs +93 -0
  52. package/build/styles.mjs +174 -0
  53. package/build/transform-cache.mjs +50 -0
  54. package/build/transform.mjs +155 -0
  55. package/build/turbopack-conditions.mjs +59 -0
  56. package/build/webpack-bridge.cjs +3 -0
  57. package/build/webpack-graph.mjs +256 -0
  58. package/build/webpack-loader.cjs +9 -0
  59. package/cli.mjs +210 -0
  60. package/compat/app-client-page.cjs +32 -0
  61. package/compat/app-context.cjs +40 -0
  62. package/compat/app-context.d.cts +20 -0
  63. package/compat/app-dynamic.cjs +34 -0
  64. package/compat/app-error-boundary.cjs +53 -0
  65. package/compat/app-global-error-boundary.cjs +34 -0
  66. package/compat/app-layout-context.cjs +27 -0
  67. package/compat/app-navigation-boundary.cjs +47 -0
  68. package/compat/app.cjs +33 -0
  69. package/compat/app.d.cts +9 -0
  70. package/compat/cache-handlers.cjs +223 -0
  71. package/compat/cache-life.cjs +34 -0
  72. package/compat/cache.cjs +99 -0
  73. package/compat/cache.d.cts +36 -0
  74. package/compat/compat/router.d.cts +2 -0
  75. package/compat/compat-router.cjs +7 -0
  76. package/compat/constants.cjs +8 -0
  77. package/compat/constants.d.cts +7 -0
  78. package/compat/cookies.cjs +129 -0
  79. package/compat/data-cache.cjs +389 -0
  80. package/compat/dist/shared/lib/app-router-context.shared-runtime.d.cts +3 -0
  81. package/compat/dist/shared/lib/router-context.d.cts +1 -0
  82. package/compat/dist/shared/lib/router-context.shared-runtime.d.cts +3 -0
  83. package/compat/document.cjs +73 -0
  84. package/compat/document.d.cts +41 -0
  85. package/compat/draft.cjs +28 -0
  86. package/compat/dynamic-bailout.cjs +13 -0
  87. package/compat/dynamic.cjs +119 -0
  88. package/compat/dynamic.d.cts +32 -0
  89. package/compat/error.cjs +36 -0
  90. package/compat/error.d.cts +15 -0
  91. package/compat/fetch.d.cts +10 -0
  92. package/compat/font/google.d.cts +1 -0
  93. package/compat/font/local.d.cts +2 -0
  94. package/compat/font-google.cjs +2 -0
  95. package/compat/font-google.d.cts +1944 -0
  96. package/compat/font-local.cjs +5 -0
  97. package/compat/font-local.d.cts +3 -0
  98. package/compat/font-types.d.cts +25 -0
  99. package/compat/head.cjs +180 -0
  100. package/compat/head.d.cts +3 -0
  101. package/compat/headers.cjs +89 -0
  102. package/compat/headers.d.cts +26 -0
  103. package/compat/image-config.cjs +3 -0
  104. package/compat/image-imports.d.ts +12 -0
  105. package/compat/image-shared.cjs +62 -0
  106. package/compat/image.cjs +64 -0
  107. package/compat/image.d.cts +35 -0
  108. package/compat/incremental-cache.cjs +186 -0
  109. package/compat/index.d.ts +159 -0
  110. package/compat/instant-samples.cjs +39 -0
  111. package/compat/instant.cjs +13 -0
  112. package/compat/link.cjs +68 -0
  113. package/compat/link.d.cts +17 -0
  114. package/compat/locale.cjs +30 -0
  115. package/compat/metadata.d.cts +77 -0
  116. package/compat/navigation-controls.cjs +27 -0
  117. package/compat/navigation-server.cjs +25 -0
  118. package/compat/navigation.cjs +52 -0
  119. package/compat/navigation.d.cts +28 -0
  120. package/compat/next-app-router-context.cjs +6 -0
  121. package/compat/next-router-context.cjs +3 -0
  122. package/compat/og.cjs +32 -0
  123. package/compat/og.d.cts +1 -0
  124. package/compat/paths.cjs +33 -0
  125. package/compat/preview.cjs +41 -0
  126. package/compat/rewrite.cjs +41 -0
  127. package/compat/route-request.cjs +64 -0
  128. package/compat/router.cjs +137 -0
  129. package/compat/router.d.cts +58 -0
  130. package/compat/script-context.cjs +5 -0
  131. package/compat/script-loader.cjs +135 -0
  132. package/compat/script.cjs +69 -0
  133. package/compat/script.d.cts +17 -0
  134. package/compat/server-primitives.cjs +109 -0
  135. package/compat/server.cjs +13 -0
  136. package/compat/server.d.cts +37 -0
  137. package/compat/static-generation.cjs +108 -0
  138. package/compat/use-cache.cjs +129 -0
  139. package/index.mjs +1 -0
  140. package/native/platforms.json +6 -0
  141. package/native/resolve.mjs +81 -0
  142. package/package.json +127 -0
  143. package/release/verify.mjs +59 -0
  144. package/runtime/action-client.mjs +19 -0
  145. package/runtime/action-crypto.mjs +138 -0
  146. package/runtime/action-reference.mjs +2 -0
  147. package/runtime/action-server.mjs +161 -0
  148. package/runtime/action-ssr.mjs +5 -0
  149. package/runtime/api.mjs +326 -0
  150. package/runtime/app-client.mjs +539 -0
  151. package/runtime/app-content.mjs +8 -0
  152. package/runtime/app-errors.mjs +12 -0
  153. package/runtime/app-instant.mjs +30 -0
  154. package/runtime/app-metadata.mjs +410 -0
  155. package/runtime/app-partial-model.mjs +228 -0
  156. package/runtime/app-partial.mjs +130 -0
  157. package/runtime/app-prefetch.mjs +34 -0
  158. package/runtime/app-recovery.mjs +87 -0
  159. package/runtime/app-render.mjs +544 -0
  160. package/runtime/app-routing-access.mjs +143 -0
  161. package/runtime/app-routing-context.mjs +35 -0
  162. package/runtime/app-routing-state.mjs +61 -0
  163. package/runtime/app-routing.mjs +323 -0
  164. package/runtime/app-segments.mjs +22 -0
  165. package/runtime/app-static-params.mjs +120 -0
  166. package/runtime/app-static.mjs +86 -0
  167. package/runtime/app-stream-channel.mjs +92 -0
  168. package/runtime/app-stream-html.mjs +279 -0
  169. package/runtime/build-directory.mjs +48 -0
  170. package/runtime/cache-arguments.mjs +33 -0
  171. package/runtime/cache-components-rsc.mjs +64 -0
  172. package/runtime/cache-request.mjs +23 -0
  173. package/runtime/client-navigation.mjs +40 -0
  174. package/runtime/dev-client.mjs +129 -0
  175. package/runtime/dev-file-changes.mjs +110 -0
  176. package/runtime/document.mjs +96 -0
  177. package/runtime/edge.mjs +105 -0
  178. package/runtime/env.mjs +68 -0
  179. package/runtime/fetch-cache.mjs +379 -0
  180. package/runtime/font-preload.mjs +4 -0
  181. package/runtime/http.mjs +190 -0
  182. package/runtime/incremental-cache.mjs +83 -0
  183. package/runtime/metadata-route.mjs +83 -0
  184. package/runtime/middleware.mjs +186 -0
  185. package/runtime/module-loader.mjs +21 -0
  186. package/runtime/navigation.mjs +16 -0
  187. package/runtime/og-loader.mjs +17 -0
  188. package/runtime/pages-client.mjs +197 -0
  189. package/runtime/pages-error-props.mjs +6 -0
  190. package/runtime/pages-errors.mjs +85 -0
  191. package/runtime/pages-initial-props.mjs +43 -0
  192. package/runtime/pages-navigation.mjs +565 -0
  193. package/runtime/pages-paths.mjs +80 -0
  194. package/runtime/pages-revalidate.mjs +33 -0
  195. package/runtime/partial-artifact-cache.mjs +37 -0
  196. package/runtime/partial-template.mjs +174 -0
  197. package/runtime/profile.mjs +9 -0
  198. package/runtime/profiles.json +47 -0
  199. package/runtime/render.mjs +304 -0
  200. package/runtime/request-transport.mjs +41 -0
  201. package/runtime/request-work.mjs +7 -0
  202. package/runtime/route-static.mjs +117 -0
  203. package/runtime/rsc-worker.mjs +426 -0
  204. package/runtime/script-html.mjs +61 -0
  205. package/runtime/stream-select.mjs +25 -0
  206. package/runtime/stream-utils.mjs +127 -0
  207. package/runtime/transport.mjs +108 -0
  208. package/runtime/watch-project.mjs +130 -0
  209. package/runtime/worker-artifacts.mjs +22 -0
  210. package/runtime/worker-metadata.mjs +31 -0
  211. package/runtime/worker.mjs +197 -0
@@ -0,0 +1,80 @@
1
+ import { localePath, withLocale } from '../compat/locale.cjs';
2
+ function validSegment(value, name = 'route') {
3
+ if (typeof value !== 'string' || !value || value === '.' || value === '..' || /[\\/\u0000-\u001f\u007f]/.test(value)) {
4
+ throw new Error(`getStaticPaths parameter ${name} must be a nonempty string without path separators, traversal, or control characters.`);
5
+ }
6
+ return value;
7
+ }
8
+
9
+ const parameter = /^\[(\[)?(\.\.\.)?([^\]]+)\]\]?$/;
10
+
11
+ export function staticPath(pattern, value) {
12
+ const patternSegments = pattern === '/' ? [] : pattern.split('/').slice(1);
13
+ if (typeof value === 'string') {
14
+ if (!value.startsWith('/') || value.startsWith('//') || value.includes('?') || value.includes('#')) {
15
+ throw new Error(`getStaticPaths returned an invalid pathname: ${value}`);
16
+ }
17
+ const normalized = value === '/' ? '/' : value.replace(/\/+$/, '');
18
+ const pathSegments = normalized === '/' ? [] : normalized.split('/').slice(1).map(part => validSegment(decodeURIComponent(part)));
19
+ const params = {};
20
+ let cursor = 0;
21
+ for (const segment of patternSegments) {
22
+ const match = parameter.exec(segment);
23
+ if (!match) {
24
+ if (segment !== pathSegments[cursor++]) throw new Error(`getStaticPaths pathname ${value} does not match ${pattern}.`);
25
+ } else {
26
+ let supplied;
27
+ if (match[2]) {
28
+ supplied = pathSegments.slice(cursor);
29
+ if (!supplied.length && !match[1]) throw new Error(`getStaticPaths pathname ${value} needs at least one ${match[3]} segment.`);
30
+ cursor = pathSegments.length;
31
+ } else {
32
+ if (cursor >= pathSegments.length) throw new Error(`getStaticPaths pathname ${value} is missing parameter ${match[3]}.`);
33
+ supplied = pathSegments[cursor++];
34
+ }
35
+ Object.defineProperty(params, match[3], { value: supplied, enumerable: true, configurable: true, writable: true });
36
+ }
37
+ }
38
+ if (cursor !== pathSegments.length) throw new Error(`getStaticPaths pathname ${value} does not match ${pattern}.`);
39
+ return { path: '/' + pathSegments.map(encodeURIComponent).join('/'), params };
40
+ }
41
+ if (!value || typeof value !== 'object' || Array.isArray(value) || !value.params ||
42
+ typeof value.params !== 'object' || Array.isArray(value.params) || value.locale) {
43
+ throw new Error('getStaticPaths entries must be path strings or {params:{...}} objects. Locales are not supported yet.');
44
+ }
45
+ const params = value.params;
46
+ const segments = patternSegments.flatMap(segment => {
47
+ const match = parameter.exec(segment);
48
+ if (!match) return [encodeURIComponent(validSegment(segment))];
49
+ const supplied = Object.hasOwn(params, match[3]) ? params[match[3]] : undefined;
50
+ if (match[2]) {
51
+ if (match[1] && (supplied === undefined || supplied === null || supplied === false)) return [];
52
+ if (!Array.isArray(supplied) || (!supplied.length && !match[1])) {
53
+ throw new Error(`getStaticPaths parameter ${match[3]} must be ${match[1] ? 'an array of strings' : 'a nonempty array of strings'}.`);
54
+ }
55
+ return Array.from(supplied, value => encodeURIComponent(validSegment(value, match[3])));
56
+ }
57
+ return [encodeURIComponent(validSegment(supplied, match[3]))];
58
+ });
59
+ return { path: '/' + segments.join('/'), params };
60
+ }
61
+
62
+ export function validateStaticPaths(pattern, result) {
63
+ if (!result || typeof result !== 'object' || !Array.isArray(result.paths) || ![false, true, 'blocking'].includes(result.fallback)) {
64
+ throw new Error(`getStaticPaths for ${pattern} must return {paths: [...], fallback: false | true | 'blocking'}.`);
65
+ }
66
+ return { paths: result.paths.map(value => staticPath(pattern, value)), fallback: result.fallback };
67
+ }
68
+
69
+ export function localizedStaticPaths(route, result, i18n) {
70
+ if(!route.locale) return validateStaticPaths(route.pattern,result);
71
+ if(!result||!Array.isArray(result.paths)) return validateStaticPaths(route.originalPattern,result);
72
+ const paths=[];
73
+ for(const value of result.paths){
74
+ const parsed=typeof value==='string'?localePath(value,i18n):{locale:value?.locale||i18n.defaultLocale};
75
+ if(!i18n.locales.includes(parsed.locale))throw new Error(`getStaticPaths returned unknown locale ${parsed.locale}`);
76
+ if(parsed.locale===route.locale)paths.push(typeof value==='string'?parsed.pathname:{params:value.params});
77
+ }
78
+ const validated=validateStaticPaths(route.originalPattern,{...result,paths});
79
+ return {...validated,paths:validated.paths.map(entry=>({...entry,path:withLocale(entry.path,route.locale,i18n.defaultLocale)}))};
80
+ }
@@ -0,0 +1,33 @@
1
+ import { nativeFetch, optionalRequestContext } from '../compat/data-cache.cjs';
2
+ import { removeBasePath } from '../compat/paths.cjs';
3
+
4
+ export async function revalidatePage(response, pathname, options = {}) {
5
+ if (!response._canRevalidate) throw new Error('res.revalidate() is only available in Pages API routes');
6
+ if (response.headersSent || response.writableEnded) throw new Error('res.revalidate() must be called before sending response headers');
7
+ if (typeof pathname !== 'string' || !pathname.startsWith('/') || pathname.startsWith('//') || /[\\\x00-\x1f\x7f]/.test(pathname)) {
8
+ throw new TypeError('res.revalidate() requires an absolute application path, such as /posts/1');
9
+ }
10
+ if (!options || typeof options !== 'object' || Array.isArray(options) ||
11
+ (options.unstable_onlyGenerated !== undefined && typeof options.unstable_onlyGenerated !== 'boolean')) {
12
+ throw new TypeError('res.revalidate() options.unstable_onlyGenerated must be a boolean');
13
+ }
14
+ const url = process.env.PRNEXT_CACHE_URL;
15
+ const token = process.env.PRNEXT_CACHE_TOKEN;
16
+ if (!url || !token) throw new Error('res.revalidate() requires a running PRNext server');
17
+ const context = optionalRequestContext();
18
+ const path = removeBasePath(new URL(pathname, 'http://prnext.local').pathname, context?.basePath || '');
19
+ const signal = response._revalidateSignal || context?.signal;
20
+ const timeout = AbortSignal.timeout(25_000);
21
+ const result = await nativeFetch(new URL('/pages/revalidate', url), {
22
+ method: 'POST',
23
+ headers: { 'content-type': 'application/json', authorization: `Bearer ${token}` },
24
+ body: JSON.stringify({ path, ...(options.unstable_onlyGenerated === undefined ? {} : { onlyGenerated: options.unstable_onlyGenerated }) }),
25
+ signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
26
+ });
27
+ if (!result.ok) {
28
+ await result.body?.cancel();
29
+ throw new Error(`Failed to revalidate ${path} (${result.status})`);
30
+ }
31
+ const value = await result.json();
32
+ if (typeof value?.revalidated !== 'boolean') throw new Error('Invalid PRNext page revalidation response');
33
+ }
@@ -0,0 +1,37 @@
1
+ // Memoize parsing only AFTER the authoritative data cache returned its bytes.
2
+ // Comparing exact bytes preserves expiry and invalidation even for the same key.
3
+ // Never store live React models, request contexts, cookies or headers here.
4
+ import { createHash } from 'node:crypto';
5
+
6
+ export class PartialArtifactCache {
7
+ constructor({ maxEntries = 16, maxBytes = 256 * 1024, maxEntryBytes = 64 * 1024 } = {}) {
8
+ this.entries = new Map(); this.bytes = 0;
9
+ this.maxEntries = maxEntries; this.maxBytes = maxBytes; this.maxEntryBytes = maxEntryBytes;
10
+ }
11
+ parse(key, bytes) {
12
+ const hit = this.entries.get(key);
13
+ if (hit && hit.bytes.equals(bytes)) {
14
+ this.entries.delete(key); this.entries.set(key, hit);
15
+ return hit.value;
16
+ }
17
+ const value = JSON.parse(bytes.toString());
18
+ if (hit) { this.entries.delete(key); this.bytes -= hit.bytes.length; }
19
+ if (bytes.length <= this.maxEntryBytes && bytes.length <= this.maxBytes && this.maxEntries > 0) {
20
+ while (this.entries.size && (this.entries.size >= this.maxEntries || this.bytes + bytes.length > this.maxBytes)) {
21
+ const [oldKey, old] = this.entries.entries().next().value;
22
+ this.entries.delete(oldKey); this.bytes -= old.bytes.length;
23
+ }
24
+ // Own compact storage; a cache RPC buffer can share a much larger slab.
25
+ const owned = Buffer.allocUnsafeSlow(bytes.length); bytes.copy(owned);
26
+ this.entries.set(key, { bytes: owned, value }); this.bytes += owned.length;
27
+ }
28
+ return value;
29
+ }
30
+ }
31
+
32
+ const identities = new WeakMap();
33
+ export function partialIdentity(artifact) {
34
+ let identity = identities.get(artifact);
35
+ if (!identity) { identity = createHash('sha256').update(artifact.flight).digest('hex'); identities.set(artifact, identity); }
36
+ return identity;
37
+ }
@@ -0,0 +1,174 @@
1
+ // Retain only replayable static values, never React decoder chunks (which can
2
+ // retain their response), streams, server functions or request contexts.
3
+ import { withSignal } from './stream-utils.mjs';
4
+ import { isPartialHole } from './app-partial-model.mjs';
5
+ const LAZY = Symbol.for('react.lazy');
6
+ const CLIENT = Symbol.for('react.client.reference');
7
+ const directModels = new WeakSet();
8
+ const values = new WeakMap();
9
+ const holes = new WeakSet();
10
+ const reusable = new WeakMap();
11
+ const holeError = Object.assign(new Error('Partial request data'), { digest: 'PRNEXT_PPR_DYNAMIC' });
12
+ function throwHole() { throw holeError; }
13
+ const holeLazy = () => { const value = { $$typeof: LAZY, _payload: {}, _init: throwHole }; holes.add(value); return value; };
14
+ class Unreplayable extends Error {}
15
+ // Factories at module scope cannot close over a snapshot's signal or decoder.
16
+ const readyLazy = value => { const copy = { $$typeof: LAZY, _payload: {}, _init() { return value; } }; values.set(copy, value); return copy; };
17
+ const readyPromise = value => { const copy = { then(resolve) { return resolve(value); } }; values.set(copy, value); return copy; };
18
+
19
+ // Only proven root replacements can consume the original React model directly.
20
+ // A nested hole needs the ordinary Flight decoder to locate a server component's
21
+ // rendered children. Cycles, bindings and advanced routing stay on that path.
22
+ function rootReplacementsOnly(model) {
23
+ if (!model || Object.getPrototypeOf(model) !== Object.prototype || model.routing || model.keyScopes?.some(scope => scope.liveProps?.length)) return false;
24
+ const seen = new WeakMap();
25
+ function classify(value) {
26
+ if (!value || typeof value !== 'object') return 'static';
27
+ if (holes.has(value)) return 'hole';
28
+ if (seen.has(value)) return seen.get(value);
29
+ seen.set(value, 'nested');
30
+ let result;
31
+ if (values.has(value)) result = classify(values.get(value));
32
+ else {
33
+ const children = value instanceof Map ? [...value].flat() : value instanceof Set ? [...value] : Object.values(value);
34
+ result = children.every(child => classify(child) === 'static') ? 'static' : 'nested';
35
+ }
36
+ seen.set(value, result); return result;
37
+ }
38
+ return Object.values(model).every(value => classify(value) !== 'nested');
39
+ }
40
+
41
+ export function canResumePartialDirectly(model) { return directModels.has(model); }
42
+
43
+ // Classify static subtrees once, while no request exists. A subtree with a hole,
44
+ // a live prop binding or a cycle must be copied/merged in the ordinary way.
45
+ // Key remapping is decided per navigation, without retaining its request here.
46
+ function classifyReusable(model) {
47
+ const entries = new WeakMap();
48
+ const bindings = new Set((model.keyScopes || []).filter(scope => scope.liveProps?.length).map(scope => scope.key));
49
+ const empty = [];
50
+ function visit(value) {
51
+ if (!value || typeof value !== 'object') return empty;
52
+ if (entries.has(value)) return entries.get(value);
53
+ entries.set(value, null);
54
+ if (holes.has(value) || value.$$typeof === Symbol.for('react.transitional.element') && bindings.has(value.key)) return null;
55
+ const children = values.has(value) ? [values.get(value)] : value instanceof Map ? [...value].flat() : value instanceof Set ? [...value] : Object.values(value);
56
+ const keys = new Set(value.$$typeof === Symbol.for('react.transitional.element') && value.key !== null ? [value.key] : []);
57
+ let stable = true;
58
+ for (const child of children) {
59
+ const nested = visit(child);
60
+ if (nested === null) stable = false;
61
+ else for (const key of nested) keys.add(key);
62
+ }
63
+ const result = stable ? keys.size ? [...keys] : empty : null;
64
+ entries.set(value, result);
65
+ return result;
66
+ }
67
+ visit(model);
68
+ return entries;
69
+ }
70
+ export function partialTemplateReuse(model, keyMap) {
71
+ const entries = reusable.get(model);
72
+ if (!entries) return undefined;
73
+ return value => {
74
+ if (value === model) return false; // The root receives this request's router.
75
+ const keys = entries.get(value);
76
+ return keys !== undefined && keys !== null && (!keyMap?.size || keys.every(key => !keyMap.has(key)));
77
+ };
78
+ }
79
+
80
+ export async function snapshotPartialTemplate(model, signal) {
81
+ const seen = new WeakMap();
82
+ let nodes = 0;
83
+ async function visit(value, depth = 0) {
84
+ signal?.throwIfAborted();
85
+ if (typeof value === 'function') {
86
+ if (value.$$typeof === CLIENT) return { value };
87
+ throw new Unreplayable('Function retains decoder state');
88
+ }
89
+ if (!value || typeof value !== 'object') return { value };
90
+ if (seen.has(value)) {
91
+ const copy = seen.get(value);
92
+ if (copy === undefined) throw new Unreplayable('Unresolved lazy cycle');
93
+ return { value: copy };
94
+ }
95
+ if (++nodes > 4096 || depth > 128) throw new Unreplayable('Template complexity limit');
96
+ seen.set(value, undefined);
97
+ if (value.$$typeof === LAZY || typeof value.then === 'function') {
98
+ let resolved;
99
+ try {
100
+ if (value.$$typeof === LAZY) {
101
+ for (;;) {
102
+ signal?.throwIfAborted();
103
+ try { resolved = value._init(value._payload); break; }
104
+ catch (error) { if (typeof error?.then !== 'function') throw error; await withSignal(error, signal); }
105
+ }
106
+ } else resolved = await withSignal(value, signal);
107
+ } catch (error) {
108
+ if (!isPartialHole(error)) throw new Unreplayable('Errored static value');
109
+ const copy = value.$$typeof === LAZY ? holeLazy() : { then: rejectHole };
110
+ holes.add(copy);
111
+ seen.set(value, copy); return { value: copy };
112
+ }
113
+ const resolvedCopy = (await visit(resolved, depth + 1)).value;
114
+ const copy = value.$$typeof === LAZY ? readyLazy(resolvedCopy) : readyPromise(resolvedCopy);
115
+ seen.set(value, copy); return { value: copy };
116
+ }
117
+ let copy;
118
+ if (value instanceof Date) copy = new Date(value);
119
+ else if (value instanceof ArrayBuffer) copy = value.slice(0);
120
+ else if (ArrayBuffer.isView(value)) throw new Unreplayable('Keep typed-array backing identities in the ordinary decoder');
121
+ else if (value instanceof Map) copy = new Map();
122
+ else if (value instanceof Set) copy = new Set();
123
+ else if (Array.isArray(value)) copy = [];
124
+ else if ([Object.prototype, null].includes(Object.getPrototypeOf(value))) copy = Object.create(Object.getPrototypeOf(value));
125
+ else throw new Unreplayable('Stream or non-replayable prototype');
126
+ seen.set(value, copy);
127
+ if (value instanceof Map) for (const [key, item] of value) copy.set((await visit(key, depth + 1)).value, (await visit(item, depth + 1)).value);
128
+ else if (value instanceof Set) for (const item of value) copy.add((await visit(item, depth + 1)).value);
129
+ else if (!(value instanceof Date) && !(value instanceof ArrayBuffer)) for (const key of Object.keys(value)) Object.defineProperty(copy, key, { value: (await visit(value[key], depth + 1)).value, enumerable: true, writable: true, configurable: true });
130
+ // Box thenables so this async walk does not accidentally assimilate them.
131
+ return { value: copy };
132
+ }
133
+ const copy = (await visit(model)).value;
134
+ if (rootReplacementsOnly(copy)) directModels.add(copy);
135
+ if (copy && typeof copy === 'object') reusable.set(copy, classifyReusable(copy));
136
+ return { model: copy, nodes };
137
+ }
138
+ function rejectHole(_resolve, reject) { return reject(holeError); }
139
+
140
+ export class PartialTemplates {
141
+ constructor({ maxEntries = 8, maxBytes = 512 * 1024, maxWireBytes = 64 * 1024 } = {}) {
142
+ this.entries = new Map(); this.bytes = 0;
143
+ this.maxEntries = maxEntries; this.maxBytes = maxBytes; this.maxWireBytes = maxWireBytes;
144
+ }
145
+ async get(flight, consumer, decode, signal) {
146
+ const previous = this.entries.get(flight);
147
+ if (previous && previous.moduleMap === consumer.moduleMap && previous.serverModuleMap === consumer.serverModuleMap) {
148
+ this.entries.delete(flight); this.entries.set(flight, previous);
149
+ return previous.bypass ? decode() : previous.model;
150
+ }
151
+ const decoded = await decode();
152
+ if (flight.length > this.maxWireBytes || this.maxEntries === 0) return decoded;
153
+ let snapshot;
154
+ try { snapshot = await snapshotPartialTemplate(decoded, signal); }
155
+ catch (error) {
156
+ signal?.throwIfAborted();
157
+ if (!(error instanceof Unreplayable)) throw error;
158
+ snapshot = { nodes: 0, bypass: true };
159
+ }
160
+ // Budget the encoded string conservatively plus a per-node allowance.
161
+ // This is a retention policy, not a promise about V8's exact heap size.
162
+ const size = flight.length * 2 + snapshot.nodes * 128;
163
+ if (size > this.maxBytes) return decoded;
164
+ const old = this.entries.get(flight);
165
+ if (old) { this.entries.delete(flight); this.bytes -= old.size; }
166
+ while (this.entries.size && (this.entries.size >= this.maxEntries || this.bytes + size > this.maxBytes)) {
167
+ const [key, entry] = this.entries.entries().next().value;
168
+ this.entries.delete(key); this.bytes -= entry.size;
169
+ }
170
+ this.entries.set(flight, { moduleMap: consumer.moduleMap, serverModuleMap: consumer.serverModuleMap, model: snapshot.model, bypass: snapshot.bypass, size });
171
+ this.bytes += size;
172
+ return snapshot.bypass ? decoded : snapshot.model;
173
+ }
174
+ }
@@ -0,0 +1,9 @@
1
+ import presets from './profiles.json' with { type: 'json' };
2
+
3
+ export function runtimeProfile(environment = process.env, production = environment.NODE_ENV !== 'development') {
4
+ const requested = environment.PRNEXT_PROFILE || (environment.PRNEXT_MEMORY_PROFILE === 'compact' ? 'compact' : 'balanced');
5
+ const name = requested === 'standard' ? 'classic' : requested;
6
+ if (!Object.hasOwn(presets, name)) throw new Error(`Unknown runtime profile ${JSON.stringify(name)}; use ${Object.keys(presets).join(', ')}`);
7
+ const effective = production ? name : 'classic';
8
+ return { name: effective, ...presets[effective] };
9
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "classic": {
3
+ "optimizeForSize": false,
4
+ "semiSpaceMiB": null,
5
+ "youngGenerationMiB": 16,
6
+ "renderStarts": 16,
7
+ "liveResponses": 32,
8
+ "apiConcurrency": 512,
9
+ "responseBufferMiB": 8
10
+ },
11
+ "compact": {
12
+ "optimizeForSize": true,
13
+ "semiSpaceMiB": 4,
14
+ "youngGenerationMiB": 8,
15
+ "renderStarts": 16,
16
+ "liveResponses": 32,
17
+ "apiConcurrency": 512,
18
+ "responseBufferMiB": 8
19
+ },
20
+ "balanced": {
21
+ "optimizeForSize": true,
22
+ "semiSpaceMiB": 8,
23
+ "youngGenerationMiB": 16,
24
+ "renderStarts": 16,
25
+ "liveResponses": 32,
26
+ "apiConcurrency": 256,
27
+ "responseBufferMiB": 4
28
+ },
29
+ "speed": {
30
+ "optimizeForSize": false,
31
+ "semiSpaceMiB": 16,
32
+ "youngGenerationMiB": 32,
33
+ "renderStarts": 32,
34
+ "liveResponses": 128,
35
+ "apiConcurrency": 512,
36
+ "responseBufferMiB": 16
37
+ },
38
+ "memory": {
39
+ "optimizeForSize": true,
40
+ "semiSpaceMiB": 2,
41
+ "youngGenerationMiB": 4,
42
+ "renderStarts": 8,
43
+ "liveResponses": 16,
44
+ "apiConcurrency": 64,
45
+ "responseBufferMiB": 1
46
+ }
47
+ }