vinext 1.0.0-beta.2 → 1.0.0-beta.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 (174) hide show
  1. package/README.md +6 -9
  2. package/dist/build/client-build-config.js +7 -1
  3. package/dist/build/precompress.js +1 -1
  4. package/dist/build/prerender.d.ts +2 -1
  5. package/dist/build/prerender.js +12 -2
  6. package/dist/check.js +4 -0
  7. package/dist/cli-args.d.ts +2 -1
  8. package/dist/cli-args.js +10 -0
  9. package/dist/cli.js +14 -8
  10. package/dist/client/navigation-runtime.d.ts +6 -0
  11. package/dist/client/navigation-runtime.js +1 -1
  12. package/dist/config/config-matchers.js +4 -1
  13. package/dist/config/dotenv.d.ts +3 -4
  14. package/dist/config/dotenv.js +30 -22
  15. package/dist/config/next-config.d.ts +16 -5
  16. package/dist/config/next-config.js +7 -0
  17. package/dist/entries/app-browser-entry.d.ts +4 -10
  18. package/dist/entries/app-browser-entry.js +20 -4
  19. package/dist/entries/app-rsc-entry.js +21 -3
  20. package/dist/entries/app-rsc-manifest.js +19 -0
  21. package/dist/entries/pages-client-entry.js +0 -1
  22. package/dist/entries/pages-server-entry.js +13 -3
  23. package/dist/index.js +122 -49
  24. package/dist/init-cloudflare.d.ts +3 -1
  25. package/dist/init-cloudflare.js +59 -5
  26. package/dist/plugins/client-reference-dedup.js +7 -5
  27. package/dist/plugins/extensionless-dynamic-import.js +23 -18
  28. package/dist/plugins/ignore-dynamic-requests.js +3 -1
  29. package/dist/plugins/import-meta-url.js +17 -6
  30. package/dist/plugins/require-context.js +20 -15
  31. package/dist/plugins/sass.js +3 -4
  32. package/dist/plugins/transform-cache.d.ts +19 -0
  33. package/dist/plugins/transform-cache.js +36 -0
  34. package/dist/routing/app-route-graph.d.ts +14 -6
  35. package/dist/routing/app-route-graph.js +106 -16
  36. package/dist/server/api-handler.d.ts +2 -0
  37. package/dist/server/api-handler.js +7 -6
  38. package/dist/server/app-bfcache-identity.js +28 -2
  39. package/dist/server/app-browser-entry.js +175 -75
  40. package/dist/server/app-browser-navigation-controller.d.ts +23 -21
  41. package/dist/server/app-browser-navigation-controller.js +30 -5
  42. package/dist/server/app-browser-prefetch-response.d.ts +9 -0
  43. package/dist/server/app-browser-prefetch-response.js +32 -0
  44. package/dist/server/app-browser-state.d.ts +8 -3
  45. package/dist/server/app-browser-state.js +9 -3
  46. package/dist/server/app-page-cache-finalizer.js +1 -1
  47. package/dist/server/app-page-cache.d.ts +1 -1
  48. package/dist/server/app-page-cache.js +16 -2
  49. package/dist/server/app-page-dispatch.d.ts +9 -1
  50. package/dist/server/app-page-dispatch.js +37 -8
  51. package/dist/server/app-page-element-builder.d.ts +4 -1
  52. package/dist/server/app-page-element-builder.js +34 -7
  53. package/dist/server/app-page-head.d.ts +3 -3
  54. package/dist/server/app-page-head.js +58 -13
  55. package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
  56. package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
  57. package/dist/server/app-page-probe.d.ts +4 -0
  58. package/dist/server/app-page-probe.js +6 -2
  59. package/dist/server/app-page-render.d.ts +1 -1
  60. package/dist/server/app-page-render.js +13 -3
  61. package/dist/server/app-page-request.d.ts +3 -1
  62. package/dist/server/app-page-request.js +4 -13
  63. package/dist/server/app-page-response.d.ts +2 -0
  64. package/dist/server/app-page-response.js +7 -2
  65. package/dist/server/app-page-route-wiring.d.ts +10 -2
  66. package/dist/server/app-page-route-wiring.js +269 -59
  67. package/dist/server/app-pages-bridge.d.ts +2 -1
  68. package/dist/server/app-pages-bridge.js +3 -1
  69. package/dist/server/app-route-handler-cache.js +4 -0
  70. package/dist/server/app-route-handler-dispatch.js +9 -3
  71. package/dist/server/app-route-handler-response.js +1 -1
  72. package/dist/server/app-route-module-loader.d.ts +6 -0
  73. package/dist/server/app-route-module-loader.js +3 -0
  74. package/dist/server/app-router-entry.js +6 -3
  75. package/dist/server/app-rsc-cache-busting.d.ts +7 -0
  76. package/dist/server/app-rsc-cache-busting.js +6 -0
  77. package/dist/server/app-rsc-errors.js +10 -0
  78. package/dist/server/app-rsc-handler.d.ts +2 -0
  79. package/dist/server/app-rsc-handler.js +30 -13
  80. package/dist/server/app-rsc-render-mode.d.ts +3 -2
  81. package/dist/server/app-rsc-render-mode.js +4 -1
  82. package/dist/server/app-rsc-request-normalization.js +65 -4
  83. package/dist/server/app-rsc-route-matching.d.ts +9 -0
  84. package/dist/server/app-rsc-route-matching.js +6 -0
  85. package/dist/server/app-server-action-execution.d.ts +1 -1
  86. package/dist/server/app-ssr-entry.js +4 -4
  87. package/dist/server/app-visited-response-cache.d.ts +6 -1
  88. package/dist/server/app-visited-response-cache.js +49 -1
  89. package/dist/server/cache-control.d.ts +2 -2
  90. package/dist/server/cache-control.js +2 -1
  91. package/dist/server/dev-server.d.ts +3 -1
  92. package/dist/server/dev-server.js +204 -348
  93. package/dist/server/edge-api-runtime.d.ts +10 -1
  94. package/dist/server/edge-api-runtime.js +29 -1
  95. package/dist/server/headers.d.ts +4 -2
  96. package/dist/server/headers.js +6 -3
  97. package/dist/server/isr-cache.d.ts +10 -33
  98. package/dist/server/isr-cache.js +33 -43
  99. package/dist/server/isr-decision.d.ts +2 -2
  100. package/dist/server/pages-api-route.d.ts +4 -0
  101. package/dist/server/pages-api-route.js +6 -3
  102. package/dist/server/pages-asset-tags.d.ts +1 -0
  103. package/dist/server/pages-asset-tags.js +6 -4
  104. package/dist/server/pages-dev-hydration.js +0 -1
  105. package/dist/server/pages-document-asset-props.d.ts +26 -0
  106. package/dist/server/pages-document-asset-props.js +69 -0
  107. package/dist/server/pages-get-initial-props.d.ts +1 -1
  108. package/dist/server/pages-get-initial-props.js +1 -4
  109. package/dist/server/pages-i18n.js +64 -16
  110. package/dist/server/pages-node-compat.d.ts +2 -0
  111. package/dist/server/pages-node-compat.js +10 -6
  112. package/dist/server/pages-page-data.d.ts +61 -9
  113. package/dist/server/pages-page-data.js +334 -74
  114. package/dist/server/pages-page-handler.d.ts +6 -1
  115. package/dist/server/pages-page-handler.js +119 -20
  116. package/dist/server/pages-page-response.d.ts +7 -3
  117. package/dist/server/pages-page-response.js +41 -33
  118. package/dist/server/pages-request-pipeline.d.ts +7 -0
  119. package/dist/server/pages-request-pipeline.js +5 -1
  120. package/dist/server/pages-revalidate.d.ts +1 -1
  121. package/dist/server/pages-revalidate.js +54 -8
  122. package/dist/server/pages-router-entry.d.ts +1 -0
  123. package/dist/server/pages-router-entry.js +10 -4
  124. package/dist/server/prerender-manifest.d.ts +1 -0
  125. package/dist/server/prod-server.d.ts +2 -1
  126. package/dist/server/prod-server.js +38 -13
  127. package/dist/server/revalidation-host.d.ts +11 -0
  128. package/dist/server/revalidation-host.js +19 -0
  129. package/dist/server/seed-cache.js +1 -1
  130. package/dist/server/worker-revalidation-context.d.ts +13 -0
  131. package/dist/server/worker-revalidation-context.js +30 -0
  132. package/dist/shims/app-router-scroll-state.d.ts +2 -1
  133. package/dist/shims/app-router-scroll-state.js +7 -2
  134. package/dist/shims/cache-handler.d.ts +1 -1
  135. package/dist/shims/cache-handler.js +10 -7
  136. package/dist/shims/cache-runtime.d.ts +6 -0
  137. package/dist/shims/cache-runtime.js +4 -1
  138. package/dist/shims/cache.js +1 -1
  139. package/dist/shims/document.js +15 -2
  140. package/dist/shims/error.js +1 -1
  141. package/dist/shims/fetch-cache.js +1 -1
  142. package/dist/shims/font-google-base.js +17 -8
  143. package/dist/shims/font-local.js +41 -10
  144. package/dist/shims/hash-scroll.d.ts +1 -1
  145. package/dist/shims/hash-scroll.js +2 -1
  146. package/dist/shims/headers.d.ts +10 -13
  147. package/dist/shims/headers.js +85 -36
  148. package/dist/shims/internal/cookie-serialize.d.ts +10 -9
  149. package/dist/shims/internal/cookie-serialize.js +9 -8
  150. package/dist/shims/internal/interpolate-as.d.ts +15 -1
  151. package/dist/shims/internal/interpolate-as.js +27 -2
  152. package/dist/shims/link.d.ts +2 -0
  153. package/dist/shims/link.js +77 -24
  154. package/dist/shims/metadata.d.ts +18 -34
  155. package/dist/shims/metadata.js +98 -56
  156. package/dist/shims/navigation.d.ts +18 -2
  157. package/dist/shims/navigation.js +70 -21
  158. package/dist/shims/request-context.d.ts +6 -2
  159. package/dist/shims/router.js +18 -7
  160. package/dist/shims/script.js +1 -1
  161. package/dist/shims/server.d.ts +7 -6
  162. package/dist/shims/server.js +156 -57
  163. package/dist/shims/slot.d.ts +2 -1
  164. package/dist/shims/slot.js +6 -2
  165. package/dist/shims/streamed-icons.d.ts +9 -0
  166. package/dist/shims/streamed-icons.js +33 -0
  167. package/dist/shims/unified-request-context.d.ts +21 -2
  168. package/dist/shims/unified-request-context.js +82 -1
  169. package/dist/utils/cache-control-metadata.d.ts +2 -1
  170. package/dist/utils/cache-control-metadata.js +5 -1
  171. package/dist/utils/html-limited-bots.js +1 -1
  172. package/dist/utils/protocol-headers.d.ts +7 -1
  173. package/dist/utils/protocol-headers.js +7 -1
  174. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ import { fnv1a64 } from "../utils/hash.js";
1
2
  import { escapeCSSString, formatFontClassRule, getFontMimeType, resolveSingleFaceStyle, sanitizeCSSVarName, sanitizeFallback, sanitizeFontDescriptorValue } from "./font-utils.js";
2
3
  //#region src/shims/font-local.ts
3
4
  /**
@@ -31,8 +32,15 @@ function sanitizeInternalFontFamily(name) {
31
32
  if (typeof name !== "string" || name.length === 0) return void 0;
32
33
  if (/^[$_a-zA-Z][$_a-zA-Z0-9]*$/.test(name)) return name;
33
34
  }
34
- let classCounter = 0;
35
- const injectedFonts = /* @__PURE__ */ new Set();
35
+ const _INJECTED_FONTS_KEY = Symbol.for("vinext.fontLocal.injectedFonts");
36
+ const _INJECTED_CLASS_RULES_KEY = Symbol.for("vinext.fontLocal.injectedClassRules");
37
+ const _INJECTED_VARIABLE_RULES_KEY = Symbol.for("vinext.fontLocal.injectedVariableRules");
38
+ const _INJECTED_ROOT_VARIABLES_KEY = Symbol.for("vinext.fontLocal.injectedRootVariables");
39
+ const _SSR_FONT_STYLES_KEY = Symbol.for("vinext.fontLocal.ssrFontStyles");
40
+ const _SSR_FONT_PRELOADS_KEY = Symbol.for("vinext.fontLocal.ssrFontPreloads");
41
+ const _SSR_FONT_PRELOAD_HREFS_KEY = Symbol.for("vinext.fontLocal.ssrFontPreloadHrefs");
42
+ const _g = globalThis;
43
+ const injectedFonts = _g[_INJECTED_FONTS_KEY] ??= /* @__PURE__ */ new Set();
36
44
  function generateFontFaceCSS(family, options, sources) {
37
45
  const display = options.display ?? "swap";
38
46
  const rules = [];
@@ -55,9 +63,9 @@ function generateFontFaceCSS(family, options, sources) {
55
63
  }
56
64
  return rules.join("\n");
57
65
  }
58
- const ssrFontStyles = [];
59
- const ssrFontPreloads = [];
60
- const ssrFontPreloadHrefs = /* @__PURE__ */ new Set();
66
+ const ssrFontStyles = _g[_SSR_FONT_STYLES_KEY] ??= [];
67
+ const ssrFontPreloads = _g[_SSR_FONT_PRELOADS_KEY] ??= [];
68
+ const ssrFontPreloadHrefs = _g[_SSR_FONT_PRELOAD_HREFS_KEY] ??= /* @__PURE__ */ new Set();
61
69
  /**
62
70
  * Get collected SSR font styles (used by the renderer).
63
71
  * Note: We don't clear the arrays because fonts are loaded at module import
@@ -87,7 +95,7 @@ function injectFontFaceCSS(css, id) {
87
95
  document.head.appendChild(style);
88
96
  }
89
97
  /** Track which className CSS rules have been injected. */
90
- const injectedClassRules = /* @__PURE__ */ new Set();
98
+ const injectedClassRules = _g[_INJECTED_CLASS_RULES_KEY] ??= /* @__PURE__ */ new Set();
91
99
  /**
92
100
  * Inject a CSS rule that maps a className to the exported font style.
93
101
  *
@@ -110,9 +118,9 @@ function injectClassNameRule(className, fontStyle) {
110
118
  document.head.appendChild(style);
111
119
  }
112
120
  /** Track which variable class CSS rules have been injected. */
113
- const injectedVariableRules = /* @__PURE__ */ new Set();
121
+ const injectedVariableRules = _g[_INJECTED_VARIABLE_RULES_KEY] ??= /* @__PURE__ */ new Set();
114
122
  /** Track which :root CSS variable rules have been injected. */
115
- const injectedRootVariables = /* @__PURE__ */ new Set();
123
+ const injectedRootVariables = _g[_INJECTED_ROOT_VARIABLES_KEY] ??= /* @__PURE__ */ new Set();
116
124
  /**
117
125
  * Inject a CSS rule that sets a CSS variable on an element.
118
126
  * This is what makes `<html className={font.variable}>` set the CSS variable
@@ -151,6 +159,28 @@ function normalizeSources(options) {
151
159
  return [options.src];
152
160
  }
153
161
  /**
162
+ * Derive a stable class identity from the inputs that affect the generated
163
+ * font CSS. Next.js hashes the generated font CSS for the same reason: class
164
+ * names must match across the RSC, SSR, and browser module graphs regardless
165
+ * of which graph evaluates a font call first.
166
+ */
167
+ function createLocalFontIdentity(options, sources, internalFamily) {
168
+ return fnv1a64(JSON.stringify([
169
+ internalFamily ?? "",
170
+ sources.map((source) => [
171
+ source.path,
172
+ source.weight ?? "",
173
+ source.style ?? ""
174
+ ]),
175
+ options.display ?? "swap",
176
+ options.weight ?? "",
177
+ options.style ?? "",
178
+ options.fallback ?? ["sans-serif"],
179
+ options.variable ?? "",
180
+ options.declarations?.map((declaration) => [declaration.prop, declaration.value]) ?? []
181
+ ]));
182
+ }
183
+ /**
154
184
  * Collect font source URLs for preload link generation.
155
185
  * Only collects on the server (SSR). Deduplicates by href using a Set for O(1) lookups.
156
186
  */
@@ -168,10 +198,11 @@ function collectFontPreloads(sources) {
168
198
  }
169
199
  }
170
200
  function localFont(options) {
171
- const id = classCounter++;
172
201
  const sources = normalizeSources(options);
202
+ const internalFamily = sanitizeInternalFontFamily(options._vinext?.font?.family);
203
+ const id = createLocalFontIdentity(options, sources, internalFamily);
173
204
  const singleSource = sources.length === 1 ? sources[0] : void 0;
174
- const family = sanitizeInternalFontFamily(options._vinext?.font?.family) ?? `__local_font_${id}`;
205
+ const family = internalFamily ?? `__local_font_${id}`;
175
206
  const className = `__font_local_${id}`;
176
207
  const fontFamily = `'${family}', ${(options.fallback ?? ["sans-serif"]).map(sanitizeFallback).join(", ")}`;
177
208
  const cssVarName = options.variable ? sanitizeCSSVarName(options.variable) : void 0;
@@ -1,7 +1,7 @@
1
1
  //#region src/shims/hash-scroll.d.ts
2
2
  declare function decodeHashFragment(fragment: string): string;
3
3
  declare function scrollToHashTarget(hash: string): void;
4
- declare function scrollToHashTargetOnNextFrame(hash: string): void;
4
+ declare function scrollToHashTargetOnNextFrame(hash: string, shouldScroll?: () => boolean): void;
5
5
  declare function retryScrollTo(x: number, y: number, opts?: {
6
6
  minFrames?: number;
7
7
  shouldContinue?: () => boolean;
@@ -24,8 +24,9 @@ function scrollToHashTarget(hash) {
24
24
  }
25
25
  window.scrollTo(0, 0);
26
26
  }
27
- function scrollToHashTargetOnNextFrame(hash) {
27
+ function scrollToHashTargetOnNextFrame(hash, shouldScroll) {
28
28
  requestAnimationFrame(() => {
29
+ if (shouldScroll && !shouldScroll()) return;
29
30
  scrollToHashTarget(hash);
30
31
  });
31
32
  }
@@ -222,19 +222,16 @@ type DraftModeResult = {
222
222
  * (`getDraftModeProviderForCacheScope` + `trackDynamicDraftMode`).
223
223
  */
224
224
  declare function draftMode(): Promise<DraftModeResult>;
225
+ declare const APPLY_RESPONSE_COOKIE: unique symbol;
226
+ declare const SYNCHRONIZE_REQUEST_COOKIES: unique symbol;
225
227
  declare class RequestCookies {
226
228
  private _cookies;
227
- constructor(cookies: Map<string, string>);
228
- get(name: string): {
229
- name: string;
230
- value: string;
231
- } | undefined;
229
+ private _responseCookies;
230
+ constructor(cookies: Map<string, string>, mutable?: boolean);
231
+ get(name: string): ResponseCookie | undefined;
232
232
  getAll(nameOrOptions?: string | {
233
233
  name: string;
234
- }): Array<{
235
- name: string;
236
- value: string;
237
- }>;
234
+ }): ResponseCookie[];
238
235
  has(name: string): boolean;
239
236
  /**
240
237
  * Set a cookie. In Route Handlers and Server Actions, this produces
@@ -248,11 +245,11 @@ declare class RequestCookies {
248
245
  delete(name: string): this;
249
246
  delete(options: Omit<ResponseCookie, "value" | "expires">): this;
250
247
  get size(): number;
251
- [Symbol.iterator](): MapIterator<[string, {
252
- name: string;
253
- value: string;
254
- }]>;
248
+ [Symbol.iterator](): MapIterator<[string, ResponseCookie]>;
255
249
  toString(): string;
250
+ private _ensureResponseCookies;
251
+ [APPLY_RESPONSE_COOKIE](cookie: ResponseCookie): void;
252
+ [SYNCHRONIZE_REQUEST_COOKIES](): void;
256
253
  }
257
254
  //#endregion
258
255
  export { HeadersAccessPhase, HeadersContext, type RequestCookies, VinextHeadersShimState, applyMiddlewareRequestHeaders, consumeDynamicUsage, consumeInvalidDynamicUsageError, consumeRenderRequestApiUsage, cookies, draftMode, getActiveDraftModeState, getAndClearPendingCookies, getDraftModeCookieHeader, getHeadersAccessPhase, getHeadersContext, headers, headersContextFromRequest, isDraftModeEnabled, isDraftModeRequest, markDynamicUsage, markRenderRequestApiUsage, peekDynamicUsage, peekRenderRequestApiUsage, runWithConnectionProbe, runWithHeadersContext, runWithIsolatedDynamicUsage, setHeadersAccessPhase, setHeadersContext, suspendConnectionProbe, throwIfInsideCacheScope, throwIfStaticGenerationAccessError };
@@ -3,7 +3,7 @@ import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation }
3
3
  import { MIDDLEWARE_SET_COOKIE_HEADER } from "../utils/protocol-headers.js";
4
4
  import { FLIGHT_HEADERS, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER } from "../server/headers.js";
5
5
  import { buildRequestHeadersFromMiddlewareResponse } from "../utils/middleware-request-headers.js";
6
- import { serializeSetCookie, validateCookieAttributeValue, validateCookieName } from "./internal/cookie-serialize.js";
6
+ import { serializeSetCookie, validateCookieName } from "./internal/cookie-serialize.js";
7
7
  import { parseEdgeRequestCookieHeader } from "../utils/parse-cookie.js";
8
8
  import { createPprFallbackShellSuspensePromise } from "./ppr-fallback-shell.js";
9
9
  //#region src/shims/headers.ts
@@ -29,7 +29,6 @@ const _fallbackState = _g[_FALLBACK_KEY] ??= {
29
29
  draftModeCookieHeader: null,
30
30
  phase: "render"
31
31
  };
32
- const EXPIRED_COOKIE_DATE = (/* @__PURE__ */ new Date(0)).toUTCString();
33
32
  function splitMiddlewareSetCookieHeader(value) {
34
33
  const cookies = [];
35
34
  let start = 0;
@@ -320,6 +319,7 @@ function peekDynamicUsage() {
320
319
  }
321
320
  function _setStatePhase(state, phase) {
322
321
  const previous = state.phase;
322
+ if (previous === "action" && phase === "render") state.headersContext?.mutableCookies?.[SYNCHRONIZE_REQUEST_COOKIES]();
323
323
  state.phase = phase;
324
324
  return previous;
325
325
  }
@@ -534,7 +534,7 @@ function _sealCookies(cookies) {
534
534
  } });
535
535
  }
536
536
  function _getMutableCookies(ctx) {
537
- if (!ctx.mutableCookies) ctx.mutableCookies = _wrapMutableCookies(new RequestCookies(ctx.cookies));
537
+ if (!ctx.mutableCookies) ctx.mutableCookies = _wrapMutableCookies(new RequestCookies(ctx.cookies, true));
538
538
  return ctx.mutableCookies;
539
539
  }
540
540
  function _getReadonlyCookies(ctx) {
@@ -653,7 +653,6 @@ function getAndClearPendingCookies() {
653
653
  return cookies;
654
654
  }
655
655
  const DRAFT_MODE_COOKIE = "__prerender_bypass";
656
- const DRAFT_MODE_EXPIRED_DATE = (/* @__PURE__ */ new Date(0)).toUTCString();
657
656
  /**
658
657
  * Get any Set-Cookie header generated by draftMode().enable()/disable().
659
658
  * Called by the framework after rendering to attach the header to the response.
@@ -705,10 +704,6 @@ function getActiveDraftModeState() {
705
704
  function isDraftModeEnabled() {
706
705
  return getActiveDraftModeState() ?? false;
707
706
  }
708
- function draftModeCookieAttributes() {
709
- if (typeof process !== "undefined" && process.env?.NODE_ENV === "development") return "Path=/; HttpOnly; SameSite=Lax";
710
- return "Path=/; HttpOnly; SameSite=None; Secure";
711
- }
712
707
  function createDraftModeScopeError(expression) {
713
708
  return /* @__PURE__ */ new Error(`${expression} can only be called from a Server Component, Route Handler, or Server Action.`);
714
709
  }
@@ -746,26 +741,38 @@ async function draftMode() {
746
741
  throwIfInsideCacheScope("draftMode().enable()");
747
742
  const activeContext = requireActiveDraftModeContext(state, context, "draftMode().enable()");
748
743
  markDynamicUsage();
744
+ const cookie = createDraftModeCookie(secret);
745
+ const serialized = serializeMutableCookie(cookie);
746
+ _getMutableCookies(activeContext)[APPLY_RESPONSE_COOKIE](cookie);
749
747
  activeContext.draftModeEnabled = true;
750
- activeContext.cookies.set(DRAFT_MODE_COOKIE, secret);
751
- state.draftModeCookieHeader = `${DRAFT_MODE_COOKIE}=${secret}; ${draftModeCookieAttributes()}`;
748
+ state.draftModeCookieHeader = serialized;
752
749
  },
753
750
  disable() {
754
751
  throwIfInsideCacheScope("draftMode().disable()");
755
752
  const activeContext = requireActiveDraftModeContext(state, context, "draftMode().disable()");
756
753
  markDynamicUsage();
754
+ const cookie = createDraftModeCookie("", /* @__PURE__ */ new Date(0));
755
+ const serialized = serializeMutableCookie(cookie);
756
+ _getMutableCookies(activeContext)[APPLY_RESPONSE_COOKIE](cookie);
757
757
  activeContext.draftModeEnabled = false;
758
- activeContext.cookies.delete(DRAFT_MODE_COOKIE);
759
- state.draftModeCookieHeader = `${DRAFT_MODE_COOKIE}=; ${draftModeCookieAttributes()}; Expires=${DRAFT_MODE_EXPIRED_DATE}`;
758
+ state.draftModeCookieHeader = serialized;
760
759
  }
761
760
  };
762
761
  }
762
+ const APPLY_RESPONSE_COOKIE = Symbol("vinext.apply-response-cookie");
763
+ const SYNCHRONIZE_REQUEST_COOKIES = Symbol("vinext.synchronize-request-cookies");
763
764
  var RequestCookies = class {
764
765
  _cookies;
765
- constructor(cookies) {
766
+ _responseCookies;
767
+ constructor(cookies, mutable = false) {
766
768
  this._cookies = cookies;
769
+ this._responseCookies = mutable ? new Map(Array.from(cookies, ([name, value]) => [name, normalizeMutableCookie({
770
+ name,
771
+ value
772
+ })])) : null;
767
773
  }
768
774
  get(name) {
775
+ if (this._responseCookies) return this._responseCookies.get(name);
769
776
  const value = this._cookies.get(name);
770
777
  if (value === void 0) return void 0;
771
778
  return {
@@ -775,6 +782,10 @@ var RequestCookies = class {
775
782
  }
776
783
  getAll(nameOrOptions) {
777
784
  const name = typeof nameOrOptions === "string" ? nameOrOptions : nameOrOptions?.name;
785
+ if (this._responseCookies) {
786
+ const responseCookies = [...this._responseCookies.values()];
787
+ return name === void 0 ? responseCookies : responseCookies.filter((cookie) => cookie.name === name);
788
+ }
778
789
  const result = [];
779
790
  for (const [cookieName, value] of this._cookies) if (name === void 0 || cookieName === name) result.push({
780
791
  name: cookieName,
@@ -783,7 +794,7 @@ var RequestCookies = class {
783
794
  return result;
784
795
  }
785
796
  has(name) {
786
- return this._cookies.has(name);
797
+ return this._responseCookies?.has(name) ?? this._cookies.has(name);
787
798
  }
788
799
  set(nameOrOptions, value, options) {
789
800
  let cookieName;
@@ -799,43 +810,81 @@ var RequestCookies = class {
799
810
  opts = nameOrOptions;
800
811
  }
801
812
  validateCookieName(cookieName);
802
- this._cookies.set(cookieName, cookieValue);
803
- const sameSite = opts?.sameSite === true ? "Strict" : typeof opts?.sameSite === "string" ? opts.sameSite[0].toUpperCase() + opts.sameSite.slice(1) : void 0;
804
- _getState().pendingSetCookies.push(serializeSetCookie(cookieName, cookieValue, {
805
- ...opts,
806
- expires: typeof opts?.expires === "number" ? new Date(opts.expires) : opts?.expires,
807
- sameSite
808
- }));
813
+ const responseCookie = normalizeMutableCookie({
814
+ name: cookieName,
815
+ value: cookieValue,
816
+ ...opts
817
+ });
818
+ const serialized = serializeMutableCookie(responseCookie);
819
+ this[APPLY_RESPONSE_COOKIE](responseCookie);
820
+ _getState().pendingSetCookies.push(serialized);
809
821
  return this;
810
822
  }
811
823
  delete(nameOrOptions) {
812
824
  const name = typeof nameOrOptions === "string" ? nameOrOptions : nameOrOptions.name;
813
- const path = typeof nameOrOptions === "string" ? "/" : nameOrOptions.path ?? "/";
814
- const domain = typeof nameOrOptions === "string" ? void 0 : nameOrOptions.domain;
815
825
  validateCookieName(name);
816
- validateCookieAttributeValue(path, "Path");
817
- if (domain) validateCookieAttributeValue(domain, "Domain");
818
- this._cookies.delete(name);
819
- const parts = [`${name}=`, `Path=${path}`];
820
- if (domain) parts.push(`Domain=${domain}`);
821
- parts.push(`Expires=${EXPIRED_COOKIE_DATE}`);
822
- _getState().pendingSetCookies.push(parts.join("; "));
826
+ const responseCookie = normalizeMutableCookie({
827
+ ...typeof nameOrOptions === "string" ? void 0 : nameOrOptions,
828
+ name,
829
+ value: "",
830
+ expires: /* @__PURE__ */ new Date(0)
831
+ });
832
+ const serialized = serializeMutableCookie(responseCookie);
833
+ this[APPLY_RESPONSE_COOKIE](responseCookie);
834
+ _getState().pendingSetCookies.push(serialized);
823
835
  return this;
824
836
  }
825
837
  get size() {
826
- return this._cookies.size;
838
+ return this._responseCookies?.size ?? this._cookies.size;
827
839
  }
828
840
  [Symbol.iterator]() {
829
- return new Map(Array.from(this._cookies, ([name, value]) => [name, {
830
- name,
831
- value
832
- }])).entries();
841
+ return new Map(this.getAll().map((cookie) => [cookie.name, cookie])).entries();
833
842
  }
834
843
  toString() {
844
+ if (this._responseCookies) return [...this._responseCookies.values()].map(serializeMutableCookie).join("; ");
835
845
  const parts = [];
836
- for (const [name, value] of this._cookies) parts.push(`${name}=${value}`);
846
+ for (const [name, value] of this._cookies) parts.push(`${name}=${encodeURIComponent(value)}`);
837
847
  return parts.join("; ");
838
848
  }
849
+ _ensureResponseCookies() {
850
+ if (!this._responseCookies) this._responseCookies = new Map(Array.from(this._cookies, ([name, value]) => [name, normalizeMutableCookie({
851
+ name,
852
+ value
853
+ })]));
854
+ return this._responseCookies;
855
+ }
856
+ [APPLY_RESPONSE_COOKIE](cookie) {
857
+ this._ensureResponseCookies().set(cookie.name, cookie);
858
+ }
859
+ [SYNCHRONIZE_REQUEST_COOKIES]() {
860
+ this._cookies.clear();
861
+ for (const cookie of this._ensureResponseCookies().values()) this._cookies.set(cookie.name, cookie.value);
862
+ }
839
863
  };
864
+ function createDraftModeCookie(value, expires) {
865
+ const isDevelopment = typeof process !== "undefined" && process.env?.NODE_ENV === "development";
866
+ return normalizeMutableCookie({
867
+ name: DRAFT_MODE_COOKIE,
868
+ value,
869
+ ...expires === void 0 ? {} : { expires },
870
+ httpOnly: true,
871
+ sameSite: isDevelopment ? "lax" : "none",
872
+ secure: !isDevelopment,
873
+ path: "/"
874
+ });
875
+ }
876
+ function normalizeMutableCookie(cookie) {
877
+ const normalized = { ...cookie };
878
+ if (typeof normalized.expires === "number") normalized.expires = new Date(normalized.expires);
879
+ if (normalized.maxAge) normalized.expires = new Date(Date.now() + normalized.maxAge * 1e3);
880
+ if (normalized.path == null) normalized.path = "/";
881
+ return normalized;
882
+ }
883
+ function serializeMutableCookie(cookie) {
884
+ return serializeSetCookie(cookie.name, cookie.value, {
885
+ ...cookie,
886
+ expires: typeof cookie.expires === "number" ? new Date(cookie.expires) : cookie.expires
887
+ });
888
+ }
840
889
  //#endregion
841
890
  export { applyMiddlewareRequestHeaders, consumeDynamicUsage, consumeInvalidDynamicUsageError, consumeRenderRequestApiUsage, cookies, draftMode, getActiveDraftModeState, getAndClearPendingCookies, getDraftModeCookieHeader, getHeadersAccessPhase, getHeadersContext, headers, headersContextFromRequest, isDraftModeEnabled, isDraftModeRequest, markDynamicUsage, markRenderRequestApiUsage, peekDynamicUsage, peekRenderRequestApiUsage, runWithConnectionProbe, runWithHeadersContext, runWithIsolatedDynamicUsage, setHeadersAccessPhase, setHeadersContext, suspendConnectionProbe, throwIfInsideCacheScope, throwIfStaticGenerationAccessError };
@@ -1,11 +1,11 @@
1
1
  //#region src/shims/internal/cookie-serialize.d.ts
2
2
  /**
3
- * Shared Set-Cookie serialization for the next/headers and next/server shims.
3
+ * Set-Cookie serialization for the mutable next/headers shim.
4
4
  *
5
- * Two call sites — `cookies().set()` in headers.ts and `ResponseCookies.set()`
6
- * in server.ts produce identical Set-Cookie header strings. Keep the
7
- * encoding, attribute order, and validation in one place so subtle RFC 6265
8
- * details (escaping, attribute ordering, etc.) cannot drift between them.
5
+ * `ResponseCookies` in server.ts deliberately uses the exact
6
+ * @edge-runtime/cookies serialization order and casing instead. It also has to
7
+ * preserve the absence of a default path when parsing an existing Set-Cookie
8
+ * header, while this helper always defaults new mutable cookies to `Path=/`.
9
9
  *
10
10
  * Note: this is a value-encoding helper for response cookies only. The
11
11
  * request `Cookie:` header serialization in `RequestCookies._serialize`
@@ -20,7 +20,7 @@ type SerializeSetCookieOptions = {
20
20
  expires?: Date;
21
21
  httpOnly?: boolean;
22
22
  secure?: boolean;
23
- sameSite?: "Strict" | "Lax" | "None";
23
+ sameSite?: true | false | "strict" | "lax" | "none" | "Strict" | "Lax" | "None";
24
24
  partitioned?: boolean;
25
25
  priority?: "low" | "medium" | "high";
26
26
  };
@@ -36,12 +36,13 @@ declare function validateCookieAttributeValue(value: string, attributeName: stri
36
36
  * - Encodes the value with `encodeURIComponent`.
37
37
  * - Defaults `Path` to `/` (matching @edge-runtime/cookies and Next.js).
38
38
  * - Validates path/domain to reject control characters and semicolons.
39
- * - Emits attributes in the order: Path, Domain, Max-Age, Expires, HttpOnly,
40
- * Secure, SameSite, Partitioned, Priority.
39
+ * - Emits attributes in the same order and casing as Next.js's compiled
40
+ * `@edge-runtime/cookies` serializer: Path, Expires, Max-Age, Domain,
41
+ * Secure, HttpOnly, SameSite, Partitioned, Priority.
41
42
  *
42
43
  * The caller is responsible for validating the cookie name (typically before
43
44
  * mutating any internal state) via `validateCookieName`.
44
45
  */
45
- declare function serializeSetCookie(name: string, value: string, options?: SerializeSetCookieOptions): string;
46
+ declare function serializeSetCookie(name: string, value: string | null | undefined, options?: SerializeSetCookieOptions): string;
46
47
  //#endregion
47
48
  export { serializeSetCookie, validateCookieAttributeValue, validateCookieName };
@@ -23,28 +23,29 @@ function validateCookieAttributeValue(value, attributeName) {
23
23
  * - Encodes the value with `encodeURIComponent`.
24
24
  * - Defaults `Path` to `/` (matching @edge-runtime/cookies and Next.js).
25
25
  * - Validates path/domain to reject control characters and semicolons.
26
- * - Emits attributes in the order: Path, Domain, Max-Age, Expires, HttpOnly,
27
- * Secure, SameSite, Partitioned, Priority.
26
+ * - Emits attributes in the same order and casing as Next.js's compiled
27
+ * `@edge-runtime/cookies` serializer: Path, Expires, Max-Age, Domain,
28
+ * Secure, HttpOnly, SameSite, Partitioned, Priority.
28
29
  *
29
30
  * The caller is responsible for validating the cookie name (typically before
30
31
  * mutating any internal state) via `validateCookieName`.
31
32
  */
32
33
  function serializeSetCookie(name, value, options) {
33
- const parts = [`${name}=${encodeURIComponent(value)}`];
34
+ const parts = [`${name}=${encodeURIComponent(value ?? "")}`];
34
35
  const path = options?.path ?? "/";
35
36
  validateCookieAttributeValue(path, "Path");
36
- parts.push(`Path=${path}`);
37
+ if (path) parts.push(`Path=${path}`);
38
+ if (options?.expires) parts.push(`Expires=${options.expires.toUTCString()}`);
39
+ if (options?.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
37
40
  if (options?.domain) {
38
41
  validateCookieAttributeValue(options.domain, "Domain");
39
42
  parts.push(`Domain=${options.domain}`);
40
43
  }
41
- if (options?.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
42
- if (options?.expires) parts.push(`Expires=${options.expires.toUTCString()}`);
43
- if (options?.httpOnly) parts.push("HttpOnly");
44
44
  if (options?.secure) parts.push("Secure");
45
+ if (options?.httpOnly) parts.push("HttpOnly");
45
46
  if (options?.sameSite) parts.push(`SameSite=${options.sameSite}`);
46
47
  if (options?.partitioned) parts.push("Partitioned");
47
- if (options?.priority) parts.push(`Priority=${options.priority[0].toUpperCase()}${options.priority.slice(1)}`);
48
+ if (options?.priority) parts.push(`Priority=${options.priority}`);
48
49
  return parts.join("; ");
49
50
  }
50
51
  //#endregion
@@ -14,6 +14,10 @@ type DynamicRouteHrefProjection = {
14
14
  query: ParsedUrlQuery;
15
15
  routePathname: string;
16
16
  };
17
+ type DynamicRouteHrefResolution = {
18
+ /** Route-pattern URL passed to the Pages Router. */href: string; /** Interpolated URL rendered in the anchor and displayed in the browser. */
19
+ as: string;
20
+ };
17
21
  /**
18
22
  * Resolve a bracket-pattern route href against its displayed href. Query
19
23
  * values can be supplied directly (object-form hrefs) or parsed from the route
@@ -21,5 +25,15 @@ type DynamicRouteHrefProjection = {
21
25
  * query delimiter.
22
26
  */
23
27
  declare function interpolateDynamicRouteHref(routeHref: string, asHref: string, queryInput?: UrlQuery): DynamicRouteHrefProjection | null;
28
+ /**
29
+ * Resolve the two URLs that Next.js' Pages Router derives from a dynamic
30
+ * href: the original route-pattern URL used to load the page and the
31
+ * interpolated browser URL. Dynamic params are consumed from the latter's
32
+ * query string while unrelated query values and the hash are retained.
33
+ *
34
+ * Mirrors `resolveHref(router, href, true)` from Next.js:
35
+ * packages/next/src/client/resolve-href.ts.
36
+ */
37
+ declare function resolveDynamicRouteHref(routeHref: string): DynamicRouteHrefResolution | null;
24
38
  //#endregion
25
- export { DynamicRouteHrefProjection, interpolateDynamicRouteHref };
39
+ export { DynamicRouteHrefProjection, DynamicRouteHrefResolution, interpolateDynamicRouteHref, resolveDynamicRouteHref };
@@ -176,11 +176,11 @@ function interpolateAs(route, asPathname, query) {
176
176
  * query delimiter.
177
177
  */
178
178
  function interpolateDynamicRouteHref(routeHref, asHref, queryInput) {
179
- if (!routeHref.includes("[")) return null;
180
179
  const hashIndex = routeHref.indexOf("#");
181
180
  const queryIndex = routeHref.indexOf("?");
182
181
  const pathEnd = [hashIndex, queryIndex].filter((index) => index !== -1).reduce((earliest, index) => Math.min(earliest, index), routeHref.length);
183
182
  const routePathname = routeHref.slice(0, pathEnd);
183
+ if (!routePathname.includes("[")) return null;
184
184
  const trailing = routeHref.slice(pathEnd);
185
185
  const asPathname = asHref.split(/[?#]/, 1)[0];
186
186
  const query = queryInput ? normalizeQuery(queryInput) : parseRouteHrefQuery(routeHref, queryIndex, hashIndex);
@@ -192,5 +192,30 @@ function interpolateDynamicRouteHref(routeHref, asHref, queryInput) {
192
192
  routePathname
193
193
  };
194
194
  }
195
+ /**
196
+ * Resolve the two URLs that Next.js' Pages Router derives from a dynamic
197
+ * href: the original route-pattern URL used to load the page and the
198
+ * interpolated browser URL. Dynamic params are consumed from the latter's
199
+ * query string while unrelated query values and the hash are retained.
200
+ *
201
+ * Mirrors `resolveHref(router, href, true)` from Next.js:
202
+ * packages/next/src/client/resolve-href.ts.
203
+ */
204
+ function resolveDynamicRouteHref(routeHref) {
205
+ const projection = interpolateDynamicRouteHref(routeHref, routeHref);
206
+ if (!projection?.href) return null;
207
+ const hashIndex = projection.href.indexOf("#");
208
+ const hash = hashIndex === -1 ? "" : projection.href.slice(hashIndex);
209
+ const hrefWithoutHash = hashIndex === -1 ? projection.href : projection.href.slice(0, hashIndex);
210
+ const queryIndex = hrefWithoutHash.indexOf("?");
211
+ const pathname = queryIndex === -1 ? hrefWithoutHash : hrefWithoutHash.slice(0, queryIndex);
212
+ const searchParams = new URLSearchParams(queryIndex === -1 ? "" : hrefWithoutHash.slice(queryIndex + 1));
213
+ for (const param of projection.params) searchParams.delete(param);
214
+ const search = searchParams.toString();
215
+ return {
216
+ href: routeHref,
217
+ as: `${pathname}${search ? `?${search}` : ""}${hash}`
218
+ };
219
+ }
195
220
  //#endregion
196
- export { interpolateDynamicRouteHref };
221
+ export { interpolateDynamicRouteHref, resolveDynamicRouteHref };
@@ -56,6 +56,8 @@ declare function resolveLinkPrefetchMode(prefetchProp: LinkProps["prefetch"], is
56
56
  declare function canAutoPrefetchFullAppRoute(href: string): boolean;
57
57
  declare function resolveAutoAppRoutePrefetch(href: string): {
58
58
  cacheForNavigation: boolean;
59
+ fallbackTtl: "dynamic" | "static";
60
+ minimumTtlMs: number | undefined;
59
61
  prefetchShellFirst: boolean;
60
62
  shouldPrefetch: boolean;
61
63
  };