Skip to content

feat(clerk-js): Introduce granular clerk loading status #5320

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 41 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
7faf5d2
feat(clerk-js): allow initialization with defaults
jacekradko Mar 5, 2025
ba6f072
handle partial attributes
jacekradko Mar 5, 2025
59c1a01
wip
jacekradko Mar 5, 2025
03628a4
expect TS errors for now
jacekradko Mar 5, 2025
650782d
feat: clerk loading status
jacekradko Mar 7, 2025
35ad04f
Merge branch 'main' into feat/clerk-loading-status
jacekradko Mar 10, 2025
4367db0
update load()
jacekradko Mar 10, 2025
759b1ea
Merge branch 'main' into feat/initialize-clerk-js-with-defaults
jacekradko Mar 10, 2025
82331ce
Merge branch 'feat/initialize-clerk-js-with-defaults' into feat/clerk…
jacekradko Mar 10, 2025
b27298e
wip
jacekradko Mar 10, 2025
959fb0e
wip
jacekradko Mar 10, 2025
d156038
wip
jacekradko Mar 10, 2025
4efe1a5
isomorphic clerk status
jacekradko Mar 11, 2025
68b7161
wip
jacekradko Mar 11, 2025
1c19214
add simple event emitter
jacekradko Mar 11, 2025
b186a10
add once() to eventemitter
jacekradko Mar 11, 2025
1e6e07d
use once
jacekradko Mar 11, 2025
2f69bcb
wip
jacekradko Mar 11, 2025
a5e904e
Merge branch 'main' into feat/clerk-loading-status
jacekradko Mar 13, 2025
0a20961
Merge branch 'main' into feat/clerk-loading-status
jacekradko Mar 18, 2025
c09241e
changeset
jacekradko Mar 18, 2025
cc926d8
fix build
jacekradko Mar 18, 2025
d329df8
wip
jacekradko Mar 18, 2025
bf8ec01
format
jacekradko Mar 18, 2025
39f1d5c
fix test
jacekradko Mar 18, 2025
16c1f48
Merge branch 'main' into feat/clerk-loading-status
jacekradko Mar 19, 2025
d58c2da
wip
jacekradko Mar 19, 2025
9233a13
move EventEmitter to shared
jacekradko Mar 19, 2025
ae1715a
Merge branch 'main' into feat/clerk-loading-status
jacekradko Mar 20, 2025
7ec756f
wip
jacekradko Mar 20, 2025
3c6ca76
remove dupe
jacekradko Mar 20, 2025
3761761
wip
jacekradko Mar 20, 2025
adc0125
add back import
jacekradko Mar 21, 2025
72a09b3
no vitest in shared :(
jacekradko Mar 21, 2025
6d64fdf
Merge branch 'main' into feat/clerk-loading-status
jacekradko Mar 24, 2025
474c2ef
switch uninitialized to loading
jacekradko Mar 24, 2025
85e5926
Merge branch 'main' into feat/clerk-loading-status
jacekradko Mar 26, 2025
5841d4a
Merge branch 'main' into feat/clerk-loading-status
jacekradko Mar 26, 2025
c268737
clean up status setting
jacekradko Mar 26, 2025
76d0d9b
wip
jacekradko Mar 26, 2025
3edd50f
wip
jacekradko Mar 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/twenty-eggs-post.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@clerk/clerk-js': minor
'@clerk/shared': minor
'@clerk/clerk-react': minor
'@clerk/types': minor
---

Introducing granular Clerk loading status
4 changes: 2 additions & 2 deletions packages/clerk-js/bundlewatch.config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"files": [
{ "path": "./dist/clerk.js", "maxSize": "581.5kB" },
{ "path": "./dist/clerk.browser.js", "maxSize": "79.30kB" },
{ "path": "./dist/clerk.js", "maxSize": "582kB" },
{ "path": "./dist/clerk.browser.js", "maxSize": "80kB" },
{ "path": "./dist/clerk.headless.js", "maxSize": "55KB" },
{ "path": "./dist/ui-common*.js", "maxSize": "96KB" },
{ "path": "./dist/vendors*.js", "maxSize": "30KB" },
Expand Down
81 changes: 54 additions & 27 deletions packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { inBrowser as inClientSide, isValidBrowserOnline } from '@clerk/shared/browser';
import { deprecated } from '@clerk/shared/deprecated';
import { ClerkRuntimeError, EmailLinkErrorCodeStatus, is4xxError, isClerkAPIResponseError } from '@clerk/shared/error';
import { EventEmitter } from '@clerk/shared/event-emitter';
import { parsePublishableKey } from '@clerk/shared/keys';
import { LocalStorageBroadcastChannel } from '@clerk/shared/localStorageBroadcastChannel';
import { logger } from '@clerk/shared/logger';
Expand Down Expand Up @@ -187,6 +188,7 @@ export class Clerk implements ClerkInterface {
protected internal_last_error: ClerkAPIError | null = null;
// converted to protected environment to support `updateEnvironment` type assertion
protected environment?: EnvironmentResource | null;
private eventEmitter = new EventEmitter();

#publishableKey = '';
#domain: DomainOrProxyUrl['domain'];
Expand All @@ -198,7 +200,7 @@ export class Clerk implements ClerkInterface {
//@ts-expect-error with being undefined even though it's not possible - related to issue with ts and error thrower
#fapiClient: FapiClient;
#instanceType?: InstanceType;
#loaded = false;
#status: ClerkInterface['status'] = 'uninitialized';

#listeners: Array<(emission: Resources) => void> = [];
#navigationListeners: Array<() => void> = [];
Expand Down Expand Up @@ -246,7 +248,22 @@ export class Clerk implements ClerkInterface {
}

get loaded(): boolean {
return this.#loaded;
return this.status === 'ready';
}

get status() {
return this.#status;
}

set status(status: ClerkInterface['status']) {
// console.log(`status: ${this.#status} -> ${status}`);
if (this.#status === 'ready') {
throw new Error('Clerk status cannot be changed once the instance has been loaded.');
}
if (status === 'ready') {
this.eventEmitter.emit('ready');
}
this.#status = status;
}

get isSatellite(): boolean {
Expand Down Expand Up @@ -355,41 +372,51 @@ export class Clerk implements ClerkInterface {

public getFapiClient = (): FapiClient => this.#fapiClient;

public load = async (options?: ClerkOptions): Promise<void> => {
if (this.loaded) {
public async load(options?: ClerkOptions): Promise<void> {
if (this.status === 'loading') {
logger.warnOnce('Clerk is already loading. Ignoring duplicate call.');
return;
}

// Log a development mode warning once
if (this.#instanceType === 'development') {
logger.warnOnce(
'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',
);
if (this.status === 'ready') {
logger.warnOnce('Clerk is already loaded. Skipping load process.');
return;
}

this.#options = this.#initOptions(options);
this.status = 'loading';

assertNoLegacyProp(this.#options);
try {
if (this.#instanceType === 'development') {
logger.warnOnce('Clerk is running in development mode. Usage limits apply. Do not use in production.');
}

if (this.#options.sdkMetadata) {
Clerk.sdkMetadata = this.#options.sdkMetadata;
}
this.#options = this.#initOptions(options);
assertNoLegacyProp(this.#options);

if (this.#options.telemetry !== false) {
this.telemetry = new TelemetryCollector({
clerkVersion: Clerk.version,
samplingRate: 1,
publishableKey: this.publishableKey,
...this.#options.telemetry,
});
}
if (this.#options.sdkMetadata) {
Clerk.sdkMetadata = this.#options.sdkMetadata;
}

if (this.#options.telemetry !== false) {
this.telemetry = new TelemetryCollector({
clerkVersion: Clerk.version,
samplingRate: 1,
publishableKey: this.publishableKey,
...this.#options.telemetry,
});
}

if (this.#options.standardBrowser) {
this.#loaded = await this.#loadInStandardBrowser();
} else {
this.#loaded = await this.#loadInNonStandardBrowser();
const loaded = this.#options.standardBrowser
? await this.#loadInStandardBrowser()
: await this.#loadInNonStandardBrowser();

if (loaded === false) throw new Error('Clerk failed to load');

this.status = 'ready';
} catch {
this.status = 'error';
}
};
}

#isCombinedSignInOrUpFlow(): boolean {
return Boolean(!this.#options.signUpUrl && this.#options.signInUrl && !isAbsoluteUrl(this.#options.signInUrl));
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/contexts/ClerkContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function ClerkContextProvider(props: ClerkContextProvider) {

React.useEffect(() => {
return clerk.addListener(e => setState({ ...e }));
}, []);
}, [clerk]);

const derivedState = deriveState(clerkLoaded, state, initialState);
const clerkCtx = React.useMemo(() => ({ value: clerk }), [clerkLoaded]);
Expand Down
Loading