@wular/pnext 0.0.3 → 0.0.6
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/README.md +11 -11
- package/bin/pnext +1 -1
- package/config/lint/base.js +7 -7
- package/config/ts/base.json +2 -4
- package/config/ts/react.json +2 -6
- package/package.json +23 -2
- package/reference/compat.md +1 -1
- package/reference/config.md +2 -2
- package/reference/css.md +13 -9
- package/reference/dev.md +1 -1
- package/reference/metadata.md +7 -7
- package/reference/navigation.md +35 -28
- package/reference/performance.md +70 -70
- package/reference/rendering.md +17 -17
- package/reference/routing.md +17 -17
- package/reference/typegen.md +12 -8
- package/src/api/cache.ts +36 -37
- package/src/api/client-cache.ts +2 -2
- package/src/api/client-navigation.ts +113 -109
- package/src/api/dynamic.tsx +58 -55
- package/src/api/link.tsx +49 -52
- package/src/api/navigation.ts +59 -58
- package/src/api/server.ts +152 -144
- package/src/api/suspense.ts +4 -4
- package/src/cli/adapters/vercel-warm.ts +126 -121
- package/src/cli/adapters/vercel.ts +437 -443
- package/src/cli/analyze.ts +333 -144
- package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
- package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
- package/src/cli/build.ts +976 -978
- package/src/cli/create.ts +71 -63
- package/src/cli/dev.ts +116 -116
- package/src/cli/index.ts +88 -85
- package/src/cli/migrate/package-json.ts +73 -71
- package/src/cli/migrate/report.ts +32 -33
- package/src/cli/migrate/{index.ts → run.ts} +48 -49
- package/src/cli/migrate/scan.ts +46 -46
- package/src/cli/migrate/spinner.ts +9 -9
- package/src/cli/migrate/tsconfig.ts +35 -35
- package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
- package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
- package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
- package/src/cli/start.ts +65 -66
- package/src/{typegen.ts → cli/typegen.ts} +59 -59
- package/src/client/build.ts +794 -768
- package/src/client/chunk-fold.ts +245 -240
- package/src/client/{paths.ts → chunk-name.ts} +6 -6
- package/src/client/entry.ts +162 -147
- package/src/client/prebuilt.ts +231 -223
- package/src/client/profile.ts +29 -29
- package/src/client/react-compiler.ts +20 -15
- package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
- package/src/client/reference-stub.ts +51 -51
- package/src/client/reference.ts +19 -19
- package/src/{api → client}/router/events.ts +17 -17
- package/src/{api → client}/router/history.ts +16 -16
- package/src/{api → client}/router/hub.ts +52 -53
- package/src/{api/router.ts → client/router/index.ts} +61 -60
- package/src/{api → client}/router/policies.ts +24 -24
- package/src/{api → client}/router/runtime.ts +1988 -1961
- package/src/{api → client}/router/types.ts +93 -98
- package/src/compat/actions/client-plugin.ts +42 -43
- package/src/compat/actions/client-stub.ts +14 -14
- package/src/compat/actions/{action-client.ts → client.ts} +194 -193
- package/src/compat/actions/config.ts +63 -64
- package/src/compat/actions/detect.ts +68 -68
- package/src/compat/actions/discovery.ts +105 -105
- package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
- package/src/compat/actions/early-submit.ts +1 -1
- package/src/compat/actions/endpoint.ts +198 -198
- package/src/compat/actions/extensions.ts +811 -0
- package/src/compat/actions/flight.ts +23 -23
- package/src/compat/actions/form-state.ts +34 -34
- package/src/compat/actions/hoist.ts +382 -236
- package/src/compat/actions/ids.ts +7 -12
- package/src/compat/actions/index.ts +8 -8
- package/src/compat/actions/instances.ts +46 -46
- package/src/compat/actions/origin.ts +47 -48
- package/src/compat/actions/protocol.ts +22 -22
- package/src/compat/actions/registry.ts +19 -19
- package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
- package/src/compat/actions/rewrite.ts +125 -125
- package/src/compat/actions/{action-router.ts → router.ts} +10 -10
- package/src/compat/actions/serve.ts +132 -136
- package/src/compat/actions/server-tag.ts +4 -4
- package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
- package/src/compat/actions/unrecognized-error.ts +4 -4
- package/src/compat/{index.ts → aliases.ts} +112 -109
- package/src/compat/bundler/bun-externals.ts +18 -18
- package/src/compat/bundler/cjs-exports.ts +271 -223
- package/src/compat/bundler/config.ts +116 -111
- package/src/compat/bundler/externals.ts +12 -12
- package/src/compat/bundler/import-meta-url.ts +19 -19
- package/src/compat/bundler/modularize-imports.ts +36 -33
- package/src/compat/bundler/new-url-asset.ts +22 -24
- package/src/compat/bundler/optimize-package-imports.ts +111 -107
- package/src/compat/bundler/polyfill.ts +28 -28
- package/src/compat/bundler/react-compiler.ts +35 -29
- package/src/compat/bundler/react-profiler.tsx +11 -11
- package/src/compat/bundler/relay-transform.ts +48 -47
- package/src/compat/bundler/require-context.ts +119 -113
- package/src/compat/bundler/resolve-extensions.ts +19 -19
- package/src/compat/bundler/source-cache.ts +25 -25
- package/src/compat/bundler/static-imports.ts +7 -7
- package/src/compat/bundler/symlink-imports.ts +46 -46
- package/src/compat/bundler/tsconfig-paths.ts +13 -15
- package/src/compat/bundler/wasm.ts +58 -60
- package/src/compat/bundler/webpack-loaders.ts +254 -241
- package/src/compat/bundler/worker.ts +101 -104
- package/src/compat/cache/build-flags.ts +29 -29
- package/src/compat/cache/build-prerender-errors.ts +40 -44
- package/src/compat/cache/custom-handler.ts +60 -53
- package/src/compat/cache/fetch-patch.ts +240 -240
- package/src/compat/cache/handler.ts +27 -31
- package/src/compat/cache/modern-handler.ts +148 -126
- package/src/compat/cache/resume-data-cache.ts +47 -45
- package/src/compat/cache/revalidate.ts +233 -232
- package/src/compat/cache/runtime-error.ts +35 -35
- package/src/compat/cache/use-cache-transform.ts +442 -417
- package/src/compat/cache/use-cache.ts +650 -614
- package/src/compat/cache-control.ts +140 -142
- package/src/compat/client/base-path.ts +21 -20
- package/src/compat/client/css-order.ts +18 -18
- package/src/compat/client/errors/bare-boundary.ts +11 -11
- package/src/compat/client/errors/control-flow.ts +23 -23
- package/src/compat/client/errors/error-boundary.ts +61 -61
- package/src/compat/client/errors/global-error.ts +101 -91
- package/src/compat/client/errors/install.ts +71 -72
- package/src/compat/client/errors/lazy.ts +23 -23
- package/src/compat/client/errors/primitive-throw.ts +47 -45
- package/src/compat/client/errors/soft-refresh.ts +4 -4
- package/src/compat/client/link-status.ts +33 -33
- package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
- package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
- package/src/compat/client/navigation-scroll.ts +63 -63
- package/src/compat/client/optimistic-routing.ts +93 -88
- package/src/compat/client/prefetch-cache.ts +23 -24
- package/src/compat/client/route-announcer.ts +35 -35
- package/src/compat/client/segment-cache-policy.ts +20 -20
- package/src/compat/client/segment-cache.ts +309 -315
- package/src/compat/client/segment-prefetch.ts +127 -132
- package/src/compat/client/trailing-slash.ts +5 -4
- package/src/compat/css/chunking.ts +113 -116
- package/src/compat/css/inline-css.ts +21 -21
- package/src/compat/css/lightningcss.ts +37 -38
- package/src/compat/css/modules.ts +161 -175
- package/src/compat/css/nonce.ts +7 -7
- package/src/compat/css/sass-plugin.ts +18 -21
- package/src/compat/css/sass.ts +150 -152
- package/src/compat/css/styled-jsx-runtime.ts +27 -27
- package/src/compat/css/styled-jsx.ts +21 -21
- package/src/compat/edge-runtime.ts +27 -27
- package/src/compat/{adapter → export}/build-complete.ts +72 -75
- package/src/compat/export/client.ts +29 -31
- package/src/compat/export/{index.ts → emit.ts} +111 -110
- package/src/compat/export/standalone.ts +62 -54
- package/src/compat/image-optimizer/cache.ts +51 -49
- package/src/compat/image-optimizer/detect.ts +52 -52
- package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
- package/src/compat/image-optimizer/source.ts +77 -80
- package/src/compat/lifecycle/after-scope.ts +26 -26
- package/src/compat/lifecycle/after.ts +48 -45
- package/src/compat/lifecycle/error-funnel.ts +98 -102
- package/src/compat/lifecycle/error-serialize.ts +30 -26
- package/src/compat/lifecycle/error-ui.ts +67 -32
- package/src/compat/lifecycle/instrumentation-client.ts +36 -38
- package/src/compat/lifecycle/instrumentation.ts +85 -85
- package/src/compat/lifecycle/node-console.ts +11 -11
- package/src/compat/lifecycle/testmode.ts +160 -132
- package/src/compat/mdx/compile.ts +60 -57
- package/src/compat/mdx/plugin.ts +11 -11
- package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
- package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
- package/src/compat/metadata.ts +75 -81
- package/src/compat/next/cache.ts +68 -69
- package/src/compat/next/canonical-url.ts +9 -9
- package/src/compat/next/client-cache.ts +19 -19
- package/src/compat/next/client-navigation.ts +112 -116
- package/src/compat/next/client-only.ts +1 -1
- package/src/compat/next/client-script.tsx +99 -93
- package/src/compat/next/client-server.ts +10 -10
- package/src/compat/next/config-loader.ts +176 -173
- package/src/compat/next/config.ts +7 -7
- package/src/compat/next/constants.cjs +6 -6
- package/src/compat/next/constants.ts +6 -6
- package/src/compat/next/custom-server.ts +26 -24
- package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
- package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
- package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
- package/src/compat/next/dynamic.tsx +21 -18
- package/src/compat/next/error.tsx +52 -52
- package/src/compat/next/font/cache.ts +74 -74
- package/src/compat/next/font/google.ts +2 -2
- package/src/compat/next/font/index.ts +1 -1
- package/src/compat/next/font/local.ts +3 -3
- package/src/compat/next/font/runtime-client.ts +17 -15
- package/src/compat/next/font/runtime.ts +443 -408
- package/src/compat/next/font/shared.ts +120 -108
- package/src/compat/next/form.tsx +63 -63
- package/src/compat/next/head.tsx +2 -2
- package/src/compat/next/headers.ts +103 -95
- package/src/compat/next/image/client.tsx +220 -0
- package/src/compat/next/image/config.ts +56 -58
- package/src/compat/next/image/optimizer.ts +40 -36
- package/src/compat/next/image/patterns.ts +37 -40
- package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
- package/src/compat/next/image/shared.ts +65 -57
- package/src/compat/next/image/static-metadata.ts +98 -107
- package/src/compat/next/image/validate.ts +79 -88
- package/src/compat/next/image.tsx +19 -23
- package/src/compat/next/index.ts +1 -1
- package/src/compat/next/legacy-image.tsx +59 -60
- package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
- package/src/compat/next/link.tsx +158 -158
- package/src/compat/next/navigation.cjs +12 -3
- package/src/compat/next/navigation.ts +48 -50
- package/src/compat/next/offline.ts +27 -27
- package/src/compat/next/og.ts +121 -124
- package/src/compat/next/preferred-region.ts +13 -14
- package/src/compat/next/redirects.ts +58 -56
- package/src/compat/next/resource-hints.ts +73 -76
- package/src/compat/next/rewrites.ts +130 -133
- package/src/compat/next/root-params.ts +45 -45
- package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
- package/src/compat/next/router.cjs +4 -2
- package/src/compat/next/router.ts +58 -61
- package/src/compat/next/script.tsx +108 -108
- package/src/compat/next/server-only.ts +1 -1
- package/src/compat/next/server.ts +19 -19
- package/src/compat/next/svgr.ts +18 -17
- package/src/compat/next/telemetry.ts +24 -24
- package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
- package/src/compat/next/user-agent.ts +53 -49
- package/src/compat/next/web-vitals.ts +22 -24
- package/src/compat/otel/api.ts +41 -41
- package/src/compat/otel/client-trace-metadata.ts +25 -27
- package/src/compat/otel/fetch-span.ts +29 -29
- package/src/compat/otel/tracer.ts +331 -331
- package/src/compat/pages/api.ts +456 -0
- package/src/compat/pages/client-plugin.ts +36 -36
- package/src/compat/pages/router-state.ts +34 -34
- package/src/compat/pages/{index.ts → router.ts} +130 -135
- package/src/compat/ppr/io.ts +12 -12
- package/src/compat/ppr/missing-root-params.ts +34 -38
- package/src/compat/ppr/root-params-scan.ts +66 -66
- package/src/compat/ppr/root-params-transform.ts +24 -26
- package/src/compat/ppr/root-params.ts +30 -30
- package/src/compat/ppr/segment-config-incompat.ts +6 -7
- package/src/compat/protocol.ts +71 -70
- package/src/compat/react/action-state.ts +47 -48
- package/src/compat/react/client-lite.ts +15 -15
- package/src/compat/react/client.ts +4 -4
- package/src/compat/react/compiler-runtime.ts +11 -11
- package/src/compat/react/dom-client.ts +44 -44
- package/src/compat/react/dom-react-server.ts +10 -16
- package/src/compat/react/dom-server.ts +10 -10
- package/src/compat/react/dom.ts +52 -52
- package/src/compat/react/hooks-extra.ts +34 -35
- package/src/compat/react/parity.ts +64 -61
- package/src/compat/react/preact.ts +81 -82
- package/src/compat/react/react-server.ts +28 -28
- package/src/compat/react/router-shim.ts +1 -1
- package/src/compat/react/server-component-use.ts +8 -8
- package/src/compat/react/server-inserted-html.ts +30 -31
- package/src/compat/react/server.ts +53 -55
- package/src/compat/react/use.ts +32 -32
- package/src/compat/react/view-transition.ts +20 -20
- package/src/compat/register/actions.ts +35 -824
- package/src/compat/register/boot.ts +41 -41
- package/src/compat/register/build-tier.ts +5 -5
- package/src/compat/register/build.ts +74 -70
- package/src/compat/register/bundler.ts +161 -156
- package/src/compat/register/cache.ts +20 -20
- package/src/compat/register/client-errors.ts +3 -3
- package/src/compat/register/config.ts +6 -6
- package/src/compat/register/css-extras.ts +30 -34
- package/src/compat/register/edge-runtime.ts +3 -3
- package/src/compat/register/errors.ts +10 -12
- package/src/compat/register/export.ts +16 -16
- package/src/compat/register/font.ts +11 -11
- package/src/compat/register/hooks.ts +2 -2
- package/src/compat/register/image.ts +40 -40
- package/src/compat/register/index.ts +59 -62
- package/src/compat/register/instrumentation-client.ts +10 -10
- package/src/compat/register/lifecycle.ts +25 -28
- package/src/compat/register/mdx.ts +10 -10
- package/src/compat/register/middleware.ts +226 -16
- package/src/compat/register/otel.ts +80 -88
- package/src/compat/register/pages-api.ts +10 -463
- package/src/compat/register/ppr.ts +16 -16
- package/src/compat/register/protocol.ts +17 -18
- package/src/compat/register/proxy.ts +49 -51
- package/src/compat/register/render.ts +79 -76
- package/src/compat/register/routing.ts +162 -159
- package/src/compat/register/segment.ts +24 -1887
- package/src/compat/register/static-image.ts +3 -3
- package/src/compat/register/{misc.ts → taint.ts} +9 -9
- package/src/compat/register/typed-routes.ts +14 -14
- package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
- package/src/compat/register/validation.ts +18 -18
- package/src/compat/segment/loading-boundary.ts +43 -45
- package/src/compat/segment/page-slot.ts +69 -69
- package/src/compat/segment/serve.ts +1884 -0
- package/src/compat/segment/tree.ts +113 -112
- package/src/compat/segment/vary-key.ts +38 -38
- package/src/compat/segment/vary-params.ts +138 -142
- package/src/compat/static-params.ts +14 -12
- package/src/compat/tsconfig-defaults.ts +87 -91
- package/src/compat/typecheck/{index.ts → check.ts} +234 -212
- package/src/compat/typecheck/worker.ts +15 -12
- package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
- package/src/compat/typed-routes/manifest.ts +174 -170
- package/src/compat/typed-routes/typegen.ts +127 -110
- package/src/compat/validation/errors.ts +16 -19
- package/src/compat/validation/prerender-diagnostics.ts +521 -504
- package/src/compat/validation/{index.ts → validate.ts} +647 -648
- package/src/compat-bootstrap.ts +16 -16
- package/src/config.ts +69 -69
- package/src/css/build.ts +230 -227
- package/src/css/postcss.ts +79 -80
- package/src/css/worker.ts +14 -15
- package/src/dev/client-actions.ts +10 -10
- package/src/dev/client-chunk-store.ts +27 -27
- package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
- package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
- package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
- package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
- package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
- package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
- package/src/dev/server.ts +804 -820
- package/src/env.ts +46 -43
- package/src/extensions.ts +491 -478
- package/src/index.ts +8 -8
- package/src/internal.ts +20 -23
- package/src/{islands → render}/boundary-error.ts +3 -3
- package/src/render/hooks.ts +71 -71
- package/src/render/island-context.ts +14 -14
- package/src/render/metadata.ts +310 -310
- package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
- package/src/{ppr.ts → render/ppr.ts} +244 -245
- package/src/render/renderer.ts +2076 -2082
- package/src/render/resource-hints.ts +16 -17
- package/src/render/slots.tsx +224 -235
- package/src/{islands → render}/static-children.ts +9 -12
- package/src/{islands → render}/static-slots.ts +37 -37
- package/src/{cache/context.ts → request/cache.ts} +20 -20
- package/src/request/context.ts +107 -107
- package/src/{dynamic/source.ts → resolve/dynamic.ts} +152 -143
- package/src/resolve/engine.ts +90 -77
- package/src/resolve/imports.ts +475 -463
- package/src/resolve/scan-facts.ts +442 -186
- package/src/resolve/source-text.ts +37 -37
- package/src/{dynamic → resolve}/tree-shake.ts +132 -128
- package/src/routing/forwarded.ts +19 -19
- package/src/routing/handler.ts +84 -91
- package/src/routing/href.ts +69 -70
- package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
- package/src/{proxy.ts → routing/proxy.ts} +306 -312
- package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
- package/src/routing/routes.ts +833 -848
- package/src/routing/slots.ts +164 -160
- package/src/runtime/loader.ts +952 -0
- package/src/{dev → runtime}/module-cache.ts +309 -287
- package/src/{dev → runtime}/module-generations.ts +9 -9
- package/src/{dev → runtime}/module-transform.ts +81 -72
- package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
- package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
- package/src/runtime/vendor.ts +425 -404
- package/src/styles.d.ts +9 -0
- package/src/types.ts +320 -335
- package/src/utils/ansi.ts +5 -5
- package/src/utils/{source.ts → code.ts} +15 -12
- package/src/utils/content-type.ts +3 -3
- package/src/utils/decode.ts +2 -2
- package/src/utils/dev-profile.ts +13 -13
- package/src/utils/error-log.ts +6 -6
- package/src/utils/esbuild.ts +18 -18
- package/src/utils/fs-cache.ts +13 -13
- package/src/utils/fs.ts +57 -49
- package/src/utils/html.ts +20 -24
- package/src/utils/native-require.ts +8 -8
- package/src/utils/serialize.ts +139 -146
- package/src/utils/verbose.ts +18 -18
- package/src/cli/analyze-print.ts +0 -181
- package/src/compat/middleware/manifest.ts +0 -210
- package/src/compat/next/image-client.tsx +0 -215
- package/src/compat/next/link-usage.ts +0 -29
- package/src/css/index.ts +0 -2
- package/src/render/index.ts +0 -1
- package/src/style-modules.d.ts +0 -9
package/src/compat/next/og.ts
CHANGED
|
@@ -10,56 +10,53 @@
|
|
|
10
10
|
// satori/resvg pipeline is driven through a ReadableStream that resolves the bytes lazily; the fallback
|
|
11
11
|
// path also flows through the stream.
|
|
12
12
|
|
|
13
|
-
import { createRequire } from 'node:module'
|
|
14
|
-
import path from 'node:path'
|
|
13
|
+
import { createRequire } from 'node:module'
|
|
14
|
+
import path from 'node:path'
|
|
15
15
|
|
|
16
16
|
export interface ImageResponseFont {
|
|
17
|
-
name: string
|
|
18
|
-
data: ArrayBuffer | Uint8Array
|
|
19
|
-
weight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
|
|
20
|
-
style?: 'normal' | 'italic'
|
|
17
|
+
name: string
|
|
18
|
+
data: ArrayBuffer | Uint8Array
|
|
19
|
+
weight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
|
|
20
|
+
style?: 'normal' | 'italic'
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export type ImageResponseOptions = ResponseInit & {
|
|
24
|
-
width?: number
|
|
25
|
-
height?: number
|
|
26
|
-
debug?: boolean
|
|
27
|
-
fonts?: ImageResponseFont[]
|
|
28
|
-
emoji?: string
|
|
29
|
-
}
|
|
24
|
+
width?: number
|
|
25
|
+
height?: number
|
|
26
|
+
debug?: boolean
|
|
27
|
+
fonts?: ImageResponseFont[]
|
|
28
|
+
emoji?: string
|
|
29
|
+
}
|
|
30
30
|
|
|
31
|
-
const DEFAULT_WIDTH = 1200
|
|
32
|
-
const DEFAULT_HEIGHT = 630
|
|
33
|
-
const MAX_DIMENSION = 4096
|
|
34
|
-
const MAX_RAW_IMAGE_BYTES = 3 * 1024 * 1024
|
|
35
|
-
const PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])
|
|
31
|
+
const DEFAULT_WIDTH = 1200
|
|
32
|
+
const DEFAULT_HEIGHT = 630
|
|
33
|
+
const MAX_DIMENSION = 4096
|
|
34
|
+
const MAX_RAW_IMAGE_BYTES = 3 * 1024 * 1024
|
|
35
|
+
const PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])
|
|
36
36
|
|
|
37
|
-
let crcTable: Uint32Array | undefined
|
|
37
|
+
let crcTable: Uint32Array | undefined
|
|
38
38
|
|
|
39
39
|
export class ImageResponse extends Response {
|
|
40
|
-
static displayName = 'ImageResponse'
|
|
40
|
+
static displayName = 'ImageResponse'
|
|
41
41
|
|
|
42
42
|
constructor(element: unknown, options: ImageResponseOptions = {}) {
|
|
43
43
|
// Validate dimensions eagerly so an invalid width/height throws from the
|
|
44
44
|
// constructor (matching Next), not later inside the async PNG stream — a
|
|
45
45
|
// stream-time RangeError would otherwise surface as a degraded fallback PNG
|
|
46
46
|
// instead of the synchronous error callers assert on.
|
|
47
|
-
dimension(options.width, DEFAULT_WIDTH, 'width')
|
|
48
|
-
dimension(options.height, DEFAULT_HEIGHT, 'height')
|
|
49
|
-
const headers = new Headers(options.headers)
|
|
50
|
-
headers.set('content-type', 'image/png')
|
|
47
|
+
dimension(options.width, DEFAULT_WIDTH, 'width')
|
|
48
|
+
dimension(options.height, DEFAULT_HEIGHT, 'height')
|
|
49
|
+
const headers = new Headers(options.headers)
|
|
50
|
+
headers.set('content-type', 'image/png')
|
|
51
51
|
// Match Next's default caching behavior for on-the-fly OG images.
|
|
52
52
|
if (!headers.has('cache-control')) {
|
|
53
|
-
headers.set(
|
|
54
|
-
'cache-control',
|
|
55
|
-
'public, immutable, no-transform, max-age=31536000',
|
|
56
|
-
);
|
|
53
|
+
headers.set('cache-control', 'public, immutable, no-transform, max-age=31536000')
|
|
57
54
|
}
|
|
58
55
|
super(pngStream(element, options), {
|
|
59
56
|
status: options.status,
|
|
60
57
|
statusText: options.statusText,
|
|
61
58
|
headers,
|
|
62
|
-
})
|
|
59
|
+
})
|
|
63
60
|
}
|
|
64
61
|
}
|
|
65
62
|
|
|
@@ -71,23 +68,23 @@ export class ImageResponse extends Response {
|
|
|
71
68
|
function pngStream(element: unknown, options: ImageResponseOptions): ReadableStream<Uint8Array> {
|
|
72
69
|
return new ReadableStream<Uint8Array>({
|
|
73
70
|
async start(controller) {
|
|
74
|
-
let bytes: Uint8Array
|
|
71
|
+
let bytes: Uint8Array
|
|
75
72
|
try {
|
|
76
73
|
bytes =
|
|
77
74
|
(await renderWithVercelOg(element, options)) ??
|
|
78
75
|
(await renderWithSatori(element, options)) ??
|
|
79
|
-
createFallbackPng(options)
|
|
76
|
+
createFallbackPng(options)
|
|
80
77
|
} catch {
|
|
81
|
-
bytes = createFallbackPng(options)
|
|
78
|
+
bytes = createFallbackPng(options)
|
|
82
79
|
}
|
|
83
|
-
controller.enqueue(bytes)
|
|
84
|
-
controller.close()
|
|
80
|
+
controller.enqueue(bytes)
|
|
81
|
+
controller.close()
|
|
85
82
|
},
|
|
86
|
-
})
|
|
83
|
+
})
|
|
87
84
|
}
|
|
88
85
|
|
|
89
86
|
interface VercelOgModule {
|
|
90
|
-
ImageResponse: new (element: unknown, options?: Record<string, unknown>) => Response
|
|
87
|
+
ImageResponse: new (element: unknown, options?: Record<string, unknown>) => Response
|
|
91
88
|
}
|
|
92
89
|
|
|
93
90
|
/**
|
|
@@ -103,30 +100,30 @@ async function renderWithVercelOg(
|
|
|
103
100
|
element: unknown,
|
|
104
101
|
options: ImageResponseOptions,
|
|
105
102
|
): Promise<Uint8Array | undefined> {
|
|
106
|
-
const og = await loadFromAppRoot<VercelOgModule>('@vercel/og')
|
|
107
|
-
if (!og?.ImageResponse) return undefined
|
|
103
|
+
const og = await loadFromAppRoot<VercelOgModule>('@vercel/og')
|
|
104
|
+
if (!og?.ImageResponse) return undefined
|
|
108
105
|
|
|
109
|
-
const width = dimension(options.width, DEFAULT_WIDTH, 'width')
|
|
110
|
-
const height = dimension(options.height, DEFAULT_HEIGHT, 'height')
|
|
106
|
+
const width = dimension(options.width, DEFAULT_WIDTH, 'width')
|
|
107
|
+
const height = dimension(options.height, DEFAULT_HEIGHT, 'height')
|
|
111
108
|
const response = new og.ImageResponse(element, {
|
|
112
109
|
width,
|
|
113
110
|
height,
|
|
114
111
|
...(options.fonts === undefined ? {} : { fonts: options.fonts }),
|
|
115
112
|
...(options.debug === undefined ? {} : { debug: options.debug }),
|
|
116
113
|
...(options.emoji === undefined ? {} : { emoji: options.emoji }),
|
|
117
|
-
})
|
|
118
|
-
const bytes = new Uint8Array(await response.arrayBuffer())
|
|
119
|
-
return bytes.length > 0 ? bytes : undefined
|
|
114
|
+
})
|
|
115
|
+
const bytes = new Uint8Array(await response.arrayBuffer())
|
|
116
|
+
return bytes.length > 0 ? bytes : undefined
|
|
120
117
|
}
|
|
121
118
|
|
|
122
119
|
interface SatoriModule {
|
|
123
|
-
default: (element: unknown, opts: Record<string, unknown>) => Promise<string
|
|
120
|
+
default: (element: unknown, opts: Record<string, unknown>) => Promise<string>
|
|
124
121
|
}
|
|
125
122
|
interface ResvgModule {
|
|
126
123
|
Resvg: new (
|
|
127
124
|
svg: string | Uint8Array,
|
|
128
125
|
opts?: Record<string, unknown>,
|
|
129
|
-
) => { render: () => { asPng: () => Uint8Array } }
|
|
126
|
+
) => { render: () => { asPng: () => Uint8Array } }
|
|
130
127
|
}
|
|
131
128
|
|
|
132
129
|
/**
|
|
@@ -137,15 +134,15 @@ async function renderWithSatori(
|
|
|
137
134
|
element: unknown,
|
|
138
135
|
options: ImageResponseOptions,
|
|
139
136
|
): Promise<Uint8Array | undefined> {
|
|
140
|
-
const satori = await loadOptional<SatoriModule>('satori')
|
|
141
|
-
const resvg = await loadOptional<ResvgModule>('@resvg/resvg-js')
|
|
142
|
-
if (!satori || !resvg) return undefined
|
|
137
|
+
const satori = await loadOptional<SatoriModule>('satori')
|
|
138
|
+
const resvg = await loadOptional<ResvgModule>('@resvg/resvg-js')
|
|
139
|
+
if (!satori || !resvg) return undefined
|
|
143
140
|
|
|
144
|
-
const fonts = normalizeFonts(options.fonts)
|
|
145
|
-
if (fonts.length === 0) return undefined
|
|
141
|
+
const fonts = normalizeFonts(options.fonts)
|
|
142
|
+
if (fonts.length === 0) return undefined
|
|
146
143
|
|
|
147
|
-
const width = dimension(options.width, DEFAULT_WIDTH, 'width')
|
|
148
|
-
const height = dimension(options.height, DEFAULT_HEIGHT, 'height')
|
|
144
|
+
const width = dimension(options.width, DEFAULT_WIDTH, 'width')
|
|
145
|
+
const height = dimension(options.height, DEFAULT_HEIGHT, 'height')
|
|
149
146
|
|
|
150
147
|
const svg = await satori.default(element, {
|
|
151
148
|
width,
|
|
@@ -153,29 +150,29 @@ async function renderWithSatori(
|
|
|
153
150
|
fonts,
|
|
154
151
|
...(options.debug === undefined ? {} : { debug: options.debug }),
|
|
155
152
|
...(options.emoji === undefined ? {} : { emoji: options.emoji }),
|
|
156
|
-
})
|
|
153
|
+
})
|
|
157
154
|
|
|
158
155
|
const renderer = new resvg.Resvg(svg, {
|
|
159
156
|
fitTo: { mode: 'width', value: width },
|
|
160
|
-
})
|
|
161
|
-
return renderer.render().asPng()
|
|
157
|
+
})
|
|
158
|
+
return renderer.render().asPng()
|
|
162
159
|
}
|
|
163
160
|
|
|
164
161
|
interface SatoriFont {
|
|
165
|
-
name: string
|
|
166
|
-
data: ArrayBuffer | Uint8Array
|
|
167
|
-
weight?: number
|
|
168
|
-
style?: 'normal' | 'italic'
|
|
162
|
+
name: string
|
|
163
|
+
data: ArrayBuffer | Uint8Array
|
|
164
|
+
weight?: number
|
|
165
|
+
style?: 'normal' | 'italic'
|
|
169
166
|
}
|
|
170
167
|
|
|
171
168
|
function normalizeFonts(fonts: ImageResponseFont[] | undefined): SatoriFont[] {
|
|
172
|
-
if (!fonts) return []
|
|
169
|
+
if (!fonts) return []
|
|
173
170
|
return fonts.map(font => ({
|
|
174
171
|
name: font.name,
|
|
175
172
|
data: font.data,
|
|
176
173
|
...(font.weight === undefined ? {} : { weight: font.weight }),
|
|
177
174
|
...(font.style === undefined ? {} : { style: font.style }),
|
|
178
|
-
}))
|
|
175
|
+
}))
|
|
179
176
|
}
|
|
180
177
|
|
|
181
178
|
/**
|
|
@@ -185,10 +182,10 @@ function normalizeFonts(fonts: ImageResponseFont[] | undefined): SatoriFont[] {
|
|
|
185
182
|
*/
|
|
186
183
|
async function loadOptional<T>(name: string): Promise<T | undefined> {
|
|
187
184
|
try {
|
|
188
|
-
const specifier = name
|
|
189
|
-
return (await import(/* @vite-ignore */ specifier)) as T
|
|
185
|
+
const specifier = name
|
|
186
|
+
return (await import(/* @vite-ignore */ specifier)) as T
|
|
190
187
|
} catch {
|
|
191
|
-
return undefined
|
|
188
|
+
return undefined
|
|
192
189
|
}
|
|
193
190
|
}
|
|
194
191
|
|
|
@@ -202,11 +199,11 @@ async function loadOptional<T>(name: string): Promise<T | undefined> {
|
|
|
202
199
|
*/
|
|
203
200
|
async function loadFromAppRoot<T>(name: string): Promise<T | undefined> {
|
|
204
201
|
try {
|
|
205
|
-
const resolveFrom = createRequire(path.join(process.cwd(), 'pnext-og-resolve.cjs'))
|
|
206
|
-
const resolved = resolveFrom.resolve(name)
|
|
207
|
-
return (await import(/* @vite-ignore */ resolved)) as T
|
|
202
|
+
const resolveFrom = createRequire(path.join(process.cwd(), 'pnext-og-resolve.cjs'))
|
|
203
|
+
const resolved = resolveFrom.resolve(name)
|
|
204
|
+
return (await import(/* @vite-ignore */ resolved)) as T
|
|
208
205
|
} catch {
|
|
209
|
-
return undefined
|
|
206
|
+
return undefined
|
|
210
207
|
}
|
|
211
208
|
}
|
|
212
209
|
|
|
@@ -217,108 +214,108 @@ async function loadFromAppRoot<T>(name: string): Promise<T | undefined> {
|
|
|
217
214
|
// ---------------------------------------------------------------------------
|
|
218
215
|
|
|
219
216
|
function createFallbackPng(options: ImageResponseOptions) {
|
|
220
|
-
const width = dimension(options.width, DEFAULT_WIDTH, 'width')
|
|
221
|
-
const height = dimension(options.height, DEFAULT_HEIGHT, 'height')
|
|
222
|
-
const rowBytes = Math.ceil(width / 8) + 1
|
|
223
|
-
const rawBytes = rowBytes * height
|
|
217
|
+
const width = dimension(options.width, DEFAULT_WIDTH, 'width')
|
|
218
|
+
const height = dimension(options.height, DEFAULT_HEIGHT, 'height')
|
|
219
|
+
const rowBytes = Math.ceil(width / 8) + 1
|
|
220
|
+
const rawBytes = rowBytes * height
|
|
224
221
|
if (rawBytes > MAX_RAW_IMAGE_BYTES) {
|
|
225
222
|
throw new RangeError(
|
|
226
223
|
`ImageResponse dimensions ${width}x${height} exceed the lightweight PNG limit.`,
|
|
227
|
-
)
|
|
224
|
+
)
|
|
228
225
|
}
|
|
229
226
|
|
|
230
|
-
const ihdr = new Uint8Array(13)
|
|
231
|
-
const ihdrView = new DataView(ihdr.buffer)
|
|
232
|
-
ihdrView.setUint32(0, width)
|
|
233
|
-
ihdrView.setUint32(4, height)
|
|
234
|
-
ihdr[8] = 1
|
|
227
|
+
const ihdr = new Uint8Array(13)
|
|
228
|
+
const ihdrView = new DataView(ihdr.buffer)
|
|
229
|
+
ihdrView.setUint32(0, width)
|
|
230
|
+
ihdrView.setUint32(4, height)
|
|
231
|
+
ihdr[8] = 1
|
|
235
232
|
|
|
236
233
|
return concat(
|
|
237
234
|
PNG_SIGNATURE,
|
|
238
235
|
chunk('IHDR', ihdr),
|
|
239
236
|
chunk('IDAT', zlibStoreZeros(rawBytes)),
|
|
240
237
|
chunk('IEND', new Uint8Array()),
|
|
241
|
-
)
|
|
238
|
+
)
|
|
242
239
|
}
|
|
243
240
|
|
|
244
241
|
function dimension(value: number | undefined, fallback: number, name: string) {
|
|
245
|
-
if (value === undefined) return fallback
|
|
242
|
+
if (value === undefined) return fallback
|
|
246
243
|
if (!Number.isFinite(value) || value <= 0) {
|
|
247
|
-
throw new RangeError(`ImageResponse ${name} must be a positive finite number.`)
|
|
244
|
+
throw new RangeError(`ImageResponse ${name} must be a positive finite number.`)
|
|
248
245
|
}
|
|
249
|
-
const rounded = Math.floor(value)
|
|
246
|
+
const rounded = Math.floor(value)
|
|
250
247
|
if (rounded > MAX_DIMENSION) {
|
|
251
|
-
throw new RangeError(`ImageResponse ${name} must be ${MAX_DIMENSION} pixels or less.`)
|
|
248
|
+
throw new RangeError(`ImageResponse ${name} must be ${MAX_DIMENSION} pixels or less.`)
|
|
252
249
|
}
|
|
253
|
-
return rounded
|
|
250
|
+
return rounded
|
|
254
251
|
}
|
|
255
252
|
|
|
256
253
|
function zlibStoreZeros(length: number) {
|
|
257
|
-
const blockCount = Math.max(1, Math.ceil(length / 0xffff))
|
|
258
|
-
const output = new Uint8Array(2 + blockCount * 5 + length + 4)
|
|
259
|
-
output[0] = 0x78
|
|
260
|
-
output[1] = 0x01
|
|
254
|
+
const blockCount = Math.max(1, Math.ceil(length / 0xffff))
|
|
255
|
+
const output = new Uint8Array(2 + blockCount * 5 + length + 4)
|
|
256
|
+
output[0] = 0x78
|
|
257
|
+
output[1] = 0x01
|
|
261
258
|
|
|
262
|
-
let offset = 2
|
|
263
|
-
let remaining = length
|
|
259
|
+
let offset = 2
|
|
260
|
+
let remaining = length
|
|
264
261
|
while (remaining > 0 || offset === 2) {
|
|
265
|
-
const blockLength = Math.min(remaining, 0xffff)
|
|
266
|
-
remaining -= blockLength
|
|
267
|
-
output[offset++] = remaining === 0 ? 0x01 : 0x00
|
|
268
|
-
output[offset++] = blockLength & 0xff
|
|
269
|
-
output[offset++] = (blockLength >> 8) & 0xff
|
|
270
|
-
const inverse = 0xffff - blockLength
|
|
271
|
-
output[offset++] = inverse & 0xff
|
|
272
|
-
output[offset++] = (inverse >> 8) & 0xff
|
|
273
|
-
offset += blockLength
|
|
262
|
+
const blockLength = Math.min(remaining, 0xffff)
|
|
263
|
+
remaining -= blockLength
|
|
264
|
+
output[offset++] = remaining === 0 ? 0x01 : 0x00
|
|
265
|
+
output[offset++] = blockLength & 0xff
|
|
266
|
+
output[offset++] = (blockLength >> 8) & 0xff
|
|
267
|
+
const inverse = 0xffff - blockLength
|
|
268
|
+
output[offset++] = inverse & 0xff
|
|
269
|
+
output[offset++] = (inverse >> 8) & 0xff
|
|
270
|
+
offset += blockLength
|
|
274
271
|
}
|
|
275
272
|
|
|
276
|
-
const adler = ((length % 65521) << 16) | 1
|
|
277
|
-
new DataView(output.buffer).setUint32(output.length - 4, adler)
|
|
278
|
-
return output
|
|
273
|
+
const adler = ((length % 65521) << 16) | 1
|
|
274
|
+
new DataView(output.buffer).setUint32(output.length - 4, adler)
|
|
275
|
+
return output
|
|
279
276
|
}
|
|
280
277
|
|
|
281
278
|
function chunk(type: string, data: Uint8Array) {
|
|
282
|
-
const output = new Uint8Array(12 + data.length)
|
|
283
|
-
const view = new DataView(output.buffer)
|
|
284
|
-
view.setUint32(0, data.length)
|
|
279
|
+
const output = new Uint8Array(12 + data.length)
|
|
280
|
+
const view = new DataView(output.buffer)
|
|
281
|
+
view.setUint32(0, data.length)
|
|
285
282
|
for (let index = 0; index < 4; index += 1) {
|
|
286
|
-
output[4 + index] = type.charCodeAt(index)
|
|
283
|
+
output[4 + index] = type.charCodeAt(index)
|
|
287
284
|
}
|
|
288
|
-
output.set(data, 8)
|
|
289
|
-
view.setUint32(output.length - 4, crc32(output.subarray(4, output.length - 4)))
|
|
290
|
-
return output
|
|
285
|
+
output.set(data, 8)
|
|
286
|
+
view.setUint32(output.length - 4, crc32(output.subarray(4, output.length - 4)))
|
|
287
|
+
return output
|
|
291
288
|
}
|
|
292
289
|
|
|
293
290
|
function crc32(bytes: Uint8Array) {
|
|
294
|
-
const table = crcTable ?? createCrcTable()
|
|
295
|
-
crcTable = table
|
|
296
|
-
let crc = 0xffffffff
|
|
291
|
+
const table = crcTable ?? createCrcTable()
|
|
292
|
+
crcTable = table
|
|
293
|
+
let crc = 0xffffffff
|
|
297
294
|
for (const byte of bytes) {
|
|
298
|
-
crc = table[(crc ^ byte) & 0xff]! ^ (crc >>> 8)
|
|
295
|
+
crc = table[(crc ^ byte) & 0xff]! ^ (crc >>> 8)
|
|
299
296
|
}
|
|
300
|
-
return (crc ^ 0xffffffff) >>> 0
|
|
297
|
+
return (crc ^ 0xffffffff) >>> 0
|
|
301
298
|
}
|
|
302
299
|
|
|
303
300
|
function createCrcTable() {
|
|
304
|
-
const table = new Uint32Array(256)
|
|
301
|
+
const table = new Uint32Array(256)
|
|
305
302
|
for (let index = 0; index < table.length; index += 1) {
|
|
306
|
-
let value = index
|
|
303
|
+
let value = index
|
|
307
304
|
for (let bit = 0; bit < 8; bit += 1) {
|
|
308
|
-
value = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1
|
|
305
|
+
value = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1
|
|
309
306
|
}
|
|
310
|
-
table[index] = value >>> 0
|
|
307
|
+
table[index] = value >>> 0
|
|
311
308
|
}
|
|
312
|
-
return table
|
|
309
|
+
return table
|
|
313
310
|
}
|
|
314
311
|
|
|
315
312
|
function concat(...parts: Uint8Array[]) {
|
|
316
|
-
const length = parts.reduce((total, part) => total + part.length, 0)
|
|
317
|
-
const output = new Uint8Array(length)
|
|
318
|
-
let offset = 0
|
|
313
|
+
const length = parts.reduce((total, part) => total + part.length, 0)
|
|
314
|
+
const output = new Uint8Array(length)
|
|
315
|
+
let offset = 0
|
|
319
316
|
for (const part of parts) {
|
|
320
|
-
output.set(part, offset)
|
|
321
|
-
offset += part.length
|
|
317
|
+
output.set(part, offset)
|
|
318
|
+
offset += part.length
|
|
322
319
|
}
|
|
323
|
-
return output
|
|
320
|
+
return output
|
|
324
321
|
}
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
// its build manifests as a normalized string[]. It is read straight off the route's source files via
|
|
3
3
|
// regex - the value is a static export, so a scan matches how `maxDuration` is already collected in core.
|
|
4
4
|
|
|
5
|
-
import { readFileSync } from 'node:fs'
|
|
6
|
-
import type { RouteManifestEntry } from '../../types'
|
|
5
|
+
import { readFileSync } from 'node:fs'
|
|
6
|
+
import type { RouteManifestEntry } from '../../types'
|
|
7
7
|
|
|
8
|
-
const PREFERRED_REGION_RE =
|
|
9
|
-
/\bexport\s+const\s+preferredRegion\s*=\s*(\[[^\]]*\]|['"][^'"]*['"])/;
|
|
8
|
+
const PREFERRED_REGION_RE = /\bexport\s+const\s+preferredRegion\s*=\s*(\[[^\]]*\]|['"][^'"]*['"])/
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Resolve the route's `preferredRegion`, normalized to a string[] (Next accepts
|
|
@@ -14,26 +13,26 @@ const PREFERRED_REGION_RE =
|
|
|
14
13
|
*/
|
|
15
14
|
export function preferredRegionForRoute(route: RouteManifestEntry): string[] | undefined {
|
|
16
15
|
for (const file of routeSources(route)) {
|
|
17
|
-
let source: string
|
|
16
|
+
let source: string
|
|
18
17
|
try {
|
|
19
|
-
source = readFileSync(file, 'utf8')
|
|
18
|
+
source = readFileSync(file, 'utf8')
|
|
20
19
|
} catch {
|
|
21
|
-
continue
|
|
20
|
+
continue
|
|
22
21
|
}
|
|
23
|
-
const match = PREFERRED_REGION_RE.exec(source)
|
|
24
|
-
if (!match?.[1]) continue
|
|
25
|
-
return parsePreferredRegion(match[1])
|
|
22
|
+
const match = PREFERRED_REGION_RE.exec(source)
|
|
23
|
+
if (!match?.[1]) continue
|
|
24
|
+
return parsePreferredRegion(match[1])
|
|
26
25
|
}
|
|
27
|
-
return undefined
|
|
26
|
+
return undefined
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
function routeSources(route: RouteManifestEntry): string[] {
|
|
31
|
-
return [...new Set([route.file, ...route.sourceFiles])].filter(Boolean)
|
|
30
|
+
return [...new Set([route.file, ...route.sourceFiles])].filter(Boolean)
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
function parsePreferredRegion(literal: string): string[] {
|
|
35
34
|
if (literal.startsWith('[')) {
|
|
36
|
-
return Array.from(literal.matchAll(/['"]([^'"]*)['"]/g), m => m[1] ?? '')
|
|
35
|
+
return Array.from(literal.matchAll(/['"]([^'"]*)['"]/g), m => m[1] ?? '')
|
|
37
36
|
}
|
|
38
|
-
return [literal.slice(1, -1)]
|
|
37
|
+
return [literal.slice(1, -1)]
|
|
39
38
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Source params captured but not consumed by the destination are appended to the destination query,
|
|
9
9
|
* the same rule rewrites follow.
|
|
10
10
|
*/
|
|
11
|
-
import { loadNextConfig } from '../actions/config'
|
|
11
|
+
import { loadNextConfig } from '../actions/config'
|
|
12
12
|
import {
|
|
13
13
|
compileSource,
|
|
14
14
|
evaluateConditions,
|
|
@@ -17,51 +17,51 @@ import {
|
|
|
17
17
|
substitute,
|
|
18
18
|
type RewriteCondition,
|
|
19
19
|
type RewriteRequestContext,
|
|
20
|
-
} from './rewrites'
|
|
20
|
+
} from './rewrites'
|
|
21
21
|
|
|
22
22
|
export interface CompatRedirect {
|
|
23
|
-
regex: RegExp
|
|
24
|
-
paramNames: string[]
|
|
25
|
-
destination: string
|
|
26
|
-
has: RewriteCondition[]
|
|
27
|
-
missing: RewriteCondition[]
|
|
28
|
-
status: number
|
|
23
|
+
regex: RegExp
|
|
24
|
+
paramNames: string[]
|
|
25
|
+
destination: string
|
|
26
|
+
has: RewriteCondition[]
|
|
27
|
+
missing: RewriteCondition[]
|
|
28
|
+
status: number
|
|
29
29
|
/** `basePath: false`: the source matches the RAW path, outside the basePath. */
|
|
30
|
-
outsideBasePath: boolean
|
|
30
|
+
outsideBasePath: boolean
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
interface RedirectEntry {
|
|
34
|
-
source?: string
|
|
35
|
-
destination?: string
|
|
36
|
-
permanent?: boolean
|
|
37
|
-
statusCode?: number
|
|
38
|
-
has?: unknown
|
|
39
|
-
missing?: unknown
|
|
40
|
-
basePath?: unknown
|
|
34
|
+
source?: string
|
|
35
|
+
destination?: string
|
|
36
|
+
permanent?: boolean
|
|
37
|
+
statusCode?: number
|
|
38
|
+
has?: unknown
|
|
39
|
+
missing?: unknown
|
|
40
|
+
basePath?: unknown
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export async function loadCompatRedirects(root: string): Promise<CompatRedirect[]> {
|
|
44
|
-
const config = await loadNextConfig(root)
|
|
45
|
-
const producer = config?.redirects
|
|
46
|
-
if (typeof producer !== 'function') return []
|
|
47
|
-
let value: unknown
|
|
44
|
+
const config = await loadNextConfig(root)
|
|
45
|
+
const producer = config?.redirects
|
|
46
|
+
if (typeof producer !== 'function') return []
|
|
47
|
+
let value: unknown
|
|
48
48
|
try {
|
|
49
|
-
value = await producer()
|
|
49
|
+
value = await producer()
|
|
50
50
|
} catch {
|
|
51
|
-
return []
|
|
51
|
+
return []
|
|
52
52
|
}
|
|
53
|
-
if (!Array.isArray(value)) return []
|
|
53
|
+
if (!Array.isArray(value)) return []
|
|
54
54
|
|
|
55
|
-
const redirects: CompatRedirect[] = []
|
|
55
|
+
const redirects: CompatRedirect[] = []
|
|
56
56
|
for (const entry of value as RedirectEntry[]) {
|
|
57
|
-
if (!entry?.source || !entry.destination) continue
|
|
58
|
-
const has = parseConditions(entry.has)
|
|
59
|
-
const missing = parseConditions(entry.missing)
|
|
60
|
-
if (has === null || missing === null) continue
|
|
61
|
-
const compiled = compileSource(entry.source)
|
|
62
|
-
if (!compiled) continue
|
|
57
|
+
if (!entry?.source || !entry.destination) continue
|
|
58
|
+
const has = parseConditions(entry.has)
|
|
59
|
+
const missing = parseConditions(entry.missing)
|
|
60
|
+
if (has === null || missing === null) continue // unsupported condition shape
|
|
61
|
+
const compiled = compileSource(entry.source)
|
|
62
|
+
if (!compiled) continue
|
|
63
63
|
const status =
|
|
64
|
-
typeof entry.statusCode === 'number' ? entry.statusCode : entry.permanent ? 308 : 307
|
|
64
|
+
typeof entry.statusCode === 'number' ? entry.statusCode : entry.permanent ? 308 : 307
|
|
65
65
|
redirects.push({
|
|
66
66
|
...compiled,
|
|
67
67
|
destination: entry.destination,
|
|
@@ -69,14 +69,14 @@ export async function loadCompatRedirects(root: string): Promise<CompatRedirect[
|
|
|
69
69
|
missing,
|
|
70
70
|
status,
|
|
71
71
|
outsideBasePath: entry.basePath === false,
|
|
72
|
-
})
|
|
72
|
+
})
|
|
73
73
|
}
|
|
74
|
-
return redirects
|
|
74
|
+
return redirects
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
export interface RedirectResult {
|
|
78
|
-
location: string
|
|
79
|
-
status: number
|
|
78
|
+
location: string
|
|
79
|
+
status: number
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/** First matching redirect for the request, or undefined. */
|
|
@@ -90,42 +90,44 @@ export function resolveCompatRedirect(
|
|
|
90
90
|
headers: new Headers(),
|
|
91
91
|
cookies: {},
|
|
92
92
|
query: new URLSearchParams(),
|
|
93
|
-
}
|
|
93
|
+
}
|
|
94
94
|
for (const redirect of redirects) {
|
|
95
|
-
const match = redirect.regex.exec(pathname)
|
|
96
|
-
if (!match) continue
|
|
95
|
+
const match = redirect.regex.exec(pathname)
|
|
96
|
+
if (!match) continue
|
|
97
97
|
|
|
98
|
-
const params: Record<string, string> = {}
|
|
98
|
+
const params: Record<string, string> = {}
|
|
99
99
|
redirect.paramNames.forEach((name, index) => {
|
|
100
|
-
params[name] = match[index + 1] ?? ''
|
|
101
|
-
})
|
|
100
|
+
params[name] = match[index + 1] ?? ''
|
|
101
|
+
})
|
|
102
102
|
|
|
103
|
-
const conditionParams = evaluateConditions(redirect, context)
|
|
104
|
-
if (conditionParams === null) continue
|
|
105
|
-
Object.assign(params, conditionParams)
|
|
103
|
+
const conditionParams = evaluateConditions(redirect, context)
|
|
104
|
+
if (conditionParams === null) continue // has/missing gate failed
|
|
105
|
+
Object.assign(params, conditionParams)
|
|
106
106
|
|
|
107
|
-
const [destPath, destQuery] = splitDestination(redirect.destination)
|
|
108
|
-
const { pathname: resolvedPath, used: usedInPath } = substitute(destPath, params)
|
|
107
|
+
const [destPath, destQuery] = splitDestination(redirect.destination)
|
|
108
|
+
const { pathname: resolvedPath, used: usedInPath } = substitute(destPath, params)
|
|
109
109
|
|
|
110
|
-
const query = new URLSearchParams()
|
|
111
|
-
let usedInQuery = false
|
|
110
|
+
const query = new URLSearchParams()
|
|
111
|
+
let usedInQuery = false
|
|
112
112
|
if (destQuery) {
|
|
113
113
|
for (const [key, rawValue] of new URLSearchParams(destQuery).entries()) {
|
|
114
|
-
const { pathname: value, used } = substitute(rawValue, params)
|
|
115
|
-
if (used) usedInQuery = true
|
|
116
|
-
query.set(key, value)
|
|
114
|
+
const { pathname: value, used } = substitute(rawValue, params)
|
|
115
|
+
if (used) usedInQuery = true
|
|
116
|
+
query.set(key, value)
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
// Next appends source params not consumed by the destination to the query.
|
|
120
120
|
if (usedInPath.size === 0 && !usedInQuery) {
|
|
121
121
|
for (const [name, value] of Object.entries(params)) {
|
|
122
|
-
if (!query.has(name)) query.append(name, value)
|
|
122
|
+
if (!query.has(name)) query.append(name, value)
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
const search = query.toString()
|
|
127
|
-
const location = search
|
|
128
|
-
|
|
126
|
+
const search = query.toString()
|
|
127
|
+
const location = search
|
|
128
|
+
? `${resolvedPath}${resolvedPath.includes('?') ? '&' : '?'}${search}`
|
|
129
|
+
: resolvedPath
|
|
130
|
+
return { location, status: redirect.status }
|
|
129
131
|
}
|
|
130
|
-
return undefined
|
|
132
|
+
return undefined
|
|
131
133
|
}
|