@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,410 @@
1
+ import React from 'react';
2
+ import { posix } from 'node:path';
3
+ import { paramsBySegment } from './app-segments.mjs';
4
+ import { validateMetadataVariants } from './metadata-route.mjs';
5
+ import { currentRequest, runRequestContext } from '../compat/headers.cjs';
6
+ import { staticParams, dynamicUsage } from '../compat/static-generation.cjs';
7
+
8
+ const list = value => value == null ? [] : Array.isArray(value) ? value : [value];
9
+ const object = value => value && typeof value === 'object' ? value : {};
10
+ const descriptor = value => typeof value === 'object' && !(value instanceof URL) ? value : { url: value };
11
+ const titleValue = value => typeof value === 'string' ? value : value?.absolute;
12
+ const socialArrays = ['emails', 'phoneNumbers', 'faxNumbers', 'alternateLocale', 'audio', 'videos', 'authors', 'tags', 'albums', 'songs', 'musicians', 'creators', 'actors', 'directors', 'writers'];
13
+ const robotKeys = ['noarchive', 'nosnippet', 'noimageindex', 'nocache', 'notranslate', 'indexifembedded', 'nositelinkssearchbox', 'unavailable_after', 'max-video-preview', 'max-image-preview', 'max-snippet'];
14
+
15
+ function strings(value) {
16
+ if (value instanceof URL) return value.href;
17
+ if (Array.isArray(value)) return value.map(strings);
18
+ if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, strings(item)]));
19
+ return value;
20
+ }
21
+
22
+ function resolveTitle(value, template) {
23
+ const own = object(value);
24
+ const text = typeof value === 'string' ? value : own.default;
25
+ return { absolute: own.absolute || (text ? template ? template.replace(/%s/g, text) : text : ''), template: own.template || null };
26
+ }
27
+
28
+ function metadataBase(value) {
29
+ if (value == null) return null;
30
+ try { return new URL(value).href; }
31
+ catch { throw new TypeError(`metadataBase is not a valid URL: ${value}`); }
32
+ }
33
+
34
+ // Next composes paths under metadataBase, including leading slashes. Resolve
35
+ // at the exporting segment so a child's different base cannot alter its parent.
36
+ function resolveUrl(value, base) {
37
+ if (value instanceof URL) return value.href;
38
+ try { return new URL(value).href; } catch {}
39
+ const origin = new URL(base || `http://localhost:${process.env.PORT || 3000}`);
40
+ return new URL(posix.join(origin.pathname, value), origin).href;
41
+ }
42
+
43
+ function resolvePageUrl(value, base, pathname, trailingSlash, alternate = false) {
44
+ if ((alternate && value instanceof URL) || (typeof value === 'string' && value.startsWith('./'))) {
45
+ let request;
46
+ try { request = currentRequest(); } catch { /* Direct metadata resolution. */ }
47
+ if (request?.staticState && request.partialParams?.length) dynamicUsage('params for a path not supplied by generateStaticParams', request);
48
+ }
49
+ if (alternate && value instanceof URL) {
50
+ const absolute = new URL(pathname, value);
51
+ value.searchParams.forEach((item, key) => absolute.searchParams.set(key, item));
52
+ value = absolute;
53
+ }
54
+ if (typeof value === 'string' && value.startsWith('./')) value = posix.resolve(pathname, value);
55
+ let result = base ? resolveUrl(value, base) : value;
56
+ if (result instanceof URL || (base && typeof result === 'string')) {
57
+ const url = new URL(result);
58
+ result = url.pathname === '/' && !url.search ? url.origin : url.href;
59
+ }
60
+ if (trailingSlash && typeof result === 'string' && !result.endsWith('/') && !result.includes('?')) {
61
+ try {
62
+ const url = new URL(result);
63
+ if ((!base || url.origin === new URL(base).origin) && !/^(?:\/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+))(\/?|$)/i.test(url.pathname)) result += '/';
64
+ } catch {}
65
+ }
66
+ return result;
67
+ }
68
+
69
+ function imageBase(base) {
70
+ if (base) return base;
71
+ const preview = process.env.VERCEL_BRANCH_URL || process.env.VERCEL_URL;
72
+ if (process.env.NODE_ENV === 'production' && process.env.VERCEL_ENV === 'preview' && preview) return `https://${preview}`;
73
+ if (process.env.NODE_ENV !== 'development' && process.env.VERCEL_PROJECT_PRODUCTION_URL) return `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`;
74
+ return null;
75
+ }
76
+
77
+ function resolveSocial(value, base, pathname, trailingSlash, template, twitter = false) {
78
+ if (!value) return null;
79
+ const resolved = { ...strings(value), title: resolveTitle(value.title, template) };
80
+ if (value.images != null) resolved.images = list(value.images).filter(item => item && descriptor(item).url).map(item => {
81
+ const image = descriptor(item);
82
+ return { ...strings(image), url: resolveUrl(image.url, imageBase(base)) };
83
+ });
84
+ if (twitter) {
85
+ resolved.card = value.card || (resolved.images?.length ? 'summary_large_image' : 'summary');
86
+ if (resolved.card === 'player') resolved.players = list(resolved.players);
87
+ if (resolved.card === 'app') resolved.app ||= {};
88
+ } else {
89
+ resolved.url = value.url ? resolvePageUrl(value.url, base, pathname, trailingSlash) : null;
90
+ for (const field of socialArrays) if (field in value) resolved[field] = value[field] == null ? null : list(strings(value[field]));
91
+ }
92
+ return resolved;
93
+ }
94
+
95
+ function normalize(own, previous, pathname, trailingSlash, templates) {
96
+ if (!Object.keys(own).length) return own;
97
+ const base = metadataBase(own.metadataBase !== undefined ? own.metadataBase : previous.metadataBase);
98
+ const resolved = strings(own);
99
+ if (Object.hasOwn(own, 'metadataBase')) resolved.metadataBase = base;
100
+ if (own.other) resolved.other = { ...previous.other, ...strings(own.other) };
101
+ else if (Object.hasOwn(own, 'other')) resolved.other = previous.other;
102
+ const pageUrl = value => resolvePageUrl(value, base, pathname, trailingSlash, true);
103
+ if (own.alternates) {
104
+ resolved.alternates = { canonical: own.alternates.canonical ? { url: pageUrl(descriptor(own.alternates.canonical).url) } : null };
105
+ for (const field of ['languages', 'media', 'types']) {
106
+ resolved.alternates[field] = own.alternates[field] ? Object.fromEntries(Object.entries(own.alternates[field]).map(([key, values]) => [key,
107
+ list(values).filter(Boolean).map(value => { const item = descriptor(value); return { url: pageUrl(item.url), title: item.title }; }),
108
+ ])) : null;
109
+ }
110
+ }
111
+ if (Object.hasOwn(own, 'openGraph')) resolved.openGraph = resolveSocial(own.openGraph, base, pathname, trailingSlash, templates.openGraph);
112
+ if (Object.hasOwn(own, 'twitter')) resolved.twitter = resolveSocial(own.twitter, base, pathname, trailingSlash, templates.twitter, true);
113
+ for (const field of ['authors', 'keywords', 'archives', 'assets', 'bookmarks']) if (own[field] != null) resolved[field] = list(strings(own[field]));
114
+ if (own.verification) {
115
+ resolved.verification = {};
116
+ for (const field of ['google', 'yahoo', 'yandex', 'me']) if (own.verification[field]) resolved.verification[field] = list(strings(own.verification[field]));
117
+ if (own.verification.other) resolved.verification.other = Object.fromEntries(Object.entries(own.verification.other).map(([key, value]) => [key, list(strings(value))]));
118
+ }
119
+ if (own.appleWebApp) resolved.appleWebApp = own.appleWebApp === true ? { capable: true } : {
120
+ capable: 'capable' in own.appleWebApp ? !!own.appleWebApp.capable : true,
121
+ title: own.appleWebApp.title || null,
122
+ startupImage: own.appleWebApp.startupImage ? list(own.appleWebApp.startupImage).map(item => strings(descriptor(item))) : null,
123
+ statusBarStyle: own.appleWebApp.statusBarStyle || 'default',
124
+ };
125
+ if (own.appLinks) resolved.appLinks = Object.fromEntries(Object.entries(own.appLinks).map(([key, value]) => [key, list(strings(value))]));
126
+ if (own.facebook) resolved.facebook = { ...own.facebook, admins: own.facebook.admins == null ? undefined : list(own.facebook.admins) };
127
+ if (own.itunes) resolved.itunes = { ...own.itunes, appArgument: own.itunes.appArgument ? pageUrl(own.itunes.appArgument) : undefined };
128
+ if (own.pagination) resolved.pagination = Object.fromEntries(['previous', 'next'].map(key => [key, own.pagination[key] ? pageUrl(own.pagination[key]) : null]));
129
+ if (own.robots) resolved.robots = { basic: robotContent(own.robots), googleBot: robotContent(object(own.robots).googleBot) };
130
+ if (own.icons) {
131
+ const icons = typeof own.icons === 'string' || own.icons instanceof URL || Array.isArray(own.icons) ? { icon: own.icons } : own.icons;
132
+ resolved.icons = Object.fromEntries(Object.entries(icons).map(([key, value]) => [key, list(value).filter(Boolean).map(item => strings(descriptor(item)))]));
133
+ }
134
+ return resolved;
135
+ }
136
+
137
+ async function exported(module, field, generator, props, parent) {
138
+ if (!module) return {};
139
+ if (module[field] !== undefined && module[generator] !== undefined) throw new Error(`A route cannot export both ${field} and ${generator}`);
140
+ // Generators receive their own snapshot: mutations cannot alter a shared export
141
+ // or the metadata inherited by the page/request that follows them.
142
+ const value = module[generator] === undefined ? module[field] : await module[generator](props, Promise.resolve(structuredClone(parent)));
143
+ if (value != null && (typeof value !== 'object' || Array.isArray(value))) throw new Error(`${field} must be an object`);
144
+ return value || {};
145
+ }
146
+
147
+ function routePath(segments, params) {
148
+ const parts = [];
149
+ for (const { segment } of segments) {
150
+ if (!segment || segment.startsWith('(') || segment.startsWith('@')) continue;
151
+ const dynamic = /^\[\[?(?:\.\.\.)?([^\]]+)\]\]?$/.exec(segment);
152
+ if (dynamic) parts.push(...list(params[dynamic[1]]).map(value => encodeURIComponent(value)));
153
+ else parts.push(segment);
154
+ }
155
+ return `/${parts.join('/')}`;
156
+ }
157
+
158
+ async function resolveMetadataFiles(files, params) {
159
+ const result = {};
160
+ for (const file of files) {
161
+ const scoped = {};
162
+ for (const match of file.path.matchAll(/\[\[?(?:\.\.\.)?([^\]]+)\]\]?/g)) if (params[match[1]] !== undefined) scoped[match[1]] = params[match[1]];
163
+ const variants = file.multiple ? validateMetadataVariants(await file.module.generateImageMetadata({ params: scoped }), 'generateImageMetadata') : [file.info || { ...file.module, ...file.module?.size, type: file.module?.contentType }];
164
+ for (const item of variants) {
165
+ let url = file.pattern.replace(/\[__metadata_id__\]/g, encodeURIComponent(String(item.id)));
166
+ url = url.replace(/\[\[\.\.\.([^\]]+)\]\]|\[\.\.\.([^\]]+)\]|\[([^\]]+)\]/g, (_match, optional, catchall, single) => list(params[optional || catchall || single]).map(encodeURIComponent).join('/'));
167
+ if (item.hash && file.kind !== 'favicon') url += '?' + item.hash;
168
+ const width = item.width ?? item.size?.width;
169
+ const height = item.height ?? item.size?.height;
170
+ const descriptor = { url, ...(item.type || item.contentType ? { type: item.type || item.contentType } : {}), ...(width ? { width } : {}), ...(height ? { height } : {}), ...(item.alt ? { alt: item.alt } : {}) };
171
+ if (['favicon', 'icon', 'apple-icon'].includes(file.kind)) {
172
+ const kind = file.kind === 'apple-icon' ? 'apple' : 'icon';
173
+ result.icons ||= {};
174
+ (result.icons[kind] ||= []).push({ url, type: descriptor.type, ...(item.sizes || width && height ? { sizes: item.sizes || `${width}x${height}` } : {}) });
175
+ } else (result[file.kind === 'opengraph-image' ? 'openGraph' : 'twitter'] ||= []).push(descriptor);
176
+ }
177
+ }
178
+ return result;
179
+ }
180
+
181
+ /** Resolve in segment order, with shallow inheritance like Next's Metadata API. */
182
+ export async function resolveMetadata(entry, { params, searchParams, pathname, trailingSlash = false }, notFoundIndex = -1) {
183
+ let metadata = {};
184
+ let viewport = { width: 'device-width', initialScale: 1 };
185
+ let title = null;
186
+ let templates = { title: null, openGraph: null, twitter: null };
187
+ let leafTemplates = templates;
188
+ const segments = notFoundIndex < 0 ? entry.segments || [] : (entry.segments || []).slice(0, notFoundIndex + 1);
189
+ let request;
190
+ try { request = currentRequest(); } catch { /* Metadata can also be resolved directly. */ }
191
+ // Resolve a generator before awaiting its params: a generator which doesn't
192
+ // read them may still expose uncached metadata that build validation must see.
193
+ const values = request?.staticState && request.partialParams?.length ? request.params : await params;
194
+ const scopedParams = paramsBySegment(segments, values);
195
+ pathname ||= routePath(entry.segments || [], values || {});
196
+ async function apply(module, props, activeTemplates, updateTemplate, filePath, fileValues = values) {
197
+ const parent = { ...metadata, title };
198
+ let own = await exported(module, 'metadata', 'generateMetadata', props, parent);
199
+ if (filePath !== undefined) {
200
+ const selected = (entry.metadataFiles || []).filter(item => item.path === filePath);
201
+ let resolved = {};
202
+ if (selected.length) {
203
+ resolved = await fileValues || {};
204
+ const required = Object.fromEntries(Object.entries(resolved).filter(([name]) => selected.some(item =>
205
+ [...item.path.matchAll(/\[\[?(?:\.\.\.)?([^\]]+)\]\]?/g)].some(match => match[1] === name))));
206
+ await staticParams(required, request);
207
+ }
208
+ const files = await resolveMetadataFiles(selected, resolved);
209
+ own = { ...own };
210
+ for (const field of ['openGraph', 'twitter']) if (files[field] && !Object.hasOwn(own[field] || {}, 'images')) own[field] = { ...(own[field] || metadata[field]), images: files[field] };
211
+ if (files.icons) own.icons = { ...metadata.icons, ...own.icons, ...files.icons };
212
+ }
213
+ metadata = { ...metadata, ...normalize(own, metadata, pathname, trailingSlash, activeTemplates) };
214
+ if (Object.hasOwn(own, 'title')) title = resolveTitle(own.title, activeTemplates.title);
215
+ if (updateTemplate) templates = {
216
+ title: title?.template || null,
217
+ openGraph: metadata.openGraph?.title?.template || null,
218
+ twitter: metadata.twitter?.title?.template || null,
219
+ };
220
+ const ownViewport = await exported(module, 'viewport', 'generateViewport', props, viewport);
221
+ viewport = { ...viewport, ...ownViewport };
222
+ }
223
+ if (entry.metadataItems && notFoundIndex < 0) {
224
+ // App metadata is accumulated in loader-tree order, including parallel
225
+ // branches. Each generator sees metadata resolved by preceding branches.
226
+ for (const [index, item] of entry.metadataItems.entries()) {
227
+ const resolve = async () => apply(item.module, { params: item.params, ...(item.searchParams ? { searchParams: item.searchParams } : {}) },
228
+ templates, index < entry.metadataItems.length - 2, item.filePath, item.params);
229
+ if (item.context) await runRequestContext({ ...currentRequest(), ...item.context, privateCache: undefined, privateCacheBytes: 0, draftProvider: undefined }, resolve);
230
+ else await resolve();
231
+ }
232
+ } else {
233
+ for (const [index, segment] of segments.entries()) {
234
+ leafTemplates = templates;
235
+ await apply(segment.layout, { params: staticParams(scopedParams.get(segment), request) }, templates, true, index < segments.length - 1 || notFoundIndex >= 0 ? segment.path : undefined);
236
+ }
237
+ // A layout's template applies below its segment, never to its sibling page.
238
+ if (notFoundIndex < 0) await apply(entry.page, { params, searchParams }, leafTemplates, false, segments.at(-1)?.path);
239
+ else metadata.robots = { basic: 'noindex' };
240
+ }
241
+ const favicon = (entry.metadataFiles || []).find(file => file.kind === 'favicon');
242
+ if (favicon) {
243
+ const fileIcons = (await resolveMetadataFiles([favicon], values || {})).icons.icon;
244
+ metadata.icons = { ...metadata.icons, icon: [...fileIcons, ...list(metadata.icons?.icon).filter(item => descriptor(item).url !== fileIcons[0].url)] };
245
+ }
246
+ const og = metadata.openGraph;
247
+ if (og) {
248
+ const twitter = metadata.twitter;
249
+ const fallback = resolveSocial({ title: titleValue(og.title) ? og.title : title, description: og.description || metadata.description, images: og.images }, metadata.metadataBase, pathname, trailingSlash, templates.twitter, true);
250
+ metadata.twitter = twitter ? { ...twitter,
251
+ ...!titleValue(twitter.title) && { title: fallback.title },
252
+ ...!twitter.description && { description: fallback.description },
253
+ ...!twitter.images && { images: fallback.images },
254
+ } : fallback;
255
+ }
256
+ for (const social of [metadata.openGraph, metadata.twitter]) if (social) {
257
+ if (!titleValue(social.title) && titleValue(title)) social.title = title;
258
+ if (!social.description && metadata.description) social.description = metadata.description;
259
+ }
260
+ return { ...metadata, ...entry.staticMetadata, title: title?.absolute || null, viewport };
261
+ }
262
+
263
+ function robotContent(value) {
264
+ if (typeof value === 'string') return value;
265
+ const parts = [];
266
+ for (const name of ['index', 'follow']) if (typeof value?.[name] === 'boolean') parts.push(value[name] ? name : `no${name}`);
267
+ for (const name of robotKeys) {
268
+ const option = value?.[name];
269
+ if (option !== undefined && option !== false) parts.push(typeof option === 'boolean' ? name : `${name}:${option}`);
270
+ }
271
+ return parts.join(', ');
272
+ }
273
+
274
+ export function metadataElements(metadata) {
275
+ const elements = [];
276
+ let count = 0;
277
+ const tag = (name, props, text) => elements.push(React.createElement(name, { ...props, key: `metadata-${count++}` }, text));
278
+ const meta = (name, content, attributes = {}) => {
279
+ if (content !== undefined && content !== null && content !== '') tag('meta', { name, content: String(content), ...attributes });
280
+ };
281
+ const property = (name, content) => {
282
+ for (const value of list(content)) if (value != null && value !== '') tag('meta', { property: name, content: String(value) });
283
+ };
284
+ const properties = (prefix, value, fields) => {
285
+ for (const [field, label] of Object.entries(fields)) property(`${prefix}:${label}`, value?.[field]);
286
+ };
287
+ // The base property of a descriptor omits the trailing colon.
288
+ const media = (emit, prefix, values, fields) => {
289
+ for (const value of list(values)) if (value) {
290
+ const item = descriptor(value);
291
+ if (item.url) emit(prefix, item.url);
292
+ for (const [field, label] of Object.entries(fields)) if (item[field] != null && item[field] !== '') emit(`${prefix}:${label}`, item[field]);
293
+ }
294
+ };
295
+ tag('meta', { charSet: 'utf-8' });
296
+ const viewport = metadata.viewport || { width: 'device-width', initialScale: 1 };
297
+ const fields = { width: 'width', height: 'height', initialScale: 'initial-scale', minimumScale: 'minimum-scale', maximumScale: 'maximum-scale', userScalable: 'user-scalable', viewportFit: 'viewport-fit', interactiveWidget: 'interactive-widget' };
298
+ meta('viewport', Object.entries(fields).filter(([name]) => viewport[name] != null).map(([name, label]) => `${label}=${typeof viewport[name] === 'boolean' ? viewport[name] ? 'yes' : 'no' : viewport[name]}`).join(', '));
299
+ for (const color of list(viewport.themeColor)) if (color) meta('theme-color', typeof color === 'string' ? color : color.color, color.media ? { media: color.media } : {});
300
+ meta('color-scheme', viewport.colorScheme);
301
+ if (titleValue(metadata.title)) tag('title', {}, titleValue(metadata.title));
302
+ for (const [field, name] of Object.entries({ description: 'description', applicationName: 'application-name', generator: 'generator', referrer: 'referrer', creator: 'creator', publisher: 'publisher', category: 'category', abstract: 'abstract', classification: 'classification' })) meta(name, metadata[field]);
303
+ meta('keywords', list(metadata.keywords).join(','));
304
+ for (const author of list(metadata.authors)) if (author) {
305
+ if (author.url) tag('link', { rel: 'author', href: String(author.url) });
306
+ meta('author', typeof author === 'string' ? author : author.name);
307
+ }
308
+ if (metadata.manifest) tag('link', { rel: 'manifest', href: String(metadata.manifest) });
309
+ for (const rel of ['archives', 'assets', 'bookmarks']) for (const url of list(metadata[rel])) if (url) tag('link', { rel, href: String(url) });
310
+ for (const [field, rel] of [['previous', 'prev'], ['next', 'next']]) if (metadata.pagination?.[field]) tag('link', { rel, href: String(metadata.pagination[field]) });
311
+ meta('robots', metadata.robots?.basic ?? robotContent(metadata.robots));
312
+ meta('googlebot', typeof metadata.robots?.googleBot === 'string' ? metadata.robots.googleBot : robotContent(metadata.robots?.googleBot));
313
+ if (metadata.formatDetection) meta('format-detection', ['telephone', 'date', 'address', 'email', 'url'].filter(name => metadata.formatDetection[name] === false).map(name => `${name}=no`).join(', '));
314
+ const alternates = object(metadata.alternates);
315
+ if (alternates.canonical) {
316
+ const item = descriptor(alternates.canonical);
317
+ if (item.url) tag('link', { rel: 'canonical', href: String(item.url), title: item.title });
318
+ }
319
+ for (const [field, attribute] of [['languages', 'hrefLang'], ['media', 'media'], ['types', 'type']]) {
320
+ for (const [name, values] of Object.entries(alternates[field] || {})) for (const value of list(values)) if (value) {
321
+ const item = descriptor(value);
322
+ if (item.url) tag('link', { rel: 'alternate', [attribute]: name, href: String(item.url), title: item.title });
323
+ }
324
+ }
325
+ const verification = object(metadata.verification);
326
+ for (const [field, name] of Object.entries({ google: 'google-site-verification', yahoo: 'y_key', yandex: 'yandex-verification', me: 'me' })) for (const value of list(verification[field])) meta(name, value);
327
+ for (const [name, values] of Object.entries(verification.other || {})) for (const value of list(values)) meta(name, value);
328
+ if (metadata.appleWebApp) {
329
+ const apple = metadata.appleWebApp === true ? { capable: true } : metadata.appleWebApp;
330
+ if (apple.capable !== false) meta('mobile-web-app-capable', 'yes');
331
+ meta('apple-mobile-web-app-title', apple.title);
332
+ for (const value of list(apple.startupImage)) if (value) {
333
+ const image = descriptor(value);
334
+ if (image.url) tag('link', { rel: 'apple-touch-startup-image', href: String(image.url), media: image.media });
335
+ }
336
+ meta('apple-mobile-web-app-status-bar-style', apple.statusBarStyle);
337
+ }
338
+ if (metadata.itunes) meta('apple-itunes-app', `app-id=${metadata.itunes.appId}${metadata.itunes.appArgument ? `, app-argument=${metadata.itunes.appArgument}` : ''}`);
339
+ property('fb:app_id', metadata.facebook?.appId);
340
+ property('fb:admins', metadata.facebook?.admins);
341
+ property('pinterest-rich-pin', metadata.pinterest?.richPin);
342
+ const icons = metadata.icons;
343
+ for (const [name, rel] of Object.entries({ shortcut: 'shortcut icon', icon: 'icon', apple: 'apple-touch-icon', other: 'icon' })) {
344
+ const items = typeof icons === 'string' || icons instanceof URL || Array.isArray(icons) ? name === 'icon' ? icons : [] : object(icons)[name];
345
+ for (const item of list(items)) if (item) {
346
+ const icon = descriptor(item);
347
+ if (icon.url) tag('link', { rel: icon.rel || rel, href: String(icon.url), sizes: icon.sizes, type: icon.type, media: icon.media, color: icon.color, fetchPriority: icon.fetchPriority });
348
+ }
349
+ }
350
+ const og = object(metadata.openGraph);
351
+ property('og:title', titleValue(og.title));
352
+ properties('og', og, { description: 'description', locale: 'locale', siteName: 'site_name', countryName: 'country_name', determiner: 'determiner', url: 'url', ttl: 'ttl', emails: 'email', phoneNumbers: 'phone_number', faxNumbers: 'fax_number', alternateLocale: 'locale:alternate' });
353
+ const imageFields = { secureUrl: 'secure_url', type: 'type', width: 'width', height: 'height', alt: 'alt' };
354
+ media(property, 'og:image', og.images, imageFields);
355
+ media(property, 'og:video', og.videos, { secureUrl: 'secure_url', type: 'type', width: 'width', height: 'height' });
356
+ media(property, 'og:audio', og.audio, { secureUrl: 'secure_url', type: 'type' });
357
+ if (Object.hasOwn(og, 'type')) {
358
+ property('og:type', og.type);
359
+ switch (og.type) {
360
+ case 'website': case 'video.tv_show': case 'video.other': break;
361
+ case 'article':
362
+ properties('article', og, { publishedTime: 'published_time', modifiedTime: 'modified_time', expirationTime: 'expiration_time', authors: 'author', section: 'section', tags: 'tag' }); break;
363
+ case 'book':
364
+ properties('book', og, { isbn: 'isbn', releaseDate: 'release_date', authors: 'author', tags: 'tag' }); break;
365
+ case 'profile':
366
+ properties('profile', og, { firstName: 'first_name', lastName: 'last_name', username: 'username', gender: 'gender' }); break;
367
+ case 'music.song':
368
+ property('music:duration', og.duration);
369
+ media(property, 'music:album', og.albums, { disc: 'disc', track: 'track' });
370
+ property('music:musician', og.musicians); break;
371
+ case 'music.album': case 'music.playlist':
372
+ media(property, 'music:song', og.songs, { disc: 'disc', track: 'track' });
373
+ if (og.type === 'music.album') properties('music', og, { musicians: 'musician', releaseDate: 'release_date' });
374
+ else property('music:creator', og.creators);
375
+ break;
376
+ case 'music.radio_station': property('music:creator', og.creators); break;
377
+ case 'video.movie': case 'video.episode':
378
+ media(property, 'video:actor', og.actors, { role: 'role' });
379
+ properties('video', og, { directors: 'director', writers: 'writer', duration: 'duration', releaseDate: 'release_date', tags: 'tag' });
380
+ if (og.type === 'video.episode') property('video:series', og.series);
381
+ break;
382
+ default: throw new Error(`Invalid OpenGraph type: ${og.type}`);
383
+ }
384
+ }
385
+ const twitter = object(metadata.twitter);
386
+ meta('twitter:title', titleValue(twitter.title));
387
+ for (const field of ['card', 'description', 'site', 'siteId', 'creator', 'creatorId']) meta(`twitter:${field === 'siteId' ? 'site:id' : field === 'creatorId' ? 'creator:id' : field}`, twitter[field]);
388
+ media(meta, 'twitter:image', twitter.images, imageFields);
389
+ if (twitter.card === 'player') for (const player of list(twitter.players)) if (player) {
390
+ for (const [field, name] of Object.entries({ playerUrl: 'player', streamUrl: 'player:stream', width: 'player:width', height: 'player:height' })) meta(`twitter:${name}`, player[field]);
391
+ }
392
+ if (twitter.card === 'app') for (const platform of ['iphone', 'ipad', 'googleplay']) {
393
+ meta(`twitter:app:name:${platform}`, twitter.app?.name);
394
+ meta(`twitter:app:id:${platform}`, twitter.app?.id?.[platform]);
395
+ meta(`twitter:app:url:${platform}`, twitter.app?.url?.[platform]);
396
+ }
397
+ const appFields = {
398
+ ios: ['url', 'app_store_id', 'app_name'], iphone: ['url', 'app_store_id', 'app_name'], ipad: ['url', 'app_store_id', 'app_name'],
399
+ android: ['package', 'url', 'class', 'app_name'], windows_phone: ['url', 'app_id', 'app_name'], windows: ['url', 'app_id', 'app_name'],
400
+ windows_universal: ['url', 'app_id', 'app_name'], web: ['url', 'should_fallback'],
401
+ };
402
+ for (const [platform, fields] of Object.entries(appFields)) for (const item of list(metadata.appLinks?.[platform])) for (const field of fields) property(`al:${platform}:${field}`, item?.[field]);
403
+ for (const [name, values] of Object.entries(metadata.other || {})) for (const value of list(values)) meta(name, value);
404
+ return elements;
405
+ }
406
+
407
+ export async function Metadata({ entry, params, searchParams, pathname, trailingSlash, notFoundIndex }) {
408
+ const metadata = await resolveMetadata(entry, { params, searchParams, pathname, trailingSlash }, notFoundIndex);
409
+ return React.createElement(React.Fragment, null, ...metadataElements(metadata));
410
+ }
@@ -0,0 +1,228 @@
1
+ // Flight's lazy values are part of React's decoded model. Preserve their shape
2
+ // while replacing only the explicit request-data holes in a build-time tree.
3
+ // The official encoder produces all final wire IDs; no Flight rows are edited.
4
+ import { withSignal } from './stream-utils.mjs';
5
+ const LAZY = Symbol.for('react.lazy');
6
+ const ELEMENT = Symbol.for('react.transitional.element');
7
+ const POSTPONED = 'PRNEXT_PPR_DYNAMIC';
8
+ const HOLE = Symbol('PRNext partial hole');
9
+
10
+ export function isPartialHole(error) { return error?.digest === POSTPONED || error?.code === POSTPONED; }
11
+
12
+ export function boundedPartialFlight(source, limit = 16 * 1024 * 1024) {
13
+ let size = 0, closed = false;
14
+ const reader = source.getReader();
15
+ async function close(reason, cancel = false) {
16
+ if (closed) return;
17
+ closed = true;
18
+ try { if (cancel) await reader.cancel(reason); }
19
+ finally { reader.releaseLock(); }
20
+ }
21
+ // A single reader keeps the same bound and backpressure without allocating a
22
+ // TransformStream's second queue and pipe pump for every PPR continuation.
23
+ return new ReadableStream({
24
+ async pull(controller) {
25
+ try {
26
+ const {value, done} = await reader.read();
27
+ if (closed) return;
28
+ if (done) { await close(); controller.close(); return; }
29
+ size += value.byteLength;
30
+ if (size > limit) throw new Error('Partial live Flight exceeds the 16 MiB response limit');
31
+ controller.enqueue(value);
32
+ } catch (error) {
33
+ if (closed) return;
34
+ controller.error(error);
35
+ try { await close(error, true); } catch { /* Keep the original stream failure. */ }
36
+ }
37
+ },
38
+ cancel(reason) { return close(reason, true); },
39
+ }, { highWaterMark: 0 });
40
+ }
41
+
42
+ export async function preparePartialModel(model, {signal} = {}) {
43
+ const visited = new WeakSet();
44
+ async function visit(value) {
45
+ signal?.throwIfAborted();
46
+ if (!value || typeof value !== 'object' || visited.has(value)) return;
47
+ visited.add(value);
48
+ if (value instanceof Map) {
49
+ await Promise.all([...value].flatMap(([key, item]) => [visit(key), visit(item)]));
50
+ return;
51
+ }
52
+ if (value instanceof Set) { await Promise.all([...value].map(visit)); return; }
53
+ if (value.$$typeof === LAZY || typeof value.then === 'function') {
54
+ try { await visit(await unwrap(value, signal)); }
55
+ catch (error) { if (!isPartialHole(error)) throw error; }
56
+ return;
57
+ }
58
+ if (value.$$typeof === ELEMENT) {
59
+ // Only modules present in the static model are imported. A dynamic hole
60
+ // must not make us preload unrelated client modules from the whole app.
61
+ if (value.type?.$$typeof === LAZY) await unwrap(value.type, signal);
62
+ await visit(value.props);
63
+ return;
64
+ }
65
+ const prototype = Object.getPrototypeOf(value);
66
+ if (Array.isArray(value) || prototype === Object.prototype || prototype === null) {
67
+ await Promise.all(Object.values(value).map(visit));
68
+ }
69
+ }
70
+ await visit(model);
71
+ }
72
+
73
+ function unwrap(value, signal) {
74
+ for (;;) {
75
+ signal?.throwIfAborted();
76
+ if (value?.$$typeof === LAZY) {
77
+ try { value = value._init(value._payload); }
78
+ catch (error) {
79
+ if (typeof error?.then !== 'function') throw error;
80
+ return withSignal(error, signal).then(() => unwrap(value, signal));
81
+ }
82
+ } else if (value && typeof value.then === 'function') return withSignal(value, signal).then(result => unwrap(result, signal));
83
+ else return value;
84
+ }
85
+ }
86
+
87
+ function lazy(callback, onHole, suspended) {
88
+ let status = 'uninitialized', value, promise;
89
+ return { $$typeof: LAZY, _payload: {}, _init() {
90
+ // Decoded static chunks are usually already ready. Do not allocate a
91
+ // microtask/promise for each one, or traverse branches React never renders.
92
+ if (status === 'uninitialized') {
93
+ try {
94
+ value = callback();
95
+ if (value && typeof value.then === 'function') {
96
+ status = 'pending';
97
+ promise = Promise.resolve(value).then(result => { status = 'fulfilled'; value = result; }, error => { status = 'rejected'; value = error; });
98
+ } else status = 'fulfilled';
99
+ } catch (error) { status = 'rejected'; value = error; }
100
+ }
101
+ if (status === 'pending') throw promise;
102
+ if (status === 'rejected') throw value;
103
+ if (value === HOLE) { onHole(); throw suspended; }
104
+ return value;
105
+ } };
106
+ }
107
+
108
+ function patternParams(pattern, pathname) {
109
+ const actual = pathname.split('/').filter(Boolean).map(decodeURIComponent);
110
+ const values = {};
111
+ for (const [index, segment] of pattern.split('/').filter(Boolean).entries()) {
112
+ const match = /^\[\[?(\.\.\.)?([^\]]+)\]\]?$/.exec(segment);
113
+ if (match && index < actual.length) values[match[2]] = match[1] ? actual.slice(index) : actual[index];
114
+ }
115
+ return values;
116
+ }
117
+
118
+ export function partialKeyMap(scopes = [], params = {}, pathname) {
119
+ return new Map(scopes.map(({ key, prefix, suffix = '', names, pattern, segments }) => {
120
+ const values = pattern && pathname ? patternParams(pattern, pathname) : params;
121
+ if (segments) {
122
+ const selected = segments.map(segment => {
123
+ const match = /^\[\[?(?:\.\.\.)?([^\]]+)\]\]?$/.exec(segment);
124
+ return match ? Array.isArray(values[match[1]]) ? values[match[1]].join('/') : values[match[1]] : segment;
125
+ }).filter(value => value !== undefined && !value.startsWith('@'));
126
+ return [key, selected.join('/') || prefix + JSON.stringify(Object.fromEntries(names.filter(name => Object.hasOwn(values, name)).map(name => [name, values[name]]))) + suffix];
127
+ }
128
+ return [key, prefix + JSON.stringify(Object.fromEntries(names.filter(name => Object.hasOwn(values, name)).map(name => [name, values[name]]))) + suffix];
129
+ }));
130
+ }
131
+
132
+ export function partialModel(model, { live, keyMap, reuse, onHole = () => {}, suspended = new Promise(() => {}) } = {}) {
133
+ const seen = new WeakMap();
134
+ const positions = new WeakMap();
135
+ const at = (collection,index) => {
136
+ let values=positions.get(collection);
137
+ if(!values){values=[...collection.keys()];positions.set(collection,values);}
138
+ return values[index];
139
+ };
140
+ const bindings = new Map((model.keyScopes || []).filter(scope => scope.liveProps?.length).map(scope => [scope.key, scope.liveProps]));
141
+ const property = (getter, key) => {
142
+ let ready = false, value;
143
+ return () => {
144
+ if (!ready) {
145
+ const parent = unwrap(getter());
146
+ value = parent && typeof parent.then === 'function' ? parent.then(item => item?.[key]) : parent?.[key];
147
+ ready = true;
148
+ }
149
+ return value;
150
+ };
151
+ };
152
+ function visit(value, current) {
153
+ if (!value || typeof value !== 'object') return value;
154
+ if (reuse?.(value)) return value;
155
+ if (seen.has(value)) return seen.get(value);
156
+ if (value.$$typeof === LAZY) {
157
+ const hole = error => {
158
+ if (!isPartialHole(error)) throw error;
159
+ if (current) return unwrap(current());
160
+ return HOLE;
161
+ };
162
+ const result=lazy(() => {
163
+ try {
164
+ const resolved = unwrap(value);
165
+ return resolved && typeof resolved.then === 'function' ? resolved.then(item => visit(item, current), hole) : visit(resolved, current);
166
+ } catch (error) { return hole(error); }
167
+ }, onHole, suspended);
168
+ seen.set(value,result);return result;
169
+ }
170
+ if (typeof value.then === 'function') {
171
+ const result=Promise.resolve(value).then(
172
+ result => visit(result, current), error => {
173
+ if (!isPartialHole(error)) throw error;
174
+ if (current) return unwrap(current());
175
+ onHole();
176
+ return suspended;
177
+ },
178
+ );
179
+ seen.set(value,result);return result;
180
+ }
181
+ if (value instanceof Map || value instanceof Set) {
182
+ if (seen.has(value)) return seen.get(value);
183
+ const copy = value instanceof Map ? new Map() : new Set();
184
+ seen.set(value, copy);
185
+ let index = 0;
186
+ for (const entry of value) {
187
+ const position = index++;
188
+ if (value instanceof Map) {
189
+ const [key, item] = entry;
190
+ const liveKey = current ? async () => at(await unwrap(await current()),position) : null;
191
+ // Primitive keys stay stable even if the live Map changes order.
192
+ const liveItem = current ? async () => {
193
+ const map = await unwrap(await current());
194
+ return map.get(key !== null && typeof key === 'object' ? await liveKey() : key);
195
+ } : null;
196
+ copy.set(visit(key, liveKey), visit(item, liveItem));
197
+ } else {
198
+ copy.add(visit(entry, current ? async () => at(await unwrap(await current()),position) : null));
199
+ }
200
+ }
201
+ return copy;
202
+ }
203
+ // Dates, typed arrays and references retain their rich-data identities.
204
+ const prototype = Object.getPrototypeOf(value);
205
+ if (!Array.isArray(value) && value.$$typeof !== ELEMENT && prototype !== Object.prototype && prototype !== null) return value;
206
+ if (seen.has(value)) return seen.get(value);
207
+ const copy = Array.isArray(value) ? [] : Object.create(prototype);
208
+ seen.set(value, copy);
209
+ for (const key of Object.keys(value)) {
210
+ if (value.$$typeof === ELEMENT && key !== 'props') copy[key] = key === 'key' && keyMap?.has(value[key]) ? keyMap.get(value[key]) : value[key];
211
+ else copy[key] = visit(value[key], current ? property(current, key) : null);
212
+ }
213
+ if (live && current && value.$$typeof === ELEMENT && bindings.has(value.key)) return lazy(async () => {
214
+ const actual = await unwrap(await current());
215
+ for (const name of bindings.get(value.key)) copy.props[name] = actual?.props?.[name];
216
+ return copy;
217
+ }, onHole, suspended);
218
+ return copy;
219
+ }
220
+ const output = visit(model, live ? () => live : null);
221
+ if (live) {
222
+ // These describe the current navigation, not the build-time document.
223
+ output.router = live.router;
224
+ if (model.routing) output.routing = live.routing;
225
+ delete output.keyScopes;
226
+ }
227
+ return output;
228
+ }