vinext 1.4.2 → 1.4.4

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 (116) hide show
  1. package/README.md +4 -1
  2. package/dist/build/prerender-paths.d.ts +11 -1
  3. package/dist/build/prerender-paths.js +41 -11
  4. package/dist/cli.js +1 -1
  5. package/dist/client/dev-error-overlay.d.ts +2 -1
  6. package/dist/client/dev-error-overlay.js +8 -3
  7. package/dist/config/config-matchers.js +6 -2
  8. package/dist/config/next-config.d.ts +9 -0
  9. package/dist/entries/app-rsc-entry.js +24 -12
  10. package/dist/entries/pages-server-entry.js +17 -4
  11. package/dist/index.js +48 -39
  12. package/dist/init-platform.js +2 -2
  13. package/dist/init.js +42 -5
  14. package/dist/server/api-handler.js +8 -5
  15. package/dist/server/app-browser-entry.js +26 -47
  16. package/dist/server/app-browser-error.d.ts +1 -1
  17. package/dist/server/app-browser-error.js +13 -4
  18. package/dist/server/app-browser-navigation-controller.js +2 -2
  19. package/dist/server/app-browser-server-action-client.js +2 -2
  20. package/dist/server/app-browser-state.js +2 -2
  21. package/dist/server/app-browser-supplemental-refresh.d.ts +13 -0
  22. package/dist/server/app-browser-supplemental-refresh.js +36 -4
  23. package/dist/server/app-browser-visible-commit.js +1 -1
  24. package/dist/server/app-history-state.js +1 -1
  25. package/dist/server/app-middleware.d.ts +2 -0
  26. package/dist/server/app-middleware.js +1 -0
  27. package/dist/server/app-page-boundary-render.js +4 -2
  28. package/dist/server/app-page-dispatch.d.ts +1 -0
  29. package/dist/server/app-page-dispatch.js +5 -1
  30. package/dist/server/app-page-element-builder.d.ts +2 -0
  31. package/dist/server/app-page-element-builder.js +6 -5
  32. package/dist/server/app-page-render.js +1 -1
  33. package/dist/server/app-page-route-wiring.d.ts +7 -5
  34. package/dist/server/app-page-route-wiring.js +11 -9
  35. package/dist/server/app-pages-bridge.d.ts +6 -0
  36. package/dist/server/app-pages-bridge.js +8 -3
  37. package/dist/server/app-request-context.js +1 -1
  38. package/dist/server/app-request-stage-independent-entry.js +2 -3
  39. package/dist/server/app-route-handler-dispatch.js +1 -1
  40. package/dist/server/app-route-handler-execution.js +1 -1
  41. package/dist/server/app-route-handler-response.d.ts +1 -0
  42. package/dist/server/app-route-handler-response.js +27 -2
  43. package/dist/server/app-router-entry.js +2 -3
  44. package/dist/server/app-rsc-cache-busting.d.ts +8 -0
  45. package/dist/server/app-rsc-cache-busting.js +13 -1
  46. package/dist/server/app-rsc-combined-handler.js +2 -1
  47. package/dist/server/app-rsc-handler.d.ts +4 -0
  48. package/dist/server/app-rsc-handler.js +41 -24
  49. package/dist/server/app-rsc-response-finalizer.d.ts +15 -3
  50. package/dist/server/app-rsc-response-finalizer.js +51 -7
  51. package/dist/server/app-rsc-response-stage.js +1 -1
  52. package/dist/server/app-server-action-execution.js +1 -1
  53. package/dist/server/app-ssr-entry.js +18 -10
  54. package/dist/server/config-headers.d.ts +7 -0
  55. package/dist/server/config-headers.js +20 -1
  56. package/dist/server/edge-api-runtime.d.ts +6 -2
  57. package/dist/server/edge-api-runtime.js +21 -4
  58. package/dist/server/image-animation.d.ts +9 -0
  59. package/dist/server/image-animation.js +118 -0
  60. package/dist/server/image-optimization.d.ts +25 -5
  61. package/dist/server/image-optimization.js +305 -21
  62. package/dist/server/image-source-reader.d.ts +34 -0
  63. package/dist/server/image-source-reader.js +126 -0
  64. package/dist/server/metadata-route-response.d.ts +6 -1
  65. package/dist/server/metadata-route-response.js +24 -6
  66. package/dist/server/middleware-matcher.d.ts +1 -0
  67. package/dist/server/middleware-matcher.js +1 -1
  68. package/dist/server/middleware-runtime.d.ts +5 -0
  69. package/dist/server/middleware-runtime.js +48 -15
  70. package/dist/server/middleware.d.ts +1 -1
  71. package/dist/server/middleware.js +2 -1
  72. package/dist/server/navigation-planner.js +14 -6
  73. package/dist/server/nitro-request-target-plugin.d.ts +17 -0
  74. package/dist/server/nitro-request-target-plugin.js +22 -0
  75. package/dist/server/open-redirect.d.ts +2 -1
  76. package/dist/server/open-redirect.js +2 -1
  77. package/dist/server/pages-client-assets.d.ts +1 -0
  78. package/dist/server/pages-i18n.d.ts +7 -0
  79. package/dist/server/pages-i18n.js +14 -1
  80. package/dist/server/pages-node-compat.js +7 -3
  81. package/dist/server/pages-page-handler.js +4 -4
  82. package/dist/server/pages-request-pipeline.d.ts +9 -0
  83. package/dist/server/pages-request-pipeline.js +29 -10
  84. package/dist/server/pages-request-stage-entry.js +7 -2
  85. package/dist/server/pages-router-entry.js +3 -0
  86. package/dist/server/prod-server.d.ts +1 -1
  87. package/dist/server/prod-server.js +48 -54
  88. package/dist/server/remove-request-headers.d.ts +16 -0
  89. package/dist/server/remove-request-headers.js +23 -0
  90. package/dist/server/srvx-middleware-origin.d.ts +13 -0
  91. package/dist/server/srvx-middleware-origin.js +19 -0
  92. package/dist/server/srvx-request-target.d.ts +12 -0
  93. package/dist/server/srvx-request-target.js +20 -0
  94. package/dist/server/static-file-cache.js +10 -22
  95. package/dist/server/worker-image-optimization.d.ts +8 -1
  96. package/dist/server/worker-image-optimization.js +25 -6
  97. package/dist/server/worker-revalidation-context.js +1 -0
  98. package/dist/shims/cache-runtime.js +1 -1
  99. package/dist/shims/cache.js +6 -3
  100. package/dist/shims/client-page-root.js +2 -2
  101. package/dist/shims/error-boundary.d.ts +6 -2
  102. package/dist/shims/error-boundary.js +6 -4
  103. package/dist/shims/internal/navigation-untracked.js +1 -1
  104. package/dist/shims/metadata.js +1 -1
  105. package/dist/shims/navigation-server.js +1 -1
  106. package/dist/shims/navigation.js +4 -4
  107. package/dist/shims/navigation.react-server.js +3 -3
  108. package/dist/shims/request-context.d.ts +3 -1
  109. package/dist/shims/root-params.js +16 -2
  110. package/dist/shims/slot.js +3 -3
  111. package/dist/shims/url-utils.js +6 -1
  112. package/dist/utils/client-entry-manifest.d.ts +5 -0
  113. package/dist/utils/client-entry-manifest.js +1 -0
  114. package/dist/utils/client-runtime-metadata.d.ts +5 -0
  115. package/dist/utils/client-runtime-metadata.js +33 -7
  116. package/package.json +1 -1
@@ -2,7 +2,7 @@ import { VINEXT_PRERENDER_SPECIAL_ERROR_HEADER } from "./headers.js";
2
2
  import { readStaticFileSignal, readStaticFileSignalRequestHeaders } from "./static-file-signal.js";
3
3
  import { attachRequestCfMetadata, cloneRequestWithUrl } from "./request-pipeline.js";
4
4
  import { isNextStaticPath } from "../utils/asset-prefix.js";
5
- import { handleConfiguredImageOptimization, invalidImageSourceResponse, isImageSourceBodyStatus } from "./image-optimization.js";
5
+ import { handleConfiguredImageOptimization, invalidImageSourceResponse, isImageSourceBodyStatus, markImageBuildAsset } from "./image-optimization.js";
6
6
  import { createStaticAssetRequest, mergeHeaders } from "./worker-utils.js";
7
7
  import { resolveClientBuildFile } from "./client-build-files.js";
8
8
  //#region src/server/worker-image-optimization.ts
@@ -53,28 +53,47 @@ async function fetchWorkerImageSource(imageUrl, request, options) {
53
53
  } catch {
54
54
  return invalidImageSourceResponse();
55
55
  }
56
- const buildAssetPath = resolveNextStaticFile(decodedPathname, options) ?? resolveClientBuildFile(sourceUrl.pathname, options.basePath, options.clientBuildFiles);
57
- if (buildAssetPath !== null) return options.assets.fetch(new Request(new URL(buildAssetPath, request.url)));
56
+ const nextStaticFile = resolveNextStaticFile(decodedPathname, options);
57
+ if (nextStaticFile !== null) return markImageBuildAsset(await options.assets.fetch(new Request(new URL(nextStaticFile, request.url))));
58
+ const clientBuildFile = resolveClientBuildFile(sourceUrl.pathname, options.basePath, options.clientBuildFiles);
59
+ if (clientBuildFile !== null) return options.assets.fetch(new Request(new URL(clientBuildFile, request.url)));
58
60
  const sourceResponse = await options.fetchPublicFile(attachRequestCfMetadata(new Request(sourceUrl, { signal: request.signal }), request));
59
61
  if (sourceResponse && isImageSourceBodyStatus(sourceResponse.status)) return sourceResponse;
60
62
  if (sourceResponse) cancelResponseBody(sourceResponse);
61
63
  return invalidImageSourceResponse();
62
64
  }
63
65
  /**
66
+ * Give a public-file source the Cache-Control middleware or `headers()` staged
67
+ * for it, which a direct request receives in place of the asset default and
68
+ * Next.js' internal source fetch reads.
69
+ */
70
+ function withStagedCacheControl(asset, cacheControl) {
71
+ if (cacheControl === null) return asset;
72
+ const headers = new Headers(asset.headers);
73
+ headers.set("Cache-Control", cacheControl);
74
+ return new Response(asset.body, {
75
+ status: asset.status,
76
+ statusText: asset.statusText,
77
+ headers
78
+ });
79
+ }
80
+ /**
64
81
  * Return the public file a framework static-file signal selects, provided the
65
82
  * pipeline answered with a body-bearing 2xx outcome. The file is fetched with middleware's
66
- * request header overrides, as resolveStaticAssetSignal fetches it.
83
+ * request header overrides, as resolveStaticAssetSignal fetches it, and keeps
84
+ * the Cache-Control the pipeline staged.
67
85
  */
68
86
  function fetchSignalledPublicFile(response, sourceRequest, assets) {
69
87
  const signal = isImageSourceBodyStatus(response.status) ? readStaticFileSignal(response) : null;
70
88
  const requestHeaders = readStaticFileSignalRequestHeaders(response);
89
+ const stagedCacheControl = response.headers.get("Cache-Control");
71
90
  cancelResponseBody(response);
72
91
  if (signal === null) return null;
73
92
  let assetPath = signal;
74
93
  try {
75
94
  assetPath = decodeURIComponent(signal);
76
95
  } catch {}
77
- return Promise.resolve(assets.fetch(createStaticAssetRequest(assetPath, sourceRequest, requestHeaders)));
96
+ return Promise.resolve(assets.fetch(createStaticAssetRequest(assetPath, sourceRequest, requestHeaders))).then((asset) => withStagedCacheControl(asset, stagedCacheControl));
78
97
  }
79
98
  /**
80
99
  * Serve the App Router image endpoint once the request handler admitted it
@@ -111,4 +130,4 @@ async function serveWorkerImageOptimization(request, signal, allowedWidths, imag
111
130
  });
112
131
  }
113
132
  //#endregion
114
- export { fetchSignalledPublicFile, fetchWorkerImageSource, serveWorkerImageOptimization };
133
+ export { fetchSignalledPublicFile, fetchWorkerImageSource, serveWorkerImageOptimization, withStagedCacheControl };
@@ -12,6 +12,7 @@ function deriveExecutionContext(base, dispatchPagesRevalidate, isInternalPagesRe
12
12
  ...base?.cache === void 0 ? {} : { cache: base.cache },
13
13
  ...base?.assets === void 0 ? {} : { assets: base.assets },
14
14
  ...base?.trustedRevalidateOrigin === void 0 ? {} : { trustedRevalidateOrigin: base.trustedRevalidateOrigin },
15
+ ...base?.middlewareOrigin === void 0 ? {} : { middlewareOrigin: base.middlewareOrigin },
15
16
  dispatchPagesRevalidate,
16
17
  isInternalPagesRevalidation
17
18
  };
@@ -14,9 +14,9 @@ import { isDraftModeEnabled } from "./headers.js";
14
14
  import { _registerCacheContextAccessor } from "./internal/cache-context-accessor.js";
15
15
  import { _hasPendingRevalidatedTag, _isOnDemandRevalidate, _setRequestScopedCacheLife, cacheLifeProfiles, runWithDetachedCacheObservations, shouldServeStaleUnstableCacheEntry } from "./cache-request-state.js";
16
16
  import "./fetch-cache.js";
17
- import { hasUseCacheLayoutMarker, hasUseCachePageMarker, isUseCacheFunctionReference, withoutUseCacheSegmentMarker } from "./internal/app-page-props-cache-key.js";
18
17
  import { isWellKnownProperty } from "./internal/thenable-well-known-properties.js";
19
18
  import { getFallbackParamsSnapshot, restoreThenableParams } from "./thenable-params.js";
19
+ import { hasUseCacheLayoutMarker, hasUseCachePageMarker, isUseCacheFunctionReference, withoutUseCacheSegmentMarker } from "./internal/app-page-props-cache-key.js";
20
20
  import { getCurrentRootParams } from "./root-params.js";
21
21
  import { flightArgumentsKey, restoreFlightReply, snapshotFlightReply } from "./cache-flight-arguments.js";
22
22
  import { cache } from "react";
@@ -334,7 +334,7 @@ function unstable_cacheTag(...tags) {
334
334
  }
335
335
  return cacheTag(...tags);
336
336
  }
337
- const UNSTABLE_CACHE_KEY_PREFIX = "unstable_cache:v3";
337
+ const UNSTABLE_CACHE_KEY_PREFIX = "unstable_cache:v4";
338
338
  /**
339
339
  * AsyncLocalStorage to track whether we're inside an unstable_cache() callback.
340
340
  * Stored on globalThis via Symbol so headers.ts can detect the scope without
@@ -403,9 +403,12 @@ function scheduleUnstableCacheBackgroundRevalidation(cacheKey, refresh) {
403
403
  pending.set(cacheKey, trackedRevalidation);
404
404
  waitUntilUnstableCacheRevalidation(trackedRevalidation);
405
405
  }
406
+ function runUnstableCacheCallback(fn, args) {
407
+ return _unstableCacheAls.run(true, () => _unstableCacheCallbackAls.run({ useCacheContext: _useCacheContextAls.getStore() }, () => fn(...args)));
408
+ }
406
409
  async function refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds, staleEntry, deferWrite) {
407
410
  const argsUnchanged = serializeArgsOrNull(args) === keyedArgs;
408
- const result = await _unstableCacheAls.run(true, () => _unstableCacheCallbackAls.run({ useCacheContext: _useCacheContextAls.getStore() }, () => fn(...args)));
411
+ const result = await runUnstableCacheCallback(fn, args);
409
412
  if (!argsUnchanged) return result;
410
413
  const cacheValue = {
411
414
  kind: "FETCH",
@@ -478,7 +481,7 @@ function unstable_cache(fn, keyParts, options) {
478
481
  }
479
482
  }
480
483
  }
481
- if (isDraftMode) return await _unstableCacheAls.run(true, () => fn(...args));
484
+ if (isDraftMode) return await runUnstableCacheCallback(fn, args);
482
485
  return await refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds, staleEntry, !staleEntry && isInsideWorkStoreScope());
483
486
  };
484
487
  return cachedFn;
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { searchParamsToRecord } from "../utils/query.js";
3
+ import { isOwnPropertyCheck, isWellKnownProperty } from "./internal/thenable-well-known-properties.js";
3
4
  import { getNavigationContext } from "./navigation-context-state.js";
4
- import { getClientNavigationRenderContext, useSearchParams } from "./navigation.js";
5
5
  import "./navigation-server.js";
6
+ import { getClientNavigationRenderContext, useSearchParams } from "./navigation.js";
6
7
  import { RenderedSearchContext } from "./slot.js";
7
- import { isOwnPropertyCheck, isWellKnownProperty } from "./internal/thenable-well-known-properties.js";
8
8
  import { createElement, use, useMemo } from "react";
9
9
  //#region src/shims/client-page-root.tsx
10
10
  /**
@@ -6,6 +6,8 @@ export type ErrorBoundaryProps = {
6
6
  reset: () => void;
7
7
  }>;
8
8
  children: React.ReactNode;
9
+ /** Whether this boundary renders global-error, at the root of the page. */
10
+ isGlobalErrorBoundary?: boolean;
9
11
  resetKey?: string | null;
10
12
  };
11
13
  export type SerializedBoundaryError = {
@@ -38,6 +40,7 @@ type RedirectBoundaryProps = {
38
40
  type ErrorBoundaryInnerProps = {
39
41
  isImplicitRootErrorBoundary?: boolean;
40
42
  pathname: string | null;
43
+ wrapsGlobalErrorBoundary?: boolean;
41
44
  } & ErrorBoundaryProps;
42
45
  export type ErrorBoundaryState = {
43
46
  error: CapturedError | null;
@@ -66,13 +69,14 @@ export declare class ErrorBoundaryInner extends React.Component<ErrorBoundaryInn
66
69
  reset: () => void;
67
70
  render(): string | number | bigint | boolean | React.JSX.Element | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | Iterable<React.ReactNode> | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | React.ReactPortal | null | undefined> | null | undefined;
68
71
  }
69
- export declare function ErrorBoundary({ fallback, children, resetKey }: ErrorBoundaryProps): React.JSX.Element;
70
- export declare function GlobalErrorBoundary({ fallback, children }: {
72
+ export declare function ErrorBoundary({ fallback, children, isGlobalErrorBoundary, resetKey }: ErrorBoundaryProps): React.JSX.Element;
73
+ export declare function GlobalErrorBoundary({ fallback, children, wrapsGlobalErrorBoundary }: {
71
74
  fallback: React.ComponentType<{
72
75
  error: unknown;
73
76
  reset: () => void;
74
77
  }>;
75
78
  children: React.ReactNode;
79
+ wrapsGlobalErrorBoundary?: boolean;
76
80
  }): React.JSX.Element;
77
81
  type NotFoundBoundaryProps = {
78
82
  fallback: React.ReactNode;
@@ -2,11 +2,11 @@
2
2
  import { isNavigationSignalError } from "../utils/navigation-signal.js";
3
3
  import { handleAppNavigationFailure } from "../client/app-nav-failure-handler.js";
4
4
  import { VINEXT_DEV_ERROR_RECOVERY_EVENT } from "../utils/dev-error-recovery-event.js";
5
+ import { consumeCaughtNotFoundBoundary, useCaughtNotFoundBoundary } from "./caught-not-found-boundaries.js";
5
6
  import { decodeRedirectError, forbidden, isRedirectError, notFound, unauthorized } from "./navigation-errors.js";
6
7
  import "./navigation-server.js";
7
- import { isRedirectToCurrentUrl, useErrorBoundaryPathname, useErrorBoundaryRouter } from "./error-boundary-navigation.js";
8
8
  import DefaultGlobalError from "./default-global-error.js";
9
- import { consumeCaughtNotFoundBoundary, useCaughtNotFoundBoundary } from "./caught-not-found-boundaries.js";
9
+ import { isRedirectToCurrentUrl, useErrorBoundaryPathname, useErrorBoundaryRouter } from "./error-boundary-navigation.js";
10
10
  import React from "react";
11
11
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
12
12
  //#region src/shims/error-boundary.tsx
@@ -174,21 +174,23 @@ var ErrorBoundaryInner = class extends React.Component {
174
174
  return this.props.children;
175
175
  }
176
176
  };
177
- function ErrorBoundary({ fallback, children, resetKey }) {
177
+ function ErrorBoundary({ fallback, children, isGlobalErrorBoundary, resetKey }) {
178
178
  const pathname = useErrorBoundaryPathname();
179
179
  return /* @__PURE__ */ jsx(ErrorBoundaryInner, {
180
180
  pathname,
181
181
  resetKey,
182
182
  fallback,
183
+ isGlobalErrorBoundary,
183
184
  children
184
185
  });
185
186
  }
186
- function GlobalErrorBoundary({ fallback, children }) {
187
+ function GlobalErrorBoundary({ fallback, children, wrapsGlobalErrorBoundary }) {
187
188
  const pathname = useErrorBoundaryPathname();
188
189
  return /* @__PURE__ */ jsx(ErrorBoundaryInner, {
189
190
  pathname,
190
191
  fallback,
191
192
  isImplicitRootErrorBoundary: fallback === DefaultGlobalError,
193
+ wrapsGlobalErrorBoundary,
192
194
  children
193
195
  });
194
196
  }
@@ -1,5 +1,5 @@
1
- import { getPagesNavigationContext } from "./pages-router-accessor.js";
2
1
  import { getNavigationContext } from "../navigation-context-state.js";
2
+ import { getPagesNavigationContext } from "./pages-router-accessor.js";
3
3
  import { getClientNavigationState, useClientNavigationRenderSnapshot } from "../navigation.js";
4
4
  //#region src/shims/internal/navigation-untracked.ts
5
5
  /**
@@ -1,6 +1,6 @@
1
1
  import { isAbsoluteOrProtocolRelativeUrl } from "./url-utils.js";
2
- import { withUseCacheLayoutMarker, withUseCachePageMarker } from "./internal/app-page-props-cache-key.js";
3
2
  import { makeThenableParams } from "./thenable-params.js";
3
+ import { withUseCacheLayoutMarker, withUseCachePageMarker } from "./internal/app-page-props-cache-key.js";
4
4
  import React from "react";
5
5
  import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
6
6
  //#region src/shims/metadata.tsx
@@ -1,3 +1,3 @@
1
- import { BailoutToCSRError, DynamicServerError, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, decodeRedirectError, forbidden, getAccessFallbackHTTPStatus, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, unauthorized, unstable_rethrow } from "./navigation-errors.js";
2
1
  import { GLOBAL_ACCESSORS_KEY, ServerInsertedHTMLContext, _registerStateAccessors, clearClientHydrationContext, clearServerInsertedHTML, flushServerInsertedHTML, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getLayoutSegmentContext, getNavigationContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "./navigation-context-state.js";
2
+ import { BailoutToCSRError, DynamicServerError, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, decodeRedirectError, forbidden, getAccessFallbackHTTPStatus, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, unauthorized, unstable_rethrow } from "./navigation-errors.js";
3
3
  export { BailoutToCSRError, DynamicServerError, GLOBAL_ACCESSORS_KEY, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, ServerInsertedHTMLContext, _registerStateAccessors, clearClientHydrationContext, clearServerInsertedHTML, decodeRedirectError, flushServerInsertedHTML, forbidden, getAccessFallbackHTTPStatus, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getLayoutSegmentContext, getNavigationContext, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext, unauthorized, unstable_rethrow };
@@ -15,7 +15,9 @@ import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscR
15
15
  import { clearAppNavigationFailureTarget, stageAppNavigationFailureTarget } from "../client/app-nav-failure-handler.js";
16
16
  import { notifyAppRouterTransitionStart } from "../client/instrumentation-client-state.js";
17
17
  import { getNavigationRuntime, hasAppNavigationRuntime, loadNavigationRuntimeRouteManifest } from "../client/navigation-runtime.js";
18
- import { UnrecognizedActionError, unstable_isUnrecognizedActionError } from "./unrecognized-action-error.js";
18
+ import { beginAppRouterScrollIntent, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent, isLatestAppRouterScrollIntent } from "./app-router-scroll-state.js";
19
+ import { GLOBAL_ACCESSORS_KEY, ServerInsertedHTMLContext, _registerStateAccessors, clearClientHydrationContext, clearServerInsertedHTML, flushServerInsertedHTML, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getLayoutSegmentContext, getNavigationContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "./navigation-context-state.js";
20
+ import { BailoutToCSRError, DynamicServerError, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, decodeRedirectError, forbidden, getAccessFallbackHTTPStatus, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, unauthorized, unstable_rethrow } from "./navigation-errors.js";
19
21
  import { AppElementsWire } from "../server/app-elements-wire.js";
20
22
  import "../server/app-elements.js";
21
23
  import { PUBLIC_INITIAL_BFCACHE_ID } from "../server/app-bfcache-id.js";
@@ -25,11 +27,9 @@ import { hasPendingAppRouterPageRedirect } from "../server/app-browser-mpa-navig
25
27
  import { extractRscCompletionMetadata } from "../server/rsc-completion-metadata.js";
26
28
  import { navigationPlanner } from "../server/navigation-planner.js";
27
29
  import { ReadonlyURLSearchParams } from "./readonly-url-search-params.js";
28
- import { BailoutToCSRError, DynamicServerError, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, decodeRedirectError, forbidden, getAccessFallbackHTTPStatus, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, unauthorized, unstable_rethrow } from "./navigation-errors.js";
29
30
  import { getPagesNavigationContext } from "./internal/pages-router-accessor.js";
30
- import { beginAppRouterScrollIntent, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent, isLatestAppRouterScrollIntent } from "./app-router-scroll-state.js";
31
- import { GLOBAL_ACCESSORS_KEY, ServerInsertedHTMLContext, _registerStateAccessors, clearClientHydrationContext, clearServerInsertedHTML, flushServerInsertedHTML, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getLayoutSegmentContext, getNavigationContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "./navigation-context-state.js";
32
31
  import { cancelAppPrefetchFetch, promoteAppPrefetchFetch, releaseAppPrefetchFetchSlot, scheduleAppPrefetchFetch } from "./internal/app-prefetch-fetch-queue.js";
32
+ import { UnrecognizedActionError, unstable_isUnrecognizedActionError } from "./unrecognized-action-error.js";
33
33
  import * as React$1 from "react";
34
34
  //#region src/shims/navigation.ts
35
35
  /**
@@ -1,8 +1,8 @@
1
- import { ReadonlyURLSearchParams } from "./readonly-url-search-params.js";
2
- import { BailoutToCSRError, DynamicServerError, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, forbidden, getAccessFallbackHTTPStatus, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, unauthorized, unstable_rethrow } from "./navigation-errors.js";
1
+ import { throwClientHookError } from "./client-hook-error.js";
3
2
  import { GLOBAL_ACCESSORS_KEY, ServerInsertedHTMLContext, _registerStateAccessors, clearServerInsertedHTML, flushServerInsertedHTML, getLayoutSegmentContext, getNavigationContext, renderServerInsertedHTML, setNavigationContext } from "./navigation-context-state.js";
3
+ import { BailoutToCSRError, DynamicServerError, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, forbidden, getAccessFallbackHTTPStatus, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, unauthorized, unstable_rethrow } from "./navigation-errors.js";
4
4
  import "./navigation-server.js";
5
- import { throwClientHookError } from "./client-hook-error.js";
5
+ import { ReadonlyURLSearchParams } from "./readonly-url-search-params.js";
6
6
  //#region src/shims/navigation.react-server.ts
7
7
  function usePathname() {
8
8
  throwClientHookError("usePathname()");
@@ -46,7 +46,9 @@ export type ExecutionContextLike = {
46
46
  requestSignal?: AbortSignal;
47
47
  /**
48
48
  * Server-owned origin middleware runs against. Set by the Node production
49
- * server, whose request URL comes from the client's Host header. Without it
49
+ * server and by entries that srvx serves on Node (Nitro's Node presets,
50
+ * @vitejs/plugin-rsc's dev and preview handlers), whose request URL comes
51
+ * from the client's Host header. Without it
50
52
  * middleware uses the request URL's origin, which Workers can trust because
51
53
  * the platform routes by hostname.
52
54
  */
@@ -1,6 +1,6 @@
1
1
  import { getOrCreateAls } from "./internal/als-registry.js";
2
2
  import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
3
- import { _recordUseCacheRootParamRead } from "./internal/cache-context-accessor.js";
3
+ import { _recordUseCacheRootParamRead, getRegisteredCacheContext } from "./internal/cache-context-accessor.js";
4
4
  //#region src/shims/root-params.ts
5
5
  function createRootParamsUsageError(message) {
6
6
  return new Error(message);
@@ -9,6 +9,7 @@ const _FALLBACK_KEY = Symbol.for("vinext.rootParams.fallback");
9
9
  const _g = globalThis;
10
10
  const _als = getOrCreateAls("vinext.rootParams.als");
11
11
  const _usageAls = getOrCreateAls("vinext.rootParams.usage.als");
12
+ const _unstableCacheCallbackAls = getOrCreateAls("vinext.unstableCache.callbackAls");
12
13
  const _fallbackState = _g[_FALLBACK_KEY] ??= { rootParams: null };
13
14
  function getState() {
14
15
  if (isInsideUnifiedScope()) return getRequestContext();
@@ -19,6 +20,16 @@ function pickRootParams(params, rootParamNames) {
19
20
  for (const name of rootParamNames ?? []) picked[name] = params[name];
20
21
  return picked;
21
22
  }
23
+ /**
24
+ * Like Next.js's `unstable-cache` work unit, an unstable_cache() callback has
25
+ * no root params, and neither does a "use cache" scope entered from it: the
26
+ * callback's entry is keyed by its source and arguments alone.
27
+ */
28
+ function getUnstableCacheScope() {
29
+ const callback = _unstableCacheCallbackAls.getStore();
30
+ if (!callback) return null;
31
+ return (callback.useCacheContext ?? null) === getRegisteredCacheContext() ? "callback" : "use-cache";
32
+ }
22
33
  function setRootParams(params) {
23
34
  getState().rootParams = params;
24
35
  }
@@ -26,12 +37,15 @@ function getRootParam(name) {
26
37
  const usage = _usageAls.getStore();
27
38
  if (usage?.kind === "server-action" && usage.phase === "active") throw createRootParamsUsageError(`\`import('next/root-params').${name}()\` was used inside a Server Action. This is not supported. Functions from 'next/root-params' can only be called in the context of a route.`);
28
39
  if (usage?.kind === "route-handler" && usage.phase === "active") throw createRootParamsUsageError(`Route ${usage.routePattern} used \`import('next/root-params').${name}()\` inside a Route Handler. Support for this API in Route Handlers is planned for a future version of Next.js.`);
40
+ const unstableCacheScope = getUnstableCacheScope();
41
+ if (unstableCacheScope === "callback") throw createRootParamsUsageError(`\`import('next/root-params').${name}()\` was used inside \`unstable_cache\`. This is not supported. Use \`"use cache"\` instead.`);
42
+ if (unstableCacheScope === "use-cache") throw createRootParamsUsageError(`\`import('next/root-params').${name}()\` was used inside \`"use cache"\` nested within \`unstable_cache\`. Root params are not available in this context.`);
29
43
  _recordUseCacheRootParamRead(name);
30
44
  return Promise.resolve(getState().rootParams?.[name]);
31
45
  }
32
46
  /** @internal Current route root params for `"use cache"` key derivation. */
33
47
  function getCurrentRootParams() {
34
- return getState().rootParams;
48
+ return getUnstableCacheScope() ? null : getState().rootParams;
35
49
  }
36
50
  function runWithRootParamsUsage(usage, fn, controller) {
37
51
  const state = {
@@ -1,10 +1,10 @@
1
1
  "use client";
2
+ import { getBfcacheIdMapContext, getBfcacheSegmentIdContext } from "./navigation-context-state.js";
3
+ import { notFound } from "./navigation-errors.js";
4
+ import "./navigation-server.js";
2
5
  import { APP_BFCACHE_SEGMENT_IDENTITIES_KEY, AppElementsWire, UNMATCHED_SLOT } from "../server/app-elements-wire.js";
3
6
  import { readOptimisticStateKeyRebinder } from "../server/app-elements.js";
4
7
  import { isNestedBfcacheSlotSegmentId, isNestedBfcacheSlotSegmentIdFor } from "../server/bfcache-identity.js";
5
- import { notFound } from "./navigation-errors.js";
6
- import { getBfcacheIdMapContext, getBfcacheSegmentIdContext } from "./navigation-context-state.js";
7
- import "./navigation-server.js";
8
8
  import * as React$1 from "react";
9
9
  import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
10
10
  //#region src/shims/slot.tsx
@@ -115,12 +115,17 @@ function normalizePathTrailingSlash(path, trailingSlash) {
115
115
  if (!path.startsWith("/") || path.startsWith("//") || process.env.__VINEXT_MANUAL_TRAILING_SLASH === "true") return path;
116
116
  const { pathname, query, hash } = parsePath(path);
117
117
  if (trailingSlash) {
118
- if (/\.[^/]+\/?$/.test(pathname)) return `${removeRouteTrailingSlash(pathname)}${query}${hash}`;
118
+ if (hasFileLikeLastSegment(pathname)) return `${removeRouteTrailingSlash(pathname)}${query}${hash}`;
119
119
  if (pathname.endsWith("/")) return `${pathname}${query}${hash}`;
120
120
  return `${pathname}/${query}${hash}`;
121
121
  }
122
122
  return `${removeRouteTrailingSlash(pathname)}${query}${hash}`;
123
123
  }
124
+ function hasFileLikeLastSegment(pathname) {
125
+ const end = pathname.endsWith("/") ? pathname.length - 1 : pathname.length;
126
+ const dot = pathname.indexOf(".", pathname.lastIndexOf("/", end - 1) + 1);
127
+ return dot !== -1 && dot < end - 1;
128
+ }
124
129
  /**
125
130
  * Prepend basePath to a local path for browser URLs / fetches.
126
131
  */
@@ -11,6 +11,11 @@ export declare const VINEXT_CLIENT_REFERENCE_SOURCES = ".vite/vinext-client-refe
11
11
  export type ClientEntryManifest = {
12
12
  pagesClientEntry?: string;
13
13
  appBrowserEntry?: string;
14
+ /**
15
+ * The chunk holding the App Router browser runtime, whose evaluation starts
16
+ * hydration. A user chunk group can move it out of `appBrowserEntry`.
17
+ */
18
+ appBrowserRuntime?: string;
14
19
  /** The App Router RouteManifest chunk, which the browser entry imports as it boots. */
15
20
  appRouteManifest?: string;
16
21
  /** What each App Router client reference loads in the browser. */
@@ -20,6 +20,7 @@ function readClientEntryManifest(clientDir) {
20
20
  const manifest = {};
21
21
  if (typeof value.pagesClientEntry === "string") manifest.pagesClientEntry = value.pagesClientEntry;
22
22
  if (typeof value.appBrowserEntry === "string") manifest.appBrowserEntry = value.appBrowserEntry;
23
+ if (typeof value.appBrowserRuntime === "string") manifest.appBrowserRuntime = value.appBrowserRuntime;
23
24
  if (typeof value.appRouteManifest === "string") manifest.appRouteManifest = value.appRouteManifest;
24
25
  if (isUnknownRecord(value.appClientReferenceChunks)) manifest.appClientReferenceChunks = value.appClientReferenceChunks;
25
26
  return manifest.pagesClientEntry || manifest.appBrowserEntry ? manifest : void 0;
@@ -3,6 +3,7 @@ import { ClientReferenceChunks } from "./client-reference-chunks.js";
3
3
  type ClientRuntimeMetadata = {
4
4
  clientEntryFile?: string;
5
5
  appBootstrapPreinitModules?: string[];
6
+ appBootstrapPreloadModules?: string[];
6
7
  appRouteManifestModule?: string;
7
8
  appClientReferenceChunks?: ClientReferenceChunks;
8
9
  cssGraph?: Record<string, {
@@ -17,6 +18,10 @@ type ClientRuntimeMetadata = {
17
18
  * Read the client build manifest and compute runtime metadata used by
18
19
  * Cloudflare worker entry injection and Node production server startup.
19
20
  *
21
+ * - `appBootstrapPreinitModules` — chunks the App Router browser entry imports,
22
+ * preinitialized during SSR.
23
+ * - `appBootstrapPreloadModules` — those of them that would start hydration if
24
+ * evaluated ahead of the shell, preloaded during SSR instead.
20
25
  * - `appRouteManifestModule` — the App Router RouteManifest chunk, which the
21
26
  * browser entry imports as it boots, preloaded during SSR.
22
27
  * - `appClientReferenceChunks` — what each App Router client reference loads
@@ -41,11 +41,28 @@ function collectCssGraph(buildManifest, applyBase) {
41
41
  }
42
42
  return Object.keys(cssGraph).length > 0 ? cssGraph : void 0;
43
43
  }
44
- function collectAppBootstrapPreinitModules(buildManifest, appBrowserEntry, applyBase) {
45
- if (!appBrowserEntry) return void 0;
44
+ /**
45
+ * The chunks the App Router browser entry statically imports, split by whether
46
+ * they can run ahead of the shell. A preinitialized module evaluates as soon as
47
+ * it loads, which on a reload can be before `<body>` has streamed. Evaluating
48
+ * the browser runtime starts hydration, so a chunk that holds or imports it is
49
+ * only preloaded, and runs when React's bootstrap script does. A user chunk
50
+ * group can put the runtime in one of these chunks.
51
+ */
52
+ function collectAppBootstrapModules(buildManifest, appBrowserEntry, appBrowserRuntime, applyBase) {
53
+ if (!appBrowserEntry) return {};
46
54
  const entryKey = Object.keys(buildManifest).find((key) => buildManifest[key].file === appBrowserEntry);
47
- if (!entryKey) return void 0;
48
- const modules = [];
55
+ if (!entryKey) return {};
56
+ function evaluatesRuntime(key, seen = /* @__PURE__ */ new Set()) {
57
+ if (seen.has(key)) return false;
58
+ seen.add(key);
59
+ const chunk = buildManifest[key];
60
+ if (!chunk) return false;
61
+ if (chunk.file === appBrowserRuntime) return true;
62
+ return (chunk.imports ?? []).some((importedKey) => evaluatesRuntime(importedKey, seen));
63
+ }
64
+ const preinit = [];
65
+ const preload = [];
49
66
  const seenFiles = /* @__PURE__ */ new Set();
50
67
  const visitedChunks = /* @__PURE__ */ new Set();
51
68
  function visit(key) {
@@ -57,18 +74,25 @@ function collectAppBootstrapPreinitModules(buildManifest, appBrowserEntry, apply
57
74
  const importedChunk = buildManifest[importedKey];
58
75
  if (importedChunk?.file.endsWith(".js") && !seenFiles.has(importedChunk.file)) {
59
76
  seenFiles.add(importedChunk.file);
60
- modules.push(applyBase(importedChunk.file));
77
+ (evaluatesRuntime(importedKey) ? preload : preinit).push(applyBase(importedChunk.file));
61
78
  }
62
79
  visit(importedKey);
63
80
  }
64
81
  }
65
82
  visit(entryKey);
66
- return modules.length > 0 ? modules : void 0;
83
+ return {
84
+ ...preinit.length > 0 ? { preinit } : {},
85
+ ...preload.length > 0 ? { preload } : {}
86
+ };
67
87
  }
68
88
  /**
69
89
  * Read the client build manifest and compute runtime metadata used by
70
90
  * Cloudflare worker entry injection and Node production server startup.
71
91
  *
92
+ * - `appBootstrapPreinitModules` — chunks the App Router browser entry imports,
93
+ * preinitialized during SSR.
94
+ * - `appBootstrapPreloadModules` — those of them that would start hydration if
95
+ * evaluated ahead of the shell, preloaded during SSR instead.
72
96
  * - `appRouteManifestModule` — the App Router RouteManifest chunk, which the
73
97
  * browser entry imports as it boots, preloaded during SSR.
74
98
  * - `appClientReferenceChunks` — what each App Router client reference loads
@@ -118,7 +142,9 @@ function computeClientRuntimeMetadata(opts) {
118
142
  metadata.cssGraph = collectCssGraph(buildManifest, (file) => manifestFileWithBase(file, opts.assetBase));
119
143
  metadata.sharedChunks = collectSharedChunkFiles(buildManifest, (file) => manifestFileWithBase(file, opts.assetBase));
120
144
  }
121
- metadata.appBootstrapPreinitModules = collectAppBootstrapPreinitModules(buildManifest, clientEntryManifest?.appBrowserEntry, appAssetUrl);
145
+ const appBootstrapModules = collectAppBootstrapModules(buildManifest, clientEntryManifest?.appBrowserEntry, clientEntryManifest?.appBrowserRuntime, appAssetUrl);
146
+ metadata.appBootstrapPreinitModules = appBootstrapModules.preinit;
147
+ metadata.appBootstrapPreloadModules = appBootstrapModules.preload;
122
148
  if (clientEntryManifest?.appRouteManifest) metadata.appRouteManifestModule = appAssetUrl(clientEntryManifest.appRouteManifest);
123
149
  const lazyChunks = computeLazyChunks(buildManifest).map((file) => manifestFileWithBase(file, opts.assetBase));
124
150
  if (lazyChunks.length > 0) metadata.lazyChunks = lazyChunks;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vinext",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "description": "Run Next.js apps on Vite. Drop-in replacement for the next CLI.",
5
5
  "homepage": "https://vinext.dev/",
6
6
  "license": "MIT",