vinext 0.0.49 → 0.0.51
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.
- package/dist/build/client-build-config.js.map +1 -1
- package/dist/build/google-fonts/build-url.js.map +1 -1
- package/dist/build/google-fonts/fallback-metrics-data.js +14031 -0
- package/dist/build/google-fonts/fallback-metrics-data.js.map +1 -0
- package/dist/build/google-fonts/fallback-metrics.d.ts +13 -0
- package/dist/build/google-fonts/fallback-metrics.js +46 -0
- package/dist/build/google-fonts/fallback-metrics.js.map +1 -0
- package/dist/build/google-fonts/get-axes.js.map +1 -1
- package/dist/build/google-fonts/sort-variants.js.map +1 -1
- package/dist/build/google-fonts/validate.js.map +1 -1
- package/dist/build/layout-classification.js.map +1 -1
- package/dist/build/nitro-route-rules.js.map +1 -1
- package/dist/build/precompress.d.ts +13 -2
- package/dist/build/precompress.js +12 -3
- package/dist/build/precompress.js.map +1 -1
- package/dist/build/prerender.d.ts +17 -1
- package/dist/build/prerender.js +114 -23
- package/dist/build/prerender.js.map +1 -1
- package/dist/build/report.d.ts +5 -4
- package/dist/build/report.js +196 -348
- package/dist/build/report.js.map +1 -1
- package/dist/build/route-classification-injector.js.map +1 -1
- package/dist/build/route-classification-manifest.js.map +1 -1
- package/dist/build/run-prerender.js.map +1 -1
- package/dist/build/server-manifest.js.map +1 -1
- package/dist/build/ssr-manifest.js.map +1 -1
- package/dist/build/standalone.js.map +1 -1
- package/dist/build/static-export.js.map +1 -1
- package/dist/check.js +2 -1
- package/dist/check.js.map +1 -1
- package/dist/cli-args.js.map +1 -1
- package/dist/cli.js +68 -7
- package/dist/cli.js.map +1 -1
- package/dist/client/instrumentation-client-state.js.map +1 -1
- package/dist/client/validate-module-path.js.map +1 -1
- package/dist/client/vinext-next-data.d.ts +5 -1
- package/dist/client/window-next.d.ts +151 -0
- package/dist/client/window-next.js +48 -0
- package/dist/client/window-next.js.map +1 -0
- package/dist/cloudflare/kv-cache-handler.js.map +1 -1
- package/dist/cloudflare/tpr.js +2 -1
- package/dist/cloudflare/tpr.js.map +1 -1
- package/dist/config/config-matchers.d.ts +3 -1
- package/dist/config/config-matchers.js +5 -4
- package/dist/config/config-matchers.js.map +1 -1
- package/dist/config/dotenv.d.ts +11 -1
- package/dist/config/dotenv.js.map +1 -1
- package/dist/config/next-config.d.ts +93 -6
- package/dist/config/next-config.js +233 -6
- package/dist/config/next-config.js.map +1 -1
- package/dist/config/tsconfig-paths.d.ts +13 -0
- package/dist/config/tsconfig-paths.js +117 -0
- package/dist/config/tsconfig-paths.js.map +1 -0
- package/dist/deploy.js +16 -7
- package/dist/deploy.js.map +1 -1
- package/dist/entries/app-browser-entry.d.ts +3 -1
- package/dist/entries/app-browser-entry.js +36 -2
- package/dist/entries/app-browser-entry.js.map +1 -1
- package/dist/entries/app-rsc-entry.d.ts +19 -1
- package/dist/entries/app-rsc-entry.js +49 -12
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/app-rsc-manifest.d.ts +9 -0
- package/dist/entries/app-rsc-manifest.js +8 -1
- package/dist/entries/app-rsc-manifest.js.map +1 -1
- package/dist/entries/app-ssr-entry.js.map +1 -1
- package/dist/entries/pages-client-entry.js +3 -5
- package/dist/entries/pages-client-entry.js.map +1 -1
- package/dist/entries/pages-entry-helpers.js.map +1 -1
- package/dist/entries/pages-server-entry.js +34 -1
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/entries/runtime-entry-module.js.map +1 -1
- package/dist/index.js +204 -53
- package/dist/index.js.map +1 -1
- package/dist/init.js.map +1 -1
- package/dist/plugins/async-hooks-stub.js.map +1 -1
- package/dist/plugins/client-reference-dedup.d.ts +15 -2
- package/dist/plugins/client-reference-dedup.js +138 -16
- package/dist/plugins/client-reference-dedup.js.map +1 -1
- package/dist/plugins/fonts.d.ts +2 -2
- package/dist/plugins/fonts.js +15 -6
- package/dist/plugins/fonts.js.map +1 -1
- package/dist/plugins/instrumentation-client.js.map +1 -1
- package/dist/plugins/og-assets.js.map +1 -1
- package/dist/plugins/optimize-imports.js.map +1 -1
- package/dist/plugins/postcss.js.map +1 -1
- package/dist/plugins/rsc-client-reference-loaders.d.ts +7 -0
- package/dist/plugins/rsc-client-reference-loaders.js +48 -0
- package/dist/plugins/rsc-client-reference-loaders.js.map +1 -0
- package/dist/plugins/rsc-client-shim-excludes.js.map +1 -1
- package/dist/plugins/sass.d.ts +34 -0
- package/dist/plugins/sass.js +22 -0
- package/dist/plugins/sass.js.map +1 -0
- package/dist/plugins/server-externals-manifest.js.map +1 -1
- package/dist/plugins/strip-server-exports.js.map +1 -1
- package/dist/routing/app-route-graph.d.ts +78 -6
- package/dist/routing/app-route-graph.js +241 -25
- package/dist/routing/app-route-graph.js.map +1 -1
- package/dist/routing/app-router.js.map +1 -1
- package/dist/routing/file-matcher.js.map +1 -1
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/routing/route-matching.js.map +1 -1
- package/dist/routing/route-pattern.d.ts +56 -1
- package/dist/routing/route-pattern.js +60 -1
- package/dist/routing/route-pattern.js.map +1 -1
- package/dist/routing/route-trie.js.map +1 -1
- package/dist/routing/route-validation.js.map +1 -1
- package/dist/routing/utils.js.map +1 -1
- package/dist/server/api-handler.js.map +1 -1
- package/dist/server/app-browser-action-result.d.ts +44 -0
- package/dist/server/app-browser-action-result.js +79 -0
- package/dist/server/app-browser-action-result.js.map +1 -0
- package/dist/server/app-browser-entry.js +330 -133
- package/dist/server/app-browser-entry.js.map +1 -1
- package/dist/server/app-browser-error.js.map +1 -1
- package/dist/server/app-browser-hydration.d.ts +31 -0
- package/dist/server/app-browser-hydration.js +30 -0
- package/dist/server/app-browser-hydration.js.map +1 -0
- package/dist/server/app-browser-navigation-controller.d.ts +20 -4
- package/dist/server/app-browser-navigation-controller.js +90 -23
- package/dist/server/app-browser-navigation-controller.js.map +1 -1
- package/dist/server/app-browser-popstate.d.ts +16 -0
- package/dist/server/app-browser-popstate.js +17 -0
- package/dist/server/app-browser-popstate.js.map +1 -0
- package/dist/server/app-browser-rsc-redirect.d.ts +28 -0
- package/dist/server/app-browser-rsc-redirect.js +37 -0
- package/dist/server/app-browser-rsc-redirect.js.map +1 -0
- package/dist/server/app-browser-state.d.ts +27 -23
- package/dist/server/app-browser-state.js +158 -54
- package/dist/server/app-browser-state.js.map +1 -1
- package/dist/server/app-browser-stream.d.ts +9 -4
- package/dist/server/app-browser-stream.js +29 -8
- package/dist/server/app-browser-stream.js.map +1 -1
- package/dist/server/app-browser-visible-commit.d.ts +11 -1
- package/dist/server/app-browser-visible-commit.js +69 -21
- package/dist/server/app-browser-visible-commit.js.map +1 -1
- package/dist/server/app-client-reference-preloader.js.map +1 -1
- package/dist/server/app-elements-wire.d.ts +43 -6
- package/dist/server/app-elements-wire.js +121 -5
- package/dist/server/app-elements-wire.js.map +1 -1
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +2 -2
- package/dist/server/app-elements.js.map +1 -1
- package/dist/server/app-fallback-renderer.d.ts +10 -1
- package/dist/server/app-fallback-renderer.js +37 -1
- package/dist/server/app-fallback-renderer.js.map +1 -1
- package/dist/server/app-history-state.d.ts +26 -0
- package/dist/server/app-history-state.js +53 -0
- package/dist/server/app-history-state.js.map +1 -0
- package/dist/server/app-hook-warning-suppression.js.map +1 -1
- package/dist/server/app-middleware.d.ts +1 -1
- package/dist/server/app-middleware.js +4 -9
- package/dist/server/app-middleware.js.map +1 -1
- package/dist/server/app-mounted-slots-header.js.map +1 -1
- package/dist/server/app-page-boundary-render.d.ts +11 -1
- package/dist/server/app-page-boundary-render.js +27 -19
- package/dist/server/app-page-boundary-render.js.map +1 -1
- package/dist/server/app-page-boundary.d.ts +1 -0
- package/dist/server/app-page-boundary.js +10 -7
- package/dist/server/app-page-boundary.js.map +1 -1
- package/dist/server/app-page-cache.d.ts +23 -3
- package/dist/server/app-page-cache.js +63 -27
- package/dist/server/app-page-cache.js.map +1 -1
- package/dist/server/app-page-dispatch.d.ts +11 -1
- package/dist/server/app-page-dispatch.js +85 -14
- package/dist/server/app-page-dispatch.js.map +1 -1
- package/dist/server/app-page-element-builder.d.ts +10 -1
- package/dist/server/app-page-element-builder.js +38 -6
- package/dist/server/app-page-element-builder.js.map +1 -1
- package/dist/server/app-page-execution.js +2 -3
- package/dist/server/app-page-execution.js.map +1 -1
- package/dist/server/app-page-head.d.ts +7 -0
- package/dist/server/app-page-head.js +6 -1
- package/dist/server/app-page-head.js.map +1 -1
- package/dist/server/app-page-method.js.map +1 -1
- package/dist/server/app-page-params.js.map +1 -1
- package/dist/server/app-page-probe.d.ts +23 -1
- package/dist/server/app-page-probe.js +29 -1
- package/dist/server/app-page-probe.js.map +1 -1
- package/dist/server/app-page-render-observation.d.ts +35 -0
- package/dist/server/app-page-render-observation.js +68 -0
- package/dist/server/app-page-render-observation.js.map +1 -0
- package/dist/server/app-page-render.d.ts +12 -2
- package/dist/server/app-page-render.js +90 -7
- package/dist/server/app-page-render.js.map +1 -1
- package/dist/server/app-page-request.d.ts +1 -0
- package/dist/server/app-page-request.js +2 -1
- package/dist/server/app-page-request.js.map +1 -1
- package/dist/server/app-page-response.d.ts +2 -0
- package/dist/server/app-page-response.js +18 -7
- package/dist/server/app-page-response.js.map +1 -1
- package/dist/server/app-page-route-wiring.d.ts +9 -3
- package/dist/server/app-page-route-wiring.js +91 -62
- package/dist/server/app-page-route-wiring.js.map +1 -1
- package/dist/server/app-page-segment-state.d.ts +10 -0
- package/dist/server/app-page-segment-state.js +87 -0
- package/dist/server/app-page-segment-state.js.map +1 -0
- package/dist/server/app-page-stream.d.ts +9 -2
- package/dist/server/app-page-stream.js +4 -1
- package/dist/server/app-page-stream.js.map +1 -1
- package/dist/server/app-post-middleware-context.js.map +1 -1
- package/dist/server/app-prerender-endpoints.js.map +1 -1
- package/dist/server/app-prerender-static-params.js.map +1 -1
- package/dist/server/app-render-dependency.js.map +1 -1
- package/dist/server/app-request-context.js.map +1 -1
- package/dist/server/app-route-handler-cache.js.map +1 -1
- package/dist/server/app-route-handler-dispatch.js +3 -1
- package/dist/server/app-route-handler-dispatch.js.map +1 -1
- package/dist/server/app-route-handler-execution.js.map +1 -1
- package/dist/server/app-route-handler-policy.js +1 -0
- package/dist/server/app-route-handler-policy.js.map +1 -1
- package/dist/server/app-route-handler-response.js +4 -3
- package/dist/server/app-route-handler-response.js.map +1 -1
- package/dist/server/app-route-handler-runtime.js.map +1 -1
- package/dist/server/app-router-entry.js +7 -15
- package/dist/server/app-router-entry.js.map +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +23 -2
- package/dist/server/app-rsc-cache-busting.js +75 -19
- package/dist/server/app-rsc-cache-busting.js.map +1 -1
- package/dist/server/app-rsc-embedded-chunks.d.ts +9 -0
- package/dist/server/app-rsc-embedded-chunks.js +34 -0
- package/dist/server/app-rsc-embedded-chunks.js.map +1 -0
- package/dist/server/app-rsc-error-handler.js.map +1 -1
- package/dist/server/app-rsc-errors.d.ts +4 -1
- package/dist/server/app-rsc-errors.js +1 -1
- package/dist/server/app-rsc-errors.js.map +1 -1
- package/dist/server/app-rsc-handler.d.ts +18 -1
- package/dist/server/app-rsc-handler.js +55 -16
- package/dist/server/app-rsc-handler.js.map +1 -1
- package/dist/server/app-rsc-render-mode.d.ts +11 -0
- package/dist/server/app-rsc-render-mode.js +21 -0
- package/dist/server/app-rsc-render-mode.js.map +1 -0
- package/dist/server/app-rsc-request-normalization.d.ts +4 -1
- package/dist/server/app-rsc-request-normalization.js +7 -2
- package/dist/server/app-rsc-request-normalization.js.map +1 -1
- package/dist/server/app-rsc-response-finalizer.d.ts +2 -1
- package/dist/server/app-rsc-response-finalizer.js +6 -1
- package/dist/server/app-rsc-response-finalizer.js.map +1 -1
- package/dist/server/app-rsc-route-matching.d.ts +23 -0
- package/dist/server/app-rsc-route-matching.js +45 -23
- package/dist/server/app-rsc-route-matching.js.map +1 -1
- package/dist/server/app-segment-config.js.map +1 -1
- package/dist/server/app-server-action-execution.d.ts +51 -5
- package/dist/server/app-server-action-execution.js +161 -51
- package/dist/server/app-server-action-execution.js.map +1 -1
- package/dist/server/app-ssr-entry.d.ts +7 -0
- package/dist/server/app-ssr-entry.js +44 -14
- package/dist/server/app-ssr-entry.js.map +1 -1
- package/dist/server/app-ssr-error-meta.d.ts +14 -0
- package/dist/server/app-ssr-error-meta.js +50 -0
- package/dist/server/app-ssr-error-meta.js.map +1 -0
- package/dist/server/app-ssr-stream.d.ts +1 -1
- package/dist/server/app-ssr-stream.js +9 -12
- package/dist/server/app-ssr-stream.js.map +1 -1
- package/dist/server/app-static-generation.js.map +1 -1
- package/dist/server/artifact-compatibility.d.ts +12 -2
- package/dist/server/artifact-compatibility.js +12 -8
- package/dist/server/artifact-compatibility.js.map +1 -1
- package/dist/server/cache-control.js +1 -0
- package/dist/server/cache-control.js.map +1 -1
- package/dist/server/cache-proof.d.ts +124 -5
- package/dist/server/cache-proof.js +416 -18
- package/dist/server/cache-proof.js.map +1 -1
- package/dist/server/csp.js.map +1 -1
- package/dist/server/dev-error-overlay-store.js.map +1 -1
- package/dist/server/dev-error-overlay.js +5 -0
- package/dist/server/dev-error-overlay.js.map +1 -1
- package/dist/server/dev-lockfile.d.ts +110 -0
- package/dist/server/dev-lockfile.js +180 -0
- package/dist/server/dev-lockfile.js.map +1 -0
- package/dist/server/dev-module-runner.js.map +1 -1
- package/dist/server/dev-origin-check.js.map +1 -1
- package/dist/server/dev-route-files.js.map +1 -1
- package/dist/server/dev-server.js +23 -10
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/file-based-metadata.d.ts +13 -0
- package/dist/server/file-based-metadata.js +49 -2
- package/dist/server/file-based-metadata.js.map +1 -1
- package/dist/server/headers.d.ts +81 -0
- package/dist/server/headers.js +104 -0
- package/dist/server/headers.js.map +1 -0
- package/dist/server/html.js +1 -1
- package/dist/server/html.js.map +1 -1
- package/dist/server/http-error-responses.d.ts +10 -0
- package/dist/server/http-error-responses.js +11 -1
- package/dist/server/http-error-responses.js.map +1 -1
- package/dist/server/image-optimization.d.ts +11 -1
- package/dist/server/image-optimization.js.map +1 -1
- package/dist/server/implicit-tags.js +2 -1
- package/dist/server/implicit-tags.js.map +1 -1
- package/dist/server/instrumentation-runtime.js.map +1 -1
- package/dist/server/instrumentation.js.map +1 -1
- package/dist/server/isr-cache.d.ts +12 -2
- package/dist/server/isr-cache.js +16 -5
- package/dist/server/isr-cache.js.map +1 -1
- package/dist/server/metadata-route-build-data.js.map +1 -1
- package/dist/server/metadata-route-response.js +22 -5
- package/dist/server/metadata-route-response.js.map +1 -1
- package/dist/server/metadata-routes.js +27 -8
- package/dist/server/metadata-routes.js.map +1 -1
- package/dist/server/middleware-matcher.js.map +1 -1
- package/dist/server/middleware-request-headers.d.ts +4 -1
- package/dist/server/middleware-request-headers.js +15 -8
- package/dist/server/middleware-request-headers.js.map +1 -1
- package/dist/server/middleware-response-headers.d.ts +2 -1
- package/dist/server/middleware-response-headers.js +1 -1
- package/dist/server/middleware-response-headers.js.map +1 -1
- package/dist/server/middleware-runtime.d.ts +1 -0
- package/dist/server/middleware-runtime.js +7 -3
- package/dist/server/middleware-runtime.js.map +1 -1
- package/dist/server/middleware.d.ts +12 -0
- package/dist/server/middleware.js +12 -0
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/navigation-planner.d.ts +133 -0
- package/dist/server/navigation-planner.js +432 -0
- package/dist/server/navigation-planner.js.map +1 -0
- package/dist/server/navigation-trace.d.ts +19 -2
- package/dist/server/navigation-trace.js +20 -1
- package/dist/server/navigation-trace.js.map +1 -1
- package/dist/server/next-error-digest.d.ts +3 -2
- package/dist/server/next-error-digest.js +4 -2
- package/dist/server/next-error-digest.js.map +1 -1
- package/dist/server/normalize-path.d.ts +2 -1
- package/dist/server/normalize-path.js +4 -1
- package/dist/server/normalize-path.js.map +1 -1
- package/dist/server/pages-api-route.js +1 -0
- package/dist/server/pages-api-route.js.map +1 -1
- package/dist/server/pages-i18n.js.map +1 -1
- package/dist/server/pages-media-type.js.map +1 -1
- package/dist/server/pages-node-compat.js.map +1 -1
- package/dist/server/pages-page-data.d.ts +3 -2
- package/dist/server/pages-page-data.js +27 -5
- package/dist/server/pages-page-data.js.map +1 -1
- package/dist/server/pages-page-response.js +2 -1
- package/dist/server/pages-page-response.js.map +1 -1
- package/dist/server/prerender-work-unit-setup.js +1 -1
- package/dist/server/prerender-work-unit-setup.js.map +1 -1
- package/dist/server/prod-server.d.ts +28 -1
- package/dist/server/prod-server.js +97 -22
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/request-log.js.map +1 -1
- package/dist/server/request-pipeline.d.ts +1 -13
- package/dist/server/request-pipeline.js +3 -25
- package/dist/server/request-pipeline.js.map +1 -1
- package/dist/server/rsc-stream-hints.js.map +1 -1
- package/dist/server/seed-cache.js.map +1 -1
- package/dist/server/server-action-not-found.d.ts +16 -3
- package/dist/server/server-action-not-found.js +22 -4
- package/dist/server/server-action-not-found.js.map +1 -1
- package/dist/server/server-globals.d.ts +5 -0
- package/dist/server/server-globals.js +37 -0
- package/dist/server/server-globals.js.map +1 -0
- package/dist/server/socket-error-backstop.js.map +1 -1
- package/dist/server/static-file-cache.js +1 -1
- package/dist/server/static-file-cache.js.map +1 -1
- package/dist/server/worker-utils.d.ts +0 -7
- package/dist/server/worker-utils.js +3 -2
- package/dist/server/worker-utils.js.map +1 -1
- package/dist/shims/amp.js.map +1 -1
- package/dist/shims/app.d.ts +37 -4
- package/dist/shims/app.js +50 -1
- package/dist/shims/app.js.map +1 -0
- package/dist/shims/cache-for-request.js.map +1 -1
- package/dist/shims/cache-runtime.d.ts +19 -2
- package/dist/shims/cache-runtime.js +87 -19
- package/dist/shims/cache-runtime.js.map +1 -1
- package/dist/shims/cache.d.ts +20 -21
- package/dist/shims/cache.js +101 -15
- package/dist/shims/cache.js.map +1 -1
- package/dist/shims/client-hook-error.js.map +1 -1
- package/dist/shims/compat-router.js.map +1 -1
- package/dist/shims/config.js.map +1 -1
- package/dist/shims/constants.js.map +1 -1
- package/dist/shims/document.js.map +1 -1
- package/dist/shims/dynamic.d.ts +18 -10
- package/dist/shims/dynamic.js +107 -51
- package/dist/shims/dynamic.js.map +1 -1
- package/dist/shims/error-boundary.d.ts +35 -6
- package/dist/shims/error-boundary.js +116 -33
- package/dist/shims/error-boundary.js.map +1 -1
- package/dist/shims/error.d.ts +18 -1
- package/dist/shims/error.js +56 -1
- package/dist/shims/error.js.map +1 -1
- package/dist/shims/fetch-cache.d.ts +25 -1
- package/dist/shims/fetch-cache.js +159 -13
- package/dist/shims/fetch-cache.js.map +1 -1
- package/dist/shims/font-google-base.d.ts +22 -8
- package/dist/shims/font-google-base.js +41 -71
- package/dist/shims/font-google-base.js.map +1 -1
- package/dist/shims/font-local.d.ts +3 -20
- package/dist/shims/font-local.js +23 -75
- package/dist/shims/font-local.js.map +1 -1
- package/dist/shims/font-utils.d.ts +51 -0
- package/dist/shims/font-utils.js +97 -0
- package/dist/shims/font-utils.js.map +1 -0
- package/dist/shims/form.js +3 -1
- package/dist/shims/form.js.map +1 -1
- package/dist/shims/hash-scroll.d.ts +7 -0
- package/dist/shims/hash-scroll.js +30 -0
- package/dist/shims/hash-scroll.js.map +1 -0
- package/dist/shims/head-state.js.map +1 -1
- package/dist/shims/head.d.ts +3 -1
- package/dist/shims/head.js +28 -16
- package/dist/shims/head.js.map +1 -1
- package/dist/shims/headers.d.ts +11 -12
- package/dist/shims/headers.js +45 -8
- package/dist/shims/headers.js.map +1 -1
- package/dist/shims/i18n-context.js.map +1 -1
- package/dist/shims/i18n-state.js.map +1 -1
- package/dist/shims/image-config.d.ts +14 -1
- package/dist/shims/image-config.js +24 -1
- package/dist/shims/image-config.js.map +1 -1
- package/dist/shims/image.d.ts +1 -0
- package/dist/shims/image.js +159 -80
- package/dist/shims/image.js.map +1 -1
- package/dist/shims/internal/als-registry.js.map +1 -1
- package/dist/shims/internal/app-router-context.d.ts +7 -6
- package/dist/shims/internal/app-router-context.js +17 -6
- package/dist/shims/internal/app-router-context.js.map +1 -1
- package/dist/shims/internal/cookie-serialize.js.map +1 -1
- package/dist/shims/internal/make-hanging-promise.d.ts +1 -1
- package/dist/shims/internal/make-hanging-promise.js +1 -1
- package/dist/shims/internal/make-hanging-promise.js.map +1 -1
- package/dist/shims/internal/parse-cookie-header.js.map +1 -1
- package/dist/shims/internal/utils.js.map +1 -1
- package/dist/shims/internal/work-unit-async-storage.js +2 -2
- package/dist/shims/internal/work-unit-async-storage.js.map +1 -1
- package/dist/shims/layout-segment-context.js.map +1 -1
- package/dist/shims/legacy-image.js.map +1 -1
- package/dist/shims/link-prefetch.d.ts +42 -0
- package/dist/shims/link-prefetch.js +45 -0
- package/dist/shims/link-prefetch.js.map +1 -0
- package/dist/shims/link.d.ts +37 -4
- package/dist/shims/link.js +156 -46
- package/dist/shims/link.js.map +1 -1
- package/dist/shims/metadata.d.ts +16 -30
- package/dist/shims/metadata.js +87 -28
- package/dist/shims/metadata.js.map +1 -1
- package/dist/shims/navigation-state.js.map +1 -1
- package/dist/shims/navigation.d.ts +172 -10
- package/dist/shims/navigation.js +335 -70
- package/dist/shims/navigation.js.map +1 -1
- package/dist/shims/navigation.react-server.d.ts +3 -2
- package/dist/shims/navigation.react-server.js +5 -2
- package/dist/shims/navigation.react-server.js.map +1 -1
- package/dist/shims/offline.js.map +1 -1
- package/dist/shims/pages-router-runtime.d.ts +7 -0
- package/dist/shims/pages-router-runtime.js +16 -0
- package/dist/shims/pages-router-runtime.js.map +1 -0
- package/dist/shims/readonly-url-search-params.js.map +1 -1
- package/dist/shims/request-context.js.map +1 -1
- package/dist/shims/root-params.js.map +1 -1
- package/dist/shims/router-state.js.map +1 -1
- package/dist/shims/router.d.ts +69 -7
- package/dist/shims/router.js +232 -249
- package/dist/shims/router.js.map +1 -1
- package/dist/shims/script-nonce-context.js.map +1 -1
- package/dist/shims/script.js +110 -32
- package/dist/shims/script.js.map +1 -1
- package/dist/shims/server.js +12 -15
- package/dist/shims/server.js.map +1 -1
- package/dist/shims/slot.d.ts +7 -1
- package/dist/shims/slot.js +60 -7
- package/dist/shims/slot.js.map +1 -1
- package/dist/shims/thenable-params.js.map +1 -1
- package/dist/shims/unified-request-context.js +5 -0
- package/dist/shims/unified-request-context.js.map +1 -1
- package/dist/shims/unrecognized-action-error.d.ts +35 -0
- package/dist/shims/unrecognized-action-error.js +41 -0
- package/dist/shims/unrecognized-action-error.js.map +1 -0
- package/dist/shims/url-safety.js.map +1 -1
- package/dist/shims/url-utils.d.ts +22 -1
- package/dist/shims/url-utils.js +76 -3
- package/dist/shims/url-utils.js.map +1 -1
- package/dist/shims/use-merged-ref.js.map +1 -1
- package/dist/shims/web-vitals.d.ts +4 -21
- package/dist/shims/web-vitals.js +19 -6
- package/dist/shims/web-vitals.js.map +1 -1
- package/dist/utils/asset-prefix.d.ts +69 -0
- package/dist/utils/asset-prefix.js +91 -0
- package/dist/utils/asset-prefix.js.map +1 -0
- package/dist/utils/base-path.d.ts +7 -1
- package/dist/utils/base-path.js +10 -1
- package/dist/utils/base-path.js.map +1 -1
- package/dist/utils/cache-control-metadata.js.map +1 -1
- package/dist/utils/domain-locale.js.map +1 -1
- package/dist/utils/encode-cache-tag.d.ts +31 -0
- package/dist/utils/encode-cache-tag.js +38 -0
- package/dist/utils/encode-cache-tag.js.map +1 -0
- package/dist/utils/error-cause.js.map +1 -1
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/lazy-chunks.js.map +1 -1
- package/dist/utils/manifest-paths.js.map +1 -1
- package/dist/utils/mdx-scan.js.map +1 -1
- package/dist/utils/navigation-signal.d.ts +5 -0
- package/dist/utils/navigation-signal.js +14 -0
- package/dist/utils/navigation-signal.js.map +1 -0
- package/dist/utils/project.js.map +1 -1
- package/dist/utils/public-routes.js.map +1 -1
- package/dist/utils/query.js.map +1 -1
- package/dist/utils/safe-json-file.js.map +1 -1
- package/dist/utils/sorted-array.d.ts +9 -0
- package/dist/utils/sorted-array.js +22 -0
- package/dist/utils/sorted-array.js.map +1 -0
- package/dist/utils/text-stream.js.map +1 -1
- package/dist/utils/vinext-root.js.map +1 -1
- package/package.json +8 -6
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
//#region src/shims/font-utils.ts
|
|
2
|
+
/**
|
|
3
|
+
* Escape a string for safe interpolation inside a CSS single-quoted string.
|
|
4
|
+
*
|
|
5
|
+
* Prevents CSS injection by escaping characters that could break out of
|
|
6
|
+
* a `'...'` CSS string context: backslashes, single quotes, and newlines.
|
|
7
|
+
*
|
|
8
|
+
* Used by font-google-base.ts, font-local.ts, and fallback-metrics.ts.
|
|
9
|
+
*/
|
|
10
|
+
function escapeCSSString(value) {
|
|
11
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\a ").replace(/\r/g, "\\d ");
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Validate a CSS custom property name (e.g. `--font-inter`).
|
|
15
|
+
*
|
|
16
|
+
* Custom properties must start with `--` and only contain alphanumeric
|
|
17
|
+
* characters, hyphens, and underscores. Anything else could be used to
|
|
18
|
+
* break out of the CSS declaration and inject arbitrary rules.
|
|
19
|
+
*
|
|
20
|
+
* Returns the name if valid, undefined otherwise.
|
|
21
|
+
*/
|
|
22
|
+
function sanitizeCSSVarName(name) {
|
|
23
|
+
if (/^--[a-zA-Z0-9_-]+$/.test(name)) return name;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Sanitize a CSS font-family fallback name.
|
|
27
|
+
*
|
|
28
|
+
* Generic family names (sans-serif, serif, monospace, etc.) are used as-is.
|
|
29
|
+
* Named families are wrapped in escaped quotes. This prevents injection via
|
|
30
|
+
* crafted fallback values like `); } body { color: red; } .x {`.
|
|
31
|
+
*/
|
|
32
|
+
function sanitizeFallback(name) {
|
|
33
|
+
const generics = new Set([
|
|
34
|
+
"serif",
|
|
35
|
+
"sans-serif",
|
|
36
|
+
"monospace",
|
|
37
|
+
"cursive",
|
|
38
|
+
"fantasy",
|
|
39
|
+
"system-ui",
|
|
40
|
+
"ui-serif",
|
|
41
|
+
"ui-sans-serif",
|
|
42
|
+
"ui-monospace",
|
|
43
|
+
"ui-rounded",
|
|
44
|
+
"emoji",
|
|
45
|
+
"math",
|
|
46
|
+
"fangsong"
|
|
47
|
+
]);
|
|
48
|
+
const trimmed = name.trim();
|
|
49
|
+
if (generics.has(trimmed)) return trimmed;
|
|
50
|
+
return `'${escapeCSSString(trimmed)}'`;
|
|
51
|
+
}
|
|
52
|
+
function singleFontOptionValue(value) {
|
|
53
|
+
if (Array.isArray(value)) return new Set(value).size === 1 ? value[0] : void 0;
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
function sanitizeFontDescriptorValue(value) {
|
|
57
|
+
if (/[{};]|\/\*|\*\/|<\//i.test(value)) return void 0;
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
function resolveFontWeight(weight) {
|
|
61
|
+
const value = singleFontOptionValue(weight);
|
|
62
|
+
if (!value || value.includes(" ")) return void 0;
|
|
63
|
+
const numericWeight = Number(value);
|
|
64
|
+
return Number.isFinite(numericWeight) ? numericWeight : void 0;
|
|
65
|
+
}
|
|
66
|
+
function resolveFontStyle(style) {
|
|
67
|
+
const value = singleFontOptionValue(style);
|
|
68
|
+
if (!value || value.includes(" ")) return void 0;
|
|
69
|
+
return sanitizeFontDescriptorValue(value);
|
|
70
|
+
}
|
|
71
|
+
function resolveGoogleFontStyle(style) {
|
|
72
|
+
if (style === void 0) return "normal";
|
|
73
|
+
const value = singleFontOptionValue(style);
|
|
74
|
+
if (!value) return void 0;
|
|
75
|
+
if (value === "normal" || value === "italic") return value;
|
|
76
|
+
}
|
|
77
|
+
function resolveSingleFaceStyle(input) {
|
|
78
|
+
const fontWeight = input.internalWeight ?? resolveFontWeight(input.weight);
|
|
79
|
+
const fontStyle = (input.internalStyle ? sanitizeFontDescriptorValue(input.internalStyle) : void 0) ?? (input.google ? resolveGoogleFontStyle(input.style) : resolveFontStyle(input.style));
|
|
80
|
+
return {
|
|
81
|
+
fontFamily: input.fontFamily,
|
|
82
|
+
...fontWeight !== void 0 ? { fontWeight } : {},
|
|
83
|
+
...fontStyle ? { fontStyle } : {}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function formatFontClassRule(className, style) {
|
|
87
|
+
const fontStyle = style.fontStyle ? sanitizeFontDescriptorValue(style.fontStyle) : void 0;
|
|
88
|
+
return `.${className} { ${[
|
|
89
|
+
`font-family: ${style.fontFamily}`,
|
|
90
|
+
...style.fontWeight !== void 0 ? [`font-weight: ${style.fontWeight}`] : [],
|
|
91
|
+
...fontStyle ? [`font-style: ${fontStyle}`] : []
|
|
92
|
+
].join("; ")}; }\n`;
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
export { escapeCSSString, formatFontClassRule, resolveFontStyle, resolveFontWeight, resolveGoogleFontStyle, resolveSingleFaceStyle, sanitizeCSSVarName, sanitizeFallback, sanitizeFontDescriptorValue, singleFontOptionValue };
|
|
96
|
+
|
|
97
|
+
//# sourceMappingURL=font-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"font-utils.js","names":[],"sources":["../../src/shims/font-utils.ts"],"sourcesContent":["export type FontStyle = {\n fontFamily: string;\n fontWeight?: number;\n fontStyle?: string;\n};\n\nexport type FontFaceStyleInput = {\n fontFamily: string;\n weight?: string | string[];\n style?: string | string[];\n internalWeight?: number;\n internalStyle?: string;\n google?: boolean;\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 *\n * Used by font-google-base.ts, font-local.ts, and fallback-metrics.ts.\n */\nexport function 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 */\nexport function 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 */\nexport function sanitizeFallback(name: string): string {\n // CSS generic font families — safe to use unquoted\n const generics = new Set([\n \"serif\",\n \"sans-serif\",\n \"monospace\",\n \"cursive\",\n \"fantasy\",\n \"system-ui\",\n \"ui-serif\",\n \"ui-sans-serif\",\n \"ui-monospace\",\n \"ui-rounded\",\n \"emoji\",\n \"math\",\n \"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\nexport function singleFontOptionValue(value: string | string[] | undefined): string | undefined {\n if (Array.isArray(value)) {\n const values = new Set(value);\n return values.size === 1 ? value[0] : undefined;\n }\n return value;\n}\n\nexport function sanitizeFontDescriptorValue(value: string): string | undefined {\n if (/[{};]|\\/\\*|\\*\\/|<\\//i.test(value)) return undefined;\n return value;\n}\n\nexport function resolveFontWeight(weight: string | string[] | undefined): number | undefined {\n const value = singleFontOptionValue(weight);\n if (!value || value.includes(\" \")) return undefined;\n const numericWeight = Number(value);\n return Number.isFinite(numericWeight) ? numericWeight : undefined;\n}\n\nexport function resolveFontStyle(style: string | string[] | undefined): string | undefined {\n const value = singleFontOptionValue(style);\n if (!value || value.includes(\" \")) return undefined;\n return sanitizeFontDescriptorValue(value);\n}\n\nexport function resolveGoogleFontStyle(style: string | string[] | undefined): string | undefined {\n if (style === undefined) return \"normal\";\n const value = singleFontOptionValue(style);\n if (!value) return undefined;\n if (value === \"normal\" || value === \"italic\") return value;\n return undefined;\n}\n\nexport function resolveSingleFaceStyle(input: FontFaceStyleInput): FontStyle {\n const fontWeight = input.internalWeight ?? resolveFontWeight(input.weight);\n const internalStyle = input.internalStyle\n ? sanitizeFontDescriptorValue(input.internalStyle)\n : undefined;\n const fontStyle =\n internalStyle ??\n (input.google ? resolveGoogleFontStyle(input.style) : resolveFontStyle(input.style));\n\n return {\n fontFamily: input.fontFamily,\n ...(fontWeight !== undefined ? { fontWeight } : {}),\n ...(fontStyle ? { fontStyle } : {}),\n };\n}\n\nexport function formatFontClassRule(className: string, style: FontStyle): string {\n const fontStyle = style.fontStyle ? sanitizeFontDescriptorValue(style.fontStyle) : undefined;\n const declarations = [\n `font-family: ${style.fontFamily}`,\n ...(style.fontWeight !== undefined ? [`font-weight: ${style.fontWeight}`] : []),\n ...(fontStyle ? [`font-style: ${fontStyle}`] : []),\n ];\n return `.${className} { ${declarations.join(\"; \")}; }\\n`;\n}\n"],"mappings":";;;;;;;;;AAuBA,SAAgB,gBAAgB,OAAuB;CACrD,OAAO,MACJ,QAAQ,OAAO,OAAO,CACtB,QAAQ,MAAM,MAAM,CACpB,QAAQ,OAAO,OAAO,CACtB,QAAQ,OAAO,OAAO;;;;;;;;;;;AAY3B,SAAgB,mBAAmB,MAAkC;CACnE,IAAI,qBAAqB,KAAK,KAAK,EAAE,OAAO;;;;;;;;;AAW9C,SAAgB,iBAAiB,MAAsB;CAErD,MAAM,WAAW,IAAI,IAAI;EACvB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CACF,MAAM,UAAU,KAAK,MAAM;CAC3B,IAAI,SAAS,IAAI,QAAQ,EAAE,OAAO;CAElC,OAAO,IAAI,gBAAgB,QAAQ,CAAC;;AAGtC,SAAgB,sBAAsB,OAA0D;CAC9F,IAAI,MAAM,QAAQ,MAAM,EAEtB,OAAO,IADY,IAAI,MACV,CAAC,SAAS,IAAI,MAAM,KAAK,KAAA;CAExC,OAAO;;AAGT,SAAgB,4BAA4B,OAAmC;CAC7E,IAAI,uBAAuB,KAAK,MAAM,EAAE,OAAO,KAAA;CAC/C,OAAO;;AAGT,SAAgB,kBAAkB,QAA2D;CAC3F,MAAM,QAAQ,sBAAsB,OAAO;CAC3C,IAAI,CAAC,SAAS,MAAM,SAAS,IAAI,EAAE,OAAO,KAAA;CAC1C,MAAM,gBAAgB,OAAO,MAAM;CACnC,OAAO,OAAO,SAAS,cAAc,GAAG,gBAAgB,KAAA;;AAG1D,SAAgB,iBAAiB,OAA0D;CACzF,MAAM,QAAQ,sBAAsB,MAAM;CAC1C,IAAI,CAAC,SAAS,MAAM,SAAS,IAAI,EAAE,OAAO,KAAA;CAC1C,OAAO,4BAA4B,MAAM;;AAG3C,SAAgB,uBAAuB,OAA0D;CAC/F,IAAI,UAAU,KAAA,GAAW,OAAO;CAChC,MAAM,QAAQ,sBAAsB,MAAM;CAC1C,IAAI,CAAC,OAAO,OAAO,KAAA;CACnB,IAAI,UAAU,YAAY,UAAU,UAAU,OAAO;;AAIvD,SAAgB,uBAAuB,OAAsC;CAC3E,MAAM,aAAa,MAAM,kBAAkB,kBAAkB,MAAM,OAAO;CAI1E,MAAM,aAHgB,MAAM,gBACxB,4BAA4B,MAAM,cAAc,GAChD,KAAA,OAGD,MAAM,SAAS,uBAAuB,MAAM,MAAM,GAAG,iBAAiB,MAAM,MAAM;CAErF,OAAO;EACL,YAAY,MAAM;EAClB,GAAI,eAAe,KAAA,IAAY,EAAE,YAAY,GAAG,EAAE;EAClD,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;EACnC;;AAGH,SAAgB,oBAAoB,WAAmB,OAA0B;CAC/E,MAAM,YAAY,MAAM,YAAY,4BAA4B,MAAM,UAAU,GAAG,KAAA;CAMnF,OAAO,IAAI,UAAU,KAAK;EAJxB,gBAAgB,MAAM;EACtB,GAAI,MAAM,eAAe,KAAA,IAAY,CAAC,gBAAgB,MAAM,aAAa,GAAG,EAAE;EAC9E,GAAI,YAAY,CAAC,eAAe,YAAY,GAAG,EAAE;EAEb,CAAC,KAAK,KAAK,CAAC"}
|
package/dist/shims/form.js
CHANGED
package/dist/shims/form.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.js","names":[],"sources":["../../src/shims/form.tsx"],"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 { forwardRef, useActionState, type FormHTMLAttributes, type ForwardedRef } from \"react\";\nimport { navigateClientSide } from \"./navigation.js\";\nimport { isDangerousScheme } from \"./url-safety.js\";\nimport { toSameOriginPath } from \"./url-utils.js\";\n\n// Re-export useActionState from React 19 to match Next.js's next/form module\nexport { useActionState };\n\ntype FormSubmitter = HTMLButtonElement | HTMLInputElement;\nconst SUPPORTED_FORM_ENCTYPE = \"application/x-www-form-urlencoded\";\nconst SUPPORTED_FORM_METHOD = \"GET\";\nconst SUPPORTED_FORM_TARGET = \"_self\";\n\nfunction isSafeAction(action: string): boolean {\n // Block dangerous URI schemes\n if (isDangerousScheme(action)) return false;\n // Block protocol-relative URLs (//evil.com/...)\n if (action.startsWith(\"//\")) return false;\n // Block absolute URLs to external origins (client-side: compare origins)\n if (/^https?:\\/\\//i.test(action)) {\n if (typeof window !== \"undefined\") {\n try {\n const actionUrl = new URL(action);\n return actionUrl.origin === window.location.origin;\n } catch {\n return false;\n }\n }\n // Server-side: block all absolute URLs (can't compare origins)\n return false;\n }\n return true;\n}\n\nfunction getSubmitter(nativeEvent: unknown): FormSubmitter | null {\n const submitter =\n nativeEvent &&\n typeof nativeEvent === \"object\" &&\n \"submitter\" in nativeEvent &&\n nativeEvent.submitter instanceof Element\n ? nativeEvent.submitter\n : null;\n\n if (submitter instanceof HTMLButtonElement || submitter instanceof HTMLInputElement) {\n return submitter;\n }\n return null;\n}\n\nfunction getEffectiveMethod(\n submitter: FormSubmitter | null,\n formMethod: FormHTMLAttributes<HTMLFormElement>[\"method\"],\n): string {\n const override = submitter?.getAttribute(\"formmethod\");\n return (override ?? formMethod ?? \"GET\").toUpperCase();\n}\n\nfunction getEffectiveAction(submitter: FormSubmitter | null, formAction: string): string {\n return submitter?.getAttribute(\"formaction\") ?? formAction;\n}\n\nfunction checkFormActionUrl(action: string, source: \"action\" | \"formAction\"): void {\n const aPropName = source === \"action\" ? \"an `action`\" : \"a `formAction`\";\n\n let testUrl: URL;\n try {\n testUrl = new URL(action, \"http://n\");\n } catch {\n console.error(`<Form> received ${aPropName} that cannot be parsed as a URL: \"${action}\".`);\n return;\n }\n\n if (testUrl.searchParams.size) {\n console.warn(\n `<Form> received ${aPropName} that contains search params: \"${action}\". This is not supported, and they will be ignored. ` +\n `If you need to pass in additional search params, use an \\`<input type=\"hidden\" />\\` instead.`,\n );\n }\n}\n\nfunction hasUnsupportedSubmitterAttributes(submitter: FormSubmitter): boolean {\n const formEncType = submitter.getAttribute(\"formenctype\");\n if (formEncType !== null && formEncType !== SUPPORTED_FORM_ENCTYPE) {\n console.error(\n `<Form>'s \\`encType\\` was set to an unsupported value via \\`formEncType=\"${formEncType}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n const formMethod = submitter.getAttribute(\"formmethod\");\n if (formMethod !== null && formMethod.toUpperCase() !== SUPPORTED_FORM_METHOD) {\n console.error(\n `<Form>'s \\`method\\` was set to an unsupported value via \\`formMethod=\"${formMethod}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n const formTarget = submitter.getAttribute(\"formtarget\");\n if (formTarget !== null && formTarget !== SUPPORTED_FORM_TARGET) {\n console.error(\n `<Form>'s \\`target\\` was set to an unsupported value via \\`formTarget=\"${formTarget}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n return false;\n}\n\nfunction createFormSubmitDestinationUrl(\n action: string,\n form: HTMLFormElement,\n submitter: FormSubmitter | null,\n): string {\n const targetUrl = new URL(action, window.location.href);\n if (targetUrl.searchParams.size) {\n targetUrl.search = \"\";\n }\n\n const formData = buildFormData(form, submitter);\n for (const [name, value] of formData) {\n targetUrl.searchParams.append(name, typeof value === \"string\" ? value : value.name);\n }\n\n return toSameOriginPath(targetUrl.href) ?? targetUrl.href;\n}\n\nfunction buildFormData(form: HTMLFormElement, submitter: FormSubmitter | null): FormData {\n if (!submitter) return new FormData(form);\n\n try {\n return new FormData(form, submitter);\n } catch {\n const formData = new FormData(form);\n if (!submitter.disabled && submitter.name) {\n formData.append(submitter.name, submitter.value);\n }\n return formData;\n }\n}\n\ntype FormProps = {\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} & FormHTMLAttributes<HTMLFormElement>;\n\nconst Form = forwardRef(function Form(props: FormProps, ref: ForwardedRef<HTMLFormElement>) {\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} onSubmit={onSubmit} {...rest} />;\n }\n\n // Block dangerous action URLs. Render <form> without action attribute\n // so it submits to the current page (safe default).\n if (process.env.NODE_ENV !== \"production\") {\n checkFormActionUrl(action, \"action\");\n }\n\n if (!isSafeAction(action)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(`<Form> blocked unsafe action: ${action}`);\n }\n return <form ref={ref} onSubmit={onSubmit} {...rest} />;\n }\n\n async function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) {\n // Call user's onSubmit first\n if (onSubmit) {\n onSubmit(e);\n if (e.defaultPrevented) return;\n }\n\n const submitter = getSubmitter(e.nativeEvent);\n if (submitter && hasUnsupportedSubmitterAttributes(submitter)) {\n return;\n }\n\n // Only intercept GET forms for client-side navigation\n const method = getEffectiveMethod(submitter, rest.method);\n if (method !== \"GET\") return;\n\n const effectiveAction = getEffectiveAction(submitter, action as string);\n if (process.env.NODE_ENV !== \"production\" && submitter?.getAttribute(\"formaction\") !== null) {\n checkFormActionUrl(effectiveAction, \"formAction\");\n }\n if (!isSafeAction(effectiveAction)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(`<Form> blocked unsafe action: ${effectiveAction}`);\n }\n e.preventDefault();\n return;\n }\n\n e.preventDefault();\n const url = createFormSubmitDestinationUrl(effectiveAction, e.currentTarget, submitter);\n\n // Navigate client-side\n if (typeof window.__VINEXT_RSC_NAVIGATE__ === \"function\") {\n // App Router: use the shared navigator so URL/history publish stays\n // aligned with the committed RSC tree.\n await navigateClientSide(url, replace ? \"replace\" : \"push\", scroll);\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 // App Router: scroll is handled inside navigateClientSide (called above).\n // Pages Router: scroll manually since pushState/popstate doesn't auto-scroll.\n if (typeof window.__VINEXT_RSC_NAVIGATE__ !== \"function\" && scroll) {\n window.scrollTo(0, 0);\n }\n }\n\n return <form ref={ref} action={action} onSubmit={handleSubmit} {...rest} />;\n});\n\nexport default Form;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"form.js","names":[],"sources":["../../src/shims/form.tsx"],"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 { forwardRef, useActionState, type FormHTMLAttributes, type ForwardedRef } from \"react\";\nimport { navigateClientSide } from \"./navigation.js\";\nimport { isDangerousScheme } from \"./url-safety.js\";\nimport { toSameOriginPath } from \"./url-utils.js\";\n\n// Re-export useActionState from React 19 to match Next.js's next/form module\nexport { useActionState };\n\ntype FormSubmitter = HTMLButtonElement | HTMLInputElement;\nconst SUPPORTED_FORM_ENCTYPE = \"application/x-www-form-urlencoded\";\nconst SUPPORTED_FORM_METHOD = \"GET\";\nconst SUPPORTED_FORM_TARGET = \"_self\";\n\nfunction isSafeAction(action: string): boolean {\n // Block dangerous URI schemes\n if (isDangerousScheme(action)) return false;\n // Block protocol-relative URLs (//evil.com/...)\n if (action.startsWith(\"//\")) return false;\n // Block absolute URLs to external origins (client-side: compare origins)\n if (/^https?:\\/\\//i.test(action)) {\n if (typeof window !== \"undefined\") {\n try {\n const actionUrl = new URL(action);\n return actionUrl.origin === window.location.origin;\n } catch {\n return false;\n }\n }\n // Server-side: block all absolute URLs (can't compare origins)\n return false;\n }\n return true;\n}\n\nfunction getSubmitter(nativeEvent: unknown): FormSubmitter | null {\n const submitter =\n nativeEvent &&\n typeof nativeEvent === \"object\" &&\n \"submitter\" in nativeEvent &&\n nativeEvent.submitter instanceof Element\n ? nativeEvent.submitter\n : null;\n\n if (submitter instanceof HTMLButtonElement || submitter instanceof HTMLInputElement) {\n return submitter;\n }\n return null;\n}\n\nfunction getEffectiveMethod(\n submitter: FormSubmitter | null,\n formMethod: FormHTMLAttributes<HTMLFormElement>[\"method\"],\n): string {\n const override = submitter?.getAttribute(\"formmethod\");\n return (override ?? formMethod ?? \"GET\").toUpperCase();\n}\n\nfunction getEffectiveAction(submitter: FormSubmitter | null, formAction: string): string {\n return submitter?.getAttribute(\"formaction\") ?? formAction;\n}\n\nfunction checkFormActionUrl(action: string, source: \"action\" | \"formAction\"): void {\n const aPropName = source === \"action\" ? \"an `action`\" : \"a `formAction`\";\n\n let testUrl: URL;\n try {\n testUrl = new URL(action, \"http://n\");\n } catch {\n console.error(`<Form> received ${aPropName} that cannot be parsed as a URL: \"${action}\".`);\n return;\n }\n\n if (testUrl.searchParams.size) {\n console.warn(\n `<Form> received ${aPropName} that contains search params: \"${action}\". This is not supported, and they will be ignored. ` +\n `If you need to pass in additional search params, use an \\`<input type=\"hidden\" />\\` instead.`,\n );\n }\n}\n\nfunction hasUnsupportedSubmitterAttributes(submitter: FormSubmitter): boolean {\n const formEncType = submitter.getAttribute(\"formenctype\");\n if (formEncType !== null && formEncType !== SUPPORTED_FORM_ENCTYPE) {\n console.error(\n `<Form>'s \\`encType\\` was set to an unsupported value via \\`formEncType=\"${formEncType}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n const formMethod = submitter.getAttribute(\"formmethod\");\n if (formMethod !== null && formMethod.toUpperCase() !== SUPPORTED_FORM_METHOD) {\n console.error(\n `<Form>'s \\`method\\` was set to an unsupported value via \\`formMethod=\"${formMethod}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n const formTarget = submitter.getAttribute(\"formtarget\");\n if (formTarget !== null && formTarget !== SUPPORTED_FORM_TARGET) {\n console.error(\n `<Form>'s \\`target\\` was set to an unsupported value via \\`formTarget=\"${formTarget}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n return false;\n}\n\nfunction createFormSubmitDestinationUrl(\n action: string,\n form: HTMLFormElement,\n submitter: FormSubmitter | null,\n): string {\n const targetUrl = new URL(action, window.location.href);\n if (targetUrl.searchParams.size) {\n targetUrl.search = \"\";\n }\n\n const formData = buildFormData(form, submitter);\n for (const [name, value] of formData) {\n targetUrl.searchParams.append(name, typeof value === \"string\" ? value : value.name);\n }\n\n return toSameOriginPath(targetUrl.href) ?? targetUrl.href;\n}\n\nfunction buildFormData(form: HTMLFormElement, submitter: FormSubmitter | null): FormData {\n if (!submitter) return new FormData(form);\n\n try {\n return new FormData(form, submitter);\n } catch {\n const formData = new FormData(form);\n if (!submitter.disabled && submitter.name) {\n formData.append(submitter.name, submitter.value);\n }\n return formData;\n }\n}\n\ntype FormProps = {\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} & FormHTMLAttributes<HTMLFormElement>;\n\nconst Form = forwardRef(function Form(props: FormProps, ref: ForwardedRef<HTMLFormElement>) {\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} onSubmit={onSubmit} {...rest} />;\n }\n\n // Block dangerous action URLs. Render <form> without action attribute\n // so it submits to the current page (safe default).\n if (process.env.NODE_ENV !== \"production\") {\n checkFormActionUrl(action, \"action\");\n }\n\n if (!isSafeAction(action)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(`<Form> blocked unsafe action: ${action}`);\n }\n return <form ref={ref} onSubmit={onSubmit} {...rest} />;\n }\n\n async function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) {\n // Call user's onSubmit first\n if (onSubmit) {\n onSubmit(e);\n if (e.defaultPrevented) return;\n }\n\n const submitter = getSubmitter(e.nativeEvent);\n if (submitter && hasUnsupportedSubmitterAttributes(submitter)) {\n return;\n }\n\n // Only intercept GET forms for client-side navigation\n const method = getEffectiveMethod(submitter, rest.method);\n if (method !== \"GET\") return;\n\n const effectiveAction = getEffectiveAction(submitter, action as string);\n if (process.env.NODE_ENV !== \"production\" && submitter?.getAttribute(\"formaction\") !== null) {\n checkFormActionUrl(effectiveAction, \"formAction\");\n }\n if (!isSafeAction(effectiveAction)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(`<Form> blocked unsafe action: ${effectiveAction}`);\n }\n e.preventDefault();\n return;\n }\n\n e.preventDefault();\n const url = createFormSubmitDestinationUrl(effectiveAction, e.currentTarget, submitter);\n\n // Navigate client-side\n if (typeof window.__VINEXT_RSC_NAVIGATE__ === \"function\") {\n // App Router: use the shared navigator so URL/history publish stays\n // aligned with the committed RSC tree.\n await navigateClientSide(url, replace ? \"replace\" : \"push\", scroll);\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 // App Router: scroll is handled inside navigateClientSide (called above).\n // Pages Router: scroll manually since pushState/popstate doesn't auto-scroll.\n if (typeof window.__VINEXT_RSC_NAVIGATE__ !== \"function\" && scroll) {\n window.scrollTo(0, 0);\n }\n }\n\n return (\n <form\n ref={ref}\n action={action}\n onSubmit={(event) => {\n void handleSubmit(event);\n }}\n {...rest}\n />\n );\n});\n\nexport default Form;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
//#region src/shims/hash-scroll.d.ts
|
|
2
|
+
declare function decodeHashFragment(fragment: string): string;
|
|
3
|
+
declare function scrollToHashTarget(hash: string): void;
|
|
4
|
+
declare function scrollToHashTargetOnNextFrame(hash: string): void;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { decodeHashFragment, scrollToHashTarget, scrollToHashTargetOnNextFrame };
|
|
7
|
+
//# sourceMappingURL=hash-scroll.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/shims/hash-scroll.ts
|
|
2
|
+
function decodeHashFragment(fragment) {
|
|
3
|
+
try {
|
|
4
|
+
return decodeURIComponent(fragment);
|
|
5
|
+
} catch {
|
|
6
|
+
return fragment;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function scrollToHashTarget(hash) {
|
|
10
|
+
const fragment = decodeHashFragment(hash.startsWith("#") ? hash.slice(1) : hash);
|
|
11
|
+
if (fragment === "" || fragment === "top") {
|
|
12
|
+
window.scrollTo(0, 0);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const idElement = document.getElementById(fragment);
|
|
16
|
+
if (idElement) {
|
|
17
|
+
idElement.scrollIntoView({ behavior: "auto" });
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
document.getElementsByName(fragment)[0]?.scrollIntoView({ behavior: "auto" });
|
|
21
|
+
}
|
|
22
|
+
function scrollToHashTargetOnNextFrame(hash) {
|
|
23
|
+
requestAnimationFrame(() => {
|
|
24
|
+
scrollToHashTarget(hash);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { decodeHashFragment, scrollToHashTarget, scrollToHashTargetOnNextFrame };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=hash-scroll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash-scroll.js","names":[],"sources":["../../src/shims/hash-scroll.ts"],"sourcesContent":["export function decodeHashFragment(fragment: string): string {\n try {\n return decodeURIComponent(fragment);\n } catch {\n // Malformed percent escapes cannot be decoded; keep navigation alive and\n // attempt browser-style matching against the raw fragment.\n return fragment;\n }\n}\n\nexport function scrollToHashTarget(hash: string): void {\n const fragment = decodeHashFragment(hash.startsWith(\"#\") ? hash.slice(1) : hash);\n\n if (fragment === \"\" || fragment === \"top\") {\n window.scrollTo(0, 0);\n return;\n }\n\n const idElement = document.getElementById(fragment);\n if (idElement) {\n idElement.scrollIntoView({ behavior: \"auto\" });\n return;\n }\n\n document.getElementsByName(fragment)[0]?.scrollIntoView({ behavior: \"auto\" });\n}\n\nexport function scrollToHashTargetOnNextFrame(hash: string): void {\n requestAnimationFrame(() => {\n scrollToHashTarget(hash);\n });\n}\n"],"mappings":";AAAA,SAAgB,mBAAmB,UAA0B;CAC3D,IAAI;EACF,OAAO,mBAAmB,SAAS;SAC7B;EAGN,OAAO;;;AAIX,SAAgB,mBAAmB,MAAoB;CACrD,MAAM,WAAW,mBAAmB,KAAK,WAAW,IAAI,GAAG,KAAK,MAAM,EAAE,GAAG,KAAK;CAEhF,IAAI,aAAa,MAAM,aAAa,OAAO;EACzC,OAAO,SAAS,GAAG,EAAE;EACrB;;CAGF,MAAM,YAAY,SAAS,eAAe,SAAS;CACnD,IAAI,WAAW;EACb,UAAU,eAAe,EAAE,UAAU,QAAQ,CAAC;EAC9C;;CAGF,SAAS,kBAAkB,SAAS,CAAC,IAAI,eAAe,EAAE,UAAU,QAAQ,CAAC;;AAG/E,SAAgB,8BAA8B,MAAoB;CAChE,4BAA4B;EAC1B,mBAAmB,KAAK;GACxB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"head-state.js","names":[],"sources":["../../src/shims/head-state.ts"],"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 type React from \"react\";\nimport { _registerHeadStateAccessors } from \"./head.js\";\nimport { getOrCreateAls } from \"./internal/als-registry.js\";\nimport {\n getRequestContext,\n isInsideUnifiedScope,\n runWithUnifiedStateMutation,\n} from \"./unified-request-context.js\";\n\n// ---------------------------------------------------------------------------\n// ALS setup\n// ---------------------------------------------------------------------------\n\nexport type HeadState = {\n ssrHeadChildren: React.ReactNode[];\n};\n\nconst _FALLBACK_KEY = Symbol.for(\"vinext.head.fallback\");\nconst _g = globalThis as unknown as Record<PropertyKey, unknown>;\nconst _als = getOrCreateAls<HeadState>(\"vinext.head.als\");\n\nconst _fallbackState = (_g[_FALLBACK_KEY] ??= {\n ssrHeadChildren: [],\n} satisfies HeadState) as HeadState;\n\nfunction _getState(): HeadState {\n if (isInsideUnifiedScope()) {\n return getRequestContext();\n }\n return _als.getStore() ?? _fallbackState;\n}\n\n/**\n * Run a function within a head state ALS scope.\n * Ensures per-request isolation for Pages Router <Head> elements\n * on concurrent runtimes.\n */\nexport function runWithHeadState<T>(fn: () => Promise<T>): Promise<T>;\nexport function runWithHeadState<T>(fn: () => T | Promise<T>): T | Promise<T>;\nexport function runWithHeadState<T>(fn: () => T | Promise<T>): T | Promise<T> {\n if (isInsideUnifiedScope()) {\n return runWithUnifiedStateMutation((uCtx) => {\n uCtx.ssrHeadChildren = [];\n }, fn);\n }\n\n const state: HeadState = {\n ssrHeadChildren: [],\n };\n return _als.run(state, fn);\n}\n\n// ---------------------------------------------------------------------------\n// Register ALS-backed accessors into head.ts\n// ---------------------------------------------------------------------------\n\n_registerHeadStateAccessors({\n getSSRHeadChildren(): React.ReactNode[] {\n return _getState().ssrHeadChildren;\n },\n\n resetSSRHead(): void {\n _getState().ssrHeadChildren = [];\n },\n});\n"],"mappings":";;;;AA2BA,MAAM,gBAAgB,OAAO,IAAI,uBAAuB;AACxD,MAAM,KAAK;AACX,MAAM,OAAO,eAA0B,kBAAkB;AAEzD,MAAM,iBAAkB,GAAG,mBAAmB,EAC5C,iBAAiB,EAAE,EACpB;AAED,SAAS,YAAuB;
|
|
1
|
+
{"version":3,"file":"head-state.js","names":[],"sources":["../../src/shims/head-state.ts"],"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 type React from \"react\";\nimport { _registerHeadStateAccessors } from \"./head.js\";\nimport { getOrCreateAls } from \"./internal/als-registry.js\";\nimport {\n getRequestContext,\n isInsideUnifiedScope,\n runWithUnifiedStateMutation,\n} from \"./unified-request-context.js\";\n\n// ---------------------------------------------------------------------------\n// ALS setup\n// ---------------------------------------------------------------------------\n\nexport type HeadState = {\n ssrHeadChildren: React.ReactNode[];\n};\n\nconst _FALLBACK_KEY = Symbol.for(\"vinext.head.fallback\");\nconst _g = globalThis as unknown as Record<PropertyKey, unknown>;\nconst _als = getOrCreateAls<HeadState>(\"vinext.head.als\");\n\nconst _fallbackState = (_g[_FALLBACK_KEY] ??= {\n ssrHeadChildren: [],\n} satisfies HeadState) as HeadState;\n\nfunction _getState(): HeadState {\n if (isInsideUnifiedScope()) {\n return getRequestContext();\n }\n return _als.getStore() ?? _fallbackState;\n}\n\n/**\n * Run a function within a head state ALS scope.\n * Ensures per-request isolation for Pages Router <Head> elements\n * on concurrent runtimes.\n */\nexport function runWithHeadState<T>(fn: () => Promise<T>): Promise<T>;\nexport function runWithHeadState<T>(fn: () => T | Promise<T>): T | Promise<T>;\nexport function runWithHeadState<T>(fn: () => T | Promise<T>): T | Promise<T> {\n if (isInsideUnifiedScope()) {\n return runWithUnifiedStateMutation((uCtx) => {\n uCtx.ssrHeadChildren = [];\n }, fn);\n }\n\n const state: HeadState = {\n ssrHeadChildren: [],\n };\n return _als.run(state, fn);\n}\n\n// ---------------------------------------------------------------------------\n// Register ALS-backed accessors into head.ts\n// ---------------------------------------------------------------------------\n\n_registerHeadStateAccessors({\n getSSRHeadChildren(): React.ReactNode[] {\n return _getState().ssrHeadChildren;\n },\n\n resetSSRHead(): void {\n _getState().ssrHeadChildren = [];\n },\n});\n"],"mappings":";;;;AA2BA,MAAM,gBAAgB,OAAO,IAAI,uBAAuB;AACxD,MAAM,KAAK;AACX,MAAM,OAAO,eAA0B,kBAAkB;AAEzD,MAAM,iBAAkB,GAAG,mBAAmB,EAC5C,iBAAiB,EAAE,EACpB;AAED,SAAS,YAAuB;CAC9B,IAAI,sBAAsB,EACxB,OAAO,mBAAmB;CAE5B,OAAO,KAAK,UAAU,IAAI;;AAU5B,SAAgB,iBAAoB,IAA0C;CAC5E,IAAI,sBAAsB,EACxB,OAAO,6BAA6B,SAAS;EAC3C,KAAK,kBAAkB,EAAE;IACxB,GAAG;CAMR,OAAO,KAAK,IAAI,EAFd,iBAAiB,EAAE,EAEA,EAAE,GAAG;;AAO5B,4BAA4B;CAC1B,qBAAwC;EACtC,OAAO,WAAW,CAAC;;CAGrB,eAAqB;EACnB,WAAW,CAAC,kBAAkB,EAAE;;CAEnC,CAAC"}
|
package/dist/shims/head.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ declare function _registerHeadStateAccessors(accessors: {
|
|
|
16
16
|
declare function resetSSRHead(): void;
|
|
17
17
|
/** Get collected head HTML. Call after render. */
|
|
18
18
|
declare function getSSRHeadHTML(): string;
|
|
19
|
+
type HeadDOMElement = Pick<HTMLElement, "innerHTML" | "setAttribute" | "textContent">;
|
|
19
20
|
declare function reduceHeadChildren(headChildren: React.ReactNode[]): React.ReactElement[];
|
|
20
21
|
declare function isSafeAttrName(name: string): boolean;
|
|
21
22
|
declare function escapeAttr(s: string): string;
|
|
@@ -30,9 +31,10 @@ declare function escapeAttr(s: string): string;
|
|
|
30
31
|
* context but prevents the HTML parser from seeing a closing tag.
|
|
31
32
|
*/
|
|
32
33
|
declare function escapeInlineContent(content: string, tag: string): string;
|
|
34
|
+
declare function _applyHeadPropsToElement(domEl: HeadDOMElement, props: Record<string, unknown>): void;
|
|
33
35
|
declare function Head({
|
|
34
36
|
children
|
|
35
37
|
}: HeadProps): null;
|
|
36
38
|
//#endregion
|
|
37
|
-
export { _registerHeadStateAccessors, Head as default, escapeAttr, escapeInlineContent, getSSRHeadHTML, isSafeAttrName, reduceHeadChildren, resetSSRHead };
|
|
39
|
+
export { _applyHeadPropsToElement, _registerHeadStateAccessors, Head as default, escapeAttr, escapeInlineContent, getSSRHeadHTML, isSafeAttrName, reduceHeadChildren, resetSSRHead };
|
|
38
40
|
//# sourceMappingURL=head.d.ts.map
|
package/dist/shims/head.js
CHANGED
|
@@ -145,12 +145,12 @@ function isSafeAttrName(name) {
|
|
|
145
145
|
function headChildToHTML(tag, props) {
|
|
146
146
|
const attrs = [];
|
|
147
147
|
let innerHTML = "";
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
148
|
+
const rawHtml = getDangerouslySetInnerHTML(props.dangerouslySetInnerHTML);
|
|
149
|
+
if (rawHtml != null) innerHTML = rawHtml;
|
|
150
|
+
else if (typeof props.children === "string") innerHTML = escapeHTML(props.children);
|
|
151
|
+
else if (Array.isArray(props.children)) innerHTML = escapeHTML(props.children.join(""));
|
|
152
|
+
for (const [key, value] of Object.entries(props)) if (key === "children" || key === "dangerouslySetInnerHTML") continue;
|
|
153
|
+
else if (key === "className") attrs.push(`class="${escapeAttr(String(value))}"`);
|
|
154
154
|
else if (typeof value === "string") {
|
|
155
155
|
if (!isSafeAttrName(key)) continue;
|
|
156
156
|
attrs.push(`${key}="${escapeAttr(value)}"`);
|
|
@@ -183,21 +183,33 @@ function escapeInlineContent(content, tag) {
|
|
|
183
183
|
const pattern = new RegExp(`<\\/(${tag})`, "gi");
|
|
184
184
|
return content.replace(pattern, "<\\/$1");
|
|
185
185
|
}
|
|
186
|
+
function getDangerouslySetInnerHTML(value) {
|
|
187
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
188
|
+
const html = Reflect.get(value, "__html");
|
|
189
|
+
return typeof html === "string" ? html : void 0;
|
|
190
|
+
}
|
|
191
|
+
function _applyHeadPropsToElement(domEl, props) {
|
|
192
|
+
const rawHtml = getDangerouslySetInnerHTML(props.dangerouslySetInnerHTML);
|
|
193
|
+
if (rawHtml != null) domEl.innerHTML = rawHtml;
|
|
194
|
+
else if (typeof props.children === "string") domEl.textContent = props.children;
|
|
195
|
+
else if (Array.isArray(props.children)) domEl.textContent = props.children.join("");
|
|
196
|
+
for (const [key, value] of Object.entries(props)) if (key === "children" || key === "dangerouslySetInnerHTML") continue;
|
|
197
|
+
else if (key === "className") domEl.setAttribute("class", String(value));
|
|
198
|
+
else if (typeof value === "boolean" && value) {
|
|
199
|
+
if (!isSafeAttrName(key)) continue;
|
|
200
|
+
domEl.setAttribute(key, "");
|
|
201
|
+
} else if (typeof value === "string") {
|
|
202
|
+
if (!isSafeAttrName(key)) continue;
|
|
203
|
+
domEl.setAttribute(key, value);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
186
206
|
function syncClientHead() {
|
|
187
207
|
document.querySelectorAll("[data-vinext-head]").forEach((el) => el.remove());
|
|
188
208
|
for (const child of reduceHeadChildren([..._clientHeadChildren.values()])) {
|
|
189
209
|
if (typeof child.type !== "string") continue;
|
|
190
210
|
const domEl = document.createElement(child.type);
|
|
191
211
|
const props = child.props;
|
|
192
|
-
|
|
193
|
-
else if (key === "dangerouslySetInnerHTML") {} else if (key === "className") domEl.setAttribute("class", String(value));
|
|
194
|
-
else if (typeof value === "boolean" && value) {
|
|
195
|
-
if (!isSafeAttrName(key)) continue;
|
|
196
|
-
domEl.setAttribute(key, "");
|
|
197
|
-
} else if (key !== "children" && typeof value === "string") {
|
|
198
|
-
if (!isSafeAttrName(key)) continue;
|
|
199
|
-
domEl.setAttribute(key, value);
|
|
200
|
-
}
|
|
212
|
+
_applyHeadPropsToElement(domEl, props);
|
|
201
213
|
domEl.setAttribute("data-vinext-head", "true");
|
|
202
214
|
document.head.appendChild(domEl);
|
|
203
215
|
}
|
|
@@ -221,6 +233,6 @@ function Head({ children }) {
|
|
|
221
233
|
return null;
|
|
222
234
|
}
|
|
223
235
|
//#endregion
|
|
224
|
-
export { _registerHeadStateAccessors, Head as default, escapeAttr, escapeInlineContent, getSSRHeadHTML, isSafeAttrName, reduceHeadChildren, resetSSRHead };
|
|
236
|
+
export { _applyHeadPropsToElement, _registerHeadStateAccessors, Head as default, escapeAttr, escapeInlineContent, getSSRHeadHTML, isSafeAttrName, reduceHeadChildren, resetSSRHead };
|
|
225
237
|
|
|
226
238
|
//# sourceMappingURL=head.js.map
|
package/dist/shims/head.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"head.js","names":[],"sources":["../../src/shims/head.ts"],"sourcesContent":["/**\n * next/head shim\n *\n * In the Pages Router, <Head> manages document <head> elements.\n * - On the server: collects elements into a module-level array that the\n * dev-server reads after render and injects into the HTML <head>.\n * - On the client: reduces all mounted <Head> instances into one deduped\n * document.head projection and applies it with DOM manipulation.\n */\nimport React, { useEffect, useRef, Children, isValidElement } from \"react\";\n\ntype HeadProps = {\n children?: React.ReactNode;\n};\n\n// --- SSR head collection ---\n// State uses a registration pattern so this module can be bundled for the\n// browser. The ALS-backed implementation lives in head-state.ts (server-only).\n\nlet _ssrHeadChildren: React.ReactNode[] = [];\nconst _clientHeadChildren = new Map<symbol, React.ReactNode>();\n\nlet _getSSRHeadChildren = (): React.ReactNode[] => _ssrHeadChildren;\nlet _resetSSRHeadImpl = (): void => {\n _ssrHeadChildren = [];\n};\n\n/**\n * Register ALS-backed state accessors. Called by head-state.ts on import.\n * @internal\n */\nexport function _registerHeadStateAccessors(accessors: {\n getSSRHeadChildren: () => React.ReactNode[];\n resetSSRHead: () => void;\n}): void {\n _getSSRHeadChildren = accessors.getSSRHeadChildren;\n _resetSSRHeadImpl = accessors.resetSSRHead;\n}\n\n/** Reset the SSR head collector. Call before render. */\nexport function resetSSRHead(): void {\n _resetSSRHeadImpl();\n}\n\n/** Get collected head HTML. Call after render. */\nexport function getSSRHeadHTML(): string {\n return reduceHeadChildren(_getSSRHeadChildren())\n .map((child) => headChildToHTML(child.type as string, child.props as Record<string, unknown>))\n .filter(Boolean)\n .join(\"\\n \");\n}\n\n/**\n * Tags allowed inside <head>. Anything else is silently dropped.\n * This prevents injection of dangerous elements like <iframe>, <object>, etc.\n */\nconst ALLOWED_HEAD_TAGS = new Set([\"title\", \"meta\", \"link\", \"style\", \"script\", \"base\", \"noscript\"]);\nconst ALLOWED_HEAD_TAGS_LIST = Array.from(ALLOWED_HEAD_TAGS).join(\", \");\nconst META_TYPES = [\"name\", \"httpEquiv\", \"charSet\", \"itemProp\"] as const;\n\n/** Self-closing tags: no inner content, emit as <tag ... /> */\nconst SELF_CLOSING_HEAD_TAGS = new Set([\"meta\", \"link\", \"base\"]);\n\n/** Tags whose content is raw text — closing-tag sequences must be escaped during SSR. */\nconst RAW_CONTENT_TAGS = new Set([\"script\", \"style\"]);\n\nfunction warnDisallowedHeadTag(tag: string): void {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[vinext] <Head> ignoring disallowed tag <${tag}>. ` +\n `Only ${ALLOWED_HEAD_TAGS_LIST} are allowed.`,\n );\n }\n}\n\nfunction collectHeadElements(\n list: React.ReactElement[],\n child: React.ReactNode,\n): React.ReactElement[] {\n if (\n child == null ||\n typeof child === \"boolean\" ||\n typeof child === \"string\" ||\n typeof child === \"number\"\n ) {\n return list;\n }\n if (!isValidElement(child)) {\n return list;\n }\n if (child.type === React.Fragment) {\n return Children.toArray((child.props as { children?: React.ReactNode }).children).reduce(\n collectHeadElements,\n list,\n );\n }\n if (typeof child.type !== \"string\") {\n return list;\n }\n if (!ALLOWED_HEAD_TAGS.has(child.type)) {\n warnDisallowedHeadTag(child.type);\n return list;\n }\n return list.concat(child);\n}\n\nfunction normalizeHeadKey(key: React.Key | null): string | null {\n if (key == null || typeof key === \"number\") return null;\n const normalizedKey = String(key);\n const separatorIndex = normalizedKey.indexOf(\"$\");\n return separatorIndex > 0 ? normalizedKey.slice(separatorIndex + 1) : null;\n}\n\nfunction createUniqueHeadFilter(): (child: React.ReactElement) => boolean {\n const keys = new Set<string>();\n const tags = new Set<string>();\n const metaTypes = new Set<string>();\n const metaCategories = new Map<string, Set<string>>();\n\n return (child) => {\n let isUnique = true;\n const normalizedKey = normalizeHeadKey(child.key);\n const hasKey = normalizedKey !== null;\n if (normalizedKey) {\n if (keys.has(normalizedKey)) {\n isUnique = false;\n } else {\n keys.add(normalizedKey);\n }\n }\n\n switch (child.type) {\n case \"title\":\n case \"base\":\n if (tags.has(child.type)) {\n isUnique = false;\n } else {\n tags.add(child.type);\n }\n break;\n case \"meta\": {\n const props = child.props as Record<string, unknown>;\n for (const metaType of META_TYPES) {\n if (!Object.prototype.hasOwnProperty.call(props, metaType)) continue;\n if (metaType === \"charSet\") {\n if (metaTypes.has(metaType)) {\n isUnique = false;\n } else {\n metaTypes.add(metaType);\n }\n continue;\n }\n\n const category = props[metaType];\n if (typeof category !== \"string\") continue;\n\n let categories = metaCategories.get(metaType);\n if (!categories) {\n categories = new Set<string>();\n metaCategories.set(metaType, categories);\n }\n\n if ((metaType !== \"name\" || !hasKey) && categories.has(category)) {\n isUnique = false;\n } else {\n categories.add(category);\n }\n }\n break;\n }\n default:\n break;\n }\n\n return isUnique;\n };\n}\n\nexport function reduceHeadChildren(headChildren: React.ReactNode[]): React.ReactElement[] {\n return headChildren\n .reduce<React.ReactNode[]>(\n (flattenedChildren, child) => flattenedChildren.concat(Children.toArray(child)),\n [],\n )\n .reduce(collectHeadElements, [])\n .reverse()\n .filter(createUniqueHeadFilter())\n .reverse();\n}\n\n/**\n * Validate an HTML attribute name. Rejects names that could break out of\n * the attribute context during SSR serialization, or that represent inline\n * event handlers (on*). Only allows alphanumeric characters, hyphens, and\n * common data-attribute patterns.\n */\nconst SAFE_ATTR_NAME_RE = /^[a-zA-Z][a-zA-Z0-9\\-:.]*$/;\n\nexport function isSafeAttrName(name: string): boolean {\n if (!SAFE_ATTR_NAME_RE.test(name)) return false;\n // Block inline event handlers (onclick, onerror, etc.)\n if (name.length > 2 && name[0] === \"o\" && name[1] === \"n\" && name[2] >= \"A\" && name[2] <= \"z\")\n return false;\n return true;\n}\n\n/**\n * Convert props + tag to an HTML string for SSR head injection.\n * Callers must only pass tags that have already been validated against\n * ALLOWED_HEAD_TAGS (e.g. via reduceHeadChildren / collectHeadElements).\n */\nfunction headChildToHTML(tag: string, props: Record<string, unknown>): string {\n const attrs: string[] = [];\n let innerHTML = \"\";\n\n for (const [key, value] of Object.entries(props)) {\n if (key === \"children\") {\n if (typeof value === \"string\") innerHTML = escapeHTML(value);\n } else if (key === \"dangerouslySetInnerHTML\") {\n // Intentionally raw — developer explicitly opted in.\n // SECURITY NOTE: This injects raw HTML during SSR. The client-side\n // path skips dangerouslySetInnerHTML for safety. Developers must never\n // pass unsanitized user input here — it is a stored XSS vector.\n const html = value as { __html?: string };\n if (html?.__html) innerHTML = html.__html;\n } else if (key === \"className\") {\n attrs.push(`class=\"${escapeAttr(String(value))}\"`);\n } else if (typeof value === \"string\") {\n if (!isSafeAttrName(key)) continue;\n attrs.push(`${key}=\"${escapeAttr(value)}\"`);\n } else if (typeof value === \"boolean\" && value) {\n if (!isSafeAttrName(key)) continue;\n attrs.push(key);\n }\n }\n\n const attrStr = attrs.length ? \" \" + attrs.join(\" \") : \"\";\n\n if (SELF_CLOSING_HEAD_TAGS.has(tag)) {\n return `<${tag}${attrStr} data-vinext-head=\"true\" />`;\n }\n\n // For raw-content tags (script, style), escape closing-tag sequences so the\n // HTML parser doesn't prematurely terminate the element.\n if (RAW_CONTENT_TAGS.has(tag) && innerHTML) {\n innerHTML = escapeInlineContent(innerHTML, tag);\n }\n\n return `<${tag}${attrStr} data-vinext-head=\"true\">${innerHTML}</${tag}>`;\n}\n\nfunction escapeHTML(s: string): string {\n return s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nexport function escapeAttr(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\n/**\n * Escape content that will be placed inside a raw <script> or <style> tag\n * during SSR. The HTML parser treats `</script>` (or `</style>`) as the end\n * of the block regardless of JavaScript string context, so any occurrence\n * of `</` followed by the tag name must be escaped.\n *\n * We replace `</script` and `</style` (case-insensitive) with `<\\/script`\n * and `<\\/style` respectively. The `<\\/` form is harmless in JS/CSS string\n * context but prevents the HTML parser from seeing a closing tag.\n */\nexport function escapeInlineContent(content: string, tag: string): string {\n // Build a pattern like `<\\/script` or `<\\/style`, case-insensitive.\n // `tag` is always a literal developer-controlled value (\"script\" or \"style\")\n // guarded by the RAW_CONTENT_TAGS.has(tag) check at all call sites — never user input.\n const pattern = new RegExp(`<\\\\/(${tag})`, \"gi\");\n return content.replace(pattern, \"<\\\\/$1\");\n}\n\nfunction syncClientHead(): void {\n document.querySelectorAll(\"[data-vinext-head]\").forEach((el) => el.remove());\n\n for (const child of reduceHeadChildren([..._clientHeadChildren.values()])) {\n if (typeof child.type !== \"string\") continue;\n\n const domEl = document.createElement(child.type);\n const props = child.props as Record<string, unknown>;\n\n for (const [key, value] of Object.entries(props)) {\n if (key === \"children\" && typeof value === \"string\") {\n domEl.textContent = value;\n } else if (key === \"dangerouslySetInnerHTML\") {\n // skip for safety\n } else if (key === \"className\") {\n domEl.setAttribute(\"class\", String(value));\n } else if (typeof value === \"boolean\" && value) {\n if (!isSafeAttrName(key)) continue;\n domEl.setAttribute(key, \"\");\n } else if (key !== \"children\" && typeof value === \"string\") {\n if (!isSafeAttrName(key)) continue;\n domEl.setAttribute(key, value);\n }\n }\n\n domEl.setAttribute(\"data-vinext-head\", \"true\");\n document.head.appendChild(domEl);\n }\n}\n\n// --- Component ---\n\nfunction Head({ children }: HeadProps): null {\n const headInstanceIdRef = useRef<symbol | null>(null);\n if (headInstanceIdRef.current === null) {\n headInstanceIdRef.current = Symbol(\"vinext-head\");\n }\n\n // SSR path: collect elements for later injection\n if (typeof window === \"undefined\") {\n _getSSRHeadChildren().push(children);\n return null;\n }\n\n // Client path: update the shared head projection after hydration.\n // oxlint-disable-next-line react-hooks/rules-of-hooks\n useEffect(() => {\n const instanceId = headInstanceIdRef.current!;\n _clientHeadChildren.set(instanceId, children);\n syncClientHead();\n\n return () => {\n _clientHeadChildren.delete(instanceId);\n syncClientHead();\n };\n }, [children]);\n\n return null;\n}\n\nexport default Head;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"head.js","names":[],"sources":["../../src/shims/head.ts"],"sourcesContent":["/**\n * next/head shim\n *\n * In the Pages Router, <Head> manages document <head> elements.\n * - On the server: collects elements into a module-level array that the\n * dev-server reads after render and injects into the HTML <head>.\n * - On the client: reduces all mounted <Head> instances into one deduped\n * document.head projection and applies it with DOM manipulation.\n */\nimport React, { useEffect, useRef, Children, isValidElement } from \"react\";\n\ntype HeadProps = {\n children?: React.ReactNode;\n};\n\n// --- SSR head collection ---\n// State uses a registration pattern so this module can be bundled for the\n// browser. The ALS-backed implementation lives in head-state.ts (server-only).\n\nlet _ssrHeadChildren: React.ReactNode[] = [];\nconst _clientHeadChildren = new Map<symbol, React.ReactNode>();\n\nlet _getSSRHeadChildren = (): React.ReactNode[] => _ssrHeadChildren;\nlet _resetSSRHeadImpl = (): void => {\n _ssrHeadChildren = [];\n};\n\n/**\n * Register ALS-backed state accessors. Called by head-state.ts on import.\n * @internal\n */\nexport function _registerHeadStateAccessors(accessors: {\n getSSRHeadChildren: () => React.ReactNode[];\n resetSSRHead: () => void;\n}): void {\n _getSSRHeadChildren = accessors.getSSRHeadChildren;\n _resetSSRHeadImpl = accessors.resetSSRHead;\n}\n\n/** Reset the SSR head collector. Call before render. */\nexport function resetSSRHead(): void {\n _resetSSRHeadImpl();\n}\n\n/** Get collected head HTML. Call after render. */\nexport function getSSRHeadHTML(): string {\n return reduceHeadChildren(_getSSRHeadChildren())\n .map((child) => headChildToHTML(child.type as string, child.props as Record<string, unknown>))\n .filter(Boolean)\n .join(\"\\n \");\n}\n\n/**\n * Tags allowed inside <head>. Anything else is silently dropped.\n * This prevents injection of dangerous elements like <iframe>, <object>, etc.\n */\nconst ALLOWED_HEAD_TAGS = new Set([\"title\", \"meta\", \"link\", \"style\", \"script\", \"base\", \"noscript\"]);\nconst ALLOWED_HEAD_TAGS_LIST = Array.from(ALLOWED_HEAD_TAGS).join(\", \");\nconst META_TYPES = [\"name\", \"httpEquiv\", \"charSet\", \"itemProp\"] as const;\n\n/** Self-closing tags: no inner content, emit as <tag ... /> */\nconst SELF_CLOSING_HEAD_TAGS = new Set([\"meta\", \"link\", \"base\"]);\n\n/** Tags whose content is raw text — closing-tag sequences must be escaped during SSR. */\nconst RAW_CONTENT_TAGS = new Set([\"script\", \"style\"]);\n\ntype HeadDOMElement = Pick<HTMLElement, \"innerHTML\" | \"setAttribute\" | \"textContent\">;\n\nfunction warnDisallowedHeadTag(tag: string): void {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[vinext] <Head> ignoring disallowed tag <${tag}>. ` +\n `Only ${ALLOWED_HEAD_TAGS_LIST} are allowed.`,\n );\n }\n}\n\nfunction collectHeadElements(\n list: React.ReactElement[],\n child: React.ReactNode,\n): React.ReactElement[] {\n if (\n child == null ||\n typeof child === \"boolean\" ||\n typeof child === \"string\" ||\n typeof child === \"number\"\n ) {\n return list;\n }\n if (!isValidElement(child)) {\n return list;\n }\n if (child.type === React.Fragment) {\n return Children.toArray((child.props as { children?: React.ReactNode }).children).reduce(\n collectHeadElements,\n list,\n );\n }\n if (typeof child.type !== \"string\") {\n return list;\n }\n if (!ALLOWED_HEAD_TAGS.has(child.type)) {\n warnDisallowedHeadTag(child.type);\n return list;\n }\n return list.concat(child);\n}\n\nfunction normalizeHeadKey(key: React.Key | null): string | null {\n if (key == null || typeof key === \"number\") return null;\n const normalizedKey = String(key);\n const separatorIndex = normalizedKey.indexOf(\"$\");\n return separatorIndex > 0 ? normalizedKey.slice(separatorIndex + 1) : null;\n}\n\nfunction createUniqueHeadFilter(): (child: React.ReactElement) => boolean {\n const keys = new Set<string>();\n const tags = new Set<string>();\n const metaTypes = new Set<string>();\n const metaCategories = new Map<string, Set<string>>();\n\n return (child) => {\n let isUnique = true;\n const normalizedKey = normalizeHeadKey(child.key);\n const hasKey = normalizedKey !== null;\n if (normalizedKey) {\n if (keys.has(normalizedKey)) {\n isUnique = false;\n } else {\n keys.add(normalizedKey);\n }\n }\n\n switch (child.type) {\n case \"title\":\n case \"base\":\n if (tags.has(child.type)) {\n isUnique = false;\n } else {\n tags.add(child.type);\n }\n break;\n case \"meta\": {\n const props = child.props as Record<string, unknown>;\n for (const metaType of META_TYPES) {\n if (!Object.prototype.hasOwnProperty.call(props, metaType)) continue;\n if (metaType === \"charSet\") {\n if (metaTypes.has(metaType)) {\n isUnique = false;\n } else {\n metaTypes.add(metaType);\n }\n continue;\n }\n\n const category = props[metaType];\n if (typeof category !== \"string\") continue;\n\n let categories = metaCategories.get(metaType);\n if (!categories) {\n categories = new Set<string>();\n metaCategories.set(metaType, categories);\n }\n\n if ((metaType !== \"name\" || !hasKey) && categories.has(category)) {\n isUnique = false;\n } else {\n categories.add(category);\n }\n }\n break;\n }\n default:\n break;\n }\n\n return isUnique;\n };\n}\n\nexport function reduceHeadChildren(headChildren: React.ReactNode[]): React.ReactElement[] {\n return headChildren\n .reduce<React.ReactNode[]>(\n (flattenedChildren, child) => flattenedChildren.concat(Children.toArray(child)),\n [],\n )\n .reduce(collectHeadElements, [])\n .reverse()\n .filter(createUniqueHeadFilter())\n .reverse();\n}\n\n/**\n * Validate an HTML attribute name. Rejects names that could break out of\n * the attribute context during SSR serialization, or that represent inline\n * event handlers (on*). Only allows alphanumeric characters, hyphens, and\n * common data-attribute patterns.\n */\nconst SAFE_ATTR_NAME_RE = /^[a-zA-Z][a-zA-Z0-9\\-:.]*$/;\n\nexport function isSafeAttrName(name: string): boolean {\n if (!SAFE_ATTR_NAME_RE.test(name)) return false;\n // Block inline event handlers (onclick, onerror, etc.)\n if (name.length > 2 && name[0] === \"o\" && name[1] === \"n\" && name[2] >= \"A\" && name[2] <= \"z\")\n return false;\n return true;\n}\n\n/**\n * Convert props + tag to an HTML string for SSR head injection.\n * Callers must only pass tags that have already been validated against\n * ALLOWED_HEAD_TAGS (e.g. via reduceHeadChildren / collectHeadElements).\n */\nfunction headChildToHTML(tag: string, props: Record<string, unknown>): string {\n const attrs: string[] = [];\n let innerHTML = \"\";\n\n // dangerouslySetInnerHTML takes precedence over children, regardless of\n // prop iteration order. Check it first to match Next.js semantics.\n const rawHtml = getDangerouslySetInnerHTML(props.dangerouslySetInnerHTML);\n if (rawHtml != null) {\n // Intentionally raw — developer explicitly opted in.\n // SECURITY NOTE: This injects raw HTML. Developers must never pass\n // unsanitized user input here — it is a stored XSS vector.\n innerHTML = rawHtml;\n } else if (typeof props.children === \"string\") {\n innerHTML = escapeHTML(props.children);\n } else if (Array.isArray(props.children)) {\n innerHTML = escapeHTML(props.children.join(\"\"));\n }\n\n for (const [key, value] of Object.entries(props)) {\n if (key === \"children\" || key === \"dangerouslySetInnerHTML\") {\n continue;\n } else if (key === \"className\") {\n attrs.push(`class=\"${escapeAttr(String(value))}\"`);\n } else if (typeof value === \"string\") {\n if (!isSafeAttrName(key)) continue;\n attrs.push(`${key}=\"${escapeAttr(value)}\"`);\n } else if (typeof value === \"boolean\" && value) {\n if (!isSafeAttrName(key)) continue;\n attrs.push(key);\n }\n }\n\n const attrStr = attrs.length ? \" \" + attrs.join(\" \") : \"\";\n\n if (SELF_CLOSING_HEAD_TAGS.has(tag)) {\n return `<${tag}${attrStr} data-vinext-head=\"true\" />`;\n }\n\n // For raw-content tags (script, style), escape closing-tag sequences so the\n // HTML parser doesn't prematurely terminate the element.\n if (RAW_CONTENT_TAGS.has(tag) && innerHTML) {\n innerHTML = escapeInlineContent(innerHTML, tag);\n }\n\n return `<${tag}${attrStr} data-vinext-head=\"true\">${innerHTML}</${tag}>`;\n}\n\nfunction escapeHTML(s: string): string {\n return s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nexport function escapeAttr(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\n/**\n * Escape content that will be placed inside a raw <script> or <style> tag\n * during SSR. The HTML parser treats `</script>` (or `</style>`) as the end\n * of the block regardless of JavaScript string context, so any occurrence\n * of `</` followed by the tag name must be escaped.\n *\n * We replace `</script` and `</style` (case-insensitive) with `<\\/script`\n * and `<\\/style` respectively. The `<\\/` form is harmless in JS/CSS string\n * context but prevents the HTML parser from seeing a closing tag.\n */\nexport function escapeInlineContent(content: string, tag: string): string {\n // Build a pattern like `<\\/script` or `<\\/style`, case-insensitive.\n // `tag` is always a literal developer-controlled value (\"script\" or \"style\")\n // guarded by the RAW_CONTENT_TAGS.has(tag) check at all call sites — never user input.\n const pattern = new RegExp(`<\\\\/(${tag})`, \"gi\");\n return content.replace(pattern, \"<\\\\/$1\");\n}\n\nfunction getDangerouslySetInnerHTML(value: unknown): string | undefined {\n if (typeof value !== \"object\" || value === null) return undefined;\n\n const html = Reflect.get(value, \"__html\");\n return typeof html === \"string\" ? html : undefined;\n}\n\nexport function _applyHeadPropsToElement(\n domEl: HeadDOMElement,\n props: Record<string, unknown>,\n): void {\n const rawHtml = getDangerouslySetInnerHTML(props.dangerouslySetInnerHTML);\n\n if (rawHtml != null) {\n domEl.innerHTML = rawHtml;\n } else if (typeof props.children === \"string\") {\n domEl.textContent = props.children;\n } else if (Array.isArray(props.children)) {\n domEl.textContent = props.children.join(\"\");\n }\n\n for (const [key, value] of Object.entries(props)) {\n if (key === \"children\" || key === \"dangerouslySetInnerHTML\") {\n continue;\n } else if (key === \"className\") {\n domEl.setAttribute(\"class\", String(value));\n } else if (typeof value === \"boolean\" && value) {\n if (!isSafeAttrName(key)) continue;\n domEl.setAttribute(key, \"\");\n } else if (typeof value === \"string\") {\n if (!isSafeAttrName(key)) continue;\n domEl.setAttribute(key, value);\n }\n }\n}\n\nfunction syncClientHead(): void {\n document.querySelectorAll(\"[data-vinext-head]\").forEach((el) => el.remove());\n\n for (const child of reduceHeadChildren([..._clientHeadChildren.values()])) {\n if (typeof child.type !== \"string\") continue;\n\n const domEl = document.createElement(child.type);\n const props = child.props as Record<string, unknown>;\n _applyHeadPropsToElement(domEl, props);\n\n domEl.setAttribute(\"data-vinext-head\", \"true\");\n document.head.appendChild(domEl);\n }\n}\n\n// --- Component ---\n\nfunction Head({ children }: HeadProps): null {\n const headInstanceIdRef = useRef<symbol | null>(null);\n if (headInstanceIdRef.current === null) {\n headInstanceIdRef.current = Symbol(\"vinext-head\");\n }\n\n // SSR path: collect elements for later injection\n if (typeof window === \"undefined\") {\n _getSSRHeadChildren().push(children);\n return null;\n }\n\n // Client path: update the shared head projection after hydration.\n // oxlint-disable-next-line react-hooks/rules-of-hooks\n useEffect(() => {\n const instanceId = headInstanceIdRef.current!;\n _clientHeadChildren.set(instanceId, children);\n syncClientHead();\n\n return () => {\n _clientHeadChildren.delete(instanceId);\n syncClientHead();\n };\n }, [children]);\n\n return null;\n}\n\nexport default Head;\n"],"mappings":"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"}
|
package/dist/shims/headers.d.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
+
import { RenderRequestApiKind } from "../server/cache-proof.js";
|
|
2
|
+
|
|
1
3
|
//#region src/shims/headers.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* next/headers shim
|
|
4
|
-
*
|
|
5
|
-
* Provides cookies() and headers() functions for App Router Server Components.
|
|
6
|
-
* These read from a request context set by the RSC handler before rendering.
|
|
7
|
-
*
|
|
8
|
-
* In Next.js 15+, cookies() and headers() return Promises (async).
|
|
9
|
-
* We support both the sync (legacy) and async patterns.
|
|
10
|
-
*/
|
|
11
4
|
type HeadersContext = {
|
|
12
5
|
headers: Headers;
|
|
13
6
|
cookies: Map<string, string>;
|
|
@@ -20,7 +13,8 @@ type HeadersContext = {
|
|
|
20
13
|
type HeadersAccessPhase = "render" | "action" | "route-handler";
|
|
21
14
|
type VinextHeadersShimState = {
|
|
22
15
|
headersContext: HeadersContext | null;
|
|
23
|
-
dynamicUsageDetected: boolean;
|
|
16
|
+
dynamicUsageDetected: boolean;
|
|
17
|
+
renderRequestApiUsage: Set<RenderRequestApiKind>; /** Error recorded by throwIfInsideCacheScope for dev diagnostics, persists even if caught by user code. */
|
|
24
18
|
invalidDynamicUsageError: unknown;
|
|
25
19
|
pendingSetCookies: string[];
|
|
26
20
|
draftModeCookieHeader: string | null;
|
|
@@ -36,6 +30,9 @@ type VinextHeadersShimState = {
|
|
|
36
30
|
* Called by connection(), cookies(), headers(), and noStore().
|
|
37
31
|
*/
|
|
38
32
|
declare function markDynamicUsage(): void;
|
|
33
|
+
declare function markRenderRequestApiUsage(kind: RenderRequestApiKind): void;
|
|
34
|
+
declare function peekRenderRequestApiUsage(): RenderRequestApiKind[];
|
|
35
|
+
declare function consumeRenderRequestApiUsage(): RenderRequestApiKind[];
|
|
39
36
|
/**
|
|
40
37
|
* Throw if the current execution is inside a "use cache" or unstable_cache()
|
|
41
38
|
* scope. Called by dynamic request APIs (headers, cookies, connection) to
|
|
@@ -60,6 +57,7 @@ declare function consumeInvalidDynamicUsageError(): unknown;
|
|
|
60
57
|
*/
|
|
61
58
|
declare function consumeDynamicUsage(): boolean;
|
|
62
59
|
declare function setHeadersAccessPhase(phase: HeadersAccessPhase): HeadersAccessPhase;
|
|
60
|
+
declare function getHeadersAccessPhase(): HeadersAccessPhase;
|
|
63
61
|
/**
|
|
64
62
|
* Set the headers/cookies context for the current RSC render.
|
|
65
63
|
* Called by the framework's RSC entry before rendering each request.
|
|
@@ -152,8 +150,9 @@ declare function getAndClearPendingCookies(): string[];
|
|
|
152
150
|
* Called by the framework after rendering to attach the header to the response.
|
|
153
151
|
*/
|
|
154
152
|
declare function getDraftModeCookieHeader(): string | null;
|
|
153
|
+
declare function isDraftModeRequest(request: Request): boolean;
|
|
155
154
|
type DraftModeResult = {
|
|
156
|
-
isEnabled: boolean;
|
|
155
|
+
readonly isEnabled: boolean;
|
|
157
156
|
enable(): void;
|
|
158
157
|
disable(): void;
|
|
159
158
|
};
|
|
@@ -218,5 +217,5 @@ declare class RequestCookies {
|
|
|
218
217
|
toString(): string;
|
|
219
218
|
}
|
|
220
219
|
//#endregion
|
|
221
|
-
export { HeadersAccessPhase, HeadersContext, type RequestCookies, VinextHeadersShimState, applyMiddlewareRequestHeaders, consumeDynamicUsage, consumeInvalidDynamicUsageError, cookies, draftMode, getAndClearPendingCookies, getDraftModeCookieHeader, getHeadersContext, headers, headersContextFromRequest, markDynamicUsage, runWithHeadersContext, setHeadersAccessPhase, setHeadersContext, throwIfInsideCacheScope };
|
|
220
|
+
export { HeadersAccessPhase, HeadersContext, type RequestCookies, VinextHeadersShimState, applyMiddlewareRequestHeaders, consumeDynamicUsage, consumeInvalidDynamicUsageError, consumeRenderRequestApiUsage, cookies, draftMode, getAndClearPendingCookies, getDraftModeCookieHeader, getHeadersAccessPhase, getHeadersContext, headers, headersContextFromRequest, isDraftModeRequest, markDynamicUsage, markRenderRequestApiUsage, peekRenderRequestApiUsage, runWithHeadersContext, setHeadersAccessPhase, setHeadersContext, throwIfInsideCacheScope };
|
|
222
221
|
//# sourceMappingURL=headers.d.ts.map
|