This is page 3445 of 3460. Use http://codebase.md/clerk/javascript?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── bright-papayas-accept.md
│ ├── changelog.js
│ ├── config.json
│ ├── moody-parks-scream.md
│ ├── README.md
│ ├── ripe-ants-carry.md
│ ├── ripe-banks-pay.md
│ └── shaggy-numbers-attack.md
├── .coderabbit.yaml
├── .cursor
│ └── rules
│ ├── clerk-js-ui.mdc
│ ├── development.mdc
│ ├── global.mdc
│ ├── monorepo.mdc
│ ├── nextjs.mdc
│ ├── react.mdc
│ └── typescript.mdc
├── .dockerignore
├── .editorconfig
├── .github
│ ├── .cache-version
│ ├── actions
│ │ ├── ensure-stable-pr
│ │ │ └── action.yml
│ │ ├── init
│ │ │ └── action.yml
│ │ ├── init-blacksmith
│ │ │ └── action.yml
│ │ ├── verdaccio
│ │ │ └── action.yml
│ │ └── version-prepatch
│ │ └── action.yml
│ ├── ISSUE_TEMPLATE
│ │ ├── BUG_REPORT.yml
│ │ └── config.yml
│ ├── labeler.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows
│ ├── ci.yml
│ ├── e2e-cleanups.yml
│ ├── labeler.yml
│ ├── lock-threads.yml
│ ├── major-version-check.yml
│ ├── nightly-checks.yml
│ ├── pr-title-linter.yml
│ ├── preview.retheme.yml
│ ├── preview.yml
│ ├── release-canary.yml
│ ├── release-snapshot.yml
│ ├── release.yml
│ └── validate-renovate-config.yml
├── .gitignore
├── .husky
│ └── pre-commit
├── .jit
│ └── config.yml
├── .lintstagedrc.json
├── .npmrc
├── .nvmrc
├── .prettierignore
├── .typedoc
│ ├── __tests__
│ │ ├── __snapshots__
│ │ │ └── file-structure.test.ts.snap
│ │ └── file-structure.test.ts
│ ├── custom-plugin.mjs
│ ├── custom-router.mjs
│ ├── custom-theme.mjs
│ ├── README.md
│ ├── tsconfig.json
│ └── typedoc-prettier-config.json
├── .vscode
│ ├── extensions.json
│ ├── launch.json
│ ├── settings.json
│ └── tasks.json
├── commitlint.config.ts
├── docs
│ ├── CICD.md
│ ├── CODE_OF_CONDUCT.md
│ ├── CONTRIBUTING.md
│ ├── PUBLISH.md
│ └── SECURITY.md
├── eslint.config.mjs
├── integration
│ ├── .env.local.sample
│ ├── .keys.json.sample
│ ├── certs
│ │ └── README.md
│ ├── cleanup
│ │ └── cleanup.setup.ts
│ ├── constants.ts
│ ├── deployments
│ │ └── vercel.test.ts
│ ├── models
│ │ ├── application.ts
│ │ ├── applicationConfig.ts
│ │ ├── deployment.ts
│ │ ├── environment.ts
│ │ ├── helpers.ts
│ │ ├── longRunningApplication.ts
│ │ └── stateFile.ts
│ ├── playwright.cleanup.config.ts
│ ├── playwright.config.ts
│ ├── playwright.deployments.config.ts
│ ├── presets
│ │ ├── astro.ts
│ │ ├── custom-flows.ts
│ │ ├── elements.ts
│ │ ├── envs.ts
│ │ ├── expo.ts
│ │ ├── express.ts
│ │ ├── index.ts
│ │ ├── longRunningApps.ts
│ │ ├── next.ts
│ │ ├── nuxt.ts
│ │ ├── react-router.ts
│ │ ├── react.ts
│ │ ├── tanstack.ts
│ │ ├── utils.ts
│ │ └── vue.ts
│ ├── README.md
│ ├── scripts
│ │ ├── awaitableTreekill.ts
│ │ ├── clerkJsServer.ts
│ │ ├── index.ts
│ │ ├── logger.ts
│ │ ├── proxyServer.ts
│ │ ├── range.ts
│ │ ├── run.ts
│ │ ├── setup.ts
│ │ ├── waitForIdleProcess.ts
│ │ └── waitForServer.ts
│ ├── templates
│ │ ├── astro-hybrid
│ │ │ ├── .gitignore
│ │ │ ├── astro.config.mjs
│ │ │ ├── package.json
│ │ │ ├── public
│ │ │ │ └── favicon.svg
│ │ │ ├── src
│ │ │ │ ├── layouts
│ │ │ │ │ └── Layout.astro
│ │ │ │ ├── middleware.ts
│ │ │ │ └── pages
│ │ │ │ ├── index.astro
│ │ │ │ ├── only-admins.astro
│ │ │ │ ├── only-members.astro
│ │ │ │ └── ssr.astro
│ │ │ └── tsconfig.json
│ │ ├── astro-node
│ │ │ ├── .gitignore
│ │ │ ├── astro.config.mjs
│ │ │ ├── package.json
│ │ │ ├── public
│ │ │ │ └── favicon.svg
│ │ │ ├── README.md
│ │ │ ├── src
│ │ │ │ ├── components
│ │ │ │ │ ├── Card.astro
│ │ │ │ │ ├── CustomUserButton.astro
│ │ │ │ │ ├── LanguagePicker.tsx
│ │ │ │ │ ├── page-with-user.tsx
│ │ │ │ │ ├── SignOutReact.tsx
│ │ │ │ │ └── StreamUser.astro
│ │ │ │ ├── env.d.ts
│ │ │ │ ├── layouts
│ │ │ │ │ ├── Layout.astro
│ │ │ │ │ ├── react
│ │ │ │ │ │ └── Layout.astro
│ │ │ │ │ ├── Streaming.astro
│ │ │ │ │ └── ViewTransitionsLayout.astro
│ │ │ │ ├── middleware.ts
│ │ │ │ └── pages
│ │ │ │ ├── api
│ │ │ │ │ ├── auth
│ │ │ │ │ │ └── me.ts
│ │ │ │ │ └── protected
│ │ │ │ │ ├── current-org.ts
│ │ │ │ │ └── only-admin.ts
│ │ │ │ ├── billing
│ │ │ │ │ ├── checkout-btn.astro
│ │ │ │ │ ├── plan-details-btn.astro
│ │ │ │ │ └── subscription-details-btn.astro
│ │ │ │ ├── buttons.astro
│ │ │ │ ├── custom-pages
│ │ │ │ │ ├── organization-profile.astro
│ │ │ │ │ └── user-profile.astro
│ │ │ │ ├── discover.astro
│ │ │ │ ├── index.astro
│ │ │ │ ├── only-admins.astro
│ │ │ │ ├── only-members.astro
│ │ │ │ ├── organization.astro
│ │ │ │ ├── pricing-table.astro
│ │ │ │ ├── react
│ │ │ │ │ ├── index.astro
│ │ │ │ │ ├── only-admins.astro
│ │ │ │ │ ├── only-members.astro
│ │ │ │ │ ├── sign-in.astro
│ │ │ │ │ └── user.astro
│ │ │ │ ├── server-islands.astro
│ │ │ │ ├── sign-in.astro
│ │ │ │ ├── transitions
│ │ │ │ │ ├── index.astro
│ │ │ │ │ └── sign-in.astro
│ │ │ │ ├── user.astro
│ │ │ │ └── utility.astro
│ │ │ ├── tailwind.config.cjs
│ │ │ └── tsconfig.json
│ │ ├── custom-flows-react-vite
│ │ │ ├── .gitignore
│ │ │ ├── components.json
│ │ │ ├── eslint.config.js
│ │ │ ├── index.html
│ │ │ ├── package.json
│ │ │ ├── src
│ │ │ │ ├── components
│ │ │ │ │ └── ui
│ │ │ │ │ ├── button.tsx
│ │ │ │ │ ├── card.tsx
│ │ │ │ │ ├── input.tsx
│ │ │ │ │ └── label.tsx
│ │ │ │ ├── index.css
│ │ │ │ ├── lib
│ │ │ │ │ └── utils.ts
│ │ │ │ ├── main.tsx
│ │ │ │ ├── routes
│ │ │ │ │ ├── Home.tsx
│ │ │ │ │ ├── Protected.tsx
│ │ │ │ │ ├── SignIn.tsx
│ │ │ │ │ └── SignUp.tsx
│ │ │ │ └── vite-env.d.ts
│ │ │ ├── tsconfig.app.json
│ │ │ ├── tsconfig.json
│ │ │ ├── tsconfig.node.json
│ │ │ └── vite.config.ts
│ │ ├── elements-next
│ │ │ ├── .gitignore
│ │ │ ├── next.config.js
│ │ │ ├── package.json
│ │ │ ├── postcss.config.js
│ │ │ ├── README.md
│ │ │ ├── src
│ │ │ │ ├── app
│ │ │ │ │ ├── favicon.ico
│ │ │ │ │ ├── globals.css
│ │ │ │ │ ├── layout.tsx
│ │ │ │ │ ├── otp
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── page.tsx
│ │ │ │ │ ├── sign-in
│ │ │ │ │ │ └── [[...sign-in]]
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── sign-up
│ │ │ │ │ │ └── [[...sign-up]]
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ └── validate-password
│ │ │ │ │ └── page.tsx
│ │ │ │ └── middleware.ts
│ │ │ ├── tailwind.config.js
│ │ │ └── tsconfig.json
│ │ ├── expo-web
│ │ │ ├── .gitignore
│ │ │ ├── app
│ │ │ │ ├── _layout.tsx
│ │ │ │ ├── +html.tsx
│ │ │ │ ├── +not-found.tsx
│ │ │ │ ├── custom-sign-in.tsx
│ │ │ │ ├── custom-sign-up.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ └── sign-in.tsx
│ │ │ ├── app.json
│ │ │ ├── assets
│ │ │ │ └── images
│ │ │ │ ├── icon.png
│ │ │ │ └── splash.png
│ │ │ ├── babel.config.js
│ │ │ ├── constants
│ │ │ │ └── Colors.ts
│ │ │ ├── metro.config.js
│ │ │ ├── package.json
│ │ │ ├── README.md
│ │ │ └── tsconfig.json
│ │ ├── express-vite
│ │ │ ├── .gitignore
│ │ │ ├── index.html
│ │ │ ├── package.json
│ │ │ ├── src
│ │ │ │ ├── client
│ │ │ │ │ ├── main.ts
│ │ │ │ │ ├── tsconfig.json
│ │ │ │ │ └── vite-env.d.ts
│ │ │ │ └── server
│ │ │ │ └── main.ts
│ │ │ └── tsconfig.json
│ │ ├── index.ts
│ │ ├── next-app-router
│ │ │ ├── .gitignore
│ │ │ ├── next.config.js
│ │ │ ├── package.json
│ │ │ ├── public
│ │ │ │ ├── next.svg
│ │ │ │ └── vercel.svg
│ │ │ ├── README.md
│ │ │ ├── src
│ │ │ │ ├── app
│ │ │ │ │ ├── (reverification)
│ │ │ │ │ │ ├── action-with-use-reverification
│ │ │ │ │ │ │ └── page.tsx
│ │ │ │ │ │ ├── actions.ts
│ │ │ │ │ │ ├── button-action.tsx
│ │ │ │ │ │ └── requires-re-verification
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── api
│ │ │ │ │ │ ├── me
│ │ │ │ │ │ │ └── route.ts
│ │ │ │ │ │ └── settings
│ │ │ │ │ │ └── route.ts
│ │ │ │ │ ├── api-keys
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── billing
│ │ │ │ │ │ ├── checkout-btn
│ │ │ │ │ │ │ └── page.tsx
│ │ │ │ │ │ ├── hooks
│ │ │ │ │ │ │ └── page.tsx
│ │ │ │ │ │ ├── plan-details-btn
│ │ │ │ │ │ │ └── page.tsx
│ │ │ │ │ │ └── subscription-details-btn
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── buttons
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── client-id.tsx
│ │ │ │ │ ├── create-organization
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── csp
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── favicon.ico
│ │ │ │ │ ├── globals.css
│ │ │ │ │ ├── hash
│ │ │ │ │ │ └── sign-in
│ │ │ │ │ │ └── [[...page]]
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── jwt-v2-organizations
│ │ │ │ │ │ ├── (tests)
│ │ │ │ │ │ │ ├── conditionals.tsx
│ │ │ │ │ │ │ ├── has-client
│ │ │ │ │ │ │ │ └── page.tsx
│ │ │ │ │ │ │ ├── has-server
│ │ │ │ │ │ │ │ └── page.tsx
│ │ │ │ │ │ │ ├── has-ssr
│ │ │ │ │ │ │ │ ├── client.tsx
│ │ │ │ │ │ │ │ └── page.tsx
│ │ │ │ │ │ │ ├── layout.tsx
│ │ │ │ │ │ │ └── page.tsx
│ │ │ │ │ │ ├── client-jwt.tsx
│ │ │ │ │ │ └── server-jwt.tsx
│ │ │ │ │ ├── layout.tsx
│ │ │ │ │ ├── only-admin
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── organization-list
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── organization-profile
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── organization-switcher
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── organizations-by-id
│ │ │ │ │ │ └── [id]
│ │ │ │ │ │ ├── page.tsx
│ │ │ │ │ │ └── settings
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── organizations-by-slug
│ │ │ │ │ │ └── [slug]
│ │ │ │ │ │ ├── page.tsx
│ │ │ │ │ │ └── settings
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── page-protected
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── page.module.css
│ │ │ │ │ ├── page.tsx
│ │ │ │ │ ├── personal-account
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── pricing-table
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── protected
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── settings
│ │ │ │ │ │ ├── auth-has
│ │ │ │ │ │ │ └── page.tsx
│ │ │ │ │ │ ├── auth-protect
│ │ │ │ │ │ │ └── page.tsx
│ │ │ │ │ │ ├── rcc-protect
│ │ │ │ │ │ │ └── page.tsx
│ │ │ │ │ │ ├── rsc-protect
│ │ │ │ │ │ │ └── page.tsx
│ │ │ │ │ │ └── useAuth-has
│ │ │ │ │ │ ├── layout.tsx
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── sign-in
│ │ │ │ │ │ └── [[...catchall]]
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── sign-in-or-up
│ │ │ │ │ │ └── [[...catchall]]
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── sign-up
│ │ │ │ │ │ └── [[...catchall]]
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── switcher
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── user
│ │ │ │ │ │ └── [[...catchall]]
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── user-avatar
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ ├── user-button
│ │ │ │ │ │ └── page.tsx
│ │ │ │ │ └── waitlist
│ │ │ │ │ └── page.tsx
│ │ │ │ └── middleware.ts
│ │ │ └── tsconfig.json
│ │ ├── next-app-router-quickstart
│ │ │ ├── .gitignore
│ │ │ ├── next.config.js
│ │ │ ├── package.json
│ │ │ ├── public
│ │ │ │ ├── next.svg
│ │ │ │ └── vercel.svg
│ │ │ ├── README.md
│ │ │ ├── src
│ │ │ │ ├── app
│ │ │ │ │ ├── favicon.ico
│ │ │ │ │ ├── globals.css
│ │ │ │ │ ├── layout.tsx
│ │ │ │ │ ├── page.module.css
│ │ │ │ │ └── page.tsx
│ │ │ │ └── middleware.ts
│ │ │ └── tsconfig.json
│ │ ├── nuxt-node
│ │ │ ├── app
│ │ │ │ ├── app.vue
│ │ │ │ ├── middleware
│ │ │ │ │ └── auth.global.js
│ │ │ │ └── pages
│ │ │ │ ├── index.vue
│ │ │ │ ├── only-admin.vue
│ │ │ │ ├── pricing-table.vue
│ │ │ │ ├── sign-in.vue
│ │ │ │ └── user.vue
│ │ │ ├── nuxt.config.js
│ │ │ ├── package.json
│ │ │ └── server
│ │ │ └── api
│ │ │ └── me.js
│ │ ├── react-cra
│ │ │ ├── .gitignore
│ │ │ ├── package.json
│ │ │ ├── public
│ │ │ │ ├── favicon.ico
│ │ │ │ ├── index.html
│ │ │ │ ├── logo192.png
│ │ │ │ ├── logo512.png
│ │ │ │ ├── manifest.json
│ │ │ │ └── robots.txt
│ │ │ ├── README.md
│ │ │ ├── src
│ │ │ │ ├── App.css
│ │ │ │ ├── App.tsx
│ │ │ │ ├── index.css
│ │ │ │ ├── index.tsx
│ │ │ │ ├── logo.svg
│ │ │ │ └── react-app-env.d.ts
│ │ │ └── tsconfig.json
│ │ ├── react-router-library
│ │ │ ├── index.html
│ │ │ ├── package.json
│ │ │ ├── public
│ │ │ │ └── vite.svg
│ │ │ ├── README.md
│ │ │ ├── src
│ │ │ │ ├── App.css
│ │ │ │ ├── App.tsx
│ │ │ │ ├── assets
│ │ │ │ │ └── react.svg
│ │ │ │ ├── index.css
│ │ │ │ ├── main.tsx
│ │ │ │ └── vite-env.d.ts
│ │ │ ├── tsconfig.app.json
│ │ │ ├── tsconfig.json
│ │ │ ├── tsconfig.node.json
│ │ │ └── vite.config.ts
│ │ ├── react-router-node
│ │ │ ├── .gitignore
│ │ │ ├── app
│ │ │ │ ├── root.tsx
│ │ │ │ ├── routes
│ │ │ │ │ ├── home.tsx
│ │ │ │ │ ├── protected.tsx
│ │ │ │ │ ├── sign-in.tsx
│ │ │ │ │ └── sign-up.tsx
│ │ │ │ └── routes.ts
│ │ │ ├── package.json
│ │ │ ├── public
│ │ │ │ └── favicon.ico
│ │ │ ├── react-router.config.ts
│ │ │ ├── README.md
│ │ │ ├── tsconfig.json
│ │ │ └── vite.config.ts
│ │ ├── react-vite
│ │ │ ├── .gitignore
│ │ │ ├── index.html
│ │ │ ├── package.json
│ │ │ ├── public
│ │ │ │ └── vite.svg
│ │ │ ├── src
│ │ │ │ ├── App.css
│ │ │ │ ├── App.tsx
│ │ │ │ ├── assets
│ │ │ │ │ └── react.svg
│ │ │ │ ├── buttons
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── clerk-status
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── client-id.tsx
│ │ │ │ ├── create-organization
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── custom-user-button
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── with-dynamic-items.tsx
│ │ │ │ │ ├── with-dynamic-label-and-custom-pages.tsx
│ │ │ │ │ └── with-dynamic-labels.tsx
│ │ │ │ ├── custom-user-button-trigger
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── custom-user-profile
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── index.css
│ │ │ │ ├── main.tsx
│ │ │ │ ├── organization-list
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── organization-profile
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── organization-switcher
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── PageContext.tsx
│ │ │ │ ├── protected
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── sign-in
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── sign-up
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── user
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── user-avatar
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── user-button
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── vite-env.d.ts
│ │ │ │ └── waitlist
│ │ │ │ └── index.tsx
│ │ │ ├── tsconfig.json
│ │ │ ├── tsconfig.node.json
│ │ │ └── vite.config.ts
│ │ ├── tanstack-react-start
│ │ │ ├── .gitignore
│ │ │ ├── package.json
│ │ │ ├── README.md
│ │ │ ├── src
│ │ │ │ ├── router.tsx
│ │ │ │ ├── routes
│ │ │ │ │ ├── __root.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── sign-in.tsx
│ │ │ │ │ └── user.tsx
│ │ │ │ ├── routeTree.gen.ts
│ │ │ │ ├── start.ts
│ │ │ │ └── styles
│ │ │ │ └── app.css
│ │ │ ├── tsconfig.json
│ │ │ └── vite.config.ts
│ │ └── vue-vite
│ │ ├── .gitignore
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.vue
│ │ │ ├── assets
│ │ │ │ └── styles.css
│ │ │ ├── components
│ │ │ │ ├── CustomUserButton.vue
│ │ │ │ └── LanguagePicker.vue
│ │ │ ├── main.ts
│ │ │ ├── router.ts
│ │ │ ├── views
│ │ │ │ ├── Admin.vue
│ │ │ │ ├── billing
│ │ │ │ │ ├── CheckoutBtn.vue
│ │ │ │ │ ├── PlanDetailsBtn.vue
│ │ │ │ │ └── SubscriptionDetailsBtn.vue
│ │ │ │ ├── custom-pages
│ │ │ │ │ ├── OrganizationProfile.vue
│ │ │ │ │ └── UserProfile.vue
│ │ │ │ ├── Home.vue
│ │ │ │ ├── PricingTable.vue
│ │ │ │ ├── Profile.vue
│ │ │ │ ├── SignIn.vue
│ │ │ │ ├── Unstyled.vue
│ │ │ │ └── UserAvatar.vue
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── tests
│ │ ├── appearance.test.ts
│ │ ├── astro
│ │ │ ├── components.test.ts
│ │ │ ├── hybrid.test.ts
│ │ │ └── machine.test.ts
│ │ ├── billing-hooks.test.ts
│ │ ├── components.test.ts
│ │ ├── content-security-policy.test.ts
│ │ ├── custom-flows
│ │ │ ├── sign-in.test.ts
│ │ │ └── sign-up.test.ts
│ │ ├── custom-pages.test.ts
│ │ ├── db-jwt.test.ts
│ │ ├── dynamic-keys.test.ts
│ │ ├── elements
│ │ │ ├── next-sign-in.test.ts
│ │ │ ├── next-sign-up.test.ts
│ │ │ ├── otp.test.ts
│ │ │ └── validate-password.test.ts
│ │ ├── email-code.test.ts
│ │ ├── email-link.test.ts
│ │ ├── expo-web
│ │ │ ├── basic.test.ts
│ │ │ └── custom-flows.test.ts
│ │ ├── express
│ │ │ └── basic.test.ts
│ │ ├── global.setup.ts
│ │ ├── global.teardown.ts
│ │ ├── handshake
│ │ │ └── handshake.test.ts
│ │ ├── handshake.test.ts
│ │ ├── impersonation-flow.test.ts
│ │ ├── last-authentication-strategy.test.ts
│ │ ├── legal-consent.test.ts
│ │ ├── localhost
│ │ │ ├── localhost-different-port-different-instance.test.ts
│ │ │ ├── localhost-different-port-same-instance.test.ts
│ │ │ └── localhost-switch-instance.test.ts
│ │ ├── machine-auth
│ │ │ ├── api-keys.test.ts
│ │ │ ├── component.test.ts
│ │ │ └── m2m.test.ts
│ │ ├── middleware-placement.test.ts
│ │ ├── navigation.test.ts
│ │ ├── next-account-portal
│ │ │ ├── clerk-v4-ap-core-1.test.ts
│ │ │ ├── clerk-v4-ap-core-2.test.ts
│ │ │ ├── clerk-v5-ap-core-1.test.ts
│ │ │ ├── clerk-v5-ap-core-2.test.ts
│ │ │ └── common.ts
│ │ ├── next-build.test.ts
│ │ ├── next-quickstart-keyless.test.ts
│ │ ├── next-quickstart.test.ts
│ │ ├── non-secure-context.test.ts
│ │ ├── nuxt
│ │ │ ├── basic.test.ts
│ │ │ └── middleware.test.ts
│ │ ├── oauth-flows.test.ts
│ │ ├── pricing-table.test.ts
│ │ ├── protect-jwt-v2.test.ts
│ │ ├── protect.test.ts
│ │ ├── react-router
│ │ │ ├── basic.test.ts
│ │ │ ├── library-mode.test.ts
│ │ │ └── pre-middleware.test.ts
│ │ ├── redirects.test.ts
│ │ ├── resiliency.test.ts
│ │ ├── restricted-mode.test.ts
│ │ ├── reverification.test.ts
│ │ ├── session-tasks-eject-flow.test.ts
│ │ ├── session-tasks-multi-session.test.ts
│ │ ├── session-tasks-sign-in.test.ts
│ │ ├── session-tasks-sign-up.test.ts
│ │ ├── session-token-cache
│ │ │ ├── multi-session.test.ts
│ │ │ └── single-session.test.ts
│ │ ├── sessions
│ │ │ ├── root-subdomain-prod-instances.test.ts
│ │ │ └── utils.ts
│ │ ├── sign-in-flow.test.ts
│ │ ├── sign-in-or-up-component.test.ts
│ │ ├── sign-in-or-up-email-links-flow.test.ts
│ │ ├── sign-in-or-up-flow.test.ts
│ │ ├── sign-in-or-up-restricted-mode.test.ts
│ │ ├── sign-out-smoke.test.ts
│ │ ├── sign-up-flow.test.ts
│ │ ├── snapshots
│ │ │ └── appearance.test.ts-snapshots
│ │ │ └── appearance-prop-all-clerk-themes-render-1-chrome-darwin.png
│ │ ├── tanstack-start
│ │ │ └── basic.test.ts
│ │ ├── unsafeMetadata.test.ts
│ │ ├── update-props.test.ts
│ │ ├── user-avatar.test.ts
│ │ ├── user-profile.test.ts
│ │ ├── vue
│ │ │ └── components.test.ts
│ │ ├── waitlist-mode.test.ts
│ │ └── whatsapp-phone-code.test.ts
│ ├── testUtils
│ │ ├── emailService.ts
│ │ ├── handshake.ts
│ │ ├── index.ts
│ │ ├── invitationsService.ts
│ │ ├── organizationsService.ts
│ │ ├── phoneUtils.ts
│ │ ├── testAgainstRunningApps.ts
│ │ └── usersService.ts
│ ├── tsconfig.json
│ └── types.d.ts
├── jest.setup-after-env.ts
├── LICENSE
├── package.json
├── packages
│ ├── agent-toolkit
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── ai-sdk
│ │ │ │ ├── adapter.ts
│ │ │ │ └── index.ts
│ │ │ ├── global.d.ts
│ │ │ ├── langchain
│ │ │ │ ├── adapter.ts
│ │ │ │ └── index.ts
│ │ │ ├── lib
│ │ │ │ ├── clerk-client.ts
│ │ │ │ ├── clerk-tool.ts
│ │ │ │ ├── constants.ts
│ │ │ │ ├── inject-session-claims.ts
│ │ │ │ ├── tools
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── invitations.ts
│ │ │ │ │ ├── organizations.ts
│ │ │ │ │ └── users.ts
│ │ │ │ ├── types.ts
│ │ │ │ ├── utils.ts
│ │ │ │ └── utilts.test.ts
│ │ │ └── modelcontextprotocol
│ │ │ ├── adapter.ts
│ │ │ ├── index.ts
│ │ │ └── local-server.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.test.json
│ │ └── tsup.config.ts
│ ├── astro
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── client
│ │ │ └── package.json
│ │ ├── env.d.ts
│ │ ├── hotload
│ │ │ └── package.json
│ │ ├── internal
│ │ │ └── package.json
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── react
│ │ │ └── package.json
│ │ ├── README.md
│ │ ├── server
│ │ │ └── package.json
│ │ ├── src
│ │ │ ├── astro-components
│ │ │ │ ├── control
│ │ │ │ │ ├── AuthenticateWithRedirectCallback.astro
│ │ │ │ │ ├── BaseClerkControlElement.ts
│ │ │ │ │ ├── Protect.astro
│ │ │ │ │ ├── ProtectCSR.astro
│ │ │ │ │ ├── ProtectSSR.astro
│ │ │ │ │ ├── SignedIn.astro
│ │ │ │ │ ├── SignedInCSR.astro
│ │ │ │ │ ├── SignedInSSR.astro
│ │ │ │ │ ├── SignedOut.astro
│ │ │ │ │ ├── SignedOutCSR.astro
│ │ │ │ │ └── SignedOutSSR.astro
│ │ │ │ ├── index.ts
│ │ │ │ ├── interactive
│ │ │ │ │ ├── CreateOrganization.astro
│ │ │ │ │ ├── CustomProfilePageRenderer.astro
│ │ │ │ │ ├── GoogleOneTap.astro
│ │ │ │ │ ├── InternalUIComponentRenderer.astro
│ │ │ │ │ ├── OrganizationList.astro
│ │ │ │ │ ├── OrganizationProfile
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── OrganizationProfile.astro
│ │ │ │ │ │ ├── OrganizationProfileLink.astro
│ │ │ │ │ │ └── OrganizationProfilePage.astro
│ │ │ │ │ ├── OrganizationSwitcher
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── OrganizationProfileLink.astro
│ │ │ │ │ │ ├── OrganizationProfilePage.astro
│ │ │ │ │ │ └── OrganizationSwitcher.astro
│ │ │ │ │ ├── OrganizationSwitcher.astro
│ │ │ │ │ ├── PricingTable.astro
│ │ │ │ │ ├── SignIn.astro
│ │ │ │ │ ├── SignUp.astro
│ │ │ │ │ ├── UserAvatar.astro
│ │ │ │ │ ├── UserButton
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── MenuItemRenderer.astro
│ │ │ │ │ │ ├── UserButton.astro
│ │ │ │ │ │ ├── UserButtonAction.astro
│ │ │ │ │ │ ├── UserButtonLink.astro
│ │ │ │ │ │ ├── UserButtonMenuItems.astro
│ │ │ │ │ │ └── UserButtonUserProfilePage.astro
│ │ │ │ │ ├── UserProfile
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── UserProfile.astro
│ │ │ │ │ │ ├── UserProfileLink.astro
│ │ │ │ │ │ └── UserProfilePage.astro
│ │ │ │ │ └── Waitlist.astro
│ │ │ │ └── unstyled
│ │ │ │ ├── CheckoutButton.astro
│ │ │ │ ├── PlanDetailsButton.astro
│ │ │ │ ├── SignInButton.astro
│ │ │ │ ├── SignOutButton.astro
│ │ │ │ ├── SignUpButton.astro
│ │ │ │ ├── SubscriptionDetailsButton.astro
│ │ │ │ └── utils.ts
│ │ │ ├── async-local-storage.client.ts
│ │ │ ├── async-local-storage.server.ts
│ │ │ ├── client
│ │ │ │ └── index.ts
│ │ │ ├── env.d.ts
│ │ │ ├── global.d.ts
│ │ │ ├── index.ts
│ │ │ ├── integration
│ │ │ │ ├── create-integration.ts
│ │ │ │ └── vite-plugin-astro-config.ts
│ │ │ ├── internal
│ │ │ │ ├── create-clerk-instance.ts
│ │ │ │ ├── create-injection-script-runner.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── invoke-clerk-astro-js-functions.ts
│ │ │ │ ├── merge-env-vars-with-params.ts
│ │ │ │ ├── mount-clerk-astro-js-components.ts
│ │ │ │ ├── run-once.ts
│ │ │ │ ├── swap-document.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils
│ │ │ │ └── generateSafeId.ts
│ │ │ ├── react
│ │ │ │ ├── CheckoutButton.tsx
│ │ │ │ ├── controlComponents.tsx
│ │ │ │ ├── hooks.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── PlanDetailsButton.tsx
│ │ │ │ ├── SignInButton.tsx
│ │ │ │ ├── SignOutButton.tsx
│ │ │ │ ├── SignUpButton.tsx
│ │ │ │ ├── SubscriptionDetailsButton.tsx
│ │ │ │ ├── types.ts
│ │ │ │ ├── uiComponents.tsx
│ │ │ │ └── utils.tsx
│ │ │ ├── server
│ │ │ │ ├── build-clerk-hotload-script.ts
│ │ │ │ ├── clerk-client.ts
│ │ │ │ ├── clerk-middleware.ts
│ │ │ │ ├── current-user.ts
│ │ │ │ ├── get-safe-env.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── route-matcher.ts
│ │ │ │ ├── server-redirect-with-auth.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ ├── stores
│ │ │ │ ├── external.ts
│ │ │ │ └── internal.ts
│ │ │ ├── types.ts
│ │ │ └── webhooks.ts
│ │ ├── tsconfig.json
│ │ ├── tsup.config.ts
│ │ ├── turbo.json
│ │ └── webhooks
│ │ └── package.json
│ ├── backend
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── errors
│ │ │ └── package.json
│ │ ├── internal
│ │ │ └── package.json
│ │ ├── jwt
│ │ │ └── package.json
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── createRedirect.test.ts
│ │ │ │ ├── exports.test.ts
│ │ │ │ └── webhooks.test.ts
│ │ │ ├── api
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── ClientApi.test.ts
│ │ │ │ │ ├── factory.test.ts
│ │ │ │ │ ├── M2MTokenApi.test.ts
│ │ │ │ │ ├── MachineApi.test.ts
│ │ │ │ │ ├── SamlConnectionApi.test.ts
│ │ │ │ │ └── SessionApi.test.ts
│ │ │ │ ├── endpoints
│ │ │ │ │ ├── AbstractApi.ts
│ │ │ │ │ ├── AccountlessApplicationsAPI.ts
│ │ │ │ │ ├── ActorTokenApi.ts
│ │ │ │ │ ├── AllowlistIdentifierApi.ts
│ │ │ │ │ ├── APIKeysApi.ts
│ │ │ │ │ ├── BetaFeaturesApi.ts
│ │ │ │ │ ├── BillingApi.ts
│ │ │ │ │ ├── BlocklistIdentifierApi.ts
│ │ │ │ │ ├── ClientApi.ts
│ │ │ │ │ ├── DomainApi.ts
│ │ │ │ │ ├── EmailAddressApi.ts
│ │ │ │ │ ├── IdPOAuthAccessTokenApi.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── InstanceApi.ts
│ │ │ │ │ ├── InvitationApi.ts
│ │ │ │ │ ├── JwksApi.ts
│ │ │ │ │ ├── JwtTemplatesApi.ts
│ │ │ │ │ ├── M2MTokenApi.ts
│ │ │ │ │ ├── MachineApi.ts
│ │ │ │ │ ├── OAuthApplicationsApi.ts
│ │ │ │ │ ├── OrganizationApi.ts
│ │ │ │ │ ├── PhoneNumberApi.ts
│ │ │ │ │ ├── ProxyCheckApi.ts
│ │ │ │ │ ├── RedirectUrlApi.ts
│ │ │ │ │ ├── SamlConnectionApi.ts
│ │ │ │ │ ├── SessionApi.ts
│ │ │ │ │ ├── SignInTokenApi.ts
│ │ │ │ │ ├── SignUpApi.ts
│ │ │ │ │ ├── TestingTokenApi.ts
│ │ │ │ │ ├── UserApi.ts
│ │ │ │ │ ├── util-types.ts
│ │ │ │ │ ├── WaitlistEntryApi.ts
│ │ │ │ │ └── WebhookApi.ts
│ │ │ │ ├── factory.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── request.ts
│ │ │ │ └── resources
│ │ │ │ ├── AccountlessApplication.ts
│ │ │ │ ├── ActorToken.ts
│ │ │ │ ├── AllowlistIdentifier.ts
│ │ │ │ ├── APIKey.ts
│ │ │ │ ├── BlocklistIdentifier.ts
│ │ │ │ ├── Client.ts
│ │ │ │ ├── CnameTarget.ts
│ │ │ │ ├── CommercePlan.ts
│ │ │ │ ├── CommerceSubscription.ts
│ │ │ │ ├── CommerceSubscriptionItem.ts
│ │ │ │ ├── Cookies.ts
│ │ │ │ ├── DeletedObject.ts
│ │ │ │ ├── Deserializer.ts
│ │ │ │ ├── Domain.ts
│ │ │ │ ├── Email.ts
│ │ │ │ ├── EmailAddress.ts
│ │ │ │ ├── Enums.ts
│ │ │ │ ├── ExternalAccount.ts
│ │ │ │ ├── Feature.ts
│ │ │ │ ├── HandshakePayload.ts
│ │ │ │ ├── IdentificationLink.ts
│ │ │ │ ├── IdPOAuthAccessToken.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── Instance.ts
│ │ │ │ ├── InstanceRestrictions.ts
│ │ │ │ ├── InstanceSettings.ts
│ │ │ │ ├── Invitation.ts
│ │ │ │ ├── JSON.ts
│ │ │ │ ├── JwtTemplate.ts
│ │ │ │ ├── M2MToken.ts
│ │ │ │ ├── Machine.ts
│ │ │ │ ├── MachineScope.ts
│ │ │ │ ├── MachineSecretKey.ts
│ │ │ │ ├── OauthAccessToken.ts
│ │ │ │ ├── OAuthApplication.ts
│ │ │ │ ├── Organization.ts
│ │ │ │ ├── OrganizationDomain.ts
│ │ │ │ ├── OrganizationInvitation.ts
│ │ │ │ ├── OrganizationMembership.ts
│ │ │ │ ├── OrganizationSettings.ts
│ │ │ │ ├── PhoneNumber.ts
│ │ │ │ ├── ProxyCheck.ts
│ │ │ │ ├── RedirectUrl.ts
│ │ │ │ ├── SamlAccount.ts
│ │ │ │ ├── SamlConnection.ts
│ │ │ │ ├── Session.ts
│ │ │ │ ├── SignInTokens.ts
│ │ │ │ ├── SignUpAttempt.ts
│ │ │ │ ├── SMSMessage.ts
│ │ │ │ ├── TestingToken.ts
│ │ │ │ ├── Token.ts
│ │ │ │ ├── User.ts
│ │ │ │ ├── Verification.ts
│ │ │ │ ├── WaitlistEntry.ts
│ │ │ │ ├── Web3Wallet.ts
│ │ │ │ └── Webhooks.ts
│ │ │ ├── constants.ts
│ │ │ ├── createRedirect.ts
│ │ │ ├── errors.ts
│ │ │ ├── fixtures
│ │ │ │ ├── index.ts
│ │ │ │ ├── jwks.json
│ │ │ │ ├── machine.ts
│ │ │ │ └── user.json
│ │ │ ├── global.d.ts
│ │ │ ├── index.ts
│ │ │ ├── internal.ts
│ │ │ ├── jwt
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── assertions.test.ts
│ │ │ │ │ ├── cryptoKeys.test.ts
│ │ │ │ │ ├── signJwt.test.ts
│ │ │ │ │ └── verifyJwt.test.ts
│ │ │ │ ├── algorithms.ts
│ │ │ │ ├── assertions.ts
│ │ │ │ ├── cryptoKeys.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── legacyReturn.ts
│ │ │ │ ├── signJwt.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── verifyJwt.ts
│ │ │ ├── mock-server.ts
│ │ │ ├── runtime
│ │ │ │ ├── browser
│ │ │ │ │ └── crypto.mjs
│ │ │ │ └── node
│ │ │ │ ├── crypto.js
│ │ │ │ └── crypto.mjs
│ │ │ ├── runtime.ts
│ │ │ ├── tokens
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── authenticateContext.test.ts
│ │ │ │ │ ├── authObjects.test.ts
│ │ │ │ │ ├── authStatus.test.ts
│ │ │ │ │ ├── clerkRequest.test.ts
│ │ │ │ │ ├── factory.test.ts
│ │ │ │ │ ├── getAuth.test-d.ts
│ │ │ │ │ ├── handshake.test.ts
│ │ │ │ │ ├── keys.test.ts
│ │ │ │ │ ├── machine.test.ts
│ │ │ │ │ ├── organizationMatcher.test.ts
│ │ │ │ │ ├── request.test-d.ts
│ │ │ │ │ ├── request.test.ts
│ │ │ │ │ └── verify.test.ts
│ │ │ │ ├── authenticateContext.ts
│ │ │ │ ├── authObjects.ts
│ │ │ │ ├── authStatus.ts
│ │ │ │ ├── clerkRequest.ts
│ │ │ │ ├── clerkUrl.ts
│ │ │ │ ├── cookie.ts
│ │ │ │ ├── factory.ts
│ │ │ │ ├── handshake.ts
│ │ │ │ ├── keys.ts
│ │ │ │ ├── machine.ts
│ │ │ │ ├── organizationMatcher.ts
│ │ │ │ ├── request.ts
│ │ │ │ ├── tokenTypes.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── verify.ts
│ │ │ ├── util
│ │ │ │ ├── __tests__
│ │ │ │ │ └── path.test.ts
│ │ │ │ ├── decorateObjectWithResources.ts
│ │ │ │ ├── mergePreDefinedOptions.ts
│ │ │ │ ├── optionsAssertions.ts
│ │ │ │ ├── path.ts
│ │ │ │ ├── rfc4648.ts
│ │ │ │ └── shared.ts
│ │ │ └── webhooks.ts
│ │ ├── tsconfig.declarations.json
│ │ ├── tsconfig.json
│ │ ├── tsup.config.ts
│ │ ├── typedoc.json
│ │ ├── vitest.config.mts
│ │ ├── vitest.setup.mts
│ │ └── webhooks
│ │ └── package.json
│ ├── chrome-extension
│ │ ├── .gitignore
│ │ ├── background
│ │ │ └── package.json
│ │ ├── CHANGELOG.md
│ │ ├── docs
│ │ │ ├── clerk-provider.md
│ │ │ ├── manifest.md
│ │ │ └── service-worker.md
│ │ ├── internal
│ │ │ └── package.json
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── react
│ │ │ └── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── __snapshots__
│ │ │ │ │ └── exports.test.ts.snap
│ │ │ │ └── exports.test.ts
│ │ │ ├── background
│ │ │ │ ├── clerk.ts
│ │ │ │ └── index.ts
│ │ │ ├── global.d.ts
│ │ │ ├── index.ts
│ │ │ ├── internal
│ │ │ │ ├── clerk.ts
│ │ │ │ ├── constants.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── utils
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── cookies.test.ts
│ │ │ │ │ ├── manifest.test.ts
│ │ │ │ │ └── storage.test.ts
│ │ │ │ ├── cookies.ts
│ │ │ │ ├── errors.ts
│ │ │ │ ├── jwt-handler.ts
│ │ │ │ ├── manifest.ts
│ │ │ │ ├── request-handler.ts
│ │ │ │ ├── response-handler.ts
│ │ │ │ └── storage.ts
│ │ │ ├── react
│ │ │ │ ├── ClerkProvider.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── NotSupported.tsx
│ │ │ │ └── re-exports.ts
│ │ │ └── types.ts
│ │ ├── tsconfig.declarations.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.test.json
│ │ ├── tsup.config.ts
│ │ ├── vitest.config.mts
│ │ └── vitest.setup.mts
│ ├── clerk-js
│ │ ├── .gitignore
│ │ ├── bundle-check.mjs
│ │ ├── bundlewatch-fix.mjs
│ │ ├── bundlewatch.config.json
│ │ ├── CHANGELOG.md
│ │ ├── clerk.png
│ │ ├── docs
│ │ │ └── events.md
│ │ ├── headless
│ │ │ ├── index.d.ts
│ │ │ └── index.js
│ │ ├── LICENSE
│ │ ├── no-rhc
│ │ │ ├── index.d.ts
│ │ │ └── index.js
│ │ ├── package.json
│ │ ├── playwright.config.ts
│ │ ├── README.md
│ │ ├── rspack.config.js
│ │ ├── sandbox
│ │ │ ├── app.ts
│ │ │ ├── integration
│ │ │ │ ├── create-organization.spec.ts
│ │ │ │ ├── create-organization.spec.ts-snapshots
│ │ │ │ │ └── create-organization-chromium-darwin.png
│ │ │ │ ├── global.setup.ts
│ │ │ │ ├── helpers.ts
│ │ │ │ ├── oauth-consent.spec.ts
│ │ │ │ ├── oauth-consent.spec.ts-snapshots
│ │ │ │ │ └── oauth-consent-chromium-darwin.png
│ │ │ │ ├── org-switcher.spec.ts
│ │ │ │ ├── org-switcher.spec.ts-snapshots
│ │ │ │ │ ├── organization-switcher-action-hover-chromium-darwin.png
│ │ │ │ │ └── organization-switcher-popover-chromium-darwin.png
│ │ │ │ ├── organization-list.spec.ts
│ │ │ │ ├── organization-list.spec.ts-snapshots
│ │ │ │ │ └── organization-list-chromium-darwin.png
│ │ │ │ ├── sign-in-modal.spec.ts
│ │ │ │ ├── sign-in-modal.spec.ts-snapshots
│ │ │ │ │ └── sign-in-modal-chromium-darwin.png
│ │ │ │ ├── sign-in.spec.ts
│ │ │ │ ├── sign-in.spec.ts-snapshots
│ │ │ │ │ ├── sign-in-action-link-hover-chromium-darwin.png
│ │ │ │ │ ├── sign-in-chromium-darwin.png
│ │ │ │ │ ├── sign-in-primary-button-hover-chromium-darwin.png
│ │ │ │ │ └── sign-in-secondary-button-hover-chromium-darwin.png
│ │ │ │ ├── sign-up.spec.ts
│ │ │ │ ├── sign-up.spec.ts-snapshots
│ │ │ │ │ └── sign-up-chromium-darwin.png
│ │ │ │ ├── user-button.spec.ts
│ │ │ │ ├── user-button.spec.ts-snapshots
│ │ │ │ │ └── user-button-popover-chromium-darwin.png
│ │ │ │ ├── user-profile.spec.ts
│ │ │ │ └── user-profile.spec.ts-snapshots
│ │ │ │ └── user-profile-chromium-darwin.png
│ │ │ └── template.html
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ └── headless.test.ts
│ │ │ ├── core
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── clerk.redirects.test.ts
│ │ │ │ │ ├── clerk.test.ts
│ │ │ │ │ ├── fapiClient.test.ts
│ │ │ │ │ └── tokenCache.test.ts
│ │ │ │ ├── auth
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ ├── cookieSuffix.test.ts
│ │ │ │ │ │ ├── devBrowser.test.ts
│ │ │ │ │ │ ├── getCookieDomain.test.ts
│ │ │ │ │ │ └── getSecureAttribute.test.ts
│ │ │ │ │ ├── AuthCookieService.ts
│ │ │ │ │ ├── CaptchaHeartbeat.ts
│ │ │ │ │ ├── cookies
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ ├── clientUat.test.ts
│ │ │ │ │ │ │ └── session.test.ts
│ │ │ │ │ │ ├── activeContext.ts
│ │ │ │ │ │ ├── clientUat.ts
│ │ │ │ │ │ ├── devBrowser.ts
│ │ │ │ │ │ └── session.ts
│ │ │ │ │ ├── cookieSuffix.ts
│ │ │ │ │ ├── devBrowser.ts
│ │ │ │ │ ├── getCookieDomain.ts
│ │ │ │ │ ├── getSecureAttribute.ts
│ │ │ │ │ ├── safeLock.ts
│ │ │ │ │ └── SessionCookiePoller.ts
│ │ │ │ ├── clerk.ts
│ │ │ │ ├── constants.ts
│ │ │ │ ├── errors.ts
│ │ │ │ ├── events.ts
│ │ │ │ ├── fapiClient.ts
│ │ │ │ ├── fraudProtection.test.ts
│ │ │ │ ├── fraudProtection.ts
│ │ │ │ ├── jwt-client.ts
│ │ │ │ ├── modules
│ │ │ │ │ ├── apiKeys
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── billing
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── namespace.ts
│ │ │ │ │ │ └── payment-source-methods.ts
│ │ │ │ │ ├── checkout
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── manager.test.ts
│ │ │ │ │ │ ├── instance.ts
│ │ │ │ │ │ └── manager.ts
│ │ │ │ │ └── debug
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ └── logger.test.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── logger.ts
│ │ │ │ │ ├── transports
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── telemetry.test.ts
│ │ │ │ │ │ ├── composite.ts
│ │ │ │ │ │ ├── console.ts
│ │ │ │ │ │ └── telemetry.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── query-core.ts
│ │ │ │ ├── resources
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ ├── AuthConfig.test.ts
│ │ │ │ │ │ ├── Base.test.ts
│ │ │ │ │ │ ├── Client.test.ts
│ │ │ │ │ │ ├── Environment.test.ts
│ │ │ │ │ │ ├── ExternalAccount.test.ts
│ │ │ │ │ │ ├── Image.test.ts
│ │ │ │ │ │ ├── Organization.test.ts
│ │ │ │ │ │ ├── OrganizationDomain.test.ts
│ │ │ │ │ │ ├── OrganizationInvitation.test.ts
│ │ │ │ │ │ ├── OrganizationMembership.test.ts
│ │ │ │ │ │ ├── OrganizationMembershipRequest.test.ts
│ │ │ │ │ │ ├── OrganizationSuggestion.test.ts
│ │ │ │ │ │ ├── PublicUserData.test.ts
│ │ │ │ │ │ ├── Session.test.ts
│ │ │ │ │ │ ├── SignIn.test.ts
│ │ │ │ │ │ ├── Token.test.ts
│ │ │ │ │ │ ├── User.test.ts
│ │ │ │ │ │ ├── UserSettings.test.ts
│ │ │ │ │ │ ├── Waitlist.test.ts
│ │ │ │ │ │ └── Web3Wallet.test.ts
│ │ │ │ │ ├── APIKey.ts
│ │ │ │ │ ├── APIKeySettings.ts
│ │ │ │ │ ├── AuthConfig.ts
│ │ │ │ │ ├── BackupCode.ts
│ │ │ │ │ ├── Base.ts
│ │ │ │ │ ├── BillingCheckout.ts
│ │ │ │ │ ├── BillingPayer.ts
│ │ │ │ │ ├── BillingPayment.ts
│ │ │ │ │ ├── BillingPaymentMethod.ts
│ │ │ │ │ ├── BillingPlan.ts
│ │ │ │ │ ├── BillingStatement.ts
│ │ │ │ │ ├── BillingSubscription.ts
│ │ │ │ │ ├── Client.ts
│ │ │ │ │ ├── CommerceSettings.ts
│ │ │ │ │ ├── DeletedObject.ts
│ │ │ │ │ ├── DisplayConfig.ts
│ │ │ │ │ ├── EmailAddress.ts
│ │ │ │ │ ├── EnterpriseAccount.ts
│ │ │ │ │ ├── Environment.ts
│ │ │ │ │ ├── ExternalAccount.ts
│ │ │ │ │ ├── Feature.ts
│ │ │ │ │ ├── IdentificationLink.ts
│ │ │ │ │ ├── Image.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── internal.ts
│ │ │ │ │ ├── Organization.ts
│ │ │ │ │ ├── OrganizationDomain.ts
│ │ │ │ │ ├── OrganizationInvitation.ts
│ │ │ │ │ ├── OrganizationMembership.ts
│ │ │ │ │ ├── OrganizationMembershipRequest.ts
│ │ │ │ │ ├── OrganizationSettings.ts
│ │ │ │ │ ├── OrganizationSuggestion.ts
│ │ │ │ │ ├── Passkey.ts
│ │ │ │ │ ├── Permission.ts
│ │ │ │ │ ├── PhoneNumber.ts
│ │ │ │ │ ├── PublicUserData.ts
│ │ │ │ │ ├── Role.ts
│ │ │ │ │ ├── SamlAccount.ts
│ │ │ │ │ ├── Session.ts
│ │ │ │ │ ├── SessionVerification.ts
│ │ │ │ │ ├── SessionWithActivities.ts
│ │ │ │ │ ├── SignIn.ts
│ │ │ │ │ ├── SignUp.ts
│ │ │ │ │ ├── Token.ts
│ │ │ │ │ ├── TOTP.ts
│ │ │ │ │ ├── User.ts
│ │ │ │ │ ├── UserData.ts
│ │ │ │ │ ├── UserOrganizationInvitation.ts
│ │ │ │ │ ├── UserSettings.ts
│ │ │ │ │ ├── Verification.ts
│ │ │ │ │ ├── Waitlist.ts
│ │ │ │ │ └── Web3Wallet.ts
│ │ │ │ ├── sessionTasks.ts
│ │ │ │ ├── signals.ts
│ │ │ │ ├── state.ts
│ │ │ │ ├── tokenCache.ts
│ │ │ │ └── warnings.ts
│ │ │ ├── emotion.d.ts
│ │ │ ├── global.d.ts
│ │ │ ├── index.browser.ts
│ │ │ ├── index.headless.browser.ts
│ │ │ ├── index.headless.ts
│ │ │ ├── index.legacy.browser.ts
│ │ │ ├── index.ts
│ │ │ ├── test
│ │ │ │ ├── core-fixtures.ts
│ │ │ │ ├── create-fixtures.tsx
│ │ │ │ ├── fixture-helpers.ts
│ │ │ │ ├── fixtures.ts
│ │ │ │ ├── mock-helpers.ts
│ │ │ │ └── utils.ts
│ │ │ ├── ui
│ │ │ │ ├── baseTheme.ts
│ │ │ │ ├── common
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ ├── redirects.test.ts
│ │ │ │ │ │ ├── verification.test.ts
│ │ │ │ │ │ └── withRedirect.test.tsx
│ │ │ │ │ ├── CalloutWithAction.tsx
│ │ │ │ │ ├── ChooseEnterpriseConnectionCard.tsx
│ │ │ │ │ ├── constants.ts
│ │ │ │ │ ├── CustomPageContentContainer.tsx
│ │ │ │ │ ├── DevOnly.tsx
│ │ │ │ │ ├── EmailLinkCompleteFlowCard.tsx
│ │ │ │ │ ├── EmailLinkStatusCard.tsx
│ │ │ │ │ ├── EmailLinkVerify.tsx
│ │ │ │ │ ├── forms.ts
│ │ │ │ │ ├── Gate.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── InfiniteListSpinner.tsx
│ │ │ │ │ ├── NotificationCountBadge.tsx
│ │ │ │ │ ├── organizations
│ │ │ │ │ │ └── OrganizationPreview.tsx
│ │ │ │ │ ├── PrintableComponent.tsx
│ │ │ │ │ ├── ProviderInitialIcon.tsx
│ │ │ │ │ ├── QRCode.tsx
│ │ │ │ │ ├── redirects.ts
│ │ │ │ │ ├── RemoveResourceForm.tsx
│ │ │ │ │ ├── SSOCallback.tsx
│ │ │ │ │ ├── verification.ts
│ │ │ │ │ ├── withRedirect.tsx
│ │ │ │ │ └── Wizard.tsx
│ │ │ │ ├── components
│ │ │ │ │ ├── ApiKeys
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── ApiKeys.spec.tsx
│ │ │ │ │ │ ├── APIKeyModal.tsx
│ │ │ │ │ │ ├── ApiKeys.tsx
│ │ │ │ │ │ ├── APIKeysTable.tsx
│ │ │ │ │ │ ├── CopyAPIKeyModal.tsx
│ │ │ │ │ │ ├── CreateAPIKeyForm.tsx
│ │ │ │ │ │ ├── RevokeAPIKeyConfirmationModal.tsx
│ │ │ │ │ │ └── utils.ts
│ │ │ │ │ ├── BlankCaptchaModal
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Checkout
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── Checkout.test.tsx
│ │ │ │ │ │ ├── CheckoutComplete.tsx
│ │ │ │ │ │ ├── CheckoutForm.tsx
│ │ │ │ │ │ ├── CheckoutPage.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── parts.tsx
│ │ │ │ │ ├── CreateOrganization
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── CreateOrganization.test.tsx
│ │ │ │ │ │ ├── CreateOrganization.tsx
│ │ │ │ │ │ ├── CreateOrganizationForm.tsx
│ │ │ │ │ │ ├── CreateOrganizationPage.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── GoogleOneTap
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── one-tap-start.tsx
│ │ │ │ │ ├── ImpersonationFab
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── KeylessPrompt
│ │ │ │ │ │ ├── ClerkLogoIcon.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── KeySlashIcon.tsx
│ │ │ │ │ │ └── use-revalidate-environment.ts
│ │ │ │ │ ├── OAuthConsent
│ │ │ │ │ │ └── OAuthConsent.tsx
│ │ │ │ │ ├── OrganizationList
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── OrganizationList.test.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── OrganizationListPage.tsx
│ │ │ │ │ │ ├── shared.tsx
│ │ │ │ │ │ ├── UserInvitationList.tsx
│ │ │ │ │ │ ├── UserMembershipList.tsx
│ │ │ │ │ │ ├── UserSuggestionList.tsx
│ │ │ │ │ │ └── utils.ts
│ │ │ │ │ ├── OrganizationProfile
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ ├── InviteMembersPage.test.tsx
│ │ │ │ │ │ │ ├── LeaveOrganizationPage.test.tsx
│ │ │ │ │ │ │ ├── OrganizationGeneralPage.test.tsx
│ │ │ │ │ │ │ ├── OrganizationMembers.test.tsx
│ │ │ │ │ │ │ ├── OrganizationProfile.test.tsx
│ │ │ │ │ │ │ ├── ProfileSettingsPage.test.tsx
│ │ │ │ │ │ │ └── utils.ts
│ │ │ │ │ │ ├── ActionConfirmationPage.tsx
│ │ │ │ │ │ ├── ActiveMembersList.tsx
│ │ │ │ │ │ ├── AddDomainForm.tsx
│ │ │ │ │ │ ├── BillingWidget.tsx
│ │ │ │ │ │ ├── DomainList.tsx
│ │ │ │ │ │ ├── EnrollmentBadge.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── InvitedMembersList.tsx
│ │ │ │ │ │ ├── InviteMembersForm.tsx
│ │ │ │ │ │ ├── InviteMembersScreen.tsx
│ │ │ │ │ │ ├── MemberListTable.tsx
│ │ │ │ │ │ ├── MembersActions.tsx
│ │ │ │ │ │ ├── MembershipWidget.tsx
│ │ │ │ │ │ ├── MembersSearch.tsx
│ │ │ │ │ │ ├── OrganizationApiKeysPage.tsx
│ │ │ │ │ │ ├── OrganizationBillingPage.tsx
│ │ │ │ │ │ ├── OrganizationGeneralPage.tsx
│ │ │ │ │ │ ├── OrganizationMembers.tsx
│ │ │ │ │ │ ├── OrganizationMembersTabInvitations.tsx
│ │ │ │ │ │ ├── OrganizationMembersTabRequests.tsx
│ │ │ │ │ │ ├── OrganizationPaymentAttemptPage.tsx
│ │ │ │ │ │ ├── OrganizationPlansPage.tsx
│ │ │ │ │ │ ├── OrganizationProfileAvatarUploader.tsx
│ │ │ │ │ │ ├── OrganizationProfileNavbar.tsx
│ │ │ │ │ │ ├── OrganizationProfileRoutes.tsx
│ │ │ │ │ │ ├── OrganizationStatementPage.tsx
│ │ │ │ │ │ ├── ProfileForm.tsx
│ │ │ │ │ │ ├── RemoveDomainForm.tsx
│ │ │ │ │ │ ├── RemoveDomainScreen.tsx
│ │ │ │ │ │ ├── RequestToJoinList.tsx
│ │ │ │ │ │ ├── VerifiedDomainForm.tsx
│ │ │ │ │ │ ├── VerifiedDomainScreen.tsx
│ │ │ │ │ │ ├── VerifyDomainForm.tsx
│ │ │ │ │ │ └── VerifyDomainScreen.tsx
│ │ │ │ │ ├── OrganizationSwitcher
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ ├── OrganizationSwitcher.test.tsx
│ │ │ │ │ │ │ ├── test-utils.ts
│ │ │ │ │ │ │ └── utils.test.ts
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── OrganizationSwitcherPopover.tsx
│ │ │ │ │ │ ├── OrganizationSwitcherTrigger.tsx
│ │ │ │ │ │ ├── OtherOrganizationActions.tsx
│ │ │ │ │ │ ├── UserInvitationSuggestionList.tsx
│ │ │ │ │ │ ├── UserMembershipList.tsx
│ │ │ │ │ │ └── utils.ts
│ │ │ │ │ ├── PaymentAttempts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── PaymentAttemptPage.tsx
│ │ │ │ │ │ └── PaymentAttemptsList.tsx
│ │ │ │ │ ├── PaymentMethods
│ │ │ │ │ │ ├── AddPaymentMethod.tsx
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── PaymentElementSkeleton.tsx
│ │ │ │ │ │ ├── PaymentMethodRow.tsx
│ │ │ │ │ │ ├── PaymentMethods.tsx
│ │ │ │ │ │ └── TestPaymentMethod.tsx
│ │ │ │ │ ├── Plans
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── PlanDetails.test.tsx
│ │ │ │ │ │ └── PlanDetails.tsx
│ │ │ │ │ ├── prefetch-organization-list.tsx
│ │ │ │ │ ├── PricingTable
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── PricingTable.test.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── PricingTable.tsx
│ │ │ │ │ │ ├── PricingTableDefault.tsx
│ │ │ │ │ │ ├── PricingTableMatrix.tsx
│ │ │ │ │ │ └── utils
│ │ │ │ │ │ ├── pricing-footer-state.spec.ts
│ │ │ │ │ │ └── pricing-footer-state.ts
│ │ │ │ │ ├── SessionTasks
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── tasks
│ │ │ │ │ │ ├── TaskChooseOrganization
│ │ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ │ └── TaskChooseOrganization.test.tsx
│ │ │ │ │ │ │ ├── ChooseOrganizationScreen.tsx
│ │ │ │ │ │ │ ├── CreateOrganizationScreen.tsx
│ │ │ │ │ │ │ └── index.tsx
│ │ │ │ │ │ └── withTaskGuard.ts
│ │ │ │ │ ├── SignIn
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ ├── handleCombinedFlowTransfer.test.ts
│ │ │ │ │ │ │ ├── ResetPassword.test.tsx
│ │ │ │ │ │ │ ├── ResetPasswordSuccess.test.tsx
│ │ │ │ │ │ │ ├── SignInAccountSwitcher.test.tsx
│ │ │ │ │ │ │ ├── SignInFactorOne.test.tsx
│ │ │ │ │ │ │ ├── SignInFactorOneCodeForm.test.tsx
│ │ │ │ │ │ │ ├── SignInFactorTwo.test.tsx
│ │ │ │ │ │ │ ├── SignInStart.test.tsx
│ │ │ │ │ │ │ └── utils.test.ts
│ │ │ │ │ │ ├── AlternativeMethods.tsx
│ │ │ │ │ │ ├── handleCombinedFlowTransfer.ts
│ │ │ │ │ │ ├── HavingTrouble.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── lazy-sign-up.ts
│ │ │ │ │ │ ├── ResetPassword.tsx
│ │ │ │ │ │ ├── ResetPasswordSuccess.tsx
│ │ │ │ │ │ ├── shared.ts
│ │ │ │ │ │ ├── SignInAccountSwitcher.tsx
│ │ │ │ │ │ ├── SignInAlternativePhoneCodePhoneNumberCard.tsx
│ │ │ │ │ │ ├── SignInFactorOne.tsx
│ │ │ │ │ │ ├── SignInFactorOneAlternativeChannelCodeForm.tsx
│ │ │ │ │ │ ├── SignInFactorOneAlternativePhoneCodeCard.tsx
│ │ │ │ │ │ ├── SignInFactorOneCodeForm.tsx
│ │ │ │ │ │ ├── SignInFactorOneEmailCodeCard.tsx
│ │ │ │ │ │ ├── SignInFactorOneEmailLinkCard.tsx
│ │ │ │ │ │ ├── SignInFactorOneEnterpriseConnections.tsx
│ │ │ │ │ │ ├── SignInFactorOneForgotPasswordCard.tsx
│ │ │ │ │ │ ├── SignInFactorOnePasskey.tsx
│ │ │ │ │ │ ├── SignInFactorOnePasswordCard.tsx
│ │ │ │ │ │ ├── SignInFactorOnePhoneCodeCard.tsx
│ │ │ │ │ │ ├── SignInFactorTwo.tsx
│ │ │ │ │ │ ├── SignInFactorTwoAlternativeMethods.tsx
│ │ │ │ │ │ ├── SignInFactorTwoBackupCodeCard.tsx
│ │ │ │ │ │ ├── SignInFactorTwoCodeForm.tsx
│ │ │ │ │ │ ├── SignInFactorTwoPhoneCodeCard.tsx
│ │ │ │ │ │ ├── SignInFactorTwoTOTPCard.tsx
│ │ │ │ │ │ ├── SignInSocialButtons.tsx
│ │ │ │ │ │ ├── SignInSSOCallback.tsx
│ │ │ │ │ │ ├── SignInStart.tsx
│ │ │ │ │ │ ├── useResetPasswordFactor.tsx
│ │ │ │ │ │ ├── utils.ts
│ │ │ │ │ │ └── withHavingTrouble.tsx
│ │ │ │ │ ├── SignUp
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ ├── SignUpContinue.test.tsx
│ │ │ │ │ │ │ ├── SignUpEmailLinkFlowComplete.test.tsx
│ │ │ │ │ │ │ ├── signUpFormHelpers.test.ts
│ │ │ │ │ │ │ ├── SignUpStart.test.tsx
│ │ │ │ │ │ │ ├── SignUpVerifyEmail.test.tsx
│ │ │ │ │ │ │ └── SignUpVerifyPhone.test.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── SignUpContinue.tsx
│ │ │ │ │ │ ├── SignUpEmailCodeCard.tsx
│ │ │ │ │ │ ├── SignUpEmailLinkCard.tsx
│ │ │ │ │ │ ├── SignUpEnterpriseConnections.tsx
│ │ │ │ │ │ ├── SignUpForm.tsx
│ │ │ │ │ │ ├── signUpFormHelpers.ts
│ │ │ │ │ │ ├── SignUpPhoneCodeCard.tsx
│ │ │ │ │ │ ├── SignUpRestrictedAccess.tsx
│ │ │ │ │ │ ├── SignUpSocialButtons.tsx
│ │ │ │ │ │ ├── SignUpSSOCallback.tsx
│ │ │ │ │ │ ├── SignUpStart.tsx
│ │ │ │ │ │ ├── SignUpStartAlternativePhoneCodePhoneNumberCard.tsx
│ │ │ │ │ │ ├── SignUpVerificationCodeForm.tsx
│ │ │ │ │ │ ├── SignUpVerifyEmail.tsx
│ │ │ │ │ │ ├── SignUpVerifyPhone.tsx
│ │ │ │ │ │ └── util.ts
│ │ │ │ │ ├── Statements
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── Statement.tsx
│ │ │ │ │ │ ├── StatementPage.tsx
│ │ │ │ │ │ └── StatementsList.tsx
│ │ │ │ │ ├── SubscriptionDetails
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── SubscriptionDetails.test.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Subscriptions
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── SubscriptionsList.test.tsx
│ │ │ │ │ │ ├── badge.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── SubscriptionsList.tsx
│ │ │ │ │ ├── UserAvatar
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── UserButton
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── UserButton.test.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── SessionActions.tsx
│ │ │ │ │ │ ├── useMultisessionActions.tsx
│ │ │ │ │ │ ├── UserButtonPopover.tsx
│ │ │ │ │ │ ├── UserButtonTopLevelIdentifier.tsx
│ │ │ │ │ │ └── UserButtonTrigger.tsx
│ │ │ │ │ ├── UserProfile
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ ├── AccountPage.test.tsx
│ │ │ │ │ │ │ ├── ConnectedAccountsSection.test.tsx
│ │ │ │ │ │ │ ├── EmailsSection.test.tsx
│ │ │ │ │ │ │ ├── EnterpriseAccountsSection.test.tsx
│ │ │ │ │ │ │ ├── MfaPage.test.tsx
│ │ │ │ │ │ │ ├── PasskeysSection.test.tsx
│ │ │ │ │ │ │ ├── PasswordSection.test.tsx
│ │ │ │ │ │ │ ├── PhoneSection.test.tsx
│ │ │ │ │ │ │ ├── SecurityPage.test.tsx
│ │ │ │ │ │ │ ├── UsernameSection.test.tsx
│ │ │ │ │ │ │ ├── UserProfile.test.tsx
│ │ │ │ │ │ │ ├── UserProfileSection.test.tsx
│ │ │ │ │ │ │ └── utils.test.ts
│ │ │ │ │ │ ├── AccountPage.tsx
│ │ │ │ │ │ ├── ActiveDevicesSection.tsx
│ │ │ │ │ │ ├── AddAuthenticatorApp.tsx
│ │ │ │ │ │ ├── ApiKeysPage.tsx
│ │ │ │ │ │ ├── BillingPage.tsx
│ │ │ │ │ │ ├── ConnectedAccountsMenu.tsx
│ │ │ │ │ │ ├── ConnectedAccountsSection.tsx
│ │ │ │ │ │ ├── DeleteSection.tsx
│ │ │ │ │ │ ├── DeleteUserForm.tsx
│ │ │ │ │ │ ├── EmailForm.tsx
│ │ │ │ │ │ ├── EmailsSection.tsx
│ │ │ │ │ │ ├── EnterpriseAccountsSection.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── LinkButtonWithDescription.tsx
│ │ │ │ │ │ ├── MfaBackupCodeCreateForm.tsx
│ │ │ │ │ │ ├── MfaBackupCodeList.tsx
│ │ │ │ │ │ ├── MfaBackupCodeScreen.tsx
│ │ │ │ │ │ ├── MfaBackupCodeTile.tsx
│ │ │ │ │ │ ├── MfaForm.tsx
│ │ │ │ │ │ ├── MfaPhoneCodeScreen.tsx
│ │ │ │ │ │ ├── MfaScreens.tsx
│ │ │ │ │ │ ├── MfaSection.tsx
│ │ │ │ │ │ ├── MfaTOTPScreen.tsx
│ │ │ │ │ │ ├── PasskeySection.tsx
│ │ │ │ │ │ ├── PasswordForm.tsx
│ │ │ │ │ │ ├── PasswordSection.tsx
│ │ │ │ │ │ ├── PhoneForm.tsx
│ │ │ │ │ │ ├── PhoneSection.tsx
│ │ │ │ │ │ ├── PlansPage.tsx
│ │ │ │ │ │ ├── ProfileForm.tsx
│ │ │ │ │ │ ├── RemoveResourceForm.tsx
│ │ │ │ │ │ ├── SecurityPage.tsx
│ │ │ │ │ │ ├── UsernameForm.tsx
│ │ │ │ │ │ ├── UsernameSection.tsx
│ │ │ │ │ │ ├── UserProfileAvatarUploader.tsx
│ │ │ │ │ │ ├── UserProfileNavbar.tsx
│ │ │ │ │ │ ├── UserProfileRoutes.tsx
│ │ │ │ │ │ ├── UserProfileSection.tsx
│ │ │ │ │ │ ├── utils.ts
│ │ │ │ │ │ ├── VerifyTOTP.tsx
│ │ │ │ │ │ ├── VerifyWithCode.tsx
│ │ │ │ │ │ ├── VerifyWithEnterpriseConnection.tsx
│ │ │ │ │ │ ├── VerifyWithLink.tsx
│ │ │ │ │ │ ├── Web3Form.tsx
│ │ │ │ │ │ └── Web3Section.tsx
│ │ │ │ │ ├── UserVerification
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ ├── UVFactorOne.test.tsx
│ │ │ │ │ │ │ └── UVFactorTwo.test.tsx
│ │ │ │ │ │ ├── AlternativeMethods.tsx
│ │ │ │ │ │ ├── HavingTrouble.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── use-after-verification.ts
│ │ │ │ │ │ ├── useReverificationAlternativeStrategies.ts
│ │ │ │ │ │ ├── UserVerificationFactorOne.tsx
│ │ │ │ │ │ ├── UserVerificationFactorOnePassword.tsx
│ │ │ │ │ │ ├── UserVerificationFactorTwo.tsx
│ │ │ │ │ │ ├── UserVerificationFactorTwoTOTP.tsx
│ │ │ │ │ │ ├── useUserVerificationSession.tsx
│ │ │ │ │ │ ├── utils.ts
│ │ │ │ │ │ ├── UVFactorOneCodeForm.tsx
│ │ │ │ │ │ ├── UVFactorOneEmailCodeCard.tsx
│ │ │ │ │ │ ├── UVFactorOnePasskeysCard.tsx
│ │ │ │ │ │ ├── UVFactorOnePhoneCodeCard.tsx
│ │ │ │ │ │ ├── UVFactorTwoAlternativeMethods.tsx
│ │ │ │ │ │ ├── UVFactorTwoBackupCodeCard.tsx
│ │ │ │ │ │ ├── UVFactorTwoCodeForm.tsx
│ │ │ │ │ │ ├── UVFactorTwoPhoneCodeCard.tsx
│ │ │ │ │ │ └── withHavingTrouble.tsx
│ │ │ │ │ └── Waitlist
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ └── Waitlist.test.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── WaitlistForm.tsx
│ │ │ │ │ └── waitlistFormHelpers.ts
│ │ │ │ ├── Components.tsx
│ │ │ │ ├── constants.ts
│ │ │ │ ├── contexts
│ │ │ │ │ ├── AcceptedUserInvitations.tsx
│ │ │ │ │ ├── ClerkUIComponentsContext.tsx
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── ApiKeys.ts
│ │ │ │ │ │ ├── Checkout.ts
│ │ │ │ │ │ ├── CreateOrganization.ts
│ │ │ │ │ │ ├── GoogleOneTap.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── OAuthConsent.ts
│ │ │ │ │ │ ├── OrganizationList.ts
│ │ │ │ │ │ ├── OrganizationProfile.ts
│ │ │ │ │ │ ├── OrganizationSwitcher.ts
│ │ │ │ │ │ ├── Plans.tsx
│ │ │ │ │ │ ├── PricingTable.ts
│ │ │ │ │ │ ├── SessionTasks.ts
│ │ │ │ │ │ ├── SignIn.ts
│ │ │ │ │ │ ├── SignOut.ts
│ │ │ │ │ │ ├── SignUp.ts
│ │ │ │ │ │ ├── SubscriberType.ts
│ │ │ │ │ │ ├── SubscriptionDetails.ts
│ │ │ │ │ │ ├── UserAvatar.ts
│ │ │ │ │ │ ├── UserButton.ts
│ │ │ │ │ │ ├── UserProfile.ts
│ │ │ │ │ │ ├── UserVerification.ts
│ │ │ │ │ │ └── Waitlist.ts
│ │ │ │ │ ├── CoreClerkContextWrapper.tsx
│ │ │ │ │ ├── CoreClientContext.tsx
│ │ │ │ │ ├── CoreSessionContext.tsx
│ │ │ │ │ ├── CoreUserContext.tsx
│ │ │ │ │ ├── EnvironmentContext.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── OptionsContext.tsx
│ │ │ │ │ └── utils.ts
│ │ │ │ ├── customizables
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ ├── elementDescriptors.test.tsx
│ │ │ │ │ │ ├── FlowRoot.spec.tsx
│ │ │ │ │ │ ├── makeCustomizable.test.tsx
│ │ │ │ │ │ ├── parseAppearance.test.tsx
│ │ │ │ │ │ ├── parseVariables.test.ts
│ │ │ │ │ │ └── test-utils.ts
│ │ │ │ │ ├── AppearanceContext.tsx
│ │ │ │ │ ├── classGeneration.ts
│ │ │ │ │ ├── elementDescriptors.ts
│ │ │ │ │ ├── Flow.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── makeCustomizable.tsx
│ │ │ │ │ ├── makeResponsive.tsx
│ │ │ │ │ ├── parseAppearance.ts
│ │ │ │ │ ├── parseVariables.ts
│ │ │ │ │ └── sanitizeDomProps.tsx
│ │ │ │ ├── elements
│ │ │ │ │ ├── __mocks__
│ │ │ │ │ │ └── @formkit
│ │ │ │ │ │ └── auto-animate
│ │ │ │ │ │ └── react
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ ├── CodeControl.test.tsx
│ │ │ │ │ │ ├── LinkRenderer.test.tsx
│ │ │ │ │ │ ├── PlainInput.test.tsx
│ │ │ │ │ │ ├── RadioGroup.test.tsx
│ │ │ │ │ │ └── TimerButton.test.tsx
│ │ │ │ │ ├── Action
│ │ │ │ │ │ ├── ActionCard.tsx
│ │ │ │ │ │ ├── ActionClosed.tsx
│ │ │ │ │ │ ├── ActionOpen.tsx
│ │ │ │ │ │ ├── ActionRoot.tsx
│ │ │ │ │ │ ├── ActionTrigger.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Actions.tsx
│ │ │ │ │ ├── Alert.tsx
│ │ │ │ │ ├── Animated.tsx
│ │ │ │ │ ├── ApplicationLogo.tsx
│ │ │ │ │ ├── ArrowBlockButton.tsx
│ │ │ │ │ ├── Avatar.tsx
│ │ │ │ │ ├── AvatarUploader.tsx
│ │ │ │ │ ├── BackLink.tsx
│ │ │ │ │ ├── Badge.tsx
│ │ │ │ │ ├── CaptchaElement.tsx
│ │ │ │ │ ├── Card
│ │ │ │ │ │ ├── CardAction.tsx
│ │ │ │ │ │ ├── CardAlert.tsx
│ │ │ │ │ │ ├── CardClerkAndPagesTag.tsx
│ │ │ │ │ │ ├── CardContent.tsx
│ │ │ │ │ │ ├── CardFooter.tsx
│ │ │ │ │ │ ├── CardRoot.tsx
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── ClipboardInput.tsx
│ │ │ │ │ ├── CodeControl.tsx
│ │ │ │ │ ├── contexts
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── DataTable.tsx
│ │ │ │ │ ├── DevModeNotice.tsx
│ │ │ │ │ ├── Divider.tsx
│ │ │ │ │ ├── Drawer.tsx
│ │ │ │ │ ├── ErrorCard.tsx
│ │ │ │ │ ├── FieldControl.tsx
│ │ │ │ │ ├── Form.tsx
│ │ │ │ │ ├── FormattedPhoneNumber.tsx
│ │ │ │ │ ├── FormButtons.tsx
│ │ │ │ │ ├── FormContainer.tsx
│ │ │ │ │ ├── FormControl.tsx
│ │ │ │ │ ├── FullHeightLoader.tsx
│ │ │ │ │ ├── Gauge.tsx
│ │ │ │ │ ├── Header.tsx
│ │ │ │ │ ├── IconButton.tsx
│ │ │ │ │ ├── IconCircle.tsx
│ │ │ │ │ ├── IdentityPreview.tsx
│ │ │ │ │ ├── InformationBox.tsx
│ │ │ │ │ ├── InputGroup.tsx
│ │ │ │ │ ├── InputWithIcon.tsx
│ │ │ │ │ ├── InvisibleRootBox.tsx
│ │ │ │ │ ├── LegalConsentCheckbox.tsx
│ │ │ │ │ ├── LineItems.tsx
│ │ │ │ │ ├── LinkRenderer.tsx
│ │ │ │ │ ├── LoadingCard.tsx
│ │ │ │ │ ├── Menu.tsx
│ │ │ │ │ ├── Modal.tsx
│ │ │ │ │ ├── Navbar.tsx
│ │ │ │ │ ├── OrganizationAvatar.tsx
│ │ │ │ │ ├── OrganizationPreview.tsx
│ │ │ │ │ ├── Pagination.tsx
│ │ │ │ │ ├── PasswordInput.tsx
│ │ │ │ │ ├── PersonalWorkspacePreview.tsx
│ │ │ │ │ ├── PhoneInput
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── useFormattedPhoneNumber.test.ts
│ │ │ │ │ │ ├── countryCodeData.ts
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── useFormattedPhoneNumber.ts
│ │ │ │ │ ├── Popover.tsx
│ │ │ │ │ ├── PopoverCard.tsx
│ │ │ │ │ ├── Portal.tsx
│ │ │ │ │ ├── PreviewButton.tsx
│ │ │ │ │ ├── ProfileCard
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── ProfileCardContent.tsx
│ │ │ │ │ │ └── ProfileCardRoot.tsx
│ │ │ │ │ ├── RadioGroup.tsx
│ │ │ │ │ ├── ReversibleContainer.tsx
│ │ │ │ │ ├── RootBox.tsx
│ │ │ │ │ ├── RouterLink.tsx
│ │ │ │ │ ├── Section.tsx
│ │ │ │ │ ├── SegmentedControl.tsx
│ │ │ │ │ ├── Select.tsx
│ │ │ │ │ ├── SocialButtons.tsx
│ │ │ │ │ ├── SuccessPage.tsx
│ │ │ │ │ ├── Switch.tsx
│ │ │ │ │ ├── Tabs.tsx
│ │ │ │ │ ├── TagInput.tsx
│ │ │ │ │ ├── ThreeDotsMenu.tsx
│ │ │ │ │ ├── TimerButton.tsx
│ │ │ │ │ ├── Tooltip.tsx
│ │ │ │ │ ├── UserAvatar.tsx
│ │ │ │ │ ├── UserPreview.tsx
│ │ │ │ │ ├── utils.ts
│ │ │ │ │ ├── VerificationCodeCard.tsx
│ │ │ │ │ ├── VerificationLinkCard.tsx
│ │ │ │ │ └── withAvatarShimmer.tsx
│ │ │ │ ├── foundations
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ └── createInternalTheme.test.ts
│ │ │ │ │ ├── borders.ts
│ │ │ │ │ ├── colors.ts
│ │ │ │ │ ├── createInternalTheme.ts
│ │ │ │ │ ├── defaultFoundations.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── opacity.ts
│ │ │ │ │ ├── shadows.ts
│ │ │ │ │ ├── sizes.ts
│ │ │ │ │ ├── transitions.ts
│ │ │ │ │ ├── typography.ts
│ │ │ │ │ └── zIndices.ts
│ │ │ │ ├── hooks
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ ├── useCoreOrganization.test.tsx
│ │ │ │ │ │ ├── useCoreOrganizationList.test.tsx
│ │ │ │ │ │ ├── useDevMode.test.tsx
│ │ │ │ │ │ ├── useDirection.test.ts
│ │ │ │ │ │ ├── useEnabledThirdPartyProviders.test.tsx
│ │ │ │ │ │ ├── usePasswordComplexity.test.tsx
│ │ │ │ │ │ └── useSupportEmail.test.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── useAlternativeStrategies.ts
│ │ │ │ │ ├── useClerkModalStateParams.tsx
│ │ │ │ │ ├── useClipboard.ts
│ │ │ │ │ ├── useDebounce.ts
│ │ │ │ │ ├── useDevMode.tsx
│ │ │ │ │ ├── useDirection.ts
│ │ │ │ │ ├── useEmailLink.ts
│ │ │ │ │ ├── useEnabledThirdPartyProviders.tsx
│ │ │ │ │ ├── useEnterpriseSSOLink.ts
│ │ │ │ │ ├── useFetch.ts
│ │ │ │ │ ├── useFetchRoles.ts
│ │ │ │ │ ├── useInView.ts
│ │ │ │ │ ├── useLoadingStatus.ts
│ │ │ │ │ ├── useMultipleSessions.ts
│ │ │ │ │ ├── useNavigateToFlowStart.ts
│ │ │ │ │ ├── useOrganizationListInView.ts
│ │ │ │ │ ├── usePassword.ts
│ │ │ │ │ ├── usePasswordComplexity.ts
│ │ │ │ │ ├── usePopover.ts
│ │ │ │ │ ├── usePrefersReducedMotion.ts
│ │ │ │ │ ├── usePreloadTasks.ts
│ │ │ │ │ ├── useSafeState.ts
│ │ │ │ │ ├── useScrollLock.ts
│ │ │ │ │ ├── useSearchInput.ts
│ │ │ │ │ ├── useSetSessionWithTimeout.ts
│ │ │ │ │ ├── useSupportEmail.ts
│ │ │ │ │ ├── useTabState.ts
│ │ │ │ │ └── useWindowEventListener.ts
│ │ │ │ ├── icons
│ │ │ │ │ ├── add.svg
│ │ │ │ │ ├── apple-pay.svg
│ │ │ │ │ ├── arrow-left.svg
│ │ │ │ │ ├── arrow-right-button.svg
│ │ │ │ │ ├── arrow-right.svg
│ │ │ │ │ ├── arrows-up-down.svg
│ │ │ │ │ ├── auth-app.svg
│ │ │ │ │ ├── billing.svg
│ │ │ │ │ ├── block.svg
│ │ │ │ │ ├── caret-left.svg
│ │ │ │ │ ├── caret-right.svg
│ │ │ │ │ ├── caret.svg
│ │ │ │ │ ├── chat-alt.svg
│ │ │ │ │ ├── check-circle.svg
│ │ │ │ │ ├── check.svg
│ │ │ │ │ ├── checkmark-filled.svg
│ │ │ │ │ ├── chevron-down.svg
│ │ │ │ │ ├── chevron-up-down.svg
│ │ │ │ │ ├── clipboard-outline.svg
│ │ │ │ │ ├── clipboard.svg
│ │ │ │ │ ├── close.svg
│ │ │ │ │ ├── code.svg
│ │ │ │ │ ├── cog-filled.svg
│ │ │ │ │ ├── cog.svg
│ │ │ │ │ ├── copy.svg
│ │ │ │ │ ├── credit-card.svg
│ │ │ │ │ ├── device-laptop.svg
│ │ │ │ │ ├── device-mobile.svg
│ │ │ │ │ ├── dot-circle-horizontal.svg
│ │ │ │ │ ├── download.svg
│ │ │ │ │ ├── email.svg
│ │ │ │ │ ├── exclamation-circle.svg
│ │ │ │ │ ├── exclamation-triangle.svg
│ │ │ │ │ ├── eye-slash.svg
│ │ │ │ │ ├── eye.svg
│ │ │ │ │ ├── fingerprint.svg
│ │ │ │ │ ├── folder.svg
│ │ │ │ │ ├── generic-pay.svg
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── information-circle.svg
│ │ │ │ │ ├── link.svg
│ │ │ │ │ ├── lock-closed.svg
│ │ │ │ │ ├── lock-dotted-circle.svg
│ │ │ │ │ ├── logo-mark-new.svg
│ │ │ │ │ ├── logo-mark.svg
│ │ │ │ │ ├── magnifying-glass.svg
│ │ │ │ │ ├── menu.svg
│ │ │ │ │ ├── minus.svg
│ │ │ │ │ ├── mobile-small.svg
│ │ │ │ │ ├── mobile.svg
│ │ │ │ │ ├── organization.svg
│ │ │ │ │ ├── pencil-edit.svg
│ │ │ │ │ ├── pencil.svg
│ │ │ │ │ ├── plans.svg
│ │ │ │ │ ├── plus.svg
│ │ │ │ │ ├── print.svg
│ │ │ │ │ ├── question-mark.svg
│ │ │ │ │ ├── request-auth.svg
│ │ │ │ │ ├── rotate-left-right.svg
│ │ │ │ │ ├── selector.svg
│ │ │ │ │ ├── signout-double.svg
│ │ │ │ │ ├── signout.svg
│ │ │ │ │ ├── spinner-jumbo.svg
│ │ │ │ │ ├── switch-arrow-right.svg
│ │ │ │ │ ├── switch-arrows.svg
│ │ │ │ │ ├── threeDots.svg
│ │ │ │ │ ├── tick-shield.svg
│ │ │ │ │ ├── times.svg
│ │ │ │ │ ├── trash.svg
│ │ │ │ │ ├── upload.svg
│ │ │ │ │ ├── user.svg
│ │ │ │ │ ├── userAdd.svg
│ │ │ │ │ └── users.svg
│ │ │ │ ├── lazyModules
│ │ │ │ │ ├── common.ts
│ │ │ │ │ ├── components.ts
│ │ │ │ │ ├── drawers.tsx
│ │ │ │ │ ├── MountedCheckoutDrawer.tsx
│ │ │ │ │ ├── MountedPlanDetailDrawer.tsx
│ │ │ │ │ ├── MountedSubscriptionDetailDrawer.tsx
│ │ │ │ │ └── providers.tsx
│ │ │ │ ├── localization
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ ├── applyTokensToString.test.ts
│ │ │ │ │ │ ├── makeLocalizable.test.tsx
│ │ │ │ │ │ └── parseLocalization.test.tsx
│ │ │ │ │ ├── applyTokensToString.ts
│ │ │ │ │ ├── defaultEnglishResource.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── localizationKeys.ts
│ │ │ │ │ ├── localizationModifiers.ts
│ │ │ │ │ ├── makeLocalizable.tsx
│ │ │ │ │ └── parseLocalization.ts
│ │ │ │ ├── portal
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── primitives
│ │ │ │ │ ├── Alert.tsx
│ │ │ │ │ ├── AlertIcon.tsx
│ │ │ │ │ ├── applyDataStateProps.ts
│ │ │ │ │ ├── Badge.tsx
│ │ │ │ │ ├── Box.tsx
│ │ │ │ │ ├── Button.tsx
│ │ │ │ │ ├── Dd.tsx
│ │ │ │ │ ├── Dl.tsx
│ │ │ │ │ ├── Dt.tsx
│ │ │ │ │ ├── Flex.tsx
│ │ │ │ │ ├── Form.tsx
│ │ │ │ │ ├── FormErrorText.tsx
│ │ │ │ │ ├── FormInfoText.tsx
│ │ │ │ │ ├── FormLabel.tsx
│ │ │ │ │ ├── FormSuccessText.tsx
│ │ │ │ │ ├── FormWarningText.tsx
│ │ │ │ │ ├── gapPropertyCompat.ts
│ │ │ │ │ ├── Grid.tsx
│ │ │ │ │ ├── Heading.tsx
│ │ │ │ │ ├── hooks
│ │ │ │ │ │ ├── useFormField.tsx
│ │ │ │ │ │ └── useInput.ts
│ │ │ │ │ ├── Hr.tsx
│ │ │ │ │ ├── Icon.tsx
│ │ │ │ │ ├── Image.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── Input.tsx
│ │ │ │ │ ├── Link.tsx
│ │ │ │ │ ├── NotificationBadge.tsx
│ │ │ │ │ ├── Span.tsx
│ │ │ │ │ ├── Spinner.tsx
│ │ │ │ │ ├── Table.tsx
│ │ │ │ │ ├── Tbody.tsx
│ │ │ │ │ ├── Td.tsx
│ │ │ │ │ ├── Text.tsx
│ │ │ │ │ ├── Th.tsx
│ │ │ │ │ ├── Thead.tsx
│ │ │ │ │ └── Tr.tsx
│ │ │ │ ├── router
│ │ │ │ │ ├── __mocks__
│ │ │ │ │ │ └── RouteContext.tsx
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ ├── HashRouter.test.tsx
│ │ │ │ │ │ ├── PathRouter.test.tsx
│ │ │ │ │ │ ├── Switch.test.tsx
│ │ │ │ │ │ └── VirtualRouter.test.tsx
│ │ │ │ │ ├── BaseRouter.tsx
│ │ │ │ │ ├── HashRouter.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── newPaths.ts
│ │ │ │ │ ├── PathRouter.tsx
│ │ │ │ │ ├── pathToRegexp.ts
│ │ │ │ │ ├── Route.tsx
│ │ │ │ │ ├── RouteContext.tsx
│ │ │ │ │ ├── Switch.tsx
│ │ │ │ │ └── VirtualRouter.tsx
│ │ │ │ ├── styledSystem
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ └── createVariants.test.ts
│ │ │ │ │ ├── animations.ts
│ │ │ │ │ ├── breakpoints.tsx
│ │ │ │ │ ├── common.ts
│ │ │ │ │ ├── createCssVariables.ts
│ │ │ │ │ ├── createVariants.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── InternalThemeProvider.tsx
│ │ │ │ │ ├── StyleCacheProvider.tsx
│ │ │ │ │ └── types.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── createCustomMenuItems.test.ts
│ │ │ │ │ ├── createCustomPages.test.ts
│ │ │ │ │ ├── cssSupports.test.ts
│ │ │ │ │ ├── cssVariables.test.ts
│ │ │ │ │ ├── factorSorting.test.ts
│ │ │ │ │ ├── formatSafeIdentifier.test.ts
│ │ │ │ │ ├── intl.test.ts
│ │ │ │ │ ├── originPrefersPopup.test.ts
│ │ │ │ │ ├── passwordUtils.test.tsx
│ │ │ │ │ ├── phoneUtils.test.ts
│ │ │ │ │ └── truncateTextWithEndVisible.test.ts
│ │ │ │ ├── colors
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ ├── constants.test.ts
│ │ │ │ │ │ ├── index.test.ts
│ │ │ │ │ │ ├── legacy.test.ts
│ │ │ │ │ │ ├── modern.test.ts
│ │ │ │ │ │ ├── scales.test.ts
│ │ │ │ │ │ └── utils.test.ts
│ │ │ │ │ ├── constants.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── legacy.ts
│ │ │ │ │ ├── modern.ts
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── scales.ts
│ │ │ │ │ └── utils.ts
│ │ │ │ ├── containsAllOf.ts
│ │ │ │ ├── createCustomMenuItems.tsx
│ │ │ │ ├── createCustomPages.tsx
│ │ │ │ ├── createInfiniteAccessProxy.ts
│ │ │ │ ├── createSlug.ts
│ │ │ │ ├── cssSupports.ts
│ │ │ │ ├── cssVariables.ts
│ │ │ │ ├── errorHandler.ts
│ │ │ │ ├── ExternalElementMounter.tsx
│ │ │ │ ├── factorSorting.ts
│ │ │ │ ├── formatDate.ts
│ │ │ │ ├── formatSafeIdentifier.ts
│ │ │ │ ├── fromEntries.ts
│ │ │ │ ├── getClosestProfileScrollBox.ts
│ │ │ │ ├── getRelativeToNowDateKey.ts
│ │ │ │ ├── getValidReactChildren.ts
│ │ │ │ ├── intl.ts
│ │ │ │ ├── isMobileDevice.ts
│ │ │ │ ├── mergeRefs.ts
│ │ │ │ ├── originPrefersPopup.ts
│ │ │ │ ├── passwordUtils.ts
│ │ │ │ ├── phoneUtils.ts
│ │ │ │ ├── range.ts
│ │ │ │ ├── readObjectPath.ts
│ │ │ │ ├── removeUndefinedProps.ts
│ │ │ │ ├── roleLocalizationKey.ts
│ │ │ │ ├── runtimeEnvironment.ts
│ │ │ │ ├── sanitizeCustomLinkURL.ts
│ │ │ │ ├── sleep.ts
│ │ │ │ ├── timeAgo.ts
│ │ │ │ ├── truncateTextWithEndVisible.ts
│ │ │ │ ├── useFormControl.ts
│ │ │ │ ├── usernameUtils.ts
│ │ │ │ └── web3CallbackErrorHandler.ts
│ │ │ └── utils
│ │ │ ├── __tests__
│ │ │ │ ├── appearance.test.ts
│ │ │ │ ├── captcha.test.ts
│ │ │ │ ├── completeSignUpFlow.test.ts
│ │ │ │ ├── date.test.ts
│ │ │ │ ├── dynamicParamParser.test.ts
│ │ │ │ ├── email.test.ts
│ │ │ │ ├── encoders.test.ts
│ │ │ │ ├── errors.test.ts
│ │ │ │ ├── filterUndefinedValues.test.ts
│ │ │ │ ├── getClerkQueryParam.test.ts
│ │ │ │ ├── ignoreEventValue.test.ts
│ │ │ │ ├── instance.test.ts
│ │ │ │ ├── jwt.test.ts
│ │ │ │ ├── locale.test.ts
│ │ │ │ ├── localStorage.test.ts
│ │ │ │ ├── memoizeStateListenerCallback.test.ts
│ │ │ │ ├── organization.test.ts
│ │ │ │ ├── passkeys.test.ts
│ │ │ │ ├── path.test.ts
│ │ │ │ ├── queryStateParams.test.ts
│ │ │ │ ├── querystring.test.ts
│ │ │ │ ├── redirectUrls.test.ts
│ │ │ │ ├── resourceParams.test.ts
│ │ │ │ ├── runAsyncResourceTask.test.ts
│ │ │ │ ├── tokenId.test.ts
│ │ │ │ └── url.test.ts
│ │ │ ├── appearance.ts
│ │ │ ├── assertNoLegacyProp.ts
│ │ │ ├── authenticateWithPopup.ts
│ │ │ ├── beforeUnloadTracker.ts
│ │ │ ├── billing.ts
│ │ │ ├── captcha
│ │ │ │ ├── CaptchaChallenge.ts
│ │ │ │ ├── constants.ts
│ │ │ │ ├── getCaptchaToken.ts
│ │ │ │ ├── retrieveCaptchaInfo.ts
│ │ │ │ ├── turnstile.ts
│ │ │ │ └── types.ts
│ │ │ ├── completeSignUpFlow.ts
│ │ │ ├── componentGuards.ts
│ │ │ ├── convertPageToOffsetSearchParams.ts
│ │ │ ├── date.ts
│ │ │ ├── debug.ts
│ │ │ ├── disambiguateRedirectOptions.ts
│ │ │ ├── dynamicParamParser.ts
│ │ │ ├── email.ts
│ │ │ ├── encoders.ts
│ │ │ ├── errors.ts
│ │ │ ├── errorThrower.ts
│ │ │ ├── filterUndefinedValues.ts
│ │ │ ├── getClerkQueryParam.ts
│ │ │ ├── hex.ts
│ │ │ ├── ignoreEventValue.ts
│ │ │ ├── image.ts
│ │ │ ├── index.ts
│ │ │ ├── injectedWeb3Providers.ts
│ │ │ ├── instance.ts
│ │ │ ├── jwt.ts
│ │ │ ├── locale.ts
│ │ │ ├── localStorage.ts
│ │ │ ├── memoizeStateListenerCallback.ts
│ │ │ ├── normalizeRoutingOptions.ts
│ │ │ ├── one-tap.ts
│ │ │ ├── organization.ts
│ │ │ ├── pageLifecycle.ts
│ │ │ ├── passkeys.ts
│ │ │ ├── passwords
│ │ │ │ ├── complexity.ts
│ │ │ │ ├── password.ts
│ │ │ │ └── strength.ts
│ │ │ ├── path.ts
│ │ │ ├── props.ts
│ │ │ ├── queryStateParams.ts
│ │ │ ├── querystring.ts
│ │ │ ├── redirectUrls.ts
│ │ │ ├── resourceParams.ts
│ │ │ ├── runAsyncResourceTask.ts
│ │ │ ├── runtime.ts
│ │ │ ├── setWebpackChunkPublicPath.ts
│ │ │ ├── tokenId.ts
│ │ │ ├── url.ts
│ │ │ ├── user.ts
│ │ │ ├── web3.ts
│ │ │ ├── windowNavigate.ts
│ │ │ └── zxcvbn.ts
│ │ ├── svgTransform.js
│ │ ├── tsconfig.declarations.json
│ │ ├── tsconfig.dev.json
│ │ ├── tsconfig.json
│ │ ├── turbo.json
│ │ ├── vercel.json
│ │ ├── vitest.config.mts
│ │ └── vitest.setup.mts
│ ├── dev-cli
│ │ ├── bin
│ │ │ └── cli.js
│ │ ├── CHANGELOG.md
│ │ ├── jsconfig.json
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── schema.json
│ │ └── src
│ │ ├── cli.js
│ │ ├── codemods
│ │ │ ├── index.js
│ │ │ └── transforms
│ │ │ └── add-clerkjsurl-to-provider.cjs
│ │ ├── commands
│ │ │ ├── auth.js
│ │ │ ├── config.js
│ │ │ ├── init.js
│ │ │ ├── set-instance.js
│ │ │ ├── set-root.js
│ │ │ ├── setup.js
│ │ │ └── watch.js
│ │ └── utils
│ │ ├── errors.js
│ │ ├── getClerkPackages.js
│ │ ├── getConfiguration.js
│ │ ├── getMonorepoRoot.js
│ │ ├── getPackageJSON.js
│ │ └── getPackageVersion.js
│ ├── elements
│ │ ├── .gitignore
│ │ ├── .npmignore
│ │ ├── CHANGELOG.md
│ │ ├── examples
│ │ │ └── nextjs
│ │ │ ├── .env.local.example
│ │ │ ├── .gitignore
│ │ │ ├── app
│ │ │ │ ├── example
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── favicon.ico
│ │ │ │ ├── globals.css
│ │ │ │ ├── layout.tsx
│ │ │ │ ├── modal
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── otp-playground
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── page.tsx
│ │ │ │ ├── sign-in
│ │ │ │ │ └── [[...sign-in]]
│ │ │ │ │ └── page.tsx
│ │ │ │ └── sign-up
│ │ │ │ └── [[...sign-up]]
│ │ │ │ └── page.tsx
│ │ │ ├── components
│ │ │ │ ├── design.tsx
│ │ │ │ ├── form.tsx
│ │ │ │ ├── social-providers.tsx
│ │ │ │ └── spinner.tsx
│ │ │ ├── middleware.ts
│ │ │ ├── next.config.js
│ │ │ ├── package.json
│ │ │ ├── postcss.config.js
│ │ │ ├── public
│ │ │ │ ├── clerk.svg
│ │ │ │ ├── next.svg
│ │ │ │ └── vercel.svg
│ │ │ ├── README.md
│ │ │ ├── tailwind.config.ts
│ │ │ └── tsconfig.json
│ │ ├── jest.config.js
│ │ ├── jest.setup.js
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── index.ts
│ │ │ ├── internals
│ │ │ │ ├── constants
│ │ │ │ │ └── index.ts
│ │ │ │ ├── errors
│ │ │ │ │ └── index.ts
│ │ │ │ ├── machines
│ │ │ │ │ ├── form
│ │ │ │ │ │ ├── form.context.ts
│ │ │ │ │ │ ├── form.machine.ts
│ │ │ │ │ │ ├── form.types.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── shared
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── shared.actions.test.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── shared.actions.ts
│ │ │ │ │ │ ├── shared.actors.ts
│ │ │ │ │ │ └── shared.types.ts
│ │ │ │ │ ├── sign-in
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── router.selectors.test.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── reset-password.machine.ts
│ │ │ │ │ │ ├── reset-password.types.ts
│ │ │ │ │ │ ├── router.machine.ts
│ │ │ │ │ │ ├── router.selectors.ts
│ │ │ │ │ │ ├── router.types.ts
│ │ │ │ │ │ ├── start.machine.ts
│ │ │ │ │ │ ├── start.types.ts
│ │ │ │ │ │ ├── utils
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── starting-factors.ts
│ │ │ │ │ │ ├── verification.machine.ts
│ │ │ │ │ │ └── verification.types.ts
│ │ │ │ │ ├── sign-up
│ │ │ │ │ │ ├── continue.machine.ts
│ │ │ │ │ │ ├── continue.types.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── router.machine.ts
│ │ │ │ │ │ ├── router.types.ts
│ │ │ │ │ │ ├── start.machine.ts
│ │ │ │ │ │ ├── start.types.ts
│ │ │ │ │ │ ├── utils
│ │ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ │ └── fields-to-params.test.ts
│ │ │ │ │ │ │ ├── fields-to-params.ts
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ ├── verification.machine.ts
│ │ │ │ │ │ └── verification.types.ts
│ │ │ │ │ ├── third-party
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── third-party.actors.ts
│ │ │ │ │ │ ├── third-party.machine.ts
│ │ │ │ │ │ └── third-party.types.ts
│ │ │ │ │ ├── types
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── router.types.ts
│ │ │ │ │ └── utils
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ ├── assert.test.ts
│ │ │ │ │ │ ├── formatters.test.ts
│ │ │ │ │ │ ├── next.test.ts
│ │ │ │ │ │ └── strategies.test.ts
│ │ │ │ │ ├── assert.ts
│ │ │ │ │ ├── clerkjs.ts
│ │ │ │ │ ├── formatters.ts
│ │ │ │ │ ├── next.ts
│ │ │ │ │ └── strategies.ts
│ │ │ │ └── utils
│ │ │ │ └── inspector
│ │ │ │ ├── browser
│ │ │ │ │ └── index.ts
│ │ │ │ ├── console
│ │ │ │ │ ├── console.ts
│ │ │ │ │ └── index.ts
│ │ │ │ └── index.ts
│ │ │ ├── react
│ │ │ │ ├── common
│ │ │ │ │ ├── connections.tsx
│ │ │ │ │ ├── form
│ │ │ │ │ │ ├── field-error.tsx
│ │ │ │ │ │ ├── field-state.tsx
│ │ │ │ │ │ ├── field.tsx
│ │ │ │ │ │ ├── form.tsx
│ │ │ │ │ │ ├── global-error.tsx
│ │ │ │ │ │ ├── hooks
│ │ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ │ ├── use-field-feedback.test.ts
│ │ │ │ │ │ │ │ ├── use-form.test.tsx
│ │ │ │ │ │ │ │ ├── use-global-errors.test.ts
│ │ │ │ │ │ │ │ └── use-previous.test.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── use-field-context.ts
│ │ │ │ │ │ │ ├── use-field-feedback.ts
│ │ │ │ │ │ │ ├── use-field-state.ts
│ │ │ │ │ │ │ ├── use-field.ts
│ │ │ │ │ │ │ ├── use-form.ts
│ │ │ │ │ │ │ ├── use-global-errors.ts
│ │ │ │ │ │ │ ├── use-input.tsx
│ │ │ │ │ │ │ ├── use-previous.ts
│ │ │ │ │ │ │ └── use-validity-state-context.ts
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── input.tsx
│ │ │ │ │ │ ├── label.tsx
│ │ │ │ │ │ ├── otp.tsx
│ │ │ │ │ │ ├── submit.tsx
│ │ │ │ │ │ ├── types.ts
│ │ │ │ │ │ └── utils
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ ├── determine-input-type-from-name.test.ts
│ │ │ │ │ │ │ └── enrich-field-state.test.ts
│ │ │ │ │ │ ├── determine-input-type-from-name.ts
│ │ │ │ │ │ ├── enrich-field-state.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── link.tsx
│ │ │ │ │ └── loading.tsx
│ │ │ │ ├── hooks
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ ├── use-active-states.hook.test.ts
│ │ │ │ │ │ ├── use-active-tags.hook.test.ts
│ │ │ │ │ │ └── use-focus.hook.test.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── use-active-states.hook.ts
│ │ │ │ │ ├── use-active-tags.hook.ts
│ │ │ │ │ ├── use-focus.hook.ts
│ │ │ │ │ ├── use-loading.hook.ts
│ │ │ │ │ ├── use-password.hook.ts
│ │ │ │ │ └── use-third-party-provider.hook.ts
│ │ │ │ ├── router
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── next.ts
│ │ │ │ │ └── virtual.ts
│ │ │ │ ├── sign-in
│ │ │ │ │ ├── action
│ │ │ │ │ │ ├── action.tsx
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── navigate.tsx
│ │ │ │ │ │ ├── resend.tsx
│ │ │ │ │ │ └── set-active-session.tsx
│ │ │ │ │ ├── captcha.tsx
│ │ │ │ │ ├── choose-session
│ │ │ │ │ │ ├── __tests__
│ │ │ │ │ │ │ └── choose-session.test.tsx
│ │ │ │ │ │ ├── choose-session.hooks.ts
│ │ │ │ │ │ ├── choose-session.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── choose-strategy.tsx
│ │ │ │ │ ├── context
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── router.context.ts
│ │ │ │ │ │ ├── sign-in-strategy.context.ts
│ │ │ │ │ │ └── strategies.context.ts
│ │ │ │ │ ├── identifiers.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── passkey.tsx
│ │ │ │ │ ├── reset-password.tsx
│ │ │ │ │ ├── root.tsx
│ │ │ │ │ ├── sso-callback.tsx
│ │ │ │ │ ├── start.tsx
│ │ │ │ │ ├── step.tsx
│ │ │ │ │ └── verifications.tsx
│ │ │ │ ├── sign-up
│ │ │ │ │ ├── action
│ │ │ │ │ │ ├── action.tsx
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── navigate.tsx
│ │ │ │ │ │ └── resend.tsx
│ │ │ │ │ ├── captcha.tsx
│ │ │ │ │ ├── context
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── router.context.ts
│ │ │ │ │ │ └── strategies.context.ts
│ │ │ │ │ ├── continue.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── restricted.tsx
│ │ │ │ │ ├── root.tsx
│ │ │ │ │ ├── start.tsx
│ │ │ │ │ ├── step.tsx
│ │ │ │ │ └── verifications.tsx
│ │ │ │ └── utils
│ │ │ │ ├── __tests__
│ │ │ │ │ └── map-scope-to-strategy.test.ts
│ │ │ │ ├── create-context-for-dom-validation.ts
│ │ │ │ ├── create-context-from-actor-ref.ts
│ │ │ │ ├── generate-password-error-text.ts
│ │ │ │ ├── is-react-fragment.ts
│ │ │ │ ├── is-valid-component-type.ts
│ │ │ │ ├── map-scope-to-strategy.ts
│ │ │ │ └── path-inference
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── next.test.ts
│ │ │ │ │ └── utils.test.ts
│ │ │ │ ├── next.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── types
│ │ │ │ ├── clerk.d.ts
│ │ │ │ ├── common.d.ts
│ │ │ │ └── global.d.ts
│ │ │ └── utils
│ │ │ ├── clerk-js.ts
│ │ │ ├── is-absolute-url.ts
│ │ │ ├── safe-access.ts
│ │ │ ├── test-utils.ts
│ │ │ └── third-party-strategies.ts
│ │ ├── tsconfig.declarations.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.test.json
│ │ ├── tsup.config.ts
│ │ └── turbo.json
│ ├── expo
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── local-credentials
│ │ │ └── package.json
│ │ ├── package.json
│ │ ├── passkeys
│ │ │ └── package.json
│ │ ├── README.md
│ │ ├── resource-cache
│ │ │ └── package.json
│ │ ├── secure-store
│ │ │ └── package.json
│ │ ├── src
│ │ │ ├── cache
│ │ │ │ ├── dummy-data
│ │ │ │ │ ├── client-resource.ts
│ │ │ │ │ ├── environment-resource.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── MemoryTokenCache.ts
│ │ │ │ ├── ResourceCache.ts
│ │ │ │ └── types.ts
│ │ │ ├── components
│ │ │ │ ├── controlComponents.tsx
│ │ │ │ └── index.ts
│ │ │ ├── errorThrower.ts
│ │ │ ├── experimental.ts
│ │ │ ├── global.d.ts
│ │ │ ├── hooks
│ │ │ │ ├── __tests__
│ │ │ │ │ └── useSignInWithApple.test.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── useAuth.ts
│ │ │ │ ├── useOAuth.ts
│ │ │ │ ├── useSignInWithApple.ios.ts
│ │ │ │ ├── useSignInWithApple.ts
│ │ │ │ └── useSSO.ts
│ │ │ ├── index.ts
│ │ │ ├── local-credentials
│ │ │ │ ├── index.tsx
│ │ │ │ └── useLocalCredentials
│ │ │ │ ├── index.tsx
│ │ │ │ ├── shared.ts
│ │ │ │ ├── useLocalCredentials.ts
│ │ │ │ └── useLocalCredentials.web.ts
│ │ │ ├── passkeys
│ │ │ │ └── index.ts
│ │ │ ├── polyfills
│ │ │ │ ├── base64Polyfill.ts
│ │ │ │ └── index.ts
│ │ │ ├── provider
│ │ │ │ ├── ClerkProvider.tsx
│ │ │ │ └── singleton
│ │ │ │ ├── createClerkInstance.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── singleton.ts
│ │ │ │ ├── singleton.web.ts
│ │ │ │ └── types.ts
│ │ │ ├── resource-cache
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── dummy-test-data.ts
│ │ │ │ │ └── secure-store.test.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── resource-cache.ts
│ │ │ ├── secure-store
│ │ │ │ └── index.ts
│ │ │ ├── token-cache
│ │ │ │ └── index.ts
│ │ │ ├── utils
│ │ │ │ ├── errors.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── runtime.ts
│ │ │ └── web
│ │ │ ├── index.ts
│ │ │ └── uiComponents.tsx
│ │ ├── token-cache
│ │ │ └── package.json
│ │ ├── tsconfig.declarations.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.test.json
│ │ ├── tsup.config.ts
│ │ ├── vitest.config.mts
│ │ ├── vitest.setup.mts
│ │ └── web
│ │ └── package.json
│ ├── expo-passkeys
│ │ ├── .gitignore
│ │ ├── .npmignore
│ │ ├── android
│ │ │ ├── build.gradle
│ │ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── expo
│ │ │ └── modules
│ │ │ └── clerkexpopasskeys
│ │ │ ├── ClerkExpoPasskeysExceptions.kt
│ │ │ ├── ClerkExpoPasskeysModule.kt
│ │ │ └── CredentialManager.kt
│ │ ├── app.json
│ │ ├── CHANGELOG.md
│ │ ├── expo-module.config.json
│ │ ├── ios
│ │ │ ├── AccountManager.swift
│ │ │ ├── ClerkExpoPasskeys.podspec
│ │ │ ├── ClerkExpoPasskeysModule.swift
│ │ │ └── Helpers.swift
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── ClerkExpoPasskeys.types.ts
│ │ │ ├── ClerkExpoPasskeysModule.ts
│ │ │ ├── ClerkExpoPasskeysModule.web.ts
│ │ │ ├── index.ts
│ │ │ └── utils.ts
│ │ ├── tsconfig.declarations.json
│ │ ├── tsconfig.json
│ │ └── tsup.config.ts
│ ├── express
│ │ ├── .gitignore
│ │ ├── .npmignore
│ │ ├── CHANGELOG.md
│ │ ├── env.d.ts
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── __snapshots__
│ │ │ │ │ └── exports.test.ts.snap
│ │ │ │ ├── clerkMiddleware.test.ts
│ │ │ │ ├── exports.test.ts
│ │ │ │ ├── getAuth.test.ts
│ │ │ │ ├── helpers.ts
│ │ │ │ └── requireAuth.test.ts
│ │ │ ├── authenticateRequest.ts
│ │ │ ├── clerkClient.ts
│ │ │ ├── clerkMiddleware.ts
│ │ │ ├── errors.ts
│ │ │ ├── getAuth.ts
│ │ │ ├── global.d.ts
│ │ │ ├── index.ts
│ │ │ ├── requireAuth.ts
│ │ │ ├── types.ts
│ │ │ ├── utils.ts
│ │ │ └── webhooks.ts
│ │ ├── tsconfig.json
│ │ ├── tsup.config.ts
│ │ ├── turbo.json
│ │ ├── vitest.config.mts
│ │ ├── vitest.setup.mts
│ │ └── webhooks
│ │ └── package.json
│ ├── fastify
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── __snapshots__
│ │ │ │ │ ├── clerkClient.test.ts.snap
│ │ │ │ │ ├── constants.test.ts.snap
│ │ │ │ │ ├── exports.test.ts.snap
│ │ │ │ │ └── getAuth.test.ts.snap
│ │ │ │ ├── clerkClient.test.ts
│ │ │ │ ├── clerkPlugin.test.ts
│ │ │ │ ├── constants.test.ts
│ │ │ │ ├── exports.test.ts
│ │ │ │ ├── getAuth.test.ts
│ │ │ │ └── withClerkMiddleware.test.ts
│ │ │ ├── clerkClient.ts
│ │ │ ├── clerkPlugin.ts
│ │ │ ├── constants.ts
│ │ │ ├── errors.ts
│ │ │ ├── getAuth.ts
│ │ │ ├── global.d.ts
│ │ │ ├── index.ts
│ │ │ ├── test
│ │ │ │ └── utils.ts
│ │ │ ├── types.ts
│ │ │ ├── utils.ts
│ │ │ ├── webhooks.ts
│ │ │ └── withClerkMiddleware.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.test.json
│ │ ├── tsup.config.ts
│ │ ├── vitest.config.mts
│ │ ├── vitest.setup.mts
│ │ └── webhooks
│ │ └── package.json
│ ├── localizations
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── ar-SA.ts
│ │ │ ├── be-BY.ts
│ │ │ ├── bg-BG.ts
│ │ │ ├── bn-IN.ts
│ │ │ ├── ca-ES.ts
│ │ │ ├── cs-CZ.ts
│ │ │ ├── da-DK.ts
│ │ │ ├── de-DE.ts
│ │ │ ├── el-GR.ts
│ │ │ ├── en-GB.ts
│ │ │ ├── en-US.ts
│ │ │ ├── es-CR.ts
│ │ │ ├── es-ES.ts
│ │ │ ├── es-MX.ts
│ │ │ ├── es-UY.ts
│ │ │ ├── fa-IR.ts
│ │ │ ├── fi-FI.ts
│ │ │ ├── fr-FR.ts
│ │ │ ├── he-IL.ts
│ │ │ ├── hi-IN.ts
│ │ │ ├── hr-HR.ts
│ │ │ ├── hu-HU.ts
│ │ │ ├── id-ID.ts
│ │ │ ├── index.ts
│ │ │ ├── is-IS.ts
│ │ │ ├── it-IT.ts
│ │ │ ├── ja-JP.ts
│ │ │ ├── kk-KZ.ts
│ │ │ ├── ko-KR.ts
│ │ │ ├── mn-MN.ts
│ │ │ ├── ms-MY.ts
│ │ │ ├── nb-NO.ts
│ │ │ ├── nl-BE.ts
│ │ │ ├── nl-NL.ts
│ │ │ ├── pl-PL.ts
│ │ │ ├── pt-BR.ts
│ │ │ ├── pt-PT.ts
│ │ │ ├── ro-RO.ts
│ │ │ ├── ru-RU.ts
│ │ │ ├── sk-SK.ts
│ │ │ ├── sr-RS.ts
│ │ │ ├── sv-SE.ts
│ │ │ ├── ta-IN.ts
│ │ │ ├── te-IN.ts
│ │ │ ├── th-TH.ts
│ │ │ ├── tr-TR.ts
│ │ │ ├── uk-UA.ts
│ │ │ ├── utils
│ │ │ │ ├── enUS_v4.ts
│ │ │ │ ├── generate.ts
│ │ │ │ └── utils.ts
│ │ │ ├── vi-VN.ts
│ │ │ ├── zh-CN.ts
│ │ │ └── zh-TW.ts
│ │ ├── subpaths.mjs
│ │ ├── tsconfig.json
│ │ └── tsup.config.ts
│ ├── nextjs
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── errors
│ │ │ └── package.json
│ │ ├── internal
│ │ │ └── package.json
│ │ ├── LICENSE
│ │ ├── package.cjs.json
│ │ ├── package.esm.json
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── server
│ │ │ └── package.json
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── keyless-custom-headers.test.ts
│ │ │ │ └── webhooks.test.ts
│ │ │ ├── app-router
│ │ │ │ ├── client
│ │ │ │ │ ├── ClerkProvider.tsx
│ │ │ │ │ ├── keyless-cookie-sync.tsx
│ │ │ │ │ ├── keyless-creator-reader.tsx
│ │ │ │ │ ├── useAwaitablePush.ts
│ │ │ │ │ ├── useAwaitableReplace.ts
│ │ │ │ │ └── useInternalNavFun.ts
│ │ │ │ ├── keyless-actions.ts
│ │ │ │ ├── server
│ │ │ │ │ ├── auth.ts
│ │ │ │ │ ├── ClerkProvider.tsx
│ │ │ │ │ ├── controlComponents.tsx
│ │ │ │ │ ├── currentUser.ts
│ │ │ │ │ ├── keyless-provider.tsx
│ │ │ │ │ └── utils.ts
│ │ │ │ └── server-actions.ts
│ │ │ ├── client-boundary
│ │ │ │ ├── ClerkProvider.tsx
│ │ │ │ ├── controlComponents.ts
│ │ │ │ ├── hooks
│ │ │ │ │ ├── useEnforceCatchAllRoute.tsx
│ │ │ │ │ ├── useEnforceRoutingProps.tsx
│ │ │ │ │ ├── usePagesRouter.tsx
│ │ │ │ │ ├── usePathnameWithoutCatchAll.tsx
│ │ │ │ │ └── useSafeLayoutEffect.tsx
│ │ │ │ ├── hooks.ts
│ │ │ │ ├── NextOptionsContext.tsx
│ │ │ │ ├── PromisifiedAuthProvider.tsx
│ │ │ │ └── uiComponents.tsx
│ │ │ ├── components.client.ts
│ │ │ ├── components.server.ts
│ │ │ ├── constants.ts
│ │ │ ├── errors.ts
│ │ │ ├── experimental.ts
│ │ │ ├── global.d.ts
│ │ │ ├── index.ts
│ │ │ ├── internal.ts
│ │ │ ├── pages
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── __snapshots__
│ │ │ │ │ │ └── exports.test.ts.snap
│ │ │ │ │ ├── exports.test.ts
│ │ │ │ │ └── index.test.tsx
│ │ │ │ └── ClerkProvider.tsx
│ │ │ ├── runtime
│ │ │ │ ├── browser
│ │ │ │ │ └── safe-node-apis.js
│ │ │ │ └── node
│ │ │ │ └── safe-node-apis.js
│ │ │ ├── server
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── __snapshots__
│ │ │ │ │ │ └── exports.test.ts.snap
│ │ │ │ │ ├── clerkClient.test.ts
│ │ │ │ │ ├── clerkMiddleware.test.ts
│ │ │ │ │ ├── content-security-policy.test.ts
│ │ │ │ │ ├── createGetAuth.test.ts
│ │ │ │ │ ├── exports.test.ts
│ │ │ │ │ └── getAuthDataFromRequest.test.ts
│ │ │ │ ├── buildClerkProps.ts
│ │ │ │ ├── clerkClient.ts
│ │ │ │ ├── clerkMiddleware.ts
│ │ │ │ ├── constants.ts
│ │ │ │ ├── content-security-policy.ts
│ │ │ │ ├── createClerkClient.ts
│ │ │ │ ├── createGetAuth.ts
│ │ │ │ ├── data
│ │ │ │ │ └── getAuthDataFromRequest.ts
│ │ │ │ ├── errors.ts
│ │ │ │ ├── errorThrower.ts
│ │ │ │ ├── fs
│ │ │ │ │ ├── middleware-location.ts
│ │ │ │ │ └── utils.ts
│ │ │ │ ├── headers-utils.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── keyless-custom-headers.ts
│ │ │ │ ├── keyless-log-cache.ts
│ │ │ │ ├── keyless-node.ts
│ │ │ │ ├── keyless-telemetry.ts
│ │ │ │ ├── keyless.ts
│ │ │ │ ├── middleware-storage.ts
│ │ │ │ ├── nextErrors.ts
│ │ │ │ ├── nextFetcher.ts
│ │ │ │ ├── protect.ts
│ │ │ │ ├── routeMatcher.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ ├── types
│ │ │ │ └── safe-node-apis.d.ts
│ │ │ ├── types.ts
│ │ │ ├── utils
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── debugLogger.test.ts
│ │ │ │ │ ├── matcher.test.ts
│ │ │ │ │ └── removeBasePath.test.ts
│ │ │ │ ├── clerk-js-script.tsx
│ │ │ │ ├── debugLogger.ts
│ │ │ │ ├── feature-flags.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── invalidateNextRouterCache.ts
│ │ │ │ ├── logFormatter.ts
│ │ │ │ ├── mergeNextClerkPropsWithEnv.ts
│ │ │ │ ├── only-try.ts
│ │ │ │ ├── removeBasePath.ts
│ │ │ │ ├── response.ts
│ │ │ │ ├── router-telemetry.ts
│ │ │ │ ├── sdk-versions.ts
│ │ │ │ └── serverRedirectWithAuth.ts
│ │ │ ├── vendor
│ │ │ │ ├── crypto-es.js
│ │ │ │ └── README.md
│ │ │ └── webhooks.ts
│ │ ├── tsconfig.declarations.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.test.json
│ │ ├── tsup.config.ts
│ │ ├── typedoc.json
│ │ ├── vitest.config.mts
│ │ ├── vitest.setup.mts
│ │ └── webhooks
│ │ └── package.json
│ ├── nuxt
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── global.d.ts
│ │ │ ├── module.ts
│ │ │ └── runtime
│ │ │ ├── client
│ │ │ │ ├── index.ts
│ │ │ │ └── routeMatcher.ts
│ │ │ ├── components
│ │ │ │ └── index.ts
│ │ │ ├── composables
│ │ │ │ └── index.ts
│ │ │ ├── errors.ts
│ │ │ ├── plugin.ts
│ │ │ ├── server
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── auth.test-d.ts
│ │ │ │ │ └── clerkMiddleware.test.ts
│ │ │ │ ├── clerkClient.ts
│ │ │ │ ├── clerkMiddleware.ts
│ │ │ │ ├── errors.ts
│ │ │ │ ├── getAuth.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── middleware.ts
│ │ │ │ ├── routeMatcher.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ └── webhooks.ts
│ │ ├── tsconfig.json
│ │ ├── tsup.config.ts
│ │ └── vitest.config.ts
│ ├── react
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── clerk.png
│ │ ├── docs
│ │ │ ├── use-auth.md
│ │ │ ├── use-sign-in.md
│ │ │ └── use-sign-up.md
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ └── isomorphicClerk.test.ts
│ │ │ ├── components
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── CheckoutButton.test.tsx
│ │ │ │ │ ├── OrganizationProfile.test.tsx
│ │ │ │ │ ├── OrganizationSwitcher.test.tsx
│ │ │ │ │ ├── PlanDetailsButton.test.tsx
│ │ │ │ │ ├── SignIn.test.tsx
│ │ │ │ │ ├── SignInButton.test.tsx
│ │ │ │ │ ├── SignInWithMetamaskButton.test.tsx
│ │ │ │ │ ├── SignOutButton.test.tsx
│ │ │ │ │ ├── SignUp.test.tsx
│ │ │ │ │ ├── SignUpButton.test.tsx
│ │ │ │ │ ├── SubscriptionDetailsButton.test.tsx
│ │ │ │ │ ├── UserAvatar.test.tsx
│ │ │ │ │ ├── UserButton.test.tsx
│ │ │ │ │ └── UserProfile.test.tsx
│ │ │ │ ├── CheckoutButton.tsx
│ │ │ │ ├── ClerkHostRenderer.tsx
│ │ │ │ ├── controlComponents.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── PlanDetailsButton.tsx
│ │ │ │ ├── SignInButton.tsx
│ │ │ │ ├── SignInWithMetamaskButton.tsx
│ │ │ │ ├── SignOutButton.tsx
│ │ │ │ ├── SignUpButton.tsx
│ │ │ │ ├── SubscriptionDetailsButton.tsx
│ │ │ │ ├── uiComponents.tsx
│ │ │ │ └── withClerk.tsx
│ │ │ ├── contexts
│ │ │ │ ├── __tests__
│ │ │ │ │ └── ClerkProvider.test.tsx
│ │ │ │ ├── assertHelpers.ts
│ │ │ │ ├── AuthContext.ts
│ │ │ │ ├── ClerkContextProvider.tsx
│ │ │ │ ├── ClerkProvider.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── IsomorphicClerkContext.tsx
│ │ │ │ ├── OrganizationContext.tsx
│ │ │ │ ├── SessionContext.tsx
│ │ │ │ └── UserContext.tsx
│ │ │ ├── errors
│ │ │ │ ├── errorThrower.ts
│ │ │ │ └── messages.ts
│ │ │ ├── errors.ts
│ │ │ ├── experimental.ts
│ │ │ ├── global.d.ts
│ │ │ ├── hooks
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── __snapshots__
│ │ │ │ │ │ └── useRoutingProps.test.tsx.snap
│ │ │ │ │ ├── useAuth.test.tsx
│ │ │ │ │ ├── useAuth.type.test.ts
│ │ │ │ │ └── useRoutingProps.test.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── useAssertWrappedByClerkProvider.ts
│ │ │ │ ├── useAuth.ts
│ │ │ │ ├── useClerkSignal.ts
│ │ │ │ ├── useEmailLink.ts
│ │ │ │ ├── useRoutingProps.ts
│ │ │ │ ├── useSignIn.ts
│ │ │ │ ├── useSignUp.ts
│ │ │ │ └── utils.ts
│ │ │ ├── index.ts
│ │ │ ├── internal.ts
│ │ │ ├── isomorphicClerk.ts
│ │ │ ├── polyfills.ts
│ │ │ ├── stateProxy.ts
│ │ │ ├── types.ts
│ │ │ └── utils
│ │ │ ├── __tests__
│ │ │ │ ├── useCustomMenuItems.test.tsx
│ │ │ │ └── useMaxAllowedInstancesGuard.test.tsx
│ │ │ ├── childrenUtils.tsx
│ │ │ ├── componentValidation.ts
│ │ │ ├── index.ts
│ │ │ ├── isConstructor.ts
│ │ │ ├── isDevOrStageUrl.tsx
│ │ │ ├── useCustomElementPortal.tsx
│ │ │ ├── useCustomMenuItems.tsx
│ │ │ ├── useCustomPages.tsx
│ │ │ ├── useMaxAllowedInstancesGuard.tsx
│ │ │ └── useWaitForComponentMount.ts
│ │ ├── subpaths.mjs
│ │ ├── tsconfig.declarations.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.test.json
│ │ ├── tsup.config.ts
│ │ ├── turbo.json
│ │ ├── typedoc.json
│ │ ├── vitest.config.mts
│ │ └── vitest.setup.mts
│ ├── react-router
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── __snapshots__
│ │ │ │ │ └── exports.test.ts.snap
│ │ │ │ └── exports.test.ts
│ │ │ ├── api
│ │ │ │ └── index.ts
│ │ │ ├── client
│ │ │ │ ├── index.ts
│ │ │ │ ├── ReactRouterClerkProvider.tsx
│ │ │ │ ├── ReactRouterOptionsContext.tsx
│ │ │ │ ├── types.ts
│ │ │ │ ├── uiComponents.tsx
│ │ │ │ ├── useAwaitableNavigate.tsx
│ │ │ │ └── usePathnameWithoutSplatRouteParams.tsx
│ │ │ ├── errors.ts
│ │ │ ├── errorThrower.ts
│ │ │ ├── global.d.ts
│ │ │ ├── index.ts
│ │ │ ├── server
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── clerkMiddleware.test.ts
│ │ │ │ │ ├── getAuth.test.ts
│ │ │ │ │ └── rootAuthLoader.test.ts
│ │ │ │ ├── clerkClient.ts
│ │ │ │ ├── clerkMiddleware.ts
│ │ │ │ ├── getAuth.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── legacyAuthenticateRequest.ts
│ │ │ │ ├── loadOptions.ts
│ │ │ │ ├── rootAuthLoader.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ ├── ssr
│ │ │ │ └── index.ts
│ │ │ ├── utils
│ │ │ │ ├── __tests__
│ │ │ │ │ └── assert.test.ts
│ │ │ │ ├── assert.ts
│ │ │ │ ├── env.ts
│ │ │ │ └── errors.ts
│ │ │ └── webhooks.ts
│ │ ├── tsconfig.json
│ │ ├── tsup.config.ts
│ │ ├── vitest.config.mts
│ │ └── vitest.setup.mts
│ ├── remix
│ │ ├── api.server
│ │ │ └── package.json
│ │ ├── CHANGELOG.md
│ │ ├── errors
│ │ │ └── package.json
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── __snapshots__
│ │ │ │ │ └── exports.test.ts.snap
│ │ │ │ └── exports.test.ts
│ │ │ ├── api
│ │ │ │ └── index.ts
│ │ │ ├── client
│ │ │ │ ├── ClerkApp.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── RemixClerkProvider.tsx
│ │ │ │ ├── RemixOptionsContext.tsx
│ │ │ │ ├── types.ts
│ │ │ │ ├── uiComponents.tsx
│ │ │ │ ├── useAwaitableNavigate.tsx
│ │ │ │ └── usePathnameWithoutSplatRouteParams.tsx
│ │ │ ├── errors.ts
│ │ │ ├── errorThrower.ts
│ │ │ ├── global.d.ts
│ │ │ ├── globalPolyfill.ts
│ │ │ ├── index.ts
│ │ │ ├── ssr
│ │ │ │ ├── authenticateRequest.ts
│ │ │ │ ├── getAuth.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── loadOptions.ts
│ │ │ │ ├── rootAuthLoader.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ └── utils
│ │ │ ├── errors.ts
│ │ │ ├── index.ts
│ │ │ └── utils.ts
│ │ ├── ssr.server
│ │ │ └── package.json
│ │ ├── tsconfig.declarations.json
│ │ ├── tsconfig.json
│ │ ├── tsup.config.ts
│ │ ├── vitest.config.mts
│ │ └── vitest.setup.mts
│ ├── shared
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── docs
│ │ │ ├── use-clerk.md
│ │ │ ├── use-session-list.md
│ │ │ ├── use-session.md
│ │ │ └── use-user.md
│ │ ├── global.d.ts
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── scripts
│ │ │ └── postinstall.mjs
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── apiUrlFromPublishableKey.spec.ts
│ │ │ │ ├── browser.spec.ts
│ │ │ │ ├── buildAccountsBaseUrl.spec.ts
│ │ │ │ ├── color.spec.ts
│ │ │ │ ├── color.test.ts.bak
│ │ │ │ ├── date.spec.ts
│ │ │ │ ├── deprecated.spec.ts
│ │ │ │ ├── deriveState.spec.ts
│ │ │ │ ├── devbrowser.spec.ts
│ │ │ │ ├── error.spec.ts
│ │ │ │ ├── eventBus.spec.ts
│ │ │ │ ├── fastDeepMerge.spec.ts
│ │ │ │ ├── handleValueOrFn.spec.ts
│ │ │ │ ├── jwtPayloadParser.spec.ts
│ │ │ │ ├── keys.spec.ts
│ │ │ │ ├── loadClerkJsScript.spec.ts
│ │ │ │ ├── localStorageBroadcastChannel.spec.ts
│ │ │ │ ├── logger.spec.ts
│ │ │ │ ├── netlifyCacheHandler.spec.ts
│ │ │ │ ├── organization.spec.ts
│ │ │ │ ├── pathMatcher.spec.ts
│ │ │ │ ├── proxy.spec.ts
│ │ │ │ ├── retry.spec.ts
│ │ │ │ ├── telemetry.logs.spec.ts
│ │ │ │ ├── underscore.spec.ts
│ │ │ │ ├── url.spec.ts
│ │ │ │ └── versionSelector.spec.ts
│ │ │ ├── alternativePhoneCode.ts
│ │ │ ├── apiUrlFromPublishableKey.ts
│ │ │ ├── authorization-errors.ts
│ │ │ ├── authorization.ts
│ │ │ ├── browser.ts
│ │ │ ├── buildAccountsBaseUrl.ts
│ │ │ ├── clerkEventBus.ts
│ │ │ ├── color.ts
│ │ │ ├── compiled
│ │ │ │ └── path-to-regexp
│ │ │ │ ├── index.js
│ │ │ │ └── LICENSE.txt
│ │ │ ├── constants.ts
│ │ │ ├── cookie.ts
│ │ │ ├── date.ts
│ │ │ ├── deprecated.ts
│ │ │ ├── deriveState.ts
│ │ │ ├── devBrowser.ts
│ │ │ ├── dom
│ │ │ │ ├── index.ts
│ │ │ │ └── waitForElement.ts
│ │ │ ├── error.ts
│ │ │ ├── errors
│ │ │ │ ├── clerkApiError.ts
│ │ │ │ ├── clerkApiResponseError.ts
│ │ │ │ ├── clerkError.ts
│ │ │ │ ├── clerkRuntimeError.ts
│ │ │ │ ├── createErrorTypeGuard.ts
│ │ │ │ ├── emailLinkError.ts
│ │ │ │ ├── errorThrower.ts
│ │ │ │ ├── globalHookError.ts
│ │ │ │ ├── helpers.ts
│ │ │ │ ├── metamaskError.ts
│ │ │ │ ├── parseError.ts
│ │ │ │ └── webAuthNError.ts
│ │ │ ├── eventBus.ts
│ │ │ ├── file.ts
│ │ │ ├── getEnvVariable.ts
│ │ │ ├── globs.ts
│ │ │ ├── handleValueOrFn.ts
│ │ │ ├── index.ts
│ │ │ ├── isomorphicAtob.ts
│ │ │ ├── isomorphicBtoa.ts
│ │ │ ├── jwtPayloadParser.ts
│ │ │ ├── keys.ts
│ │ │ ├── loadClerkJsScript.ts
│ │ │ ├── loadScript.ts
│ │ │ ├── localStorageBroadcastChannel.ts
│ │ │ ├── logger.ts
│ │ │ ├── netlifyCacheHandler.ts
│ │ │ ├── oauth.ts
│ │ │ ├── object.ts
│ │ │ ├── organization.ts
│ │ │ ├── pathMatcher.ts
│ │ │ ├── pathToRegexp.ts
│ │ │ ├── poller.ts
│ │ │ ├── proxy.ts
│ │ │ ├── react
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── commerce.test.tsx
│ │ │ │ │ └── useReverification.spec.ts
│ │ │ │ ├── clerk-rq
│ │ │ │ │ ├── queryOptions.ts
│ │ │ │ │ ├── types.ts
│ │ │ │ │ ├── use-clerk-query-client.ts
│ │ │ │ │ ├── useBaseQuery.ts
│ │ │ │ │ └── useQuery.ts
│ │ │ │ ├── clerk-swr.ts
│ │ │ │ ├── commerce.tsx
│ │ │ │ ├── contexts.tsx
│ │ │ │ ├── hooks
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ ├── createBillingPaginatedHook.spec.tsx
│ │ │ │ │ │ ├── useCheckout.type.spec.ts
│ │ │ │ │ │ ├── usePagesOrInfinite.spec.ts
│ │ │ │ │ │ ├── usePlans.spec.tsx
│ │ │ │ │ │ ├── usePreviousValue.spec.ts
│ │ │ │ │ │ ├── useSafeValues.spec.ts
│ │ │ │ │ │ ├── useSubscription.spec.tsx
│ │ │ │ │ │ └── wrapper.tsx
│ │ │ │ │ ├── createBillingPaginatedHook.tsx
│ │ │ │ │ ├── createContextAndHook.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── useAPIKeys.ts
│ │ │ │ │ ├── useCheckout.ts
│ │ │ │ │ ├── useClerk.ts
│ │ │ │ │ ├── useDeepEqualMemo.ts
│ │ │ │ │ ├── useOrganization.tsx
│ │ │ │ │ ├── useOrganizationList.tsx
│ │ │ │ │ ├── usePagesOrInfinite.ts
│ │ │ │ │ ├── usePaymentAttempts.tsx
│ │ │ │ │ ├── usePaymentMethods.tsx
│ │ │ │ │ ├── usePlans.tsx
│ │ │ │ │ ├── usePreviousValue.ts
│ │ │ │ │ ├── useReverification.ts
│ │ │ │ │ ├── useSafeLayoutEffect.tsx
│ │ │ │ │ ├── useSession.ts
│ │ │ │ │ ├── useSessionList.ts
│ │ │ │ │ ├── useStatements.tsx
│ │ │ │ │ ├── useSubscription.rq.tsx
│ │ │ │ │ ├── useSubscription.swr.tsx
│ │ │ │ │ ├── useSubscription.tsx
│ │ │ │ │ ├── useSubscription.types.ts
│ │ │ │ │ └── useUser.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── providers
│ │ │ │ │ ├── SWRConfigCompat.rq.tsx
│ │ │ │ │ ├── SWRConfigCompat.swr.tsx
│ │ │ │ │ └── SWRConfigCompat.tsx
│ │ │ │ ├── stripe-react
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── utils.ts
│ │ │ │ └── types.ts
│ │ │ ├── retry.ts
│ │ │ ├── router
│ │ │ │ ├── __tests__
│ │ │ │ │ └── router.spec.ts
│ │ │ │ ├── react.tsx
│ │ │ │ └── router.ts
│ │ │ ├── router.ts
│ │ │ ├── saml.ts
│ │ │ ├── telemetry
│ │ │ │ ├── collector.ts
│ │ │ │ ├── events
│ │ │ │ │ ├── __tests__
│ │ │ │ │ │ └── theme-usage.spec.ts
│ │ │ │ │ ├── component-mounted.ts
│ │ │ │ │ ├── framework-metadata.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── method-called.ts
│ │ │ │ │ └── theme-usage.ts
│ │ │ │ ├── throttler.ts
│ │ │ │ └── types.ts
│ │ │ ├── telemetry.ts
│ │ │ ├── types
│ │ │ │ ├── apiKeys.ts
│ │ │ │ ├── apiKeysSettings.ts
│ │ │ │ ├── appearance.ts
│ │ │ │ ├── attributes.ts
│ │ │ │ ├── authConfig.ts
│ │ │ │ ├── authObject.ts
│ │ │ │ ├── backupCode.ts
│ │ │ │ ├── billing.ts
│ │ │ │ ├── clerk.ts
│ │ │ │ ├── client.ts
│ │ │ │ ├── commerceSettings.ts
│ │ │ │ ├── customMenuItems.ts
│ │ │ │ ├── customPages.ts
│ │ │ │ ├── deletedObject.ts
│ │ │ │ ├── displayConfig.ts
│ │ │ │ ├── elementIds.ts
│ │ │ │ ├── emailAddress.ts
│ │ │ │ ├── enterpriseAccount.ts
│ │ │ │ ├── environment.ts
│ │ │ │ ├── errors.ts
│ │ │ │ ├── externalAccount.ts
│ │ │ │ ├── factors.ts
│ │ │ │ ├── hooks.ts
│ │ │ │ ├── identificationLink.ts
│ │ │ │ ├── identifiers.ts
│ │ │ │ ├── image.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── instance.ts
│ │ │ │ ├── json.ts
│ │ │ │ ├── jwt.ts
│ │ │ │ ├── jwtv2.ts
│ │ │ │ ├── key.ts
│ │ │ │ ├── localization.ts
│ │ │ │ ├── multiDomain.ts
│ │ │ │ ├── oauth.ts
│ │ │ │ ├── organization.ts
│ │ │ │ ├── organizationDomain.ts
│ │ │ │ ├── organizationInvitation.ts
│ │ │ │ ├── organizationMembership.ts
│ │ │ │ ├── organizationMembershipRequest.ts
│ │ │ │ ├── organizationSettings.ts
│ │ │ │ ├── organizationSuggestion.ts
│ │ │ │ ├── pagination.ts
│ │ │ │ ├── passkey.ts
│ │ │ │ ├── passwords.ts
│ │ │ │ ├── permission.ts
│ │ │ │ ├── phoneCodeChannel.ts
│ │ │ │ ├── phoneNumber.ts
│ │ │ │ ├── protect.ts
│ │ │ │ ├── redirects.ts
│ │ │ │ ├── resource.ts
│ │ │ │ ├── role.ts
│ │ │ │ ├── router.ts
│ │ │ │ ├── runtime-values.ts
│ │ │ │ ├── saml.ts
│ │ │ │ ├── samlAccount.ts
│ │ │ │ ├── samlConnection.ts
│ │ │ │ ├── session.ts
│ │ │ │ ├── sessionVerification.ts
│ │ │ │ ├── signIn.ts
│ │ │ │ ├── signInCommon.ts
│ │ │ │ ├── signInFuture.ts
│ │ │ │ ├── signUp.ts
│ │ │ │ ├── signUpCommon.ts
│ │ │ │ ├── signUpFuture.ts
│ │ │ │ ├── snapshots.ts
│ │ │ │ ├── ssr.ts
│ │ │ │ ├── state.ts
│ │ │ │ ├── strategies.ts
│ │ │ │ ├── telemetry.ts
│ │ │ │ ├── theme.ts
│ │ │ │ ├── token.ts
│ │ │ │ ├── totp.ts
│ │ │ │ ├── user.ts
│ │ │ │ ├── userOrganizationInvitation.ts
│ │ │ │ ├── userSettings.ts
│ │ │ │ ├── utils copy.ts
│ │ │ │ ├── utils.ts
│ │ │ │ ├── verification.ts
│ │ │ │ ├── virtual-data-hooks.d.ts
│ │ │ │ ├── waitlist.ts
│ │ │ │ ├── web3.ts
│ │ │ │ └── web3Wallet.ts
│ │ │ ├── underscore.ts
│ │ │ ├── url.ts
│ │ │ ├── utils
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── createDeferredPromise.spec.ts
│ │ │ │ │ └── instance.spec.ts
│ │ │ │ ├── allSettled.ts
│ │ │ │ ├── createDeferredPromise.ts
│ │ │ │ ├── fastDeepMerge.ts
│ │ │ │ ├── handleValueOrFn.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── instance.ts
│ │ │ │ ├── logErrorInDevMode.ts
│ │ │ │ ├── noop.ts
│ │ │ │ └── runtimeEnvironment.ts
│ │ │ ├── versionSelector.ts
│ │ │ ├── web3.ts
│ │ │ ├── webauthn.ts
│ │ │ └── workerTimers
│ │ │ ├── createWorkerTimers.ts
│ │ │ ├── index.ts
│ │ │ ├── workerTimers.built.ts
│ │ │ ├── workerTimers.types.ts
│ │ │ └── workerTimers.worker.ts
│ │ ├── subpaths.mjs
│ │ ├── tsconfig.json
│ │ ├── tsconfig.test.json
│ │ ├── tsdown.config.mts
│ │ ├── typedoc.json
│ │ ├── vitest.config.mts
│ │ └── vitest.setup.mts
│ ├── tanstack-react-start
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── errors
│ │ │ └── package.json
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── server
│ │ │ └── package.json
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── __snapshots__
│ │ │ │ │ └── exports.test.ts.snap
│ │ │ │ └── exports.test.ts
│ │ │ ├── client
│ │ │ │ ├── ClerkProvider.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── OptionsContext.tsx
│ │ │ │ ├── types.ts
│ │ │ │ ├── uiComponents.tsx
│ │ │ │ ├── useAwaitableNavigate.ts
│ │ │ │ └── utils.ts
│ │ │ ├── errors.ts
│ │ │ ├── experimental.ts
│ │ │ ├── global.d.ts
│ │ │ ├── index.ts
│ │ │ ├── server
│ │ │ │ ├── auth.ts
│ │ │ │ ├── clerkClient.ts
│ │ │ │ ├── clerkMiddleware.ts
│ │ │ │ ├── constants.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── loadOptions.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils
│ │ │ │ └── index.ts
│ │ │ ├── utils
│ │ │ │ ├── env.ts
│ │ │ │ ├── errors.ts
│ │ │ │ └── index.ts
│ │ │ └── webhooks.ts
│ │ ├── tsconfig.declarations.json
│ │ ├── tsconfig.json
│ │ ├── tsup.config.ts
│ │ ├── vite-env.d.ts
│ │ ├── vitest.config.mts
│ │ ├── vitest.setup.mts
│ │ └── webhooks
│ │ └── package.json
│ ├── testing
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── cypress
│ │ │ └── package.json
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── playwright
│ │ │ └── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── common
│ │ │ │ ├── constants.ts
│ │ │ │ ├── errors.ts
│ │ │ │ ├── helpers-utils.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── setup.ts
│ │ │ │ └── types.ts
│ │ │ ├── cypress
│ │ │ │ ├── custom-commands.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── setup.ts
│ │ │ │ └── setupClerkTestingToken.ts
│ │ │ ├── index.ts
│ │ │ └── playwright
│ │ │ ├── helpers.ts
│ │ │ ├── index.ts
│ │ │ ├── setup.ts
│ │ │ ├── setupClerkTestingToken.ts
│ │ │ └── unstable
│ │ │ ├── index.ts
│ │ │ └── page-objects
│ │ │ ├── apiKeys.ts
│ │ │ ├── app.ts
│ │ │ ├── checkout.ts
│ │ │ ├── clerk.ts
│ │ │ ├── common.ts
│ │ │ ├── expect.ts
│ │ │ ├── impersonation.ts
│ │ │ ├── index.ts
│ │ │ ├── keylessPopover.ts
│ │ │ ├── organizationSwitcher.ts
│ │ │ ├── planDetails.ts
│ │ │ ├── pricingTable.ts
│ │ │ ├── sessionTask.ts
│ │ │ ├── signIn.ts
│ │ │ ├── signUp.ts
│ │ │ ├── subscriptionDetails.ts
│ │ │ ├── testingToken.ts
│ │ │ ├── userAvatar.ts
│ │ │ ├── userButton.ts
│ │ │ ├── userProfile.ts
│ │ │ ├── userVerification.ts
│ │ │ └── waitlist.ts
│ │ ├── tsconfig.json
│ │ └── tsup.config.ts
│ ├── themes
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── createTheme.ts
│ │ │ ├── index.ts
│ │ │ └── themes
│ │ │ ├── dark.ts
│ │ │ ├── index.ts
│ │ │ ├── neobrutalism.ts
│ │ │ ├── shadcn.css
│ │ │ ├── shadcn.ts
│ │ │ ├── shadesOfPurple.ts
│ │ │ └── simple.ts
│ │ ├── tsconfig.json
│ │ └── tsup.config.ts
│ ├── types
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ └── src
│ │ ├── index.d.mts
│ │ ├── index.d.ts
│ │ ├── index.js
│ │ └── index.mjs
│ ├── upgrade
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── app.js
│ │ │ ├── cli.js
│ │ │ ├── codemods
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── __fixtures__
│ │ │ │ │ │ ├── transform-async-request.fixtures.js
│ │ │ │ │ │ └── transform-clerk-provider-dynamic.fixtures.js
│ │ │ │ │ ├── transform-async-request.test.js
│ │ │ │ │ └── transform-clerk-provider-dynamic.test.js
│ │ │ │ ├── index.js
│ │ │ │ ├── transform-async-request.cjs
│ │ │ │ └── transform-clerk-provider-dynamic.cjs
│ │ │ ├── components
│ │ │ │ ├── Codemod.js
│ │ │ │ ├── Command.js
│ │ │ │ ├── Header.js
│ │ │ │ ├── Scan.js
│ │ │ │ ├── SDKWorkflow.js
│ │ │ │ └── UpgradeSDK.js
│ │ │ ├── constants
│ │ │ │ ├── sdks.js
│ │ │ │ └── versions.js
│ │ │ ├── guide-generators
│ │ │ │ ├── core-2
│ │ │ │ │ ├── backend
│ │ │ │ │ │ ├── import-paths.mdx
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── intro.mdx
│ │ │ │ │ ├── chrome-extension
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── intro.mdx
│ │ │ │ │ ├── expo
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── intro.mdx
│ │ │ │ │ ├── fastify
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── intro.mdx
│ │ │ │ │ ├── generate-all.sh
│ │ │ │ │ ├── js
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── intro.mdx
│ │ │ │ │ ├── nextjs
│ │ │ │ │ │ ├── import-changes.mdx
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── intro.mdx
│ │ │ │ │ │ ├── middleware-changes.mdx
│ │ │ │ │ │ └── nextjs-version.mdx
│ │ │ │ │ ├── node
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── intro.mdx
│ │ │ │ │ │ └── node-setters-removals.mdx
│ │ │ │ │ ├── overview
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── intro.mdx
│ │ │ │ │ ├── react
│ │ │ │ │ │ ├── hof-removals.mdx
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── intro.mdx
│ │ │ │ │ ├── remix
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── intro.mdx
│ │ │ │ │ ├── retheme
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── intro.mdx
│ │ │ │ │ └── shared
│ │ │ │ │ ├── after-sign-x-handling.mdx
│ │ │ │ │ ├── cli.mdx
│ │ │ │ │ ├── deprecation-removals.mdx
│ │ │ │ │ ├── image-url.mdx
│ │ │ │ │ ├── node-version.mdx
│ │ │ │ │ ├── orgs-claim.mdx
│ │ │ │ │ ├── pagination-args.mdx
│ │ │ │ │ ├── pagination-return.mdx
│ │ │ │ │ ├── path-routing.mdx
│ │ │ │ │ ├── prepare.mdx
│ │ │ │ │ ├── react-version.mdx
│ │ │ │ │ ├── redesign-preview.mdx
│ │ │ │ │ └── update-version.mdx
│ │ │ │ └── text-generation.js
│ │ │ ├── img
│ │ │ │ ├── alternativemethods-backlink.png
│ │ │ │ ├── button-to-organizationlistcreateorganizationactionbutton.png
│ │ │ │ ├── connected-accounts-dropdown.png
│ │ │ │ ├── mfa-dropdown.png
│ │ │ │ ├── organizationprofile-settings.png
│ │ │ │ ├── remove-identitypreview-avatar.png
│ │ │ │ ├── remove-socialbuttonsblockbuttonarrow.png
│ │ │ │ ├── userbuttonpopoveractionbuttontext-removed.png
│ │ │ │ └── userprofile-security.png
│ │ │ ├── util
│ │ │ │ ├── all-titles.js
│ │ │ │ ├── expandable-list.js
│ │ │ │ ├── generate-changelog.js
│ │ │ │ ├── get-clerk-version.js
│ │ │ │ ├── guess-framework.js
│ │ │ │ └── load-change.js
│ │ │ └── versions
│ │ │ └── core-2
│ │ │ ├── backend
│ │ │ │ ├── api-url-value-changed.md
│ │ │ │ ├── authenticaterequest-params-change.md
│ │ │ │ ├── buildrequesturl-removed.md
│ │ │ │ ├── clerk-import.md
│ │ │ │ ├── client-unstableoptions-removed.md
│ │ │ │ ├── clockskewinseconds.md
│ │ │ │ ├── constants-import-path-move.md
│ │ │ │ ├── createauthenticaterequest-import-path-move.md
│ │ │ │ ├── createclerkclient-apikey.md
│ │ │ │ ├── createclerkclient-frontendapi.md
│ │ │ │ ├── createemail-removed.md
│ │ │ │ ├── createisomorphicrequest-import-path-move.md
│ │ │ │ ├── createisomorphicrequest-removed.md
│ │ │ │ ├── decodejwt-import-path-move.md
│ │ │ │ ├── getclientlist-arguments.md
│ │ │ │ ├── getorganizationmembershiplist-return-signature.md
│ │ │ │ ├── getpendingorganizationinvitationlist.md
│ │ │ │ ├── getsessionlist-arguments.md
│ │ │ │ ├── httpoptions-removed.md
│ │ │ │ ├── interstitial-removed.md
│ │ │ │ ├── members-count.md
│ │ │ │ ├── membershiprole.md
│ │ │ │ ├── organizationjson-logourl.md
│ │ │ │ ├── organizationmembershippublicuserdatajson-profileimageurl.md
│ │ │ │ ├── pkgversion.md
│ │ │ │ ├── redirect-import-path-move.md
│ │ │ │ ├── signjwt-import-path-move.md
│ │ │ │ ├── signjwterror-import-move.md
│ │ │ │ ├── tokenverificationerror-import-move.md
│ │ │ │ ├── tokenverificationerroraction-import-move.md
│ │ │ │ ├── tokenverificationerrorcode-import-move.md
│ │ │ │ ├── tokenverificationerrorreason-import-move.md
│ │ │ │ ├── userjson-profileimageurl.md
│ │ │ │ └── verifyjwt-import-path-move.md
│ │ │ ├── chrome-extension
│ │ │ │ └── clerkprovider-tokencache.md
│ │ │ ├── common
│ │ │ │ ├── aftersignxurl-changes.md
│ │ │ │ ├── afterswitchorganizationurl.md
│ │ │ │ ├── alternativemethods-backlink.md
│ │ │ │ ├── api-key-to-secret-key.md
│ │ │ │ ├── appearance-organizationpreview-organizationswitcher.md
│ │ │ │ ├── appearance-variables-breaking-changes.md
│ │ │ │ ├── button-to-organizationlistcreateorganizationactionbutton.md
│ │ │ │ ├── card-changes.md
│ │ │ │ ├── changed-localization-keys.md
│ │ │ │ ├── clerkprovider-frontendapi-2.md
│ │ │ │ ├── clerkprovideroptionswrapper-dropped.md
│ │ │ │ ├── connected-accounts-dropdown.md
│ │ │ │ ├── emaillinkerrorcode-import-change.md
│ │ │ │ ├── externalaccount-avatarurl.md
│ │ │ │ ├── externalaccount-picture.md
│ │ │ │ ├── externalaccountjson-avatarurl.md
│ │ │ │ ├── frontend-api-to-publishable-key.md
│ │ │ │ ├── getallowlistidentifierlist-return-signature.md
│ │ │ │ ├── getclientlist-return-signature.md
│ │ │ │ ├── getinvitationlist-return-signature.md
│ │ │ │ ├── getorganizationinvitationlist-return-signature.md
│ │ │ │ ├── getorganizationinvitationlist-return-type-change.md
│ │ │ │ ├── getorganizationlist-return-signature.md
│ │ │ │ ├── getorganizationlist-return-type-change.md
│ │ │ │ ├── getorganizationmembershiplist-return-type-change.md
│ │ │ │ ├── getredirecturllist-return-signature.md
│ │ │ │ ├── getsessionlist-return-signature.md
│ │ │ │ ├── getuserlist-return-signature.md
│ │ │ │ ├── getuseroauthaccesstoken-return-signature.md
│ │ │ │ ├── handlemagiclinkverification.md
│ │ │ │ ├── isclerkapiresponserror-import-change.md
│ │ │ │ ├── isemaillinkerror-import-change.md
│ │ │ │ ├── isknownerror-import-change.md
│ │ │ │ ├── ismagiclinkerror.md
│ │ │ │ ├── ismetamaskerror-import-change.md
│ │ │ │ ├── magiclinkerror.md
│ │ │ │ ├── magiclinkerrorcode.md
│ │ │ │ ├── mfa-dropdown.md
│ │ │ │ ├── min-react-version.md
│ │ │ │ ├── multisessionappsupport-import-change.md
│ │ │ │ ├── navigate-to-routerpush-routerreplace.md
│ │ │ │ ├── new-localization-keys.md
│ │ │ │ ├── organization-create-string.md
│ │ │ │ ├── organization-getdomains-arguments-change.md
│ │ │ │ ├── organization-getinvitations-arguments-changed.md
│ │ │ │ ├── organization-getmembershiprequests-arguments-changed.md
│ │ │ │ ├── organization-getmemberships-arguments-changed.md
│ │ │ │ ├── organization-getpendinginvitations.md
│ │ │ │ ├── organization-getroles-arguments-changed.md
│ │ │ │ ├── organization-logourl.md
│ │ │ │ ├── organizationmembershippublicuserdata-profileimageurl.md
│ │ │ │ ├── organizationprofile-settings.md
│ │ │ │ ├── organizationswitcherpopoveractionbuttontext-removed.md
│ │ │ │ ├── remove-identitypreview-avatar.md
│ │ │ │ ├── remove-socialbuttonsblockbuttonarrow.md
│ │ │ │ ├── removed-localization-keys.md
│ │ │ │ ├── setsession.md
│ │ │ │ ├── signoutcallback-to-redirecturl.md
│ │ │ │ ├── supported-external-accounts-removed.md
│ │ │ │ ├── usemagiclink.md
│ │ │ │ ├── useorganization-invitationlist.md
│ │ │ │ ├── useorganization-membershiplist.md
│ │ │ │ ├── useorganizations.md
│ │ │ │ ├── user-getorganizationinvitations-arguments-changed.md
│ │ │ │ ├── user-getorganizationmemberships-arguments-changed.md
│ │ │ │ ├── user-getorganizationmemberships-return-signature.md
│ │ │ │ ├── user-getorganizationsuggestions-arguments-changed.md
│ │ │ │ ├── user-profileimageurl.md
│ │ │ │ ├── user-update-password.md
│ │ │ │ ├── userbuttonpopoveractionbuttontext-removed.md
│ │ │ │ ├── userbuttontrigger-userbuttonbox-invert.md
│ │ │ │ ├── userprofile-prop.md
│ │ │ │ ├── userprofile-security.md
│ │ │ │ ├── withclerk-hof-removed.md
│ │ │ │ ├── withclerk-removed.md
│ │ │ │ ├── withsession-hof-removed.md
│ │ │ │ ├── withsession-removed.md
│ │ │ │ ├── withuser-hof-removed.md
│ │ │ │ └── withuser-removed-2.md
│ │ │ ├── expo
│ │ │ │ ├── apikey-to-publishable-key.md
│ │ │ │ └── clerkprovider-frontendapi.md
│ │ │ ├── fastify
│ │ │ │ ├── api-url-value-change.md
│ │ │ │ ├── clerk-import-change.md
│ │ │ │ ├── clerkplugin-frontendapi.md
│ │ │ │ ├── createclerkclient-apikey.md
│ │ │ │ └── createclerkclient-frontendapi.md
│ │ │ ├── index.js
│ │ │ ├── js
│ │ │ │ ├── clerk-import.md
│ │ │ │ ├── clerk-isready-removed.md
│ │ │ │ ├── experimental-canusecaptcha.md
│ │ │ │ ├── experimental-captchasitekey.md
│ │ │ │ ├── experimental-captchaurl.md
│ │ │ │ ├── getorganizationmemberships.md
│ │ │ │ ├── lastorganizationinvitation-member.md
│ │ │ │ ├── redirecttohome.md
│ │ │ │ ├── signup-attemptweb3walletverification-generatedsignature.md
│ │ │ │ ├── unstable-invitationupdate.md
│ │ │ │ ├── unstable-membershipupdate.md
│ │ │ │ ├── user-createexternalaccount-redirecturl.md
│ │ │ │ └── user-orgpublicdata-profileimageurl.md
│ │ │ ├── nextjs
│ │ │ │ ├── api-url-value-change.md
│ │ │ │ ├── auth-import-change.md
│ │ │ │ ├── auth-middleware-deprecated.md
│ │ │ │ ├── authmiddleware-apikey.md
│ │ │ │ ├── authmiddleware-frontendapi.md
│ │ │ │ ├── authmiddleware-import-change.md
│ │ │ │ ├── buildclerkprops-import-change.md
│ │ │ │ ├── clerk-import-change.md
│ │ │ │ ├── clerk-js-version-next-public.md
│ │ │ │ ├── constants-import-change.md
│ │ │ │ ├── createauthenticaterequest-import-change.md
│ │ │ │ ├── createclerkclient-apikey.md
│ │ │ │ ├── createclerkclient-frontendapi.md
│ │ │ │ ├── createisomorphicrequest-import-change.md
│ │ │ │ ├── currentuser-import-change.md
│ │ │ │ ├── decodejwt-import-change.md
│ │ │ │ ├── emaillinkerrorcode-import-change.md
│ │ │ │ ├── getauth-apikey.md
│ │ │ │ ├── import-api-url.md
│ │ │ │ ├── import-api-version.md
│ │ │ │ ├── import-clerk-js-url.md
│ │ │ │ ├── import-clerk-js-version.md
│ │ │ │ ├── import-domain.md
│ │ │ │ ├── import-is-satellite.md
│ │ │ │ ├── import-nextjs-api.md
│ │ │ │ ├── import-nextjs-app-beta.md
│ │ │ │ ├── import-nextjs-edge-middleware.md
│ │ │ │ ├── import-nextjs-edge-middlewarefiles.md
│ │ │ │ ├── import-nextjs-ssr.md
│ │ │ │ ├── import-proxy-url.md
│ │ │ │ ├── import-publishable-key.md
│ │ │ │ ├── import-secret-key.md
│ │ │ │ ├── import-sign-in-url.md
│ │ │ │ ├── import-sign-up-url.md
│ │ │ │ ├── isclerkapiresponserror-import-change.md
│ │ │ │ ├── isemaillinkerror-import-change.md
│ │ │ │ ├── isknownerror-import-change.md
│ │ │ │ ├── min-nextjs-version.md
│ │ │ │ ├── multisessionappsupport-import-change.md
│ │ │ │ ├── next-public-clerk-js-url.md
│ │ │ │ ├── redirect-import-change.md
│ │ │ │ ├── redirecttosignin-import-path.md
│ │ │ │ ├── redirecttosignup-import-path.md
│ │ │ │ ├── signjwt-import-change.md
│ │ │ │ ├── verifyjwt-import-change.md
│ │ │ │ ├── verifytoken-import-change.md
│ │ │ │ └── with-clerk-middleware-removed.md
│ │ │ ├── node
│ │ │ │ ├── api-url-value-change.md
│ │ │ │ ├── cjs-esm-instance.md
│ │ │ │ ├── clerk-import-change.md
│ │ │ │ ├── clerkexpressrequireauth-apikey.md
│ │ │ │ ├── clerkexpressrequireauth-frontendapi.md
│ │ │ │ ├── clerkexpresswithauth-apikey.md
│ │ │ │ ├── clerkexpresswithauth-frontendapi.md
│ │ │ │ ├── createclerkclient-apikey.md
│ │ │ │ ├── createclerkclient-frontendapi.md
│ │ │ │ ├── createclerkexpressrequireauth-apikey.md
│ │ │ │ ├── createclerkexpressrequireauth-frontendapi.md
│ │ │ │ ├── createclerkexpressrequireauth-public-key-required.md
│ │ │ │ ├── createclerkexpresswithauth-apikey.md
│ │ │ │ ├── createclerkexpresswithauth-frontendapi.md
│ │ │ │ ├── createclerkexpresswithauth-publickey-required.md
│ │ │ │ ├── legacyauthobject-removed.md
│ │ │ │ ├── setclerkapikey.md
│ │ │ │ ├── setclerkapiversion.md
│ │ │ │ ├── setclerkhttpoptions.md
│ │ │ │ └── setclerkserverapiurl.md
│ │ │ ├── react
│ │ │ │ └── api-url-value-change.md
│ │ │ ├── remix
│ │ │ │ ├── clerk-import-change.md
│ │ │ │ ├── clerkerrorboundary-removed.md
│ │ │ │ ├── createclerkclient-apikey.md
│ │ │ │ ├── getauth-apikey.md
│ │ │ │ ├── rootauthloader-apikey.md
│ │ │ │ └── rootauthloader-frontendapi.md
│ │ │ └── shared
│ │ │ ├── getrequesturl.md
│ │ │ ├── organizationcontext.md
│ │ │ └── useorganizationlist-organizationlist.md
│ │ ├── tsconfig.json
│ │ └── vitest.config.js
│ └── vue
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── package.json
│ ├── README.md
│ ├── src
│ │ ├── components
│ │ │ ├── __tests__
│ │ │ │ ├── SignInButton.test.ts
│ │ │ │ ├── SignInWithMetamaskButton.test.ts
│ │ │ │ ├── SignOutButton.test.ts
│ │ │ │ └── SignUpButton.test.ts
│ │ │ ├── CheckoutButton.vue
│ │ │ ├── ClerkHostRenderer.ts
│ │ │ ├── controlComponents.ts
│ │ │ ├── index.ts
│ │ │ ├── PlanDetailsButton.vue
│ │ │ ├── SignInButton.vue
│ │ │ ├── SignInWithMetamaskButton.vue
│ │ │ ├── SignOutButton.vue
│ │ │ ├── SignUpButton.vue
│ │ │ ├── SubscriptionDetailsButton.vue
│ │ │ └── ui-components
│ │ │ ├── CreateOrganization.vue
│ │ │ ├── GoogleOneTap.vue
│ │ │ ├── OrganizationList.vue
│ │ │ ├── OrganizationProfile
│ │ │ │ ├── index.ts
│ │ │ │ └── OrganizationProfile.vue
│ │ │ ├── OrganizationSwitcher
│ │ │ │ ├── index.ts
│ │ │ │ └── OrganizationSwitcher.vue
│ │ │ ├── PricingTable.vue
│ │ │ ├── SignIn.vue
│ │ │ ├── SignUp.vue
│ │ │ ├── UserAvatar.vue
│ │ │ ├── UserButton
│ │ │ │ ├── index.ts
│ │ │ │ └── UserButton.vue
│ │ │ ├── UserProfile
│ │ │ │ ├── index.ts
│ │ │ │ └── UserProfile.vue
│ │ │ └── Waitlist.vue
│ │ ├── composables
│ │ │ ├── __tests__
│ │ │ │ └── useClerkContext.test.ts
│ │ │ ├── index.ts
│ │ │ ├── useAuth.ts
│ │ │ ├── useClerk.ts
│ │ │ ├── useClerkContext.ts
│ │ │ ├── useOrganization.ts
│ │ │ ├── useSession.ts
│ │ │ ├── useSessionList.ts
│ │ │ ├── useSignIn.ts
│ │ │ ├── useSignUp.ts
│ │ │ └── useUser.ts
│ │ ├── env.d.ts
│ │ ├── errors
│ │ │ ├── errorThrower.ts
│ │ │ └── messages.ts
│ │ ├── errors.ts
│ │ ├── experimental.ts
│ │ ├── global.d.ts
│ │ ├── index.ts
│ │ ├── internal.ts
│ │ ├── keys.ts
│ │ ├── plugin.ts
│ │ ├── types.ts
│ │ └── utils
│ │ ├── __tests__
│ │ │ ├── useCustomElementPortal.test.ts
│ │ │ └── useCustomMenuItems.test.ts
│ │ ├── childrenUtils.ts
│ │ ├── componentValidation.ts
│ │ ├── index.ts
│ │ ├── toComputedRefs.ts
│ │ ├── updateClerkOptions.ts
│ │ ├── useClerkLoaded.ts
│ │ ├── useCustomElementPortal.ts
│ │ ├── useCustomMenuItems.ts
│ │ └── useCustomPages.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ ├── vitest.config.ts
│ └── vitest.setup.ts
├── patches
│ └── [email protected]
├── playground
│ ├── app-router
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── next.config.js
│ │ ├── package.json
│ │ ├── public
│ │ │ ├── next.svg
│ │ │ └── vercel.svg
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── app
│ │ │ │ ├── action
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── api
│ │ │ │ │ └── hello
│ │ │ │ │ └── route.ts
│ │ │ │ ├── client
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── favicon.ico
│ │ │ │ ├── globals.css
│ │ │ │ ├── layout.tsx
│ │ │ │ ├── page.module.css
│ │ │ │ ├── page.tsx
│ │ │ │ ├── protected
│ │ │ │ │ ├── ClientSideWrapper.tsx
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── sign-in
│ │ │ │ │ └── [[...catchall]]
│ │ │ │ │ └── page.tsx
│ │ │ │ └── sign-up
│ │ │ │ └── [[...catchall]]
│ │ │ │ └── page.tsx
│ │ │ ├── common
│ │ │ │ └── Links.tsx
│ │ │ ├── middleware.ts
│ │ │ └── pages
│ │ │ ├── _app.tsx
│ │ │ ├── profile
│ │ │ │ └── [[...index]].tsx
│ │ │ └── user
│ │ │ └── [[...index]].tsx
│ │ └── tsconfig.json
│ ├── browser-extension
│ │ ├── .env.chrome.example
│ │ ├── .env.development.example
│ │ ├── .gitignore
│ │ ├── assets
│ │ │ └── icon.png
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── assets
│ │ │ │ ├── chrome-extension-sdk-2.jpg
│ │ │ │ ├── dark-logo.png
│ │ │ │ ├── icon.png
│ │ │ │ └── light-logo.png
│ │ │ ├── background
│ │ │ │ └── index.ts
│ │ │ ├── components
│ │ │ │ ├── nav-bar.tsx
│ │ │ │ └── ui
│ │ │ │ └── button.tsx
│ │ │ ├── content.tsx
│ │ │ ├── features
│ │ │ │ └── count-button.tsx
│ │ │ ├── popup
│ │ │ │ ├── index.tsx
│ │ │ │ ├── layouts
│ │ │ │ │ └── root-layout.tsx
│ │ │ │ └── routes
│ │ │ │ ├── home.tsx
│ │ │ │ ├── sdk-features.tsx
│ │ │ │ ├── settings.tsx
│ │ │ │ ├── sign-in.tsx
│ │ │ │ └── sign-up.tsx
│ │ │ ├── sidepanel
│ │ │ │ └── index.tsx
│ │ │ ├── style.css
│ │ │ ├── tabs
│ │ │ │ ├── background-worker-demo.html
│ │ │ │ └── background-worker-demo.tsx
│ │ │ └── utils
│ │ │ └── components.ts
│ │ ├── tailwind.config.js
│ │ └── tsconfig.json
│ ├── cra-js
│ │ ├── .gitignore
│ │ ├── package.json
│ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ ├── index.html
│ │ │ ├── logo192.png
│ │ │ ├── logo512.png
│ │ │ ├── manifest.json
│ │ │ └── robots.txt
│ │ ├── README.md
│ │ └── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ ├── reportWebVitals.js
│ │ └── setupTests.js
│ ├── expo
│ │ ├── .env.example
│ │ ├── .gitignore
│ │ ├── app.json.example
│ │ ├── App.tsx
│ │ ├── babel.config.js
│ │ ├── metro.config.js
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── tsconfig.json
│ │ └── webpack.config.js
│ ├── express
│ │ ├── .env.sample
│ │ ├── .gitignore
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── loadEnv.ts
│ │ │ ├── routes
│ │ │ │ ├── index.ts
│ │ │ │ ├── private.ts
│ │ │ │ └── public.ts
│ │ │ ├── server.ts
│ │ │ └── views
│ │ │ └── home.ejs
│ │ └── tsconfig.json
│ ├── fastify
│ │ ├── .env.sample
│ │ ├── .gitignore
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── loadEnv.ts
│ │ │ ├── routes
│ │ │ │ ├── index.ts
│ │ │ │ ├── private.ts
│ │ │ │ └── public.ts
│ │ │ ├── server.ts
│ │ │ └── templates
│ │ │ └── sign-in.ejs
│ │ └── tsconfig.json
│ ├── nextjs
│ │ ├── .env.example
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── app
│ │ │ ├── api
│ │ │ │ └── route.ts
│ │ │ ├── app-dir
│ │ │ │ ├── client
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── client-component.tsx
│ │ │ │ ├── create-organization
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── discover
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── organization
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── page.tsx
│ │ │ │ ├── sign-in
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── sign-up
│ │ │ │ │ └── page.tsx
│ │ │ │ └── user
│ │ │ │ └── page.tsx
│ │ │ └── layout.tsx
│ │ ├── middleware.ts
│ │ ├── next.config.js
│ │ ├── package.json
│ │ ├── pages
│ │ │ ├── _app.tsx
│ │ │ ├── api
│ │ │ │ ├── hello-edge.ts
│ │ │ │ └── hello.ts
│ │ │ ├── create-organization
│ │ │ │ └── [[...index]].tsx
│ │ │ ├── custom
│ │ │ │ └── forgotPassword.tsx
│ │ │ ├── discover
│ │ │ │ └── index.tsx
│ │ │ ├── index.tsx
│ │ │ ├── organization
│ │ │ │ └── [[...index]].tsx
│ │ │ ├── organization-list
│ │ │ │ └── [[...index]].tsx
│ │ │ ├── redirect-helpers
│ │ │ │ └── index.tsx
│ │ │ ├── session-examples
│ │ │ │ └── index.tsx
│ │ │ ├── sign-in
│ │ │ │ └── [[...index]].tsx
│ │ │ ├── sign-up
│ │ │ │ └── [[...index]].tsx
│ │ │ ├── user
│ │ │ │ └── [[...index]].tsx
│ │ │ ├── user-examples
│ │ │ │ └── index.tsx
│ │ │ └── waitlist
│ │ │ └── index.tsx
│ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ └── vercel.svg
│ │ ├── README.md
│ │ ├── styles
│ │ │ ├── globals.css
│ │ │ └── Home.module.css
│ │ └── tsconfig.json
│ ├── react-router
│ │ ├── .env.example
│ │ ├── .gitignore
│ │ ├── app
│ │ │ ├── app.css
│ │ │ ├── root.tsx
│ │ │ ├── routes
│ │ │ │ ├── home.tsx
│ │ │ │ ├── profile-form.tsx
│ │ │ │ ├── profile.tsx
│ │ │ │ ├── sign-in.tsx
│ │ │ │ ├── sign-up.tsx
│ │ │ │ ├── use-auth.tsx
│ │ │ │ └── use-user.tsx
│ │ │ ├── routes.ts
│ │ │ └── welcome
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo-light.svg
│ │ │ └── welcome.tsx
│ │ ├── package.json
│ │ ├── public
│ │ │ └── favicon.ico
│ │ ├── react-router.config.ts
│ │ ├── README.md
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── remix-cf-pages
│ │ ├── .gitignore
│ │ ├── .node-version
│ │ ├── app
│ │ │ ├── entry.client.tsx
│ │ │ ├── entry.server.tsx
│ │ │ ├── root.tsx
│ │ │ └── routes
│ │ │ ├── index.tsx
│ │ │ └── sign-in.tsx
│ │ ├── package.json
│ │ ├── public
│ │ │ ├── _headers
│ │ │ ├── _routes.json
│ │ │ └── favicon.ico
│ │ ├── README.md
│ │ ├── remix.config.js
│ │ ├── remix.env.d.ts
│ │ ├── server.js
│ │ └── tsconfig.json
│ ├── remix-cf-worker
│ │ ├── .gitignore
│ │ ├── app
│ │ │ ├── entry.client.tsx
│ │ │ ├── entry.server.tsx
│ │ │ ├── root.tsx
│ │ │ └── routes
│ │ │ └── index.tsx
│ │ ├── package.json
│ │ ├── public
│ │ │ └── favicon.ico
│ │ ├── README.md
│ │ ├── remix.config.js
│ │ ├── remix.env.d.ts
│ │ ├── server.js
│ │ ├── tsconfig.json
│ │ └── wrangler.toml
│ ├── remix-node
│ │ ├── .gitignore
│ │ ├── app
│ │ │ ├── entry.client.tsx
│ │ │ ├── entry.server.tsx
│ │ │ ├── root.tsx
│ │ │ └── routes
│ │ │ ├── _index.tsx
│ │ │ ├── protected.tsx
│ │ │ ├── sign-in.$.tsx
│ │ │ └── sign-up.$.tsx
│ │ ├── package.json
│ │ ├── public
│ │ │ └── favicon.ico
│ │ ├── README.md
│ │ ├── remix.config.js
│ │ ├── remix.env.d.ts
│ │ └── tsconfig.json
│ ├── vanillajs
│ │ └── index.html
│ └── vite-react-ts
│ ├── .env.example
│ ├── .gitignore
│ ├── index.html
│ ├── package.json
│ ├── public
│ │ ├── clerk.svg
│ │ └── vite.svg
│ ├── src
│ │ ├── App.css
│ │ ├── App.tsx
│ │ ├── assets
│ │ │ └── react.svg
│ │ ├── index.css
│ │ ├── main.tsx
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── prettier.config.mjs
├── README.md
├── renovate.json5
├── scripts
│ ├── .env.example
│ ├── 1password-keys.mjs
│ ├── backport.mjs
│ ├── canary.mjs
│ ├── common.mjs
│ ├── format-non-workspace.mjs
│ ├── format-package.mjs
│ ├── install-site-in-isolation.mjs
│ ├── lint.mjs
│ ├── notify.mjs
│ ├── nuke.mjs
│ ├── renovate-config-generator.mjs
│ ├── search-for-rhc.mjs
│ ├── snapshot.mjs
│ ├── subpath-workaround.mjs
│ ├── tsconfig.json
│ ├── utils.ts
│ └── vitest-debug.mjs
├── tsconfig.json
├── tsconfig.typedoc.json
├── turbo.json
├── typedoc.config.mjs
├── verdaccio.install.yaml
├── verdaccio.publish.yaml
└── vitest.workspace.mjs
```
# Files
--------------------------------------------------------------------------------
/packages/clerk-js/src/core/clerk.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { inBrowser as inClientSide, isValidBrowserOnline } from '@clerk/shared/browser';
2 | import { clerkEvents, createClerkEventBus } from '@clerk/shared/clerkEventBus';
3 | import { deprecated } from '@clerk/shared/deprecated';
4 | import {
5 | ClerkRuntimeError,
6 | EmailLinkError,
7 | EmailLinkErrorCodeStatus,
8 | is4xxError,
9 | isClerkAPIResponseError,
10 | isClerkRuntimeError,
11 | } from '@clerk/shared/error';
12 | import { parsePublishableKey } from '@clerk/shared/keys';
13 | import { logger } from '@clerk/shared/logger';
14 | import { CLERK_NETLIFY_CACHE_BUST_PARAM } from '@clerk/shared/netlifyCacheHandler';
15 | import { isHttpOrHttps, isValidProxyUrl, proxyUrlToAbsoluteURL } from '@clerk/shared/proxy';
16 | import {
17 | eventPrebuiltComponentMounted,
18 | eventPrebuiltComponentOpened,
19 | eventThemeUsage,
20 | TelemetryCollector,
21 | } from '@clerk/shared/telemetry';
22 | import type {
23 | __experimental_CheckoutInstance,
24 | __experimental_CheckoutOptions,
25 | __internal_CheckoutProps,
26 | __internal_OAuthConsentProps,
27 | __internal_PlanDetailsProps,
28 | __internal_SubscriptionDetailsProps,
29 | __internal_UserVerificationModalProps,
30 | APIKeysNamespace,
31 | APIKeysProps,
32 | AuthenticateWithBaseParams,
33 | AuthenticateWithCoinbaseWalletParams,
34 | AuthenticateWithGoogleOneTapParams,
35 | AuthenticateWithMetamaskParams,
36 | AuthenticateWithOKXWalletParams,
37 | BillingNamespace,
38 | Clerk as ClerkInterface,
39 | ClerkAPIError,
40 | ClerkAuthenticateWithWeb3Params,
41 | ClerkOptions,
42 | ClientJSONSnapshot,
43 | ClientResource,
44 | CreateOrganizationParams,
45 | CreateOrganizationProps,
46 | CredentialReturn,
47 | DomainOrProxyUrl,
48 | EnvironmentJSON,
49 | EnvironmentJSONSnapshot,
50 | EnvironmentResource,
51 | GenerateSignatureParams,
52 | GoogleOneTapProps,
53 | HandleEmailLinkVerificationParams,
54 | HandleOAuthCallbackParams,
55 | InstanceType,
56 | JoinWaitlistParams,
57 | ListenerCallback,
58 | NavigateOptions,
59 | OrganizationListProps,
60 | OrganizationProfileProps,
61 | OrganizationResource,
62 | OrganizationSwitcherProps,
63 | PricingTableProps,
64 | PublicKeyCredentialCreationOptionsWithoutExtensions,
65 | PublicKeyCredentialRequestOptionsWithoutExtensions,
66 | PublicKeyCredentialWithAuthenticatorAssertionResponse,
67 | PublicKeyCredentialWithAuthenticatorAttestationResponse,
68 | RedirectOptions,
69 | Resources,
70 | SDKMetadata,
71 | SessionResource,
72 | SetActiveParams,
73 | SignedInSessionResource,
74 | SignInProps,
75 | SignInRedirectOptions,
76 | SignInResource,
77 | SignOut,
78 | SignOutCallback,
79 | SignOutOptions,
80 | SignUpField,
81 | SignUpProps,
82 | SignUpRedirectOptions,
83 | SignUpResource,
84 | TaskChooseOrganizationProps,
85 | TasksRedirectOptions,
86 | UnsubscribeCallback,
87 | UserAvatarProps,
88 | UserButtonProps,
89 | UserProfileProps,
90 | UserResource,
91 | WaitlistProps,
92 | WaitlistResource,
93 | Web3Provider,
94 | } from '@clerk/shared/types';
95 | import { addClerkPrefix, isAbsoluteUrl, stripScheme } from '@clerk/shared/url';
96 | import { allSettled, handleValueOrFn, noop } from '@clerk/shared/utils';
97 | import type { QueryClient } from '@tanstack/query-core';
98 |
99 | import { debugLogger, initDebugLogger } from '@/utils/debug';
100 |
101 | import type { MountComponentRenderer } from '../ui/Components';
102 | import {
103 | ALLOWED_PROTOCOLS,
104 | buildURL,
105 | canViewOrManageAPIKeys,
106 | completeSignUpFlow,
107 | createAllowedRedirectOrigins,
108 | createBeforeUnloadTracker,
109 | createPageLifecycle,
110 | disabledAllBillingFeatures,
111 | disabledAPIKeysFeature,
112 | disabledOrganizationsFeature,
113 | errorThrower,
114 | generateSignatureWithBase,
115 | generateSignatureWithCoinbaseWallet,
116 | generateSignatureWithMetamask,
117 | generateSignatureWithOKXWallet,
118 | getClerkQueryParam,
119 | getWeb3Identifier,
120 | hasExternalAccountSignUpError,
121 | inActiveBrowserTab,
122 | inBrowser,
123 | isDevAccountPortalOrigin,
124 | isError,
125 | isOrganizationId,
126 | isRedirectForFAPIInitiatedFlow,
127 | isSignedInAndSingleSessionModeEnabled,
128 | noOrganizationExists,
129 | noUserExists,
130 | processCssLayerNameExtraction,
131 | removeClerkQueryParam,
132 | requiresUserInput,
133 | stripOrigin,
134 | windowNavigate,
135 | } from '../utils';
136 | import { assertNoLegacyProp } from '../utils/assertNoLegacyProp';
137 | import { CLERK_ENVIRONMENT_STORAGE_ENTRY, SafeLocalStorage } from '../utils/localStorage';
138 | import { memoizeListenerCallback } from '../utils/memoizeStateListenerCallback';
139 | import { RedirectUrls } from '../utils/redirectUrls';
140 | import { AuthCookieService } from './auth/AuthCookieService';
141 | import { CaptchaHeartbeat } from './auth/CaptchaHeartbeat';
142 | import { CLERK_SATELLITE_URL, CLERK_SUFFIXED_COOKIES, CLERK_SYNCED, ERROR_CODES } from './constants';
143 | import {
144 | clerkErrorInitFailed,
145 | clerkInvalidSignInUrlFormat,
146 | clerkInvalidSignInUrlOrigin,
147 | clerkMissingProxyUrlAndDomain,
148 | clerkMissingSignInUrlAsSatellite,
149 | clerkOAuthCallbackDidNotCompleteSignInSignUp,
150 | clerkRedirectUrlIsMissingScheme,
151 | clerkUnsupportedEnvironmentWarning,
152 | } from './errors';
153 | import { eventBus, events } from './events';
154 | import type { FapiClient, FapiRequestCallback } from './fapiClient';
155 | import { createFapiClient } from './fapiClient';
156 | import { createClientFromJwt } from './jwt-client';
157 | import { APIKeys } from './modules/apiKeys';
158 | import { Billing } from './modules/billing';
159 | import { createCheckoutInstance } from './modules/checkout/instance';
160 | import { BaseResource, Client, Environment, Organization, Waitlist } from './resources/internal';
161 | import { getTaskEndpoint, navigateIfTaskExists, warnMissingPendingTaskHandlers } from './sessionTasks';
162 | import { State } from './state';
163 | import { warnings } from './warnings';
164 |
165 | type SetActiveHook = (intent?: 'sign-out') => void | Promise<void>;
166 |
167 | declare global {
168 | interface Window {
169 | Clerk?: Clerk;
170 | __clerk_publishable_key?: string;
171 | __clerk_proxy_url?: ClerkInterface['proxyUrl'];
172 | __clerk_domain?: ClerkInterface['domain'];
173 | }
174 | }
175 |
176 | const CANNOT_RENDER_BILLING_DISABLED_ERROR_CODE = 'cannot_render_billing_disabled';
177 | const CANNOT_RENDER_USER_MISSING_ERROR_CODE = 'cannot_render_user_missing';
178 | const CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE = 'cannot_render_organizations_disabled';
179 | const CANNOT_RENDER_ORGANIZATION_MISSING_ERROR_CODE = 'cannot_render_organization_missing';
180 | const CANNOT_RENDER_SINGLE_SESSION_ENABLED_ERROR_CODE = 'cannot_render_single_session_enabled';
181 | const CANNOT_RENDER_API_KEYS_DISABLED_ERROR_CODE = 'cannot_render_api_keys_disabled';
182 | const CANNOT_RENDER_API_KEYS_ORG_UNAUTHORIZED_ERROR_CODE = 'cannot_render_api_keys_org_unauthorized';
183 | const defaultOptions: ClerkOptions = {
184 | polling: true,
185 | standardBrowser: true,
186 | touchSession: true,
187 | isSatellite: false,
188 | signInUrl: undefined,
189 | signUpUrl: undefined,
190 | afterSignOutUrl: undefined,
191 | signInFallbackRedirectUrl: undefined,
192 | signUpFallbackRedirectUrl: undefined,
193 | signInForceRedirectUrl: undefined,
194 | signUpForceRedirectUrl: undefined,
195 | newSubscriptionRedirectUrl: undefined,
196 | };
197 |
198 | export class Clerk implements ClerkInterface {
199 | public static mountComponentRenderer?: MountComponentRenderer;
200 |
201 | public static version: string = __PKG_VERSION__;
202 | public static sdkMetadata: SDKMetadata = {
203 | name: __PKG_NAME__,
204 | version: __PKG_VERSION__,
205 | };
206 |
207 | private static _billing: BillingNamespace;
208 | private static _apiKeys: APIKeysNamespace;
209 | private _checkout: ClerkInterface['__experimental_checkout'] | undefined;
210 |
211 | public client: ClientResource | undefined;
212 | public session: SignedInSessionResource | null | undefined;
213 | public organization: OrganizationResource | null | undefined;
214 | public user: UserResource | null | undefined;
215 | public __internal_country?: string | null;
216 | public telemetry: TelemetryCollector | undefined;
217 | public readonly __internal_state: State = new State();
218 |
219 | protected internal_last_error: ClerkAPIError | null = null;
220 | // converted to protected environment to support `updateEnvironment` type assertion
221 | protected environment?: EnvironmentResource | null;
222 |
223 | #queryClient: QueryClient | undefined;
224 | #publishableKey = '';
225 | #domain: DomainOrProxyUrl['domain'];
226 | #proxyUrl: DomainOrProxyUrl['proxyUrl'];
227 | #authService?: AuthCookieService;
228 | #captchaHeartbeat?: CaptchaHeartbeat;
229 | #broadcastChannel: BroadcastChannel | null = null;
230 | #componentControls?: ReturnType<MountComponentRenderer> | null;
231 | //@ts-expect-error with being undefined even though it's not possible - related to issue with ts and error thrower
232 | #fapiClient: FapiClient;
233 | #instanceType?: InstanceType;
234 | #status: ClerkInterface['status'] = 'loading';
235 | #listeners: Array<(emission: Resources) => void> = [];
236 | #navigationListeners: Array<() => void> = [];
237 | #options: ClerkOptions = {};
238 | #pageLifecycle: ReturnType<typeof createPageLifecycle> | null = null;
239 | #touchThrottledUntil = 0;
240 | #publicEventBus = createClerkEventBus();
241 |
242 | get __internal_queryClient(): { __tag: 'clerk-rq-client'; client: QueryClient } | undefined {
243 | if (!this.#queryClient) {
244 | void import('./query-core')
245 | .then(module => module.QueryClient)
246 | .then(QueryClient => {
247 | if (this.#queryClient) {
248 | return;
249 | }
250 | this.#queryClient = new QueryClient();
251 | // @ts-expect-error - queryClientStatus is not typed
252 | this.#publicEventBus.emit('queryClientStatus', 'ready');
253 | });
254 | }
255 |
256 | return this.#queryClient
257 | ? {
258 | __tag: 'clerk-rq-client',
259 | client: this.#queryClient,
260 | }
261 | : undefined;
262 | }
263 |
264 | public __internal_getCachedResources:
265 | | (() => Promise<{ client: ClientJSONSnapshot | null; environment: EnvironmentJSONSnapshot | null }>)
266 | | undefined;
267 |
268 | public __internal_createPublicCredentials:
269 | | ((
270 | publicKey: PublicKeyCredentialCreationOptionsWithoutExtensions,
271 | ) => Promise<CredentialReturn<PublicKeyCredentialWithAuthenticatorAttestationResponse>>)
272 | | undefined;
273 |
274 | public __internal_getPublicCredentials:
275 | | (({
276 | publicKeyOptions,
277 | }: {
278 | publicKeyOptions: PublicKeyCredentialRequestOptionsWithoutExtensions;
279 | }) => Promise<CredentialReturn<PublicKeyCredentialWithAuthenticatorAssertionResponse>>)
280 | | undefined;
281 |
282 | public __internal_isWebAuthnSupported: (() => boolean) | undefined;
283 | public __internal_isWebAuthnAutofillSupported: (() => Promise<boolean>) | undefined;
284 | public __internal_isWebAuthnPlatformAuthenticatorSupported: (() => Promise<boolean>) | undefined;
285 |
286 | public __internal_setActiveInProgress = false;
287 |
288 | get publishableKey(): string {
289 | return this.#publishableKey;
290 | }
291 |
292 | get version(): string {
293 | return Clerk.version;
294 | }
295 |
296 | set sdkMetadata(metadata: SDKMetadata) {
297 | Clerk.sdkMetadata = metadata;
298 | }
299 |
300 | get sdkMetadata(): SDKMetadata {
301 | return Clerk.sdkMetadata;
302 | }
303 |
304 | get loaded(): boolean {
305 | return this.status === 'degraded' || this.status === 'ready';
306 | }
307 |
308 | get status(): ClerkInterface['status'] {
309 | return this.#status;
310 | }
311 |
312 | get isSatellite(): boolean {
313 | if (inBrowser()) {
314 | return handleValueOrFn(this.#options.isSatellite, new URL(window.location.href), false);
315 | }
316 | return false;
317 | }
318 |
319 | get domain(): string {
320 | if (inBrowser()) {
321 | const strippedDomainString = stripScheme(handleValueOrFn(this.#domain, new URL(window.location.href)));
322 | if (this.#instanceType === 'production') {
323 | return addClerkPrefix(strippedDomainString);
324 | }
325 | return strippedDomainString;
326 | }
327 | return '';
328 | }
329 |
330 | get proxyUrl(): string {
331 | if (inBrowser()) {
332 | const _unfilteredProxy = handleValueOrFn(this.#proxyUrl, new URL(window.location.href));
333 | if (!isValidProxyUrl(_unfilteredProxy)) {
334 | errorThrower.throwInvalidProxyUrl({ url: _unfilteredProxy });
335 | }
336 | return proxyUrlToAbsoluteURL(_unfilteredProxy);
337 | }
338 | return '';
339 | }
340 |
341 | get frontendApi(): string {
342 | const publishableKey = parsePublishableKey(this.publishableKey);
343 |
344 | if (!publishableKey) {
345 | return errorThrower.throwInvalidPublishableKeyError({
346 | key: this.publishableKey,
347 | });
348 | }
349 |
350 | return publishableKey.frontendApi;
351 | }
352 |
353 | get instanceType() {
354 | return this.#instanceType;
355 | }
356 |
357 | get isStandardBrowser(): boolean {
358 | return this.#options.standardBrowser || false;
359 | }
360 |
361 | get billing(): BillingNamespace {
362 | if (!Clerk._billing) {
363 | Clerk._billing = new Billing();
364 | }
365 | return Clerk._billing;
366 | }
367 |
368 | get apiKeys(): APIKeysNamespace {
369 | if (!Clerk._apiKeys) {
370 | Clerk._apiKeys = new APIKeys();
371 | }
372 | return Clerk._apiKeys;
373 | }
374 |
375 | __experimental_checkout(options: __experimental_CheckoutOptions): __experimental_CheckoutInstance {
376 | if (!this._checkout) {
377 | this._checkout = params => createCheckoutInstance(this, params);
378 | }
379 | return this._checkout(options);
380 | }
381 |
382 | public __internal_getOption<K extends keyof ClerkOptions>(key: K): ClerkOptions[K] {
383 | return this.#options[key];
384 | }
385 |
386 | get isSignedIn(): boolean {
387 | const hasPendingSession = this?.session?.status === 'pending';
388 | if (hasPendingSession) {
389 | return false;
390 | }
391 |
392 | return !!this.session;
393 | }
394 |
395 | public constructor(key: string, options?: DomainOrProxyUrl) {
396 | key = (key || '').trim();
397 |
398 | if (!key) {
399 | return errorThrower.throwMissingPublishableKeyError();
400 | }
401 |
402 | const publishableKey = parsePublishableKey(key);
403 |
404 | if (!publishableKey) {
405 | return errorThrower.throwInvalidPublishableKeyError({ key });
406 | }
407 |
408 | this.#domain = options?.domain;
409 | this.#proxyUrl = options?.proxyUrl;
410 | this.environment = Environment.getInstance();
411 | this.#instanceType = publishableKey.instanceType;
412 | this.#publishableKey = key;
413 |
414 | this.#fapiClient = createFapiClient({
415 | domain: this.domain,
416 | frontendApi: this.frontendApi,
417 | // this.instanceType is assigned above
418 | instanceType: this.instanceType as InstanceType,
419 | isSatellite: this.isSatellite,
420 | getSessionId: () => {
421 | return this.session?.id;
422 | },
423 | proxyUrl: this.proxyUrl,
424 | });
425 | this.#publicEventBus.emit(clerkEvents.Status, 'loading');
426 | this.#publicEventBus.prioritizedOn(clerkEvents.Status, s => (this.#status = s));
427 |
428 | // This line is used for the piggy-backing mechanism
429 | BaseResource.clerk = this;
430 | }
431 |
432 | public getFapiClient = (): FapiClient => this.#fapiClient;
433 |
434 | public load = async (options?: ClerkOptions): Promise<void> => {
435 | debugLogger.info('load() start', {}, 'clerk');
436 | if (this.loaded) {
437 | return;
438 | }
439 |
440 | // Log a development mode warning once
441 | if (this.#instanceType === 'development') {
442 | logger.warnOnce(
443 | 'Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview',
444 | );
445 | }
446 |
447 | this.#options = this.#initOptions(options);
448 |
449 | // In development mode, if custom router options are provided, warn if both routerPush and routerReplace are not provided
450 | if (
451 | this.#instanceType === 'development' &&
452 | (this.#options.routerPush || this.#options.routerReplace) &&
453 | (!this.#options.routerPush || !this.#options.routerReplace)
454 | ) {
455 | // Typing this.#options as ClerkOptions to ensure proper type checking. TypeScript will infer the type as `never`
456 | // since missing both `routerPush` and `routerReplace` is not a valid ClerkOptions.
457 | const options = this.#options as ClerkOptions;
458 | const missingRouter = !options.routerPush ? 'routerPush' : 'routerReplace';
459 | logger.warnOnce(
460 | `Clerk: Both \`routerPush\` and \`routerReplace\` need to be defined, but \`${missingRouter}\` is not defined. This may cause issues with navigation in your application.`,
461 | );
462 | }
463 |
464 | /**
465 | * Listen to `Session.getToken` resolving to emit the updated session
466 | * with the new token to the state listeners.
467 | */
468 | eventBus.on(events.SessionTokenResolved, () => {
469 | this.#setAccessors(this.session);
470 | this.#emit();
471 | });
472 |
473 | assertNoLegacyProp(this.#options);
474 |
475 | if (this.#options.sdkMetadata) {
476 | Clerk.sdkMetadata = this.#options.sdkMetadata;
477 | }
478 |
479 | if (this.#options.telemetry !== false) {
480 | this.telemetry = new TelemetryCollector({
481 | clerkVersion: Clerk.version,
482 | samplingRate: 1,
483 | perEventSampling: this.#options.__internal_keyless_claimKeylessApplicationUrl ? false : undefined,
484 | publishableKey: this.publishableKey,
485 | ...this.#options.telemetry,
486 | });
487 |
488 | // Record theme usage telemetry when appearance is provided
489 | if (this.#options.appearance) {
490 | this.telemetry.record(eventThemeUsage(this.#options.appearance));
491 | }
492 | }
493 |
494 | try {
495 | if (this.#options.standardBrowser) {
496 | await this.#loadInStandardBrowser();
497 | } else {
498 | await this.#loadInNonStandardBrowser();
499 | }
500 | const telemetry = this.#options.telemetry;
501 | const telemetryEnabled = telemetry !== false && !telemetry?.disabled;
502 |
503 | const isKeyless = Boolean(this.#options.__internal_keyless_claimKeylessApplicationUrl);
504 | const hasClientDebugMode = Boolean(this.environment?.clientDebugMode);
505 | const isProd = this.environment?.isProduction?.() ?? false;
506 |
507 | const shouldEnable = hasClientDebugMode || (isKeyless && !isProd);
508 | const logLevel = isKeyless && !hasClientDebugMode ? 'error' : undefined;
509 |
510 | if (shouldEnable) {
511 | initDebugLogger({
512 | enabled: true,
513 | ...(logLevel ? { logLevel } : {}),
514 | ...(telemetryEnabled && this.telemetry ? { telemetryCollector: this.telemetry } : {}),
515 | });
516 | }
517 | debugLogger.info('load() complete', {}, 'clerk');
518 | } catch (error) {
519 | this.#publicEventBus.emit(clerkEvents.Status, 'error');
520 | debugLogger.error('load() failed', { error }, 'clerk');
521 | // bubble up the error
522 | throw error;
523 | }
524 | };
525 |
526 | #isCombinedSignInOrUpFlow(): boolean {
527 | return Boolean(!this.#options.signUpUrl && this.#options.signInUrl && !isAbsoluteUrl(this.#options.signInUrl));
528 | }
529 |
530 | public signOut: SignOut = async (callbackOrOptions?: SignOutCallback | SignOutOptions, options?: SignOutOptions) => {
531 | if (!this.client || this.client.sessions.length === 0) {
532 | return;
533 | }
534 |
535 | const onBeforeSetActive: SetActiveHook =
536 | typeof window !== 'undefined' && typeof window.__unstable__onBeforeSetActive === 'function'
537 | ? window.__unstable__onBeforeSetActive
538 | : noop;
539 |
540 | const onAfterSetActive: SetActiveHook =
541 | typeof window !== 'undefined' && typeof window.__unstable__onAfterSetActive === 'function'
542 | ? window.__unstable__onAfterSetActive
543 | : noop;
544 |
545 | const opts = callbackOrOptions && typeof callbackOrOptions === 'object' ? callbackOrOptions : options || {};
546 |
547 | const redirectUrl = opts?.redirectUrl || this.buildAfterSignOutUrl();
548 | debugLogger.debug(
549 | 'signOut() start',
550 | {
551 | hasClient: Boolean(this.client),
552 | multiSessionCount: this.client?.signedInSessions.length ?? 0,
553 | redirectUrl,
554 | sessionTarget: opts?.sessionId ?? null,
555 | },
556 | 'clerk',
557 | );
558 | const signOutCallback = typeof callbackOrOptions === 'function' ? callbackOrOptions : undefined;
559 |
560 | const executeSignOut = async () => {
561 | const tracker = createBeforeUnloadTracker(this.#options.standardBrowser);
562 |
563 | // Notify other tabs that user is signing out and clean up cookies.
564 | eventBus.emit(events.UserSignOut, null);
565 |
566 | this.#setTransitiveState();
567 |
568 | await tracker.track(async () => {
569 | if (signOutCallback) {
570 | await signOutCallback();
571 | } else {
572 | await this.navigate(redirectUrl);
573 | }
574 | });
575 |
576 | if (tracker.isUnloading()) {
577 | return;
578 | }
579 |
580 | this.#setAccessors();
581 | this.#emit();
582 |
583 | await onAfterSetActive();
584 | };
585 |
586 | /**
587 | * Clears the router cache for `@clerk/nextjs` on all routes except the current one.
588 | * Note: Calling `onBeforeSetActive` before signing out, allows for new RSC prefetch requests to render as signed in.
589 | * Since we are calling `onBeforeSetActive` before signing out, we should NOT pass `"sign-out"`.
590 | */
591 | await onBeforeSetActive();
592 | if (!opts.sessionId || this.client.signedInSessions.length === 1) {
593 | if (this.#options.experimental?.persistClient ?? true) {
594 | await this.client.removeSessions();
595 | } else {
596 | await this.client.destroy();
597 | }
598 |
599 | await executeSignOut();
600 |
601 | debugLogger.info('signOut() complete', { redirectUrl: stripOrigin(redirectUrl) }, 'clerk');
602 | return;
603 | }
604 |
605 | // Multi-session handling
606 | const session = this.client.signedInSessions.find(s => s.id === opts.sessionId);
607 | const shouldSignOutCurrent = session?.id && this.session?.id === session.id;
608 |
609 | await session?.remove();
610 |
611 | if (shouldSignOutCurrent) {
612 | await executeSignOut();
613 | debugLogger.info('signOut() complete', { redirectUrl: stripOrigin(redirectUrl) }, 'clerk');
614 | }
615 | };
616 |
617 | public openGoogleOneTap = (props?: GoogleOneTapProps): void => {
618 | const component = 'GoogleOneTap';
619 | this.assertComponentsReady(this.#componentControls);
620 | void this.#componentControls
621 | .ensureMounted({ preloadHint: component })
622 | .then(controls => controls.openModal('googleOneTap', props || {}));
623 |
624 | this.telemetry?.record(eventPrebuiltComponentOpened(component, props));
625 | };
626 |
627 | public closeGoogleOneTap = (): void => {
628 | this.assertComponentsReady(this.#componentControls);
629 | void this.#componentControls.ensureMounted().then(controls => controls.closeModal('googleOneTap'));
630 | };
631 |
632 | public openSignIn = (props?: SignInProps): void => {
633 | this.assertComponentsReady(this.#componentControls);
634 | if (isSignedInAndSingleSessionModeEnabled(this, this.environment)) {
635 | if (this.#instanceType === 'development') {
636 | throw new ClerkRuntimeError(warnings.cannotOpenSignInOrSignUp, {
637 | code: CANNOT_RENDER_SINGLE_SESSION_ENABLED_ERROR_CODE,
638 | });
639 | }
640 | return;
641 | }
642 | const component = 'SignIn';
643 | void this.#componentControls
644 | .ensureMounted({ preloadHint: component })
645 | .then(controls => controls.openModal('signIn', props || {}));
646 |
647 | const additionalData = { withSignUp: props?.withSignUp ?? this.#isCombinedSignInOrUpFlow() };
648 | this.telemetry?.record(eventPrebuiltComponentOpened(component, props, additionalData));
649 | };
650 |
651 | public closeSignIn = (): void => {
652 | this.assertComponentsReady(this.#componentControls);
653 | void this.#componentControls.ensureMounted().then(controls => controls.closeModal('signIn'));
654 | };
655 |
656 | public __internal_openCheckout = (props?: __internal_CheckoutProps): void => {
657 | this.assertComponentsReady(this.#componentControls);
658 | if (disabledAllBillingFeatures(this, this.environment)) {
659 | if (this.#instanceType === 'development') {
660 | throw new ClerkRuntimeError(warnings.cannotRenderAnyBillingComponent('Checkout'), {
661 | code: CANNOT_RENDER_BILLING_DISABLED_ERROR_CODE,
662 | });
663 | }
664 | return;
665 | }
666 | if (noUserExists(this)) {
667 | if (this.#instanceType === 'development') {
668 | throw new ClerkRuntimeError(warnings.cannotOpenCheckout, {
669 | code: CANNOT_RENDER_USER_MISSING_ERROR_CODE,
670 | });
671 | }
672 | return;
673 | }
674 |
675 | void this.#componentControls
676 | .ensureMounted({ preloadHint: 'Checkout' })
677 | .then(controls => controls.openDrawer('checkout', props || {}));
678 | };
679 |
680 | public __internal_closeCheckout = (): void => {
681 | this.assertComponentsReady(this.#componentControls);
682 | void this.#componentControls.ensureMounted().then(controls => controls.closeDrawer('checkout'));
683 | };
684 |
685 | public __internal_openPlanDetails = (props: __internal_PlanDetailsProps): void => {
686 | this.assertComponentsReady(this.#componentControls);
687 | if (disabledAllBillingFeatures(this, this.environment)) {
688 | if (this.#instanceType === 'development') {
689 | throw new ClerkRuntimeError(warnings.cannotRenderAnyBillingComponent('PlanDetails'), {
690 | code: CANNOT_RENDER_BILLING_DISABLED_ERROR_CODE,
691 | });
692 | }
693 | return;
694 | }
695 | const component = 'PlanDetails';
696 | void this.#componentControls
697 | .ensureMounted({ preloadHint: component })
698 | .then(controls => controls.openDrawer('planDetails', props || {}));
699 |
700 | this.telemetry?.record(eventPrebuiltComponentOpened(component, props));
701 | };
702 |
703 | public __internal_closePlanDetails = (): void => {
704 | this.assertComponentsReady(this.#componentControls);
705 | void this.#componentControls.ensureMounted().then(controls => controls.closeDrawer('planDetails'));
706 | };
707 |
708 | public __internal_openSubscriptionDetails = (props?: __internal_SubscriptionDetailsProps): void => {
709 | this.assertComponentsReady(this.#componentControls);
710 | void this.#componentControls
711 | .ensureMounted({ preloadHint: 'SubscriptionDetails' })
712 | .then(controls => controls.openDrawer('subscriptionDetails', props || {}));
713 | };
714 |
715 | public __internal_closeSubscriptionDetails = (): void => {
716 | this.assertComponentsReady(this.#componentControls);
717 | void this.#componentControls.ensureMounted().then(controls => controls.closeDrawer('subscriptionDetails'));
718 | };
719 |
720 | public __internal_openReverification = (props?: __internal_UserVerificationModalProps): void => {
721 | this.assertComponentsReady(this.#componentControls);
722 | if (noUserExists(this)) {
723 | if (this.#instanceType === 'development') {
724 | throw new ClerkRuntimeError(warnings.cannotOpenUserProfile, {
725 | code: CANNOT_RENDER_USER_MISSING_ERROR_CODE,
726 | });
727 | }
728 | return;
729 | }
730 | void this.#componentControls
731 | .ensureMounted({ preloadHint: 'UserVerification' })
732 | .then(controls => controls.openModal('userVerification', props || {}));
733 |
734 | this.telemetry?.record(eventPrebuiltComponentOpened(`UserVerification`, props));
735 | };
736 |
737 | public __internal_closeReverification = (): void => {
738 | this.assertComponentsReady(this.#componentControls);
739 | void this.#componentControls.ensureMounted().then(controls => controls.closeModal('userVerification'));
740 | };
741 |
742 | public __internal_openBlankCaptchaModal = (): Promise<unknown> => {
743 | this.assertComponentsReady(this.#componentControls);
744 | return this.#componentControls
745 | .ensureMounted({ preloadHint: 'BlankCaptchaModal' })
746 | .then(controls => controls.openModal('blankCaptcha', {}));
747 | };
748 |
749 | public __internal_closeBlankCaptchaModal = (): Promise<unknown> => {
750 | this.assertComponentsReady(this.#componentControls);
751 | return this.#componentControls
752 | .ensureMounted({ preloadHint: 'BlankCaptchaModal' })
753 | .then(controls => controls.closeModal('blankCaptcha'));
754 | };
755 |
756 | public __internal_loadStripeJs = async () => {
757 | if (__BUILD_DISABLE_RHC__) {
758 | clerkUnsupportedEnvironmentWarning('Stripe');
759 | return { loadStripe: () => Promise.resolve(null) };
760 | }
761 |
762 | const { loadStripe } = await import('@stripe/stripe-js');
763 | return loadStripe;
764 | };
765 |
766 | public openSignUp = (props?: SignUpProps): void => {
767 | this.assertComponentsReady(this.#componentControls);
768 | if (isSignedInAndSingleSessionModeEnabled(this, this.environment)) {
769 | if (this.#instanceType === 'development') {
770 | throw new ClerkRuntimeError(warnings.cannotOpenSignInOrSignUp, {
771 | code: CANNOT_RENDER_SINGLE_SESSION_ENABLED_ERROR_CODE,
772 | });
773 | }
774 | return;
775 | }
776 | void this.#componentControls
777 | .ensureMounted({ preloadHint: 'SignUp' })
778 | .then(controls => controls.openModal('signUp', props || {}));
779 |
780 | this.telemetry?.record(eventPrebuiltComponentOpened('SignUp', props));
781 | };
782 |
783 | public closeSignUp = (): void => {
784 | this.assertComponentsReady(this.#componentControls);
785 | void this.#componentControls.ensureMounted().then(controls => controls.closeModal('signUp'));
786 | };
787 |
788 | public openUserProfile = (props?: UserProfileProps): void => {
789 | this.assertComponentsReady(this.#componentControls);
790 | if (noUserExists(this)) {
791 | if (this.#instanceType === 'development') {
792 | throw new ClerkRuntimeError(warnings.cannotOpenUserProfile, {
793 | code: CANNOT_RENDER_USER_MISSING_ERROR_CODE,
794 | });
795 | }
796 | return;
797 | }
798 | void this.#componentControls
799 | .ensureMounted({ preloadHint: 'UserProfile' })
800 | .then(controls => controls.openModal('userProfile', props || {}));
801 |
802 | const additionalData = (props?.customPages?.length || 0) > 0 ? { customPages: true } : undefined;
803 | this.telemetry?.record(eventPrebuiltComponentOpened('UserProfile', props, additionalData));
804 | };
805 |
806 | public closeUserProfile = (): void => {
807 | this.assertComponentsReady(this.#componentControls);
808 | void this.#componentControls.ensureMounted().then(controls => controls.closeModal('userProfile'));
809 | };
810 |
811 | public openOrganizationProfile = (props?: OrganizationProfileProps): void => {
812 | this.assertComponentsReady(this.#componentControls);
813 | if (disabledOrganizationsFeature(this, this.environment)) {
814 | if (this.#instanceType === 'development') {
815 | throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationProfile'), {
816 | code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
817 | });
818 | }
819 | return;
820 | }
821 | if (noOrganizationExists(this)) {
822 | if (this.#instanceType === 'development') {
823 | throw new ClerkRuntimeError(warnings.cannotRenderComponentWhenOrgDoesNotExist, {
824 | code: CANNOT_RENDER_ORGANIZATION_MISSING_ERROR_CODE,
825 | });
826 | }
827 | return;
828 | }
829 | void this.#componentControls
830 | .ensureMounted({ preloadHint: 'OrganizationProfile' })
831 | .then(controls => controls.openModal('organizationProfile', props || {}));
832 |
833 | this.telemetry?.record(eventPrebuiltComponentOpened('OrganizationProfile', props));
834 | };
835 |
836 | public closeOrganizationProfile = (): void => {
837 | this.assertComponentsReady(this.#componentControls);
838 | void this.#componentControls.ensureMounted().then(controls => controls.closeModal('organizationProfile'));
839 | };
840 |
841 | public openCreateOrganization = (props?: CreateOrganizationProps): void => {
842 | this.assertComponentsReady(this.#componentControls);
843 | if (disabledOrganizationsFeature(this, this.environment)) {
844 | if (this.#instanceType === 'development') {
845 | throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('CreateOrganization'), {
846 | code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
847 | });
848 | }
849 | return;
850 | }
851 | void this.#componentControls
852 | .ensureMounted({ preloadHint: 'CreateOrganization' })
853 | .then(controls => controls.openModal('createOrganization', props || {}));
854 |
855 | this.telemetry?.record(eventPrebuiltComponentOpened('CreateOrganization', props));
856 | };
857 |
858 | public closeCreateOrganization = (): void => {
859 | this.assertComponentsReady(this.#componentControls);
860 | void this.#componentControls.ensureMounted().then(controls => controls.closeModal('createOrganization'));
861 | };
862 |
863 | public openWaitlist = (props?: WaitlistProps): void => {
864 | this.assertComponentsReady(this.#componentControls);
865 | void this.#componentControls
866 | .ensureMounted({ preloadHint: 'Waitlist' })
867 | .then(controls => controls.openModal('waitlist', props || {}));
868 |
869 | this.telemetry?.record(eventPrebuiltComponentOpened('Waitlist', props));
870 | };
871 |
872 | public closeWaitlist = (): void => {
873 | this.assertComponentsReady(this.#componentControls);
874 | void this.#componentControls.ensureMounted().then(controls => controls.closeModal('waitlist'));
875 | };
876 |
877 | public mountSignIn = (node: HTMLDivElement, props?: SignInProps): void => {
878 | this.assertComponentsReady(this.#componentControls);
879 | const component = 'SignIn';
880 | void this.#componentControls.ensureMounted({ preloadHint: component }).then(controls =>
881 | controls.mountComponent({
882 | name: component,
883 | appearanceKey: 'signIn',
884 | node,
885 | props,
886 | }),
887 | );
888 |
889 | const additionalData = { withSignUp: props?.withSignUp ?? this.#isCombinedSignInOrUpFlow() };
890 | this.telemetry?.record(eventPrebuiltComponentMounted(component, props, additionalData));
891 | };
892 |
893 | public unmountSignIn = (node: HTMLDivElement): void => {
894 | this.assertComponentsReady(this.#componentControls);
895 | void this.#componentControls.ensureMounted().then(controls =>
896 | controls.unmountComponent({
897 | node,
898 | }),
899 | );
900 | };
901 |
902 | public mountUserAvatar = (node: HTMLDivElement, props?: UserAvatarProps): void => {
903 | this.assertComponentsReady(this.#componentControls);
904 | const component = 'UserAvatar';
905 | void this.#componentControls.ensureMounted({ preloadHint: component }).then(controls =>
906 | controls.mountComponent({
907 | name: component,
908 | appearanceKey: 'userAvatar',
909 | node,
910 | props,
911 | }),
912 | );
913 |
914 | this.telemetry?.record(eventPrebuiltComponentMounted(component, props));
915 | };
916 |
917 | public unmountUserAvatar = (node: HTMLDivElement): void => {
918 | this.assertComponentsReady(this.#componentControls);
919 | void this.#componentControls.ensureMounted().then(controls =>
920 | controls.unmountComponent({
921 | node,
922 | }),
923 | );
924 | };
925 |
926 | public mountSignUp = (node: HTMLDivElement, props?: SignUpProps): void => {
927 | this.assertComponentsReady(this.#componentControls);
928 | const component = 'SignUp';
929 | void this.#componentControls.ensureMounted({ preloadHint: component }).then(controls =>
930 | controls.mountComponent({
931 | name: component,
932 | appearanceKey: 'signUp',
933 | node,
934 | props,
935 | }),
936 | );
937 |
938 | this.telemetry?.record(eventPrebuiltComponentMounted(component, props));
939 | };
940 |
941 | public unmountSignUp = (node: HTMLDivElement): void => {
942 | this.assertComponentsReady(this.#componentControls);
943 | void this.#componentControls.ensureMounted().then(controls =>
944 | controls.unmountComponent({
945 | node,
946 | }),
947 | );
948 | };
949 |
950 | public mountUserProfile = (node: HTMLDivElement, props?: UserProfileProps): void => {
951 | this.assertComponentsReady(this.#componentControls);
952 | if (noUserExists(this)) {
953 | if (this.#instanceType === 'development') {
954 | throw new ClerkRuntimeError(warnings.cannotRenderComponentWhenUserDoesNotExist, {
955 | code: CANNOT_RENDER_USER_MISSING_ERROR_CODE,
956 | });
957 | }
958 | return;
959 | }
960 | const component = 'UserProfile';
961 | void this.#componentControls.ensureMounted({ preloadHint: component }).then(controls =>
962 | controls.mountComponent({
963 | name: component,
964 | appearanceKey: 'userProfile',
965 | node,
966 | props,
967 | }),
968 | );
969 |
970 | const additionalData = (props?.customPages?.length || 0) > 0 ? { customPages: true } : undefined;
971 | this.telemetry?.record(eventPrebuiltComponentMounted(component, props, additionalData));
972 | };
973 |
974 | public unmountUserProfile = (node: HTMLDivElement): void => {
975 | this.assertComponentsReady(this.#componentControls);
976 | void this.#componentControls.ensureMounted().then(controls =>
977 | controls.unmountComponent({
978 | node,
979 | }),
980 | );
981 | };
982 |
983 | public mountOrganizationProfile = (node: HTMLDivElement, props?: OrganizationProfileProps) => {
984 | this.assertComponentsReady(this.#componentControls);
985 | if (disabledOrganizationsFeature(this, this.environment)) {
986 | if (this.#instanceType === 'development') {
987 | throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationProfile'), {
988 | code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
989 | });
990 | }
991 | return;
992 | }
993 | const userExists = !noUserExists(this);
994 | if (noOrganizationExists(this) && userExists) {
995 | if (this.#instanceType === 'development') {
996 | throw new ClerkRuntimeError(warnings.cannotRenderComponentWhenOrgDoesNotExist, {
997 | code: CANNOT_RENDER_ORGANIZATION_MISSING_ERROR_CODE,
998 | });
999 | }
1000 | return;
1001 | }
1002 | void this.#componentControls.ensureMounted({ preloadHint: 'OrganizationProfile' }).then(controls =>
1003 | controls.mountComponent({
1004 | name: 'OrganizationProfile',
1005 | appearanceKey: 'userProfile',
1006 | node,
1007 | props,
1008 | }),
1009 | );
1010 |
1011 | this.telemetry?.record(eventPrebuiltComponentMounted('OrganizationProfile', props));
1012 | };
1013 |
1014 | public unmountOrganizationProfile = (node: HTMLDivElement) => {
1015 | this.assertComponentsReady(this.#componentControls);
1016 | void this.#componentControls.ensureMounted().then(controls =>
1017 | controls.unmountComponent({
1018 | node,
1019 | }),
1020 | );
1021 | };
1022 |
1023 | public mountCreateOrganization = (node: HTMLDivElement, props?: CreateOrganizationProps) => {
1024 | this.assertComponentsReady(this.#componentControls);
1025 | if (disabledOrganizationsFeature(this, this.environment)) {
1026 | if (this.#instanceType === 'development') {
1027 | throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('CreateOrganization'), {
1028 | code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
1029 | });
1030 | }
1031 | return;
1032 | }
1033 | void this.#componentControls?.ensureMounted({ preloadHint: 'CreateOrganization' }).then(controls =>
1034 | controls.mountComponent({
1035 | name: 'CreateOrganization',
1036 | appearanceKey: 'createOrganization',
1037 | node,
1038 | props,
1039 | }),
1040 | );
1041 |
1042 | this.telemetry?.record(eventPrebuiltComponentMounted('CreateOrganization', props));
1043 | };
1044 |
1045 | public unmountCreateOrganization = (node: HTMLDivElement) => {
1046 | this.assertComponentsReady(this.#componentControls);
1047 | void this.#componentControls?.ensureMounted().then(controls =>
1048 | controls.unmountComponent({
1049 | node,
1050 | }),
1051 | );
1052 | };
1053 |
1054 | public mountOrganizationSwitcher = (node: HTMLDivElement, props?: OrganizationSwitcherProps) => {
1055 | this.assertComponentsReady(this.#componentControls);
1056 | if (disabledOrganizationsFeature(this, this.environment)) {
1057 | if (this.#instanceType === 'development') {
1058 | throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationSwitcher'), {
1059 | code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
1060 | });
1061 | }
1062 | return;
1063 | }
1064 | void this.#componentControls?.ensureMounted({ preloadHint: 'OrganizationSwitcher' }).then(controls =>
1065 | controls.mountComponent({
1066 | name: 'OrganizationSwitcher',
1067 | appearanceKey: 'organizationSwitcher',
1068 | node,
1069 | props,
1070 | }),
1071 | );
1072 |
1073 | this.telemetry?.record(
1074 | eventPrebuiltComponentMounted('OrganizationSwitcher', {
1075 | ...props,
1076 | forceOrganizationSelection: this.environment?.organizationSettings.forceOrganizationSelection,
1077 | }),
1078 | );
1079 | };
1080 |
1081 | public unmountOrganizationSwitcher = (node: HTMLDivElement): void => {
1082 | this.assertComponentsReady(this.#componentControls);
1083 | void this.#componentControls?.ensureMounted().then(controls => controls.unmountComponent({ node }));
1084 | };
1085 |
1086 | public __experimental_prefetchOrganizationSwitcher = () => {
1087 | this.assertComponentsReady(this.#componentControls);
1088 | void this.#componentControls
1089 | ?.ensureMounted({ preloadHint: 'OrganizationSwitcher' })
1090 | .then(controls => controls.prefetch('organizationSwitcher'));
1091 | };
1092 |
1093 | public mountOrganizationList = (node: HTMLDivElement, props?: OrganizationListProps) => {
1094 | this.assertComponentsReady(this.#componentControls);
1095 | if (disabledOrganizationsFeature(this, this.environment)) {
1096 | if (this.#instanceType === 'development') {
1097 | throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationList'), {
1098 | code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
1099 | });
1100 | }
1101 | return;
1102 | }
1103 | void this.#componentControls?.ensureMounted({ preloadHint: 'OrganizationList' }).then(controls =>
1104 | controls.mountComponent({
1105 | name: 'OrganizationList',
1106 | appearanceKey: 'organizationList',
1107 | node,
1108 | props,
1109 | }),
1110 | );
1111 |
1112 | this.telemetry?.record(
1113 | eventPrebuiltComponentMounted('OrganizationList', {
1114 | ...props,
1115 | forceOrganizationSelection: this.environment?.organizationSettings.forceOrganizationSelection,
1116 | }),
1117 | );
1118 | };
1119 |
1120 | public unmountOrganizationList = (node: HTMLDivElement): void => {
1121 | this.assertComponentsReady(this.#componentControls);
1122 | void this.#componentControls?.ensureMounted().then(controls => controls.unmountComponent({ node }));
1123 | };
1124 |
1125 | public mountUserButton = (node: HTMLDivElement, props?: UserButtonProps) => {
1126 | this.assertComponentsReady(this.#componentControls);
1127 | void this.#componentControls?.ensureMounted({ preloadHint: 'UserButton' }).then(controls =>
1128 | controls.mountComponent({
1129 | name: 'UserButton',
1130 | appearanceKey: 'userButton',
1131 | node,
1132 | props,
1133 | }),
1134 | );
1135 |
1136 | const additionalData = {
1137 | ...(props?.customMenuItems?.length || 0 > 0 ? { customItems: true } : undefined),
1138 | ...(props?.__experimental_asStandalone ? { standalone: true } : undefined),
1139 | };
1140 |
1141 | this.telemetry?.record(eventPrebuiltComponentMounted('UserButton', props, additionalData));
1142 | };
1143 |
1144 | public unmountUserButton = (node: HTMLDivElement): void => {
1145 | this.assertComponentsReady(this.#componentControls);
1146 | void this.#componentControls?.ensureMounted().then(controls => controls.unmountComponent({ node }));
1147 | };
1148 |
1149 | public mountWaitlist = (node: HTMLDivElement, props?: WaitlistProps) => {
1150 | this.assertComponentsReady(this.#componentControls);
1151 | void this.#componentControls?.ensureMounted({ preloadHint: 'Waitlist' }).then(controls =>
1152 | controls.mountComponent({
1153 | name: 'Waitlist',
1154 | appearanceKey: 'waitlist',
1155 | node,
1156 | props,
1157 | }),
1158 | );
1159 |
1160 | this.telemetry?.record(eventPrebuiltComponentMounted('Waitlist', props));
1161 | };
1162 |
1163 | public unmountWaitlist = (node: HTMLDivElement): void => {
1164 | this.assertComponentsReady(this.#componentControls);
1165 | void this.#componentControls?.ensureMounted().then(controls => controls.unmountComponent({ node }));
1166 | };
1167 |
1168 | public mountPricingTable = (node: HTMLDivElement, props?: PricingTableProps): void => {
1169 | this.assertComponentsReady(this.#componentControls);
1170 | if (disabledAllBillingFeatures(this, this.environment)) {
1171 | if (this.#instanceType === 'development') {
1172 | throw new ClerkRuntimeError(warnings.cannotRenderAnyBillingComponent('PricingTable'), {
1173 | code: CANNOT_RENDER_BILLING_DISABLED_ERROR_CODE,
1174 | });
1175 | }
1176 | return;
1177 | }
1178 | // Temporary backward compatibility for legacy prop: `forOrganizations`. Will be removed in the coming minor release.
1179 | const nextProps = { ...(props as any) } as PricingTableProps & { forOrganizations?: boolean };
1180 | if (typeof (props as any)?.forOrganizations !== 'undefined') {
1181 | logger.warnOnce(
1182 | 'Clerk: [IMPORTANT] <PricingTable /> prop `forOrganizations` is deprecated and will be removed in the coming minors. Use `for="organization"` instead.',
1183 | );
1184 | }
1185 |
1186 | void this.#componentControls.ensureMounted({ preloadHint: 'PricingTable' }).then(controls =>
1187 | controls.mountComponent({
1188 | name: 'PricingTable',
1189 | appearanceKey: 'pricingTable',
1190 | node,
1191 | props: nextProps,
1192 | }),
1193 | );
1194 |
1195 | this.telemetry?.record(eventPrebuiltComponentMounted('PricingTable', nextProps));
1196 | };
1197 |
1198 | public unmountPricingTable = (node: HTMLDivElement): void => {
1199 | this.assertComponentsReady(this.#componentControls);
1200 | void this.#componentControls.ensureMounted().then(controls =>
1201 | controls.unmountComponent({
1202 | node,
1203 | }),
1204 | );
1205 | };
1206 |
1207 | public __internal_mountOAuthConsent = (node: HTMLDivElement, props?: __internal_OAuthConsentProps) => {
1208 | this.assertComponentsReady(this.#componentControls);
1209 | void this.#componentControls.ensureMounted({ preloadHint: 'OAuthConsent' }).then(controls =>
1210 | controls.mountComponent({
1211 | name: 'OAuthConsent',
1212 | appearanceKey: '__internal_oauthConsent',
1213 | node,
1214 | props,
1215 | }),
1216 | );
1217 | };
1218 |
1219 | public __internal_unmountOAuthConsent = (node: HTMLDivElement) => {
1220 | this.assertComponentsReady(this.#componentControls);
1221 | void this.#componentControls.ensureMounted().then(controls => controls.unmountComponent({ node }));
1222 | };
1223 |
1224 | /**
1225 | * @experimental This API is in early access and may change in future releases.
1226 | *
1227 | * Mount a api keys component at the target element.
1228 | * @param targetNode Target to mount the APIKeys component.
1229 | * @param props Configuration parameters.
1230 | */
1231 | public mountApiKeys = (node: HTMLDivElement, props?: APIKeysProps) => {
1232 | this.assertComponentsReady(this.#componentControls);
1233 |
1234 | logger.warnOnce('Clerk: <APIKeys /> component is in early access and not yet recommended for production use.');
1235 |
1236 | if (disabledAPIKeysFeature(this, this.environment)) {
1237 | if (this.#instanceType === 'development') {
1238 | throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponent, {
1239 | code: CANNOT_RENDER_API_KEYS_DISABLED_ERROR_CODE,
1240 | });
1241 | }
1242 | return;
1243 | }
1244 |
1245 | if (this.organization && !canViewOrManageAPIKeys(this)) {
1246 | if (this.#instanceType === 'development') {
1247 | throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponentForOrgWhenUnauthorized, {
1248 | code: CANNOT_RENDER_API_KEYS_ORG_UNAUTHORIZED_ERROR_CODE,
1249 | });
1250 | }
1251 | return;
1252 | }
1253 |
1254 | void this.#componentControls.ensureMounted({ preloadHint: 'APIKeys' }).then(controls =>
1255 | controls.mountComponent({
1256 | name: 'APIKeys',
1257 | appearanceKey: 'apiKeys',
1258 | node,
1259 | props,
1260 | }),
1261 | );
1262 |
1263 | this.telemetry?.record(eventPrebuiltComponentMounted('APIKeys', props));
1264 | };
1265 |
1266 | /**
1267 | * @experimental This API is in early access and may change in future releases.
1268 | *
1269 | * Unmount a api keys component from the target element.
1270 | * If there is no component mounted at the target node, results in a noop.
1271 | *
1272 | * @param targetNode Target node to unmount the ApiKeys component from.
1273 | */
1274 | public unmountApiKeys = (node: HTMLDivElement) => {
1275 | this.assertComponentsReady(this.#componentControls);
1276 | void this.#componentControls.ensureMounted().then(controls => controls.unmountComponent({ node }));
1277 | };
1278 |
1279 | public mountTaskChooseOrganization = (node: HTMLDivElement, props?: TaskChooseOrganizationProps) => {
1280 | this.assertComponentsReady(this.#componentControls);
1281 |
1282 | if (disabledOrganizationsFeature(this, this.environment)) {
1283 | if (this.#instanceType === 'development') {
1284 | throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('TaskChooseOrganization'), {
1285 | code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
1286 | });
1287 | }
1288 | return;
1289 | }
1290 |
1291 | void this.#componentControls.ensureMounted({ preloadHint: 'TaskChooseOrganization' }).then(controls =>
1292 | controls.mountComponent({
1293 | name: 'TaskChooseOrganization',
1294 | appearanceKey: 'taskChooseOrganization',
1295 | node,
1296 | props,
1297 | }),
1298 | );
1299 |
1300 | this.telemetry?.record(eventPrebuiltComponentMounted('TaskChooseOrganization', props));
1301 | };
1302 |
1303 | public unmountTaskChooseOrganization = (node: HTMLDivElement) => {
1304 | this.assertComponentsReady(this.#componentControls);
1305 | void this.#componentControls.ensureMounted().then(controls => controls.unmountComponent({ node }));
1306 | };
1307 |
1308 | /**
1309 | * `setActive` can be used to set the active session and/or organization.
1310 | */
1311 | public setActive = async (params: SetActiveParams): Promise<void> => {
1312 | const { organization, beforeEmit, redirectUrl, navigate: setActiveNavigate } = params;
1313 | let { session } = params;
1314 | this.__internal_setActiveInProgress = true;
1315 | debugLogger.debug(
1316 | 'setActive() start',
1317 | {
1318 | hasClient: Boolean(this.client),
1319 | sessionTarget: typeof session === 'string' ? session : (session?.id ?? session ?? null),
1320 | organizationTarget:
1321 | typeof organization === 'string' ? organization : (organization?.id ?? organization ?? null),
1322 | redirectUrl: redirectUrl ?? null,
1323 | },
1324 | 'clerk',
1325 | );
1326 | try {
1327 | if (!this.client) {
1328 | debugLogger.warn('Clerk setActive called before client is loaded', {}, 'clerk');
1329 | throw new Error('setActive is being called before the client is loaded. Wait for init.');
1330 | }
1331 |
1332 | if (session === undefined && !this.session) {
1333 | debugLogger.warn('Clerk setActive precondition not met: no target session and no active session', {}, 'clerk');
1334 | throw new Error(
1335 | 'setActive should either be called with a session param or there should be already an active session.',
1336 | );
1337 | }
1338 |
1339 | if (typeof session === 'string') {
1340 | session = (this.client.sessions.find(x => x.id === session) as SignedInSessionResource) || null;
1341 | }
1342 |
1343 | const onBeforeSetActive: SetActiveHook =
1344 | typeof window !== 'undefined' && typeof window.__unstable__onBeforeSetActive === 'function'
1345 | ? window.__unstable__onBeforeSetActive
1346 | : noop;
1347 |
1348 | const onAfterSetActive: SetActiveHook =
1349 | typeof window !== 'undefined' && typeof window.__unstable__onAfterSetActive === 'function'
1350 | ? window.__unstable__onAfterSetActive
1351 | : noop;
1352 |
1353 | let newSession = session === undefined ? this.session : session;
1354 | if (newSession?.status === 'pending') {
1355 | warnMissingPendingTaskHandlers({ ...this.#options, ...params });
1356 | }
1357 |
1358 | // At this point, the `session` variable should contain either an `SignedInSessionResource`
1359 | // ,`null` or `undefined`.
1360 | // We now want to set the last active organization id on that session (if it exists).
1361 | // However, if the `organization` parameter is not given (i.e. `undefined`), we want
1362 | // to keep the organization id that the session had.
1363 | const shouldSwitchOrganization = organization !== undefined;
1364 |
1365 | if (newSession && shouldSwitchOrganization) {
1366 | const organizationIdOrSlug = typeof organization === 'string' ? organization : organization?.id;
1367 |
1368 | if (isOrganizationId(organizationIdOrSlug)) {
1369 | newSession.lastActiveOrganizationId = organizationIdOrSlug || null;
1370 | } else {
1371 | const matchingOrganization = newSession.user.organizationMemberships.find(
1372 | mem => mem.organization.slug === organizationIdOrSlug,
1373 | );
1374 |
1375 | const newLastActiveOrganizationId = matchingOrganization?.organization.id || null;
1376 | const isPersonalWorkspace = newLastActiveOrganizationId === null;
1377 |
1378 | // Do not update in-memory to personal workspace if force organization selection is enabled
1379 | if (this.environment?.organizationSettings?.forceOrganizationSelection && isPersonalWorkspace) {
1380 | return;
1381 | }
1382 |
1383 | newSession.lastActiveOrganizationId = newLastActiveOrganizationId;
1384 | }
1385 | }
1386 |
1387 | // Do not revalidate server cache for pending sessions to avoid unmount of `SignIn/SignUp` AIOs when navigating to task
1388 | if (newSession?.status !== 'pending') {
1389 | /**
1390 | * Hint to each framework, that the user will be signed out when `{session: null}` is provided.
1391 | */
1392 | await onBeforeSetActive(newSession === null ? 'sign-out' : undefined);
1393 | }
1394 |
1395 | //1. setLastActiveSession to passed user session (add a param).
1396 | // Note that this will also update the session's active organization
1397 | // id.
1398 | if (inActiveBrowserTab() || !this.#options.standardBrowser) {
1399 | await this.#touchCurrentSession(newSession);
1400 | // reload session from updated client
1401 | newSession = this.#getSessionFromClient(newSession?.id);
1402 | }
1403 |
1404 | // getToken syncs __session and __client_uat to cookies using events.TokenUpdate dispatched event.
1405 | const token = await newSession?.getToken();
1406 | if (!token) {
1407 | if (!isValidBrowserOnline()) {
1408 | debugLogger.warn(
1409 | 'Token is null when setting active session (offline)',
1410 | { sessionId: newSession?.id },
1411 | 'clerk',
1412 | );
1413 | }
1414 | eventBus.emit(events.TokenUpdate, { token: null });
1415 | }
1416 |
1417 | //2. If there's a beforeEmit, typically we're navigating. Emit the session as
1418 | // undefined, then wait for beforeEmit to complete before emitting the new session.
1419 | // When undefined, neither SignedIn nor SignedOut renders, which avoids flickers or
1420 | // automatic reloading when reloading shouldn't be happening.
1421 | const tracker = createBeforeUnloadTracker(this.#options.standardBrowser);
1422 |
1423 | if (beforeEmit) {
1424 | deprecated(
1425 | 'Clerk.setActive({beforeEmit})',
1426 | 'Use the `redirectUrl` property instead. Example `Clerk.setActive({redirectUrl:"/"})`',
1427 | );
1428 | await tracker.track(async () => {
1429 | this.#setTransitiveState();
1430 | await beforeEmit(newSession);
1431 | });
1432 | }
1433 |
1434 | const taskUrl =
1435 | newSession?.status === 'pending' &&
1436 | newSession?.currentTask &&
1437 | this.#options.taskUrls?.[newSession?.currentTask.key];
1438 |
1439 | if (!beforeEmit && (redirectUrl || taskUrl || setActiveNavigate)) {
1440 | await tracker.track(async () => {
1441 | if (!this.client) {
1442 | // Typescript is not happy because since thinks this.client might have changed to undefined because the function is asynchronous.
1443 | return;
1444 | }
1445 |
1446 | if (newSession?.status !== 'pending') {
1447 | this.#setTransitiveState();
1448 | }
1449 |
1450 | if (taskUrl) {
1451 | const taskUrlWithRedirect = redirectUrl
1452 | ? buildURL({ base: taskUrl, hashSearchParams: { redirectUrl } }, { stringify: true })
1453 | : taskUrl;
1454 | await this.navigate(taskUrlWithRedirect);
1455 | } else if (setActiveNavigate && newSession) {
1456 | await setActiveNavigate({ session: newSession });
1457 | } else if (redirectUrl) {
1458 | if (this.client.isEligibleForTouch()) {
1459 | const absoluteRedirectUrl = new URL(redirectUrl, window.location.href);
1460 | const redirectUrlWithAuth = this.buildUrlWithAuth(
1461 | this.client.buildTouchUrl({ redirectUrl: absoluteRedirectUrl }),
1462 | );
1463 | await this.navigate(redirectUrlWithAuth);
1464 | }
1465 | await this.navigate(redirectUrl);
1466 | }
1467 | });
1468 | }
1469 |
1470 | //3. Check if hard reloading (onbeforeunload). If not, set the user/session and emit
1471 | if (tracker.isUnloading()) {
1472 | return;
1473 | }
1474 |
1475 | this.#setAccessors(newSession);
1476 | this.#emit();
1477 |
1478 | // Do not revalidate server cache for pending sessions to avoid unmount of `SignIn/SignUp` AIOs when navigating to task
1479 | // newSession can be mutated by the time we get here (org change session touch)
1480 | if (newSession?.status !== 'pending') {
1481 | await onAfterSetActive();
1482 | }
1483 | } finally {
1484 | this.__internal_setActiveInProgress = false;
1485 | }
1486 | };
1487 |
1488 | public addListener = (listener: ListenerCallback): UnsubscribeCallback => {
1489 | listener = memoizeListenerCallback(listener);
1490 | this.#listeners.push(listener);
1491 | // emit right away
1492 | if (this.client) {
1493 | listener({
1494 | client: this.client,
1495 | session: this.session,
1496 | user: this.user,
1497 | organization: this.organization,
1498 | });
1499 | }
1500 |
1501 | const unsubscribe = () => {
1502 | this.#listeners = this.#listeners.filter(l => l !== listener);
1503 | };
1504 | return unsubscribe;
1505 | };
1506 | public on: ClerkInterface['on'] = (...args) => {
1507 | this.#publicEventBus.on(...args);
1508 | };
1509 |
1510 | public off: ClerkInterface['off'] = (...args) => {
1511 | this.#publicEventBus.off(...args);
1512 | };
1513 |
1514 | public __internal_addNavigationListener = (listener: () => void): UnsubscribeCallback => {
1515 | this.#navigationListeners.push(listener);
1516 | const unsubscribe = () => {
1517 | this.#navigationListeners = this.#navigationListeners.filter(l => l !== listener);
1518 | };
1519 | return unsubscribe;
1520 | };
1521 |
1522 | public navigate = async (to: string | undefined, options?: NavigateOptions): Promise<unknown> => {
1523 | if (!to || !inBrowser()) {
1524 | return;
1525 | }
1526 |
1527 | /**
1528 | * Trigger all navigation listeners. In order for modal UI components to close.
1529 | */
1530 | setTimeout(() => {
1531 | this.#emitNavigationListeners();
1532 | }, 0);
1533 |
1534 | let toURL = new URL(to, window.location.href);
1535 |
1536 | if (!this.#allowedRedirectProtocols.includes(toURL.protocol)) {
1537 | console.warn(
1538 | `Clerk: "${toURL.protocol}" is not a valid protocol. Redirecting to "/" instead. If you think this is a mistake, please open an issue.`,
1539 | );
1540 | toURL = new URL('/', window.location.href);
1541 | }
1542 |
1543 | const customNavigate =
1544 | options?.replace && this.#options.routerReplace ? this.#options.routerReplace : this.#options.routerPush;
1545 |
1546 | debugLogger.info(`Clerk is navigating to: ${toURL}`);
1547 | if (this.#options.routerDebug) {
1548 | console.log(`Clerk is navigating to: ${toURL}`);
1549 | }
1550 |
1551 | // Custom protocol URLs have an origin value of 'null'. In many cases, this indicates deep-linking and we want to ensure the customNavigate function is used if available.
1552 | if ((toURL.origin !== 'null' && toURL.origin !== window.location.origin) || !customNavigate) {
1553 | windowNavigate(toURL);
1554 | return;
1555 | }
1556 |
1557 | const metadata = {
1558 | ...(options?.metadata ? { __internal_metadata: options?.metadata } : {}),
1559 | windowNavigate,
1560 | };
1561 | // React router only wants the path, search or hash portion.
1562 | return await customNavigate(stripOrigin(toURL), metadata);
1563 | };
1564 |
1565 | public buildUrlWithAuth(to: string): string {
1566 | if (this.#instanceType === 'production') {
1567 | return to;
1568 | }
1569 |
1570 | const toURL = new URL(to, window.location.origin);
1571 |
1572 | if (toURL.origin === window.location.origin) {
1573 | return toURL.href;
1574 | }
1575 |
1576 | if (!this.#authService) {
1577 | return toURL.href;
1578 | }
1579 |
1580 | return this.#authService.decorateUrlWithDevBrowserToken(toURL).href;
1581 | }
1582 |
1583 | public buildSignInUrl(options?: SignInRedirectOptions): string {
1584 | return this.#buildUrl(
1585 | 'signInUrl',
1586 | { ...options, redirectUrl: options?.redirectUrl || window.location.href },
1587 | options?.initialValues,
1588 | );
1589 | }
1590 |
1591 | public buildSignUpUrl(options?: SignUpRedirectOptions): string {
1592 | return this.#buildUrl(
1593 | 'signUpUrl',
1594 | { ...options, redirectUrl: options?.redirectUrl || window.location.href },
1595 | options?.initialValues,
1596 | );
1597 | }
1598 |
1599 | public buildUserProfileUrl(): string {
1600 | if (!this.environment || !this.environment.displayConfig) {
1601 | return '';
1602 | }
1603 | return this.buildUrlWithAuth(this.environment.displayConfig.userProfileUrl);
1604 | }
1605 |
1606 | public buildHomeUrl(): string {
1607 | if (!this.environment || !this.environment.displayConfig) {
1608 | return '';
1609 | }
1610 | return this.buildUrlWithAuth(this.environment.displayConfig.homeUrl);
1611 | }
1612 |
1613 | public buildAfterSignInUrl({ params }: { params?: URLSearchParams } = {}): string {
1614 | return this.buildUrlWithAuth(new RedirectUrls(this.#options, {}, params).getAfterSignInUrl());
1615 | }
1616 |
1617 | public buildAfterSignUpUrl({ params }: { params?: URLSearchParams } = {}): string {
1618 | return this.buildUrlWithAuth(new RedirectUrls(this.#options, {}, params).getAfterSignUpUrl());
1619 | }
1620 |
1621 | public buildAfterSignOutUrl(): string {
1622 | if (!this.#options.afterSignOutUrl) {
1623 | return '/';
1624 | }
1625 |
1626 | return this.buildUrlWithAuth(this.#options.afterSignOutUrl);
1627 | }
1628 |
1629 | public buildNewSubscriptionRedirectUrl(): string {
1630 | if (!this.#options.newSubscriptionRedirectUrl) {
1631 | return this.buildAfterSignInUrl();
1632 | }
1633 |
1634 | return this.#options.newSubscriptionRedirectUrl;
1635 | }
1636 |
1637 | public buildWaitlistUrl(options?: { initialValues?: Record<string, string> }): string {
1638 | if (!this.environment || !this.environment.displayConfig) {
1639 | return '';
1640 | }
1641 | const waitlistUrl = this.#options['waitlistUrl'] || this.environment.displayConfig.waitlistUrl;
1642 | const initValues = new URLSearchParams(options?.initialValues || {});
1643 | return buildURL({ base: waitlistUrl, hashSearchParams: [initValues] }, { stringify: true });
1644 | }
1645 |
1646 | public buildAfterMultiSessionSingleSignOutUrl(): string {
1647 | if (!this.environment) {
1648 | return '';
1649 | }
1650 |
1651 | if (this.#options.afterMultiSessionSingleSignOutUrl) {
1652 | return this.buildUrlWithAuth(this.#options.afterMultiSessionSingleSignOutUrl);
1653 | }
1654 |
1655 | if (this.#options.signInUrl) {
1656 | return this.buildUrlWithAuth(
1657 | buildURL(
1658 | {
1659 | base: this.#options.signInUrl,
1660 | hashPath: 'choose',
1661 | },
1662 | { stringify: true },
1663 | ),
1664 | );
1665 | }
1666 |
1667 | return this.buildUrlWithAuth(this.environment.displayConfig.afterSignOutOneUrl);
1668 | }
1669 |
1670 | public buildCreateOrganizationUrl(): string {
1671 | if (!this.environment || !this.environment.displayConfig) {
1672 | return '';
1673 | }
1674 | return this.buildUrlWithAuth(this.environment.displayConfig.createOrganizationUrl);
1675 | }
1676 |
1677 | public buildOrganizationProfileUrl(): string {
1678 | if (!this.environment || !this.environment.displayConfig) {
1679 | return '';
1680 | }
1681 | return this.buildUrlWithAuth(this.environment.displayConfig.organizationProfileUrl);
1682 | }
1683 |
1684 | public buildTasksUrl(options?: TasksRedirectOptions): string {
1685 | const currentTask = this.session?.currentTask;
1686 | if (!currentTask) {
1687 | return '';
1688 | }
1689 |
1690 | const customTaskUrl = this.#options.taskUrls?.[currentTask.key];
1691 | if (customTaskUrl) {
1692 | return customTaskUrl;
1693 | }
1694 |
1695 | return buildURL(
1696 | {
1697 | base: this.buildSignInUrl(options),
1698 | hashPath: getTaskEndpoint(currentTask),
1699 | },
1700 | {
1701 | stringify: true,
1702 | },
1703 | );
1704 | }
1705 |
1706 | #redirectToSatellite = async (): Promise<unknown> => {
1707 | if (!inBrowser()) {
1708 | return;
1709 | }
1710 | const searchParams = new URLSearchParams({
1711 | [CLERK_SYNCED]: 'true',
1712 | });
1713 |
1714 | const satelliteUrl = getClerkQueryParam(CLERK_SATELLITE_URL);
1715 | if (!satelliteUrl || !isHttpOrHttps(satelliteUrl)) {
1716 | clerkRedirectUrlIsMissingScheme();
1717 | }
1718 |
1719 | const backToSatelliteUrl = buildURL(
1720 | { base: getClerkQueryParam(CLERK_SATELLITE_URL) as string, searchParams },
1721 | { stringify: true },
1722 | );
1723 | return this.navigate(this.buildUrlWithAuth(backToSatelliteUrl));
1724 | };
1725 |
1726 | public redirectWithAuth = async (to: string): Promise<unknown> => {
1727 | if (inBrowser()) {
1728 | return this.navigate(this.buildUrlWithAuth(to));
1729 | }
1730 | return;
1731 | };
1732 |
1733 | public redirectToSignIn = async (options?: SignInRedirectOptions): Promise<unknown> => {
1734 | if (inBrowser()) {
1735 | return this.navigate(this.buildSignInUrl(options));
1736 | }
1737 | return;
1738 | };
1739 |
1740 | public redirectToSignUp = async (options?: SignUpRedirectOptions): Promise<unknown> => {
1741 | if (inBrowser()) {
1742 | return this.navigate(this.buildSignUpUrl(options));
1743 | }
1744 | return;
1745 | };
1746 |
1747 | public redirectToUserProfile = async (): Promise<unknown> => {
1748 | if (inBrowser()) {
1749 | return this.navigate(this.buildUserProfileUrl());
1750 | }
1751 | return;
1752 | };
1753 |
1754 | public redirectToCreateOrganization = async (): Promise<unknown> => {
1755 | if (inBrowser()) {
1756 | return this.navigate(this.buildCreateOrganizationUrl());
1757 | }
1758 | return;
1759 | };
1760 |
1761 | public redirectToOrganizationProfile = async (): Promise<unknown> => {
1762 | if (inBrowser()) {
1763 | return this.navigate(this.buildOrganizationProfileUrl());
1764 | }
1765 | return;
1766 | };
1767 |
1768 | public redirectToAfterSignIn = async (): Promise<unknown> => {
1769 | if (inBrowser()) {
1770 | return this.navigate(this.buildAfterSignInUrl());
1771 | }
1772 | return;
1773 | };
1774 |
1775 | public redirectToAfterSignUp = async (): Promise<unknown> => {
1776 | if (inBrowser()) {
1777 | return this.navigate(this.buildAfterSignUpUrl());
1778 | }
1779 | return;
1780 | };
1781 |
1782 | public redirectToAfterSignOut = async (): Promise<unknown> => {
1783 | if (inBrowser()) {
1784 | return this.navigate(this.buildAfterSignOutUrl());
1785 | }
1786 | return;
1787 | };
1788 |
1789 | public redirectToWaitlist = async (): Promise<unknown> => {
1790 | if (inBrowser()) {
1791 | return this.navigate(this.buildWaitlistUrl());
1792 | }
1793 | return;
1794 | };
1795 |
1796 | public redirectToTasks = async (options?: TasksRedirectOptions): Promise<unknown> => {
1797 | if (inBrowser()) {
1798 | return this.navigate(this.buildTasksUrl(options));
1799 | }
1800 | return;
1801 | };
1802 |
1803 | public handleEmailLinkVerification = async (
1804 | params: HandleEmailLinkVerificationParams,
1805 | customNavigate?: (to: string) => Promise<unknown>,
1806 | ): Promise<unknown> => {
1807 | if (!this.client) {
1808 | return;
1809 | }
1810 |
1811 | const verificationStatus = getClerkQueryParam('__clerk_status');
1812 | if (verificationStatus === 'expired') {
1813 | throw new EmailLinkError(EmailLinkErrorCodeStatus.Expired);
1814 | } else if (verificationStatus === 'client_mismatch') {
1815 | throw new EmailLinkError(EmailLinkErrorCodeStatus.ClientMismatch);
1816 | } else if (verificationStatus !== 'verified') {
1817 | throw new EmailLinkError(EmailLinkErrorCodeStatus.Failed);
1818 | }
1819 |
1820 | const newSessionId = getClerkQueryParam('__clerk_created_session');
1821 | const { signIn, signUp, sessions } = this.client;
1822 |
1823 | const shouldCompleteOnThisDevice = sessions.some(s => s.id === newSessionId);
1824 | const shouldContinueOnThisDevice =
1825 | signIn.status === 'needs_second_factor' || signUp.status === 'missing_requirements';
1826 |
1827 | const navigate = (to: string) =>
1828 | customNavigate && typeof customNavigate === 'function' ? customNavigate(to) : this.navigate(to);
1829 |
1830 | const redirectContinue = params.redirectUrl ? () => navigate(params.redirectUrl as string) : noop;
1831 |
1832 | if (shouldCompleteOnThisDevice) {
1833 | return this.setActive({
1834 | session: newSessionId,
1835 | redirectUrl: params.redirectUrlComplete,
1836 | });
1837 | } else if (shouldContinueOnThisDevice) {
1838 | return redirectContinue();
1839 | }
1840 |
1841 | if (typeof params.onVerifiedOnOtherDevice === 'function') {
1842 | params.onVerifiedOnOtherDevice();
1843 | }
1844 | return null;
1845 | };
1846 |
1847 | public handleGoogleOneTapCallback = async (
1848 | signInOrUp: SignInResource | SignUpResource,
1849 | params: HandleOAuthCallbackParams,
1850 | customNavigate?: (to: string) => Promise<unknown>,
1851 | ): Promise<unknown> => {
1852 | if (!this.loaded || !this.environment || !this.client) {
1853 | return;
1854 | }
1855 | const { signIn: _signIn, signUp: _signUp } = this.client;
1856 |
1857 | const signIn = 'identifier' in (signInOrUp || {}) ? (signInOrUp as SignInResource) : _signIn;
1858 | const signUp = 'missingFields' in (signInOrUp || {}) ? (signInOrUp as SignUpResource) : _signUp;
1859 |
1860 | const navigate = (to: string) =>
1861 | customNavigate && typeof customNavigate === 'function'
1862 | ? customNavigate(this.buildUrlWithAuth(to))
1863 | : this.navigate(this.buildUrlWithAuth(to));
1864 |
1865 | return this._handleRedirectCallback(params, {
1866 | signUp,
1867 | signIn,
1868 | navigate,
1869 | });
1870 | };
1871 |
1872 | private _handleRedirectCallback = async (
1873 | params: HandleOAuthCallbackParams,
1874 | {
1875 | signIn,
1876 | signUp,
1877 | navigate,
1878 | }: {
1879 | signIn: SignInResource;
1880 | signUp: SignUpResource;
1881 | navigate: (to: string) => Promise<unknown>;
1882 | },
1883 | ): Promise<unknown> => {
1884 | if (!this.loaded || !this.environment || !this.client) {
1885 | return;
1886 | }
1887 |
1888 | // If `handleRedirectCallback` is called on a window without an opener property (such as when the OAuth flow popup
1889 | // directs the opening page to navigate to the /sso-callback route), we need to reload the signIn and signUp resources
1890 | // to ensure that we have the latest state. This operation can fail when we try reloading a resource that doesn't
1891 | // exist (such as when reloading a signIn resource during a signUp attempt), but this can be safely ignored.
1892 | if (!window.opener && params.reloadResource) {
1893 | try {
1894 | if (params.reloadResource === 'signIn') {
1895 | await signIn.reload();
1896 | } else if (params.reloadResource === 'signUp') {
1897 | await signUp.reload();
1898 | }
1899 | } catch {
1900 | // This catch intentionally left blank.
1901 | }
1902 | }
1903 |
1904 | const { displayConfig } = this.environment;
1905 | const { firstFactorVerification } = signIn;
1906 | const { externalAccount } = signUp.verifications;
1907 | const su = {
1908 | status: signUp.status,
1909 | missingFields: signUp.missingFields,
1910 | externalAccountStatus: externalAccount.status,
1911 | externalAccountErrorCode: externalAccount.error?.code,
1912 | externalAccountSessionId: externalAccount.error?.meta?.sessionId,
1913 | sessionId: signUp.createdSessionId,
1914 | };
1915 |
1916 | const si = {
1917 | status: signIn.status,
1918 | firstFactorVerificationStatus: firstFactorVerification.status,
1919 | firstFactorVerificationErrorCode: firstFactorVerification.error?.code,
1920 | firstFactorVerificationSessionId: firstFactorVerification.error?.meta?.sessionId,
1921 | sessionId: signIn.createdSessionId,
1922 | };
1923 |
1924 | const makeNavigate = (to: string) => () => navigate(to);
1925 |
1926 | const navigateToSignIn = makeNavigate(params.signInUrl || displayConfig.signInUrl);
1927 |
1928 | const navigateToSignUp = makeNavigate(params.signUpUrl || displayConfig.signUpUrl);
1929 |
1930 | const navigateToFactorOne = makeNavigate(
1931 | params.firstFactorUrl ||
1932 | buildURL({ base: displayConfig.signInUrl, hashPath: '/factor-one' }, { stringify: true }),
1933 | );
1934 |
1935 | const navigateToFactorTwo = makeNavigate(
1936 | params.secondFactorUrl ||
1937 | buildURL({ base: displayConfig.signInUrl, hashPath: '/factor-two' }, { stringify: true }),
1938 | );
1939 |
1940 | const navigateToResetPassword = makeNavigate(
1941 | params.resetPasswordUrl ||
1942 | buildURL({ base: displayConfig.signInUrl, hashPath: '/reset-password' }, { stringify: true }),
1943 | );
1944 |
1945 | const redirectUrls = new RedirectUrls(this.#options, params);
1946 |
1947 | const navigateToContinueSignUp = makeNavigate(
1948 | params.continueSignUpUrl ||
1949 | buildURL(
1950 | {
1951 | base: displayConfig.signUpUrl,
1952 | hashPath: '/continue',
1953 | },
1954 | { stringify: true },
1955 | ),
1956 | );
1957 |
1958 | const navigateToNextStepSignUp = ({ missingFields }: { missingFields: SignUpField[] }) => {
1959 | if (missingFields.length) {
1960 | return navigateToContinueSignUp();
1961 | }
1962 |
1963 | return completeSignUpFlow({
1964 | signUp,
1965 | verifyEmailPath:
1966 | params.verifyEmailAddressUrl ||
1967 | buildURL(
1968 | {
1969 | base: displayConfig.signUpUrl,
1970 | hashPath: '/verify-email-address',
1971 | },
1972 | { stringify: true },
1973 | ),
1974 | verifyPhonePath:
1975 | params.verifyPhoneNumberUrl ||
1976 | buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-phone-number' }, { stringify: true }),
1977 | navigate,
1978 | });
1979 | };
1980 |
1981 | const signInUrl = params.signInUrl || displayConfig.signInUrl;
1982 | const signUpUrl = params.signUpUrl || displayConfig.signUpUrl;
1983 |
1984 | const setActiveNavigate = async ({
1985 | session,
1986 | baseUrl,
1987 | redirectUrl,
1988 | }: {
1989 | session: SessionResource;
1990 | baseUrl: string;
1991 | redirectUrl: string;
1992 | }) => {
1993 | if (!session.currentTask) {
1994 | await this.navigate(redirectUrl);
1995 | return;
1996 | }
1997 |
1998 | await navigateIfTaskExists(session, {
1999 | baseUrl,
2000 | navigate: this.navigate,
2001 | });
2002 | };
2003 |
2004 | if (si.status === 'complete') {
2005 | return this.setActive({
2006 | session: si.sessionId,
2007 | navigate: async ({ session }) => {
2008 | await setActiveNavigate({ session, baseUrl: signInUrl, redirectUrl: redirectUrls.getAfterSignInUrl() });
2009 | },
2010 | });
2011 | }
2012 |
2013 | const userExistsButNeedsToSignIn =
2014 | su.externalAccountStatus === 'transferable' && su.externalAccountErrorCode === 'external_account_exists';
2015 |
2016 | if (userExistsButNeedsToSignIn) {
2017 | const res = await signIn.create({ transfer: true });
2018 | switch (res.status) {
2019 | case 'complete':
2020 | return this.setActive({
2021 | session: res.createdSessionId,
2022 | navigate: async ({ session }) => {
2023 | await setActiveNavigate({ session, baseUrl: signUpUrl, redirectUrl: redirectUrls.getAfterSignInUrl() });
2024 | },
2025 | });
2026 | case 'needs_first_factor':
2027 | return navigateToFactorOne();
2028 | case 'needs_second_factor':
2029 | return navigateToFactorTwo();
2030 | case 'needs_new_password':
2031 | return navigateToResetPassword();
2032 | default:
2033 | clerkOAuthCallbackDidNotCompleteSignInSignUp('sign in');
2034 | }
2035 | }
2036 |
2037 | const userLockedFromSignUp = su.externalAccountErrorCode === 'user_locked';
2038 | const userLockedFromSignIn = si.firstFactorVerificationErrorCode === 'user_locked';
2039 |
2040 | if (userLockedFromSignUp) {
2041 | return navigateToSignUp();
2042 | }
2043 |
2044 | if (userLockedFromSignIn) {
2045 | return navigateToSignIn();
2046 | }
2047 |
2048 | const userHasUnverifiedEmail =
2049 | si.status === 'needs_first_factor' && !signIn.supportedFirstFactors?.every(f => f.strategy === 'enterprise_sso');
2050 |
2051 | if (userHasUnverifiedEmail) {
2052 | return navigateToFactorOne();
2053 | }
2054 |
2055 | const userNeedsNewPassword = si.status === 'needs_new_password';
2056 |
2057 | if (userNeedsNewPassword) {
2058 | return navigateToResetPassword();
2059 | }
2060 |
2061 | const userNeedsToBeCreated = si.firstFactorVerificationStatus === 'transferable';
2062 |
2063 | if (userNeedsToBeCreated) {
2064 | if (params.transferable === false) {
2065 | return navigateToSignIn();
2066 | }
2067 |
2068 | const res = await signUp.create({ transfer: true });
2069 | switch (res.status) {
2070 | case 'complete':
2071 | return this.setActive({
2072 | session: res.createdSessionId,
2073 | navigate: async ({ session }) => {
2074 | await setActiveNavigate({ session, baseUrl: signUpUrl, redirectUrl: redirectUrls.getAfterSignUpUrl() });
2075 | },
2076 | });
2077 | case 'missing_requirements':
2078 | return navigateToNextStepSignUp({ missingFields: res.missingFields });
2079 | default:
2080 | clerkOAuthCallbackDidNotCompleteSignInSignUp('sign in');
2081 | }
2082 | }
2083 |
2084 | if (su.status === 'complete') {
2085 | return this.setActive({
2086 | session: su.sessionId,
2087 | navigate: async ({ session }) => {
2088 | await setActiveNavigate({ session, baseUrl: signUpUrl, redirectUrl: redirectUrls.getAfterSignUpUrl() });
2089 | },
2090 | });
2091 | }
2092 |
2093 | if (si.status === 'needs_second_factor') {
2094 | return navigateToFactorTwo();
2095 | }
2096 |
2097 | const suUserAlreadySignedIn =
2098 | (su.externalAccountStatus === 'failed' || su.externalAccountStatus === 'unverified') &&
2099 | su.externalAccountErrorCode === 'identifier_already_signed_in' &&
2100 | su.externalAccountSessionId;
2101 |
2102 | const siUserAlreadySignedIn =
2103 | si.firstFactorVerificationStatus === 'failed' &&
2104 | si.firstFactorVerificationErrorCode === 'identifier_already_signed_in' &&
2105 | si.firstFactorVerificationSessionId;
2106 |
2107 | const userAlreadySignedIn = suUserAlreadySignedIn || siUserAlreadySignedIn;
2108 | if (userAlreadySignedIn) {
2109 | const sessionId = si.firstFactorVerificationSessionId || su.externalAccountSessionId;
2110 | if (sessionId) {
2111 | return this.setActive({
2112 | session: sessionId,
2113 | navigate: async ({ session }) => {
2114 | await setActiveNavigate({
2115 | session,
2116 | baseUrl: suUserAlreadySignedIn ? signUpUrl : signInUrl,
2117 | redirectUrl: redirectUrls.getAfterSignInUrl(),
2118 | });
2119 | },
2120 | });
2121 | }
2122 | }
2123 |
2124 | if (hasExternalAccountSignUpError(signUp)) {
2125 | return navigateToSignUp();
2126 | }
2127 |
2128 | if (su.externalAccountStatus === 'verified' && su.status === 'missing_requirements') {
2129 | return navigateToNextStepSignUp({ missingFields: signUp.missingFields });
2130 | }
2131 |
2132 | if (this.session?.currentTask) {
2133 | await this.redirectToTasks({
2134 | redirectUrl: this.buildAfterSignInUrl(),
2135 | });
2136 | return;
2137 | }
2138 |
2139 | return navigateToSignIn();
2140 | };
2141 |
2142 | public handleRedirectCallback = async (
2143 | params: HandleOAuthCallbackParams = {},
2144 | customNavigate?: (to: string) => Promise<unknown>,
2145 | ): Promise<unknown> => {
2146 | if (!this.loaded || !this.environment || !this.client) {
2147 | return;
2148 | }
2149 | const { signIn, signUp } = this.client;
2150 |
2151 | const navigate = (to: string) =>
2152 | customNavigate && typeof customNavigate === 'function' ? customNavigate(to) : this.navigate(to);
2153 |
2154 | return this._handleRedirectCallback(params, {
2155 | signUp,
2156 | signIn,
2157 | navigate,
2158 | });
2159 | };
2160 |
2161 | // TODO: Deprecate this one, and mark it as internal. Is there actual benefit for external developers to use this ? Should they ever reach for it ?
2162 | public handleUnauthenticated = async (opts = { broadcast: true }): Promise<unknown> => {
2163 | if (!this.client || !this.session) {
2164 | return;
2165 | }
2166 | try {
2167 | const newClient = await Client.getOrCreateInstance().fetch();
2168 | this.updateClient(newClient);
2169 | if (this.session) {
2170 | return;
2171 | }
2172 | if (opts.broadcast) {
2173 | eventBus.emit(events.UserSignOut, null);
2174 | }
2175 | return this.setActive({ session: null });
2176 | } catch (err) {
2177 | // `/client` can fail with either a 401, a 403, 500 or network errors.
2178 | // 401 is already handled internally in our fetcher.
2179 | // 403 means that the client is blocked, signing out the user is the only option.
2180 | // 500 means that the client is not working, signing out the user is the only option, since the intention was to sign out the user.
2181 | if (isClerkAPIResponseError(err) && [403, 500].includes(err.status)) {
2182 | return this.setActive({ session: null });
2183 | } else {
2184 | throw err;
2185 | }
2186 | }
2187 | };
2188 |
2189 | public authenticateWithGoogleOneTap = async (
2190 | params: AuthenticateWithGoogleOneTapParams,
2191 | ): Promise<SignInResource | SignUpResource> => {
2192 | if (__BUILD_DISABLE_RHC__) {
2193 | clerkUnsupportedEnvironmentWarning('Google One Tap');
2194 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2195 | return this.client!.signIn; // TODO: Remove not null assertion
2196 | }
2197 |
2198 | return this.client?.signIn
2199 | .create({
2200 | strategy: 'google_one_tap',
2201 | token: params.token,
2202 | })
2203 | .catch(err => {
2204 | if (isClerkAPIResponseError(err) && err.errors[0].code === 'external_account_not_found') {
2205 | return this.client?.signUp.create({
2206 | strategy: 'google_one_tap',
2207 | token: params.token,
2208 | legalAccepted: params.legalAccepted,
2209 | });
2210 | }
2211 | throw err;
2212 | }) as Promise<SignInResource | SignUpResource>;
2213 | };
2214 |
2215 | public authenticateWithMetamask = async (props: AuthenticateWithMetamaskParams = {}): Promise<void> => {
2216 | await this.authenticateWithWeb3({
2217 | ...props,
2218 | strategy: 'web3_metamask_signature',
2219 | });
2220 | };
2221 |
2222 | public authenticateWithCoinbaseWallet = async (props: AuthenticateWithCoinbaseWalletParams = {}): Promise<void> => {
2223 | await this.authenticateWithWeb3({
2224 | ...props,
2225 | strategy: 'web3_coinbase_wallet_signature',
2226 | });
2227 | };
2228 |
2229 | public authenticateWithBase = async (props: AuthenticateWithBaseParams = {}): Promise<void> => {
2230 | await this.authenticateWithWeb3({
2231 | ...props,
2232 | strategy: 'web3_base_signature',
2233 | });
2234 | };
2235 |
2236 | public authenticateWithOKXWallet = async (props: AuthenticateWithOKXWalletParams = {}): Promise<void> => {
2237 | await this.authenticateWithWeb3({
2238 | ...props,
2239 | strategy: 'web3_okx_wallet_signature',
2240 | });
2241 | };
2242 |
2243 | public authenticateWithWeb3 = async ({
2244 | redirectUrl,
2245 | signUpContinueUrl,
2246 | customNavigate,
2247 | unsafeMetadata,
2248 | strategy,
2249 | legalAccepted,
2250 | secondFactorUrl,
2251 | }: ClerkAuthenticateWithWeb3Params): Promise<void> => {
2252 | if (!this.client || !this.environment) {
2253 | return;
2254 | }
2255 |
2256 | const { displayConfig } = this.environment;
2257 |
2258 | const provider = strategy.replace('web3_', '').replace('_signature', '') as Web3Provider;
2259 | const identifier = await getWeb3Identifier({ provider });
2260 | let generateSignature: (params: GenerateSignatureParams) => Promise<string>;
2261 | switch (provider) {
2262 | case 'metamask':
2263 | generateSignature = generateSignatureWithMetamask;
2264 | break;
2265 | case 'base':
2266 | generateSignature = generateSignatureWithBase;
2267 | break;
2268 | case 'coinbase_wallet':
2269 | generateSignature = generateSignatureWithCoinbaseWallet;
2270 | break;
2271 | default:
2272 | generateSignature = generateSignatureWithOKXWallet;
2273 | break;
2274 | }
2275 |
2276 | const makeNavigate = (to: string) => () =>
2277 | customNavigate && typeof customNavigate === 'function' ? customNavigate(to) : this.navigate(to);
2278 |
2279 | const navigateToFactorTwo = makeNavigate(
2280 | secondFactorUrl || buildURL({ base: displayConfig.signInUrl, hashPath: '/factor-two' }, { stringify: true }),
2281 | );
2282 |
2283 | const navigateToContinueSignUp = makeNavigate(
2284 | signUpContinueUrl ||
2285 | buildURL(
2286 | {
2287 | base: displayConfig.signUpUrl,
2288 | hashPath: '/continue',
2289 | },
2290 | { stringify: true },
2291 | ),
2292 | );
2293 |
2294 | let signInOrSignUp: SignInResource | SignUpResource;
2295 | try {
2296 | signInOrSignUp = await this.client.signIn.authenticateWithWeb3({
2297 | identifier,
2298 | generateSignature,
2299 | strategy,
2300 | });
2301 | } catch (err) {
2302 | if (isError(err, ERROR_CODES.FORM_IDENTIFIER_NOT_FOUND)) {
2303 | signInOrSignUp = await this.client.signUp.authenticateWithWeb3({
2304 | identifier,
2305 | generateSignature,
2306 | unsafeMetadata,
2307 | strategy,
2308 | legalAccepted,
2309 | });
2310 |
2311 | if (
2312 | signUpContinueUrl &&
2313 | signInOrSignUp.status === 'missing_requirements' &&
2314 | signInOrSignUp.verifications.web3Wallet.status === 'verified'
2315 | ) {
2316 | await navigateToContinueSignUp();
2317 | }
2318 | } else {
2319 | throw err;
2320 | }
2321 | }
2322 |
2323 | const setActiveNavigate = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
2324 | if (!session.currentTask) {
2325 | await this.navigate(redirectUrl);
2326 | return;
2327 | }
2328 |
2329 | await navigateIfTaskExists(session, {
2330 | baseUrl: displayConfig.signInUrl,
2331 | navigate: this.navigate,
2332 | });
2333 | };
2334 |
2335 | switch (signInOrSignUp.status) {
2336 | case 'needs_second_factor':
2337 | await navigateToFactorTwo();
2338 | break;
2339 | case 'complete':
2340 | if (signInOrSignUp.createdSessionId) {
2341 | await this.setActive({
2342 | session: signInOrSignUp.createdSessionId,
2343 | navigate: async ({ session }) => {
2344 | await setActiveNavigate({ session, redirectUrl: redirectUrl ?? this.buildAfterSignInUrl() });
2345 | },
2346 | });
2347 | }
2348 | break;
2349 | default:
2350 | return;
2351 | }
2352 | };
2353 |
2354 | public createOrganization = async ({ name, slug }: CreateOrganizationParams): Promise<OrganizationResource> => {
2355 | return Organization.create({ name, slug });
2356 | };
2357 |
2358 | public getOrganization = async (organizationId: string): Promise<OrganizationResource> =>
2359 | Organization.get(organizationId);
2360 |
2361 | public joinWaitlist = async ({ emailAddress }: JoinWaitlistParams): Promise<WaitlistResource> =>
2362 | Waitlist.join({ emailAddress });
2363 |
2364 | public updateEnvironment(environment: EnvironmentResource): asserts this is { environment: EnvironmentResource } {
2365 | this.environment = environment;
2366 | }
2367 |
2368 | __internal_setCountry = (country: string | null) => {
2369 | if (!this.__internal_country) {
2370 | this.__internal_country = country;
2371 | }
2372 | };
2373 |
2374 | get __internal_last_error(): ClerkAPIError | null {
2375 | const value = this.internal_last_error;
2376 | this.internal_last_error = null;
2377 | return value;
2378 | }
2379 |
2380 | set __internal_last_error(value: ClerkAPIError | null) {
2381 | this.internal_last_error = value;
2382 | }
2383 |
2384 | updateClient = (newClient: ClientResource): void => {
2385 | if (!this.client) {
2386 | // This is the first time client is being
2387 | // set, so we also need to set session
2388 | const session = this.#options.selectInitialSession
2389 | ? this.#options.selectInitialSession(newClient)
2390 | : this.#defaultSession(newClient);
2391 | this.#setAccessors(session);
2392 | }
2393 | this.client = newClient;
2394 |
2395 | if (this.session) {
2396 | const session = this.#getSessionFromClient(this.session.id);
2397 |
2398 | const hasTransitionedToPendingStatus = this.session.status === 'active' && session?.status === 'pending';
2399 | if (hasTransitionedToPendingStatus) {
2400 | const onAfterSetActive: SetActiveHook =
2401 | typeof window !== 'undefined' && typeof window.__unstable__onAfterSetActive === 'function'
2402 | ? window.__unstable__onAfterSetActive
2403 | : noop;
2404 |
2405 | // Execute hooks to update server authentication context and trigger
2406 | // page protections in clerkMiddleware or server components
2407 | void onAfterSetActive();
2408 | }
2409 |
2410 | // Note: this might set this.session to null
2411 | this.#setAccessors(session);
2412 |
2413 | // A client response contains its associated sessions, along with a fresh token, so we dispatch a token update event.
2414 | if (!this.session?.lastActiveToken && !isValidBrowserOnline()) {
2415 | debugLogger.warn(
2416 | 'No last active token when updating client (offline)',
2417 | { sessionId: this.session?.id },
2418 | 'clerk',
2419 | );
2420 | }
2421 | eventBus.emit(events.TokenUpdate, { token: this.session?.lastActiveToken });
2422 | }
2423 |
2424 | this.#emit();
2425 | };
2426 |
2427 | get __unstable__environment(): EnvironmentResource | null | undefined {
2428 | return this.environment;
2429 | }
2430 |
2431 | // TODO: Fix this properly
2432 | // eslint-disable-next-line @typescript-eslint/require-await
2433 | __unstable__setEnvironment = async (env: EnvironmentJSON) => {
2434 | this.environment = new Environment(env);
2435 |
2436 | if (Clerk.mountComponentRenderer) {
2437 | this.#componentControls = Clerk.mountComponentRenderer(this, this.environment, this.#options);
2438 | }
2439 | };
2440 |
2441 | __unstable__onBeforeRequest = (callback: FapiRequestCallback<any>): void => {
2442 | this.#fapiClient.onBeforeRequest(callback);
2443 | };
2444 |
2445 | __unstable__onAfterResponse = (callback: FapiRequestCallback<any>): void => {
2446 | this.#fapiClient.onAfterResponse(callback);
2447 | };
2448 |
2449 | // TODO @userland-errors:
2450 | __unstable__updateProps = (_props: any) => {
2451 | // We need to re-init the options here in order to keep the options passed to ClerkProvider
2452 | // in sync with the state of clerk-js. If we don't init the options here again, the following scenario is possible:
2453 | // 1. User renders <ClerkProvider propA={undefined} propB={1} />
2454 | // 2. clerk-js initializes propA with a default value
2455 | // 3. The customer update propB independently of propA and window.Clerk.updateProps is called
2456 | // 4. If we don't merge the new props with the current options, propA will be reset to undefined
2457 | const props = {
2458 | ..._props,
2459 | options: this.#initOptions({ ...this.#options, ..._props.options }),
2460 | };
2461 |
2462 | return this.#componentControls?.ensureMounted().then(controls => controls.updateProps(props));
2463 | };
2464 |
2465 | __internal_navigateWithError(to: string, err: ClerkAPIError) {
2466 | this.__internal_last_error = err;
2467 | return this.navigate(to);
2468 | }
2469 |
2470 | #buildSyncUrlForDevelopmentInstances = (): string => {
2471 | const searchParams = new URLSearchParams({
2472 | [CLERK_SATELLITE_URL]: window.location.href,
2473 | });
2474 | return buildURL({ base: this.#options.signInUrl, searchParams }, { stringify: true });
2475 | };
2476 |
2477 | #buildSyncUrlForProductionInstances = (): string => {
2478 | let primarySyncUrl;
2479 |
2480 | if (this.proxyUrl) {
2481 | const proxy = new URL(this.proxyUrl);
2482 | primarySyncUrl = new URL(`${proxy.pathname}/v1/client/sync`, proxy.origin);
2483 | } else if (this.domain) {
2484 | primarySyncUrl = new URL(`/v1/client/sync`, `https://${this.domain}`);
2485 | }
2486 |
2487 | primarySyncUrl?.searchParams.append('redirect_url', window.location.href);
2488 |
2489 | return primarySyncUrl?.toString() || '';
2490 | };
2491 |
2492 | #shouldSyncWithPrimary = (): boolean => {
2493 | if (getClerkQueryParam(CLERK_SYNCED) === 'true') {
2494 | return false;
2495 | }
2496 |
2497 | if (!this.isSatellite) {
2498 | return false;
2499 | }
2500 |
2501 | return !!this.#authService?.isSignedOut();
2502 | };
2503 |
2504 | #shouldRedirectToSatellite = (): boolean => {
2505 | if (this.#instanceType === 'production') {
2506 | return false;
2507 | }
2508 |
2509 | if (this.isSatellite) {
2510 | return false;
2511 | }
2512 |
2513 | const satelliteUrl = getClerkQueryParam(CLERK_SATELLITE_URL);
2514 | return !!satelliteUrl;
2515 | };
2516 |
2517 | #syncWithPrimary = async () => {
2518 | if (this.instanceType === 'development') {
2519 | await this.navigate(this.#buildSyncUrlForDevelopmentInstances());
2520 | } else if (this.instanceType === 'production') {
2521 | await this.navigate(this.#buildSyncUrlForProductionInstances());
2522 | }
2523 | };
2524 |
2525 | #assertSignInFormatAndOrigin = (_signInUrl: string, origin: string) => {
2526 | let signInUrl: URL;
2527 | try {
2528 | signInUrl = new URL(_signInUrl);
2529 | } catch {
2530 | clerkInvalidSignInUrlFormat();
2531 | }
2532 |
2533 | if (signInUrl.origin === origin) {
2534 | clerkInvalidSignInUrlOrigin();
2535 | }
2536 | };
2537 |
2538 | #validateMultiDomainOptions = () => {
2539 | if (!this.isSatellite) {
2540 | return;
2541 | }
2542 |
2543 | if (this.#instanceType === 'development' && !this.#options.signInUrl) {
2544 | clerkMissingSignInUrlAsSatellite();
2545 | }
2546 |
2547 | if (!this.proxyUrl && !this.domain) {
2548 | clerkMissingProxyUrlAndDomain();
2549 | }
2550 |
2551 | if (this.#options.signInUrl) {
2552 | this.#assertSignInFormatAndOrigin(this.#options.signInUrl, window.location.origin);
2553 | }
2554 | };
2555 |
2556 | #loadInStandardBrowser = async (): Promise<void> => {
2557 | /**
2558 | * 0. Init auth service and setup dev browser
2559 | * This is not needed for production instances hence the .clear()
2560 | * At this point we have already attempted to pre-populate devBrowser with a fresh JWT, if Step 2 was successful this will not be overwritten.
2561 | * For multi-domain we want to avoid retrieving a fresh JWT from FAPI, and we need to get the token as a result of multi-domain session syncing.
2562 | */
2563 | this.#authService = await AuthCookieService.create(
2564 | this,
2565 | this.#fapiClient,
2566 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2567 | this.#instanceType!,
2568 | this.#publicEventBus,
2569 | );
2570 |
2571 | /**
2572 | * 1. Multi-domain SSO handling
2573 | * If needed the app will attempt to sync with another app hosted in a different domain in order to acquire a session
2574 | * - for development instances it populates dev browser JWT and `devBrowserHandler.setup()` should not have run.
2575 | */
2576 | this.#validateMultiDomainOptions();
2577 | if (this.#shouldSyncWithPrimary()) {
2578 | await this.#syncWithPrimary();
2579 | // ClerkJS is not considered loaded during the sync/link process with the primary domain, return early
2580 | return;
2581 | }
2582 |
2583 | /**
2584 | * 3. If the app is considered a primary domain and is in the middle of the sync/link flow, interact the loading of Clerk and redirect back to the satellite app
2585 | * Initially step 2 and 4 were considered one but for step 2 we need devBrowserHandler.setup() to not have run and step 4 requires a valid dev browser JWT
2586 | */
2587 | if (this.#shouldRedirectToSatellite()) {
2588 | await this.#redirectToSatellite();
2589 | return;
2590 | }
2591 |
2592 | /**
2593 | * 4. Continue with clerk-js setup.
2594 | * - Fetch & update environment
2595 | * - Fetch & update client
2596 | * - Mount components
2597 | */
2598 | this.#pageLifecycle = createPageLifecycle();
2599 |
2600 | if (typeof BroadcastChannel !== 'undefined') {
2601 | this.#broadcastChannel = new BroadcastChannel('clerk');
2602 | }
2603 |
2604 | this.#setupBrowserListeners();
2605 |
2606 | const isInAccountsHostedPages = isDevAccountPortalOrigin(window?.location.hostname);
2607 | const shouldTouchEnv = this.#instanceType === 'development' && !isInAccountsHostedPages;
2608 |
2609 | let initializationDegradedCounter = 0;
2610 |
2611 | let retries = 0;
2612 | while (retries < 2) {
2613 | retries++;
2614 |
2615 | try {
2616 | const initEnvironmentPromise = Environment.getInstance()
2617 | .fetch({ touch: shouldTouchEnv })
2618 | .then(res => this.updateEnvironment(res))
2619 | .catch(() => {
2620 | ++initializationDegradedCounter;
2621 | const environmentSnapshot = SafeLocalStorage.getItem<EnvironmentJSONSnapshot | null>(
2622 | CLERK_ENVIRONMENT_STORAGE_ENTRY,
2623 | null,
2624 | );
2625 |
2626 | if (environmentSnapshot) {
2627 | this.updateEnvironment(new Environment(environmentSnapshot));
2628 | }
2629 | });
2630 |
2631 | const initClient = async () => {
2632 | return Client.getOrCreateInstance()
2633 | .fetch()
2634 | .then(res => this.updateClient(res))
2635 | .catch(async e => {
2636 | /**
2637 | * Only handle non 4xx errors, like 5xx errors and network errors.
2638 | */
2639 | if (is4xxError(e)) {
2640 | // bubble it up
2641 | throw e;
2642 | }
2643 |
2644 | ++initializationDegradedCounter;
2645 |
2646 | const jwtInCookie = this.#authService?.getSessionCookie();
2647 | const localClient = createClientFromJwt(jwtInCookie);
2648 |
2649 | this.updateClient(localClient);
2650 |
2651 | /**
2652 | * In most scenarios we want the poller to stop while we are fetching a fresh token during an outage.
2653 | * We want to avoid having the below `getToken()` retrying at the same time as the poller.
2654 | */
2655 | this.#authService?.stopPollingForToken();
2656 |
2657 | // Attempt to grab a fresh token
2658 | await this.session
2659 | ?.getToken({ skipCache: true })
2660 | // If the token fetch fails, let Clerk be marked as loaded and leave it up to the poller.
2661 | .catch(() => null)
2662 | .finally(() => {
2663 | this.#authService?.startPollingForToken();
2664 | });
2665 |
2666 | // Allows for Clerk to be marked as loaded with the client and session created from the JWT.
2667 | return null;
2668 | });
2669 | };
2670 |
2671 | const initComponents = () => {
2672 | if (Clerk.mountComponentRenderer && !this.#componentControls) {
2673 | this.#componentControls = Clerk.mountComponentRenderer(
2674 | this,
2675 | this.environment as Environment,
2676 | this.#options,
2677 | );
2678 | }
2679 | };
2680 |
2681 | const [, clientResult] = await allSettled([initEnvironmentPromise, initClient()]);
2682 |
2683 | if (clientResult.status === 'rejected') {
2684 | const e = clientResult.reason;
2685 |
2686 | if (isError(e, 'requires_captcha')) {
2687 | initComponents();
2688 | await initClient();
2689 | } else {
2690 | throw e;
2691 | }
2692 | }
2693 |
2694 | this.#authService?.setClientUatCookieForDevelopmentInstances();
2695 |
2696 | if (await this.#redirectFAPIInitiatedFlow()) {
2697 | return;
2698 | }
2699 |
2700 | initComponents();
2701 |
2702 | break;
2703 | } catch (err) {
2704 | if (isError(err, 'dev_browser_unauthenticated')) {
2705 | await this.#authService.handleUnauthenticatedDevBrowser();
2706 | } else if (!isValidBrowserOnline()) {
2707 | console.warn(err);
2708 | return;
2709 | } else {
2710 | throw err;
2711 | }
2712 | }
2713 |
2714 | if (retries >= 2) {
2715 | clerkErrorInitFailed();
2716 | }
2717 | }
2718 |
2719 | this.#captchaHeartbeat = new CaptchaHeartbeat(this);
2720 | void this.#captchaHeartbeat.start();
2721 | this.#clearClerkQueryParams();
2722 | this.#handleImpersonationFab();
2723 | this.#handleKeylessPrompt();
2724 |
2725 | this.#publicEventBus.emit(clerkEvents.Status, initializationDegradedCounter > 0 ? 'degraded' : 'ready');
2726 | };
2727 |
2728 | private shouldFallbackToCachedResources = (): boolean => {
2729 | return !!this.__internal_getCachedResources;
2730 | };
2731 |
2732 | #loadInNonStandardBrowser = async (): Promise<void> => {
2733 | let environment: Environment, client: Client;
2734 | const fetchMaxTries = this.shouldFallbackToCachedResources() ? 1 : undefined;
2735 | let initializationDegradedCounter = 0;
2736 | try {
2737 | [environment, client] = await Promise.all([
2738 | Environment.getInstance().fetch({ touch: false, fetchMaxTries }),
2739 | Client.getOrCreateInstance().fetch({ fetchMaxTries }),
2740 | ]);
2741 | } catch (err) {
2742 | if (isClerkRuntimeError(err) && err.code === 'network_error' && this.shouldFallbackToCachedResources()) {
2743 | const cachedResources = await this.__internal_getCachedResources?.();
2744 | environment = new Environment(cachedResources?.environment);
2745 | Client.clearInstance();
2746 | client = Client.getOrCreateInstance(cachedResources?.client);
2747 | ++initializationDegradedCounter;
2748 | } else {
2749 | throw err;
2750 | }
2751 | }
2752 |
2753 | this.updateClient(client);
2754 | this.updateEnvironment(environment);
2755 |
2756 | // TODO: Add an auth service also for non standard browsers that will poll for the __session JWT but won't use cookies
2757 |
2758 | if (Clerk.mountComponentRenderer) {
2759 | this.#componentControls = Clerk.mountComponentRenderer(this, this.environment, this.#options);
2760 | }
2761 |
2762 | this.#publicEventBus.emit(clerkEvents.Status, initializationDegradedCounter > 0 ? 'degraded' : 'ready');
2763 | };
2764 |
2765 | // This is used by @clerk/clerk-expo
2766 | __internal_reloadInitialResources = async (): Promise<void> => {
2767 | const [environment, client] = await Promise.all([
2768 | Environment.getInstance().fetch({ touch: false, fetchMaxTries: 1 }),
2769 | Client.getOrCreateInstance().fetch({ fetchMaxTries: 1 }),
2770 | ]);
2771 |
2772 | this.updateClient(client);
2773 | this.updateEnvironment(environment);
2774 |
2775 | this.#emit();
2776 | };
2777 |
2778 | #defaultSession = (client: ClientResource): SignedInSessionResource | null => {
2779 | if (client.lastActiveSessionId) {
2780 | const currentSession = client.signedInSessions.find(s => s.id === client.lastActiveSessionId);
2781 | if (currentSession) {
2782 | return currentSession;
2783 | }
2784 | }
2785 | const session = client.signedInSessions[0];
2786 | return session || null;
2787 | };
2788 |
2789 | #setupBrowserListeners = (): void => {
2790 | if (!inClientSide()) {
2791 | return;
2792 | }
2793 |
2794 | this.#pageLifecycle?.onPageFocus(() => {
2795 | if (!this.session) {
2796 | return;
2797 | }
2798 |
2799 | // In multi-session apps, it's possible that different tabs will have different active sessions. It's critical that the tab's active session is touched in this case so the session is properly updated on the backend, and so we avoid any throttling when multi-session mode is enabled.
2800 | const multisessionMode = this.environment && !this.environment.authConfig.singleSessionMode;
2801 | if (!multisessionMode && this.#touchThrottledUntil > Date.now()) {
2802 | return;
2803 | }
2804 | this.#touchThrottledUntil = Date.now() + 5_000;
2805 |
2806 | if (this.#options.touchSession) {
2807 | void this.#touchCurrentSession(this.session);
2808 | }
2809 | });
2810 |
2811 | /**
2812 | * Background tabs get notified of cross-tab signout events.
2813 | */
2814 | this.#broadcastChannel?.addEventListener('message', (event: MessageEvent) => {
2815 | if (event.data?.type === 'signout') {
2816 | void this.handleUnauthenticated({ broadcast: false });
2817 | }
2818 | });
2819 |
2820 | /**
2821 | * Allow resources within the singleton to notify other tabs about a signout event (scoped to a single tab)
2822 | */
2823 | eventBus.on(events.UserSignOut, () => {
2824 | this.#broadcastChannel?.postMessage({ type: 'signout' });
2825 | });
2826 |
2827 | eventBus.on(events.EnvironmentUpdate, () => {
2828 | // Cache the environment snapshot for 24 hours
2829 | SafeLocalStorage.setItem(
2830 | CLERK_ENVIRONMENT_STORAGE_ENTRY,
2831 | this.environment?.__internal_toSnapshot(),
2832 | 24 * 60 * 60 * 1_000,
2833 | );
2834 | });
2835 | };
2836 |
2837 | // TODO: Be more conservative about touches. Throttle, don't touch when only one user, etc
2838 | #touchCurrentSession = async (session?: SignedInSessionResource | null): Promise<void> => {
2839 | if (!session) {
2840 | return Promise.resolve();
2841 | }
2842 |
2843 | await session.touch().catch(e => {
2844 | if (is4xxError(e)) {
2845 | void this.handleUnauthenticated();
2846 | }
2847 | });
2848 | };
2849 |
2850 | #emit = (): void => {
2851 | if (this.client) {
2852 | for (const listener of this.#listeners) {
2853 | listener({
2854 | client: this.client,
2855 | session: this.session,
2856 | user: this.user,
2857 | organization: this.organization,
2858 | });
2859 | }
2860 | }
2861 | };
2862 |
2863 | #emitNavigationListeners = (): void => {
2864 | for (const listener of this.#navigationListeners) {
2865 | listener();
2866 | }
2867 | };
2868 |
2869 | /**
2870 | * Temporarily clears the accessors before emitting changes to React context state.
2871 | * This is used during transitions like sign-out or session changes to prevent UI flickers
2872 | * such as unexpected unmount of control components
2873 | */
2874 | #setTransitiveState = () => {
2875 | this.session = undefined;
2876 | this.organization = undefined;
2877 | this.user = undefined;
2878 | this.#emit();
2879 | };
2880 |
2881 | #getLastActiveOrganizationFromSession = () => {
2882 | const orgMemberships = this.session?.user.organizationMemberships || [];
2883 | return (
2884 | orgMemberships.map(om => om.organization).find(org => org.id === this.session?.lastActiveOrganizationId) || null
2885 | );
2886 | };
2887 |
2888 | #setAccessors = (session?: SignedInSessionResource | null) => {
2889 | this.session = session || null;
2890 | this.organization = this.#getLastActiveOrganizationFromSession();
2891 | this.user = this.session ? this.session.user : null;
2892 | };
2893 |
2894 | #getSessionFromClient = (sessionId: string | undefined): SignedInSessionResource | null => {
2895 | return this.client?.signedInSessions.find(x => x.id === sessionId) || null;
2896 | };
2897 |
2898 | #handleImpersonationFab = () => {
2899 | this.addListener(({ session }) => {
2900 | const isImpersonating = !!session?.actor;
2901 | if (isImpersonating) {
2902 | void this.#componentControls?.ensureMounted().then(controls => controls.mountImpersonationFab());
2903 | }
2904 | });
2905 | };
2906 |
2907 | #handleKeylessPrompt = () => {
2908 | if (this.#options.__internal_keyless_claimKeylessApplicationUrl) {
2909 | void this.#componentControls?.ensureMounted().then(controls => {
2910 | // TODO(@pantelis): Investigate if this resets existing props
2911 | controls.updateProps({
2912 | options: {
2913 | __internal_keyless_claimKeylessApplicationUrl: this.#options.__internal_keyless_claimKeylessApplicationUrl,
2914 | __internal_keyless_copyInstanceKeysUrl: this.#options.__internal_keyless_copyInstanceKeysUrl,
2915 | __internal_keyless_dismissPrompt: this.#options.__internal_keyless_dismissPrompt,
2916 | },
2917 | });
2918 | });
2919 | }
2920 | };
2921 |
2922 | #buildUrl = (
2923 | key: 'signInUrl' | 'signUpUrl',
2924 | options: RedirectOptions,
2925 | _initValues?: Record<string, string>,
2926 | ): string => {
2927 | if (!key || !this.loaded || !this.environment || !this.environment.displayConfig) {
2928 | return '';
2929 | }
2930 |
2931 | let signInOrUpUrl = this.#options[key] || this.environment.displayConfig[key];
2932 | if (this.#isCombinedSignInOrUpFlow()) {
2933 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2934 | signInOrUpUrl = this.#options.signInUrl!;
2935 | }
2936 | const redirectUrls = new RedirectUrls(this.#options, options).toSearchParams();
2937 | const initValues = new URLSearchParams(_initValues || {});
2938 | const url = buildURL(
2939 | {
2940 | base: signInOrUpUrl,
2941 | hashPath: this.#isCombinedSignInOrUpFlow() && key === 'signUpUrl' ? '/create' : '',
2942 | hashSearchParams: [initValues, redirectUrls],
2943 | },
2944 | { stringify: true },
2945 | );
2946 | return this.buildUrlWithAuth(url);
2947 | };
2948 |
2949 | assertComponentsReady(controls: unknown): asserts controls is ReturnType<MountComponentRenderer> {
2950 | if (!Clerk.mountComponentRenderer) {
2951 | throw new Error('ClerkJS was loaded without UI components.');
2952 | }
2953 | if (!controls) {
2954 | throw new Error('ClerkJS components are not ready yet.');
2955 | }
2956 | }
2957 |
2958 | #redirectFAPIInitiatedFlow = async (): Promise<boolean> => {
2959 | const redirectUrl = new URLSearchParams(window.location.search).get('redirect_url');
2960 | const isProdInstance = this.instanceType === 'production';
2961 | const shouldRedirect = redirectUrl !== null && isRedirectForFAPIInitiatedFlow(this.frontendApi, redirectUrl);
2962 |
2963 | if (isProdInstance || !shouldRedirect) {
2964 | return false;
2965 | }
2966 |
2967 | const userSignedIn = this.session;
2968 | const signInUrl = this.#options.signInUrl || this.environment?.displayConfig.signInUrl;
2969 | const referrerIsSignInUrl = signInUrl && window.location.href.startsWith(signInUrl);
2970 | const signUpUrl = this.#options.signUpUrl || this.environment?.displayConfig.signUpUrl;
2971 | const referrerIsSignUpUrl = signUpUrl && window.location.href.startsWith(signUpUrl);
2972 |
2973 | // don't redirect if user is not signed in and referrer is sign in/up url
2974 | if (requiresUserInput(redirectUrl) && !userSignedIn && (referrerIsSignInUrl || referrerIsSignUpUrl)) {
2975 | return false;
2976 | }
2977 |
2978 | await this.navigate(this.buildUrlWithAuth(redirectUrl));
2979 | return true;
2980 | };
2981 |
2982 | #initOptions = (options?: ClerkOptions): ClerkOptions => {
2983 | const processedOptions = options ? { ...options } : {};
2984 |
2985 | // Extract cssLayerName from baseTheme if present and move it to appearance level
2986 | if (processedOptions.appearance) {
2987 | processedOptions.appearance = processCssLayerNameExtraction(processedOptions.appearance);
2988 | }
2989 |
2990 | return {
2991 | ...defaultOptions,
2992 | ...processedOptions,
2993 | allowedRedirectOrigins: createAllowedRedirectOrigins(
2994 | options?.allowedRedirectOrigins,
2995 | this.frontendApi,
2996 | this.instanceType,
2997 | ),
2998 | };
2999 | };
3000 |
3001 | /**
3002 | * The handshake payload is transported in the URL in development. In cases where FAPI is returning the handshake payload, but Clerk is being used in a client-only application,
3003 | * we remove the handshake associated parameters as they are not necessary.
3004 | */
3005 | #clearClerkQueryParams = () => {
3006 | try {
3007 | removeClerkQueryParam(CLERK_SYNCED);
3008 | removeClerkQueryParam(CLERK_NETLIFY_CACHE_BUST_PARAM);
3009 | // @nikos: we're looking into dropping this param completely
3010 | // in the meantime, we're removing it here to keep the URL clean
3011 | removeClerkQueryParam(CLERK_SUFFIXED_COOKIES);
3012 | removeClerkQueryParam('__clerk_handshake');
3013 | removeClerkQueryParam('__clerk_handshake_nonce');
3014 | removeClerkQueryParam('__clerk_help');
3015 | } catch {
3016 | // ignore
3017 | }
3018 | };
3019 |
3020 | get #allowedRedirectProtocols() {
3021 | let allowedProtocols = ALLOWED_PROTOCOLS;
3022 |
3023 | if (this.#options.allowedRedirectProtocols) {
3024 | allowedProtocols = allowedProtocols.concat(this.#options.allowedRedirectProtocols);
3025 | }
3026 |
3027 | return allowedProtocols;
3028 | }
3029 | }
3030 |
```