Skip to content

chore(auth): pin the Clerk SDK and UI versions — the served bundle is neither the installed one nor stable #7352

Description

@koala73

Problem

Two separate version-resolution gaps mean the Clerk code we ship is not the Clerk code we test or type-check against, and it can change without a commit.

1. The served SDK is not the installed SDK

package.json declares a range:

"@clerk/clerk-js": "^6.13.0"

vite.config.ts reads that string and strips the range prefix to build the Frontend API URL, injected as __CLERK_JS_VERSION__:

const CLERK_JS_VERSION = (CLERK_DEPS['@clerk/clerk-js'] || CLERK_DEV_DEPS['@clerk/clerk-js'] || '')
  .replace(/^[\^~>=<\s]*/, '');

So production loads clerk.worldmonitor.app/npm/@clerk/clerk-js@6.13.0/dist/clerk.browser.js, while the lockfile installs 6.25.6 — which is what TypeScript compiles against. Twelve minor versions of drift between the runtime and its types. A type signature can describe an API the served bundle does not have, and npm run typecheck will pass.

2. The UI bundle floats across an entire major

src/services/clerk.ts:

const CLERK_UI_VERSION = '1';

@clerk/ui@1 resolves at request time. Clerk can change or remove parts of the prebuilt SignIn / UserProfile surfaces — including the passkey management section — with no repo change, no PR, and no CI signal. Anything we delegate to those prebuilt components is only as stable as Clerk's own major.

Why now

Surfaced while planning the passkey offer (docs/plans/2026-08-29-001-feat-clerk-passkey-post-login-offer-plan.md). That plan's verified API claims are anchored to the served 6.13.0 bundle rather than node_modules, precisely because of gap 1 — an earlier draft verified against 6.25.6 and was wrong about what ships.

Not caused by the passkey work, and not blocking it. Filed separately because it is auth infrastructure touching every user: a regression here must be revertable without reverting a feature.

Proposed change

  • Pin @clerk/clerk-js to an exact version in package.json (no ^), matching the lockfile so runtime and types agree.
  • Pin CLERK_UI_VERSION to an exact version rather than a bare major.
  • Include package-lock.json in the change.
  • Add a guard test asserting the generated Frontend API URLs contain no range characters, and that the SDK version matches the installed one — so a future ^ reintroduction goes red.

Verification

  • npx tsx --test tests/clerk-runtime-version-pin.test.mts (new guard)
  • npm run typecheck
  • Manual auth smoke on a preview deploy, recording the versions used: sign in, open the account profile, confirm an existing Pro entitlement still resolves. Unit tests cannot prove this; the blast radius is every signed-in user.

Landing

Its own commit and its own PR. Do not fold into a feature branch.

Acceptance criteria

  • @clerk/clerk-js pinned exactly; lockfile updated
  • CLERK_UI_VERSION pinned exactly
  • Guard test fails if either reverts to a range
  • Manual auth smoke pass recorded with the pinned versions
  • A documented step for future Clerk upgrades: bump deliberately, re-verify against the served bundle

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium priority, schedule when capacity allowschoreMaintenance, linting, tooling

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions