diff --git a/eslint.config.mjs b/eslint.config.mjs index a311e9e2ca6..c935b457ae7 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -442,7 +442,7 @@ export default tseslint.config([ { definedTags: ['inline', 'unionReturnHeadings', 'displayFunctionSignature', 'paramExtension'], typed: false }, ], 'jsdoc/require-hyphen-before-param-description': 'warn', - 'jsdoc/require-description': 'warn', + 'jsdoc/require-description': 'off', 'jsdoc/require-description-complete-sentence': 'warn', 'jsdoc/require-param': ['warn', { ignoreWhenAllParamsMissing: true }], 'jsdoc/require-param-description': 'warn', @@ -455,6 +455,16 @@ export default tseslint.config([ ], }, }, + { + name: 'repo/jsdoc-internal', + files: ['packages/shared/src/**/internal/**/*.{ts,tsx}', 'packages/shared/src/**/*.{ts,tsx}'], + plugins: { + jsdoc: pluginJsDoc, + }, + rules: { + 'jsdoc/require-jsdoc': 'off', + }, + }, ...pluginYml.configs['flat/recommended'], { name: 'eslint-prettier', diff --git a/packages/clerk-js/package.json b/packages/clerk-js/package.json index 1096be1155f..e305d7a9aea 100644 --- a/packages/clerk-js/package.json +++ b/packages/clerk-js/package.json @@ -32,7 +32,6 @@ ], "scripts": { "build": "pnpm build:bundle && pnpm build:declarations", - "postbuild": "node ../../scripts/search-for-rhc.mjs file dist/clerk.no-rhc.mjs", "build:analyze": "rspack build --config rspack.config.js --env production --env variant=\"clerk.browser\" --env analysis --analyze", "build:bundle": "pnpm clean && rspack build --config rspack.config.js --env production", "build:declarations": "tsc -p tsconfig.declarations.json", @@ -52,6 +51,7 @@ "lint": "eslint src", "lint:attw": "attw --pack . --profile node16 --ignore-rules named-exports", "lint:publint": "publint || true", + "postbuild:disabled": "node ../../scripts/search-for-rhc.mjs file dist/clerk.no-rhc.mjs", "test": "vitest --watch=false", "test:sandbox:integration": "playwright test", "test:sandbox:integration:ui": "playwright test --ui", @@ -61,36 +61,25 @@ "browserslist": "last 2 years", "dependencies": { "@base-org/account": "2.0.1", - "@clerk/localizations": "workspace:^", "@clerk/shared": "workspace:^", "@coinbase/wallet-sdk": "4.3.0", - "@emotion/cache": "11.11.0", - "@emotion/react": "11.11.1", - "@floating-ui/react": "0.27.12", - "@floating-ui/react-dom": "^2.1.3", - "@formkit/auto-animate": "^0.8.2", "@stripe/stripe-js": "5.6.0", "@swc/helpers": "^0.5.17", "@zxcvbn-ts/core": "3.0.4", "@zxcvbn-ts/language-common": "3.0.4", "alien-signals": "2.0.6", "browser-tabs-lock": "1.3.0", - "copy-to-clipboard": "3.3.3", "core-js": "3.41.0", "crypto-js": "^4.2.0", "dequal": "2.0.3", - "input-otp": "1.4.2", - "qrcode.react": "4.2.0", "regenerator-runtime": "0.14.1", "swr": "2.3.4" }, "devDependencies": { "@clerk/testing": "workspace:^", "@rsdoctor/rspack-plugin": "^0.4.13", - "@rspack/cli": "^1.4.11", - "@rspack/core": "^1.4.11", - "@rspack/plugin-react-refresh": "^1.5.0", - "@svgr/webpack": "^6.5.1", + "@rspack/cli": "^1.6.0", + "@rspack/core": "^1.6.0", "@types/cloudflare-turnstile": "^0.2.2", "@types/node": "^22.18.12", "@types/webpack-env": "^1.18.8", @@ -99,10 +88,6 @@ "minimatch": "^10.0.3", "webpack-merge": "^5.10.0" }, - "peerDependencies": { - "react": "catalog:peer-react", - "react-dom": "catalog:peer-react" - }, "engines": { "node": ">=18.17.0" }, diff --git a/packages/clerk-js/rspack.config.js b/packages/clerk-js/rspack.config.js index 76c48d467cc..b5a11797505 100644 --- a/packages/clerk-js/rspack.config.js +++ b/packages/clerk-js/rspack.config.js @@ -5,6 +5,7 @@ const path = require('path'); const { merge } = require('webpack-merge'); const ReactRefreshPlugin = require('@rspack/plugin-react-refresh'); const { RsdoctorRspackPlugin } = require('@rsdoctor/rspack-plugin'); +const { svgLoader, typescriptLoaderProd, typescriptLoaderDev } = require('../../scripts/rspack-common'); const isProduction = mode => mode === 'production'; const isDevelopment = mode => !isProduction(mode); @@ -117,20 +118,6 @@ const common = ({ mode, variant, disableRHC = false }) => { chunks: 'all', enforce: true, }, - /** - * Sign up is shared between the SignUp component and the SignIn component. - */ - signUp: { - minChunks: 1, - name: 'signup', - test: module => !!(module.resource && module.resource.includes('/ui/components/SignUp')), - }, - common: { - minChunks: 1, - name: 'ui-common', - priority: -20, - test: module => !!(module.resource && !module.resource.includes('/ui/components')), - }, defaultVendors: { minChunks: 1, test: /[\\/]node_modules[\\/]/, @@ -152,116 +139,6 @@ const common = ({ mode, variant, disableRHC = false }) => { }; }; -/** @type { () => (import('@rspack/core').RuleSetRule) } */ -const svgLoader = () => { - return { - test: /\.svg$/, - resolve: { - fullySpecified: false, - }, - use: { - loader: '@svgr/webpack', - options: { - svgo: true, - svgoConfig: { - floatPrecision: 3, - transformPrecision: 1, - plugins: ['preset-default', 'removeDimensions', 'removeStyleElement'], - }, - }, - }, - }; -}; - -/** @type { (opts?: { targets?: string, useCoreJs?: boolean }) => (import('@rspack/core').RuleSetRule[]) } */ -const typescriptLoaderProd = ( - { targets = packageJSON.browserslist, useCoreJs = false } = { targets: packageJSON.browserslist, useCoreJs: false }, -) => { - return [ - { - test: /\.(jsx?|tsx?)$/, - exclude: /node_modules/, - use: { - loader: 'builtin:swc-loader', - options: { - env: { - targets, - ...(useCoreJs - ? { - mode: 'usage', - coreJs: require('core-js/package.json').version, - } - : {}), - }, - jsc: { - parser: { - syntax: 'typescript', - tsx: true, - }, - externalHelpers: true, - transform: { - react: { - runtime: 'automatic', - importSource: '@emotion/react', - development: false, - refresh: false, - }, - }, - }, - }, - }, - }, - { - test: /\.m?js$/, - exclude: /node_modules[\\/]core-js/, - use: { - loader: 'builtin:swc-loader', - options: { - env: { - targets, - ...(useCoreJs - ? { - mode: 'usage', - coreJs: '3.41.0', - } - : {}), - }, - isModule: 'unknown', - }, - }, - }, - ]; -}; - -/** @type { () => (import('@rspack/core').RuleSetRule[]) } */ -const typescriptLoaderDev = () => { - return [ - { - test: /\.(jsx?|tsx?)$/, - exclude: /node_modules/, - loader: 'builtin:swc-loader', - options: { - jsc: { - target: 'esnext', - parser: { - syntax: 'typescript', - tsx: true, - }, - externalHelpers: true, - transform: { - react: { - runtime: 'automatic', - importSource: '@emotion/react', - development: true, - refresh: true, - }, - }, - }, - }, - }, - ]; -}; - /** * Used for production builds that have dynamicly loaded chunks. * @type { (opts?: { targets?: string, useCoreJs?: boolean }) => (import('@rspack/core').Configuration) } @@ -613,7 +490,7 @@ const devConfig = ({ mode, env }) => { cache: true, experiments: { cache: { - type: 'persistent', + type: 'memory', }, }, }; diff --git a/packages/clerk-js/src/core/auth/cookies/__tests__/clientUat.test.ts b/packages/clerk-js/src/core/auth/cookies/__tests__/clientUat.test.ts index 889cca10513..1f48f4e4ee0 100644 --- a/packages/clerk-js/src/core/auth/cookies/__tests__/clientUat.test.ts +++ b/packages/clerk-js/src/core/auth/cookies/__tests__/clientUat.test.ts @@ -1,15 +1,15 @@ import { createCookieHandler } from '@clerk/shared/cookie'; import { addYears } from '@clerk/shared/date'; +import { inCrossOriginIframe } from '@clerk/shared/internal/clerk-js/runtime'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { inCrossOriginIframe } from '../../../../utils'; import { getCookieDomain } from '../../getCookieDomain'; import { getSecureAttribute } from '../../getSecureAttribute'; import { createClientUatCookie } from '../clientUat'; vi.mock('@clerk/shared/cookie'); vi.mock('@clerk/shared/date'); -vi.mock('../../../../utils'); +vi.mock('@clerk/shared/internal/clerk-js/runtime'); vi.mock('../../getCookieDomain'); vi.mock('../../getSecureAttribute'); diff --git a/packages/clerk-js/src/core/auth/cookies/__tests__/session.test.ts b/packages/clerk-js/src/core/auth/cookies/__tests__/session.test.ts index 6248d78b9eb..d51bf732de9 100644 --- a/packages/clerk-js/src/core/auth/cookies/__tests__/session.test.ts +++ b/packages/clerk-js/src/core/auth/cookies/__tests__/session.test.ts @@ -1,14 +1,14 @@ import { createCookieHandler } from '@clerk/shared/cookie'; import { addYears } from '@clerk/shared/date'; +import { inCrossOriginIframe } from '@clerk/shared/internal/clerk-js/runtime'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { inCrossOriginIframe } from '../../../../utils'; import { getSecureAttribute } from '../../getSecureAttribute'; import { createSessionCookie } from '../session'; vi.mock('@clerk/shared/cookie'); vi.mock('@clerk/shared/date'); -vi.mock('../../../../utils'); +vi.mock('@clerk/shared/internal/clerk-js/runtime'); vi.mock('../../getSecureAttribute'); describe('createSessionCookie', () => { diff --git a/packages/clerk-js/src/core/auth/cookies/clientUat.ts b/packages/clerk-js/src/core/auth/cookies/clientUat.ts index a6d11bdd5a7..20a620938b8 100644 --- a/packages/clerk-js/src/core/auth/cookies/clientUat.ts +++ b/packages/clerk-js/src/core/auth/cookies/clientUat.ts @@ -1,9 +1,9 @@ import { createCookieHandler } from '@clerk/shared/cookie'; import { addYears } from '@clerk/shared/date'; +import { inCrossOriginIframe } from '@clerk/shared/internal/clerk-js/runtime'; import { getSuffixedCookieName } from '@clerk/shared/keys'; import type { ClientResource } from '@clerk/shared/types'; -import { inCrossOriginIframe } from '../../../utils'; import { getCookieDomain } from '../getCookieDomain'; import { getSecureAttribute } from '../getSecureAttribute'; diff --git a/packages/clerk-js/src/core/auth/cookies/devBrowser.ts b/packages/clerk-js/src/core/auth/cookies/devBrowser.ts index 8d1e3475788..ab3f66eb6d4 100644 --- a/packages/clerk-js/src/core/auth/cookies/devBrowser.ts +++ b/packages/clerk-js/src/core/auth/cookies/devBrowser.ts @@ -1,9 +1,9 @@ import { createCookieHandler } from '@clerk/shared/cookie'; import { addYears } from '@clerk/shared/date'; import { DEV_BROWSER_JWT_KEY } from '@clerk/shared/devBrowser'; +import { inCrossOriginIframe } from '@clerk/shared/internal/clerk-js/runtime'; import { getSuffixedCookieName } from '@clerk/shared/keys'; -import { inCrossOriginIframe } from '../../../utils'; import { getSecureAttribute } from '../getSecureAttribute'; export type DevBrowserCookieHandler = { diff --git a/packages/clerk-js/src/core/auth/cookies/session.ts b/packages/clerk-js/src/core/auth/cookies/session.ts index 4651cda52ec..e4362a2522d 100644 --- a/packages/clerk-js/src/core/auth/cookies/session.ts +++ b/packages/clerk-js/src/core/auth/cookies/session.ts @@ -1,8 +1,8 @@ import { createCookieHandler } from '@clerk/shared/cookie'; import { addYears } from '@clerk/shared/date'; +import { inCrossOriginIframe } from '@clerk/shared/internal/clerk-js/runtime'; import { getSuffixedCookieName } from '@clerk/shared/keys'; -import { inCrossOriginIframe } from '../../../utils'; import { getSecureAttribute } from '../getSecureAttribute'; const SESSION_COOKIE_NAME = '__session'; diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index d3e74295a09..9af95fe797c 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -9,6 +9,30 @@ import { isClerkAPIResponseError, isClerkRuntimeError, } from '@clerk/shared/error'; +import { assertNoLegacyProp } from '@clerk/shared/internal/clerk-js/assertNoLegacyProp'; +import { + canViewOrManageAPIKeys, + disabledAllBillingFeatures, + disabledAPIKeysFeature, + disabledOrganizationsFeature, + isSignedInAndSingleSessionModeEnabled, + noOrganizationExists, + noUserExists, +} from '@clerk/shared/internal/clerk-js/componentGuards'; +import { + CLERK_SATELLITE_URL, + CLERK_SUFFIXED_COOKIES, + CLERK_SYNCED, + ERROR_CODES, +} from '@clerk/shared/internal/clerk-js/constants'; +import { RedirectUrls } from '@clerk/shared/internal/clerk-js/redirectUrls'; +import { + getTaskEndpoint, + navigateIfTaskExists, + warnMissingPendingTaskHandlers, +} from '@clerk/shared/internal/clerk-js/sessionTasks'; +import { warnings } from '@clerk/shared/internal/clerk-js/warnings'; +import { windowNavigate } from '@clerk/shared/internal/clerk-js/windowNavigate'; import { parsePublishableKey } from '@clerk/shared/keys'; import { logger } from '@clerk/shared/logger'; import { CLERK_NETLIFY_CACHE_BUST_PARAM } from '@clerk/shared/netlifyCacheHandler'; @@ -92,23 +116,19 @@ import type { WaitlistResource, Web3Provider, } from '@clerk/shared/types'; +import type { ClerkUi } from '@clerk/shared/ui'; import { addClerkPrefix, isAbsoluteUrl, stripScheme } from '@clerk/shared/url'; import { allSettled, handleValueOrFn, noop } from '@clerk/shared/utils'; import { debugLogger, initDebugLogger } from '@/utils/debug'; -import type { MountComponentRenderer } from '../ui/Components'; import { ALLOWED_PROTOCOLS, buildURL, - canViewOrManageAPIKeys, completeSignUpFlow, createAllowedRedirectOrigins, createBeforeUnloadTracker, createPageLifecycle, - disabledAllBillingFeatures, - disabledAPIKeysFeature, - disabledOrganizationsFeature, errorThrower, generateSignatureWithBase, generateSignatureWithCoinbaseWallet, @@ -123,22 +143,14 @@ import { isError, isOrganizationId, isRedirectForFAPIInitiatedFlow, - isSignedInAndSingleSessionModeEnabled, - noOrganizationExists, - noUserExists, - processCssLayerNameExtraction, removeClerkQueryParam, requiresUserInput, stripOrigin, - windowNavigate, } from '../utils'; -import { assertNoLegacyProp } from '../utils/assertNoLegacyProp'; import { CLERK_ENVIRONMENT_STORAGE_ENTRY, SafeLocalStorage } from '../utils/localStorage'; import { memoizeListenerCallback } from '../utils/memoizeStateListenerCallback'; -import { RedirectUrls } from '../utils/redirectUrls'; import { AuthCookieService } from './auth/AuthCookieService'; import { CaptchaHeartbeat } from './auth/CaptchaHeartbeat'; -import { CLERK_SATELLITE_URL, CLERK_SUFFIXED_COOKIES, CLERK_SYNCED, ERROR_CODES } from './constants'; import { clerkErrorInitFailed, clerkInvalidSignInUrlFormat, @@ -157,9 +169,7 @@ import { APIKeys } from './modules/apiKeys'; import { Billing } from './modules/billing'; import { createCheckoutInstance } from './modules/checkout/instance'; import { BaseResource, Client, Environment, Organization, Waitlist } from './resources/internal'; -import { getTaskEndpoint, navigateIfTaskExists, warnMissingPendingTaskHandlers } from './sessionTasks'; import { State } from './state'; -import { warnings } from './warnings'; type SetActiveHook = (intent?: 'sign-out') => void | Promise; @@ -195,8 +205,6 @@ const defaultOptions: ClerkOptions = { }; export class Clerk implements ClerkInterface { - public static mountComponentRenderer?: MountComponentRenderer; - public static version: string = __PKG_VERSION__; public static sdkMetadata: SDKMetadata = { name: __PKG_NAME__, @@ -225,7 +233,7 @@ export class Clerk implements ClerkInterface { #authService?: AuthCookieService; #captchaHeartbeat?: CaptchaHeartbeat; #broadcastChannel: BroadcastChannel | null = null; - #componentControls?: ReturnType | null; + #clerkUi?: Promise; //@ts-expect-error with being undefined even though it's not possible - related to issue with ts and error thrower #fapiClient: FapiClient; #instanceType?: InstanceType; @@ -422,6 +430,19 @@ export class Clerk implements ClerkInterface { this.#options = this.#initOptions(options); + // Initialize ClerkUi if it was provided + if (this.#options.clerkUiCtor) { + this.#clerkUi = this.#options.clerkUiCtor.then( + ClerkUI => + new ClerkUI( + () => this, + () => this.environment, + this.#options, + (module: string) => import(module), + ), + ); + } + // In development mode, if custom router options are provided, warn if both routerPush and routerReplace are not provided if ( this.#instanceType === 'development' && @@ -591,22 +612,18 @@ export class Clerk implements ClerkInterface { }; public openGoogleOneTap = (props?: GoogleOneTapProps): void => { + this.assertComponentsReady(this.#clerkUi); const component = 'GoogleOneTap'; - this.assertComponentsReady(this.#componentControls); - void this.#componentControls - .ensureMounted({ preloadHint: component }) - .then(controls => controls.openModal('googleOneTap', props || {})); + void this.#clerkUi.then(ui => ui.ensureMounted()).then(controls => controls.openModal('googleOneTap', props || {})); this.telemetry?.record(eventPrebuiltComponentOpened(component, props)); }; public closeGoogleOneTap = (): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.closeModal('googleOneTap')); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.closeModal('googleOneTap')); }; public openSignIn = (props?: SignInProps): void => { - this.assertComponentsReady(this.#componentControls); if (isSignedInAndSingleSessionModeEnabled(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotOpenSignInOrSignUp, { @@ -615,22 +632,19 @@ export class Clerk implements ClerkInterface { } return; } + this.assertComponentsReady(this.#clerkUi); const component = 'SignIn'; - void this.#componentControls - .ensureMounted({ preloadHint: component }) - .then(controls => controls.openModal('signIn', props || {})); + void this.#clerkUi.then(ui => ui.ensureMounted()).then(controls => controls.openModal('signIn', props || {})); const additionalData = { withSignUp: props?.withSignUp ?? this.#isCombinedSignInOrUpFlow() }; this.telemetry?.record(eventPrebuiltComponentOpened(component, props, additionalData)); }; public closeSignIn = (): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.closeModal('signIn')); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.closeModal('signIn')); }; public __internal_openCheckout = (props?: __internal_CheckoutProps): void => { - this.assertComponentsReady(this.#componentControls); if (disabledAllBillingFeatures(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAnyBillingComponent('Checkout'), { @@ -648,18 +662,15 @@ export class Clerk implements ClerkInterface { return; } - void this.#componentControls - .ensureMounted({ preloadHint: 'Checkout' }) - .then(controls => controls.openDrawer('checkout', props || {})); + this.assertComponentsReady(this.#clerkUi); + void this.#clerkUi.then(ui => ui.ensureMounted()).then(controls => controls.openDrawer('checkout', props || {})); }; public __internal_closeCheckout = (): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.closeDrawer('checkout')); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.closeDrawer('checkout')); }; public __internal_openPlanDetails = (props: __internal_PlanDetailsProps): void => { - this.assertComponentsReady(this.#componentControls); if (disabledAllBillingFeatures(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAnyBillingComponent('PlanDetails'), { @@ -668,33 +679,29 @@ export class Clerk implements ClerkInterface { } return; } + this.assertComponentsReady(this.#clerkUi); const component = 'PlanDetails'; - void this.#componentControls - .ensureMounted({ preloadHint: component }) - .then(controls => controls.openDrawer('planDetails', props || {})); + void this.#clerkUi.then(ui => ui.ensureMounted()).then(controls => controls.openDrawer('planDetails', props || {})); this.telemetry?.record(eventPrebuiltComponentOpened(component, props)); }; public __internal_closePlanDetails = (): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.closeDrawer('planDetails')); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.closeDrawer('planDetails')); }; public __internal_openSubscriptionDetails = (props?: __internal_SubscriptionDetailsProps): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls - .ensureMounted({ preloadHint: 'SubscriptionDetails' }) + this.assertComponentsReady(this.#clerkUi); + void this.#clerkUi + .then(ui => ui.ensureMounted()) .then(controls => controls.openDrawer('subscriptionDetails', props || {})); }; public __internal_closeSubscriptionDetails = (): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.closeDrawer('subscriptionDetails')); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.closeDrawer('subscriptionDetails')); }; public __internal_openReverification = (props?: __internal_UserVerificationModalProps): void => { - this.assertComponentsReady(this.#componentControls); if (noUserExists(this)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotOpenUserProfile, { @@ -703,30 +710,26 @@ export class Clerk implements ClerkInterface { } return; } - void this.#componentControls - .ensureMounted({ preloadHint: 'UserVerification' }) + this.assertComponentsReady(this.#clerkUi); + void this.#clerkUi + .then(ui => ui.ensureMounted()) .then(controls => controls.openModal('userVerification', props || {})); this.telemetry?.record(eventPrebuiltComponentOpened(`UserVerification`, props)); }; public __internal_closeReverification = (): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.closeModal('userVerification')); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.closeModal('userVerification')); }; public __internal_openBlankCaptchaModal = (): Promise => { - this.assertComponentsReady(this.#componentControls); - return this.#componentControls - .ensureMounted({ preloadHint: 'BlankCaptchaModal' }) - .then(controls => controls.openModal('blankCaptcha', {})); + this.assertComponentsReady(this.#clerkUi); + return this.#clerkUi.then(ui => ui.ensureMounted()).then(controls => controls.openModal('blankCaptcha', {})); }; public __internal_closeBlankCaptchaModal = (): Promise => { - this.assertComponentsReady(this.#componentControls); - return this.#componentControls - .ensureMounted({ preloadHint: 'BlankCaptchaModal' }) - .then(controls => controls.closeModal('blankCaptcha')); + this.assertComponentsReady(this.#clerkUi); + return this.#clerkUi.then(ui => ui.ensureMounted()).then(controls => controls.closeModal('blankCaptcha')); }; public __internal_loadStripeJs = async () => { @@ -740,7 +743,6 @@ export class Clerk implements ClerkInterface { }; public openSignUp = (props?: SignUpProps): void => { - this.assertComponentsReady(this.#componentControls); if (isSignedInAndSingleSessionModeEnabled(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotOpenSignInOrSignUp, { @@ -749,20 +751,17 @@ export class Clerk implements ClerkInterface { } return; } - void this.#componentControls - .ensureMounted({ preloadHint: 'SignUp' }) - .then(controls => controls.openModal('signUp', props || {})); + this.assertComponentsReady(this.#clerkUi); + void this.#clerkUi.then(ui => ui.ensureMounted()).then(controls => controls.openModal('signUp', props || {})); this.telemetry?.record(eventPrebuiltComponentOpened('SignUp', props)); }; public closeSignUp = (): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.closeModal('signUp')); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.closeModal('signUp')); }; public openUserProfile = (props?: UserProfileProps): void => { - this.assertComponentsReady(this.#componentControls); if (noUserExists(this)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotOpenUserProfile, { @@ -771,21 +770,18 @@ export class Clerk implements ClerkInterface { } return; } - void this.#componentControls - .ensureMounted({ preloadHint: 'UserProfile' }) - .then(controls => controls.openModal('userProfile', props || {})); + this.assertComponentsReady(this.#clerkUi); + void this.#clerkUi.then(ui => ui.ensureMounted()).then(controls => controls.openModal('userProfile', props || {})); const additionalData = (props?.customPages?.length || 0) > 0 ? { customPages: true } : undefined; this.telemetry?.record(eventPrebuiltComponentOpened('UserProfile', props, additionalData)); }; public closeUserProfile = (): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.closeModal('userProfile')); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.closeModal('userProfile')); }; public openOrganizationProfile = (props?: OrganizationProfileProps): void => { - this.assertComponentsReady(this.#componentControls); if (disabledOrganizationsFeature(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationProfile'), { @@ -802,20 +798,19 @@ export class Clerk implements ClerkInterface { } return; } - void this.#componentControls - .ensureMounted({ preloadHint: 'OrganizationProfile' }) + this.assertComponentsReady(this.#clerkUi); + void this.#clerkUi + .then(ui => ui.ensureMounted()) .then(controls => controls.openModal('organizationProfile', props || {})); this.telemetry?.record(eventPrebuiltComponentOpened('OrganizationProfile', props)); }; public closeOrganizationProfile = (): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.closeModal('organizationProfile')); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.closeModal('organizationProfile')); }; public openCreateOrganization = (props?: CreateOrganizationProps): void => { - this.assertComponentsReady(this.#componentControls); if (disabledOrganizationsFeature(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('CreateOrganization'), { @@ -824,107 +819,94 @@ export class Clerk implements ClerkInterface { } return; } - void this.#componentControls - .ensureMounted({ preloadHint: 'CreateOrganization' }) + this.assertComponentsReady(this.#clerkUi); + void this.#clerkUi + .then(ui => ui.ensureMounted()) .then(controls => controls.openModal('createOrganization', props || {})); this.telemetry?.record(eventPrebuiltComponentOpened('CreateOrganization', props)); }; public closeCreateOrganization = (): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.closeModal('createOrganization')); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.closeModal('createOrganization')); }; public openWaitlist = (props?: WaitlistProps): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls - .ensureMounted({ preloadHint: 'Waitlist' }) - .then(controls => controls.openModal('waitlist', props || {})); + this.assertComponentsReady(this.#clerkUi); + void this.#clerkUi.then(ui => ui.ensureMounted()).then(controls => controls.openModal('waitlist', props || {})); this.telemetry?.record(eventPrebuiltComponentOpened('Waitlist', props)); }; public closeWaitlist = (): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.closeModal('waitlist')); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.closeModal('waitlist')); }; public mountSignIn = (node: HTMLDivElement, props?: SignInProps): void => { - this.assertComponentsReady(this.#componentControls); + this.assertComponentsReady(this.#clerkUi); const component = 'SignIn'; - void this.#componentControls.ensureMounted({ preloadHint: component }).then(controls => - controls.mountComponent({ - name: component, - appearanceKey: 'signIn', - node, - props, - }), - ); + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: 'signIn', + node, + props, + }), + ); const additionalData = { withSignUp: props?.withSignUp ?? this.#isCombinedSignInOrUpFlow() }; this.telemetry?.record(eventPrebuiltComponentMounted(component, props, additionalData)); }; public unmountSignIn = (node: HTMLDivElement): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => - controls.unmountComponent({ - node, - }), - ); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; public mountUserAvatar = (node: HTMLDivElement, props?: UserAvatarProps): void => { - this.assertComponentsReady(this.#componentControls); + this.assertComponentsReady(this.#clerkUi); const component = 'UserAvatar'; - void this.#componentControls.ensureMounted({ preloadHint: component }).then(controls => - controls.mountComponent({ - name: component, - appearanceKey: 'userAvatar', - node, - props, - }), - ); + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: 'userAvatar', + node, + props, + }), + ); this.telemetry?.record(eventPrebuiltComponentMounted(component, props)); }; public unmountUserAvatar = (node: HTMLDivElement): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => - controls.unmountComponent({ - node, - }), - ); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; public mountSignUp = (node: HTMLDivElement, props?: SignUpProps): void => { - this.assertComponentsReady(this.#componentControls); + this.assertComponentsReady(this.#clerkUi); const component = 'SignUp'; - void this.#componentControls.ensureMounted({ preloadHint: component }).then(controls => - controls.mountComponent({ - name: component, - appearanceKey: 'signUp', - node, - props, - }), - ); + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: 'signUp', + node, + props, + }), + ); this.telemetry?.record(eventPrebuiltComponentMounted(component, props)); }; public unmountSignUp = (node: HTMLDivElement): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => - controls.unmountComponent({ - node, - }), - ); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; public mountUserProfile = (node: HTMLDivElement, props?: UserProfileProps): void => { - this.assertComponentsReady(this.#componentControls); if (noUserExists(this)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderComponentWhenUserDoesNotExist, { @@ -933,31 +915,28 @@ export class Clerk implements ClerkInterface { } return; } + this.assertComponentsReady(this.#clerkUi); const component = 'UserProfile'; - void this.#componentControls.ensureMounted({ preloadHint: component }).then(controls => - controls.mountComponent({ - name: component, - appearanceKey: 'userProfile', - node, - props, - }), - ); + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: 'userProfile', + node, + props, + }), + ); const additionalData = (props?.customPages?.length || 0) > 0 ? { customPages: true } : undefined; this.telemetry?.record(eventPrebuiltComponentMounted(component, props, additionalData)); }; public unmountUserProfile = (node: HTMLDivElement): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => - controls.unmountComponent({ - node, - }), - ); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; public mountOrganizationProfile = (node: HTMLDivElement, props?: OrganizationProfileProps) => { - this.assertComponentsReady(this.#componentControls); if (disabledOrganizationsFeature(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationProfile'), { @@ -975,29 +954,27 @@ export class Clerk implements ClerkInterface { } return; } - void this.#componentControls.ensureMounted({ preloadHint: 'OrganizationProfile' }).then(controls => - controls.mountComponent({ - name: 'OrganizationProfile', - appearanceKey: 'userProfile', - node, - props, - }), - ); + this.assertComponentsReady(this.#clerkUi); + const component = 'OrganizationProfile'; + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: 'userProfile', + node, + props, + }), + ); - this.telemetry?.record(eventPrebuiltComponentMounted('OrganizationProfile', props)); + this.telemetry?.record(eventPrebuiltComponentMounted(component, props)); }; public unmountOrganizationProfile = (node: HTMLDivElement) => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => - controls.unmountComponent({ - node, - }), - ); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; public mountCreateOrganization = (node: HTMLDivElement, props?: CreateOrganizationProps) => { - this.assertComponentsReady(this.#componentControls); if (disabledOrganizationsFeature(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('CreateOrganization'), { @@ -1006,29 +983,27 @@ export class Clerk implements ClerkInterface { } return; } - void this.#componentControls?.ensureMounted({ preloadHint: 'CreateOrganization' }).then(controls => - controls.mountComponent({ - name: 'CreateOrganization', - appearanceKey: 'createOrganization', - node, - props, - }), - ); + this.assertComponentsReady(this.#clerkUi); + const component = 'CreateOrganization'; + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: 'createOrganization', + node, + props, + }), + ); - this.telemetry?.record(eventPrebuiltComponentMounted('CreateOrganization', props)); + this.telemetry?.record(eventPrebuiltComponentMounted(component, props)); }; public unmountCreateOrganization = (node: HTMLDivElement) => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls?.ensureMounted().then(controls => - controls.unmountComponent({ - node, - }), - ); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; public mountOrganizationSwitcher = (node: HTMLDivElement, props?: OrganizationSwitcherProps) => { - this.assertComponentsReady(this.#componentControls); if (disabledOrganizationsFeature(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationSwitcher'), { @@ -1037,17 +1012,21 @@ export class Clerk implements ClerkInterface { } return; } - void this.#componentControls?.ensureMounted({ preloadHint: 'OrganizationSwitcher' }).then(controls => - controls.mountComponent({ - name: 'OrganizationSwitcher', - appearanceKey: 'organizationSwitcher', - node, - props, - }), - ); + this.assertComponentsReady(this.#clerkUi); + const component = 'OrganizationSwitcher'; + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: 'organizationSwitcher', + node, + props, + }), + ); this.telemetry?.record( - eventPrebuiltComponentMounted('OrganizationSwitcher', { + eventPrebuiltComponentMounted(component, { ...props, forceOrganizationSelection: this.environment?.organizationSettings.forceOrganizationSelection, }), @@ -1055,19 +1034,15 @@ export class Clerk implements ClerkInterface { }; public unmountOrganizationSwitcher = (node: HTMLDivElement): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls?.ensureMounted().then(controls => controls.unmountComponent({ node })); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; public __experimental_prefetchOrganizationSwitcher = () => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls - ?.ensureMounted({ preloadHint: 'OrganizationSwitcher' }) - .then(controls => controls.prefetch('organizationSwitcher')); + this.assertComponentsReady(this.#clerkUi); + void this.#clerkUi.then(ui => ui.ensureMounted()).then(controls => controls.prefetch('organizationSwitcher')); }; public mountOrganizationList = (node: HTMLDivElement, props?: OrganizationListProps) => { - this.assertComponentsReady(this.#componentControls); if (disabledOrganizationsFeature(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationList'), { @@ -1076,17 +1051,21 @@ export class Clerk implements ClerkInterface { } return; } - void this.#componentControls?.ensureMounted({ preloadHint: 'OrganizationList' }).then(controls => - controls.mountComponent({ - name: 'OrganizationList', - appearanceKey: 'organizationList', - node, - props, - }), - ); + this.assertComponentsReady(this.#clerkUi); + const component = 'OrganizationList'; + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: 'organizationList', + node, + props, + }), + ); this.telemetry?.record( - eventPrebuiltComponentMounted('OrganizationList', { + eventPrebuiltComponentMounted(component, { ...props, forceOrganizationSelection: this.environment?.organizationSettings.forceOrganizationSelection, }), @@ -1094,55 +1073,57 @@ export class Clerk implements ClerkInterface { }; public unmountOrganizationList = (node: HTMLDivElement): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls?.ensureMounted().then(controls => controls.unmountComponent({ node })); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; public mountUserButton = (node: HTMLDivElement, props?: UserButtonProps) => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls?.ensureMounted({ preloadHint: 'UserButton' }).then(controls => - controls.mountComponent({ - name: 'UserButton', - appearanceKey: 'userButton', - node, - props, - }), - ); + this.assertComponentsReady(this.#clerkUi); + const component = 'UserButton'; + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: 'userButton', + node, + props, + }), + ); const additionalData = { ...(props?.customMenuItems?.length || 0 > 0 ? { customItems: true } : undefined), ...(props?.__experimental_asStandalone ? { standalone: true } : undefined), }; - this.telemetry?.record(eventPrebuiltComponentMounted('UserButton', props, additionalData)); + this.telemetry?.record(eventPrebuiltComponentMounted(component, props, additionalData)); }; public unmountUserButton = (node: HTMLDivElement): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls?.ensureMounted().then(controls => controls.unmountComponent({ node })); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; public mountWaitlist = (node: HTMLDivElement, props?: WaitlistProps) => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls?.ensureMounted({ preloadHint: 'Waitlist' }).then(controls => - controls.mountComponent({ - name: 'Waitlist', - appearanceKey: 'waitlist', - node, - props, - }), - ); + this.assertComponentsReady(this.#clerkUi); + const component = 'Waitlist'; + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: 'waitlist', + node, + props, + }), + ); - this.telemetry?.record(eventPrebuiltComponentMounted('Waitlist', props)); + this.telemetry?.record(eventPrebuiltComponentMounted(component, props)); }; public unmountWaitlist = (node: HTMLDivElement): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls?.ensureMounted().then(controls => controls.unmountComponent({ node })); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; public mountPricingTable = (node: HTMLDivElement, props?: PricingTableProps): void => { - this.assertComponentsReady(this.#componentControls); if (disabledAllBillingFeatures(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAnyBillingComponent('PricingTable'), { @@ -1159,42 +1140,43 @@ export class Clerk implements ClerkInterface { ); } - void this.#componentControls.ensureMounted({ preloadHint: 'PricingTable' }).then(controls => - controls.mountComponent({ - name: 'PricingTable', - appearanceKey: 'pricingTable', - node, - props: nextProps, - }), - ); + this.assertComponentsReady(this.#clerkUi); + const component = 'PricingTable'; + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: 'pricingTable', + node, + props: nextProps, + }), + ); - this.telemetry?.record(eventPrebuiltComponentMounted('PricingTable', nextProps)); + this.telemetry?.record(eventPrebuiltComponentMounted(component, nextProps)); }; public unmountPricingTable = (node: HTMLDivElement): void => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => - controls.unmountComponent({ - node, - }), - ); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; public __internal_mountOAuthConsent = (node: HTMLDivElement, props?: __internal_OAuthConsentProps) => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted({ preloadHint: 'OAuthConsent' }).then(controls => - controls.mountComponent({ - name: 'OAuthConsent', - appearanceKey: '__internal_oauthConsent', - node, - props, - }), - ); + this.assertComponentsReady(this.#clerkUi); + const component = 'OAuthConsent'; + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: '__internal_oauthConsent', + node, + props, + }), + ); }; public __internal_unmountOAuthConsent = (node: HTMLDivElement) => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.unmountComponent({ node })); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; /** @@ -1205,8 +1187,6 @@ export class Clerk implements ClerkInterface { * @param props Configuration parameters. */ public mountApiKeys = (node: HTMLDivElement, props?: APIKeysProps) => { - this.assertComponentsReady(this.#componentControls); - logger.warnOnce('Clerk: component is in early access and not yet recommended for production use.'); if (disabledAPIKeysFeature(this, this.environment)) { @@ -1227,16 +1207,20 @@ export class Clerk implements ClerkInterface { return; } - void this.#componentControls.ensureMounted({ preloadHint: 'APIKeys' }).then(controls => - controls.mountComponent({ - name: 'APIKeys', - appearanceKey: 'apiKeys', - node, - props, - }), - ); + this.assertComponentsReady(this.#clerkUi); + const component = 'APIKeys'; + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: 'apiKeys', + node, + props, + }), + ); - this.telemetry?.record(eventPrebuiltComponentMounted('APIKeys', props)); + this.telemetry?.record(eventPrebuiltComponentMounted(component, props)); }; /** @@ -1248,13 +1232,10 @@ export class Clerk implements ClerkInterface { * @param targetNode Target node to unmount the ApiKeys component from. */ public unmountApiKeys = (node: HTMLDivElement) => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.unmountComponent({ node })); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; public mountTaskChooseOrganization = (node: HTMLDivElement, props?: TaskChooseOrganizationProps) => { - this.assertComponentsReady(this.#componentControls); - if (disabledOrganizationsFeature(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('TaskChooseOrganization'), { @@ -1264,21 +1245,24 @@ export class Clerk implements ClerkInterface { return; } - void this.#componentControls.ensureMounted({ preloadHint: 'TaskChooseOrganization' }).then(controls => - controls.mountComponent({ - name: 'TaskChooseOrganization', - appearanceKey: 'taskChooseOrganization', - node, - props, - }), - ); + this.assertComponentsReady(this.#clerkUi); + const component = 'TaskChooseOrganization'; + void this.#clerkUi + .then(ui => ui.ensureMounted()) + .then(controls => + controls.mountComponent({ + name: component, + appearanceKey: 'taskChooseOrganization', + node, + props, + }), + ); - this.telemetry?.record(eventPrebuiltComponentMounted('TaskChooseOrganization', props)); + this.telemetry?.record(eventPrebuiltComponentMounted(component, props)); }; public unmountTaskChooseOrganization = (node: HTMLDivElement) => { - this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted().then(controls => controls.unmountComponent({ node })); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.unmountComponent({ node })); }; /** @@ -2395,9 +2379,10 @@ export class Clerk implements ClerkInterface { __unstable__setEnvironment = async (env: EnvironmentJSON) => { this.environment = new Environment(env); - if (Clerk.mountComponentRenderer) { - this.#componentControls = Clerk.mountComponentRenderer(this, this.environment, this.#options); - } + // TODO @nikos update + // if (Clerk.mountComponentRenderer) { + // this.#componentRenderer = Clerk.mountComponentRenderer(this, this.environment, this.#options); + // } }; __unstable__onBeforeRequest = (callback: FapiRequestCallback): void => { @@ -2421,7 +2406,7 @@ export class Clerk implements ClerkInterface { options: this.#initOptions({ ...this.#options, ..._props.options }), }; - return this.#componentControls?.ensureMounted().then(controls => controls.updateProps(props)); + return this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.updateProps(props)); }; __internal_navigateWithError(to: string, err: ClerkAPIError) { @@ -2630,23 +2615,11 @@ export class Clerk implements ClerkInterface { }); }; - const initComponents = () => { - if (Clerk.mountComponentRenderer && !this.#componentControls) { - this.#componentControls = Clerk.mountComponentRenderer( - this, - this.environment as Environment, - this.#options, - ); - } - }; - const [, clientResult] = await allSettled([initEnvironmentPromise, initClient()]); - if (clientResult.status === 'rejected') { const e = clientResult.reason; if (isError(e, 'requires_captcha')) { - initComponents(); await initClient(); } else { throw e; @@ -2658,9 +2631,6 @@ export class Clerk implements ClerkInterface { if (await this.#redirectFAPIInitiatedFlow()) { return; } - - initComponents(); - break; } catch (err) { if (isError(err, 'dev_browser_unauthenticated')) { @@ -2714,13 +2684,7 @@ export class Clerk implements ClerkInterface { this.updateClient(client); this.updateEnvironment(environment); - // TODO: Add an auth service also for non standard browsers that will poll for the __session JWT but won't use cookies - - if (Clerk.mountComponentRenderer) { - this.#componentControls = Clerk.mountComponentRenderer(this, this.environment, this.#options); - } - this.#publicEventBus.emit(clerkEvents.Status, initializationDegradedCounter > 0 ? 'degraded' : 'ready'); }; @@ -2861,23 +2825,26 @@ export class Clerk implements ClerkInterface { this.addListener(({ session }) => { const isImpersonating = !!session?.actor; if (isImpersonating) { - void this.#componentControls?.ensureMounted().then(controls => controls.mountImpersonationFab()); + void this.#clerkUi?.then(ui => ui.ensureMounted()).then(controls => controls.mountImpersonationFab()); } }); }; #handleKeylessPrompt = () => { if (this.#options.__internal_keyless_claimKeylessApplicationUrl) { - void this.#componentControls?.ensureMounted().then(controls => { - // TODO(@pantelis): Investigate if this resets existing props - controls.updateProps({ - options: { - __internal_keyless_claimKeylessApplicationUrl: this.#options.__internal_keyless_claimKeylessApplicationUrl, - __internal_keyless_copyInstanceKeysUrl: this.#options.__internal_keyless_copyInstanceKeysUrl, - __internal_keyless_dismissPrompt: this.#options.__internal_keyless_dismissPrompt, - }, + void this.#clerkUi + ?.then(ui => ui.ensureMounted()) + .then(controls => { + // TODO(@pantelis): Investigate if this resets existing props + controls.updateProps({ + options: { + __internal_keyless_claimKeylessApplicationUrl: + this.#options.__internal_keyless_claimKeylessApplicationUrl, + __internal_keyless_copyInstanceKeysUrl: this.#options.__internal_keyless_copyInstanceKeysUrl, + __internal_keyless_dismissPrompt: this.#options.__internal_keyless_dismissPrompt, + }, + }); }); - }); } }; @@ -2908,12 +2875,9 @@ export class Clerk implements ClerkInterface { return this.buildUrlWithAuth(url); }; - assertComponentsReady(controls: unknown): asserts controls is ReturnType { - if (!Clerk.mountComponentRenderer) { - throw new Error('ClerkJS was loaded without UI components.'); - } - if (!controls) { - throw new Error('ClerkJS components are not ready yet.'); + assertComponentsReady(val: unknown): asserts val is ClerkUi { + if (!val) { + throw new Error('Clerk was not loaded with Ui components'); } } @@ -2942,16 +2906,9 @@ export class Clerk implements ClerkInterface { }; #initOptions = (options?: ClerkOptions): ClerkOptions => { - const processedOptions = options ? { ...options } : {}; - - // Extract cssLayerName from baseTheme if present and move it to appearance level - if (processedOptions.appearance) { - processedOptions.appearance = processCssLayerNameExtraction(processedOptions.appearance); - } - return { ...defaultOptions, - ...processedOptions, + ...options, allowedRedirectOrigins: createAllowedRedirectOrigins( options?.allowedRedirectOrigins, this.frontendApi, diff --git a/packages/clerk-js/src/core/constants.ts b/packages/clerk-js/src/core/constants.ts index 98cd6bdcce9..43ee8e244f7 100644 --- a/packages/clerk-js/src/core/constants.ts +++ b/packages/clerk-js/src/core/constants.ts @@ -1,57 +1,6 @@ -import type { SignUpModes } from '@clerk/shared/types'; - -// TODO: Do we still have a use for this or can we simply preserve all params? -export const PRESERVED_QUERYSTRING_PARAMS = [ - 'redirect_url', - 'after_sign_in_url', - 'after_sign_up_url', - 'sign_in_force_redirect_url', - 'sign_in_fallback_redirect_url', - 'sign_up_force_redirect_url', - 'sign_up_fallback_redirect_url', -]; - -export const CLERK_MODAL_STATE = '__clerk_modal_state'; -export const CLERK_SYNCED = '__clerk_synced'; -export const CLERK_SUFFIXED_COOKIES = 'suffixed_cookies'; -export const CLERK_SATELLITE_URL = '__clerk_satellite_url'; -export const ERROR_CODES = { - FORM_IDENTIFIER_NOT_FOUND: 'form_identifier_not_found', - FORM_PASSWORD_INCORRECT: 'form_password_incorrect', - FORM_PASSWORD_PWNED: 'form_password_pwned', - INVALID_STRATEGY_FOR_USER: 'strategy_for_user_invalid', - NOT_ALLOWED_TO_SIGN_UP: 'not_allowed_to_sign_up', - OAUTH_ACCESS_DENIED: 'oauth_access_denied', - OAUTH_EMAIL_DOMAIN_RESERVED_BY_SAML: 'oauth_email_domain_reserved_by_saml', - NOT_ALLOWED_ACCESS: 'not_allowed_access', - SAML_USER_ATTRIBUTE_MISSING: 'saml_user_attribute_missing', - USER_LOCKED: 'user_locked', - EXTERNAL_ACCOUNT_NOT_FOUND: 'external_account_not_found', - SIGN_UP_MODE_RESTRICTED: 'sign_up_mode_restricted', - SIGN_UP_MODE_RESTRICTED_WAITLIST: 'sign_up_restricted_waitlist', - ENTERPRISE_SSO_USER_ATTRIBUTE_MISSING: 'enterprise_sso_user_attribute_missing', - ENTERPRISE_SSO_EMAIL_ADDRESS_DOMAIN_MISMATCH: 'enterprise_sso_email_address_domain_mismatch', - ENTERPRISE_SSO_HOSTED_DOMAIN_MISMATCH: 'enterprise_sso_hosted_domain_mismatch', - SAML_EMAIL_ADDRESS_DOMAIN_MISMATCH: 'saml_email_address_domain_mismatch', - INVITATION_ACCOUNT_NOT_EXISTS: 'invitation_account_not_exists', - ORGANIZATION_MEMBERSHIP_QUOTA_EXCEEDED_FOR_SSO: 'organization_membership_quota_exceeded_for_sso', - CAPTCHA_INVALID: 'captcha_invalid', - FRAUD_DEVICE_BLOCKED: 'device_blocked', - FRAUD_ACTION_BLOCKED: 'action_blocked', - SIGNUP_RATE_LIMIT_EXCEEDED: 'signup_rate_limit_exceeded', - USER_BANNED: 'user_banned', -} as const; - -export const SIGN_IN_INITIAL_VALUE_KEYS = ['email_address', 'phone_number', 'username']; -export const SIGN_UP_INITIAL_VALUE_KEYS = ['email_address', 'phone_number', 'username', 'first_name', 'last_name']; - -export const DEBOUNCE_MS = 350; - -export const SIGN_UP_MODES = { - PUBLIC: 'public', - RESTRICTED: 'restricted', - WAITLIST: 'waitlist', -} satisfies Record; - -// This is the currently supported version of the Frontend API -export const SUPPORTED_FAPI_VERSION = '2025-04-10'; +/** + * Re-exporting constants from @clerk/shared to avoid refactoring all imports. + * The constants have been moved to @clerk/shared/internal/clerk-js/constants + * to make them available across all Clerk packages. + */ +export * from '@clerk/shared/internal/clerk-js/constants'; diff --git a/packages/clerk-js/src/core/errors.ts b/packages/clerk-js/src/core/errors.ts index 3a589781565..45005bff61e 100644 --- a/packages/clerk-js/src/core/errors.ts +++ b/packages/clerk-js/src/core/errors.ts @@ -1,129 +1 @@ -const errorPrefix = 'ClerkJS:'; - -/** - * Used to log a warning when a Clerk feature is used in an unsupported environment. - * (Development Only) - * This is a warning and not an error because the application will still work, but the feature will not be available. - * - * @param strategy The strategy that is not supported in the current environment. - * @returns void - */ -export function clerkUnsupportedEnvironmentWarning(strategy: string) { - console.warn(`${errorPrefix} ${strategy} is not supported in this environment.`); -} - -export function clerkNetworkError(url: string, e: Error): never { - throw new Error(`${errorPrefix} Network error at "${url}" - ${e}. Please try again.`); -} - -export function clerkErrorInitFailed(): never { - throw new Error(`${errorPrefix} Something went wrong initializing Clerk.`); -} - -export function clerkErrorDevInitFailed(msg = ''): never { - throw new Error(`${errorPrefix} Something went wrong initializing Clerk in development mode.${msg && ` ${msg}`}`); -} - -export function clerkErrorPathRouterMissingPath(componentName: string): never { - throw new Error( - `${errorPrefix} Missing path option. The ${componentName} component was mounted with path routing so you need to specify the path where the component is mounted on e.g. path="/sign-in".`, - ); -} - -export function clerkCoreErrorContextProviderNotFound(providerName: string): never { - throw new Error(`${errorPrefix} You must wrap your application in a <${providerName}> component.`); -} - -export function clerkCoreErrorNoClerkSingleton(): never { - throw new Error(`${errorPrefix} Clerk is undefined`); -} - -export function clerkUIErrorDOMElementNotFound(): never { - throw new Error(`${errorPrefix} The target element is empty. Provide a valid DOM element.`); -} - -export function clerkMissingFapiClientInResources(): never { - throw new Error(`${errorPrefix} Missing FAPI client in resources.`); -} - -export function clerkOAuthCallbackDidNotCompleteSignInSignUp(type: 'sign in' | 'sign up'): never { - throw new Error( - `${errorPrefix} Something went wrong initializing Clerk during the ${type} flow. Please contact support.`, - ); -} - -export function clerkVerifyEmailAddressCalledBeforeCreate(type: 'SignIn' | 'SignUp'): never { - throw new Error(`${errorPrefix} You need to start a ${type} flow by calling ${type}.create() first.`); -} - -export function clerkInvalidStrategy(functionaName: string, strategy: string): never { - throw new Error(`${errorPrefix} Strategy "${strategy}" is not a valid strategy for ${functionaName}.`); -} - -export function clerkVerifyWeb3WalletCalledBeforeCreate(type: 'SignIn' | 'SignUp'): never { - throw new Error( - `${errorPrefix} You need to start a ${type} flow by calling ${type}.create({ identifier: 'your web3 wallet address' }) first`, - ); -} - -export function clerkVerifyPasskeyCalledBeforeCreate(): never { - throw new Error( - `${errorPrefix} You need to start a SignIn flow by calling SignIn.create({ strategy: 'passkey' }) first`, - ); -} - -export function clerkMissingOptionError(name = ''): never { - throw new Error(`${errorPrefix} Missing '${name}' option`); -} - -export function clerkInvalidFAPIResponse(status: string | null, supportEmail: string): never { - throw new Error( - `${errorPrefix} Response: ${status || 0} not supported yet.\nFor more information contact us at ${supportEmail}`, - ); -} - -export function clerkMissingDevBrowserJwt(): never { - throw new Error(`${errorPrefix} Missing dev browser jwt. Please contact support.`); -} - -export function clerkMissingProxyUrlAndDomain(): never { - throw new Error( - `${errorPrefix} Missing domain and proxyUrl. A satellite application needs to specify a domain or a proxyUrl.`, - ); -} - -export function clerkInvalidSignInUrlOrigin(): never { - throw new Error(`${errorPrefix} The signInUrl needs to be on a different origin than your satellite application.`); -} - -export function clerkInvalidSignInUrlFormat(): never { - throw new Error(`${errorPrefix} The signInUrl needs to have a absolute url format.`); -} - -export function clerkMissingSignInUrlAsSatellite(): never { - throw new Error( - `${errorPrefix} Missing signInUrl. A satellite application needs to specify the signInUrl for development instances.`, - ); -} - -export function clerkRedirectUrlIsMissingScheme(): never { - throw new Error(`${errorPrefix} Invalid redirect_url. A valid http or https url should be used for the redirection.`); -} - -export function clerkFailedToLoadThirdPartyScript(name?: string): never { - throw new Error(`${errorPrefix} Unable to retrieve a third party script${name ? ` ${name}` : ''}.`); -} - -export function clerkInvalidRoutingStrategy(strategy?: string): never { - throw new Error(`${errorPrefix} Invalid routing strategy, path cannot be used in tandem with ${strategy}.`); -} - -export function clerkUnsupportedReloadMethod(className: string): never { - throw new Error(`${errorPrefix} Calling ${className}.reload is not currently supported. Please contact support.`); -} - -export function clerkMissingWebAuthnPublicKeyOptions(name: 'create' | 'get'): never { - throw new Error( - `${errorPrefix} Missing publicKey. When calling 'navigator.credentials.${name}()' it is required to pass a publicKey object.`, - ); -} +export * from '@clerk/shared/internal/clerk-js/errors'; diff --git a/packages/clerk-js/src/core/fapiClient.ts b/packages/clerk-js/src/core/fapiClient.ts index 7d03e620c27..94060f9ebf6 100644 --- a/packages/clerk-js/src/core/fapiClient.ts +++ b/packages/clerk-js/src/core/fapiClient.ts @@ -1,16 +1,13 @@ import { isBrowserOnline } from '@clerk/shared/browser'; +import { buildEmailAddress as buildEmailAddressUtil } from '@clerk/shared/internal/clerk-js/email'; +import { stringifyQueryParams } from '@clerk/shared/internal/clerk-js/querystring'; import { retry } from '@clerk/shared/retry'; import type { ClerkAPIErrorJSON, ClientJSON, InstanceType } from '@clerk/shared/types'; import { camelToSnake } from '@clerk/shared/underscore'; import { debugLogger } from '@/utils/debug'; -import { - buildEmailAddress as buildEmailAddressUtil, - buildURL as buildUrlUtil, - filterUndefinedValues, - stringifyQueryParams, -} from '../utils'; +import { buildURL as buildUrlUtil, filterUndefinedValues } from '../utils'; import { SUPPORTED_FAPI_VERSION } from './constants'; import { clerkNetworkError } from './errors'; diff --git a/packages/clerk-js/src/core/resources/Passkey.ts b/packages/clerk-js/src/core/resources/Passkey.ts index ea415f635c5..b073e1f4b4c 100644 --- a/packages/clerk-js/src/core/resources/Passkey.ts +++ b/packages/clerk-js/src/core/resources/Passkey.ts @@ -18,7 +18,7 @@ import { unixEpochToDate } from '../../utils/date'; import { serializePublicKeyCredential, webAuthnCreateCredential as webAuthnCreateCredentialOnWindow, -} from '../../utils/passkeys'; +} from '@clerk/shared/internal/clerk-js/passkeys'; import { clerkMissingWebAuthnPublicKeyOptions } from '../errors'; import { BaseResource, DeletedObject, PasskeyVerification } from './internal'; diff --git a/packages/clerk-js/src/core/resources/Session.ts b/packages/clerk-js/src/core/resources/Session.ts index b774b12730f..c6e78e1887b 100644 --- a/packages/clerk-js/src/core/resources/Session.ts +++ b/packages/clerk-js/src/core/resources/Session.ts @@ -32,7 +32,7 @@ import { convertJSONToPublicKeyRequestOptions, serializePublicKeyCredentialAssertion, webAuthnGetCredential as webAuthnGetCredentialOnWindow, -} from '@/utils/passkeys'; +} from '@clerk/shared/internal/clerk-js/passkeys'; import { TokenId } from '@/utils/tokenId'; import { clerkInvalidStrategy, clerkMissingWebAuthnPublicKeyOptions } from '../errors'; diff --git a/packages/clerk-js/src/core/resources/SignIn.ts b/packages/clerk-js/src/core/resources/SignIn.ts index e8a933190f1..e9498d9b5ad 100644 --- a/packages/clerk-js/src/core/resources/SignIn.ts +++ b/packages/clerk-js/src/core/resources/SignIn.ts @@ -1,5 +1,22 @@ import { inBrowser } from '@clerk/shared/browser'; import { ClerkWebAuthnError } from '@clerk/shared/error'; +import { + convertJSONToPublicKeyRequestOptions, + serializePublicKeyCredentialAssertion, + webAuthnGetCredential as webAuthnGetCredentialOnWindow, +} from '@clerk/shared/internal/clerk-js/passkeys'; +import { createValidatePassword } from '@clerk/shared/internal/clerk-js/passwords/password'; +import { + generateSignatureWithBase, + generateSignatureWithCoinbaseWallet, + generateSignatureWithMetamask, + generateSignatureWithOKXWallet, + getBaseIdentifier, + getCoinbaseWalletIdentifier, + getMetamaskIdentifier, + getOKXWalletIdentifier, +} from '@clerk/shared/internal/clerk-js/web3'; +import { windowNavigate } from '@clerk/shared/internal/clerk-js/windowNavigate'; import { Poller } from '@clerk/shared/poller'; import type { AttemptFirstFactorParams, @@ -63,27 +80,10 @@ import { import { debugLogger } from '@/utils/debug'; -import { - generateSignatureWithBase, - generateSignatureWithCoinbaseWallet, - generateSignatureWithMetamask, - generateSignatureWithOKXWallet, - getBaseIdentifier, - getBrowserLocale, - getClerkQueryParam, - getCoinbaseWalletIdentifier, - getMetamaskIdentifier, - getOKXWalletIdentifier, - windowNavigate, -} from '../../utils'; +import { getBrowserLocale, getClerkQueryParam } from '../../utils'; import { _authenticateWithPopup } from '../../utils/authenticateWithPopup'; -import { - convertJSONToPublicKeyRequestOptions, - serializePublicKeyCredentialAssertion, - webAuthnGetCredential as webAuthnGetCredentialOnWindow, -} from '../../utils/passkeys'; -import { createValidatePassword } from '../../utils/passwords/password'; import { runAsyncResourceTask } from '../../utils/runAsyncResourceTask'; +import { loadZxcvbn } from '../../utils/zxcvbn'; import { clerkInvalidFAPIResponse, clerkInvalidStrategy, @@ -513,7 +513,7 @@ export class SignIn extends BaseResource implements SignInResource { validatePassword: ReturnType = (password, cb) => { if (SignIn.clerk.__unstable__environment?.userSettings.passwordSettings) { - return createValidatePassword({ + return createValidatePassword(loadZxcvbn, { ...SignIn.clerk.__unstable__environment?.userSettings.passwordSettings, validatePassword: true, })(password, cb); diff --git a/packages/clerk-js/src/core/resources/SignUp.ts b/packages/clerk-js/src/core/resources/SignUp.ts index 141d768ee6f..fd5632072b2 100644 --- a/packages/clerk-js/src/core/resources/SignUp.ts +++ b/packages/clerk-js/src/core/resources/SignUp.ts @@ -1,4 +1,16 @@ import { ClerkRuntimeError, isCaptchaError, isClerkAPIResponseError } from '@clerk/shared/error'; +import { createValidatePassword } from '@clerk/shared/internal/clerk-js/passwords/password'; +import { + generateSignatureWithBase, + generateSignatureWithCoinbaseWallet, + generateSignatureWithMetamask, + generateSignatureWithOKXWallet, + getBaseIdentifier, + getCoinbaseWalletIdentifier, + getMetamaskIdentifier, + getOKXWalletIdentifier, +} from '@clerk/shared/internal/clerk-js/web3'; +import { windowNavigate } from '@clerk/shared/internal/clerk-js/windowNavigate'; import { Poller } from '@clerk/shared/poller'; import type { AttemptEmailAddressVerificationParams, @@ -42,24 +54,12 @@ import type { import { debugLogger } from '@/utils/debug'; -import { - generateSignatureWithBase, - generateSignatureWithCoinbaseWallet, - generateSignatureWithMetamask, - generateSignatureWithOKXWallet, - getBaseIdentifier, - getBrowserLocale, - getClerkQueryParam, - getCoinbaseWalletIdentifier, - getMetamaskIdentifier, - getOKXWalletIdentifier, - windowNavigate, -} from '../../utils'; +import { getBrowserLocale, getClerkQueryParam } from '../../utils'; import { _authenticateWithPopup } from '../../utils/authenticateWithPopup'; import { CaptchaChallenge } from '../../utils/captcha/CaptchaChallenge'; -import { createValidatePassword } from '../../utils/passwords/password'; import { normalizeUnsafeMetadata } from '../../utils/resourceParams'; import { runAsyncResourceTask } from '../../utils/runAsyncResourceTask'; +import { loadZxcvbn } from '../../utils/zxcvbn'; import { clerkInvalidFAPIResponse, clerkMissingOptionError, @@ -463,7 +463,7 @@ export class SignUp extends BaseResource implements SignUpResource { validatePassword: ReturnType = (password, cb) => { if (SignUp.clerk.__unstable__environment?.userSettings.passwordSettings) { - return createValidatePassword({ + return createValidatePassword(loadZxcvbn, { ...SignUp.clerk.__unstable__environment?.userSettings.passwordSettings, validatePassword: true, })(password, cb); diff --git a/packages/clerk-js/src/core/resources/User.ts b/packages/clerk-js/src/core/resources/User.ts index 8f61851b1c5..3de4b518943 100644 --- a/packages/clerk-js/src/core/resources/User.ts +++ b/packages/clerk-js/src/core/resources/User.ts @@ -1,3 +1,4 @@ +import { getFullName } from '@clerk/shared/internal/clerk-js/user'; import type { BackupCodeJSON, BackupCodeResource, @@ -34,7 +35,6 @@ import type { import { unixEpochToDate } from '../../utils/date'; import { normalizeUnsafeMetadata } from '../../utils/resourceParams'; -import { getFullName } from '../../utils/user'; import { eventBus, events } from '../events'; import { addPaymentMethod, getPaymentMethods, initializePaymentMethod } from '../modules/billing'; import { BackupCode } from './BackupCode'; diff --git a/packages/clerk-js/src/core/resources/Verification.ts b/packages/clerk-js/src/core/resources/Verification.ts index d9f739d2565..8adde912f1d 100644 --- a/packages/clerk-js/src/core/resources/Verification.ts +++ b/packages/clerk-js/src/core/resources/Verification.ts @@ -17,7 +17,7 @@ import type { } from '@clerk/shared/types'; import { unixEpochToDate } from '../../utils/date'; -import { convertJSONToPublicKeyCreateOptions } from '../../utils/passkeys'; +import { convertJSONToPublicKeyCreateOptions } from '@clerk/shared/internal/clerk-js/passkeys'; import { BaseResource } from './internal'; export class Verification extends BaseResource implements VerificationResource { diff --git a/packages/clerk-js/src/core/warnings.ts b/packages/clerk-js/src/core/warnings.ts index 1d001112280..dc18ffaced5 100644 --- a/packages/clerk-js/src/core/warnings.ts +++ b/packages/clerk-js/src/core/warnings.ts @@ -1,63 +1,2 @@ -import type { Serializable } from '@clerk/shared/types'; - -const formatWarning = (msg: string) => { - return `🔒 Clerk:\n${msg.trim()}\n(This notice only appears in development)`; -}; - -const createMessageForDisabledOrganizations = ( - componentName: - | 'OrganizationProfile' - | 'OrganizationSwitcher' - | 'OrganizationList' - | 'CreateOrganization' - | 'TaskChooseOrganization', -) => { - return formatWarning( - `The <${componentName}/> cannot be rendered when the feature is turned off. Visit 'dashboard.clerk.com' to enable the feature. Since the feature is turned off, this is no-op.`, - ); -}; -const createMessageForDisabledBilling = (componentName: 'PricingTable' | 'Checkout' | 'PlanDetails') => { - return formatWarning( - `The <${componentName}/> component cannot be rendered when billing is disabled. Visit 'https://dashboard.clerk.com/last-active?path=billing/settings' to follow the necessary steps to enable billing. Since billing is disabled, this is no-op.`, - ); -}; -const warnings = { - cannotRenderComponentWhenSessionExists: - 'The and components cannot render when a user is already signed in, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session, Clerk is redirecting to the Home URL instead.', - cannotRenderSignUpComponentWhenSessionExists: - 'The component cannot render when a user is already signed in, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session, Clerk is redirecting to the value set in `afterSignUp` URL instead.', - cannotRenderSignUpComponentWhenTaskExists: - 'The component cannot render when a user has a pending task, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session, Clerk is redirecting to the task instead.', - cannotRenderComponentWhenTaskDoesNotExist: - ' cannot render unless a session task is pending. Clerk is redirecting to the value set in `redirectUrlComplete` instead.', - cannotRenderSignInComponentWhenSessionExists: - 'The component cannot render when a user is already signed in, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session, Clerk is redirecting to the `afterSignIn` URL instead.', - cannotRenderSignInComponentWhenTaskExists: - 'The component cannot render when a user has a pending task, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session, Clerk is redirecting to the task instead.', - cannotRenderComponentWhenUserDoesNotExist: - ' cannot render unless a user is signed in. Since no user is signed in, this is no-op.', - cannotRenderComponentWhenOrgDoesNotExist: ` cannot render unless an organization is active. Since no organization is currently active, this is no-op.`, - cannotRenderAnyOrganizationComponent: createMessageForDisabledOrganizations, - cannotRenderAnyBillingComponent: createMessageForDisabledBilling, - cannotOpenUserProfile: - 'The UserProfile modal cannot render unless a user is signed in. Since no user is signed in, this is no-op.', - cannotOpenCheckout: - 'The Checkout drawer cannot render unless a user is signed in. Since no user is signed in, this is no-op.', - cannotOpenSignInOrSignUp: - 'The SignIn or SignUp modals do not render when a user is already signed in, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session, this is no-op.', - cannotRenderAPIKeysComponent: - 'The component cannot be rendered when API keys is disabled. Since API keys is disabled, this is no-op.', - cannotRenderAPIKeysComponentForOrgWhenUnauthorized: - 'The component cannot be rendered for an organization unless a user has the required permissions. Since the user does not have the necessary permissions, this is no-op.', -}; - -type SerializableWarnings = Serializable; - -for (const key of Object.keys(warnings)) { - const item = warnings[key as keyof typeof warnings]; - if (typeof item !== 'function') { - warnings[key as keyof SerializableWarnings] = formatWarning(item); - } -} - -export { warnings }; +// Re-export warnings from shared package +export { warnings } from '@clerk/shared/internal/clerk-js/warnings'; diff --git a/packages/clerk-js/src/global.d.ts b/packages/clerk-js/src/global.d.ts index 50a17aae822..b5bf3ad8a5e 100644 --- a/packages/clerk-js/src/global.d.ts +++ b/packages/clerk-js/src/global.d.ts @@ -3,18 +3,20 @@ declare module '*.svg' { export default value; } -declare const __PKG_NAME__: string; -declare const __PKG_VERSION__: string; -declare const __DEV__: boolean; +declare global { + const __PKG_NAME__: string; + const __PKG_VERSION__: string; + const __DEV__: boolean; -/** - * Build time feature flags. - */ -declare const __BUILD_DISABLE_RHC__: string; -declare const __BUILD_VARIANT_CHANNEL__: boolean; -declare const __BUILD_VARIANT_CHIPS__: boolean; + /** + * Build time feature flags. + */ + const __BUILD_DISABLE_RHC__: string; + const __BUILD_VARIANT_CHANNEL__: boolean; + const __BUILD_VARIANT_CHIPS__: boolean; -interface Window { - __unstable__onBeforeSetActive: (intent?: 'sign-out') => Promise | void; - __unstable__onAfterSetActive: () => Promise | void; + interface Window { + __unstable__onBeforeSetActive: (intent?: 'sign-out') => Promise | void; + __unstable__onAfterSetActive: () => Promise | void; + } } diff --git a/packages/clerk-js/src/index.browser.ts b/packages/clerk-js/src/index.browser.ts index 82bd326c1a6..66647f279f6 100644 --- a/packages/clerk-js/src/index.browser.ts +++ b/packages/clerk-js/src/index.browser.ts @@ -1,14 +1,10 @@ // It's crucial this is the first import, // otherwise chunk loading will not work -// eslint-disable-next-line + import './utils/setWebpackChunkPublicPath'; import { Clerk } from './core/clerk'; -import { mountComponentRenderer } from './ui/Components'; - -Clerk.mountComponentRenderer = mountComponentRenderer; - const publishableKey = document.querySelector('script[data-clerk-publishable-key]')?.getAttribute('data-clerk-publishable-key') || window.__clerk_publishable_key || @@ -26,7 +22,6 @@ const domain = if (!window.Clerk) { window.Clerk = new Clerk(publishableKey, { proxyUrl, - // @ts-expect-error domain, }); } diff --git a/packages/clerk-js/src/index.legacy.browser.ts b/packages/clerk-js/src/index.legacy.browser.ts index 7d0d031c6cc..73dab30df70 100644 --- a/packages/clerk-js/src/index.legacy.browser.ts +++ b/packages/clerk-js/src/index.legacy.browser.ts @@ -7,10 +7,6 @@ import 'regenerator-runtime/runtime'; import { Clerk } from './core/clerk'; -import { mountComponentRenderer } from './ui/Components'; - -Clerk.mountComponentRenderer = mountComponentRenderer; - const publishableKey = document.querySelector('script[data-clerk-publishable-key]')?.getAttribute('data-clerk-publishable-key') || window.__clerk_publishable_key || @@ -28,7 +24,6 @@ const domain = if (!window.Clerk) { window.Clerk = new Clerk(publishableKey, { proxyUrl, - // @ts-expect-error domain, }); } diff --git a/packages/clerk-js/src/index.ts b/packages/clerk-js/src/index.ts index 2f9acf96b6e..f7ad89e436d 100644 --- a/packages/clerk-js/src/index.ts +++ b/packages/clerk-js/src/index.ts @@ -1,7 +1,6 @@ import 'regenerator-runtime/runtime'; import { Clerk } from './core/clerk'; -import { mountComponentRenderer } from './ui/Components'; export { ClerkAPIResponseError, @@ -19,8 +18,6 @@ export { } from '@clerk/shared/error'; export { Clerk }; -Clerk.mountComponentRenderer = mountComponentRenderer; - if (module.hot) { module.hot.accept(); } diff --git a/packages/clerk-js/src/ui/components/SignUp/util.ts b/packages/clerk-js/src/ui/components/SignUp/util.ts deleted file mode 100644 index 343a9bcaccc..00000000000 --- a/packages/clerk-js/src/ui/components/SignUp/util.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '../../../utils/completeSignUpFlow'; diff --git a/packages/clerk-js/src/ui/localization/defaultEnglishResource.ts b/packages/clerk-js/src/ui/localization/defaultEnglishResource.ts deleted file mode 100644 index 81219b7f1ef..00000000000 --- a/packages/clerk-js/src/ui/localization/defaultEnglishResource.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { enUS } from '@clerk/localizations'; -import type { DeepRequired } from '@clerk/shared/types'; - -export const defaultResource = enUS as DeepRequired; diff --git a/packages/clerk-js/src/utils/beforeUnloadTracker.ts b/packages/clerk-js/src/utils/beforeUnloadTracker.ts index 59094c63bed..a9ce63b70f1 100644 --- a/packages/clerk-js/src/utils/beforeUnloadTracker.ts +++ b/packages/clerk-js/src/utils/beforeUnloadTracker.ts @@ -1,4 +1,4 @@ -import { CLERK_BEFORE_UNLOAD_EVENT } from './windowNavigate'; +import { CLERK_BEFORE_UNLOAD_EVENT } from '@clerk/shared/internal/clerk-js/windowNavigate'; /** * Tracks beforeUnload events. diff --git a/packages/clerk-js/src/utils/captcha/constants.ts b/packages/clerk-js/src/utils/captcha/constants.ts deleted file mode 100644 index 7ad0a0e05c0..00000000000 --- a/packages/clerk-js/src/utils/captcha/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const CAPTCHA_ELEMENT_ID = 'clerk-captcha'; -export const CAPTCHA_INVISIBLE_CLASSNAME = 'clerk-invisible-captcha'; diff --git a/packages/clerk-js/src/utils/captcha/turnstile.ts b/packages/clerk-js/src/utils/captcha/turnstile.ts index 1a21f57d1dc..71150d992f4 100644 --- a/packages/clerk-js/src/utils/captcha/turnstile.ts +++ b/packages/clerk-js/src/utils/captcha/turnstile.ts @@ -1,8 +1,8 @@ import { waitForElement } from '@clerk/shared/dom'; +import { CAPTCHA_ELEMENT_ID, CAPTCHA_INVISIBLE_CLASSNAME } from '@clerk/shared/internal/clerk-js/constants'; import { loadScript } from '@clerk/shared/loadScript'; import type { CaptchaAppearanceOptions, CaptchaWidgetType } from '@clerk/shared/types'; -import { CAPTCHA_ELEMENT_ID, CAPTCHA_INVISIBLE_CLASSNAME } from './constants'; import type { CaptchaOptions } from './types'; // We use the explicit render mode to be able to control when the widget is rendered. diff --git a/packages/clerk-js/src/utils/index.ts b/packages/clerk-js/src/utils/index.ts index 37bd8d49ff6..032c505c1d6 100644 --- a/packages/clerk-js/src/utils/index.ts +++ b/packages/clerk-js/src/utils/index.ts @@ -1,30 +1,23 @@ -export * from './appearance'; export * from './beforeUnloadTracker'; export * from './billing'; -export * from './completeSignUpFlow'; -export * from './componentGuards'; -export * from './dynamicParamParser'; -export * from './email'; -export * from './encoders'; +export * from '@clerk/shared/internal/clerk-js/completeSignUpFlow'; +export * from '@clerk/shared/internal/clerk-js/email'; +export * from '@clerk/shared/internal/clerk-js/encoders'; export * from './errors'; export * from './errorThrower'; export * from './filterUndefinedValues'; -export * from './getClerkQueryParam'; -export * from './hex'; +export * from '@clerk/shared/internal/clerk-js/queryParams'; export * from './ignoreEventValue'; -export * from './image'; export * from './instance'; export * from './jwt'; export * from './locale'; -export * from './normalizeRoutingOptions'; -export * from './organization'; +export * from '@clerk/shared/internal/clerk-js/organization'; export * from './pageLifecycle'; -export * from './path'; -export * from './props'; -export * from './queryStateParams'; -export * from './querystring'; -export * from './runtime'; +export * from '@clerk/shared/internal/clerk-js/path'; +export * from '@clerk/shared/internal/clerk-js/queryStateParams'; +export * from '@clerk/shared/internal/clerk-js/querystring'; +export * from '@clerk/shared/internal/clerk-js/runtime'; export * from './tokenId'; -export * from './url'; -export * from './web3'; -export * from './windowNavigate'; +export * from '@clerk/shared/internal/clerk-js/url'; +export * from '@clerk/shared/internal/clerk-js/web3'; +export * from '@clerk/shared/internal/clerk-js/windowNavigate'; diff --git a/packages/clerk-js/src/utils/instance.ts b/packages/clerk-js/src/utils/instance.ts index 404a81608ec..97b869e7675 100644 --- a/packages/clerk-js/src/utils/instance.ts +++ b/packages/clerk-js/src/utils/instance.ts @@ -1,4 +1,4 @@ -import { isDevOrStagingUrl } from './url'; +import { isDevOrStagingUrl } from '@clerk/shared/internal/clerk-js/url'; const FRONTEND_API_DEV_OR_STG_REGEX = /^clerk\.([\w|-]+\.){2,4}(dev|com)$/i; diff --git a/packages/clerk-js/src/utils/jwt.ts b/packages/clerk-js/src/utils/jwt.ts index 9bac22a6274..e921ddb8912 100644 --- a/packages/clerk-js/src/utils/jwt.ts +++ b/packages/clerk-js/src/utils/jwt.ts @@ -1,7 +1,6 @@ +import { urlDecodeB64 } from '@clerk/shared/internal/clerk-js/encoders'; import type { JWT, JwtPayload } from '@clerk/shared/types'; -import { urlDecodeB64 } from './encoders'; - export function decode(token: string): JWT { const parts = (token || '').split('.'); const [header, payload, signature] = parts; diff --git a/packages/clerk-js/tsconfig.json b/packages/clerk-js/tsconfig.json index 96bcc440b5b..896a6e8665a 100644 --- a/packages/clerk-js/tsconfig.json +++ b/packages/clerk-js/tsconfig.json @@ -24,5 +24,5 @@ "@/*": ["./src/*"] } }, - "include": ["src", "vitest.config.mts", "vitest.setup.mts"] + "include": ["src", "vitest.config.mts", "vitest.setup.mts", "../shared/internal/clerk-js/componentGuards.ts"] } diff --git a/packages/nextjs/src/app-router/client/ClerkProvider.tsx b/packages/nextjs/src/app-router/client/ClerkProvider.tsx index 99251ca98dd..2d42b9320ce 100644 --- a/packages/nextjs/src/app-router/client/ClerkProvider.tsx +++ b/packages/nextjs/src/app-router/client/ClerkProvider.tsx @@ -10,7 +10,7 @@ import React, { useEffect, useTransition } from 'react'; import { useSafeLayoutEffect } from '../../client-boundary/hooks/useSafeLayoutEffect'; import { ClerkNextOptionsProvider, useClerkNextOptions } from '../../client-boundary/NextOptionsContext'; import type { NextClerkProviderProps } from '../../types'; -import { ClerkJSScript } from '../../utils/clerk-js-script'; +import { ClerkScripts } from '../../utils/clerk-script'; import { canUseKeyless } from '../../utils/feature-flags'; import { mergeNextClerkPropsWithEnv } from '../../utils/mergeNextClerkPropsWithEnv'; import { RouterTelemetry } from '../../utils/router-telemetry'; @@ -130,7 +130,7 @@ const NextClientClerkProvider = (props: NextClerkProviderProps) => { - + {children} diff --git a/packages/nextjs/src/pages/ClerkProvider.tsx b/packages/nextjs/src/pages/ClerkProvider.tsx index 58acfc72836..cedbda19ff9 100644 --- a/packages/nextjs/src/pages/ClerkProvider.tsx +++ b/packages/nextjs/src/pages/ClerkProvider.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { useSafeLayoutEffect } from '../client-boundary/hooks/useSafeLayoutEffect'; import { ClerkNextOptionsProvider } from '../client-boundary/NextOptionsContext'; import type { NextClerkProviderProps } from '../types'; -import { ClerkJSScript } from '../utils/clerk-js-script'; +import { ClerkScripts } from '../utils/clerk-script'; import { invalidateNextRouterCache } from '../utils/invalidateNextRouterCache'; import { mergeNextClerkPropsWithEnv } from '../utils/mergeNextClerkPropsWithEnv'; import { removeBasePath } from '../utils/removeBasePath'; @@ -55,7 +55,7 @@ export function ClerkProvider({ children, ...props }: NextClerkProviderProps): J initialState={initialState} > - + {children} diff --git a/packages/nextjs/src/server/constants.ts b/packages/nextjs/src/server/constants.ts index 73c4371486f..4165b2ae4d1 100644 --- a/packages/nextjs/src/server/constants.ts +++ b/packages/nextjs/src/server/constants.ts @@ -3,6 +3,7 @@ import { isTruthy } from '@clerk/shared/underscore'; export const CLERK_JS_VERSION = process.env.NEXT_PUBLIC_CLERK_JS_VERSION || ''; export const CLERK_JS_URL = process.env.NEXT_PUBLIC_CLERK_JS_URL || ''; +export const CLERK_UI_URL = process.env.NEXT_PUBLIC_CLERK_UI_URL || ''; export const API_VERSION = process.env.CLERK_API_VERSION || 'v1'; export const SECRET_KEY = process.env.CLERK_SECRET_KEY || ''; export const MACHINE_SECRET_KEY = process.env.CLERK_MACHINE_SECRET_KEY || ''; diff --git a/packages/nextjs/src/utils/clerk-js-script.tsx b/packages/nextjs/src/utils/clerk-js-script.tsx deleted file mode 100644 index f91bddb57de..00000000000 --- a/packages/nextjs/src/utils/clerk-js-script.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { useClerk } from '@clerk/react'; -import { buildClerkJsScriptAttributes, clerkJsScriptUrl } from '@clerk/react/internal'; -import NextScript from 'next/script'; -import React from 'react'; - -import { useClerkNextOptions } from '../client-boundary/NextOptionsContext'; - -type ClerkJSScriptProps = { - router: 'app' | 'pages'; -}; - -function ClerkJSScript(props: ClerkJSScriptProps) { - const { publishableKey, clerkJSUrl, clerkJSVersion, clerkJSVariant, nonce } = useClerkNextOptions(); - const { domain, proxyUrl } = useClerk(); - - /** - * If no publishable key, avoid appending an invalid script in the DOM. - */ - if (!publishableKey) { - return null; - } - - const options = { - domain, - proxyUrl, - publishableKey, - clerkJSUrl, - clerkJSVersion, - clerkJSVariant, - nonce, - }; - const scriptUrl = clerkJsScriptUrl(options); - - /** - * Notes: - * `next/script` in 13.x.x when used with App Router will fail to pass any of our `data-*` attributes, resulting in errors - * Nextjs App Router will automatically move inline scripts inside `` - * Using the `nextjs/script` for App Router with the `beforeInteractive` strategy will throw an error because our custom script will be mounted outside the `html` tag. - */ - const Script = props.router === 'app' ? 'script' : NextScript; - - return ( -