vinext 0.0.0 → 0.0.2

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 (272) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/dist/build/static-export.d.ts +78 -0
  4. package/dist/build/static-export.d.ts.map +1 -0
  5. package/dist/build/static-export.js +553 -0
  6. package/dist/build/static-export.js.map +1 -0
  7. package/dist/check.d.ts +52 -0
  8. package/dist/check.d.ts.map +1 -0
  9. package/dist/check.js +483 -0
  10. package/dist/check.js.map +1 -0
  11. package/dist/cli.d.ts +15 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +565 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/client/entry.d.ts +2 -0
  16. package/dist/client/entry.d.ts.map +1 -0
  17. package/dist/client/entry.js +85 -0
  18. package/dist/client/entry.js.map +1 -0
  19. package/dist/cloudflare/index.d.ts +8 -0
  20. package/dist/cloudflare/index.d.ts.map +1 -0
  21. package/dist/cloudflare/index.js +8 -0
  22. package/dist/cloudflare/index.js.map +1 -0
  23. package/dist/cloudflare/kv-cache-handler.d.ts +68 -0
  24. package/dist/cloudflare/kv-cache-handler.d.ts.map +1 -0
  25. package/dist/cloudflare/kv-cache-handler.js +304 -0
  26. package/dist/cloudflare/kv-cache-handler.js.map +1 -0
  27. package/dist/cloudflare/tpr.d.ts +78 -0
  28. package/dist/cloudflare/tpr.d.ts.map +1 -0
  29. package/dist/cloudflare/tpr.js +672 -0
  30. package/dist/cloudflare/tpr.js.map +1 -0
  31. package/dist/config/config-matchers.d.ts +106 -0
  32. package/dist/config/config-matchers.d.ts.map +1 -0
  33. package/dist/config/config-matchers.js +499 -0
  34. package/dist/config/config-matchers.js.map +1 -0
  35. package/dist/config/next-config.d.ts +153 -0
  36. package/dist/config/next-config.d.ts.map +1 -0
  37. package/dist/config/next-config.js +274 -0
  38. package/dist/config/next-config.js.map +1 -0
  39. package/dist/deploy.d.ts +87 -0
  40. package/dist/deploy.d.ts.map +1 -0
  41. package/dist/deploy.js +644 -0
  42. package/dist/deploy.js.map +1 -0
  43. package/dist/index.d.ts +156 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +3296 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/init.d.ts +55 -0
  48. package/dist/init.d.ts.map +1 -0
  49. package/dist/init.js +201 -0
  50. package/dist/init.js.map +1 -0
  51. package/dist/routing/app-router.d.ts +96 -0
  52. package/dist/routing/app-router.d.ts.map +1 -0
  53. package/dist/routing/app-router.js +815 -0
  54. package/dist/routing/app-router.js.map +1 -0
  55. package/dist/routing/pages-router.d.ts +52 -0
  56. package/dist/routing/pages-router.d.ts.map +1 -0
  57. package/dist/routing/pages-router.js +239 -0
  58. package/dist/routing/pages-router.js.map +1 -0
  59. package/dist/server/api-handler.d.ts +18 -0
  60. package/dist/server/api-handler.d.ts.map +1 -0
  61. package/dist/server/api-handler.js +169 -0
  62. package/dist/server/api-handler.js.map +1 -0
  63. package/dist/server/app-dev-server.d.ts +42 -0
  64. package/dist/server/app-dev-server.d.ts.map +1 -0
  65. package/dist/server/app-dev-server.js +2718 -0
  66. package/dist/server/app-dev-server.js.map +1 -0
  67. package/dist/server/app-router-entry.d.ts +18 -0
  68. package/dist/server/app-router-entry.d.ts.map +1 -0
  69. package/dist/server/app-router-entry.js +34 -0
  70. package/dist/server/app-router-entry.js.map +1 -0
  71. package/dist/server/dev-server.d.ts +40 -0
  72. package/dist/server/dev-server.d.ts.map +1 -0
  73. package/dist/server/dev-server.js +758 -0
  74. package/dist/server/dev-server.js.map +1 -0
  75. package/dist/server/html.d.ts +22 -0
  76. package/dist/server/html.d.ts.map +1 -0
  77. package/dist/server/html.js +29 -0
  78. package/dist/server/html.js.map +1 -0
  79. package/dist/server/image-optimization.d.ts +56 -0
  80. package/dist/server/image-optimization.d.ts.map +1 -0
  81. package/dist/server/image-optimization.js +103 -0
  82. package/dist/server/image-optimization.js.map +1 -0
  83. package/dist/server/instrumentation.d.ts +68 -0
  84. package/dist/server/instrumentation.d.ts.map +1 -0
  85. package/dist/server/instrumentation.js +90 -0
  86. package/dist/server/instrumentation.js.map +1 -0
  87. package/dist/server/isr-cache.d.ts +61 -0
  88. package/dist/server/isr-cache.d.ts.map +1 -0
  89. package/dist/server/isr-cache.js +134 -0
  90. package/dist/server/isr-cache.js.map +1 -0
  91. package/dist/server/metadata-routes.d.ts +103 -0
  92. package/dist/server/metadata-routes.d.ts.map +1 -0
  93. package/dist/server/metadata-routes.js +270 -0
  94. package/dist/server/metadata-routes.js.map +1 -0
  95. package/dist/server/middleware.d.ts +77 -0
  96. package/dist/server/middleware.d.ts.map +1 -0
  97. package/dist/server/middleware.js +228 -0
  98. package/dist/server/middleware.js.map +1 -0
  99. package/dist/server/prod-server.d.ts +78 -0
  100. package/dist/server/prod-server.d.ts.map +1 -0
  101. package/dist/server/prod-server.js +712 -0
  102. package/dist/server/prod-server.js.map +1 -0
  103. package/dist/shims/amp.d.ts +17 -0
  104. package/dist/shims/amp.d.ts.map +1 -0
  105. package/dist/shims/amp.js +21 -0
  106. package/dist/shims/amp.js.map +1 -0
  107. package/dist/shims/app.d.ts +12 -0
  108. package/dist/shims/app.d.ts.map +1 -0
  109. package/dist/shims/app.js +2 -0
  110. package/dist/shims/app.js.map +1 -0
  111. package/dist/shims/cache-runtime.d.ts +68 -0
  112. package/dist/shims/cache-runtime.d.ts.map +1 -0
  113. package/dist/shims/cache-runtime.js +437 -0
  114. package/dist/shims/cache-runtime.js.map +1 -0
  115. package/dist/shims/cache.d.ts +243 -0
  116. package/dist/shims/cache.d.ts.map +1 -0
  117. package/dist/shims/cache.js +415 -0
  118. package/dist/shims/cache.js.map +1 -0
  119. package/dist/shims/client-only.d.ts +18 -0
  120. package/dist/shims/client-only.d.ts.map +1 -0
  121. package/dist/shims/client-only.js +18 -0
  122. package/dist/shims/client-only.js.map +1 -0
  123. package/dist/shims/config.d.ts +27 -0
  124. package/dist/shims/config.d.ts.map +1 -0
  125. package/dist/shims/config.js +30 -0
  126. package/dist/shims/config.js.map +1 -0
  127. package/dist/shims/constants.d.ts +13 -0
  128. package/dist/shims/constants.d.ts.map +1 -0
  129. package/dist/shims/constants.js +13 -0
  130. package/dist/shims/constants.js.map +1 -0
  131. package/dist/shims/document.d.ts +33 -0
  132. package/dist/shims/document.d.ts.map +1 -0
  133. package/dist/shims/document.js +32 -0
  134. package/dist/shims/document.js.map +1 -0
  135. package/dist/shims/dynamic.d.ts +33 -0
  136. package/dist/shims/dynamic.d.ts.map +1 -0
  137. package/dist/shims/dynamic.js +149 -0
  138. package/dist/shims/dynamic.js.map +1 -0
  139. package/dist/shims/error-boundary.d.ts +33 -0
  140. package/dist/shims/error-boundary.d.ts.map +1 -0
  141. package/dist/shims/error-boundary.js +88 -0
  142. package/dist/shims/error-boundary.js.map +1 -0
  143. package/dist/shims/error.d.ts +16 -0
  144. package/dist/shims/error.d.ts.map +1 -0
  145. package/dist/shims/error.js +45 -0
  146. package/dist/shims/error.js.map +1 -0
  147. package/dist/shims/fetch-cache.d.ts +61 -0
  148. package/dist/shims/fetch-cache.d.ts.map +1 -0
  149. package/dist/shims/fetch-cache.js +307 -0
  150. package/dist/shims/fetch-cache.js.map +1 -0
  151. package/dist/shims/font-google.d.ts +122 -0
  152. package/dist/shims/font-google.d.ts.map +1 -0
  153. package/dist/shims/font-google.js +387 -0
  154. package/dist/shims/font-google.js.map +1 -0
  155. package/dist/shims/font-local.d.ts +61 -0
  156. package/dist/shims/font-local.d.ts.map +1 -0
  157. package/dist/shims/font-local.js +303 -0
  158. package/dist/shims/font-local.js.map +1 -0
  159. package/dist/shims/form.d.ts +30 -0
  160. package/dist/shims/form.d.ts.map +1 -0
  161. package/dist/shims/form.js +78 -0
  162. package/dist/shims/form.js.map +1 -0
  163. package/dist/shims/head-state.d.ts +11 -0
  164. package/dist/shims/head-state.d.ts.map +1 -0
  165. package/dist/shims/head-state.js +47 -0
  166. package/dist/shims/head-state.js.map +1 -0
  167. package/dist/shims/head.d.ts +28 -0
  168. package/dist/shims/head.d.ts.map +1 -0
  169. package/dist/shims/head.js +148 -0
  170. package/dist/shims/head.js.map +1 -0
  171. package/dist/shims/headers.d.ts +150 -0
  172. package/dist/shims/headers.d.ts.map +1 -0
  173. package/dist/shims/headers.js +412 -0
  174. package/dist/shims/headers.js.map +1 -0
  175. package/dist/shims/image-config.d.ts +30 -0
  176. package/dist/shims/image-config.d.ts.map +1 -0
  177. package/dist/shims/image-config.js +91 -0
  178. package/dist/shims/image-config.js.map +1 -0
  179. package/dist/shims/image.d.ts +63 -0
  180. package/dist/shims/image.d.ts.map +1 -0
  181. package/dist/shims/image.js +284 -0
  182. package/dist/shims/image.js.map +1 -0
  183. package/dist/shims/internal/api-utils.d.ts +12 -0
  184. package/dist/shims/internal/api-utils.d.ts.map +1 -0
  185. package/dist/shims/internal/api-utils.js +7 -0
  186. package/dist/shims/internal/api-utils.js.map +1 -0
  187. package/dist/shims/internal/app-router-context.d.ts +21 -0
  188. package/dist/shims/internal/app-router-context.d.ts.map +1 -0
  189. package/dist/shims/internal/app-router-context.js +15 -0
  190. package/dist/shims/internal/app-router-context.js.map +1 -0
  191. package/dist/shims/internal/cookies.d.ts +9 -0
  192. package/dist/shims/internal/cookies.d.ts.map +1 -0
  193. package/dist/shims/internal/cookies.js +9 -0
  194. package/dist/shims/internal/cookies.js.map +1 -0
  195. package/dist/shims/internal/router-context.d.ts +2 -0
  196. package/dist/shims/internal/router-context.d.ts.map +1 -0
  197. package/dist/shims/internal/router-context.js +9 -0
  198. package/dist/shims/internal/router-context.js.map +1 -0
  199. package/dist/shims/internal/utils.d.ts +48 -0
  200. package/dist/shims/internal/utils.d.ts.map +1 -0
  201. package/dist/shims/internal/utils.js +35 -0
  202. package/dist/shims/internal/utils.js.map +1 -0
  203. package/dist/shims/internal/work-unit-async-storage.d.ts +12 -0
  204. package/dist/shims/internal/work-unit-async-storage.d.ts.map +1 -0
  205. package/dist/shims/internal/work-unit-async-storage.js +13 -0
  206. package/dist/shims/internal/work-unit-async-storage.js.map +1 -0
  207. package/dist/shims/layout-segment-context.d.ts +21 -0
  208. package/dist/shims/layout-segment-context.d.ts.map +1 -0
  209. package/dist/shims/layout-segment-context.js +27 -0
  210. package/dist/shims/layout-segment-context.js.map +1 -0
  211. package/dist/shims/legacy-image.d.ts +52 -0
  212. package/dist/shims/legacy-image.d.ts.map +1 -0
  213. package/dist/shims/legacy-image.js +46 -0
  214. package/dist/shims/legacy-image.js.map +1 -0
  215. package/dist/shims/link.d.ts +48 -0
  216. package/dist/shims/link.d.ts.map +1 -0
  217. package/dist/shims/link.js +395 -0
  218. package/dist/shims/link.js.map +1 -0
  219. package/dist/shims/metadata.d.ts +184 -0
  220. package/dist/shims/metadata.d.ts.map +1 -0
  221. package/dist/shims/metadata.js +472 -0
  222. package/dist/shims/metadata.js.map +1 -0
  223. package/dist/shims/navigation-state.d.ts +14 -0
  224. package/dist/shims/navigation-state.d.ts.map +1 -0
  225. package/dist/shims/navigation-state.js +77 -0
  226. package/dist/shims/navigation-state.js.map +1 -0
  227. package/dist/shims/navigation.d.ts +201 -0
  228. package/dist/shims/navigation.d.ts.map +1 -0
  229. package/dist/shims/navigation.js +672 -0
  230. package/dist/shims/navigation.js.map +1 -0
  231. package/dist/shims/og.d.ts +20 -0
  232. package/dist/shims/og.d.ts.map +1 -0
  233. package/dist/shims/og.js +19 -0
  234. package/dist/shims/og.js.map +1 -0
  235. package/dist/shims/router-state.d.ts +11 -0
  236. package/dist/shims/router-state.d.ts.map +1 -0
  237. package/dist/shims/router-state.js +56 -0
  238. package/dist/shims/router-state.js.map +1 -0
  239. package/dist/shims/router.d.ts +103 -0
  240. package/dist/shims/router.d.ts.map +1 -0
  241. package/dist/shims/router.js +536 -0
  242. package/dist/shims/router.js.map +1 -0
  243. package/dist/shims/script.d.ts +58 -0
  244. package/dist/shims/script.d.ts.map +1 -0
  245. package/dist/shims/script.js +163 -0
  246. package/dist/shims/script.js.map +1 -0
  247. package/dist/shims/server-only.d.ts +19 -0
  248. package/dist/shims/server-only.d.ts.map +1 -0
  249. package/dist/shims/server-only.js +19 -0
  250. package/dist/shims/server-only.js.map +1 -0
  251. package/dist/shims/server.d.ts +178 -0
  252. package/dist/shims/server.d.ts.map +1 -0
  253. package/dist/shims/server.js +377 -0
  254. package/dist/shims/server.js.map +1 -0
  255. package/dist/shims/web-vitals.d.ts +24 -0
  256. package/dist/shims/web-vitals.d.ts.map +1 -0
  257. package/dist/shims/web-vitals.js +17 -0
  258. package/dist/shims/web-vitals.js.map +1 -0
  259. package/dist/utils/hash.d.ts +6 -0
  260. package/dist/utils/hash.d.ts.map +1 -0
  261. package/dist/utils/hash.js +20 -0
  262. package/dist/utils/hash.js.map +1 -0
  263. package/dist/utils/project.d.ts +36 -0
  264. package/dist/utils/project.d.ts.map +1 -0
  265. package/dist/utils/project.js +112 -0
  266. package/dist/utils/project.js.map +1 -0
  267. package/dist/utils/query.d.ts +10 -0
  268. package/dist/utils/query.d.ts.map +1 -0
  269. package/dist/utils/query.js +27 -0
  270. package/dist/utils/query.js.map +1 -0
  271. package/package.json +65 -7
  272. package/index.js +0 -1
@@ -0,0 +1,303 @@
1
+ /**
2
+ * next/font/local shim
3
+ *
4
+ * Provides a runtime-compatible shim for Next.js local fonts.
5
+ * Generates @font-face CSS declarations and returns an object
6
+ * with className, style, and variable properties.
7
+ *
8
+ * Supports both client-side injection and SSR collection,
9
+ * matching the patterns used by the Google font shim.
10
+ *
11
+ * Usage:
12
+ * import localFont from 'next/font/local';
13
+ * const myFont = localFont({ src: './my-font.woff2' });
14
+ * // myFont.className -> unique CSS class
15
+ * // myFont.style -> { fontFamily: "'__local_font_0', sans-serif" }
16
+ * // myFont.variable -> generated class name (e.g. "__variable_local_0")
17
+ */
18
+ /**
19
+ * Escape a string for safe interpolation inside a CSS single-quoted string.
20
+ *
21
+ * Prevents CSS injection by escaping characters that could break out of
22
+ * a `'...'` CSS string context: backslashes, single quotes, and newlines.
23
+ */
24
+ function escapeCSSString(value) {
25
+ return value
26
+ .replace(/\\/g, "\\\\")
27
+ .replace(/'/g, "\\'")
28
+ .replace(/\n/g, "\\a ")
29
+ .replace(/\r/g, "\\d ");
30
+ }
31
+ /**
32
+ * Validate a CSS custom property name (e.g. `--font-inter`).
33
+ *
34
+ * Custom properties must start with `--` and only contain alphanumeric
35
+ * characters, hyphens, and underscores. Anything else could be used to
36
+ * break out of the CSS declaration and inject arbitrary rules.
37
+ *
38
+ * Returns the name if valid, undefined otherwise.
39
+ */
40
+ function sanitizeCSSVarName(name) {
41
+ if (/^--[a-zA-Z0-9_-]+$/.test(name))
42
+ return name;
43
+ return undefined;
44
+ }
45
+ /**
46
+ * Sanitize a CSS font-family fallback name.
47
+ *
48
+ * Generic family names (sans-serif, serif, monospace, etc.) are used as-is.
49
+ * Named families are wrapped in escaped quotes. This prevents injection via
50
+ * crafted fallback values like `); } body { color: red; } .x {`.
51
+ */
52
+ function sanitizeFallback(name) {
53
+ // CSS generic font families — safe to use unquoted
54
+ const generics = new Set([
55
+ "serif", "sans-serif", "monospace", "cursive", "fantasy",
56
+ "system-ui", "ui-serif", "ui-sans-serif", "ui-monospace", "ui-rounded",
57
+ "emoji", "math", "fangsong",
58
+ ]);
59
+ const trimmed = name.trim();
60
+ if (generics.has(trimmed))
61
+ return trimmed;
62
+ // Wrap in single quotes with escaping to prevent CSS injection
63
+ return `'${escapeCSSString(trimmed)}'`;
64
+ }
65
+ /**
66
+ * Validate a CSS property name for use in declarations.
67
+ *
68
+ * Only allows standard CSS property names (lowercase letters and hyphens)
69
+ * and custom properties (--prefixed). Rejects anything that could inject
70
+ * CSS rules via crafted property names.
71
+ */
72
+ function sanitizeCSSProperty(prop) {
73
+ if (/^(--)?[a-zA-Z][a-zA-Z0-9-]*$/.test(prop))
74
+ return prop;
75
+ return undefined;
76
+ }
77
+ /**
78
+ * Sanitize a CSS property value for use in declarations.
79
+ *
80
+ * Rejects values containing characters that could break out of a CSS
81
+ * declaration: `{`, `}`, `;`, and `</` (to prevent closing style tags).
82
+ */
83
+ function sanitizeCSSValue(value) {
84
+ if (/[{}]|<\//.test(value))
85
+ return undefined;
86
+ return value;
87
+ }
88
+ let classCounter = 0;
89
+ const injectedFonts = new Set();
90
+ function generateFontFaceCSS(family, options) {
91
+ const sources = normalizeSources(options);
92
+ const display = options.display ?? "swap";
93
+ const rules = [];
94
+ for (const src of sources) {
95
+ const weight = src.weight ?? options.weight ?? "400";
96
+ const style = src.style ?? options.style ?? "normal";
97
+ const format = src.path.endsWith(".woff2")
98
+ ? "woff2"
99
+ : src.path.endsWith(".woff")
100
+ ? "woff"
101
+ : src.path.endsWith(".ttf")
102
+ ? "truetype"
103
+ : src.path.endsWith(".otf")
104
+ ? "opentype"
105
+ : "woff2";
106
+ rules.push(`@font-face {
107
+ font-family: '${escapeCSSString(family)}';
108
+ src: url('${escapeCSSString(src.path)}') format('${format}');
109
+ font-weight: ${weight};
110
+ font-style: ${style};
111
+ font-display: ${display};
112
+ }`);
113
+ }
114
+ // Add extra declarations if provided — sanitize prop/value to prevent injection
115
+ if (options.declarations) {
116
+ for (const decl of options.declarations) {
117
+ const safeProp = sanitizeCSSProperty(decl.prop);
118
+ const safeValue = sanitizeCSSValue(decl.value);
119
+ if (safeProp && safeValue) {
120
+ rules.push(`@font-face { font-family: '${escapeCSSString(family)}'; ${safeProp}: ${safeValue}; }`);
121
+ }
122
+ }
123
+ }
124
+ return rules.join("\n");
125
+ }
126
+ // SSR: collect font styles for injection in <head>
127
+ const ssrFontStyles = [];
128
+ // SSR: collect font file URLs for <link rel="preload"> injection
129
+ const ssrFontPreloads = [];
130
+ const ssrFontPreloadHrefs = new Set();
131
+ /**
132
+ * Get collected SSR font styles (used by the renderer).
133
+ * Note: We don't clear the arrays because fonts are loaded at module import
134
+ * time and need to persist across all requests in the Workers environment.
135
+ */
136
+ export function getSSRFontStyles() {
137
+ return [...ssrFontStyles];
138
+ }
139
+ /**
140
+ * Get collected SSR font preload data (used by the renderer).
141
+ * Returns an array of { href, type } objects for emitting
142
+ * <link rel="preload" as="font" ...> tags.
143
+ */
144
+ export function getSSRFontPreloads() {
145
+ return [...ssrFontPreloads];
146
+ }
147
+ function injectFontFaceCSS(css, id) {
148
+ if (injectedFonts.has(id))
149
+ return;
150
+ injectedFonts.add(id);
151
+ // On server, store the CSS for SSR injection
152
+ if (typeof document === "undefined") {
153
+ ssrFontStyles.push(css);
154
+ return;
155
+ }
156
+ const style = document.createElement("style");
157
+ style.textContent = css;
158
+ style.setAttribute("data-vinext-font", id);
159
+ document.head.appendChild(style);
160
+ }
161
+ /** Track which className CSS rules have been injected. */
162
+ const injectedClassRules = new Set();
163
+ /**
164
+ * Inject a CSS rule that maps a className to a font-family.
165
+ *
166
+ * This is what makes `<div className={font.className}>` apply the font.
167
+ *
168
+ * In Next.js, the .className class ONLY sets font-family — it does NOT
169
+ * set CSS variables. CSS variables are handled separately by the .variable class.
170
+ */
171
+ function injectClassNameRule(className, fontFamily) {
172
+ if (injectedClassRules.has(className))
173
+ return;
174
+ injectedClassRules.add(className);
175
+ const css = `.${className} { font-family: ${fontFamily}; }\n`;
176
+ // On server, store the CSS for SSR injection
177
+ if (typeof document === "undefined") {
178
+ ssrFontStyles.push(css);
179
+ return;
180
+ }
181
+ // On client, inject a <style> tag
182
+ const style = document.createElement("style");
183
+ style.textContent = css;
184
+ style.setAttribute("data-vinext-font-class", className);
185
+ document.head.appendChild(style);
186
+ }
187
+ /** Track which variable class CSS rules have been injected. */
188
+ const injectedVariableRules = new Set();
189
+ /** Track which :root CSS variable rules have been injected. */
190
+ const injectedRootVariables = new Set();
191
+ /**
192
+ * Inject a CSS rule that sets a CSS variable on an element.
193
+ * This is what makes `<html className={font.variable}>` set the CSS variable
194
+ * that can be referenced by other styles (e.g., Tailwind's font-sans).
195
+ *
196
+ * In Next.js, the .variable class ONLY sets the CSS variable — it does NOT
197
+ * set font-family. This is critical because apps commonly apply multiple
198
+ * .variable classes to <body> (e.g., geistSans.variable + geistMono.variable).
199
+ * If we also set font-family here, the last class wins due to CSS cascade,
200
+ * causing all text to use that font (e.g., everything becomes monospace).
201
+ */
202
+ function injectVariableClassRule(variableClassName, cssVarName, fontFamily) {
203
+ if (injectedVariableRules.has(variableClassName))
204
+ return;
205
+ injectedVariableRules.add(variableClassName);
206
+ // Only set the CSS variable — do NOT set font-family.
207
+ // This matches Next.js behavior where .variable classes only define CSS variables.
208
+ let css = `.${variableClassName} { ${cssVarName}: ${fontFamily}; }\n`;
209
+ // Also inject at :root so CSS variable inheritance works throughout the page.
210
+ // This ensures Tailwind utilities like `font-sans` that reference these
211
+ // variables via var(--font-geist-sans) work correctly.
212
+ if (!injectedRootVariables.has(cssVarName)) {
213
+ injectedRootVariables.add(cssVarName);
214
+ css += `:root { ${cssVarName}: ${fontFamily}; }\n`;
215
+ }
216
+ // On server, store the CSS for SSR injection
217
+ if (typeof document === "undefined") {
218
+ ssrFontStyles.push(css);
219
+ return;
220
+ }
221
+ // On client, inject a <style> tag
222
+ const style = document.createElement("style");
223
+ style.textContent = css;
224
+ style.setAttribute("data-vinext-font-variable", variableClassName);
225
+ document.head.appendChild(style);
226
+ }
227
+ /**
228
+ * Normalize the `src` option into a flat array of `{ path, weight?, style? }`.
229
+ * Handles string, single object, and array forms.
230
+ */
231
+ function normalizeSources(options) {
232
+ if (Array.isArray(options.src))
233
+ return options.src;
234
+ if (typeof options.src === "string")
235
+ return [{ path: options.src }];
236
+ return [options.src];
237
+ }
238
+ /**
239
+ * Determine the MIME type for a font file based on its extension.
240
+ * Uses endsWith() only — matching the approach in generateFontFaceCSS —
241
+ * to avoid false positives from substring matches (e.g. ".woff" matching ".woff2").
242
+ */
243
+ function getFontMimeType(pathOrUrl) {
244
+ if (pathOrUrl.endsWith(".woff2"))
245
+ return "font/woff2";
246
+ if (pathOrUrl.endsWith(".woff"))
247
+ return "font/woff";
248
+ if (pathOrUrl.endsWith(".ttf"))
249
+ return "font/ttf";
250
+ if (pathOrUrl.endsWith(".otf"))
251
+ return "font/opentype";
252
+ return "font/woff2";
253
+ }
254
+ /**
255
+ * Collect font source URLs for preload link generation.
256
+ * Only collects on the server (SSR). Deduplicates by href using a Set for O(1) lookups.
257
+ */
258
+ function collectFontPreloads(options) {
259
+ if (typeof document !== "undefined")
260
+ return; // client-side, skip
261
+ const sources = normalizeSources(options);
262
+ for (const src of sources) {
263
+ const href = src.path;
264
+ // Only collect URLs that are absolute (start with /) — relative paths
265
+ // would resolve incorrectly from different page URLs. The vinext:local-fonts
266
+ // Vite transform should have already resolved them to absolute URLs.
267
+ if (href && href.startsWith("/") && !ssrFontPreloadHrefs.has(href)) {
268
+ ssrFontPreloadHrefs.add(href);
269
+ ssrFontPreloads.push({ href, type: getFontMimeType(href) });
270
+ }
271
+ }
272
+ }
273
+ export default function localFont(options) {
274
+ const id = classCounter++;
275
+ const family = `__local_font_${id}`;
276
+ const className = `__font_local_${id}`;
277
+ const fallback = options.fallback ?? ["sans-serif"];
278
+ // Sanitize each fallback name to prevent CSS injection via crafted values
279
+ const fontFamily = `'${family}', ${fallback.map(sanitizeFallback).join(", ")}`;
280
+ // Validate CSS variable name — reject anything that could inject CSS
281
+ const cssVarName = options.variable ? sanitizeCSSVarName(options.variable) : undefined;
282
+ // In Next.js, `variable` returns a CLASS NAME that sets the CSS variable.
283
+ // Users apply this class to set the CSS variable on that element.
284
+ const variableClassName = `__variable_local_${id}`;
285
+ // Collect font URLs for preload <link> tags (SSR only)
286
+ collectFontPreloads(options);
287
+ // Inject @font-face declarations
288
+ const css = generateFontFaceCSS(family, options);
289
+ injectFontFaceCSS(css, family);
290
+ // Inject the className -> font-family CSS rule
291
+ injectClassNameRule(className, fontFamily);
292
+ // Inject a CSS rule for the variable class name if variable is specified.
293
+ // This is what makes `<html className={font.variable}>` set the CSS variable.
294
+ if (cssVarName) {
295
+ injectVariableClassRule(variableClassName, cssVarName, fontFamily);
296
+ }
297
+ return {
298
+ className,
299
+ style: { fontFamily },
300
+ ...(cssVarName ? { variable: variableClassName } : {}),
301
+ };
302
+ }
303
+ //# sourceMappingURL=font-local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-local.js","sourceRoot":"","sources":["../../src/shims/font-local.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * next/font/local shim\n *\n * Provides a runtime-compatible shim for Next.js local fonts.\n * Generates @font-face CSS declarations and returns an object\n * with className, style, and variable properties.\n *\n * Supports both client-side injection and SSR collection,\n * matching the patterns used by the Google font shim.\n *\n * Usage:\n * import localFont from 'next/font/local';\n * const myFont = localFont({ src: './my-font.woff2' });\n * // myFont.className -> unique CSS class\n * // myFont.style -> { fontFamily: \"'__local_font_0', sans-serif\" }\n * // myFont.variable -> generated class name (e.g. \"__variable_local_0\")\n */\n\n/**\n * Escape a string for safe interpolation inside a CSS single-quoted string.\n *\n * Prevents CSS injection by escaping characters that could break out of\n * a `'...'` CSS string context: backslashes, single quotes, and newlines.\n */\nfunction escapeCSSString(value: string): string {\n return value\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/'/g, \"\\\\'\")\n .replace(/\\n/g, \"\\\\a \")\n .replace(/\\r/g, \"\\\\d \");\n}\n\n/**\n * Validate a CSS custom property name (e.g. `--font-inter`).\n *\n * Custom properties must start with `--` and only contain alphanumeric\n * characters, hyphens, and underscores. Anything else could be used to\n * break out of the CSS declaration and inject arbitrary rules.\n *\n * Returns the name if valid, undefined otherwise.\n */\nfunction sanitizeCSSVarName(name: string): string | undefined {\n if (/^--[a-zA-Z0-9_-]+$/.test(name)) return name;\n return undefined;\n}\n\n/**\n * Sanitize a CSS font-family fallback name.\n *\n * Generic family names (sans-serif, serif, monospace, etc.) are used as-is.\n * Named families are wrapped in escaped quotes. This prevents injection via\n * crafted fallback values like `); } body { color: red; } .x {`.\n */\nfunction sanitizeFallback(name: string): string {\n // CSS generic font families — safe to use unquoted\n const generics = new Set([\n \"serif\", \"sans-serif\", \"monospace\", \"cursive\", \"fantasy\",\n \"system-ui\", \"ui-serif\", \"ui-sans-serif\", \"ui-monospace\", \"ui-rounded\",\n \"emoji\", \"math\", \"fangsong\",\n ]);\n const trimmed = name.trim();\n if (generics.has(trimmed)) return trimmed;\n // Wrap in single quotes with escaping to prevent CSS injection\n return `'${escapeCSSString(trimmed)}'`;\n}\n\n/**\n * Validate a CSS property name for use in declarations.\n *\n * Only allows standard CSS property names (lowercase letters and hyphens)\n * and custom properties (--prefixed). Rejects anything that could inject\n * CSS rules via crafted property names.\n */\nfunction sanitizeCSSProperty(prop: string): string | undefined {\n if (/^(--)?[a-zA-Z][a-zA-Z0-9-]*$/.test(prop)) return prop;\n return undefined;\n}\n\n/**\n * Sanitize a CSS property value for use in declarations.\n *\n * Rejects values containing characters that could break out of a CSS\n * declaration: `{`, `}`, `;`, and `</` (to prevent closing style tags).\n */\nfunction sanitizeCSSValue(value: string): string | undefined {\n if (/[{}]|<\\//.test(value)) return undefined;\n return value;\n}\n\nlet classCounter = 0;\nconst injectedFonts = new Set<string>();\n\ninterface LocalFontSrc {\n path: string;\n weight?: string;\n style?: string;\n}\n\ninterface LocalFontOptions {\n src: string | LocalFontSrc | LocalFontSrc[];\n display?: string;\n weight?: string;\n style?: string;\n fallback?: string[];\n preload?: boolean;\n variable?: string;\n adjustFontFallback?: boolean | string;\n declarations?: Array<{ prop: string; value: string }>;\n}\n\ninterface FontResult {\n className: string;\n style: { fontFamily: string };\n variable?: string;\n}\n\nfunction generateFontFaceCSS(\n family: string,\n options: LocalFontOptions,\n): string {\n const sources = normalizeSources(options);\n\n const display = options.display ?? \"swap\";\n const rules: string[] = [];\n\n for (const src of sources) {\n const weight = src.weight ?? options.weight ?? \"400\";\n const style = src.style ?? options.style ?? \"normal\";\n const format = src.path.endsWith(\".woff2\")\n ? \"woff2\"\n : src.path.endsWith(\".woff\")\n ? \"woff\"\n : src.path.endsWith(\".ttf\")\n ? \"truetype\"\n : src.path.endsWith(\".otf\")\n ? \"opentype\"\n : \"woff2\";\n\n rules.push(`@font-face {\n font-family: '${escapeCSSString(family)}';\n src: url('${escapeCSSString(src.path)}') format('${format}');\n font-weight: ${weight};\n font-style: ${style};\n font-display: ${display};\n}`);\n }\n\n // Add extra declarations if provided — sanitize prop/value to prevent injection\n if (options.declarations) {\n for (const decl of options.declarations) {\n const safeProp = sanitizeCSSProperty(decl.prop);\n const safeValue = sanitizeCSSValue(decl.value);\n if (safeProp && safeValue) {\n rules.push(`@font-face { font-family: '${escapeCSSString(family)}'; ${safeProp}: ${safeValue}; }`);\n }\n }\n }\n\n return rules.join(\"\\n\");\n}\n\n// SSR: collect font styles for injection in <head>\nconst ssrFontStyles: string[] = [];\n\n// SSR: collect font file URLs for <link rel=\"preload\"> injection\nconst ssrFontPreloads: Array<{ href: string; type: string }> = [];\nconst ssrFontPreloadHrefs = new Set<string>();\n\n/**\n * Get collected SSR font styles (used by the renderer).\n * Note: We don't clear the arrays because fonts are loaded at module import\n * time and need to persist across all requests in the Workers environment.\n */\nexport function getSSRFontStyles(): string[] {\n return [...ssrFontStyles];\n}\n\n/**\n * Get collected SSR font preload data (used by the renderer).\n * Returns an array of { href, type } objects for emitting\n * <link rel=\"preload\" as=\"font\" ...> tags.\n */\nexport function getSSRFontPreloads(): Array<{ href: string; type: string }> {\n return [...ssrFontPreloads];\n}\n\nfunction injectFontFaceCSS(css: string, id: string): void {\n if (injectedFonts.has(id)) return;\n injectedFonts.add(id);\n\n // On server, store the CSS for SSR injection\n if (typeof document === \"undefined\") {\n ssrFontStyles.push(css);\n return;\n }\n\n const style = document.createElement(\"style\");\n style.textContent = css;\n style.setAttribute(\"data-vinext-font\", id);\n document.head.appendChild(style);\n}\n\n/** Track which className CSS rules have been injected. */\nconst injectedClassRules = new Set<string>();\n\n/**\n * Inject a CSS rule that maps a className to a font-family.\n *\n * This is what makes `<div className={font.className}>` apply the font.\n *\n * In Next.js, the .className class ONLY sets font-family — it does NOT\n * set CSS variables. CSS variables are handled separately by the .variable class.\n */\nfunction injectClassNameRule(\n className: string,\n fontFamily: string,\n): void {\n if (injectedClassRules.has(className)) return;\n injectedClassRules.add(className);\n\n const css = `.${className} { font-family: ${fontFamily}; }\\n`;\n\n // On server, store the CSS for SSR injection\n if (typeof document === \"undefined\") {\n ssrFontStyles.push(css);\n return;\n }\n\n // On client, inject a <style> tag\n const style = document.createElement(\"style\");\n style.textContent = css;\n style.setAttribute(\"data-vinext-font-class\", className);\n document.head.appendChild(style);\n}\n\n/** Track which variable class CSS rules have been injected. */\nconst injectedVariableRules = new Set<string>();\n\n/** Track which :root CSS variable rules have been injected. */\nconst injectedRootVariables = new Set<string>();\n\n/**\n * Inject a CSS rule that sets a CSS variable on an element.\n * This is what makes `<html className={font.variable}>` set the CSS variable\n * that can be referenced by other styles (e.g., Tailwind's font-sans).\n *\n * In Next.js, the .variable class ONLY sets the CSS variable — it does NOT\n * set font-family. This is critical because apps commonly apply multiple\n * .variable classes to <body> (e.g., geistSans.variable + geistMono.variable).\n * If we also set font-family here, the last class wins due to CSS cascade,\n * causing all text to use that font (e.g., everything becomes monospace).\n */\nfunction injectVariableClassRule(\n variableClassName: string,\n cssVarName: string,\n fontFamily: string,\n): void {\n if (injectedVariableRules.has(variableClassName)) return;\n injectedVariableRules.add(variableClassName);\n\n // Only set the CSS variable — do NOT set font-family.\n // This matches Next.js behavior where .variable classes only define CSS variables.\n let css = `.${variableClassName} { ${cssVarName}: ${fontFamily}; }\\n`;\n\n // Also inject at :root so CSS variable inheritance works throughout the page.\n // This ensures Tailwind utilities like `font-sans` that reference these\n // variables via var(--font-geist-sans) work correctly.\n if (!injectedRootVariables.has(cssVarName)) {\n injectedRootVariables.add(cssVarName);\n css += `:root { ${cssVarName}: ${fontFamily}; }\\n`;\n }\n\n // On server, store the CSS for SSR injection\n if (typeof document === \"undefined\") {\n ssrFontStyles.push(css);\n return;\n }\n\n // On client, inject a <style> tag\n const style = document.createElement(\"style\");\n style.textContent = css;\n style.setAttribute(\"data-vinext-font-variable\", variableClassName);\n document.head.appendChild(style);\n}\n\n/**\n * Normalize the `src` option into a flat array of `{ path, weight?, style? }`.\n * Handles string, single object, and array forms.\n */\nfunction normalizeSources(options: LocalFontOptions): LocalFontSrc[] {\n if (Array.isArray(options.src)) return options.src;\n if (typeof options.src === \"string\") return [{ path: options.src }];\n return [options.src];\n}\n\n/**\n * Determine the MIME type for a font file based on its extension.\n * Uses endsWith() only — matching the approach in generateFontFaceCSS —\n * to avoid false positives from substring matches (e.g. \".woff\" matching \".woff2\").\n */\nfunction getFontMimeType(pathOrUrl: string): string {\n if (pathOrUrl.endsWith(\".woff2\")) return \"font/woff2\";\n if (pathOrUrl.endsWith(\".woff\")) return \"font/woff\";\n if (pathOrUrl.endsWith(\".ttf\")) return \"font/ttf\";\n if (pathOrUrl.endsWith(\".otf\")) return \"font/opentype\";\n return \"font/woff2\";\n}\n\n/**\n * Collect font source URLs for preload link generation.\n * Only collects on the server (SSR). Deduplicates by href using a Set for O(1) lookups.\n */\nfunction collectFontPreloads(options: LocalFontOptions): void {\n if (typeof document !== \"undefined\") return; // client-side, skip\n\n const sources = normalizeSources(options);\n\n for (const src of sources) {\n const href = src.path;\n // Only collect URLs that are absolute (start with /) — relative paths\n // would resolve incorrectly from different page URLs. The vinext:local-fonts\n // Vite transform should have already resolved them to absolute URLs.\n if (href && href.startsWith(\"/\") && !ssrFontPreloadHrefs.has(href)) {\n ssrFontPreloadHrefs.add(href);\n ssrFontPreloads.push({ href, type: getFontMimeType(href) });\n }\n }\n}\n\nexport default function localFont(options: LocalFontOptions): FontResult {\n const id = classCounter++;\n const family = `__local_font_${id}`;\n const className = `__font_local_${id}`;\n const fallback = options.fallback ?? [\"sans-serif\"];\n // Sanitize each fallback name to prevent CSS injection via crafted values\n const fontFamily = `'${family}', ${fallback.map(sanitizeFallback).join(\", \")}`;\n // Validate CSS variable name — reject anything that could inject CSS\n const cssVarName = options.variable ? sanitizeCSSVarName(options.variable) : undefined;\n // In Next.js, `variable` returns a CLASS NAME that sets the CSS variable.\n // Users apply this class to set the CSS variable on that element.\n const variableClassName = `__variable_local_${id}`;\n\n // Collect font URLs for preload <link> tags (SSR only)\n collectFontPreloads(options);\n\n // Inject @font-face declarations\n const css = generateFontFaceCSS(family, options);\n injectFontFaceCSS(css, family);\n\n // Inject the className -> font-family CSS rule\n injectClassNameRule(className, fontFamily);\n\n // Inject a CSS rule for the variable class name if variable is specified.\n // This is what makes `<html className={font.variable}>` set the CSS variable.\n if (cssVarName) {\n injectVariableClassRule(variableClassName, cssVarName, fontFamily);\n }\n\n return {\n className,\n style: { fontFamily },\n ...(cssVarName ? { variable: variableClassName } : {}),\n };\n}\n"]}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * next/form shim
3
+ *
4
+ * Progressive enhancement form component. In Next.js, this replaces
5
+ * the standard <form> element with one that intercepts submissions
6
+ * and performs client-side navigation for GET forms (search forms).
7
+ *
8
+ * For POST forms with server actions, it delegates to React's built-in
9
+ * form action handling.
10
+ *
11
+ * Usage:
12
+ * import Form from 'next/form';
13
+ * <Form action="/search">
14
+ * <input name="q" />
15
+ * <button type="submit">Search</button>
16
+ * </Form>
17
+ */
18
+ import { useActionState, type FormHTMLAttributes } from "react";
19
+ export { useActionState };
20
+ interface FormProps extends FormHTMLAttributes<HTMLFormElement> {
21
+ /** Target URL for GET forms, or server action for POST forms */
22
+ action: string | ((formData: FormData) => void | Promise<void>);
23
+ /** Replace instead of push in history (default: false) */
24
+ replace?: boolean;
25
+ /** Scroll to top after navigation (default: true) */
26
+ scroll?: boolean;
27
+ }
28
+ declare const Form: import("react").ForwardRefExoticComponent<FormProps & import("react").RefAttributes<HTMLFormElement>>;
29
+ export default Form;
30
+ //# sourceMappingURL=form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../src/shims/form.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAEL,cAAc,EACd,KAAK,kBAAkB,EAExB,MAAM,OAAO,CAAC;AAGf,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,UAAU,SAAU,SAAQ,kBAAkB,CAAC,eAAe,CAAC;IAC7D,gEAAgE;IAChE,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAChE,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qDAAqD;IACrD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,QAAA,MAAM,IAAI,uGAmER,CAAC;AAEH,eAAe,IAAI,CAAC"}
@@ -0,0 +1,78 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /**
4
+ * next/form shim
5
+ *
6
+ * Progressive enhancement form component. In Next.js, this replaces
7
+ * the standard <form> element with one that intercepts submissions
8
+ * and performs client-side navigation for GET forms (search forms).
9
+ *
10
+ * For POST forms with server actions, it delegates to React's built-in
11
+ * form action handling.
12
+ *
13
+ * Usage:
14
+ * import Form from 'next/form';
15
+ * <Form action="/search">
16
+ * <input name="q" />
17
+ * <button type="submit">Search</button>
18
+ * </Form>
19
+ */
20
+ import { forwardRef, useActionState, } from "react";
21
+ // Re-export useActionState from React 19 to match Next.js's next/form module
22
+ export { useActionState };
23
+ const Form = forwardRef(function Form(props, ref) {
24
+ const { action, replace = false, scroll = true, onSubmit, ...rest } = props;
25
+ // If action is a function (server action), pass it directly to React
26
+ if (typeof action === "function") {
27
+ return _jsx("form", { ref: ref, action: action, onSubmit: onSubmit, ...rest });
28
+ }
29
+ async function handleSubmit(e) {
30
+ // Call user's onSubmit first
31
+ if (onSubmit) {
32
+ onSubmit(e);
33
+ if (e.defaultPrevented)
34
+ return;
35
+ }
36
+ // Only intercept GET forms for client-side navigation
37
+ const method = (rest.method ?? "GET").toUpperCase();
38
+ if (method !== "GET")
39
+ return;
40
+ e.preventDefault();
41
+ const formData = new FormData(e.currentTarget);
42
+ const params = new URLSearchParams();
43
+ for (const [key, value] of formData) {
44
+ if (typeof value === "string") {
45
+ params.append(key, value);
46
+ }
47
+ }
48
+ const url = `${action}?${params.toString()}`;
49
+ // Navigate client-side
50
+ const win = window;
51
+ if (typeof win.__VINEXT_RSC_NAVIGATE__ === "function") {
52
+ // App Router: RSC navigation. Await so scroll happens after new content renders.
53
+ if (replace) {
54
+ window.history.replaceState(null, "", url);
55
+ }
56
+ else {
57
+ window.history.pushState(null, "", url);
58
+ }
59
+ await win.__VINEXT_RSC_NAVIGATE__(url);
60
+ }
61
+ else {
62
+ // Pages Router: use router or fallback
63
+ if (replace) {
64
+ window.history.replaceState({}, "", url);
65
+ }
66
+ else {
67
+ window.history.pushState({}, "", url);
68
+ }
69
+ window.dispatchEvent(new PopStateEvent("popstate"));
70
+ }
71
+ if (scroll) {
72
+ window.scrollTo(0, 0);
73
+ }
74
+ }
75
+ return (_jsx("form", { ref: ref, action: action, onSubmit: handleSubmit, ...rest }));
76
+ });
77
+ export default Form;
78
+ //# sourceMappingURL=form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.js","sourceRoot":"","sources":["../../src/shims/form.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * next/form shim\n *\n * Progressive enhancement form component. In Next.js, this replaces\n * the standard <form> element with one that intercepts submissions\n * and performs client-side navigation for GET forms (search forms).\n *\n * For POST forms with server actions, it delegates to React's built-in\n * form action handling.\n *\n * Usage:\n * import Form from 'next/form';\n * <Form action=\"/search\">\n * <input name=\"q\" />\n * <button type=\"submit\">Search</button>\n * </Form>\n */\n\nimport {\n forwardRef,\n useActionState,\n type FormHTMLAttributes,\n type ForwardedRef,\n} from \"react\";\n\n// Re-export useActionState from React 19 to match Next.js's next/form module\nexport { useActionState };\n\ninterface FormProps extends FormHTMLAttributes<HTMLFormElement> {\n /** Target URL for GET forms, or server action for POST forms */\n action: string | ((formData: FormData) => void | Promise<void>);\n /** Replace instead of push in history (default: false) */\n replace?: boolean;\n /** Scroll to top after navigation (default: true) */\n scroll?: boolean;\n}\n\nconst Form = forwardRef(function Form(\n props: FormProps,\n ref: ForwardedRef<HTMLFormElement>,\n) {\n const { action, replace = false, scroll = true, onSubmit, ...rest } = props;\n\n // If action is a function (server action), pass it directly to React\n if (typeof action === \"function\") {\n return <form ref={ref} action={action as any} onSubmit={onSubmit as any} {...rest} />;\n }\n\n async function handleSubmit(e: any) {\n // Call user's onSubmit first\n if (onSubmit) {\n (onSubmit as any)(e);\n if (e.defaultPrevented) return;\n }\n\n // Only intercept GET forms for client-side navigation\n const method = (rest.method ?? \"GET\").toUpperCase();\n if (method !== \"GET\") return;\n\n e.preventDefault();\n\n const formData = new FormData(e.currentTarget);\n const params = new URLSearchParams();\n for (const [key, value] of formData) {\n if (typeof value === \"string\") {\n params.append(key, value);\n }\n }\n\n const url = `${action}?${params.toString()}`;\n\n // Navigate client-side\n const win = window as any;\n if (typeof win.__VINEXT_RSC_NAVIGATE__ === \"function\") {\n // App Router: RSC navigation. Await so scroll happens after new content renders.\n if (replace) {\n window.history.replaceState(null, \"\", url);\n } else {\n window.history.pushState(null, \"\", url);\n }\n await win.__VINEXT_RSC_NAVIGATE__(url);\n } else {\n // Pages Router: use router or fallback\n if (replace) {\n window.history.replaceState({}, \"\", url);\n } else {\n window.history.pushState({}, \"\", url);\n }\n window.dispatchEvent(new PopStateEvent(\"popstate\"));\n }\n\n if (scroll) {\n window.scrollTo(0, 0);\n }\n }\n\n return (\n <form\n ref={ref}\n action={action}\n onSubmit={handleSubmit}\n {...rest}\n />\n );\n});\n\nexport default Form;\n"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Server-only head state backed by AsyncLocalStorage.
3
+ *
4
+ * Provides request-scoped isolation for SSR head elements so concurrent
5
+ * requests on Workers don't leak <Head> tags between responses.
6
+ *
7
+ * This module is server-only — it imports node:async_hooks and must NOT
8
+ * be bundled for the browser.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=head-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"head-state.d.ts","sourceRoot":"","sources":["../../src/shims/head-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Server-only head state backed by AsyncLocalStorage.
3
+ *
4
+ * Provides request-scoped isolation for SSR head elements so concurrent
5
+ * requests on Workers don't leak <Head> tags between responses.
6
+ *
7
+ * This module is server-only — it imports node:async_hooks and must NOT
8
+ * be bundled for the browser.
9
+ */
10
+ import { AsyncLocalStorage } from "node:async_hooks";
11
+ import { _registerHeadStateAccessors } from "./head.js";
12
+ const _ALS_KEY = Symbol.for("vinext.head.als");
13
+ const _FALLBACK_KEY = Symbol.for("vinext.head.fallback");
14
+ const _g = globalThis;
15
+ const _als = (_g[_ALS_KEY] ??= new AsyncLocalStorage());
16
+ const _fallbackState = (_g[_FALLBACK_KEY] ??= {
17
+ ssrHeadElements: [],
18
+ });
19
+ function _enterWith(state) {
20
+ const enterWith = _als.enterWith;
21
+ if (typeof enterWith === "function") {
22
+ try {
23
+ enterWith.call(_als, state);
24
+ return;
25
+ }
26
+ catch {
27
+ // Fall through to best-effort fallback.
28
+ }
29
+ }
30
+ _fallbackState.ssrHeadElements = state.ssrHeadElements;
31
+ }
32
+ function _getState() {
33
+ return _als.getStore() ?? _fallbackState;
34
+ }
35
+ // ---------------------------------------------------------------------------
36
+ // Register ALS-backed accessors into head.ts
37
+ // ---------------------------------------------------------------------------
38
+ _registerHeadStateAccessors({
39
+ getSSRHeadElements() {
40
+ return _getState().ssrHeadElements;
41
+ },
42
+ resetSSRHead() {
43
+ _enterWith({ ssrHeadElements: [] });
44
+ _fallbackState.ssrHeadElements = [];
45
+ },
46
+ });
47
+ //# sourceMappingURL=head-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"head-state.js","sourceRoot":"","sources":["../../src/shims/head-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC;AAUxD,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAC/C,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;AACzD,MAAM,EAAE,GAAG,UAAqD,CAAC;AACjE,MAAM,IAAI,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,IAAI,iBAAiB,EAAa,CAAiC,CAAC;AAEnG,MAAM,cAAc,GAAG,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK;IAC5C,eAAe,EAAE,EAAE;CACA,CAAc,CAAC;AAEpC,SAAS,UAAU,CAAC,KAAgB;IAClC,MAAM,SAAS,GAAI,IAAY,CAAC,SAAS,CAAC;IAC1C,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC5B,OAAO;QACT,CAAC;QAAC,MAAM,CAAC;YACP,wCAAwC;QAC1C,CAAC;IACH,CAAC;IACD,cAAc,CAAC,eAAe,GAAG,KAAK,CAAC,eAAe,CAAC;AACzD,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,IAAI,CAAC,QAAQ,EAAE,IAAI,cAAc,CAAC;AAC3C,CAAC;AAED,8EAA8E;AAC9E,6CAA6C;AAC7C,8EAA8E;AAE9E,2BAA2B,CAAC;IAC1B,kBAAkB;QAChB,OAAO,SAAS,EAAE,CAAC,eAAe,CAAC;IACrC,CAAC;IAED,YAAY;QACV,UAAU,CAAC,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC,CAAC;QACpC,cAAc,CAAC,eAAe,GAAG,EAAE,CAAC;IACtC,CAAC;CACF,CAAC,CAAC","sourcesContent":["/**\n * Server-only head state backed by AsyncLocalStorage.\n *\n * Provides request-scoped isolation for SSR head elements so concurrent\n * requests on Workers don't leak <Head> tags between responses.\n *\n * This module is server-only — it imports node:async_hooks and must NOT\n * be bundled for the browser.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { _registerHeadStateAccessors } from \"./head.js\";\n\n// ---------------------------------------------------------------------------\n// ALS setup\n// ---------------------------------------------------------------------------\n\ninterface HeadState {\n ssrHeadElements: string[];\n}\n\nconst _ALS_KEY = Symbol.for(\"vinext.head.als\");\nconst _FALLBACK_KEY = Symbol.for(\"vinext.head.fallback\");\nconst _g = globalThis as unknown as Record<PropertyKey, unknown>;\nconst _als = (_g[_ALS_KEY] ??= new AsyncLocalStorage<HeadState>()) as AsyncLocalStorage<HeadState>;\n\nconst _fallbackState = (_g[_FALLBACK_KEY] ??= {\n ssrHeadElements: [],\n} satisfies HeadState) as HeadState;\n\nfunction _enterWith(state: HeadState): void {\n const enterWith = (_als as any).enterWith;\n if (typeof enterWith === \"function\") {\n try {\n enterWith.call(_als, state);\n return;\n } catch {\n // Fall through to best-effort fallback.\n }\n }\n _fallbackState.ssrHeadElements = state.ssrHeadElements;\n}\n\nfunction _getState(): HeadState {\n return _als.getStore() ?? _fallbackState;\n}\n\n// ---------------------------------------------------------------------------\n// Register ALS-backed accessors into head.ts\n// ---------------------------------------------------------------------------\n\n_registerHeadStateAccessors({\n getSSRHeadElements(): string[] {\n return _getState().ssrHeadElements;\n },\n\n resetSSRHead(): void {\n _enterWith({ ssrHeadElements: [] });\n _fallbackState.ssrHeadElements = [];\n },\n});\n"]}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * next/head shim
3
+ *
4
+ * In the Pages Router, <Head> manages document <head> elements.
5
+ * - On the server: collects elements into a module-level array that the
6
+ * dev-server reads after render and injects into the HTML <head>.
7
+ * - On the client: uses useEffect + DOM manipulation.
8
+ */
9
+ import React from "react";
10
+ interface HeadProps {
11
+ children?: React.ReactNode;
12
+ }
13
+ /**
14
+ * Register ALS-backed state accessors. Called by head-state.ts on import.
15
+ * @internal
16
+ */
17
+ export declare function _registerHeadStateAccessors(accessors: {
18
+ getSSRHeadElements: () => string[];
19
+ resetSSRHead: () => void;
20
+ }): void;
21
+ /** Reset the SSR head collector. Call before render. */
22
+ export declare function resetSSRHead(): void;
23
+ /** Get collected head HTML. Call after render. */
24
+ export declare function getSSRHeadHTML(): string;
25
+ export declare function escapeAttr(s: string): string;
26
+ declare function Head({ children }: HeadProps): null;
27
+ export default Head;
28
+ //# sourceMappingURL=head.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../../src/shims/head.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAA8C,MAAM,OAAO,CAAC;AAEnE,UAAU,SAAS;IACjB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAWD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,SAAS,EAAE;IACrD,kBAAkB,EAAE,MAAM,MAAM,EAAE,CAAC;IACnC,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B,GAAG,IAAI,CAGP;AAED,wDAAwD;AACxD,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AAED,kDAAkD;AAClD,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAgED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5C;AAID,iBAAS,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,SAAS,GAAG,IAAI,CAqD3C;AAED,eAAe,IAAI,CAAC"}