@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,144 @@
1
+ import { createHash } from 'node:crypto';
2
+
3
+ /**
4
+ * Runtime registry for INLINE server actions: functions with a 'use server' body directive that cross
5
+ * the server->client boundary as live closures - inline functions in server components, `.bind()`
6
+ * results, and HOC-wrapped actions. These cannot be resolved to a stable module export, so pnext (which
7
+ * has no compiler pass to hoist them) registers the live function instance at render time.
8
+ *
9
+ * Id scheme: `i:<routeId>:<hash>:<n>` where `hash` fingerprints the function source and `n`
10
+ * disambiguates same-source occurrences in tree order within one render. The id is deterministic for a
11
+ * deterministic render, so ids baked into static HTML can be re-materialized at runtime: on a registry
12
+ * miss the server re-renders the route, which re-registers the same ids, then retries the lookup.
13
+ *
14
+ * DIVERGENCE from Next: Next serializes the closure's captured variables into the page (encrypted) and
15
+ * re-hydrates them per request. pnext instead holds the live closure from the most recent render of the
16
+ * route in this process. Captured constants behave identically; closures over per-request state see the
17
+ * values of the most recent render rather than the exact render the user saw. Multi-process deployments
18
+ * only work because of the re-render path.
19
+ */
20
+
21
+ const instances = new Map<string, (...args: unknown[]) => unknown>();
22
+
23
+ /** Per-render dedup state; create one per page render and thread it through. */
24
+ export interface InstanceScope {
25
+ routeId: string;
26
+ counts: Map<string, number>;
27
+ }
28
+
29
+ export function createInstanceScope(routeId: string): InstanceScope {
30
+ return { routeId, counts: new Map() };
31
+ }
32
+
33
+ /**
34
+ * Register a live action function for the scope's route. Returns the wire id.
35
+ * Calling again with the same scope position (same source hash, same
36
+ * occurrence index) overwrites the previous render's instance.
37
+ */
38
+ export function registerActionInstance(scope: InstanceScope, fn: unknown): string {
39
+ const callable = fn as (...args: unknown[]) => unknown;
40
+ const hash = sourceHash(callable);
41
+ const index = scope.counts.get(hash) ?? 0;
42
+ scope.counts.set(hash, index + 1);
43
+ const id = `i:${scope.routeId}:${hash}:${index}`;
44
+ instances.set(id, callable);
45
+ return id;
46
+ }
47
+
48
+ /** Live instance for an id, if this process has rendered it. */
49
+ export function lookupActionInstance(id: string) {
50
+ return instances.get(id);
51
+ }
52
+
53
+ export function isInstanceActionId(id: string) {
54
+ return id.startsWith('i:');
55
+ }
56
+
57
+ /** Route id encoded in an instance action id, for the re-render fallback. */
58
+ export function instanceActionRouteId(id: string): string | undefined {
59
+ if (!isInstanceActionId(id)) return undefined;
60
+ // i:<routeId>:<hash>:<n> — routeId may itself contain ':'? Route ids are
61
+ // path-derived and never contain ':', so a simple split is safe; hash and n
62
+ // are the last two segments.
63
+ const parts = id.split(':');
64
+ if (parts.length < 4) return undefined;
65
+ return parts.slice(1, -2).join(':');
66
+ }
67
+
68
+ export function clearActionInstances() {
69
+ instances.clear();
70
+ }
71
+
72
+ /**
73
+ * Compile-time-injected registration for MODULE-SCOPE inline actions (a `'use server'` body directive
74
+ * on a top-level function that is not part of a module-level 'use server' file). The server compile
75
+ * pipeline appends a tagging call after each such declaration, which registers the live function under
76
+ * a stable id and mirrors React's registerServerReference metadata by defining `$$id` - real-world code
77
+ * introspects and even overrides it to simulate version skew. A caller-overridden `$$id` wins as the
78
+ * wire id, so skewed ids produce the failed-to-find flow exactly like Next.
79
+ */
80
+ export interface TaggedActionFunction {
81
+ (...args: unknown[]): unknown;
82
+ $$id?: string;
83
+ $$pnextAssignedId?: string;
84
+ $$pnextInlineAction?: true;
85
+ }
86
+
87
+ declare global {
88
+ var __pnextTagInlineAction: ((fn: unknown, id: string) => void) | undefined;
89
+ var __pnextMarkInlineAction: (<T>(fn: T) => T) | undefined;
90
+ }
91
+
92
+ /**
93
+ * Compile-time-injected mark for ANONYMOUS inline server actions - a function expression whose body
94
+ * opens with `'use server'` and that is passed straight to a prop/argument. Such a function has no name
95
+ * to hang a stable id on, and the runtime cannot recover the directive from String(fn): the loader that
96
+ * evaluates the compiled module strips directive prologues. The mark is the only thing left that
97
+ * separates it from a plain client handler at serialization time.
98
+ */
99
+ globalThis.__pnextMarkInlineAction = <T>(fn: T): T => {
100
+ if (typeof fn === 'function') (fn as TaggedActionFunction).$$pnextInlineAction = true;
101
+ return fn;
102
+ };
103
+
104
+ /** True for a compile-marked anonymous inline 'use server' function. */
105
+ export function isMarkedInlineAction(fn: unknown): boolean {
106
+ return typeof fn === 'function' && (fn as TaggedActionFunction).$$pnextInlineAction === true;
107
+ }
108
+
109
+ globalThis.__pnextTagInlineAction = (fn: unknown, id: string) => {
110
+ if (typeof fn !== 'function') return;
111
+ const tagged = fn as TaggedActionFunction;
112
+ instances.set(id, tagged);
113
+ tagged.$$pnextAssignedId = id;
114
+ if (!('$$id' in tagged)) {
115
+ Object.defineProperty(tagged, '$$id', { value: id, configurable: true, writable: true });
116
+ }
117
+ };
118
+
119
+ /** Caller-overridden $$id (version-skew simulation) wins as the wire id. */
120
+ export function overriddenActionId(fn: unknown): string | undefined {
121
+ if (typeof fn !== 'function') return undefined;
122
+ const tagged = fn as TaggedActionFunction;
123
+ if (typeof tagged.$$id === 'string' && tagged.$$id !== tagged.$$pnextAssignedId) {
124
+ return tagged.$$id;
125
+ }
126
+ return undefined;
127
+ }
128
+
129
+ /** Stable assigned id for a compile-tagged module-scope inline action. */
130
+ export function assignedInlineActionId(fn: unknown): string | undefined {
131
+ if (typeof fn !== 'function') return undefined;
132
+ const tagged = fn as TaggedActionFunction;
133
+ return typeof tagged.$$pnextAssignedId === 'string' ? tagged.$$pnextAssignedId : undefined;
134
+ }
135
+
136
+ function sourceHash(fn: (...args: unknown[]) => unknown) {
137
+ let source = '';
138
+ try {
139
+ source = String(fn);
140
+ } catch {
141
+ source = 'opaque';
142
+ }
143
+ return createHash('sha256').update(source).digest('hex').slice(0, 10);
144
+ }
@@ -0,0 +1,109 @@
1
+ // Server Actions CSRF origin validation (COMPAT).
2
+ //
3
+ // Mirrors Next's action-handler origin check: an action POST carrying an
4
+ // `origin` header whose host does not match the request host (`x-forwarded-host`
5
+ // preferred, else `host`) is rejected unless the origin is whitelisted via
6
+ // `experimental.serverActions.allowedOrigins`. Opaque origins ('null', e.g. a
7
+ // sandboxed iframe) are treated as the literal host 'null'. A missing origin is
8
+ // allowed (handcrafted request; can't carry unwilling credentials).
9
+
10
+ import { getNextConfig } from '../next/config-loader';
11
+
12
+ export const INVALID_ORIGIN_MESSAGE = 'Invalid Server Actions request.';
13
+
14
+ /** Read experimental.serverActions.allowedOrigins (also the top-level key). */
15
+ export function allowedOrigins(): string[] {
16
+ const config = getNextConfig();
17
+ const top = (config.serverActions as { allowedOrigins?: unknown } | undefined)?.allowedOrigins;
18
+ const experimental = config.experimental as
19
+ | { serverActions?: { allowedOrigins?: unknown } | boolean }
20
+ | undefined;
21
+ const exp =
22
+ experimental && typeof experimental.serverActions === 'object'
23
+ ? experimental.serverActions.allowedOrigins
24
+ : undefined;
25
+ const list = top ?? exp;
26
+ return Array.isArray(list) ? list.filter((v): v is string => typeof v === 'string') : [];
27
+ }
28
+
29
+ function firstHeaderValue(value: string | null): string | undefined {
30
+ if (value === null) return undefined;
31
+ return value.split(',')[0]?.trim() || undefined;
32
+ }
33
+
34
+ /** Request host used to compare against the origin (x-forwarded-host > host). */
35
+ function requestHost(headers: Headers): string | undefined {
36
+ return firstHeaderValue(headers.get('x-forwarded-host')) ?? headers.get('host') ?? undefined;
37
+ }
38
+
39
+ function originHost(headers: Headers): string | undefined {
40
+ const origin = headers.get('origin');
41
+ if (origin === null) return undefined;
42
+ if (origin === 'null') return 'null';
43
+ try {
44
+ return new URL(origin).host;
45
+ } catch {
46
+ return undefined;
47
+ }
48
+ }
49
+
50
+ function matchWildcardDomain(domain: string, pattern: string): boolean {
51
+ const normalizedDomain = domain.replace(/[A-Z]/g, c => c.toLowerCase());
52
+ const normalizedPattern = pattern.replace(/[A-Z]/g, c => c.toLowerCase());
53
+ const domainParts = normalizedDomain.split('.');
54
+ const patternParts = normalizedPattern.split('.');
55
+ if (patternParts.length < 1) return false;
56
+ if (domainParts.length < patternParts.length) return false;
57
+ if (patternParts.length === 1 && (patternParts[0] === '*' || patternParts[0] === '**')) {
58
+ return false;
59
+ }
60
+ while (patternParts.length) {
61
+ const patternPart = patternParts.pop();
62
+ const domainPart = domainParts.pop();
63
+ if (patternPart === '') return false;
64
+ if (patternPart === '*') {
65
+ if (domainPart) continue;
66
+ return false;
67
+ }
68
+ if (patternPart === '**') {
69
+ if (patternParts.length > 0) return false;
70
+ return domainPart !== undefined;
71
+ }
72
+ if (domainPart !== patternPart) return false;
73
+ }
74
+ return domainParts.length === 0;
75
+ }
76
+
77
+ function isCsrfOriginAllowed(originDomain: string, origins: string[]): boolean {
78
+ const normalizedOrigin = originDomain.replace(/[A-Z]/g, c => c.toLowerCase());
79
+ return origins.some(allowed => {
80
+ if (!allowed) return false;
81
+ const normalizedAllowed = allowed.replace(/[A-Z]/g, c => c.toLowerCase());
82
+ return normalizedAllowed === normalizedOrigin || matchWildcardDomain(originDomain, allowed);
83
+ });
84
+ }
85
+
86
+ /**
87
+ * Returns true when the request passes the CSRF origin check (or has no origin
88
+ * to check). Returns false when the action must be aborted; the caller responds
89
+ * with the "Invalid Server Actions request." error. Logs the mismatch like Next.
90
+ */
91
+ export function isActionOriginAllowed(request: Request): boolean {
92
+ const headers = request.headers;
93
+ const oHost = originHost(headers);
94
+ if (oHost === undefined) return true;
95
+ const host = requestHost(headers);
96
+ if (host !== undefined && oHost === host) return true;
97
+ if (isCsrfOriginAllowed(oHost, allowedOrigins())) return true;
98
+ if (host) {
99
+ const hostType = headers.has('x-forwarded-host') ? 'x-forwarded-host' : 'host';
100
+ console.error(
101
+ `\`${hostType}\` header with value \`${host}\` does not match \`origin\` header with value \`${oHost}\` from a forwarded Server Actions request. Aborting the action.`,
102
+ );
103
+ } else {
104
+ console.error(
105
+ '`x-forwarded-host` or `host` headers are not provided. One of these is needed to compare the `origin` header from a forwarded Server Actions request. Aborting the action.',
106
+ );
107
+ }
108
+ return false;
109
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Wire contract shared by the client runtime and the server endpoint. Keeping
3
+ * the constants in one place guarantees the two sides never drift.
4
+ *
5
+ * The wire shape mirrors Next.js where tests can observe it:
6
+ * - Actions POST to the CURRENT PAGE URL (not a bespoke endpoint) with the
7
+ * action id in the `next-action` header.
8
+ * - JSON-args bodies use content-type text/plain; form submissions send
9
+ * multipart/form-data (or urlencoded for no-JS submits).
10
+ * - Server errors come back as text/plain; the client treats any non-ok
11
+ * response with a different content-type as "unexpected response".
12
+ */
13
+
14
+ /** Header carrying the action id from client -> server. Mirrors Next's name. */
15
+ export const ACTION_ID_HEADER = 'next-action';
16
+
17
+ /**
18
+ * Response header set on a redirect result so the client can distinguish a
19
+ * framework redirect (soft-navigable) from an arbitrary 303 it should follow.
20
+ */
21
+ export const ACTION_REDIRECT_HEADER = 'x-action-redirect';
22
+
23
+ /**
24
+ * Response header telling the client to soft-refresh the current route after a
25
+ * successful action: set when the action revalidated data (revalidatePath/
26
+ * revalidateTag) or mutated cookies, mirroring when Next invalidates its
27
+ * client router cache.
28
+ */
29
+ export const ACTION_REFRESH_HEADER = 'x-pnext-action-refresh';
30
+
31
+ /** Next's public action revalidation header. `1` means static+dynamic invalidated. */
32
+ export const NEXT_ACTION_REVALIDATED_HEADER = 'x-action-revalidated';
33
+
34
+ /** Response header marking a notFound() result (body is the 404 page HTML). */
35
+ export const ACTION_NOT_FOUND_HEADER = 'x-pnext-action-not-found';
36
+
37
+ /**
38
+ * Next's own action-not-found marker (`x-nextjs-action-not-found`). Set on the
39
+ * 404 response for an unknown/unrecognized action id so tooling and the
40
+ * no-server-actions suite observe it; distinct from ACTION_NOT_FOUND_HEADER,
41
+ * which drives the client's 404-page swap for a notFound() result.
42
+ */
43
+ export const NEXT_ACTION_NOT_FOUND_HEADER = 'x-nextjs-action-not-found';
44
+
45
+ /** Response header carrying redirect(location, RedirectType.replace) intent. */
46
+ export const REDIRECT_TYPE_HEADER = 'x-pnext-redirect-type';
47
+
48
+ /**
49
+ * Response header carrying the boundary digest of a masked action error. Next
50
+ * transports the digest to the client so a caught action error exposes
51
+ * `error.digest`; the redacted 500 body hides the message, so the digest rides
52
+ * this header and the client stamps it onto the thrown Error. Computed with the
53
+ * same deterministic scheme as the render boundary digest (error-serialize.ts),
54
+ * so it matches the `digest: '<digest>'` line printed to the server log.
55
+ */
56
+ export const ACTION_ERROR_DIGEST_HEADER = 'x-pnext-action-error-digest';
57
+
58
+ /** Argument marker for a server-action function reference passed as an arg. */
59
+ export const ARG_ACTION_MARKER = '$$pnext_action_ref';
60
+
61
+ /**
62
+ * Hidden form field carrying the action id for no-JS progressive enhancement, emitted in
63
+ * <form action={serverAction}> so a plain HTML submit - which cannot set the next-action header -
64
+ * still identifies the form's action. A per-button `formAction` override uses SUBMIT_ACTION_ID_FIELD
65
+ * instead so it is unambiguous regardless of DOM order: the form-level field renders LAST inside
66
+ * client-island forms, so a shared field name could not tell the two apart.
67
+ */
68
+ export const ACTION_ID_FIELD = '$pnext_action_id';
69
+
70
+ /**
71
+ * Field name a `<button formAction>` submitter carries its action id under, submitted only when it is
72
+ * the submitter. Distinct from ACTION_ID_FIELD so the server can prefer a submitter override over the
73
+ * surrounding form's default without relying on field order - a no-JS submit has no `event.submitter`
74
+ * to disambiguate them.
75
+ */
76
+ export const SUBMIT_ACTION_ID_FIELD = '$pnext_submit_action_id';
77
+
78
+ /**
79
+ * Multipart field name carrying the JSON-encoded argument list when a call
80
+ * mixes JSON args with FormData/File values (e.g. bound args ahead of a form
81
+ * submission). Slots reference the other multipart parts.
82
+ */
83
+ export const ACTION_ARGS_FIELD = '$pnext_args';
84
+
85
+ /**
86
+ * Hidden form field carrying a useActionState form's CURRENT state (JSON) for
87
+ * no-JS progressive submissions. The server runs `action(state, formData)` and
88
+ * re-renders the page with the result as the form's new state.
89
+ */
90
+ export const FORM_STATE_FIELD = '$pnext_form_state';
91
+
92
+ /** Marker key for a FormData argument slot inside ACTION_ARGS_FIELD JSON. */
93
+ export const ARG_FORMDATA_MARKER = '$$pnext_fd';
94
+
95
+ /** Marker key for a File argument slot inside ACTION_ARGS_FIELD JSON. */
96
+ export const ARG_FILE_MARKER = '$$pnext_file';
97
+
98
+ /** Prefix for multipart parts belonging to a FormData argument slot. */
99
+ export const ARG_FORMDATA_PREFIX = '$pnext_fd_';
100
+
101
+ /** Prefix for multipart parts holding a File argument slot. */
102
+ export const ARG_FILE_PREFIX = '$pnext_file_';
103
+
104
+ /** Serialized-prop marker for a server-action function passed to an island. */
105
+ export const PROP_ACTION_MARKER = '$$pnextAction';
106
+
107
+ /** Maximum action argument list length, mirroring Next's guard. */
108
+ export const MAX_ACTION_ARGS = 1000;
109
+
110
+ export function tooManyArgsMessage(count: number) {
111
+ return `Server Action arguments list is too long (${count}). Maximum allowed is ${MAX_ACTION_ARGS}.`;
112
+ }
113
+
114
+ /**
115
+ * The exact text a masked production action error renders as on the client (React's minified
116
+ * flight-client error). Next's e2e tests assert this string verbatim via their error boundaries, so
117
+ * prod builds send it as the text/plain error body and the client throws it as the Error message.
118
+ */
119
+ export const MASKED_ACTION_ERROR =
120
+ 'Minified React error #441; visit https://react.dev/errors/441 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.';
121
+
122
+ /** JSON success envelope: { data } (application/json, status 200). */
123
+ export interface ActionSuccessBody {
124
+ data: unknown;
125
+ }
@@ -0,0 +1,74 @@
1
+ import { actionId } from './ids';
2
+
3
+ /**
4
+ * In-process registry mapping a stable actionId -> the module + export that
5
+ * implements it. Populated at build/startup by the wiring agent from the route
6
+ * manifest; read by the runtime endpoint to resolve an incoming action id.
7
+ *
8
+ * The id is computed with the same `actionId` scheme the client stub uses, so a
9
+ * registration and its client stub always agree on the wire id.
10
+ */
11
+
12
+ export interface ActionEntry {
13
+ /** actionId(filePath, exportName, root) — the wire id. */
14
+ id: string;
15
+ /** Module path the wiring agent will hand to importModule(). */
16
+ modulePath: string;
17
+ /** Named export within the module implementing the action. */
18
+ exportName: string;
19
+ /**
20
+ * The SOURCE file the id was computed from (workspace-relative when the registration passed `root`,
21
+ * otherwise as given). Used to decide whether a POSTed route's module graph contains the action -
22
+ * Next's "forwarded action" discrimination, see serve.ts isForwardedAction.
23
+ */
24
+ filePath: string;
25
+ }
26
+
27
+ const registry = new Map<string, ActionEntry>();
28
+
29
+ /**
30
+ * Register every action export of a module. `filePath` identifies the module for
31
+ * id computation (workspace-relative when `root` is given); `modulePath` is what
32
+ * gets stored for the endpoint's importModule() (may differ, e.g. a compiled
33
+ * output path). When `modulePath` is omitted it defaults to `filePath`.
34
+ *
35
+ * Returns the entries that were registered (with their ids), so callers can emit
36
+ * matching client stubs.
37
+ */
38
+ export function registerActionModule(
39
+ filePath: string,
40
+ exportNames: readonly string[],
41
+ options: { root?: string; modulePath?: string } = {},
42
+ ): ActionEntry[] {
43
+ const modulePath = options.modulePath ?? filePath;
44
+ const entries: ActionEntry[] = [];
45
+ for (const exportName of exportNames) {
46
+ const id = actionId(filePath, exportName, options.root);
47
+ const entry: ActionEntry = { id, modulePath, exportName, filePath };
48
+ registry.set(id, entry);
49
+ entries.push(entry);
50
+ }
51
+ return entries;
52
+ }
53
+
54
+ /** Resolve an action id to its module + export, or undefined if unknown. */
55
+ export function lookupAction(id: string): { modulePath: string; exportName: string } | undefined {
56
+ const entry = registry.get(id);
57
+ if (!entry) return undefined;
58
+ return { modulePath: entry.modulePath, exportName: entry.exportName };
59
+ }
60
+
61
+ /** The source file a registered action id was computed from, or undefined. */
62
+ export function lookupActionSource(id: string): string | undefined {
63
+ return registry.get(id)?.filePath;
64
+ }
65
+
66
+ /** Snapshot of all registered actions. */
67
+ export function allActions(): ActionEntry[] {
68
+ return [...registry.values()];
69
+ }
70
+
71
+ /** Clear the registry (test/rebuild support). */
72
+ export function clearActions(): void {
73
+ registry.clear();
74
+ }