vinext 0.0.25 → 0.0.26

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 (86) hide show
  1. package/README.md +6 -1
  2. package/dist/check.js +4 -4
  3. package/dist/check.js.map +1 -1
  4. package/dist/cli.js +32 -1
  5. package/dist/cli.js.map +1 -1
  6. package/dist/client/entry.js.map +1 -1
  7. package/dist/client/vinext-next-data.d.ts +22 -0
  8. package/dist/client/vinext-next-data.d.ts.map +1 -0
  9. package/dist/client/vinext-next-data.js +2 -0
  10. package/dist/client/vinext-next-data.js.map +1 -0
  11. package/dist/config/config-matchers.d.ts.map +1 -1
  12. package/dist/config/config-matchers.js +6 -2
  13. package/dist/config/config-matchers.js.map +1 -1
  14. package/dist/config/next-config.d.ts +31 -4
  15. package/dist/config/next-config.d.ts.map +1 -1
  16. package/dist/config/next-config.js +151 -13
  17. package/dist/config/next-config.js.map +1 -1
  18. package/dist/deploy.d.ts +11 -0
  19. package/dist/deploy.d.ts.map +1 -1
  20. package/dist/deploy.js +42 -24
  21. package/dist/deploy.js.map +1 -1
  22. package/dist/entries/app-browser-entry.d.ts +9 -0
  23. package/dist/entries/app-browser-entry.d.ts.map +1 -0
  24. package/dist/entries/app-browser-entry.js +340 -0
  25. package/dist/entries/app-browser-entry.js.map +1 -0
  26. package/dist/{server/app-dev-server.d.ts → entries/app-rsc-entry.d.ts} +4 -17
  27. package/dist/entries/app-rsc-entry.d.ts.map +1 -0
  28. package/dist/{server/app-dev-server.js → entries/app-rsc-entry.js} +360 -1205
  29. package/dist/entries/app-rsc-entry.js.map +1 -0
  30. package/dist/entries/app-ssr-entry.d.ts +8 -0
  31. package/dist/entries/app-ssr-entry.d.ts.map +1 -0
  32. package/dist/entries/app-ssr-entry.js +449 -0
  33. package/dist/entries/app-ssr-entry.js.map +1 -0
  34. package/dist/entries/pages-client-entry.d.ts +4 -0
  35. package/dist/entries/pages-client-entry.d.ts.map +1 -0
  36. package/dist/entries/pages-client-entry.js +94 -0
  37. package/dist/entries/pages-client-entry.js.map +1 -0
  38. package/dist/entries/pages-entry-helpers.d.ts +7 -0
  39. package/dist/entries/pages-entry-helpers.d.ts.map +1 -0
  40. package/dist/entries/pages-entry-helpers.js +18 -0
  41. package/dist/entries/pages-entry-helpers.js.map +1 -0
  42. package/dist/entries/pages-server-entry.d.ts +8 -0
  43. package/dist/entries/pages-server-entry.d.ts.map +1 -0
  44. package/dist/entries/pages-server-entry.js +993 -0
  45. package/dist/entries/pages-server-entry.js.map +1 -0
  46. package/dist/index.d.ts +1 -25
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +206 -1242
  49. package/dist/index.js.map +1 -1
  50. package/dist/server/instrumentation.d.ts +1 -1
  51. package/dist/server/instrumentation.js +1 -1
  52. package/dist/server/instrumentation.js.map +1 -1
  53. package/dist/server/middleware-codegen.d.ts +1 -1
  54. package/dist/server/middleware-codegen.js +1 -1
  55. package/dist/server/middleware-codegen.js.map +1 -1
  56. package/dist/server/prod-server.d.ts.map +1 -1
  57. package/dist/server/prod-server.js +18 -3
  58. package/dist/server/prod-server.js.map +1 -1
  59. package/dist/server/request-pipeline.d.ts +92 -0
  60. package/dist/server/request-pipeline.d.ts.map +1 -0
  61. package/dist/server/request-pipeline.js +202 -0
  62. package/dist/server/request-pipeline.js.map +1 -0
  63. package/dist/shims/constants.d.ts +120 -3
  64. package/dist/shims/constants.d.ts.map +1 -1
  65. package/dist/shims/constants.js +170 -3
  66. package/dist/shims/constants.js.map +1 -1
  67. package/dist/shims/headers.d.ts.map +1 -1
  68. package/dist/shims/headers.js +1 -0
  69. package/dist/shims/headers.js.map +1 -1
  70. package/dist/shims/link.d.ts.map +1 -1
  71. package/dist/shims/link.js +2 -2
  72. package/dist/shims/link.js.map +1 -1
  73. package/dist/shims/metadata.d.ts +7 -1
  74. package/dist/shims/metadata.d.ts.map +1 -1
  75. package/dist/shims/metadata.js +9 -3
  76. package/dist/shims/metadata.js.map +1 -1
  77. package/dist/shims/og.d.ts +6 -6
  78. package/dist/shims/og.js +6 -6
  79. package/dist/shims/og.js.map +1 -1
  80. package/dist/utils/project.d.ts +15 -0
  81. package/dist/utils/project.d.ts.map +1 -1
  82. package/dist/utils/project.js +48 -0
  83. package/dist/utils/project.js.map +1 -1
  84. package/package.json +1 -1
  85. package/dist/server/app-dev-server.d.ts.map +0 -1
  86. package/dist/server/app-dev-server.js.map +0 -1
@@ -0,0 +1,340 @@
1
+ /**
2
+ * Generate the virtual browser entry module.
3
+ *
4
+ * This runs in the client (browser). It hydrates the page from the
5
+ * embedded RSC payload and handles client-side navigation by re-fetching
6
+ * RSC streams.
7
+ */
8
+ export function generateBrowserEntry() {
9
+ return `
10
+ import {
11
+ createFromReadableStream,
12
+ createFromFetch,
13
+ setServerCallback,
14
+ encodeReply,
15
+ createTemporaryReferenceSet,
16
+ } from "@vitejs/plugin-rsc/browser";
17
+ import { hydrateRoot } from "react-dom/client";
18
+ import { flushSync } from "react-dom";
19
+ import { setClientParams, setNavigationContext, toRscUrl, getPrefetchCache, getPrefetchedUrls, PREFETCH_CACHE_TTL } from "next/navigation";
20
+
21
+ let reactRoot;
22
+
23
+ /**
24
+ * Convert the embedded RSC chunks back to a ReadableStream.
25
+ * Each chunk is a text string that needs to be encoded back to Uint8Array.
26
+ */
27
+ function chunksToReadableStream(chunks) {
28
+ const encoder = new TextEncoder();
29
+ return new ReadableStream({
30
+ start(controller) {
31
+ for (const chunk of chunks) {
32
+ controller.enqueue(encoder.encode(chunk));
33
+ }
34
+ controller.close();
35
+ }
36
+ });
37
+ }
38
+
39
+ /**
40
+ * Create a ReadableStream from progressively-embedded RSC chunks.
41
+ * The server injects RSC data as <script> tags that push to
42
+ * self.__VINEXT_RSC_CHUNKS__ throughout the HTML stream, and sets
43
+ * self.__VINEXT_RSC_DONE__ = true when complete.
44
+ *
45
+ * Instead of polling with setTimeout, we monkey-patch the array's
46
+ * push() method so new chunks are delivered immediately when the
47
+ * server's <script> tags execute. This eliminates unnecessary
48
+ * wakeups and reduces latency — same pattern Next.js uses with
49
+ * __next_f. The stream closes on DOMContentLoaded (when all
50
+ * server-injected scripts have executed) or when __VINEXT_RSC_DONE__
51
+ * is set, whichever comes first.
52
+ */
53
+ function createProgressiveRscStream() {
54
+ const encoder = new TextEncoder();
55
+ return new ReadableStream({
56
+ start(controller) {
57
+ const chunks = self.__VINEXT_RSC_CHUNKS__ || [];
58
+
59
+ // Deliver any chunks that arrived before this code ran
60
+ // (from <script> tags that executed before the browser entry loaded)
61
+ for (const chunk of chunks) {
62
+ controller.enqueue(encoder.encode(chunk));
63
+ }
64
+
65
+ // If the stream is already complete, close immediately
66
+ if (self.__VINEXT_RSC_DONE__) {
67
+ controller.close();
68
+ return;
69
+ }
70
+
71
+ // Monkey-patch push() so future chunks stream in immediately
72
+ // when the server's <script> tags execute
73
+ let closed = false;
74
+ function closeOnce() {
75
+ if (!closed) {
76
+ closed = true;
77
+ controller.close();
78
+ }
79
+ }
80
+
81
+ const arr = self.__VINEXT_RSC_CHUNKS__ = self.__VINEXT_RSC_CHUNKS__ || [];
82
+ arr.push = function(chunk) {
83
+ Array.prototype.push.call(this, chunk);
84
+ if (!closed) {
85
+ controller.enqueue(encoder.encode(chunk));
86
+ if (self.__VINEXT_RSC_DONE__) {
87
+ closeOnce();
88
+ }
89
+ }
90
+ return this.length;
91
+ };
92
+
93
+ // Safety net: if the server crashes mid-stream and __VINEXT_RSC_DONE__
94
+ // never arrives, close the stream when all server-injected scripts
95
+ // have executed (DOMContentLoaded). Without this, a truncated response
96
+ // leaves the ReadableStream open forever, hanging hydration.
97
+ if (typeof document !== "undefined") {
98
+ if (document.readyState === "loading") {
99
+ document.addEventListener("DOMContentLoaded", closeOnce);
100
+ } else {
101
+ // Document already loaded — close immediately if not already done
102
+ closeOnce();
103
+ }
104
+ }
105
+ }
106
+ });
107
+ }
108
+
109
+ // Register the server action callback — React calls this internally
110
+ // when a "use server" function is invoked from client code.
111
+ setServerCallback(async (id, args) => {
112
+ const temporaryReferences = createTemporaryReferenceSet();
113
+ const body = await encodeReply(args, { temporaryReferences });
114
+
115
+ const fetchResponse = await fetch(toRscUrl(window.location.pathname + window.location.search), {
116
+ method: "POST",
117
+ headers: { "x-rsc-action": id },
118
+ body,
119
+ });
120
+
121
+ // Check for redirect signal from server action that called redirect()
122
+ const actionRedirect = fetchResponse.headers.get("x-action-redirect");
123
+ if (actionRedirect) {
124
+ // External URLs (different origin) need a hard redirect — client-side
125
+ // RSC navigation only works for same-origin paths.
126
+ try {
127
+ const redirectUrl = new URL(actionRedirect, window.location.origin);
128
+ if (redirectUrl.origin !== window.location.origin) {
129
+ window.location.href = actionRedirect;
130
+ return undefined;
131
+ }
132
+ } catch {
133
+ // If URL parsing fails, fall through to client-side navigation
134
+ }
135
+
136
+ // Navigate to the redirect target using client-side navigation
137
+ const redirectType = fetchResponse.headers.get("x-action-redirect-type") || "replace";
138
+ if (redirectType === "push") {
139
+ window.history.pushState(null, "", actionRedirect);
140
+ } else {
141
+ window.history.replaceState(null, "", actionRedirect);
142
+ }
143
+ // Trigger RSC navigation to the redirect target
144
+ if (typeof window.__VINEXT_RSC_NAVIGATE__ === "function") {
145
+ window.__VINEXT_RSC_NAVIGATE__(actionRedirect);
146
+ }
147
+ return undefined;
148
+ }
149
+
150
+ const result = await createFromFetch(Promise.resolve(fetchResponse), { temporaryReferences });
151
+
152
+ // The RSC response for actions contains { root, returnValue }.
153
+ // Re-render the page with the updated tree.
154
+ if (result && typeof result === "object" && "root" in result) {
155
+ reactRoot.render(result.root);
156
+ // Return the action's return value to the caller
157
+ if (result.returnValue) {
158
+ if (!result.returnValue.ok) throw result.returnValue.data;
159
+ return result.returnValue.data;
160
+ }
161
+ return undefined;
162
+ }
163
+
164
+ // Fallback: render the entire result as the tree
165
+ reactRoot.render(result);
166
+ return result;
167
+ });
168
+
169
+ async function main() {
170
+ let rscStream;
171
+
172
+ // Use embedded RSC data for initial hydration if available.
173
+ // This ensures we use the SAME RSC payload that generated the HTML,
174
+ // avoiding hydration mismatches (React error #418).
175
+ //
176
+ // The server embeds RSC chunks progressively as <script> tags that push
177
+ // to self.__VINEXT_RSC_CHUNKS__. When complete, self.__VINEXT_RSC_DONE__
178
+ // is set and self.__VINEXT_RSC_PARAMS__ contains route params.
179
+ // For backwards compat, also check the legacy self.__VINEXT_RSC__ format.
180
+ if (self.__VINEXT_RSC_CHUNKS__ || self.__VINEXT_RSC_DONE__ || self.__VINEXT_RSC__) {
181
+ if (self.__VINEXT_RSC__) {
182
+ // Legacy format: single object with all chunks
183
+ const embedData = self.__VINEXT_RSC__;
184
+ delete self.__VINEXT_RSC__;
185
+ if (embedData.params) {
186
+ setClientParams(embedData.params);
187
+ }
188
+ // Legacy format may include nav context for hydration snapshot consistency.
189
+ if (embedData.nav) {
190
+ setNavigationContext({ pathname: embedData.nav.pathname, searchParams: new URLSearchParams(embedData.nav.searchParams || {}), params: embedData.params || {} });
191
+ }
192
+ rscStream = chunksToReadableStream(embedData.rsc);
193
+ } else {
194
+ // Progressive format: chunks arrive incrementally via script tags.
195
+ // Params are embedded in <head> so they're always available by this point.
196
+ if (self.__VINEXT_RSC_PARAMS__) {
197
+ setClientParams(self.__VINEXT_RSC_PARAMS__);
198
+ }
199
+ // Restore the server navigation context so useSyncExternalStore getServerSnapshot
200
+ // matches what was rendered on the server, preventing hydration mismatches.
201
+ if (self.__VINEXT_RSC_NAV__) {
202
+ const __nav = self.__VINEXT_RSC_NAV__;
203
+ setNavigationContext({ pathname: __nav.pathname, searchParams: new URLSearchParams(__nav.searchParams), params: self.__VINEXT_RSC_PARAMS__ || {} });
204
+ }
205
+ rscStream = createProgressiveRscStream();
206
+ }
207
+ } else {
208
+ // Fallback: fetch fresh RSC (shouldn't happen on initial page load)
209
+ const rscResponse = await fetch(toRscUrl(window.location.pathname + window.location.search));
210
+
211
+ // Hydrate useParams() with route params from the server before React hydration
212
+ const paramsHeader = rscResponse.headers.get("X-Vinext-Params");
213
+ if (paramsHeader) {
214
+ try { setClientParams(JSON.parse(paramsHeader)); } catch (_e) { /* ignore */ }
215
+ }
216
+ // Set nav context from current URL for hydration snapshot consistency.
217
+ setNavigationContext({ pathname: window.location.pathname, searchParams: new URLSearchParams(window.location.search), params: self.__VINEXT_RSC_PARAMS__ || {} });
218
+
219
+ rscStream = rscResponse.body;
220
+ }
221
+
222
+ const root = await createFromReadableStream(rscStream);
223
+
224
+ // Hydrate the document
225
+ // In development, suppress Vite's error overlay for errors caught by React error
226
+ // boundaries. Without this, React re-throws caught errors to the global handler,
227
+ // which triggers Vite's overlay even though the error was handled by an error.tsx.
228
+ // In production, preserve React's default onCaughtError (console.error) so
229
+ // boundary-caught errors remain visible to error monitoring.
230
+ reactRoot = hydrateRoot(document, root, import.meta.env.DEV ? {
231
+ onCaughtError: function() {},
232
+ } : undefined);
233
+
234
+ // Store for client-side navigation
235
+ window.__VINEXT_RSC_ROOT__ = reactRoot;
236
+
237
+ // Client-side navigation handler
238
+ // Checks the prefetch cache (populated by <Link> IntersectionObserver and
239
+ // router.prefetch()) before making a network request. This makes navigation
240
+ // near-instant for prefetched routes.
241
+ window.__VINEXT_RSC_NAVIGATE__ = async function navigateRsc(href, __redirectDepth) {
242
+ if ((__redirectDepth || 0) > 10) {
243
+ console.error("[vinext] Too many RSC redirects — aborting navigation to prevent infinite loop.");
244
+ window.location.href = href;
245
+ return;
246
+ }
247
+ try {
248
+ const url = new URL(href, window.location.origin);
249
+ const rscUrl = toRscUrl(url.pathname + url.search);
250
+
251
+ // Check the in-memory prefetch cache first
252
+ let navResponse;
253
+ const prefetchCache = getPrefetchCache();
254
+ const cached = prefetchCache.get(rscUrl);
255
+ if (cached && (Date.now() - cached.timestamp) < PREFETCH_CACHE_TTL) {
256
+ navResponse = cached.response;
257
+ prefetchCache.delete(rscUrl); // Consume the cached entry (one-time use)
258
+ getPrefetchedUrls().delete(rscUrl); // Allow re-prefetch when link is visible again
259
+ } else if (cached) {
260
+ prefetchCache.delete(rscUrl); // Expired, clean up
261
+ getPrefetchedUrls().delete(rscUrl);
262
+ }
263
+
264
+ // Fallback to network fetch if not in cache
265
+ if (!navResponse) {
266
+ navResponse = await fetch(rscUrl, {
267
+ headers: { Accept: "text/x-component" },
268
+ credentials: "include",
269
+ });
270
+ }
271
+
272
+ // Detect if fetch followed a redirect: compare the final response URL to
273
+ // what we requested. If they differ, the server issued a 3xx — push the
274
+ // canonical destination URL into history before rendering.
275
+ const __finalUrl = new URL(navResponse.url);
276
+ const __requestedUrl = new URL(rscUrl, window.location.origin);
277
+ if (__finalUrl.pathname !== __requestedUrl.pathname) {
278
+ // Strip .rsc suffix from the final URL to get the page path for history.
279
+ // Use replaceState instead of pushState: the caller (navigateImpl) already
280
+ // pushed the pre-redirect URL; replacing it avoids a stale history entry.
281
+ const __destPath = __finalUrl.pathname.replace(/\\.rsc$/, "") + __finalUrl.search;
282
+ window.history.replaceState(null, "", __destPath);
283
+ return window.__VINEXT_RSC_NAVIGATE__(__destPath, (__redirectDepth || 0) + 1);
284
+ }
285
+
286
+ // Update useParams() with route params from the server before re-rendering
287
+ const navParamsHeader = navResponse.headers.get("X-Vinext-Params");
288
+ if (navParamsHeader) {
289
+ try { setClientParams(JSON.parse(navParamsHeader)); } catch (_e) { /* ignore */ }
290
+ } else {
291
+ setClientParams({});
292
+ }
293
+
294
+ const rscPayload = await createFromFetch(Promise.resolve(navResponse));
295
+ // Use flushSync to guarantee React commits the new tree to the DOM
296
+ // synchronously before this function returns. Callers scroll to top
297
+ // after awaiting, so the new content must be painted first.
298
+ flushSync(function () { reactRoot.render(rscPayload); });
299
+ } catch (err) {
300
+ console.error("[vinext] RSC navigation error:", err);
301
+ // Fallback to full page load
302
+ window.location.href = href;
303
+ }
304
+ };
305
+
306
+ // Handle popstate (browser back/forward)
307
+ // Store the navigation promise on a well-known property so that
308
+ // restoreScrollPosition (in navigation.ts) can await it before scrolling.
309
+ // This prevents a flash where the old content is visible at the restored
310
+ // scroll position before the new RSC payload has rendered.
311
+ window.addEventListener("popstate", () => {
312
+ const p = window.__VINEXT_RSC_NAVIGATE__(window.location.href);
313
+ window.__VINEXT_RSC_PENDING__ = p;
314
+ p.finally(() => {
315
+ // Clear once settled so stale promises aren't awaited later
316
+ if (window.__VINEXT_RSC_PENDING__ === p) {
317
+ window.__VINEXT_RSC_PENDING__ = null;
318
+ }
319
+ });
320
+ });
321
+
322
+ // HMR: re-render on server module updates
323
+ if (import.meta.hot) {
324
+ import.meta.hot.on("rsc:update", async () => {
325
+ try {
326
+ const rscPayload = await createFromFetch(
327
+ fetch(toRscUrl(window.location.pathname + window.location.search))
328
+ );
329
+ reactRoot.render(rscPayload);
330
+ } catch (err) {
331
+ console.error("[vinext] RSC HMR error:", err);
332
+ }
333
+ });
334
+ }
335
+ }
336
+
337
+ main();
338
+ `;
339
+ }
340
+ //# sourceMappingURL=app-browser-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-browser-entry.js","sourceRoot":"","sources":["../../src/entries/app-browser-entry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyUR,CAAC;AACF,CAAC","sourcesContent":["/**\n * Generate the virtual browser entry module.\n *\n * This runs in the client (browser). It hydrates the page from the\n * embedded RSC payload and handles client-side navigation by re-fetching\n * RSC streams.\n */\nexport function generateBrowserEntry(): string {\n return `\nimport {\n createFromReadableStream,\n createFromFetch,\n setServerCallback,\n encodeReply,\n createTemporaryReferenceSet,\n} from \"@vitejs/plugin-rsc/browser\";\nimport { hydrateRoot } from \"react-dom/client\";\nimport { flushSync } from \"react-dom\";\nimport { setClientParams, setNavigationContext, toRscUrl, getPrefetchCache, getPrefetchedUrls, PREFETCH_CACHE_TTL } from \"next/navigation\";\n\nlet reactRoot;\n\n/**\n * Convert the embedded RSC chunks back to a ReadableStream.\n * Each chunk is a text string that needs to be encoded back to Uint8Array.\n */\nfunction chunksToReadableStream(chunks) {\n const encoder = new TextEncoder();\n return new ReadableStream({\n start(controller) {\n for (const chunk of chunks) {\n controller.enqueue(encoder.encode(chunk));\n }\n controller.close();\n }\n });\n}\n\n/**\n * Create a ReadableStream from progressively-embedded RSC chunks.\n * The server injects RSC data as <script> tags that push to\n * self.__VINEXT_RSC_CHUNKS__ throughout the HTML stream, and sets\n * self.__VINEXT_RSC_DONE__ = true when complete.\n *\n * Instead of polling with setTimeout, we monkey-patch the array's\n * push() method so new chunks are delivered immediately when the\n * server's <script> tags execute. This eliminates unnecessary\n * wakeups and reduces latency — same pattern Next.js uses with\n * __next_f. The stream closes on DOMContentLoaded (when all\n * server-injected scripts have executed) or when __VINEXT_RSC_DONE__\n * is set, whichever comes first.\n */\nfunction createProgressiveRscStream() {\n const encoder = new TextEncoder();\n return new ReadableStream({\n start(controller) {\n const chunks = self.__VINEXT_RSC_CHUNKS__ || [];\n\n // Deliver any chunks that arrived before this code ran\n // (from <script> tags that executed before the browser entry loaded)\n for (const chunk of chunks) {\n controller.enqueue(encoder.encode(chunk));\n }\n\n // If the stream is already complete, close immediately\n if (self.__VINEXT_RSC_DONE__) {\n controller.close();\n return;\n }\n\n // Monkey-patch push() so future chunks stream in immediately\n // when the server's <script> tags execute\n let closed = false;\n function closeOnce() {\n if (!closed) {\n closed = true;\n controller.close();\n }\n }\n\n const arr = self.__VINEXT_RSC_CHUNKS__ = self.__VINEXT_RSC_CHUNKS__ || [];\n arr.push = function(chunk) {\n Array.prototype.push.call(this, chunk);\n if (!closed) {\n controller.enqueue(encoder.encode(chunk));\n if (self.__VINEXT_RSC_DONE__) {\n closeOnce();\n }\n }\n return this.length;\n };\n\n // Safety net: if the server crashes mid-stream and __VINEXT_RSC_DONE__\n // never arrives, close the stream when all server-injected scripts\n // have executed (DOMContentLoaded). Without this, a truncated response\n // leaves the ReadableStream open forever, hanging hydration.\n if (typeof document !== \"undefined\") {\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", closeOnce);\n } else {\n // Document already loaded — close immediately if not already done\n closeOnce();\n }\n }\n }\n });\n}\n\n// Register the server action callback — React calls this internally\n// when a \"use server\" function is invoked from client code.\nsetServerCallback(async (id, args) => {\n const temporaryReferences = createTemporaryReferenceSet();\n const body = await encodeReply(args, { temporaryReferences });\n\n const fetchResponse = await fetch(toRscUrl(window.location.pathname + window.location.search), {\n method: \"POST\",\n headers: { \"x-rsc-action\": id },\n body,\n });\n\n // Check for redirect signal from server action that called redirect()\n const actionRedirect = fetchResponse.headers.get(\"x-action-redirect\");\n if (actionRedirect) {\n // External URLs (different origin) need a hard redirect — client-side\n // RSC navigation only works for same-origin paths.\n try {\n const redirectUrl = new URL(actionRedirect, window.location.origin);\n if (redirectUrl.origin !== window.location.origin) {\n window.location.href = actionRedirect;\n return undefined;\n }\n } catch {\n // If URL parsing fails, fall through to client-side navigation\n }\n\n // Navigate to the redirect target using client-side navigation\n const redirectType = fetchResponse.headers.get(\"x-action-redirect-type\") || \"replace\";\n if (redirectType === \"push\") {\n window.history.pushState(null, \"\", actionRedirect);\n } else {\n window.history.replaceState(null, \"\", actionRedirect);\n }\n // Trigger RSC navigation to the redirect target\n if (typeof window.__VINEXT_RSC_NAVIGATE__ === \"function\") {\n window.__VINEXT_RSC_NAVIGATE__(actionRedirect);\n }\n return undefined;\n }\n\n const result = await createFromFetch(Promise.resolve(fetchResponse), { temporaryReferences });\n\n // The RSC response for actions contains { root, returnValue }.\n // Re-render the page with the updated tree.\n if (result && typeof result === \"object\" && \"root\" in result) {\n reactRoot.render(result.root);\n // Return the action's return value to the caller\n if (result.returnValue) {\n if (!result.returnValue.ok) throw result.returnValue.data;\n return result.returnValue.data;\n }\n return undefined;\n }\n\n // Fallback: render the entire result as the tree\n reactRoot.render(result);\n return result;\n});\n\nasync function main() {\n let rscStream;\n\n // Use embedded RSC data for initial hydration if available.\n // This ensures we use the SAME RSC payload that generated the HTML,\n // avoiding hydration mismatches (React error #418).\n //\n // The server embeds RSC chunks progressively as <script> tags that push\n // to self.__VINEXT_RSC_CHUNKS__. When complete, self.__VINEXT_RSC_DONE__\n // is set and self.__VINEXT_RSC_PARAMS__ contains route params.\n // For backwards compat, also check the legacy self.__VINEXT_RSC__ format.\n if (self.__VINEXT_RSC_CHUNKS__ || self.__VINEXT_RSC_DONE__ || self.__VINEXT_RSC__) {\n if (self.__VINEXT_RSC__) {\n // Legacy format: single object with all chunks\n const embedData = self.__VINEXT_RSC__;\n delete self.__VINEXT_RSC__;\n if (embedData.params) {\n setClientParams(embedData.params);\n }\n // Legacy format may include nav context for hydration snapshot consistency.\n if (embedData.nav) {\n setNavigationContext({ pathname: embedData.nav.pathname, searchParams: new URLSearchParams(embedData.nav.searchParams || {}), params: embedData.params || {} });\n }\n rscStream = chunksToReadableStream(embedData.rsc);\n } else {\n // Progressive format: chunks arrive incrementally via script tags.\n // Params are embedded in <head> so they're always available by this point.\n if (self.__VINEXT_RSC_PARAMS__) {\n setClientParams(self.__VINEXT_RSC_PARAMS__);\n }\n // Restore the server navigation context so useSyncExternalStore getServerSnapshot\n // matches what was rendered on the server, preventing hydration mismatches.\n if (self.__VINEXT_RSC_NAV__) {\n const __nav = self.__VINEXT_RSC_NAV__;\n setNavigationContext({ pathname: __nav.pathname, searchParams: new URLSearchParams(__nav.searchParams), params: self.__VINEXT_RSC_PARAMS__ || {} });\n }\n rscStream = createProgressiveRscStream();\n }\n } else {\n // Fallback: fetch fresh RSC (shouldn't happen on initial page load)\n const rscResponse = await fetch(toRscUrl(window.location.pathname + window.location.search));\n\n // Hydrate useParams() with route params from the server before React hydration\n const paramsHeader = rscResponse.headers.get(\"X-Vinext-Params\");\n if (paramsHeader) {\n try { setClientParams(JSON.parse(paramsHeader)); } catch (_e) { /* ignore */ }\n }\n // Set nav context from current URL for hydration snapshot consistency.\n setNavigationContext({ pathname: window.location.pathname, searchParams: new URLSearchParams(window.location.search), params: self.__VINEXT_RSC_PARAMS__ || {} });\n\n rscStream = rscResponse.body;\n }\n\n const root = await createFromReadableStream(rscStream);\n\n // Hydrate the document\n // In development, suppress Vite's error overlay for errors caught by React error\n // boundaries. Without this, React re-throws caught errors to the global handler,\n // which triggers Vite's overlay even though the error was handled by an error.tsx.\n // In production, preserve React's default onCaughtError (console.error) so\n // boundary-caught errors remain visible to error monitoring.\n reactRoot = hydrateRoot(document, root, import.meta.env.DEV ? {\n onCaughtError: function() {},\n } : undefined);\n\n // Store for client-side navigation\n window.__VINEXT_RSC_ROOT__ = reactRoot;\n\n // Client-side navigation handler\n // Checks the prefetch cache (populated by <Link> IntersectionObserver and\n // router.prefetch()) before making a network request. This makes navigation\n // near-instant for prefetched routes.\n window.__VINEXT_RSC_NAVIGATE__ = async function navigateRsc(href, __redirectDepth) {\n if ((__redirectDepth || 0) > 10) {\n console.error(\"[vinext] Too many RSC redirects — aborting navigation to prevent infinite loop.\");\n window.location.href = href;\n return;\n }\n try {\n const url = new URL(href, window.location.origin);\n const rscUrl = toRscUrl(url.pathname + url.search);\n\n // Check the in-memory prefetch cache first\n let navResponse;\n const prefetchCache = getPrefetchCache();\n const cached = prefetchCache.get(rscUrl);\n if (cached && (Date.now() - cached.timestamp) < PREFETCH_CACHE_TTL) {\n navResponse = cached.response;\n prefetchCache.delete(rscUrl); // Consume the cached entry (one-time use)\n getPrefetchedUrls().delete(rscUrl); // Allow re-prefetch when link is visible again\n } else if (cached) {\n prefetchCache.delete(rscUrl); // Expired, clean up\n getPrefetchedUrls().delete(rscUrl);\n }\n\n // Fallback to network fetch if not in cache\n if (!navResponse) {\n navResponse = await fetch(rscUrl, {\n headers: { Accept: \"text/x-component\" },\n credentials: \"include\",\n });\n }\n\n // Detect if fetch followed a redirect: compare the final response URL to\n // what we requested. If they differ, the server issued a 3xx — push the\n // canonical destination URL into history before rendering.\n const __finalUrl = new URL(navResponse.url);\n const __requestedUrl = new URL(rscUrl, window.location.origin);\n if (__finalUrl.pathname !== __requestedUrl.pathname) {\n // Strip .rsc suffix from the final URL to get the page path for history.\n // Use replaceState instead of pushState: the caller (navigateImpl) already\n // pushed the pre-redirect URL; replacing it avoids a stale history entry.\n const __destPath = __finalUrl.pathname.replace(/\\\\.rsc$/, \"\") + __finalUrl.search;\n window.history.replaceState(null, \"\", __destPath);\n return window.__VINEXT_RSC_NAVIGATE__(__destPath, (__redirectDepth || 0) + 1);\n }\n\n // Update useParams() with route params from the server before re-rendering\n const navParamsHeader = navResponse.headers.get(\"X-Vinext-Params\");\n if (navParamsHeader) {\n try { setClientParams(JSON.parse(navParamsHeader)); } catch (_e) { /* ignore */ }\n } else {\n setClientParams({});\n }\n\n const rscPayload = await createFromFetch(Promise.resolve(navResponse));\n // Use flushSync to guarantee React commits the new tree to the DOM\n // synchronously before this function returns. Callers scroll to top\n // after awaiting, so the new content must be painted first.\n flushSync(function () { reactRoot.render(rscPayload); });\n } catch (err) {\n console.error(\"[vinext] RSC navigation error:\", err);\n // Fallback to full page load\n window.location.href = href;\n }\n };\n\n // Handle popstate (browser back/forward)\n // Store the navigation promise on a well-known property so that\n // restoreScrollPosition (in navigation.ts) can await it before scrolling.\n // This prevents a flash where the old content is visible at the restored\n // scroll position before the new RSC payload has rendered.\n window.addEventListener(\"popstate\", () => {\n const p = window.__VINEXT_RSC_NAVIGATE__(window.location.href);\n window.__VINEXT_RSC_PENDING__ = p;\n p.finally(() => {\n // Clear once settled so stale promises aren't awaited later\n if (window.__VINEXT_RSC_PENDING__ === p) {\n window.__VINEXT_RSC_PENDING__ = null;\n }\n });\n });\n\n // HMR: re-render on server module updates\n if (import.meta.hot) {\n import.meta.hot.on(\"rsc:update\", async () => {\n try {\n const rscPayload = await createFromFetch(\n fetch(toRscUrl(window.location.pathname + window.location.search))\n );\n reactRoot.render(rscPayload);\n } catch (err) {\n console.error(\"[vinext] RSC HMR error:\", err);\n }\n });\n }\n}\n\nmain();\n`;\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { AppRoute } from "../routing/app-router.js";
2
- import type { MetadataFileRoute } from "./metadata-routes.js";
2
+ import type { MetadataFileRoute } from "../server/metadata-routes.js";
3
3
  import type { NextRedirect, NextRewrite, NextHeader } from "../config/next-config.js";
4
4
  /**
5
5
  * Resolved config options relevant to App Router request handling.
@@ -17,6 +17,8 @@ export interface AppRouterConfig {
17
17
  allowedOrigins?: string[];
18
18
  /** Extra origins allowed for dev server access (from allowedDevOrigins). */
19
19
  allowedDevOrigins?: string[];
20
+ /** Body size limit for server actions in bytes (from experimental.serverActions.bodySizeLimit). */
21
+ bodySizeLimit?: number;
20
22
  }
21
23
  /**
22
24
  * Generate the virtual RSC entry module.
@@ -26,19 +28,4 @@ export interface AppRouterConfig {
26
28
  * nested layout + page tree, and renders it to an RSC stream.
27
29
  */
28
30
  export declare function generateRscEntry(appDir: string, routes: AppRoute[], middlewarePath?: string | null, metadataRoutes?: MetadataFileRoute[], globalErrorPath?: string | null, basePath?: string, trailingSlash?: boolean, config?: AppRouterConfig, instrumentationPath?: string | null): string;
29
- /**
30
- * Generate the virtual SSR entry module.
31
- *
32
- * This runs in the `ssr` Vite environment. It receives an RSC stream,
33
- * deserializes it to a React tree, and renders to HTML.
34
- */
35
- export declare function generateSsrEntry(): string;
36
- /**
37
- * Generate the virtual browser entry module.
38
- *
39
- * This runs in the client (browser). It hydrates the page from the
40
- * embedded RSC payload and handles client-side navigation by re-fetching
41
- * RSC streams.
42
- */
43
- export declare function generateBrowserEntry(): string;
44
- //# sourceMappingURL=app-dev-server.d.ts.map
31
+ //# sourceMappingURL=app-rsc-entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-rsc-entry.d.ts","sourceRoot":"","sources":["../../src/entries/app-rsc-entry.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAWtF;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE;QACT,WAAW,EAAE,WAAW,EAAE,CAAC;QAC3B,UAAU,EAAE,WAAW,EAAE,CAAC;QAC1B,QAAQ,EAAE,WAAW,EAAE,CAAC;KACzB,CAAC;IACF,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,4GAA4G;IAC5G,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,mGAAmG;IACnG,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,QAAQ,EAAE,EAClB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,EAC9B,cAAc,CAAC,EAAE,iBAAiB,EAAE,EACpC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,EAC/B,QAAQ,CAAC,EAAE,MAAM,EACjB,aAAa,CAAC,EAAE,OAAO,EACvB,MAAM,CAAC,EAAE,eAAe,EACxB,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,GAClC,MAAM,CA6zER"}