@wular/pnext 0.0.1

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 (371) hide show
  1. package/README.md +153 -0
  2. package/bin/pnext +67 -0
  3. package/config/lint/base.js +48 -0
  4. package/config/ts/base.json +26 -0
  5. package/config/ts/react.json +13 -0
  6. package/package.json +70 -0
  7. package/reference/compat.md +63 -0
  8. package/reference/config.md +120 -0
  9. package/reference/css.md +58 -0
  10. package/reference/dev.md +69 -0
  11. package/reference/env.md +40 -0
  12. package/reference/metadata.md +86 -0
  13. package/reference/navigation.md +149 -0
  14. package/reference/overview.md +35 -0
  15. package/reference/performance.md +97 -0
  16. package/reference/rendering.md +127 -0
  17. package/reference/routing.md +167 -0
  18. package/reference/typegen.md +64 -0
  19. package/src/api/cache.ts +80 -0
  20. package/src/api/client-cache.ts +9 -0
  21. package/src/api/client-navigation.ts +279 -0
  22. package/src/api/dynamic.tsx +102 -0
  23. package/src/api/link.tsx +119 -0
  24. package/src/api/navigation.ts +198 -0
  25. package/src/api/router/events.ts +53 -0
  26. package/src/api/router/history.ts +70 -0
  27. package/src/api/router/hub.ts +194 -0
  28. package/src/api/router/policies.ts +107 -0
  29. package/src/api/router/runtime.ts +5238 -0
  30. package/src/api/router/types.ts +299 -0
  31. package/src/api/router.ts +167 -0
  32. package/src/api/server.ts +323 -0
  33. package/src/api/suspense.ts +16 -0
  34. package/src/cache/context.ts +61 -0
  35. package/src/cli/adapters/vercel-warm.ts +375 -0
  36. package/src/cli/adapters/vercel.ts +1310 -0
  37. package/src/cli/analyze-print.ts +181 -0
  38. package/src/cli/analyze.ts +328 -0
  39. package/src/cli/boot-trace.ts +29 -0
  40. package/src/cli/build.ts +3114 -0
  41. package/src/cli/dev.ts +276 -0
  42. package/src/cli/index.ts +196 -0
  43. package/src/cli/named-bin.ts +119 -0
  44. package/src/cli/serve-ui.ts +160 -0
  45. package/src/cli/start.ts +1425 -0
  46. package/src/client/build.ts +2136 -0
  47. package/src/client/chunk-fold.ts +526 -0
  48. package/src/client/entry.ts +1525 -0
  49. package/src/client/paths.ts +22 -0
  50. package/src/client/prebuilt.ts +621 -0
  51. package/src/client/profile.ts +75 -0
  52. package/src/client/react-compiler.ts +94 -0
  53. package/src/client/reference-stub.ts +145 -0
  54. package/src/client/reference.ts +47 -0
  55. package/src/compat/actions/action-client.ts +531 -0
  56. package/src/compat/actions/action-dispatch.ts +676 -0
  57. package/src/compat/actions/action-router.ts +40 -0
  58. package/src/compat/actions/action-shared.ts +95 -0
  59. package/src/compat/actions/client-plugin.ts +135 -0
  60. package/src/compat/actions/client-stub.ts +65 -0
  61. package/src/compat/actions/config.ts +164 -0
  62. package/src/compat/actions/detect.ts +208 -0
  63. package/src/compat/actions/discovery.ts +244 -0
  64. package/src/compat/actions/early-submit.ts +40 -0
  65. package/src/compat/actions/endpoint.ts +602 -0
  66. package/src/compat/actions/flight.ts +52 -0
  67. package/src/compat/actions/form-state.ts +73 -0
  68. package/src/compat/actions/hoist.ts +485 -0
  69. package/src/compat/actions/ids.ts +39 -0
  70. package/src/compat/actions/index.ts +41 -0
  71. package/src/compat/actions/instances.ts +144 -0
  72. package/src/compat/actions/origin.ts +109 -0
  73. package/src/compat/actions/protocol.ts +125 -0
  74. package/src/compat/actions/registry.ts +74 -0
  75. package/src/compat/actions/rewrite.ts +282 -0
  76. package/src/compat/actions/serve.ts +414 -0
  77. package/src/compat/actions/server-tag.ts +21 -0
  78. package/src/compat/actions/unrecognized-error.ts +30 -0
  79. package/src/compat/adapter/build-complete.ts +257 -0
  80. package/src/compat/bundler/bun-externals.ts +53 -0
  81. package/src/compat/bundler/cjs-exports.ts +542 -0
  82. package/src/compat/bundler/config.ts +363 -0
  83. package/src/compat/bundler/externals.ts +34 -0
  84. package/src/compat/bundler/import-meta-url.ts +60 -0
  85. package/src/compat/bundler/modularize-imports.ts +119 -0
  86. package/src/compat/bundler/new-url-asset.ts +87 -0
  87. package/src/compat/bundler/optimize-package-imports.ts +273 -0
  88. package/src/compat/bundler/polyfill.ts +88 -0
  89. package/src/compat/bundler/react-compiler.ts +61 -0
  90. package/src/compat/bundler/react-profiler.tsx +25 -0
  91. package/src/compat/bundler/relay-transform.ts +116 -0
  92. package/src/compat/bundler/require-context.ts +281 -0
  93. package/src/compat/bundler/resolve-extensions.ts +75 -0
  94. package/src/compat/bundler/source-cache.ts +61 -0
  95. package/src/compat/bundler/static-imports.ts +25 -0
  96. package/src/compat/bundler/symlink-imports.ts +119 -0
  97. package/src/compat/bundler/tsconfig-paths.ts +50 -0
  98. package/src/compat/bundler/wasm.ts +153 -0
  99. package/src/compat/bundler/webpack-loaders.ts +685 -0
  100. package/src/compat/bundler/worker.ts +278 -0
  101. package/src/compat/cache/build-flags.ts +81 -0
  102. package/src/compat/cache/build-prerender-errors.ts +163 -0
  103. package/src/compat/cache/custom-handler.ts +159 -0
  104. package/src/compat/cache/fetch-patch.ts +745 -0
  105. package/src/compat/cache/handler.ts +100 -0
  106. package/src/compat/cache/modern-handler.ts +275 -0
  107. package/src/compat/cache/resume-data-cache.ts +143 -0
  108. package/src/compat/cache/revalidate.ts +759 -0
  109. package/src/compat/cache/runtime-error.ts +124 -0
  110. package/src/compat/cache/use-cache-transform.ts +961 -0
  111. package/src/compat/cache/use-cache.ts +1695 -0
  112. package/src/compat/cache-control.ts +269 -0
  113. package/src/compat/client/base-path.ts +64 -0
  114. package/src/compat/client/css-order.ts +36 -0
  115. package/src/compat/client/errors/control-flow.ts +92 -0
  116. package/src/compat/client/errors/error-boundary.ts +222 -0
  117. package/src/compat/client/errors/global-error.ts +238 -0
  118. package/src/compat/client/errors/install.ts +217 -0
  119. package/src/compat/client/errors/lazy.ts +53 -0
  120. package/src/compat/client/errors/primitive-throw.ts +126 -0
  121. package/src/compat/client/errors/soft-refresh.ts +14 -0
  122. package/src/compat/client/link-status.ts +86 -0
  123. package/src/compat/client/nav-compat-runtime.ts +57 -0
  124. package/src/compat/client/nav-compat.ts +42 -0
  125. package/src/compat/client/navigation-scroll.ts +154 -0
  126. package/src/compat/client/optimistic-routing.ts +206 -0
  127. package/src/compat/client/prefetch-cache.ts +111 -0
  128. package/src/compat/client/route-announcer.ts +72 -0
  129. package/src/compat/client/segment-cache-policy.ts +159 -0
  130. package/src/compat/client/segment-cache.ts +1077 -0
  131. package/src/compat/client/segment-prefetch.ts +375 -0
  132. package/src/compat/client/trailing-slash.ts +24 -0
  133. package/src/compat/css/chunking.ts +254 -0
  134. package/src/compat/css/inline-css.ts +73 -0
  135. package/src/compat/css/lightningcss.ts +90 -0
  136. package/src/compat/css/modules.ts +373 -0
  137. package/src/compat/css/nonce.ts +30 -0
  138. package/src/compat/css/sass-plugin.ts +65 -0
  139. package/src/compat/css/sass.ts +392 -0
  140. package/src/compat/css/styled-jsx-runtime.ts +80 -0
  141. package/src/compat/css/styled-jsx.ts +49 -0
  142. package/src/compat/edge-runtime.ts +71 -0
  143. package/src/compat/export/client.ts +112 -0
  144. package/src/compat/export/index.ts +272 -0
  145. package/src/compat/export/standalone.ts +207 -0
  146. package/src/compat/image-optimizer/cache.ts +119 -0
  147. package/src/compat/image-optimizer/detect.ts +143 -0
  148. package/src/compat/image-optimizer/index.ts +601 -0
  149. package/src/compat/image-optimizer/source.ts +243 -0
  150. package/src/compat/index.ts +458 -0
  151. package/src/compat/lifecycle/after-scope.ts +86 -0
  152. package/src/compat/lifecycle/after.ts +173 -0
  153. package/src/compat/lifecycle/error-funnel.ts +306 -0
  154. package/src/compat/lifecycle/error-serialize.ts +87 -0
  155. package/src/compat/lifecycle/error-ui.ts +167 -0
  156. package/src/compat/lifecycle/instrumentation-client.ts +138 -0
  157. package/src/compat/lifecycle/instrumentation.ts +277 -0
  158. package/src/compat/lifecycle/node-console.ts +19 -0
  159. package/src/compat/lifecycle/testmode.ts +263 -0
  160. package/src/compat/mdx/compile.ts +219 -0
  161. package/src/compat/mdx/next-mdx-stub.ts +46 -0
  162. package/src/compat/mdx/plugin.ts +37 -0
  163. package/src/compat/metadata-route-artifacts.ts +458 -0
  164. package/src/compat/metadata.ts +295 -0
  165. package/src/compat/middleware/manifest.ts +210 -0
  166. package/src/compat/misc/action-return.ts +173 -0
  167. package/src/compat/next/cache.ts +211 -0
  168. package/src/compat/next/canonical-url.ts +35 -0
  169. package/src/compat/next/client-cache.ts +57 -0
  170. package/src/compat/next/client-navigation.ts +313 -0
  171. package/src/compat/next/client-only.ts +3 -0
  172. package/src/compat/next/client-script.tsx +215 -0
  173. package/src/compat/next/client-server.ts +39 -0
  174. package/src/compat/next/config-loader.ts +569 -0
  175. package/src/compat/next/config.ts +29 -0
  176. package/src/compat/next/constants.cjs +6 -0
  177. package/src/compat/next/constants.ts +6 -0
  178. package/src/compat/next/custom-server.ts +236 -0
  179. package/src/compat/next/dist/client/components/app-router-headers.ts +32 -0
  180. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +38 -0
  181. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -0
  182. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -0
  183. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -0
  184. package/src/compat/next/dynamic.tsx +46 -0
  185. package/src/compat/next/error.tsx +148 -0
  186. package/src/compat/next/font/cache.ts +171 -0
  187. package/src/compat/next/font/google.ts +2 -0
  188. package/src/compat/next/font/index.ts +8 -0
  189. package/src/compat/next/font/local.ts +5 -0
  190. package/src/compat/next/font/runtime-client.ts +71 -0
  191. package/src/compat/next/font/runtime.ts +974 -0
  192. package/src/compat/next/font/shared.ts +281 -0
  193. package/src/compat/next/form.tsx +156 -0
  194. package/src/compat/next/head.tsx +10 -0
  195. package/src/compat/next/headers.ts +247 -0
  196. package/src/compat/next/image/config.ts +196 -0
  197. package/src/compat/next/image/optimizer.ts +96 -0
  198. package/src/compat/next/image/patterns.ts +103 -0
  199. package/src/compat/next/image/shared.ts +141 -0
  200. package/src/compat/next/image/static-metadata.ts +283 -0
  201. package/src/compat/next/image/validate.ts +269 -0
  202. package/src/compat/next/image-client.tsx +215 -0
  203. package/src/compat/next/image-props.ts +575 -0
  204. package/src/compat/next/image-usage.ts +102 -0
  205. package/src/compat/next/image.tsx +56 -0
  206. package/src/compat/next/index.ts +1 -0
  207. package/src/compat/next/legacy-image.tsx +97 -0
  208. package/src/compat/next/link-usage.ts +29 -0
  209. package/src/compat/next/link-validation-transform.ts +200 -0
  210. package/src/compat/next/link.tsx +466 -0
  211. package/src/compat/next/navigation.cjs +21 -0
  212. package/src/compat/next/navigation.ts +188 -0
  213. package/src/compat/next/offline.ts +51 -0
  214. package/src/compat/next/og.ts +324 -0
  215. package/src/compat/next/optimistic-route-state.ts +188 -0
  216. package/src/compat/next/preferred-region.ts +39 -0
  217. package/src/compat/next/redirects.ts +131 -0
  218. package/src/compat/next/resource-hints.ts +136 -0
  219. package/src/compat/next/rewrites.ts +350 -0
  220. package/src/compat/next/root-params.ts +142 -0
  221. package/src/compat/next/router.cjs +49 -0
  222. package/src/compat/next/router.ts +143 -0
  223. package/src/compat/next/script.tsx +355 -0
  224. package/src/compat/next/server-only.ts +3 -0
  225. package/src/compat/next/server.ts +28 -0
  226. package/src/compat/next/svgr.ts +58 -0
  227. package/src/compat/next/telemetry.ts +77 -0
  228. package/src/compat/next/user-agent.ts +100 -0
  229. package/src/compat/next/web-vitals.ts +56 -0
  230. package/src/compat/otel/api.ts +95 -0
  231. package/src/compat/otel/client-trace-metadata.ts +71 -0
  232. package/src/compat/otel/fetch-span.ts +77 -0
  233. package/src/compat/otel/tracer.ts +944 -0
  234. package/src/compat/pages/client-plugin.ts +108 -0
  235. package/src/compat/pages/index.ts +527 -0
  236. package/src/compat/pages/router-state.ts +94 -0
  237. package/src/compat/ppr/io.ts +38 -0
  238. package/src/compat/ppr/missing-root-params.ts +105 -0
  239. package/src/compat/ppr/root-params-scan.ts +164 -0
  240. package/src/compat/ppr/root-params-transform.ts +75 -0
  241. package/src/compat/ppr/root-params.ts +129 -0
  242. package/src/compat/ppr/segment-config-incompat.ts +34 -0
  243. package/src/compat/protocol.ts +202 -0
  244. package/src/compat/react/client.ts +59 -0
  245. package/src/compat/react/compiler-runtime.ts +60 -0
  246. package/src/compat/react/dom-client.ts +115 -0
  247. package/src/compat/react/dom-react-server.ts +20 -0
  248. package/src/compat/react/dom-server.ts +40 -0
  249. package/src/compat/react/dom.ts +154 -0
  250. package/src/compat/react/preact.ts +522 -0
  251. package/src/compat/react/react-server.ts +84 -0
  252. package/src/compat/react/router-shim.ts +26 -0
  253. package/src/compat/react/server-component-use.ts +48 -0
  254. package/src/compat/react/server-inserted-html.ts +87 -0
  255. package/src/compat/react/server.ts +156 -0
  256. package/src/compat/react/view-transition.ts +60 -0
  257. package/src/compat/register/actions.ts +875 -0
  258. package/src/compat/register/boot.ts +141 -0
  259. package/src/compat/register/build-tier.ts +11 -0
  260. package/src/compat/register/build.ts +182 -0
  261. package/src/compat/register/bundler.ts +587 -0
  262. package/src/compat/register/cache.ts +85 -0
  263. package/src/compat/register/client-errors.ts +18 -0
  264. package/src/compat/register/config.ts +17 -0
  265. package/src/compat/register/css-extras.ts +120 -0
  266. package/src/compat/register/edge-runtime.ts +6 -0
  267. package/src/compat/register/errors.ts +46 -0
  268. package/src/compat/register/export.ts +23 -0
  269. package/src/compat/register/font.ts +36 -0
  270. package/src/compat/register/hooks.ts +34 -0
  271. package/src/compat/register/image.ts +133 -0
  272. package/src/compat/register/index.ts +111 -0
  273. package/src/compat/register/instrumentation-client.ts +35 -0
  274. package/src/compat/register/lifecycle.ts +86 -0
  275. package/src/compat/register/mdx.ts +48 -0
  276. package/src/compat/register/middleware.ts +36 -0
  277. package/src/compat/register/misc.ts +44 -0
  278. package/src/compat/register/otel.ts +288 -0
  279. package/src/compat/register/pages-api.ts +473 -0
  280. package/src/compat/register/ppr.ts +56 -0
  281. package/src/compat/register/protocol.ts +57 -0
  282. package/src/compat/register/proxy.ts +127 -0
  283. package/src/compat/register/render.ts +268 -0
  284. package/src/compat/register/routing.ts +410 -0
  285. package/src/compat/register/segment.ts +1903 -0
  286. package/src/compat/register/static-image.ts +21 -0
  287. package/src/compat/register/typed-routes.ts +35 -0
  288. package/src/compat/register/usecache.ts +131 -0
  289. package/src/compat/register/validation.ts +56 -0
  290. package/src/compat/segment/loading-boundary.ts +113 -0
  291. package/src/compat/segment/page-slot.ts +200 -0
  292. package/src/compat/segment/tree.ts +481 -0
  293. package/src/compat/segment/vary-key.ts +102 -0
  294. package/src/compat/segment/vary-params.ts +551 -0
  295. package/src/compat/static-params.ts +33 -0
  296. package/src/compat/tsconfig-defaults.ts +301 -0
  297. package/src/compat/typecheck/index.ts +1481 -0
  298. package/src/compat/typecheck/worker.ts +26 -0
  299. package/src/compat/typed-routes/index.ts +92 -0
  300. package/src/compat/typed-routes/manifest.ts +356 -0
  301. package/src/compat/typed-routes/typegen.ts +566 -0
  302. package/src/compat/validation/errors.ts +159 -0
  303. package/src/compat/validation/index.ts +1770 -0
  304. package/src/compat/validation/prerender-diagnostics.ts +1508 -0
  305. package/src/compat-bootstrap.ts +67 -0
  306. package/src/config.ts +218 -0
  307. package/src/css/build.ts +697 -0
  308. package/src/css/index.ts +2 -0
  309. package/src/css/postcss.ts +236 -0
  310. package/src/css/worker.ts +34 -0
  311. package/src/dev/client-actions.ts +35 -0
  312. package/src/dev/client-chunk-store.ts +92 -0
  313. package/src/dev/client-key-cache.ts +178 -0
  314. package/src/dev/global-css-cache.ts +212 -0
  315. package/src/dev/imports.ts +2430 -0
  316. package/src/dev/module-cache.ts +721 -0
  317. package/src/dev/module-generations.ts +38 -0
  318. package/src/dev/module-transform.ts +188 -0
  319. package/src/dev/node-module-bundle-cache.ts +63 -0
  320. package/src/dev/restart-cache.ts +10 -0
  321. package/src/dev/route-bundle-key-cache.ts +154 -0
  322. package/src/dev/route-facts-cache.ts +223 -0
  323. package/src/dev/server.ts +1710 -0
  324. package/src/dynamic/source.ts +307 -0
  325. package/src/dynamic/tree-shake.ts +262 -0
  326. package/src/env.ts +92 -0
  327. package/src/extensions.ts +1898 -0
  328. package/src/index.ts +34 -0
  329. package/src/internal.ts +43 -0
  330. package/src/islands/boundary-error.ts +8 -0
  331. package/src/islands/static-children.ts +37 -0
  332. package/src/islands/static-slots.ts +106 -0
  333. package/src/ppr-postpone.ts +24 -0
  334. package/src/ppr.ts +784 -0
  335. package/src/proxy.ts +752 -0
  336. package/src/render/hooks.ts +384 -0
  337. package/src/render/index.ts +1 -0
  338. package/src/render/island-context.ts +47 -0
  339. package/src/render/metadata.ts +857 -0
  340. package/src/render/renderer.ts +7391 -0
  341. package/src/render/resource-hints.ts +44 -0
  342. package/src/render/slots.tsx +679 -0
  343. package/src/request/context.ts +396 -0
  344. package/src/resolve/engine.ts +219 -0
  345. package/src/resolve/imports.ts +1104 -0
  346. package/src/resolve/scan-facts.ts +474 -0
  347. package/src/resolve/source-text.ts +86 -0
  348. package/src/routing/forwarded.ts +41 -0
  349. package/src/routing/handler.ts +271 -0
  350. package/src/routing/href.ts +203 -0
  351. package/src/routing/metadata.ts +1018 -0
  352. package/src/routing/request-runtime.ts +43 -0
  353. package/src/routing/routes.ts +2560 -0
  354. package/src/routing/slots.ts +432 -0
  355. package/src/runtime/server.ts +3453 -0
  356. package/src/runtime/vendor.ts +1160 -0
  357. package/src/style-modules.d.ts +9 -0
  358. package/src/typegen.ts +151 -0
  359. package/src/types.ts +725 -0
  360. package/src/utils/ansi.ts +9 -0
  361. package/src/utils/content-type.ts +31 -0
  362. package/src/utils/decode.ts +7 -0
  363. package/src/utils/dev-profile.ts +31 -0
  364. package/src/utils/error-log.ts +29 -0
  365. package/src/utils/fs-cache.ts +31 -0
  366. package/src/utils/fs.ts +119 -0
  367. package/src/utils/html.ts +46 -0
  368. package/src/utils/serialize.ts +378 -0
  369. package/src/utils/source.ts +35 -0
  370. package/src/utils/verbose.ts +39 -0
  371. package/tsconfig.json +10 -0
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Client-safe font naming/hashing logic shared by the server font runtime
3
+ * (runtime.ts) and the client font runtime (runtime-client.ts).
4
+ *
5
+ * MUST stay free of `node:*` imports (no async_hooks / crypto / fs / module) so
6
+ * it can be pulled into browser bundles. The SHA-256 here is a pure-JS
7
+ * implementation whose hex output is byte-identical to
8
+ * `node:crypto.createHash('sha256').update(value).digest('hex')`, so the
9
+ * className/variable hashes a client component computes match the server's
10
+ * exactly.
11
+ */
12
+
13
+ export type FontDisplay = 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
14
+
15
+ export interface GoogleFontOptions {
16
+ axes?: string[];
17
+ adjustFontFallback?: boolean;
18
+ display?: FontDisplay;
19
+ fallback?: string[];
20
+ preload?: boolean;
21
+ subsets?: string[];
22
+ style?: string | string[];
23
+ variable?: string;
24
+ weight?: string | string[];
25
+ }
26
+
27
+ export interface LocalFontSource {
28
+ path: string;
29
+ style?: string;
30
+ weight?: string;
31
+ }
32
+
33
+ export interface LocalFontOptions {
34
+ declarations?: { prop: string; value: string }[];
35
+ display?: FontDisplay;
36
+ fallback?: string[];
37
+ preload?: boolean;
38
+ src: string | LocalFontSource | LocalFontSource[];
39
+ style?: string;
40
+ variable?: string;
41
+ weight?: string;
42
+ }
43
+
44
+ export interface NextFont {
45
+ className: string;
46
+ style: {
47
+ fontFamily: string;
48
+ fontStyle?: string;
49
+ fontWeight?: number | string;
50
+ };
51
+ }
52
+
53
+ export interface NextFontWithVariable extends NextFont {
54
+ variable: string;
55
+ }
56
+
57
+ /**
58
+ * The pre-hashed identifiers + style a client only ever needs off a font object.
59
+ * Both runtimes derive these from the same inputs, so they agree byte-for-byte.
60
+ */
61
+ export interface FontNaming {
62
+ className: string;
63
+ variable: string;
64
+ style: NextFont['style'];
65
+ /** Human-readable CSS family label (single, unquoted). */
66
+ fontFamily: string;
67
+ /** 12-char hash used for `__className_<hash>` / `__variable_<hash>`. */
68
+ hash: string;
69
+ /** Whether the loader call requested a CSS variable. When false the returned
70
+ * font object omits the `variable` key entirely (Next only exposes it when
71
+ * `variable` was passed). */
72
+ hasVariable: boolean;
73
+ }
74
+
75
+ /** Naming for a `next/font/google` loader call: keyed on `{family, options}`. */
76
+ export function googleFontNaming(family: string, options: GoogleFontOptions): FontNaming {
77
+ const normalizedKey = stableJson({ family, options });
78
+ const hash = hashString(normalizedKey).slice(0, 12);
79
+ // Next scopes the family to the human-readable font name (`'Inter'`), not a
80
+ // hashed token; only the className/variable identifiers are hashed.
81
+ const fontFamily = family;
82
+ return {
83
+ className: `__className_${hash}`,
84
+ variable: `__variable_${hash}`,
85
+ fontFamily,
86
+ hash,
87
+ hasVariable: Boolean(options.variable),
88
+ style: fontStyle(fontFamily, options, options.fallback ?? []),
89
+ };
90
+ }
91
+
92
+ /**
93
+ * Naming for a `next/font/local` loader call: keyed on `{callerFile, options}`.
94
+ * `callerFile` MUST be the same value the server sees (the transform threads it
95
+ * via `withFile(<file>)`), or the hashes diverge.
96
+ */
97
+ export function localFontNaming(
98
+ callerFile: string | undefined,
99
+ options: LocalFontOptions,
100
+ ): FontNaming {
101
+ const normalizedKey = stableJson({ callerFile, options });
102
+ const hash = hashString(normalizedKey).slice(0, 12);
103
+ // Next derives the CSS font-family from the loader call's JS binding
104
+ // (`variableName`). We reproduce the same human-readable label from the
105
+ // (first) source filename stem: `font1_roboto.woff2` -> `font1`.
106
+ const fontFamily = localFontFamilyLabel(options) ?? `local_${hash}`;
107
+ return {
108
+ className: `__className_${hash}`,
109
+ variable: `__variable_${hash}`,
110
+ fontFamily,
111
+ hash,
112
+ hasVariable: Boolean(options.variable),
113
+ style: fontStyle(fontFamily, options, options.fallback ?? []),
114
+ };
115
+ }
116
+
117
+ /** Human-readable family label from the first local `src` filename stem. */
118
+ export function localFontFamilyLabel(options: LocalFontOptions): string | undefined {
119
+ const first = Array.isArray(options.src) ? options.src[0] : options.src;
120
+ const srcPath = typeof first === 'string' ? first : first?.path;
121
+ if (!srcPath) return undefined;
122
+ const base = srcPath.replace(/^.*[\\/]/, '').replace(/\.[^.]+$/, '');
123
+ const stem = base.split('_', 1)[0] ?? base;
124
+ return /^[-_a-zA-Z0-9 ]+$/.test(stem) ? stem : undefined;
125
+ }
126
+
127
+ export function fontStyle(
128
+ family: string,
129
+ options: GoogleFontOptions | LocalFontOptions,
130
+ fallbackFonts: string[],
131
+ ): NextFont['style'] {
132
+ // Next exports numeric font-weight and the raw font-style, but only when a
133
+ // single (non-range) value is set on the loader call.
134
+ const weight =
135
+ typeof options.weight === 'string' && !isRangeValue(options.weight) && !Number.isNaN(Number(options.weight))
136
+ ? { fontWeight: Number(cssIdentifier(options.weight)) }
137
+ : {};
138
+ const style =
139
+ typeof options.style === 'string' && !isRangeValue(options.style)
140
+ ? { fontStyle: cssIdentifier(options.style) }
141
+ : {};
142
+ return {
143
+ fontFamily: familyStack(family, fallbackFonts),
144
+ ...weight,
145
+ ...style,
146
+ };
147
+ }
148
+
149
+ /**
150
+ * The full family stack Next writes into `style.fontFamily`: the font, then an
151
+ * `<family> Fallback` metrics-adjusted fallback (adjustFontFallback defaults
152
+ * on), then any user fallbacks. Single-quoted to match Next's postcss output.
153
+ */
154
+ export function familyStack(family: string, fallbackFonts: string[] = [], adjustFontFallback = true) {
155
+ return [
156
+ singleQuoteFamily(family),
157
+ ...(adjustFontFallback ? [singleQuoteFamily(`${family} Fallback`)] : []),
158
+ ...fallbackFonts.map(singleQuoteFamily),
159
+ ].join(', ');
160
+ }
161
+
162
+ export function singleQuoteFamily(value: string) {
163
+ if (!/^[-_a-zA-Z0-9 ]+$/.test(value)) throw new Error(`Invalid font family: ${value}`);
164
+ return `'${value}'`;
165
+ }
166
+
167
+ function cssIdentifier(value: string) {
168
+ if (!/^[-_a-zA-Z0-9. ]+$/.test(value)) throw new Error(`Invalid font option value: ${value}`);
169
+ return value;
170
+ }
171
+
172
+ function isRangeValue(value: string) {
173
+ return value.trim().includes(' ');
174
+ }
175
+
176
+ export function stableJson(value: unknown): string {
177
+ return JSON.stringify(sortObject(value));
178
+ }
179
+
180
+ function sortObject(value: unknown): unknown {
181
+ if (Array.isArray(value)) return value.map(sortObject);
182
+ if (!value || typeof value !== 'object') return value;
183
+ return Object.fromEntries(
184
+ Object.entries(value as Record<string, unknown>)
185
+ .filter(([, item]) => item !== undefined)
186
+ .sort(([a], [b]) => a.localeCompare(b))
187
+ .map(([key, item]) => [key, sortObject(item)]),
188
+ );
189
+ }
190
+
191
+ /**
192
+ * SHA-256 hex digest of a UTF-8 string. Pure JS (no `node:crypto`) so this
193
+ * module stays browser-safe; the hex output is byte-identical to
194
+ * `createHash('sha256').update(value).digest('hex')`, keeping client and server
195
+ * font hashes in lock-step.
196
+ */
197
+ export function hashString(value: string): string {
198
+ return sha256Hex(utf8Bytes(value));
199
+ }
200
+
201
+ function utf8Bytes(value: string): Uint8Array {
202
+ // TextEncoder is available in browsers and in Node/Bun.
203
+ return new TextEncoder().encode(value);
204
+ }
205
+
206
+ const K = new Uint32Array([
207
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
208
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
209
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
210
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
211
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
212
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
213
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
214
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
215
+ ]);
216
+
217
+ function sha256Hex(message: Uint8Array): string {
218
+ const h = new Uint32Array([
219
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
220
+ ]);
221
+
222
+ const bitLen = message.length * 8;
223
+ // Padded length: message + 0x80 + zeros + 8-byte length, up to a 64-byte block.
224
+ const padded = new Uint8Array((((message.length + 8) >> 6) + 1) << 6);
225
+ padded.set(message);
226
+ padded[message.length] = 0x80;
227
+ // 64-bit big-endian bit length in the final 8 bytes (high 32 bits ~always 0
228
+ // for our short inputs, but write both halves for correctness).
229
+ const dv = new DataView(padded.buffer);
230
+ dv.setUint32(padded.length - 8, Math.floor(bitLen / 0x100000000));
231
+ dv.setUint32(padded.length - 4, bitLen >>> 0);
232
+
233
+ const w = new Uint32Array(64);
234
+ for (let offset = 0; offset < padded.length; offset += 64) {
235
+ for (let i = 0; i < 16; i++) w[i] = dv.getUint32(offset + i * 4);
236
+ for (let i = 16; i < 64; i++) {
237
+ const w15 = w[i - 15]!;
238
+ const w2 = w[i - 2]!;
239
+ const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ (w15 >>> 3);
240
+ const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ (w2 >>> 10);
241
+ w[i] = (w[i - 16]! + s0 + w[i - 7]! + s1) >>> 0;
242
+ }
243
+
244
+ let a = h[0]!, b = h[1]!, c = h[2]!, d = h[3]!;
245
+ let e = h[4]!, f = h[5]!, g = h[6]!, hh = h[7]!;
246
+
247
+ for (let i = 0; i < 64; i++) {
248
+ const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
249
+ const ch = (e & f) ^ (~e & g);
250
+ const t1 = (hh + S1 + ch + K[i]! + w[i]!) >>> 0;
251
+ const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
252
+ const maj = (a & b) ^ (a & c) ^ (b & c);
253
+ const t2 = (S0 + maj) >>> 0;
254
+ hh = g;
255
+ g = f;
256
+ f = e;
257
+ e = (d + t1) >>> 0;
258
+ d = c;
259
+ c = b;
260
+ b = a;
261
+ a = (t1 + t2) >>> 0;
262
+ }
263
+
264
+ h[0] = (h[0]! + a) >>> 0;
265
+ h[1] = (h[1]! + b) >>> 0;
266
+ h[2] = (h[2]! + c) >>> 0;
267
+ h[3] = (h[3]! + d) >>> 0;
268
+ h[4] = (h[4]! + e) >>> 0;
269
+ h[5] = (h[5]! + f) >>> 0;
270
+ h[6] = (h[6]! + g) >>> 0;
271
+ h[7] = (h[7]! + hh) >>> 0;
272
+ }
273
+
274
+ let hex = '';
275
+ for (let i = 0; i < 8; i++) hex += h[i]!.toString(16).padStart(8, '0');
276
+ return hex;
277
+ }
278
+
279
+ function rotr(value: number, bits: number): number {
280
+ return ((value >>> bits) | (value << (32 - bits))) >>> 0;
281
+ }
@@ -0,0 +1,156 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef, type JSX, type ReactNode } from 'preact/compat';
4
+ import { prefetchRoute, softNavigate } from '../../api/router';
5
+ import { addBasePath } from '../client/base-path';
6
+
7
+ type FormAction = string | ((formData: FormData) => void | Promise<void>);
8
+ export type FormProps = Omit<JSX.HTMLAttributes<HTMLFormElement>, 'action' | 'method'> & {
9
+ action: FormAction;
10
+ replace?: boolean;
11
+ scroll?: boolean;
12
+ prefetch?: boolean;
13
+ children?: ReactNode;
14
+ };
15
+
16
+ export default function Form({
17
+ action,
18
+ replace,
19
+ scroll,
20
+ prefetch,
21
+ onSubmit,
22
+ children,
23
+ ...formProps
24
+ }: FormProps) {
25
+ const formRef = useRef<HTMLFormElement | null>(null);
26
+ const stringAction = typeof action === 'string' ? action : undefined;
27
+ const basePathAction = stringAction == null ? undefined : addBasePath(stringAction);
28
+
29
+ // Function actions are React form actions; pass through untouched so the
30
+ // renderer wires them (no soft-nav interception, no GET semantics).
31
+
32
+ useEffect(() => {
33
+ if (basePathAction == null || prefetch === false) return;
34
+ // No prefetch in dev (per-request rendering); prefetchRoute already no-ops.
35
+ // Register the form element so a revalidation-triggered retry re-prefetches
36
+ // visible forms exactly like visible links (the router reads its `action`).
37
+ void prefetchRoute(basePathAction, { element: formRef.current ?? undefined });
38
+ }, [basePathAction, prefetch]);
39
+
40
+ if (typeof action !== 'string') {
41
+ return (
42
+ <form ref={formRef} action={action as never} onSubmit={onSubmit} {...formProps}>
43
+ {children}
44
+ </form>
45
+ );
46
+ }
47
+
48
+ const handleSubmit = (event: JSX.TargetedSubmitEvent<HTMLFormElement>) => {
49
+ onSubmit?.(event);
50
+ if (event.defaultPrevented) return;
51
+
52
+ const form = event.currentTarget;
53
+ const submitter = (event as unknown as { submitter?: HTMLElement }).submitter as
54
+ | HTMLButtonElement
55
+ | HTMLInputElement
56
+ | undefined;
57
+
58
+ // A submitter that sets encType/method/target to a non-default value isn't
59
+ // supported for soft-nav; fall back to the browser's native submit.
60
+ if (submitter && hasUnsupportedSubmitterAttr(submitter)) return;
61
+ if (submitter && hasReactClientActionAttributes(submitter)) return;
62
+
63
+ event.preventDefault();
64
+
65
+ const submitterAction = submitter?.getAttribute('formaction');
66
+ const target = submitterAction != null ? submitterAction : addBasePath(action);
67
+ const destination = createFormSubmitDestinationUrl(target, form);
68
+
69
+ void softNavigate(destination.href, { replace, scroll });
70
+ };
71
+
72
+ return (
73
+ <form
74
+ ref={formRef}
75
+ action={addBasePath(action)}
76
+ method="get"
77
+ onSubmit={handleSubmit}
78
+ {...formProps}
79
+ >
80
+ {children}
81
+ </form>
82
+ );
83
+ }
84
+
85
+ export { Form };
86
+
87
+ // encType/method/target set to a non-default value can't be honored by a
88
+ // soft-nav GET; detect and warn so the caller falls back to native submit.
89
+ const SUBMITTER_ATTRS: { attr: string; name: string; defaults: string[] }[] = [
90
+ { attr: 'formEncType', name: 'encType', defaults: ['application/x-www-form-urlencoded'] },
91
+ { attr: 'formMethod', name: 'method', defaults: ['get'] },
92
+ { attr: 'formTarget', name: 'target', defaults: ['', '_self'] },
93
+ ];
94
+
95
+ function hasUnsupportedSubmitterAttr(submitter: HTMLButtonElement | HTMLInputElement) {
96
+ for (const { attr, name, defaults } of SUBMITTER_ATTRS) {
97
+ const raw = submitter.getAttribute(attr) ?? submitter.getAttribute(attr.toLowerCase());
98
+ if (raw == null || raw === '') continue;
99
+ if (defaults.includes(raw.toLowerCase())) continue;
100
+ reportUnsupportedAttr(name);
101
+ return true;
102
+ }
103
+ return false;
104
+ }
105
+
106
+ function hasReactClientActionAttributes(submitter: HTMLElement) {
107
+ const name = submitter.getAttribute('name');
108
+ return Boolean(name && (name.startsWith('$ACTION_ID_') || name.startsWith('$ACTION_REF_')));
109
+ }
110
+
111
+ function createFormSubmitDestinationUrl(action: string, form: HTMLFormElement) {
112
+ let targetUrl: URL;
113
+ try {
114
+ targetUrl = new URL(action, location.href);
115
+ } catch (error) {
116
+ throw new Error(`Cannot parse form action "${action}" as a URL`, {
117
+ cause: error as Error,
118
+ });
119
+ }
120
+ if (targetUrl.searchParams.size > 0) targetUrl.search = '';
121
+
122
+ const data = new FormData(form);
123
+ for (const [key, value] of data.entries()) {
124
+ if (typeof value === 'string') {
125
+ targetUrl.searchParams.append(key, value);
126
+ } else {
127
+ warnFileInput();
128
+ targetUrl.searchParams.append(key, (value as File).name);
129
+ }
130
+ }
131
+ return targetUrl;
132
+ }
133
+
134
+ let reportedAttrs: Set<string> | undefined;
135
+ function reportUnsupportedAttr(name: string) {
136
+ if (typeof document === 'undefined' || !isDevDocument()) return;
137
+ reportedAttrs ??= new Set();
138
+ if (reportedAttrs.has(name)) return;
139
+ reportedAttrs.add(name);
140
+ console.error(
141
+ `<Form>'s \`${name}\` was set to an unsupported value. Only the default value is supported. See https://nextjs.org/docs/app/api-reference/components/form`,
142
+ );
143
+ }
144
+
145
+ let fileInputWarned = false;
146
+ function warnFileInput() {
147
+ if (fileInputWarned || typeof document === 'undefined' || !isDevDocument()) return;
148
+ fileInputWarned = true;
149
+ console.warn(
150
+ '<Form> only supports file inputs if `action` is a function. Files will not be submitted with a string action.',
151
+ );
152
+ }
153
+
154
+ function isDevDocument() {
155
+ return typeof document !== 'undefined' && Boolean(document.querySelector('script[data-pnext-dev]'));
156
+ }
@@ -0,0 +1,10 @@
1
+ import type { ComponentChildren } from 'preact';
2
+
3
+ /**
4
+ * Pages-router head hoisting is not supported (Next's own app router rejects
5
+ * it too); metadata exports are the supported path. Render nothing so shared
6
+ * components that still carry a <Head> don't break.
7
+ */
8
+ export default function Head(_props: { children?: ComponentChildren }) {
9
+ return null;
10
+ }
@@ -0,0 +1,247 @@
1
+ import {
2
+ currentRequest,
3
+ currentResponseHeaders,
4
+ getWorkUnit,
5
+ requireRequest,
6
+ requestApiPromise,
7
+ } from '../../request/context';
8
+ import type { NextRequestCookies } from '../../types';
9
+ import {
10
+ assertDraftModeToggleAllowedInAfter,
11
+ assertRequestApiAllowedInAfter,
12
+ lifecycleError,
13
+ } from '../lifecycle/after-scope';
14
+ import { assertNoDynamicApiInsideCache, currentRenderCacheMeta } from '../cache/revalidate';
15
+
16
+ const COOKIE_MUTATION_ERROR =
17
+ 'Cookies can only be modified in a Server Action or Route Handler. ' +
18
+ 'Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#options';
19
+
20
+ /**
21
+ * Cookie writes are only legal while the request is executing a Server Action or a Route Handler. Every
22
+ * other phase - a page render, an after() callback, or middleware - throws Next's exact message. The
23
+ * check reads the LIVE work-unit phase at call time, so a cookie store captured during an action still
24
+ * throws when its .set() runs later inside after(), where the phase has flipped.
25
+ *
26
+ * Core does not set a dedicated 'handler' phase, so a route handler is detected via its routeKind
27
+ * instead; an action sets phase 'action' explicitly. Outside any work unit mutation is left alone.
28
+ */
29
+ function assertCookieMutationAllowed(): void {
30
+ const unit = getWorkUnit();
31
+ if (!unit) return;
32
+ if (unit.phase === 'action') return;
33
+ if (unit.phase !== 'after' && unit.routeKind === 'route-handler') return;
34
+ throw new Error(COOKIE_MUTATION_ERROR);
35
+ }
36
+
37
+ const HEADERS_MUTATION_ERROR =
38
+ 'Headers cannot be modified. Read more: https://nextjs.org/docs/app/api-reference/functions/headers';
39
+
40
+ /**
41
+ * headers() exposes the REQUEST headers, which Next makes read-only: append(), set() and delete() throw
42
+ * instead of mutating. getSetCookie() is always empty - request headers carry no response Set-Cookie.
43
+ * Every read method delegates to the underlying Headers unchanged.
44
+ */
45
+ function readonlyHeaders(source: Headers): Headers {
46
+ return new Proxy(source, {
47
+ get(target, prop) {
48
+ if (prop === 'append' || prop === 'set' || prop === 'delete') {
49
+ return () => {
50
+ throw new Error(HEADERS_MUTATION_ERROR);
51
+ };
52
+ }
53
+ if (prop === 'getSetCookie') return () => [];
54
+ const value: unknown = Reflect.get(target, prop, target);
55
+ return typeof value === 'function' ? (value as (...args: unknown[]) => unknown).bind(target) : value;
56
+ },
57
+ });
58
+ }
59
+
60
+ export function headers() {
61
+ assertRequestApiAllowedInAfter('headers()');
62
+ assertDynamicErrorRequestApi('headers()');
63
+ assertNoDynamicApiInsideCache('headers()');
64
+ return requestApiPromise('headers()', () =>
65
+ readonlyHeaders(new Headers(requireRequest('headers()').headers)),
66
+ );
67
+ }
68
+
69
+ export interface CookieSetOptions {
70
+ name?: string;
71
+ value?: string;
72
+ path?: string;
73
+ maxAge?: number;
74
+ expires?: Date | number;
75
+ httpOnly?: boolean;
76
+ secure?: boolean;
77
+ sameSite?: 'strict' | 'lax' | 'none' | boolean;
78
+ domain?: string;
79
+ }
80
+
81
+ /**
82
+ * Next's cookies(): reads come from the request jar; writes (allowed in Server
83
+ * Actions and Route Handlers) update the jar for subsequent reads in the same
84
+ * request AND append a set-cookie onto the response-headers channel so the
85
+ * browser persists them. Supports both set(name, value, options) and
86
+ * set({ name, value, ...options }).
87
+ */
88
+ export function cookies() {
89
+ assertRequestApiAllowedInAfter('cookies()');
90
+ assertDynamicErrorRequestApi('cookies()');
91
+ assertNoDynamicApiInsideCache('cookies()');
92
+ return requestApiPromise('cookies()', () => wrapCookieJar(requireRequest('cookies()').cookies));
93
+ }
94
+
95
+ export function assertDynamicErrorRequestApi(api: 'headers()' | 'cookies()' | 'connection()'): void {
96
+ const meta = currentRenderCacheMeta();
97
+ if (!meta?.dynamicError) return;
98
+ throw lifecycleError(
99
+ `Route ${meta.route ?? ''} with \`dynamic = "error"\` couldn't be rendered statically because it used \`${api}\`.`,
100
+ );
101
+ }
102
+
103
+ function wrapCookieJar(jar: NextRequestCookies) {
104
+ return {
105
+ get: (name: CookieNameArg) => jar.get(cookieName(name)),
106
+ getAll: (name?: CookieNameArg) => (name === undefined ? jar.getAll() : jar.getAll(cookieName(name))),
107
+ has: (name: CookieNameArg) => jar.has(cookieName(name)),
108
+ get size() {
109
+ return jar.getAll().length;
110
+ },
111
+ [Symbol.iterator]() {
112
+ return jar.getAll().map(cookie => [cookie.name, cookie] as const)[Symbol.iterator]();
113
+ },
114
+ set(nameOrOptions: string | CookieSetOptions, value?: unknown, options?: CookieSetOptions) {
115
+ assertCookieMutationAllowed();
116
+ const parsed = normalizeSetArgs(nameOrOptions, value, options);
117
+ jar.set(parsed.name, parsed.value);
118
+ currentResponseHeaders()?.append('set-cookie', serializeSetCookie(parsed));
119
+ return this;
120
+ },
121
+ delete(name: CookieNameArg) {
122
+ assertCookieMutationAllowed();
123
+ const resolved = cookieName(name);
124
+ const existed = jar.delete(resolved);
125
+ currentResponseHeaders()?.append(
126
+ 'set-cookie',
127
+ serializeSetCookie({ name: resolved, value: '', path: '/', maxAge: 0 }),
128
+ );
129
+ return existed;
130
+ },
131
+ clear() {
132
+ for (const cookie of jar.getAll()) this.delete(cookie.name);
133
+ return this;
134
+ },
135
+ toString() {
136
+ return jar
137
+ .getAll()
138
+ .map(cookie => `${cookie.name}=${encodeURIComponent(cookie.value)}`)
139
+ .join('; ');
140
+ },
141
+ };
142
+ }
143
+
144
+ interface NormalizedCookie extends CookieSetOptions {
145
+ name: string;
146
+ value: string;
147
+ }
148
+
149
+ /**
150
+ * Next's RequestCookies accessors take a name OR a cookie-shaped object, so
151
+ * `cookies().get({ name: 'x', value: undefined })` reads the same entry as `cookies().get('x')`.
152
+ */
153
+ type CookieNameArg = string | { name?: string };
154
+
155
+ function cookieName(arg: CookieNameArg): string {
156
+ if (typeof arg === 'string') return arg;
157
+ return typeof arg?.name === 'string' ? arg.name : '';
158
+ }
159
+
160
+ function normalizeSetArgs(
161
+ nameOrOptions: string | CookieSetOptions,
162
+ value?: unknown,
163
+ options?: CookieSetOptions,
164
+ ): NormalizedCookie {
165
+ if (typeof nameOrOptions === 'string') {
166
+ return { ...(options ?? {}), name: nameOrOptions, value: stringifyCookieValue(value) };
167
+ }
168
+ return {
169
+ ...nameOrOptions,
170
+ name: String(nameOrOptions.name ?? ''),
171
+ value: String(nameOrOptions.value ?? ''),
172
+ };
173
+ }
174
+
175
+ function stringifyCookieValue(raw: unknown): string {
176
+ if (raw === undefined || raw === null) return '';
177
+ if (typeof raw === 'string') return raw;
178
+ if (typeof raw === 'number' || typeof raw === 'boolean' || typeof raw === 'bigint') {
179
+ return String(raw);
180
+ }
181
+ return JSON.stringify(raw) ?? '';
182
+ }
183
+
184
+ function serializeSetCookie(cookie: NormalizedCookie): string {
185
+ const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`];
186
+ parts.push(`Path=${cookie.path ?? '/'}`);
187
+ if (cookie.maxAge !== undefined) parts.push(`Max-Age=${cookie.maxAge}`);
188
+ if (cookie.expires !== undefined) {
189
+ const date = cookie.expires instanceof Date ? cookie.expires : new Date(cookie.expires);
190
+ parts.push(`Expires=${date.toUTCString()}`);
191
+ }
192
+ if (cookie.domain) parts.push(`Domain=${cookie.domain}`);
193
+ if (cookie.httpOnly) parts.push('HttpOnly');
194
+ if (cookie.secure) parts.push('Secure');
195
+ if (cookie.sameSite) {
196
+ const value = cookie.sameSite === true ? 'Strict' : capitalize(cookie.sameSite);
197
+ parts.push(`SameSite=${value}`);
198
+ }
199
+ return parts.join('; ');
200
+ }
201
+
202
+ function capitalize(value: string) {
203
+ return value.charAt(0).toUpperCase() + value.slice(1);
204
+ }
205
+
206
+ const draftCookieName = '__prerender_bypass';
207
+
208
+ export interface DraftMode {
209
+ readonly isEnabled: boolean;
210
+ enable(): void;
211
+ disable(): void;
212
+ }
213
+
214
+ export function draftMode(): Promise<DraftMode> {
215
+ // Unlike cookies()/headers(), draftMode() never opts a page into dynamic
216
+ // rendering: during build/partial prerender there is no (real) request and
217
+ // draft is simply off, so reading isEnabled must not postpone or throw.
218
+ const request = currentRequest();
219
+ const requestCookies = request?.cookies;
220
+ return Promise.resolve({
221
+ get isEnabled() {
222
+ return requestCookies?.has(draftCookieName) ?? false;
223
+ },
224
+ enable() {
225
+ assertDraftModeToggleAllowedInAfter('enable');
226
+ const cookies = requireRequest('draftMode().enable()').cookies;
227
+ const value = cookies.get(draftCookieName)?.value || bypassToken();
228
+ cookies.set(draftCookieName, value);
229
+ currentResponseHeaders()?.append(
230
+ 'set-cookie',
231
+ `${draftCookieName}=${value}; Path=/; HttpOnly; SameSite=None; Secure`,
232
+ );
233
+ },
234
+ disable() {
235
+ assertDraftModeToggleAllowedInAfter('disable');
236
+ requireRequest('draftMode().disable()').cookies.delete(draftCookieName);
237
+ currentResponseHeaders()?.append(
238
+ 'set-cookie',
239
+ `${draftCookieName}=; Path=/; Max-Age=0; HttpOnly; SameSite=None; Secure`,
240
+ );
241
+ },
242
+ });
243
+ }
244
+
245
+ function bypassToken() {
246
+ return globalThis.crypto?.randomUUID?.().replace(/-/g, '') ?? Math.random().toString(36).slice(2);
247
+ }