Skip to content

perf: keep valibot out of production client bundles - #869

Merged
harlan-zw merged 2 commits into
mainfrom
perf/valibot-out-of-client
Aug 18, 2026
Merged

perf: keep valibot out of production client bundles#869
harlan-zw merged 2 commits into
mainfrom
perf/valibot-out-of-client

Conversation

@harlan-zw

Copy link
Copy Markdown
Collaborator

📚 Description

Every registry schema is meant to be dev only. schema: import.meta.dev ? XOptions : undefined is the pattern, and it treeshakes correctly. Two call sites broke it, so valibot's schema runtime shipped to the browser on every page: a 12 KB chunk, 3.7 KB gzipped, on the basic fixture.

The first is the one I did not expect. speedcurve.ts built its LUX passthrough list at module scope:

constLUX_USER_CONFIG_KEYS=Object.keys(SpeedCurveOptions.entries).filter(...)

That reads the schema at runtime, so the schema survives the dev guard and drags valibot in with it. The keys are listed literally now. Deriving them was there to stop the list drifting from the schema, so the type checker does that job instead:

typeForwardedKey=Exclude<keyofInferInput<typeofSpeedCurveOptions>,ComposableOnlyKey>constLUX_USER_CONFIG_KEYS=[...]asconstsatisfiesreadonly(ForwardedKey&keyofUserConfig)[]type_AllForwardedKeysListed=AssertNever<Exclude<ForwardedKey,typeofLUX_USER_CONFIG_KEYS[number]>>

Add a key to SpeedCurveOptions and forget the list, and typecheck fails.

The second is _gcm-consent.ts, which ran safeParse against a strict rebuild of gcmConsentState on every consent.default() and consent.update() call. That is real runtime validation, so no guard would have helped. It is a hand written check now, same warnings, plus the same style of compile time guard against ConsentState growing a key.

Client bundle on test/fixtures/basic goes 576 KB to 568 KB, and the valibot chunk is gone.

I only measured the basic fixture. A build using SpeedCurve or GCM consent heavily should improve by at least as much, but I have not measured one.

🤖 AI disclosure: Harlan Agent Kit modified this description. My AI open-source policy.

Two runtime call sites forced the valibot schema runtime into every production
client bundle, worth 12 KB raw (3.7 KB gzip) on the basic fixture.
- `speedcurve.ts` derived `LUX_USER_CONFIG_KEYS` from `SpeedCurveOptions.entries` at
module scope, which pinned the schema past the `import.meta.dev` guard. The keys are
now listed, with type assertions that fail to compile if they drift from the schema.
- `_gcm-consent.ts` validated consent state with `safeParse` on a strict schema. It now
uses a hand written check with the same warnings.
Every other registry schema was already dev only and treeshakes.
@vercel

vercelBot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
scripts-playgroundReadyReadyPreviewAug 18, 2026 6:12am

Request Review

@coderabbitai

coderabbitaiBot commented Aug 18, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@harlan-zw, you've reached your PR review limit, so we couldn't start this review.

Next review available in:16 minutes

Limit details: You’ve used all 4 included reviews currently available under your plan.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 89f10ea0-913c-45a1-a97f-164ee0554bd1

📥 Commits

Reviewing files that changed from the base of the PR and between 151a94b and 8978857.

📒 Files selected for processing (3)
  • packages/script/src/runtime/registry/_gcm-consent.ts
  • packages/script/src/runtime/registry/speedcurve.ts
  • test/unit/gcm-consent.test.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-newBot commented Aug 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@nuxt/scripts@869

commit: 8978857

@github-actions

Copy link
Copy Markdown

📦 Package Size

⚠️4 size metrics grew

📚 23 runtime dependencies (no change)

Package outputGzippedΔ
@nuxt/scripts · dist/runtime96 kB → 96 kB🔴 +301 B (+0.3%)
@nuxt/scripts · export ./types-source43 kB → 43 kB🔴 +244 B (+0.6%)
@nuxt/scripts · published payload206 kB → 207 kB🔴 +545 B (+0.3%)
@nuxt/scripts · registry runtime41 kB → 42 kB🔴 +301 B (+0.7%)
All tracked output (28)
Package outputGzippedRaw
@nuxt/scripts-cli · runtime dependencies72 kB355 kB
@nuxt/scripts-cli · dependency magicast72 kB355 kB
@nuxt/scripts-cli · export .3.4 kB12 kB
@nuxt/scripts-cli · published payload3.4 kB12 kB
@nuxt/scripts · runtime dependencies584 kB3.38 MB
@nuxt/scripts · dependency @nuxt/devtools-kit2.9 kB7.7 kB
@nuxt/scripts · dependency @vueuse/core174 kB707 kB
@nuxt/scripts · dependency @vueuse/shared39 kB154 kB
@nuxt/scripts · dependency h334 kB146 kB
@nuxt/scripts · dependency magic-string9.4 kB42 kB
@nuxt/scripts · dependency oxc-parser131 kB1.38 MB
@nuxt/scripts · dependency oxc-walker7.6 kB31 kB
@nuxt/scripts · dependency semver25 kB72 kB
@nuxt/scripts · dependency sirv8.8 kB21 kB
@nuxt/scripts · dependency undici2.2 kB8.6 kB
@nuxt/scripts · dependency unstorage70 kB225 kB
@nuxt/scripts · dependency valibot80 kB592 kB
@nuxt/scripts · dist/runtime96 kB280 kB🔴
@nuxt/scripts · export .25 kB105 kB
@nuxt/scripts · export ./registry28 kB89 kB
@nuxt/scripts · export ./stats13 kB89 kB
@nuxt/scripts · export ./types-source43 kB222 kB🔴
@nuxt/scripts · published payload207 kB785 kB🔴
@nuxt/scripts · components runtime2.4 kB6.2 kB
@nuxt/scripts · composables runtime7.5 kB24 kB
@nuxt/scripts · registry runtime42 kB123 kB🔴
@nuxt/scripts · server runtime27 kB80 kB
@nuxt/scripts · utils runtime2.5 kB7.4 kB
Runtime dependencies (23)
PackageDependencyRequestedResolvedCost
@nuxt/scripts-climagicast^0.5.40.5.4📦 72 kB gzip
@nuxt/scripts-clipathe^2.0.32.0.3♻️ free via Nuxt 4.5.1
@nuxt/scripts@nuxt/devtools-kit^3.4.13.4.1📦 2.9 kB gzip
@nuxt/scripts@vueuse/core^14.4.014.4.0📦 174 kB gzip
@nuxt/scripts@vueuse/shared^14.4.014.4.0📦 39 kB gzip
@nuxt/scriptsconsola^3.4.23.4.2♻️ free via Nuxt 4.5.1
@nuxt/scriptsdefu^6.1.76.1.7♻️ free via Nuxt 4.5.1
@nuxt/scriptsh3^1.15.111.15.11📦 34 kB gzip
@nuxt/scriptsmagic-string^1.1.01.1.0📦 9.4 kB gzip, Nuxt has 1.0.0
@nuxt/scriptsofetch^1.5.11.5.1♻️ free via Nuxt 4.5.1
@nuxt/scriptsohash^2.0.112.0.11♻️ free via Nuxt 4.5.1
@nuxt/scriptsoxc-parser^0.143.00.143.0📦 131 kB gzip
@nuxt/scriptsoxc-walker^1.1.11.1.1📦 7.6 kB gzip, Nuxt has 1.0.0
@nuxt/scriptspathe^2.0.32.0.3♻️ free via Nuxt 4.5.1
@nuxt/scriptssemver^7.8.57.8.5📦 25 kB gzip
@nuxt/scriptssirv^3.0.23.0.2📦 8.8 kB gzip
@nuxt/scriptsstd-env^4.2.04.2.0♻️ free via Nuxt 4.5.1
@nuxt/scriptsufo^1.6.41.6.4♻️ free via Nuxt 4.5.1
@nuxt/scriptsultrahtml^1.7.01.7.0♻️ free via Nuxt 4.5.1
@nuxt/scriptsundici^8.10.08.10.0📦 2.2 kB gzip, Nuxt has 8.9.0
@nuxt/scriptsunplugin^3.3.03.3.0♻️ free via Nuxt 4.5.1
@nuxt/scriptsunstorage^1.17.51.17.5📦 70 kB gzip
@nuxt/scriptsvalibot^1.4.21.4.2📦 80 kB gzip

Baseline: main_@_151a94b9___2026-08-18 · gzip is the comparison metric · changes below 16 B gzip are ignored

@harlan-zw
harlan-zw merged commit 7dd5880 into mainAug 18, 2026
16 checks passed
@harlan-zw
harlan-zw deleted the perf/valibot-out-of-client branch August 18, 2026 09:11
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@harlan-zw