Skip to content

feat(cli-build): inject early-auth probe script during studio build#1294

Draft
jordanl17 wants to merge 8 commits into
mainfrom
feat/early-auth-probe
Draft

feat(cli-build): inject early-auth probe script during studio build#1294
jordanl17 wants to merge 8 commits into
mainfrom
feat/early-auth-probe

Conversation

@jordanl17

@jordanl17 jordanl17 commented Jun 12, 2026

Copy link
Copy Markdown
Member

Description

The studio's first /users/me request waits for the multi-MB module bundle to download and evaluate - a round-trip that could start during HTML parse instead.

This PR injects an inline early-auth probe as the first <head> child of production-built studio index.html when cliConfig.api.projectId is set. The script fires the tagged /users/me fetch (stored token as Authorization header, else cookie credentials) and parks the result on window.__sanityEarlyAuth for the studio's auth store (companion consumer: sanity-io/sanity#13081). The probe is authored as a real TypeScript module, earlyAuthProbeScript.ts, transformed to inlinable JS at build time via Vite's transformWithOxc (~1.4KB, memoized per build); the build fails loudly if module syntax survives. Absent or invalid projectId skips injection, the dev server is untouched, and any runtime error leaves the global unset - a clean no-op until a studio ships the consumer, with no release-order hazard.

Why it matters (validated against 14 days of prod telemetry, 137k sessions): the auth round-trip is serialized after bundle eval on every studio load today, and the probe pre-pays it during HTML parse - a steady ~4-5% median (p50) improvement, ~100-150ms on every load for every user once studios rebuild on the new CLI. Slow-network users gain the most per load (3g round-trips run 0.5-2s). The change is strictly additive: a missed or stale probe costs nothing and falls through to today's behavior exactly.

What to review

  • earlyAuthProbeScript.ts - self-contained by contract (zero value imports; the transform inlines, it does not bundle). The v2026-05-04 path segment must stay in sync with the monorepo's AUTH_API_VERSION (breadcrumb in file).
  • decorateIndexWithEarlyAuthScript.ts - source resolution (.ts under vitest, compiled .js in dist), export stripping, host selection: api.sanity.io default, api.sanity.work only under isStaging().
  • Applied outermost in plugin-sanity-build-entries.ts so the probe is the first <head> child.
  • Degrades to a clean miss under strict CSP (no unsafe-inline) and custom documents without a <head> - the same constraints as the existing staging/importmap inline scripts.

Testing

27 tests: skip guards, host selection including the unset-env production default, injection position, full probe URL pinned, no-module-syntax assertion, and jsdom runtime tests that execute the injected script (token vs cookie selection, the three result discriminants the consumer depends on, error leaves the global unset). Dist-path resolution verified against the built package.

How this fits

Part of the studio startup performance effort: this is the injector half of the early auth probe (a consistent ~4-5% median auth-ready win - it removes the one network round-trip that every load pays serially after bundle eval); the consumer half is sanity-io/sanity#13081. Independent of the other startup PRs - either half can ship first (the consumer treats an absent probe as a miss).

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Stats — @sanity/cli

Compared against main (fe61c5dd)

@sanity/cli

Metric Value vs main (fe61c5d)
Internal (raw) 2.1 KB -
Internal (gzip) 799 B -
Bundled (raw) 11.13 MB -
Bundled (gzip) 2.10 MB -
Import time 854ms +7ms, +0.8%

bin:sanity

Metric Value vs main (fe61c5d)
Internal (raw) 782 B -
Internal (gzip) 423 B -
Bundled (raw) 9.87 MB -
Bundled (gzip) 1.77 MB -
Import time 1.91s +5ms, +0.2%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @sanity/cli-core

Compared against main (fe61c5dd)

Metric Value vs main (fe61c5d)
Internal (raw) 98.2 KB -
Internal (gzip) 23.3 KB -
Bundled (raw) 21.70 MB -
Bundled (gzip) 3.45 MB -
Import time 753ms -2ms, -0.3%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — create-sanity

Compared against main (fe61c5dd)

Metric Value vs main (fe61c5d)
Internal (raw) 908 B -
Internal (gzip) 483 B -
Bundled (raw) 931 B -
Bundled (gzip) 491 B -
Import time ❌ ChildProcess denied: node -
Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Coverage Delta

File Statements
packages/@sanity/cli-build/src/actions/build/decorateIndexWithEarlyAuthScript.ts 100.0% (new)
packages/@sanity/cli-build/src/actions/build/earlyAuthProbeConstants.ts 100.0% (new)
packages/@sanity/cli-build/src/actions/build/earlyAuthProbeScript.ts 90.9% (new)
packages/@sanity/cli-build/src/actions/build/getViteConfig.ts 100.0% (±0%)
packages/@sanity/cli-build/src/actions/build/vite/plugin-sanity-build-entries.ts 91.3% (±0%)
packages/@sanity/cli/src/actions/build/buildStaticFiles.ts 96.8% (±0%)
packages/@sanity/cli/src/actions/build/buildStudio.ts 95.5% (±0%)

Comparing 7 changed files against main @ 8985b42fc1aece887dc15d903ff1ebebbc99440a

Overall Coverage

Metric Coverage
Statements 87.1% (- 0.5%)
Branches 77.1% (- 0.5%)
Functions 85.1% (- 1.4%)
Lines 87.5% (- 0.5%)

Sign up for free to 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