vinext 1.0.0-beta.1 → 1.0.0-beta.3

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 (234) hide show
  1. package/README.md +10 -10
  2. package/dist/build/client-build-config.js +8 -1
  3. package/dist/build/report.d.ts +8 -1
  4. package/dist/build/report.js +12 -2
  5. package/dist/check.js +19 -2
  6. package/dist/cli.js +3 -2
  7. package/dist/config/config-matchers.d.ts +13 -37
  8. package/dist/config/config-matchers.js +35 -122
  9. package/dist/config/next-config.d.ts +15 -5
  10. package/dist/config/next-config.js +7 -1
  11. package/dist/config/request-context.d.ts +14 -0
  12. package/dist/config/request-context.js +26 -0
  13. package/dist/entries/app-browser-entry.d.ts +4 -10
  14. package/dist/entries/app-browser-entry.js +20 -4
  15. package/dist/entries/app-rsc-entry.js +30 -9
  16. package/dist/entries/app-rsc-manifest.js +36 -0
  17. package/dist/entries/pages-client-entry.js +0 -1
  18. package/dist/entries/pages-server-entry.js +13 -5
  19. package/dist/index.js +194 -101
  20. package/dist/init-cloudflare.d.ts +3 -1
  21. package/dist/init-cloudflare.js +59 -5
  22. package/dist/init.d.ts +6 -4
  23. package/dist/init.js +20 -22
  24. package/dist/plugins/extensionless-dynamic-import.js +23 -18
  25. package/dist/plugins/ignore-dynamic-requests.js +3 -1
  26. package/dist/plugins/import-meta-url.js +17 -6
  27. package/dist/plugins/og-assets.js +1 -1
  28. package/dist/plugins/require-context.js +20 -15
  29. package/dist/plugins/transform-cache.d.ts +19 -0
  30. package/dist/plugins/transform-cache.js +36 -0
  31. package/dist/routing/app-route-graph.d.ts +27 -11
  32. package/dist/routing/app-route-graph.js +158 -22
  33. package/dist/routing/app-router.js +12 -6
  34. package/dist/routing/file-matcher.d.ts +11 -22
  35. package/dist/routing/file-matcher.js +21 -33
  36. package/dist/routing/pages-router.js +23 -29
  37. package/dist/routing/route-matching.d.ts +14 -1
  38. package/dist/routing/route-matching.js +13 -1
  39. package/dist/routing/route-pattern.d.ts +2 -1
  40. package/dist/routing/route-pattern.js +7 -4
  41. package/dist/routing/route-trie.d.ts +5 -1
  42. package/dist/routing/route-trie.js +5 -2
  43. package/dist/server/api-handler.d.ts +2 -0
  44. package/dist/server/api-handler.js +3 -3
  45. package/dist/server/app-browser-entry.js +110 -37
  46. package/dist/server/app-browser-history-controller.d.ts +1 -1
  47. package/dist/server/app-browser-history-controller.js +3 -3
  48. package/dist/server/app-browser-navigation-controller.d.ts +23 -21
  49. package/dist/server/app-browser-navigation-controller.js +2 -2
  50. package/dist/server/app-browser-state.js +1 -1
  51. package/dist/server/app-browser-visible-commit.js +1 -1
  52. package/dist/server/app-fallback-renderer.d.ts +3 -0
  53. package/dist/server/app-fallback-renderer.js +4 -2
  54. package/dist/server/app-middleware.js +2 -1
  55. package/dist/server/app-page-boundary-render.d.ts +12 -1
  56. package/dist/server/app-page-boundary-render.js +84 -2
  57. package/dist/server/app-page-cache-finalizer.d.ts +1 -1
  58. package/dist/server/app-page-cache-finalizer.js +1 -1
  59. package/dist/server/app-page-cache.d.ts +2 -2
  60. package/dist/server/app-page-cache.js +16 -2
  61. package/dist/server/app-page-dispatch.d.ts +19 -2
  62. package/dist/server/app-page-dispatch.js +45 -23
  63. package/dist/server/app-page-element-builder.d.ts +9 -2
  64. package/dist/server/app-page-element-builder.js +144 -33
  65. package/dist/server/app-page-head.d.ts +56 -4
  66. package/dist/server/app-page-head.js +216 -85
  67. package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
  68. package/dist/server/app-page-http-access-fallback-metadata.js +114 -0
  69. package/dist/server/app-page-probe.d.ts +4 -0
  70. package/dist/server/app-page-probe.js +6 -2
  71. package/dist/server/app-page-render.d.ts +2 -2
  72. package/dist/server/app-page-render.js +1 -1
  73. package/dist/server/app-page-request.d.ts +15 -0
  74. package/dist/server/app-page-request.js +134 -14
  75. package/dist/server/app-page-response.js +1 -1
  76. package/dist/server/app-page-route-wiring.d.ts +16 -0
  77. package/dist/server/app-page-route-wiring.js +254 -52
  78. package/dist/server/app-pages-bridge.d.ts +1 -1
  79. package/dist/server/app-pages-bridge.js +2 -1
  80. package/dist/server/app-post-middleware-context.d.ts +1 -1
  81. package/dist/server/app-post-middleware-context.js +1 -1
  82. package/dist/server/app-route-handler-cache.js +4 -0
  83. package/dist/server/app-route-handler-dispatch.js +40 -20
  84. package/dist/server/app-route-handler-execution.d.ts +6 -1
  85. package/dist/server/app-route-handler-execution.js +42 -16
  86. package/dist/server/app-route-handler-policy.d.ts +2 -0
  87. package/dist/server/app-route-handler-policy.js +3 -3
  88. package/dist/server/app-route-handler-response.js +1 -1
  89. package/dist/server/app-route-module-loader.d.ts +9 -1
  90. package/dist/server/app-route-module-loader.js +4 -0
  91. package/dist/server/app-router-entry.js +6 -3
  92. package/dist/server/app-rsc-cache-busting.d.ts +1 -1
  93. package/dist/server/app-rsc-errors.d.ts +1 -1
  94. package/dist/server/app-rsc-errors.js +22 -2
  95. package/dist/server/app-rsc-handler.d.ts +10 -5
  96. package/dist/server/app-rsc-handler.js +73 -33
  97. package/dist/server/app-rsc-request-normalization.d.ts +3 -2
  98. package/dist/server/app-rsc-request-normalization.js +8 -2
  99. package/dist/server/app-rsc-response-finalizer.d.ts +2 -2
  100. package/dist/server/app-rsc-response-finalizer.js +5 -11
  101. package/dist/server/app-rsc-route-matching.d.ts +29 -0
  102. package/dist/server/app-rsc-route-matching.js +93 -11
  103. package/dist/server/app-segment-config.js +13 -3
  104. package/dist/server/app-server-action-execution.d.ts +3 -1
  105. package/dist/server/app-server-action-execution.js +9 -7
  106. package/dist/server/app-ssr-entry.js +6 -6
  107. package/dist/server/app-ssr-stream.d.ts +1 -7
  108. package/dist/server/app-ssr-stream.js +2 -10
  109. package/dist/server/cache-control.d.ts +2 -2
  110. package/dist/server/cache-control.js +2 -1
  111. package/dist/server/config-headers.d.ts +24 -0
  112. package/dist/server/config-headers.js +52 -0
  113. package/dist/server/dev-initial-server-error.d.ts +1 -1
  114. package/dist/server/dev-server.d.ts +3 -1
  115. package/dist/server/dev-server.js +170 -321
  116. package/dist/server/headers.d.ts +2 -2
  117. package/dist/server/headers.js +4 -3
  118. package/dist/server/isr-cache.d.ts +11 -34
  119. package/dist/server/isr-cache.js +33 -43
  120. package/dist/server/isr-decision.d.ts +2 -2
  121. package/dist/server/middleware-matcher-pattern.d.ts +22 -0
  122. package/dist/server/middleware-matcher-pattern.js +219 -0
  123. package/dist/server/middleware-matcher.d.ts +2 -7
  124. package/dist/server/middleware-matcher.js +13 -60
  125. package/dist/server/middleware-path-to-regexp.d.ts +14 -0
  126. package/dist/server/middleware-path-to-regexp.js +228 -0
  127. package/dist/server/middleware-runtime.js +4 -3
  128. package/dist/server/middleware.d.ts +1 -1
  129. package/dist/server/middleware.js +2 -1
  130. package/dist/server/pages-api-route.d.ts +2 -0
  131. package/dist/server/pages-api-route.js +4 -1
  132. package/dist/server/pages-data-route.d.ts +11 -1
  133. package/dist/server/pages-data-route.js +16 -1
  134. package/dist/server/pages-dev-hydration.js +0 -1
  135. package/dist/server/pages-get-initial-props.d.ts +16 -3
  136. package/dist/server/pages-get-initial-props.js +17 -10
  137. package/dist/server/pages-i18n.js +64 -16
  138. package/dist/server/pages-node-compat.d.ts +2 -0
  139. package/dist/server/pages-node-compat.js +7 -3
  140. package/dist/server/pages-page-data.d.ts +64 -10
  141. package/dist/server/pages-page-data.js +349 -80
  142. package/dist/server/pages-page-handler.d.ts +9 -3
  143. package/dist/server/pages-page-handler.js +131 -36
  144. package/dist/server/pages-page-response.d.ts +5 -3
  145. package/dist/server/pages-page-response.js +24 -27
  146. package/dist/server/pages-readiness.d.ts +3 -3
  147. package/dist/server/pages-request-pipeline.d.ts +8 -0
  148. package/dist/server/pages-request-pipeline.js +30 -12
  149. package/dist/server/pages-revalidate.d.ts +1 -1
  150. package/dist/server/pages-revalidate.js +54 -8
  151. package/dist/server/pages-router-entry.d.ts +1 -0
  152. package/dist/server/pages-router-entry.js +16 -4
  153. package/dist/server/prerender-route-params.js +2 -2
  154. package/dist/server/prod-server.d.ts +3 -5
  155. package/dist/server/prod-server.js +72 -35
  156. package/dist/server/request-pipeline.d.ts +15 -29
  157. package/dist/server/request-pipeline.js +24 -59
  158. package/dist/server/revalidation-host.d.ts +11 -0
  159. package/dist/server/revalidation-host.js +19 -0
  160. package/dist/server/rsc-stream-hints.js +122 -18
  161. package/dist/server/worker-revalidation-context.d.ts +13 -0
  162. package/dist/server/worker-revalidation-context.js +29 -0
  163. package/dist/shims/app.d.ts +1 -1
  164. package/dist/shims/cache-handler.d.ts +1 -1
  165. package/dist/shims/cache-handler.js +10 -7
  166. package/dist/shims/cache-request-state.d.ts +27 -1
  167. package/dist/shims/cache-request-state.js +60 -1
  168. package/dist/shims/cache-runtime.d.ts +18 -1
  169. package/dist/shims/cache-runtime.js +26 -8
  170. package/dist/shims/cache.d.ts +5 -5
  171. package/dist/shims/cache.js +27 -11
  172. package/dist/shims/document.d.ts +42 -83
  173. package/dist/shims/document.js +62 -53
  174. package/dist/shims/dynamic.d.ts +5 -25
  175. package/dist/shims/dynamic.js +29 -8
  176. package/dist/shims/error.d.ts +1 -1
  177. package/dist/shims/error.js +2 -2
  178. package/dist/shims/fetch-cache.js +13 -4
  179. package/dist/shims/font-google-base.d.ts +11 -4
  180. package/dist/shims/font-google-base.js +17 -8
  181. package/dist/shims/font-local.d.ts +10 -3
  182. package/dist/shims/font-local.js +41 -10
  183. package/dist/shims/head.d.ts +13 -2
  184. package/dist/shims/head.js +3 -3
  185. package/dist/shims/headers.d.ts +37 -43
  186. package/dist/shims/headers.js +183 -74
  187. package/dist/shims/image.d.ts +27 -37
  188. package/dist/shims/image.js +21 -14
  189. package/dist/shims/internal/cookie-serialize.d.ts +12 -9
  190. package/dist/shims/internal/cookie-serialize.js +10 -7
  191. package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
  192. package/dist/shims/internal/interpolate-as.d.ts +15 -1
  193. package/dist/shims/internal/interpolate-as.js +27 -2
  194. package/dist/shims/internal/utils.d.ts +3 -3
  195. package/dist/shims/legacy-image.d.ts +3 -37
  196. package/dist/shims/legacy-image.js +6 -16
  197. package/dist/shims/link.d.ts +15 -22
  198. package/dist/shims/link.js +34 -13
  199. package/dist/shims/metadata.d.ts +7 -17
  200. package/dist/shims/metadata.js +74 -24
  201. package/dist/shims/navigation-context-state.d.ts +1 -1
  202. package/dist/shims/navigation.d.ts +7 -3
  203. package/dist/shims/navigation.js +25 -14
  204. package/dist/shims/navigation.react-server.js +1 -1
  205. package/dist/shims/next-shims-augmentations.d.ts +1 -0
  206. package/dist/shims/next-shims-public.d.ts +1 -0
  207. package/dist/shims/public-shim-map.json.js +103 -0
  208. package/dist/shims/readonly-url-search-params.d.ts +3 -3
  209. package/dist/shims/readonly-url-search-params.js +3 -3
  210. package/dist/shims/request-context.d.ts +4 -1
  211. package/dist/shims/request-state-types.d.ts +1 -1
  212. package/dist/shims/router.d.ts +55 -21
  213. package/dist/shims/router.js +180 -44
  214. package/dist/shims/script.d.ts +6 -10
  215. package/dist/shims/script.js +7 -4
  216. package/dist/shims/server.d.ts +50 -26
  217. package/dist/shims/server.js +193 -55
  218. package/dist/shims/unified-request-context.d.ts +22 -3
  219. package/dist/shims/unified-request-context.js +84 -1
  220. package/dist/shims/url-safety.d.ts +1 -1
  221. package/dist/shims/url-safety.js +2 -2
  222. package/dist/typegen.d.ts +8 -2
  223. package/dist/typegen.js +76 -15
  224. package/dist/utils/cache-control-metadata.d.ts +2 -1
  225. package/dist/utils/cache-control-metadata.js +5 -1
  226. package/dist/utils/domain-locale.d.ts +2 -2
  227. package/dist/utils/external-url.d.ts +5 -0
  228. package/dist/utils/external-url.js +7 -0
  229. package/dist/utils/html-limited-bots.js +1 -1
  230. package/dist/utils/protocol-headers.d.ts +7 -1
  231. package/dist/utils/protocol-headers.js +7 -1
  232. package/dist/utils/regex-safety.d.ts +8 -0
  233. package/dist/utils/regex-safety.js +737 -0
  234. package/package.json +13 -2
@@ -2,9 +2,9 @@ import { getOrCreateAls } from "./internal/als-registry.js";
2
2
  import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
3
3
  import { getRequestExecutionContext } from "./request-context.js";
4
4
  import { getDataCacheHandler } from "./cache-handler.js";
5
- import { markDynamicUsage } from "./headers.js";
6
- import { _setRequestScopedCacheLife } from "./cache-request-state.js";
7
5
  import { encodeCacheTags } from "../utils/encode-cache-tag.js";
6
+ import { markDynamicUsage } from "./headers.js";
7
+ import { _hasPendingRevalidatedTag, _setRequestScopedCacheLife } from "./cache-request-state.js";
8
8
  //#region src/shims/fetch-cache.ts
9
9
  /**
10
10
  * Extended fetch() with Next.js caching semantics.
@@ -639,13 +639,22 @@ function createPatchedFetch() {
639
639
  throw err;
640
640
  }
641
641
  const handler = getDataCacheHandler();
642
+ let mustBypassPendingRevalidation = _hasPendingRevalidatedTag([...tags, ...softTags]);
642
643
  try {
643
- const cached = await handler.get(cacheKey, {
644
+ let cached = mustBypassPendingRevalidation ? null : await handler.get(cacheKey, {
644
645
  kind: "FETCH",
645
646
  tags,
646
647
  softTags,
647
648
  revalidate: revalidateSeconds
648
649
  });
650
+ if (cached?.value?.kind === "FETCH" && _hasPendingRevalidatedTag([
651
+ ...cached.value.tags ?? [],
652
+ ...tags,
653
+ ...softTags
654
+ ])) {
655
+ mustBypassPendingRevalidation = true;
656
+ cached = null;
657
+ }
649
658
  if (cached?.value && cached.value.kind === "FETCH" && cached.cacheState !== "stale") {
650
659
  await lowerFetchCacheRevalidateIfNeeded(handler, cacheKey, cached.value, tags, revalidateSeconds);
651
660
  const cachedData = cached.value.data;
@@ -679,7 +688,7 @@ function createPatchedFetch() {
679
688
  } catch (cacheErr) {
680
689
  console.error("[vinext] fetch cache read error:", cacheErr);
681
690
  }
682
- const response = await dedupeFetch(input, fetchInit);
691
+ const response = await (mustBypassPendingRevalidation ? originalFetch(input, fetchInit) : dedupeFetch(input, fetchInit));
683
692
  const cacheValue = await buildFetchCacheValue(response, tags, revalidateSeconds);
684
693
  if (cacheValue) handler.set(cacheKey, cacheValue, {
685
694
  fetchCache: true,
@@ -1,7 +1,8 @@
1
1
  import { FontStyle } from "./font-utils.js";
2
2
 
3
3
  //#region src/shims/font-google-base.d.ts
4
- type FontOptions = {
4
+ type CssVariable = `--${string}`;
5
+ type FontOptions<T extends CssVariable | undefined = CssVariable | undefined> = {
5
6
  weight?: string | string[];
6
7
  style?: string | string[];
7
8
  subsets?: string[];
@@ -9,7 +10,7 @@ type FontOptions = {
9
10
  preload?: boolean;
10
11
  fallback?: string[];
11
12
  adjustFontFallback?: boolean | string;
12
- variable?: string;
13
+ variable?: T;
13
14
  axes?: string[];
14
15
  };
15
16
  type FontResult = {
@@ -24,7 +25,7 @@ type InternalGoogleFontRuntimeOptions = {
24
25
  fontWeight?: number;
25
26
  fontStyle?: "normal" | "italic";
26
27
  };
27
- type FontLoaderOptions = FontOptions & {
28
+ type FontLoaderOptions<T extends CssVariable | undefined = CssVariable | undefined> = FontOptions<T> & {
28
29
  /**
29
30
  * Internal payload injected by the vinext:google-fonts transform after
30
31
  * metadata validation. Runtime must prefer these values over user options
@@ -76,7 +77,13 @@ declare function getSSRFontPreloads(): Array<{
76
77
  href: string;
77
78
  type: string;
78
79
  }>;
79
- type FontLoader = (options?: FontLoaderOptions) => FontResult;
80
+ type NextFont = Omit<FontResult, "variable"> & {
81
+ variable?: undefined;
82
+ };
83
+ type NextFontWithVariable = Omit<NextFont, "variable"> & {
84
+ variable: string;
85
+ };
86
+ type FontLoader = <T extends CssVariable | undefined = undefined>(options?: FontLoaderOptions<T>) => T extends undefined ? NextFont : NextFontWithVariable;
80
87
  declare function createFontLoader(family: string): FontLoader;
81
88
  declare const googleFonts: Record<string, FontLoader>;
82
89
  //#endregion
@@ -19,7 +19,16 @@ import { buildGoogleFontsUrl as buildGoogleFontsUrl$1 } from "../build/google-fo
19
19
  * // inter.style -> { fontFamily: "'Inter', 'Inter Fallback'", fontStyle: "normal" }
20
20
  * // inter.variable -> CSS class that sets the font CSS variable when requested
21
21
  */
22
- const injectedFonts = /* @__PURE__ */ new Set();
22
+ const _INJECTED_FONTS_KEY = Symbol.for("vinext.font.injectedFonts");
23
+ const _INJECTED_CLASS_RULES_KEY = Symbol.for("vinext.font.injectedClassRules");
24
+ const _INJECTED_VARIABLE_RULES_KEY = Symbol.for("vinext.font.injectedVariableRules");
25
+ const _INJECTED_SELF_HOSTED_KEY = Symbol.for("vinext.font.injectedSelfHosted");
26
+ const _SSR_FONT_STYLES_KEY = Symbol.for("vinext.font.ssrFontStyles");
27
+ const _SSR_FONT_URLS_KEY = Symbol.for("vinext.font.ssrFontUrls");
28
+ const _SSR_FONT_PRELOADS_KEY = Symbol.for("vinext.font.ssrFontPreloads");
29
+ const _SSR_FONT_PRELOAD_HREFS_KEY = Symbol.for("vinext.font.ssrFontPreloadHrefs");
30
+ const _g = globalThis;
31
+ const injectedFonts = _g[_INJECTED_FONTS_KEY] ??= /* @__PURE__ */ new Set();
23
32
  /**
24
33
  * Convert a font family name to a CSS variable name.
25
34
  * e.g., "Inter" -> "--font-inter", "Roboto Mono" -> "--font-roboto-mono"
@@ -128,7 +137,7 @@ function injectFontStylesheet(url) {
128
137
  }
129
138
  }
130
139
  /** Track which className CSS rules have been injected. */
131
- const injectedClassRules = /* @__PURE__ */ new Set();
140
+ const injectedClassRules = _g[_INJECTED_CLASS_RULES_KEY] ??= /* @__PURE__ */ new Set();
132
141
  /**
133
142
  * Inject a CSS rule that maps a className to the exported font style.
134
143
  *
@@ -152,7 +161,7 @@ function injectClassNameRule(className, fontStyle) {
152
161
  document.head.appendChild(styleElement);
153
162
  }
154
163
  /** Track which variable class CSS rules have been injected. */
155
- const injectedVariableRules = /* @__PURE__ */ new Set();
164
+ const injectedVariableRules = _g[_INJECTED_VARIABLE_RULES_KEY] ??= /* @__PURE__ */ new Set();
156
165
  /**
157
166
  * Inject a CSS rule that sets a CSS variable on an element.
158
167
  * This is what makes `<html className={inter.variable}>` set the CSS variable
@@ -177,7 +186,7 @@ function injectVariableClassRule(variableClassName, cssVarName, fontFamily) {
177
186
  style.setAttribute("data-vinext-font-variable", variableClassName);
178
187
  document.head.appendChild(style);
179
188
  }
180
- const ssrFontStyles = [];
189
+ const ssrFontStyles = _g[_SSR_FONT_STYLES_KEY] ??= [];
181
190
  /**
182
191
  * Get collected SSR font class styles (used by the renderer).
183
192
  * Note: We don't clear the arrays because fonts are loaded at module import
@@ -186,7 +195,7 @@ const ssrFontStyles = [];
186
195
  function getSSRFontStyles() {
187
196
  return [...ssrFontStyles];
188
197
  }
189
- const ssrFontUrls = [];
198
+ const ssrFontUrls = _g[_SSR_FONT_URLS_KEY] ??= [];
190
199
  /**
191
200
  * Get collected SSR font URLs (used by the renderer).
192
201
  * Note: We don't clear the arrays because fonts are loaded at module import
@@ -195,8 +204,8 @@ const ssrFontUrls = [];
195
204
  function getSSRFontLinks() {
196
205
  return [...ssrFontUrls];
197
206
  }
198
- const ssrFontPreloads = [];
199
- const ssrFontPreloadHrefs = /* @__PURE__ */ new Set();
207
+ const ssrFontPreloads = _g[_SSR_FONT_PRELOADS_KEY] ??= [];
208
+ const ssrFontPreloadHrefs = _g[_SSR_FONT_PRELOAD_HREFS_KEY] ??= /* @__PURE__ */ new Set();
200
209
  /**
201
210
  * Get collected SSR font preload data (used by the renderer).
202
211
  * Returns an array of { href, type } objects for emitting
@@ -220,7 +229,7 @@ function collectFontPreloads(urls) {
220
229
  }
221
230
  }
222
231
  /** Track injected self-hosted @font-face blocks (deduplicate) */
223
- const injectedSelfHosted = /* @__PURE__ */ new Set();
232
+ const injectedSelfHosted = _g[_INJECTED_SELF_HOSTED_KEY] ??= /* @__PURE__ */ new Set();
224
233
  /**
225
234
  * Inject self-hosted @font-face CSS (from the build plugin).
226
235
  * This replaces the CDN <link> tag with inline CSS.
@@ -6,14 +6,15 @@ type LocalFontSrc = {
6
6
  weight?: string;
7
7
  style?: string;
8
8
  };
9
- type LocalFontOptions = {
9
+ type CssVariable = `--${string}`;
10
+ type LocalFontOptions<T extends CssVariable | undefined = CssVariable | undefined> = {
10
11
  src: string | LocalFontSrc | LocalFontSrc[];
11
12
  display?: string;
12
13
  weight?: string;
13
14
  style?: string;
14
15
  fallback?: string[];
15
16
  preload?: boolean;
16
- variable?: string;
17
+ variable?: T;
17
18
  adjustFontFallback?: boolean | string;
18
19
  declarations?: Array<{
19
20
  prop: string;
@@ -30,6 +31,12 @@ type FontResult = {
30
31
  style: FontStyle;
31
32
  variable?: string;
32
33
  };
34
+ type NextFont = Omit<FontResult, "variable"> & {
35
+ variable?: undefined;
36
+ };
37
+ type NextFontWithVariable = Omit<NextFont, "variable"> & {
38
+ variable: string;
39
+ };
33
40
  /**
34
41
  * Get collected SSR font styles (used by the renderer).
35
42
  * Note: We don't clear the arrays because fonts are loaded at module import
@@ -45,6 +52,6 @@ declare function getSSRFontPreloads(): Array<{
45
52
  href: string;
46
53
  type: string;
47
54
  }>;
48
- declare function localFont(options: LocalFontOptions): FontResult;
55
+ declare function localFont<T extends CssVariable | undefined = undefined>(options: LocalFontOptions<T>): T extends undefined ? NextFont : NextFontWithVariable;
49
56
  //#endregion
50
57
  export { localFont as default, getSSRFontPreloads, getSSRFontStyles };
@@ -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;
@@ -28,6 +28,17 @@ declare function resetSSRHead(): void;
28
28
  * Pass an empty array (or simply don't call this) to skip the merge.
29
29
  */
30
30
  declare function setDocumentInitialHead(head: React.ReactNode[]): void;
31
+ /**
32
+ * Default head tags emitted alongside every Pages Router render — charset
33
+ * first, then viewport. Mirrors Next.js's `defaultHead()` in
34
+ * `packages/next/src/shared/lib/head.tsx`, which seeds the head array used
35
+ * by `HeadManagerContext` before any user `<Head>` reduces over it.
36
+ *
37
+ * The canonical Next.js order is `<meta charset>` then `<meta viewport>`
38
+ * then user tags, all with `data-next-head=""`. See assertion in
39
+ * `test/e2e/next-head/index.test.ts`.
40
+ */
41
+ declare function defaultHead(): React.ReactElement[];
31
42
  /** Get collected head HTML. Call after render. */
32
43
  declare function getSSRHeadHTML(): string;
33
44
  type HeadDOMElement = Pick<HTMLElement, "innerHTML" | "setAttribute" | "textContent">;
@@ -68,6 +79,6 @@ declare function _applyHeadPropsToElement(domEl: HeadDOMElement, props: Record<s
68
79
  declare function _syncClientHead(): void;
69
80
  declare function Head({
70
81
  children
71
- }: HeadProps): null;
82
+ }: HeadProps): React.ReactElement;
72
83
  //#endregion
73
- export { _applyHeadPropsToElement, _clientHeadChildren, _registerHeadStateAccessors, _syncClientHead, Head as default, escapeAttr, escapeInlineContent, getSSRHeadHTML, isSafeAttrName, reduceHeadChildren, resetSSRHead, setDocumentInitialHead };
84
+ export { _applyHeadPropsToElement, _clientHeadChildren, _registerHeadStateAccessors, _syncClientHead, Head as default, defaultHead, escapeAttr, escapeInlineContent, getSSRHeadHTML, isSafeAttrName, reduceHeadChildren, resetSSRHead, setDocumentInitialHead };
@@ -323,7 +323,7 @@ function Head({ children }) {
323
323
  if (headInstanceIdRef.current === null) headInstanceIdRef.current = Symbol("vinext-head");
324
324
  if (typeof window === "undefined") {
325
325
  _getSSRHeadChildren().push(children);
326
- return null;
326
+ return React.createElement(React.Fragment);
327
327
  }
328
328
  useEffect(() => {
329
329
  const instanceId = headInstanceIdRef.current;
@@ -334,7 +334,7 @@ function Head({ children }) {
334
334
  _syncClientHead();
335
335
  };
336
336
  }, [children]);
337
- return null;
337
+ return React.createElement(React.Fragment);
338
338
  }
339
339
  //#endregion
340
- export { _applyHeadPropsToElement, _clientHeadChildren, _registerHeadStateAccessors, _syncClientHead, Head as default, escapeAttr, escapeInlineContent, getSSRHeadHTML, isSafeAttrName, reduceHeadChildren, resetSSRHead, setDocumentInitialHead };
340
+ export { _applyHeadPropsToElement, _clientHeadChildren, _registerHeadStateAccessors, _syncClientHead, Head as default, defaultHead, escapeAttr, escapeInlineContent, getSSRHeadHTML, isSafeAttrName, reduceHeadChildren, resetSSRHead, setDocumentInitialHead };
@@ -1,4 +1,6 @@
1
1
  import { RenderRequestApiKind } from "../server/cache-proof.js";
2
+ import { ReadonlyRequestCookies } from "@vinext/types/next/upstream/dist/server/web/spec-extension/adapters/request-cookies";
3
+ import { ResponseCookie } from "@vinext/types/next/upstream/dist/compiled/@edge-runtime/cookies/index";
2
4
 
3
5
  //#region src/shims/headers.d.ts
4
6
  type HeadersContext = {
@@ -27,6 +29,8 @@ type VinextHeadersShimState = {
27
29
  phase: HeadersAccessPhase;
28
30
  };
29
31
  type ConnectionProbeState = {
32
+ active: boolean;
33
+ dynamicUsageTarget: VinextHeadersShimState;
30
34
  interrupted: boolean;
31
35
  interrupt: () => void;
32
36
  pending: Promise<never>;
@@ -47,6 +51,16 @@ type ConnectionProbeResult<T> = {
47
51
  * Called by connection(), cookies(), headers(), and noStore().
48
52
  */
49
53
  declare function markDynamicUsage(): void;
54
+ /**
55
+ * Measure dynamic usage in a child async scope without clearing the parent.
56
+ * Concurrent work that already belongs to the request (such as deferred
57
+ * metadata) keeps writing to the parent state and therefore remains visible
58
+ * to the final cache policy.
59
+ */
60
+ declare function runWithIsolatedDynamicUsage<T>(fn: () => T | Promise<T>): Promise<{
61
+ result: T;
62
+ dynamicDetected: boolean;
63
+ }>;
50
64
  declare function markRenderRequestApiUsage(kind: RenderRequestApiKind): void;
51
65
  declare function throwIfStaticGenerationAccessError(): void;
52
66
  declare function runWithConnectionProbe<T>(fn: () => T | Promise<T>): Promise<ConnectionProbeResult<T>>;
@@ -162,11 +176,8 @@ declare function headersContextFromRequest(request: Request, options?: HeadersCo
162
176
  * the context is already available).
163
177
  */
164
178
  declare function headers(): Promise<Headers> & Headers;
165
- /**
166
- * Cookie jar from the incoming request.
167
- * Returns a ReadonlyRequestCookies-like object.
168
- */
169
- declare function cookies(): Promise<RequestCookies> & RequestCookies;
179
+ type VinextReadonlyRequestCookies = Omit<ReadonlyRequestCookies, keyof RequestCookies> & RequestCookies;
180
+ declare function cookies(): Promise<VinextReadonlyRequestCookies> & VinextReadonlyRequestCookies;
170
181
  /** Accumulated Set-Cookie headers from cookies().set() / .delete() calls */
171
182
  /**
172
183
  * Get and clear all pending Set-Cookie headers generated by cookies().set()/delete().
@@ -179,6 +190,12 @@ declare function getAndClearPendingCookies(): string[];
179
190
  */
180
191
  declare function getDraftModeCookieHeader(): string | null;
181
192
  declare function isDraftModeRequest(request: Request, draftModeSecret: string): boolean;
193
+ /**
194
+ * Read draft mode from the live request context without recording request API
195
+ * usage. `null` means there is no active request context, which lets framework
196
+ * callers fall back to an explicitly supplied request when needed.
197
+ */
198
+ declare function getActiveDraftModeState(): boolean | null;
182
199
  /**
183
200
  * Read the active request's draft-mode state without recording request API usage.
184
201
  * Internal cache implementations use this to bypass persistent reads and writes,
@@ -205,57 +222,34 @@ type DraftModeResult = {
205
222
  * (`getDraftModeProviderForCacheScope` + `trackDynamicDraftMode`).
206
223
  */
207
224
  declare function draftMode(): Promise<DraftModeResult>;
225
+ declare const APPLY_RESPONSE_COOKIE: unique symbol;
226
+ declare const SYNCHRONIZE_REQUEST_COOKIES: unique symbol;
208
227
  declare class RequestCookies {
209
228
  private _cookies;
210
- constructor(cookies: Map<string, string>);
211
- get(name: string): {
212
- name: string;
213
- value: string;
214
- } | undefined;
229
+ private _responseCookies;
230
+ constructor(cookies: Map<string, string>, mutable?: boolean);
231
+ get(name: string): ResponseCookie | undefined;
215
232
  getAll(nameOrOptions?: string | {
216
233
  name: string;
217
- }): Array<{
218
- name: string;
219
- value: string;
220
- }>;
234
+ }): ResponseCookie[];
221
235
  has(name: string): boolean;
222
236
  /**
223
237
  * Set a cookie. In Route Handlers and Server Actions, this produces
224
238
  * a Set-Cookie header on the response.
225
239
  */
226
- set(nameOrOptions: string | {
227
- name: string;
228
- value: string;
229
- path?: string;
230
- domain?: string;
231
- maxAge?: number;
232
- expires?: Date;
233
- httpOnly?: boolean;
234
- secure?: boolean;
235
- sameSite?: "Strict" | "Lax" | "None";
236
- }, value?: string, options?: {
237
- path?: string;
238
- domain?: string;
239
- maxAge?: number;
240
- expires?: Date;
241
- httpOnly?: boolean;
242
- secure?: boolean;
243
- sameSite?: "Strict" | "Lax" | "None";
244
- }): this;
240
+ set(options: ResponseCookie): this;
241
+ set(key: string, value: string, cookie?: Partial<ResponseCookie>): this;
245
242
  /**
246
243
  * Delete a cookie by emitting an expired Set-Cookie header.
247
244
  */
248
- delete(nameOrOptions: string | {
249
- name: string;
250
- path?: string;
251
- domain?: string;
252
- }): this;
245
+ delete(name: string): this;
246
+ delete(options: Omit<ResponseCookie, "value" | "expires">): this;
253
247
  get size(): number;
254
- [Symbol.iterator](): IterableIterator<[string, {
255
- name: string;
256
- value: string;
257
- }]>;
248
+ [Symbol.iterator](): MapIterator<[string, ResponseCookie]>;
258
249
  toString(): string;
250
+ private _ensureResponseCookies;
251
+ [APPLY_RESPONSE_COOKIE](cookie: ResponseCookie): void;
252
+ [SYNCHRONIZE_REQUEST_COOKIES](): void;
259
253
  }
260
254
  //#endregion
261
- export { HeadersAccessPhase, HeadersContext, type RequestCookies, VinextHeadersShimState, applyMiddlewareRequestHeaders, consumeDynamicUsage, consumeInvalidDynamicUsageError, consumeRenderRequestApiUsage, cookies, draftMode, getAndClearPendingCookies, getDraftModeCookieHeader, getHeadersAccessPhase, getHeadersContext, headers, headersContextFromRequest, isDraftModeEnabled, isDraftModeRequest, markDynamicUsage, markRenderRequestApiUsage, peekDynamicUsage, peekRenderRequestApiUsage, runWithConnectionProbe, runWithHeadersContext, setHeadersAccessPhase, setHeadersContext, suspendConnectionProbe, throwIfInsideCacheScope, throwIfStaticGenerationAccessError };
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 };