@wular/pnext 0.0.4 → 0.0.7
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 +22 -16
- package/bin/pnext +6 -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 +87 -22
- package/reference/config.md +40 -93
- package/reference/css.md +42 -33
- package/reference/dev.md +26 -33
- package/reference/env.md +18 -20
- package/reference/getting-started.md +132 -0
- package/reference/metadata.md +31 -46
- package/reference/navigation.md +65 -105
- package/reference/performance.md +21 -142
- package/reference/rendering.md +78 -75
- package/reference/routing.md +97 -107
- package/reference/typegen.md +27 -29
- package/src/api/cache.ts +36 -37
- package/src/api/client-cache.ts +2 -2
- package/src/api/client-navigation.ts +121 -114
- package/src/api/dynamic.tsx +58 -55
- package/src/api/link.tsx +52 -53
- 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 +72 -64
- package/src/cli/dev.ts +116 -116
- package/src/cli/index.ts +94 -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 +802 -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 +32 -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 +2022 -1995
- package/src/{api → client}/router/types.ts +98 -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 -1897
- 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 +86 -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 +2087 -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} +139 -139
- package/src/resolve/engine.ts +90 -77
- package/src/resolve/imports.ts +475 -463
- package/src/resolve/scan-facts.ts +318 -296
- 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 +87 -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 +827 -815
- package/src/routing/slots.ts +164 -160
- package/src/runtime/loader.ts +954 -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 +428 -406
- package/src/styles.d.ts +9 -0
- package/src/types.ts +324 -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/reference/overview.md +0 -35
- 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
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync, type Dirent } from 'node:fs'
|
|
2
|
-
import path from 'node:path'
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, type Dirent } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Build-time app facts that let compat drop code an app cannot reach. The fact here: whether any
|
|
6
6
|
* first-party source can ask for a blurred image placeholder. `placeholder="blur"` on `next/image` (or
|
|
7
|
-
* getImageProps) is the only route into image
|
|
7
|
+
* getImageProps) is the only route into image/props' inline blur-SVG builder, which otherwise ships on
|
|
8
8
|
* every page that uses next/image.
|
|
9
9
|
*
|
|
10
10
|
* The gate is one-sided by construction: every unreadable case answers "yes". A wrong "no" would render
|
|
@@ -12,29 +12,29 @@ import path from 'node:path';
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/** Skipped wholesale: not first-party source, or build output. */
|
|
15
|
-
const skipDirs = new Set(['node_modules', 'dist', 'build', 'out', 'coverage', 'public', 'target'])
|
|
16
|
-
const sourceFile = /\.(?:[cm]?[jt]sx?|mdx)
|
|
15
|
+
const skipDirs = new Set(['node_modules', 'dist', 'build', 'out', 'coverage', 'public', 'target'])
|
|
16
|
+
const sourceFile = /\.(?:[cm]?[jt]sx?|mdx)$/
|
|
17
17
|
|
|
18
18
|
/** Only these APIs reach the branch, so only files naming one can matter. */
|
|
19
|
-
const imageApi = /next\/(?:legacy\/)?image|getImageProps|unstable_getImgProps
|
|
19
|
+
const imageApi = /next\/(?:legacy\/)?image|getImageProps|unstable_getImgProps/
|
|
20
20
|
|
|
21
21
|
/** A literal `placeholder="blur"`, in JSX-prop or object-property form. */
|
|
22
|
-
const literalBlur = /\bplaceholder\s*[=:]\s*\{?\s*['"`]blur['"`]
|
|
22
|
+
const literalBlur = /\bplaceholder\s*[=:]\s*\{?\s*['"`]blur['"`]/
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* A `placeholder` whose value is not a string literal — `placeholder={value}`,
|
|
26
26
|
* `placeholder: value`. Unreadable from source text, so it counts as usage.
|
|
27
27
|
*/
|
|
28
|
-
const opaquePlaceholder = /\bplaceholder\s*[=:]\s*(?!\{?\s*['"`])
|
|
28
|
+
const opaquePlaceholder = /\bplaceholder\s*[=:]\s*(?!\{?\s*['"`])/
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Props reaching the image from somewhere this scan cannot see: a spread into
|
|
32
32
|
* any element in an image-using file, or a re-export that lets another file
|
|
33
33
|
* render `<Image>` without ever naming `next/image`.
|
|
34
34
|
*/
|
|
35
|
-
const opaqueProps = /\{\s*\.\.\.|export\s[^;]*from\s*['"]next\/(?:legacy\/)?image
|
|
35
|
+
const opaqueProps = /\{\s*\.\.\.|export\s[^;]*from\s*['"]next\/(?:legacy\/)?image/
|
|
36
36
|
|
|
37
|
-
const cache = new Map<string, boolean>()
|
|
37
|
+
const cache = new Map<string, boolean>()
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Roots to scan: the app, plus the workspace's own packages when the app sits in one - a linked
|
|
@@ -42,61 +42,92 @@ const cache = new Map<string, boolean>();
|
|
|
42
42
|
* there must be seen.
|
|
43
43
|
*/
|
|
44
44
|
function scanRoots(root: string) {
|
|
45
|
-
const roots = [root]
|
|
45
|
+
const roots = [root]
|
|
46
46
|
for (let dir = path.dirname(root); dir !== path.dirname(dir); dir = path.dirname(dir)) {
|
|
47
|
-
if (
|
|
48
|
-
|
|
47
|
+
if (
|
|
48
|
+
!existsSync(path.join(dir, 'pnpm-workspace.yaml')) &&
|
|
49
|
+
!existsSync(path.join(dir, 'lerna.json'))
|
|
50
|
+
) {
|
|
51
|
+
continue
|
|
49
52
|
}
|
|
50
|
-
const packages = path.join(dir, 'packages')
|
|
51
|
-
if (existsSync(packages)) roots.push(packages)
|
|
52
|
-
break
|
|
53
|
+
const packages = path.join(dir, 'packages')
|
|
54
|
+
if (existsSync(packages)) roots.push(packages)
|
|
55
|
+
break
|
|
53
56
|
}
|
|
54
|
-
return roots
|
|
57
|
+
return roots
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
export function appUsesBlurPlaceholder(root: string) {
|
|
58
|
-
const cached = cache.get(root)
|
|
59
|
-
if (cached !== undefined) return cached
|
|
60
|
-
const used = scanFirstPartySource(root, blurSource)
|
|
61
|
-
cache.set(root, used)
|
|
62
|
-
return used
|
|
61
|
+
const cached = cache.get(root)
|
|
62
|
+
if (cached !== undefined) return cached
|
|
63
|
+
const used = scanFirstPartySource(root, blurSource)
|
|
64
|
+
cache.set(root, used)
|
|
65
|
+
return used
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
function blurSource(source: string): boolean {
|
|
66
69
|
// The common case is one `test` and out: almost no file in an app names an
|
|
67
70
|
// image API, and only those that do pay for the rest.
|
|
68
|
-
if (!imageApi.test(source)) return false
|
|
69
|
-
return literalBlur.test(source) || opaquePlaceholder.test(source) || opaqueProps.test(source)
|
|
71
|
+
if (!imageApi.test(source)) return false
|
|
72
|
+
return literalBlur.test(source) || opaquePlaceholder.test(source) || opaqueProps.test(source)
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
/** Walk the app's first-party source (plus workspace packages) until `match`. */
|
|
73
76
|
export function scanFirstPartySource(root: string, match: (source: string) => boolean): boolean {
|
|
74
|
-
return scanRoots(root).some(dir => scan(dir, match))
|
|
77
|
+
return scanRoots(root).some(dir => scan(dir, match))
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
function scan(dir: string, match: (source: string) => boolean): boolean {
|
|
78
|
-
let entries: Dirent[]
|
|
81
|
+
let entries: Dirent[]
|
|
79
82
|
try {
|
|
80
|
-
entries = readdirSync(dir, { withFileTypes: true })
|
|
83
|
+
entries = readdirSync(dir, { withFileTypes: true })
|
|
81
84
|
} catch {
|
|
82
|
-
return false
|
|
85
|
+
return false
|
|
83
86
|
}
|
|
84
87
|
for (const entry of entries) {
|
|
85
|
-
if (entry.name.startsWith('.')) continue
|
|
86
|
-
const file = path.join(dir, entry.name)
|
|
88
|
+
if (entry.name.startsWith('.')) continue
|
|
89
|
+
const file = path.join(dir, entry.name)
|
|
87
90
|
if (entry.isDirectory()) {
|
|
88
|
-
if (skipDirs.has(entry.name)) continue
|
|
89
|
-
if (scan(file, match)) return true
|
|
90
|
-
continue
|
|
91
|
+
if (skipDirs.has(entry.name)) continue
|
|
92
|
+
if (scan(file, match)) return true
|
|
93
|
+
continue
|
|
91
94
|
}
|
|
92
|
-
if (!sourceFile.test(entry.name)) continue
|
|
93
|
-
let source: string
|
|
95
|
+
if (!sourceFile.test(entry.name)) continue
|
|
96
|
+
let source: string
|
|
94
97
|
try {
|
|
95
|
-
source = readFileSync(file, 'utf8')
|
|
98
|
+
source = readFileSync(file, 'utf8')
|
|
96
99
|
} catch {
|
|
97
|
-
continue
|
|
100
|
+
continue
|
|
98
101
|
}
|
|
99
|
-
if (match(source)) return true
|
|
102
|
+
if (match(source)) return true
|
|
100
103
|
}
|
|
101
|
-
return false
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Build-time app fact: can any first-party source render `<Link legacyBehavior>`? The legacy child path
|
|
109
|
+
* (child validation, the deprecation notice, the server/lazy child diagnostics, cloneElement) is a
|
|
110
|
+
* meaningful slice of `next/link`, and next/link is initial on every page that links anywhere.
|
|
111
|
+
*
|
|
112
|
+
* One-sided like the blur gate: every unreadable case answers "yes". A wrong "no" renders the legacy
|
|
113
|
+
* child shape on the server and a plain anchor on the client - a hydration mismatch. The scan sees the
|
|
114
|
+
* app and its workspace packages, not registry dependencies.
|
|
115
|
+
*/
|
|
116
|
+
const legacyProp = /\blegacyBehavior\b/
|
|
117
|
+
|
|
118
|
+
/** A Link re-exported from here can be rendered by a file this scan can't tie
|
|
119
|
+
* back to `next/link`, so treat the re-export itself as usage. */
|
|
120
|
+
const reexportedLink = /export\s[^;]*from\s*['"]next\/link['"]/
|
|
121
|
+
|
|
122
|
+
const legacyLinkCache = new Map<string, boolean>()
|
|
123
|
+
|
|
124
|
+
export function appUsesLegacyLink(root: string) {
|
|
125
|
+
const cached = legacyLinkCache.get(root)
|
|
126
|
+
if (cached !== undefined) return cached
|
|
127
|
+
const used = scanFirstPartySource(
|
|
128
|
+
root,
|
|
129
|
+
source => legacyProp.test(source) || reexportedLink.test(source),
|
|
130
|
+
)
|
|
131
|
+
legacyLinkCache.set(root, used)
|
|
132
|
+
return used
|
|
102
133
|
}
|
|
@@ -7,23 +7,23 @@
|
|
|
7
7
|
// (request/context, ppr, cache/revalidate) into the browser bundle.
|
|
8
8
|
|
|
9
9
|
/** Native URLPattern when the runtime provides one (Bun/Node 23+/browsers). */
|
|
10
|
-
export const URLPattern = (globalThis as { URLPattern?: unknown }).URLPattern
|
|
10
|
+
export const URLPattern = (globalThis as { URLPattern?: unknown }).URLPattern
|
|
11
11
|
|
|
12
12
|
export interface UserAgent {
|
|
13
|
-
ua: string
|
|
14
|
-
browser: { name?: string; version?: string }
|
|
15
|
-
os: { name?: string; version?: string }
|
|
16
|
-
device: { type?: string; vendor?: string; model?: string }
|
|
17
|
-
engine: { name?: string; version?: string }
|
|
18
|
-
cpu: { architecture?: string }
|
|
19
|
-
isBot: boolean
|
|
13
|
+
ua: string
|
|
14
|
+
browser: { name?: string; version?: string }
|
|
15
|
+
os: { name?: string; version?: string }
|
|
16
|
+
device: { type?: string; vendor?: string; model?: string }
|
|
17
|
+
engine: { name?: string; version?: string }
|
|
18
|
+
cpu: { architecture?: string }
|
|
19
|
+
isBot: boolean
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const botPattern =
|
|
23
|
-
/bot|crawler|spider|crawling|slurp|bingpreview|facebookexternalhit|mediapartners|headless/i
|
|
23
|
+
/bot|crawler|spider|crawling|slurp|bingpreview|facebookexternalhit|mediapartners|headless/i
|
|
24
24
|
|
|
25
25
|
export function userAgentFromString(input: string | undefined): UserAgent {
|
|
26
|
-
const ua = input ?? ''
|
|
26
|
+
const ua = input ?? ''
|
|
27
27
|
return {
|
|
28
28
|
ua,
|
|
29
29
|
browser: parseBrowser(ua),
|
|
@@ -32,69 +32,73 @@ export function userAgentFromString(input: string | undefined): UserAgent {
|
|
|
32
32
|
engine: parseEngine(ua),
|
|
33
33
|
cpu: parseCpu(ua),
|
|
34
34
|
isBot: botPattern.test(ua),
|
|
35
|
-
}
|
|
35
|
+
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export function userAgent({ headers }: { headers: Headers }): UserAgent {
|
|
39
|
-
return userAgentFromString(headers.get('user-agent') ?? undefined)
|
|
39
|
+
return userAgentFromString(headers.get('user-agent') ?? undefined)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function parseBrowser(ua: string): UserAgent['browser'] {
|
|
43
|
-
const edge = ua.match(/Edg(?:e|A|iOS)?\/([\d.]+)/)
|
|
44
|
-
if (edge) return { name: 'Edge', version: edge[1] }
|
|
45
|
-
const samsung = ua.match(/SamsungBrowser\/([\d.]+)/)
|
|
46
|
-
if (samsung) return { name: 'Samsung Internet', version: samsung[1] }
|
|
47
|
-
const chrome = ua.match(/(?:Chrome|CriOS)\/([\d.]+)/)
|
|
48
|
-
if (chrome) return { name: 'Chrome', version: chrome[1] }
|
|
49
|
-
const firefox = ua.match(/(?:Firefox|FxiOS)\/([\d.]+)/)
|
|
50
|
-
if (firefox) return { name: 'Firefox', version: firefox[1] }
|
|
51
|
-
if (ua.includes('Safari')) return { name: 'Safari', version: ua.match(/Version\/([\d.]+)/)?.[1] }
|
|
52
|
-
return {}
|
|
43
|
+
const edge = ua.match(/Edg(?:e|A|iOS)?\/([\d.]+)/)
|
|
44
|
+
if (edge) return { name: 'Edge', version: edge[1] }
|
|
45
|
+
const samsung = ua.match(/SamsungBrowser\/([\d.]+)/)
|
|
46
|
+
if (samsung) return { name: 'Samsung Internet', version: samsung[1] }
|
|
47
|
+
const chrome = ua.match(/(?:Chrome|CriOS)\/([\d.]+)/)
|
|
48
|
+
if (chrome) return { name: 'Chrome', version: chrome[1] }
|
|
49
|
+
const firefox = ua.match(/(?:Firefox|FxiOS)\/([\d.]+)/)
|
|
50
|
+
if (firefox) return { name: 'Firefox', version: firefox[1] }
|
|
51
|
+
if (ua.includes('Safari')) return { name: 'Safari', version: ua.match(/Version\/([\d.]+)/)?.[1] }
|
|
52
|
+
return {}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
function parseEngine(ua: string): UserAgent['engine'] {
|
|
56
|
-
const gecko = ua.match(/rv:([\d.]+)\).*Gecko/)
|
|
57
|
-
if (gecko) return { name: 'Gecko', version: gecko[1] }
|
|
58
|
-
const webkit = ua.match(/AppleWebKit\/([\d.]+)/)
|
|
56
|
+
const gecko = ua.match(/rv:([\d.]+)\).*Gecko/)
|
|
57
|
+
if (gecko) return { name: 'Gecko', version: gecko[1] }
|
|
58
|
+
const webkit = ua.match(/AppleWebKit\/([\d.]+)/)
|
|
59
59
|
if (webkit) {
|
|
60
|
-
const chrome = ua.match(/(?:Chrome|CriOS|Edg(?:e|A|iOS)?|OPR)\/([\d.]+)/)
|
|
61
|
-
if (chrome) return { name: 'Blink', version: chrome[1] }
|
|
62
|
-
return { name: 'WebKit', version: webkit[1] }
|
|
60
|
+
const chrome = ua.match(/(?:Chrome|CriOS|Edg(?:e|A|iOS)?|OPR)\/([\d.]+)/)
|
|
61
|
+
if (chrome) return { name: 'Blink', version: chrome[1] }
|
|
62
|
+
return { name: 'WebKit', version: webkit[1] }
|
|
63
63
|
}
|
|
64
|
-
return {}
|
|
64
|
+
return {}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function parseCpu(ua: string): UserAgent['cpu'] {
|
|
68
|
-
if (/arm64|aarch64/i.test(ua)) return { architecture: 'arm64' }
|
|
69
|
-
if (/\barm\b/i.test(ua)) return { architecture: 'arm' }
|
|
70
|
-
if (/x86_64|x64|Win64|WOW64/i.test(ua)) return { architecture: 'amd64' }
|
|
71
|
-
if (/i686|\bx86\b/i.test(ua)) return { architecture: 'ia32' }
|
|
72
|
-
return {}
|
|
68
|
+
if (/arm64|aarch64/i.test(ua)) return { architecture: 'arm64' }
|
|
69
|
+
if (/\barm\b/i.test(ua)) return { architecture: 'arm' }
|
|
70
|
+
if (/x86_64|x64|Win64|WOW64/i.test(ua)) return { architecture: 'amd64' }
|
|
71
|
+
if (/i686|\bx86\b/i.test(ua)) return { architecture: 'ia32' }
|
|
72
|
+
return {}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
function parseOs(ua: string): UserAgent['os'] {
|
|
76
|
-
if (ua.includes('Windows'))
|
|
77
|
-
|
|
76
|
+
if (ua.includes('Windows'))
|
|
77
|
+
return { name: 'Windows', version: ua.match(/Windows NT ([\d.]+)/)?.[1] }
|
|
78
|
+
if (ua.includes('Android')) return { name: 'Android', version: ua.match(/Android ([\d.]+)/)?.[1] }
|
|
78
79
|
if (/iPhone|iPad|iPod/.test(ua)) {
|
|
79
|
-
return { name: 'iOS', version: ua.match(/OS (\d+(?:[_.]\d+)*)/)?.[1]?.replace(/_/g, '.') }
|
|
80
|
+
return { name: 'iOS', version: ua.match(/OS (\d+(?:[_.]\d+)*)/)?.[1]?.replace(/_/g, '.') }
|
|
80
81
|
}
|
|
81
82
|
if (ua.includes('Mac OS X')) {
|
|
82
|
-
return {
|
|
83
|
+
return {
|
|
84
|
+
name: 'Mac OS',
|
|
85
|
+
version: ua.match(/Mac OS X (\d+(?:[_.]\d+)*)/)?.[1]?.replace(/_/g, '.'),
|
|
86
|
+
}
|
|
83
87
|
}
|
|
84
|
-
if (ua.includes('Linux')) return { name: 'Linux' }
|
|
85
|
-
return {}
|
|
88
|
+
if (ua.includes('Linux')) return { name: 'Linux' }
|
|
89
|
+
return {}
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
function parseDevice(ua: string): UserAgent['device'] {
|
|
89
|
-
if (ua.includes('iPhone')) return { type: 'mobile', vendor: 'Apple', model: 'iPhone' }
|
|
90
|
-
if (ua.includes('iPad')) return { type: 'tablet', vendor: 'Apple', model: 'iPad' }
|
|
93
|
+
if (ua.includes('iPhone')) return { type: 'mobile', vendor: 'Apple', model: 'iPhone' }
|
|
94
|
+
if (ua.includes('iPad')) return { type: 'tablet', vendor: 'Apple', model: 'iPad' }
|
|
91
95
|
if (ua.includes('Android')) {
|
|
92
|
-
const type = ua.includes('Mobile') ? 'mobile' : 'tablet'
|
|
93
|
-
const samsung = ua.match(/\b(SM-[\w]+)\b/)
|
|
94
|
-
if (samsung) return { type, vendor: 'Samsung', model: samsung[1] }
|
|
95
|
-
const pixel = ua.match(/;\s*(Pixel[\w ]*?)\s*(?:Build|\))/)
|
|
96
|
-
if (pixel) return { type, vendor: 'Google', model: pixel[1] }
|
|
97
|
-
return { type }
|
|
96
|
+
const type = ua.includes('Mobile') ? 'mobile' : 'tablet'
|
|
97
|
+
const samsung = ua.match(/\b(SM-[\w]+)\b/)
|
|
98
|
+
if (samsung) return { type, vendor: 'Samsung', model: samsung[1] }
|
|
99
|
+
const pixel = ua.match(/;\s*(Pixel[\w ]*?)\s*(?:Build|\))/)
|
|
100
|
+
if (pixel) return { type, vendor: 'Google', model: pixel[1] }
|
|
101
|
+
return { type }
|
|
98
102
|
}
|
|
99
|
-
return ua.includes('Mobile') ? { type: 'mobile' } : {}
|
|
103
|
+
return ua.includes('Mobile') ? { type: 'mobile' } : {}
|
|
100
104
|
}
|
|
@@ -1,30 +1,28 @@
|
|
|
1
|
-
'use client'
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
3
|
// `next/web-vitals` compat surface. Next measures Core Web Vitals (plus its own Next.js-* custom
|
|
4
4
|
// metrics) and invokes the reporter callback per registered hook instance. The Core Web Vitals are
|
|
5
5
|
// backed by the `web-vitals` package - this is browser-bundled client code, so the import stays a plain
|
|
6
6
|
// static import for the bundler to resolve. Next's own custom metrics are not emitted here.
|
|
7
|
-
import { useEffect, useRef } from 'preact/hooks'
|
|
8
|
-
import * as webVitals from 'web-vitals'
|
|
9
|
-
import type { Metric } from 'web-vitals'
|
|
7
|
+
import { useEffect, useRef } from 'preact/hooks'
|
|
8
|
+
import * as webVitals from 'web-vitals'
|
|
9
|
+
import type { Metric } from 'web-vitals'
|
|
10
10
|
|
|
11
11
|
export interface NextWebVitalsMetric {
|
|
12
|
-
id: string
|
|
13
|
-
name: string
|
|
14
|
-
startTime: number
|
|
15
|
-
value: number
|
|
16
|
-
label: 'web-vital' | 'custom'
|
|
12
|
+
id: string
|
|
13
|
+
name: string
|
|
14
|
+
startTime: number
|
|
15
|
+
value: number
|
|
16
|
+
label: 'web-vital' | 'custom'
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export function useReportWebVitals(
|
|
20
|
-
reportWebVitalsFn: (metric: NextWebVitalsMetric) => void,
|
|
21
|
-
): void {
|
|
19
|
+
export function useReportWebVitals(reportWebVitalsFn: (metric: NextWebVitalsMetric) => void): void {
|
|
22
20
|
// Route metrics through a ref so the latest reporter identity receives them
|
|
23
21
|
// without resubscribing to web-vitals on every render.
|
|
24
|
-
const reporterRef = useRef(reportWebVitalsFn)
|
|
22
|
+
const reporterRef = useRef(reportWebVitalsFn)
|
|
25
23
|
useEffect(() => {
|
|
26
|
-
reporterRef.current = reportWebVitalsFn
|
|
27
|
-
}, [reportWebVitalsFn])
|
|
24
|
+
reporterRef.current = reportWebVitalsFn
|
|
25
|
+
}, [reportWebVitalsFn])
|
|
28
26
|
|
|
29
27
|
useEffect(() => {
|
|
30
28
|
const report = (metric: Metric): void => {
|
|
@@ -34,23 +32,23 @@ export function useReportWebVitals(
|
|
|
34
32
|
startTime: metric.entries[0]?.startTime ?? 0,
|
|
35
33
|
value: metric.value,
|
|
36
34
|
label: 'web-vital',
|
|
37
|
-
})
|
|
38
|
-
}
|
|
35
|
+
})
|
|
36
|
+
}
|
|
39
37
|
|
|
40
|
-
webVitals.onTTFB(report)
|
|
41
|
-
webVitals.onFCP(report)
|
|
42
|
-
webVitals.onLCP(report)
|
|
38
|
+
webVitals.onTTFB(report)
|
|
39
|
+
webVitals.onFCP(report)
|
|
40
|
+
webVitals.onLCP(report)
|
|
43
41
|
// CLS and INP accumulate over the page lifetime and, by default, only settle
|
|
44
42
|
// when the page is hidden. A page that is never hidden (or one torn down by a
|
|
45
43
|
// navigation before the visibilitychange flush lands) therefore never reports
|
|
46
44
|
// them at all. Both metrics are documented as multi-report: `reportAllChanges`
|
|
47
45
|
// emits each new value as it is determined instead of a single deferred one,
|
|
48
46
|
// so opting in makes delivery deterministic without dropping any information.
|
|
49
|
-
webVitals.onCLS(report, { reportAllChanges: true })
|
|
50
|
-
webVitals.onINP(report, { reportAllChanges: true })
|
|
47
|
+
webVitals.onCLS(report, { reportAllChanges: true })
|
|
48
|
+
webVitals.onINP(report, { reportAllChanges: true })
|
|
51
49
|
// `onFID` is deprecated (removed in newer majors); subscribe only if present.
|
|
52
50
|
if (typeof webVitals.onFID === 'function') {
|
|
53
|
-
webVitals.onFID(report)
|
|
51
|
+
webVitals.onFID(report)
|
|
54
52
|
}
|
|
55
|
-
}, [])
|
|
53
|
+
}, [])
|
|
56
54
|
}
|
package/src/compat/otel/api.ts
CHANGED
|
@@ -10,31 +10,31 @@
|
|
|
10
10
|
// share ONE api singleton - the same module instance the app's provider.register() mutated. A bare
|
|
11
11
|
// import would resolve against pnext's location and never see the registered provider.
|
|
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
|
// Structural subset of @opentelemetry/api we use. Kept local so pnext carries no
|
|
17
17
|
// type dependency on the package (it may be absent).
|
|
18
18
|
export interface OtelSpanContext {
|
|
19
|
-
traceId: string
|
|
20
|
-
spanId: string
|
|
21
|
-
traceFlags: number
|
|
19
|
+
traceId: string
|
|
20
|
+
spanId: string
|
|
21
|
+
traceFlags: number
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export interface OtelSpan {
|
|
25
|
-
setAttribute(key: string, value: unknown): OtelSpan
|
|
26
|
-
setAttributes(attrs: Record<string, unknown>): OtelSpan
|
|
27
|
-
setStatus(status: { code: number; message?: string }): OtelSpan
|
|
28
|
-
recordException(error: unknown): void
|
|
29
|
-
updateName(name: string): OtelSpan
|
|
30
|
-
end(endTime?: number): void
|
|
31
|
-
spanContext(): OtelSpanContext
|
|
32
|
-
isRecording(): boolean
|
|
25
|
+
setAttribute(key: string, value: unknown): OtelSpan
|
|
26
|
+
setAttributes(attrs: Record<string, unknown>): OtelSpan
|
|
27
|
+
setStatus(status: { code: number; message?: string }): OtelSpan
|
|
28
|
+
recordException(error: unknown): void
|
|
29
|
+
updateName(name: string): OtelSpan
|
|
30
|
+
end(endTime?: number): void
|
|
31
|
+
spanContext(): OtelSpanContext
|
|
32
|
+
isRecording(): boolean
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export interface OtelContext {
|
|
36
36
|
// opaque
|
|
37
|
-
readonly __brand?: 'otel-context'
|
|
37
|
+
readonly __brand?: 'otel-context'
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export interface OtelTracer {
|
|
@@ -42,54 +42,54 @@ export interface OtelTracer {
|
|
|
42
42
|
name: string,
|
|
43
43
|
options?: { kind?: number; attributes?: Record<string, unknown>; startTime?: number },
|
|
44
44
|
context?: OtelContext,
|
|
45
|
-
): OtelSpan
|
|
45
|
+
): OtelSpan
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export interface OtelApi {
|
|
49
49
|
trace: {
|
|
50
|
-
getTracer(name: string, version?: string): OtelTracer
|
|
51
|
-
getSpan(context: OtelContext): OtelSpan | undefined
|
|
52
|
-
getSpanContext(context: OtelContext): OtelSpanContext | undefined
|
|
53
|
-
setSpan(context: OtelContext, span: OtelSpan): OtelContext
|
|
54
|
-
setSpanContext(context: OtelContext, sc: OtelSpanContext): OtelContext
|
|
55
|
-
}
|
|
50
|
+
getTracer(name: string, version?: string): OtelTracer
|
|
51
|
+
getSpan(context: OtelContext): OtelSpan | undefined
|
|
52
|
+
getSpanContext(context: OtelContext): OtelSpanContext | undefined
|
|
53
|
+
setSpan(context: OtelContext, span: OtelSpan): OtelContext
|
|
54
|
+
setSpanContext(context: OtelContext, sc: OtelSpanContext): OtelContext
|
|
55
|
+
}
|
|
56
56
|
context: {
|
|
57
|
-
active(): OtelContext
|
|
58
|
-
with<T>(context: OtelContext, fn: () => T): T
|
|
59
|
-
}
|
|
57
|
+
active(): OtelContext
|
|
58
|
+
with<T>(context: OtelContext, fn: () => T): T
|
|
59
|
+
}
|
|
60
60
|
propagation: {
|
|
61
|
-
extract(context: OtelContext, carrier: unknown, getter?: unknown): OtelContext
|
|
62
|
-
inject(context: OtelContext, carrier: unknown, setter?: unknown): void
|
|
63
|
-
fields(): string[]
|
|
64
|
-
}
|
|
65
|
-
ROOT_CONTEXT: OtelContext
|
|
66
|
-
SpanKind: { INTERNAL: number; SERVER: number; CLIENT: number; PRODUCER: number; CONSUMER: number }
|
|
67
|
-
SpanStatusCode: { UNSET: number; OK: number; ERROR: number }
|
|
68
|
-
TraceFlags: { NONE: number; SAMPLED: number }
|
|
61
|
+
extract(context: OtelContext, carrier: unknown, getter?: unknown): OtelContext
|
|
62
|
+
inject(context: OtelContext, carrier: unknown, setter?: unknown): void
|
|
63
|
+
fields(): string[]
|
|
64
|
+
}
|
|
65
|
+
ROOT_CONTEXT: OtelContext
|
|
66
|
+
SpanKind: { INTERNAL: number; SERVER: number; CLIENT: number; PRODUCER: number; CONSUMER: number }
|
|
67
|
+
SpanStatusCode: { UNSET: number; OK: number; ERROR: number }
|
|
68
|
+
TraceFlags: { NONE: number; SAMPLED: number }
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
let resolved: OtelApi | null | undefined
|
|
71
|
+
let resolved: OtelApi | null | undefined
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
74
|
* Load `@opentelemetry/api` from the app root (once). Returns null when the
|
|
75
75
|
* package is not installed — callers then no-op. Never throws.
|
|
76
76
|
*/
|
|
77
77
|
export function getOtelApi(appRoot: string): OtelApi | null {
|
|
78
|
-
if (resolved !== undefined) return resolved
|
|
79
|
-
resolved = null
|
|
78
|
+
if (resolved !== undefined) return resolved
|
|
79
|
+
resolved = null
|
|
80
80
|
try {
|
|
81
81
|
// Resolve against a file inside the app root so Node walks the app's
|
|
82
82
|
// node_modules (where instrumentation.ts's @opentelemetry/api lives).
|
|
83
|
-
const req = createRequire(path.join(appRoot, 'package.json'))
|
|
84
|
-
const api = req('@opentelemetry/api') as OtelApi
|
|
85
|
-
if (api?.trace && api.context) resolved = api
|
|
83
|
+
const req = createRequire(path.join(appRoot, 'package.json'))
|
|
84
|
+
const api = req('@opentelemetry/api') as OtelApi
|
|
85
|
+
if (api?.trace && api.context) resolved = api
|
|
86
86
|
} catch {
|
|
87
|
-
resolved = null
|
|
87
|
+
resolved = null
|
|
88
88
|
}
|
|
89
|
-
return resolved
|
|
89
|
+
return resolved
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/** Test/bootstrap reset. */
|
|
93
93
|
export function resetOtelApi(): void {
|
|
94
|
-
resolved = undefined
|
|
94
|
+
resolved = undefined
|
|
95
95
|
}
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
// nothing, while dev emits for static too. The tags are produced once per document render, so soft navs
|
|
11
11
|
// never mutate them.
|
|
12
12
|
|
|
13
|
-
import { getNextConfig } from '../next/config-loader'
|
|
14
|
-
import { activeOtelContext, carrierSetter } from './tracer'
|
|
13
|
+
import { getNextConfig } from '../next/config-loader'
|
|
14
|
+
import { activeOtelContext, carrierSetter } from './tracer'
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Return the clientTraceMetadata `<meta>` tag HTML for the current render, or ''
|
|
@@ -22,44 +22,42 @@ import { activeOtelContext, carrierSetter } from './tracer';
|
|
|
22
22
|
* defensively.
|
|
23
23
|
*/
|
|
24
24
|
export function clientTraceMetadataTags(input: {
|
|
25
|
-
dynamic: boolean
|
|
26
|
-
isNextStart: boolean
|
|
27
|
-
rsc: boolean
|
|
25
|
+
dynamic: boolean
|
|
26
|
+
isNextStart: boolean
|
|
27
|
+
rsc: boolean
|
|
28
28
|
}): string {
|
|
29
|
-
const keys = configuredKeys()
|
|
30
|
-
if (keys.length === 0) return ''
|
|
31
|
-
if (input.rsc) return ''
|
|
29
|
+
const keys = configuredKeys()
|
|
30
|
+
if (keys.length === 0) return ''
|
|
31
|
+
if (input.rsc) return ''
|
|
32
32
|
// Prod + static → no injection. Dev injects for static too (handled by the
|
|
33
33
|
// caller passing dynamic=true in dev, since a dev render is never prebuilt).
|
|
34
|
-
if (input.isNextStart && !input.dynamic) return ''
|
|
34
|
+
if (input.isNextStart && !input.dynamic) return ''
|
|
35
35
|
|
|
36
|
-
const active = activeOtelContext()
|
|
37
|
-
if (!active) return ''
|
|
38
|
-
const { api, context } = active
|
|
36
|
+
const active = activeOtelContext()
|
|
37
|
+
if (!active) return ''
|
|
38
|
+
const { api, context } = active
|
|
39
39
|
|
|
40
|
-
const carrier: Record<string, string> = {}
|
|
40
|
+
const carrier: Record<string, string> = {}
|
|
41
41
|
try {
|
|
42
|
-
api.propagation.inject(context, carrier, carrierSetter)
|
|
42
|
+
api.propagation.inject(context, carrier, carrierSetter)
|
|
43
43
|
} catch {
|
|
44
|
-
return ''
|
|
44
|
+
return ''
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
const tags: string[] = []
|
|
47
|
+
const tags: string[] = []
|
|
48
48
|
for (const key of keys) {
|
|
49
|
-
const value = carrier[key]
|
|
50
|
-
if (value === undefined || value === null) continue
|
|
51
|
-
tags.push(`<meta name="${escapeAttr(key)}" content="${escapeAttr(String(value))}"/>`)
|
|
49
|
+
const value = carrier[key]
|
|
50
|
+
if (value === undefined || value === null) continue
|
|
51
|
+
tags.push(`<meta name="${escapeAttr(key)}" content="${escapeAttr(String(value))}"/>`)
|
|
52
52
|
}
|
|
53
|
-
return tags.join('')
|
|
53
|
+
return tags.join('')
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function configuredKeys(): string[] {
|
|
57
|
-
const experimental = getNextConfig().experimental as
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (!Array.isArray(list)) return [];
|
|
62
|
-
return list.filter((k): k is string => typeof k === 'string');
|
|
57
|
+
const experimental = getNextConfig().experimental as { clientTraceMetadata?: unknown } | undefined
|
|
58
|
+
const list = experimental?.clientTraceMetadata
|
|
59
|
+
if (!Array.isArray(list)) return []
|
|
60
|
+
return list.filter((k): k is string => typeof k === 'string')
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
function escapeAttr(value: string): string {
|
|
@@ -67,5 +65,5 @@ function escapeAttr(value: string): string {
|
|
|
67
65
|
.replace(/&/g, '&')
|
|
68
66
|
.replace(/"/g, '"')
|
|
69
67
|
.replace(/</g, '<')
|
|
70
|
-
.replace(/>/g, '>')
|
|
68
|
+
.replace(/>/g, '>')
|
|
71
69
|
}
|