Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
45 changes: 45 additions & 0 deletions .changeset/align-experimental-unstable-prefixes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
"@clerk/clerk-js": major
"@clerk/react": major
"@clerk/nextjs": major
"@clerk/vue": major
"@clerk/astro": major
"@clerk/expo": major
"@clerk/chrome-extension": major
"@clerk/shared": major
"@clerk/ui": major
---

Align experimental/unstable prefixes to use consistent naming:

- Renamed all `__unstable_*` methods to `__internal_*` (for internal APIs)
- Renamed all `experimental__*` and `experimental_*` methods to `__experimental_*` (for beta features)
- Removed deprecated billing-related props and `experimental__forceOauthFirst`
- Moved `createTheme` and `simple` to `@clerk/ui/themes/experimental` export path (removed `__experimental_` prefix since they're now in the experimental export)

**Breaking Changes:**

### @clerk/clerk-js
- `__unstable__environment` → `__internal_environment`
- `__unstable__updateProps` → `__internal_updateProps`
- `__unstable__setEnvironment` → `__internal_setEnvironment`
- `__unstable__onBeforeRequest` → `__internal_onBeforeRequest`
- `__unstable__onAfterResponse` → `__internal_onAfterResponse`
- `__unstable__onBeforeSetActive` → `__internal_onBeforeSetActive` (window global)
- `__unstable__onAfterSetActive` → `__internal_onAfterSetActive` (window global)

### @clerk/nextjs
- `__unstable_invokeMiddlewareOnAuthStateChange` → `__internal_invokeMiddlewareOnAuthStateChange`

### @clerk/ui
- `experimental_createTheme` / `__experimental_createTheme` → `createTheme` (now exported from `@clerk/ui/themes/experimental`)
- `experimental__simple` / `__experimental_simple` → `simple` (now exported from `@clerk/ui/themes/experimental`)
Comment on lines +35 to +36
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving to the preferred /experimental export pattern


### @clerk/chrome-extension
- `__unstable__createClerkClient` → `createClerkClient` (exported from `@clerk/chrome-extension/background`)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this should remain experimental/internal 🤔 @tmilewski do you have context?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brkalow Yeah, this can be moved to stable at this point.


### Removed (multiple packages)
- `__unstable_manageBillingUrl` (removed)
- `__unstable_manageBillingLabel` (removed)
- `__unstable_manageBillingMembersLimit` (removed)
Comment on lines +42 to +44
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can tell, these are unused and were to support Dashboard's use of our orgs components a few years ago

- `experimental__forceOauthFirst` (removed)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Underlying properties on the API response object were removed in core 2, so finishing the cleanup here

4 changes: 4 additions & 0 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ Read more about this in the [`clerk-docs` CONTRIBUTING.md](https://github.com/cl

Then, to preview how the `<Typedoc />` component renders, the `clerk-docs` PR will have a Vercel preview. Or to get local previews set up, see the [section in `clerk/clerk` about setting up local docs](https://github.com/clerk/clerk?tab=readme-ov-file#5-optional-set-up-local-docs).

### Experimental and internal APIs

In some cases, we might need to add new methods to our publicly exposed APIs that are meant for internal use, or as experimental releases before the APIs are stabilized. For internal methods or properties, use the `__internal_` prefix. For experimental methods or properties that are attached to existing APIs, use the `__experimental_` prefix. For new exports, it is also acceptable to export from an `/experimental` subpath. Exports from `/experimental` are not covered by regular SemVer guarantees.
Comment on lines +198 to +200
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documenting new patterns


## Opening a Pull Request

1. Search our repository for open or closed [Pull Requests](https://github.com/clerk/javascript/pulls) that relate to your submission. You don't want to duplicate effort.
Expand Down
63 changes: 63 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,61 @@ const noNavigateUseClerk = {
},
};

const noUnstableMethods = {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New ESLint rule that should hopefully avoid any accidental addition of __unstable_ prefixes

meta: {
type: 'problem',
docs: {
description: 'Disallow methods or properties starting with `__unstable_`',
recommended: false,
},
messages: {
noUnstable:
'Do not define methods or properties starting with `__unstable_`. For internal APIs, use `__internal_`, for experimental APIs, use `__experimental_`.',
},
schema: [],
},
create(context) {
return {
MemberExpression(node) {
if (
node.property.type === 'Identifier' &&
typeof node.property.name === 'string' &&
node.property.name.startsWith('__unstable_')
) {
context.report({
node: node.property,
messageId: 'noUnstable',
});
}
},
Property(node) {
if (
node.key.type === 'Identifier' &&
typeof node.key.name === 'string' &&
node.key.name.startsWith('__unstable_')
) {
context.report({
node: node.key,
messageId: 'noUnstable',
});
}
},
MethodDefinition(node) {
if (
node.key.type === 'Identifier' &&
typeof node.key.name === 'string' &&
node.key.name.startsWith('__unstable_')
) {
context.report({
node: node.key,
messageId: 'noUnstable',
});
}
},
};
},
};

export default tseslint.config([
{
name: 'repo/ignores',
Expand Down Expand Up @@ -161,6 +216,11 @@ export default tseslint.config([
{
name: 'repo/global',
plugins: {
'custom-rules': {
rules: {
'no-unstable-methods': noUnstableMethods,
},
},
'simple-import-sort': pluginSimpleImportSort,
'unused-imports': pluginUnusedImports,
turbo: pluginTurbo,
Expand All @@ -176,6 +236,7 @@ export default tseslint.config([
},
},
rules: {
'custom-rules/no-unstable-methods': 'error',
'no-label-var': 'error',
'no-undef-init': 'warn',
'no-restricted-imports': [
Expand Down Expand Up @@ -359,11 +420,13 @@ export default tseslint.config([
'custom-rules': {
rules: {
'no-navigate-useClerk': noNavigateUseClerk,
'no-unstable-methods': noUnstableMethods,
},
},
},
rules: {
'custom-rules/no-navigate-useClerk': 'error',
'custom-rules/no-unstable-methods': 'error',
},
},
{
Expand Down
2 changes: 1 addition & 1 deletion integration/tests/update-props.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ testAgainstRunningApps({ withPattern: ['react.vite.withEmailCodes'] })('sign in
await u.page.waitForFunction(async () => {
// Emulate ClerkProvider being unmounted and mounted again
// as updateProps is going to be called without the default options set by window.Clerk.load()
await (window.Clerk as any).__unstable__updateProps({ options: {} });
await (window.Clerk as any).__internal_updateProps({ options: {} });
});
await u.po.signIn.setIdentifier(fakeUser.email);
await u.po.signIn.continue();
Expand Down
8 changes: 4 additions & 4 deletions packages/astro/src/internal/create-clerk-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ async function createClerkInstanceInternal<TUi extends Ui = Ui>(options?: AstroC
clerkUiCtor = options?.clerkUiCtor
? Promise.resolve(options.clerkUiCtor)
: loadClerkUiScript(options).then(() => {
if (!window.__unstable_ClerkUiCtor) {
if (!window.__internal_ClerkUiCtor) {
throw new Error('Failed to download latest Clerk UI. Contact support@clerk.com.');
}
// After the check, TypeScript knows it's defined
return window.__unstable_ClerkUiCtor;
return window.__internal_ClerkUiCtor;
});

await clerkPromise;
Expand Down Expand Up @@ -106,8 +106,8 @@ function updateClerkOptions<TUi extends Ui = Ui>(options: AstroClerkUpdateOption
options: { ...initOptions, ...options },
appearance: { ...initOptions?.appearance, ...options.appearance },
} as unknown as { options: ClerkOptions; appearance?: any };
// `__unstable__updateProps` is not exposed as public API from `@clerk/types`
void (clerk as any).__unstable__updateProps(updateOptions);
// `__internal_updateProps` is not exposed as public API from `@clerk/types`
void (clerk as any).__internal_updateProps(updateOptions);
}

export { createClerkInstance, updateClerkOptions };
14 changes: 7 additions & 7 deletions packages/astro/src/react/uiComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export const SignIn = withClerk(({ clerk, ...props }: WithClerkProp<SignInProps>
<Portal
mount={clerk?.mountSignIn}
unmount={clerk?.unmountSignIn}
updateProps={(clerk as any)?.__unstable__updateProps}
updateProps={(clerk as any)?.__internal_updateProps}
props={props}
/>
);
Expand All @@ -106,7 +106,7 @@ export const SignUp = withClerk(({ clerk, ...props }: WithClerkProp<SignUpProps>
<Portal
mount={clerk?.mountSignUp}
unmount={clerk?.unmountSignUp}
updateProps={(clerk as any)?.__unstable__updateProps}
updateProps={(clerk as any)?.__internal_updateProps}
props={props}
/>
);
Expand All @@ -117,7 +117,7 @@ export const UserButton = withClerk(({ clerk, ...props }: WithClerkProp<UserButt
<Portal
mount={clerk?.mountUserButton}
unmount={clerk?.unmountUserButton}
updateProps={(clerk as any)?.__unstable__updateProps}
updateProps={(clerk as any)?.__internal_updateProps}
props={props}
/>
);
Expand All @@ -128,7 +128,7 @@ export const UserProfile = withClerk(({ clerk, ...props }: WithClerkProp<UserPro
<Portal
mount={clerk?.mountUserProfile}
unmount={clerk?.unmountUserProfile}
updateProps={(clerk as any)?.__unstable__updateProps}
updateProps={(clerk as any)?.__internal_updateProps}
props={props}
/>
);
Expand All @@ -139,7 +139,7 @@ export const OrganizationProfile = withClerk(({ clerk, ...props }: WithClerkProp
<Portal
mount={clerk?.mountOrganizationProfile}
unmount={clerk?.unmountOrganizationProfile}
updateProps={(clerk as any)?.__unstable__updateProps}
updateProps={(clerk as any)?.__internal_updateProps}
props={props}
/>
);
Expand All @@ -150,7 +150,7 @@ export const OrganizationSwitcher = withClerk(({ clerk, ...props }: WithClerkPro
<Portal
mount={clerk?.mountOrganizationSwitcher}
unmount={clerk?.unmountOrganizationSwitcher}
updateProps={(clerk as any)?.__unstable__updateProps}
updateProps={(clerk as any)?.__internal_updateProps}
props={props}
/>
);
Expand All @@ -161,7 +161,7 @@ export const OrganizationList = withClerk(({ clerk, ...props }: WithClerkProp<Or
<Portal
mount={clerk?.mountOrganizationList}
unmount={clerk?.unmountOrganizationList}
updateProps={(clerk as any)?.__unstable__updateProps}
updateProps={(clerk as any)?.__internal_updateProps}
props={props}
/>
);
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ declare global {
__astro_clerk_component_props: Map<string, Map<string, Record<string, unknown>>>;
__astro_clerk_function_props: Map<string, Map<string, Record<string, unknown>>>;
Clerk: BrowserClerk;
__unstable_ClerkUiCtor?: ClerkUiConstructor;
__internal_ClerkUiCtor?: ClerkUiConstructor;
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/chrome-extension/src/internal/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ export function createClerkClient({
listener?.add();
}

clerk.__unstable__onAfterResponse(responseHandler(jwt, { isProd }));
clerk.__unstable__onBeforeRequest(requestHandler(jwt, { isProd }));
clerk.__internal_onAfterResponse(responseHandler(jwt, { isProd }));
clerk.__internal_onBeforeRequest(requestHandler(jwt, { isProd }));

return clerk;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Clerk } from '@clerk/clerk-js';
import { AUTH_HEADER } from '../constants';
import type { JWTHandler } from './jwt-handler';

type Handler = Parameters<Clerk['__unstable__onBeforeRequest']>[0];
type Handler = Parameters<Clerk['__internal_onBeforeRequest']>[0];
type Req = Parameters<Handler>[0];

/** Append the JWT to the FAPI request */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Clerk } from '@clerk/clerk-js';
import { AUTH_HEADER } from '../constants';
import type { JWTHandler } from './jwt-handler';

type Handler = Parameters<Clerk['__unstable__onAfterResponse']>[0];
type Handler = Parameters<Clerk['__internal_onAfterResponse']>[0];
type Res = Parameters<Handler>[1];

/** Retrieve the JWT to the FAPI response */
Expand Down
6 changes: 3 additions & 3 deletions packages/clerk-js/sandbox/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ function appearanceVariableOptions() {
});

const updateVariables = () => {
void Clerk.__unstable__updateProps({
void Clerk.__internal_updateProps({
appearance: {
// Preserve existing appearance properties like baseTheme
...Clerk.__internal_getOption('appearance'),
Expand Down Expand Up @@ -239,7 +239,7 @@ function otherOptions() {
});

const updateOtherOptions = () => {
void Clerk.__unstable__updateProps({
void Clerk.__internal_updateProps({
options: Object.fromEntries(
Object.entries(otherOptionsInputs).map(([key, input]) => {
sessionStorage.setItem(key, input.value);
Expand Down Expand Up @@ -314,7 +314,7 @@ void (async () => {
Clerk.mountWaitlist(app, componentControls.waitlist.getProps() ?? {});
},
'/keyless': () => {
void Clerk.__unstable__updateProps({
void Clerk.__internal_updateProps({
options: {
__internal_keyless_claimKeylessApplicationUrl: 'https://dashboard.clerk.com',
__internal_keyless_copyInstanceKeysUrl: 'https://dashboard.clerk.com',
Expand Down
Loading
Loading