Skip to content

Commit 21725f4

Browse files
authored
Merge branch 'main' into patch-2
2 parents 99a00b4 + 6f4cf71 commit 21725f4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1215
-284
lines changed

.changeset/cool-showers-admire.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
'@clerk/nextjs': minor
3+
'@clerk/clerk-react': minor
4+
---
5+
6+
Introduce `useClerk().status` alongside `<ClerkFailed />` and `<ClerkDegraded />`.
7+
8+
### `useClerk().status`
9+
Possible values for `useClerk().status` are:
10+
- `"loading"`: Set during initialization
11+
- `"error"`: Set when hotloading clerk-js failed or `Clerk.load()` failed
12+
- `"ready"`: Set when Clerk is fully operational
13+
- `"degraded"`: Set when Clerk is partially operational
14+
The computed value of `useClerk().loaded` is:
15+
16+
- `true` when `useClerk().status` is either `"ready"` or `"degraded"`.
17+
- `false` when `useClerk().status` is `"loading"` or `"error"`.
18+
19+
### `<ClerkFailed />`
20+
```tsx
21+
<ClerkLoaded>
22+
<MyCustomSignInForm/>
23+
</ClerkLoaded>
24+
<ClerkFailed>
25+
<ContactSupportBanner/>
26+
</ClerkFailed>
27+
```
28+
29+
### `<ClerkDegraded />`
30+
```tsx
31+
<ClerkLoaded>
32+
<MyCustomPasskeyRegistration/>
33+
<ClerkDegraded>
34+
We are experiencing issues, registering a passkey might fail.
35+
</ClerkDegraded>
36+
</ClerkLoaded>
37+
```

.changeset/eight-friends-fail.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
---
4+
5+
Fix duplicate checkout calls when clicking Get Started buttons

.changeset/fuzzy-pears-win.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
---
4+
5+
Remove the experimental commerce flag

.changeset/mean-impalas-reply.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
---
4+
5+
Rollback change to lazy-loading suspense wrapper

.changeset/olive-onions-do.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@clerk/clerk-js': minor
3+
'@clerk/types': minor
4+
---
5+
6+
Introduce `Clerk.status` for tracking the state of the clerk singleton.
7+
Possible values for `Clerk.status` are:
8+
- `"loading"`: Set during initialization
9+
- `"error"`: Set when hotloading clerk-js failed or `Clerk.load()` failed
10+
- `"ready"`: Set when Clerk is fully operational
11+
- `"degraded"`: Set when Clerk is partially operational
12+
13+
The computed value of `Clerk.loaded` is:
14+
- `true` when `Clerk.status` is either `"ready"` or `"degraded"`.
15+
- `false` when `Clerk.status` is `"loading"` or `"error"`.

.changeset/social-pandas-return.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': minor
3+
---
4+
5+
Introduce `clerk.legacy.browser.js` for legacy browser support.

.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ exports[`Typedoc output > should have a deliberate file structure 1`] = `
1515
"types/clerk-pagination-params.mdx",
1616
"types/clerk-pagination-request.mdx",
1717
"types/clerk-resource.mdx",
18+
"types/clerk-status.mdx",
1819
"types/clerk.mdx",
1920
"types/create-organization-params.mdx",
2021
"types/element-object-key.mdx",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
export function Conditionals({
2+
hasImpersonationRead,
3+
hasMagicLinksCreate,
4+
hasMagicLinksRead,
5+
hasImpersonationManage,
6+
hasAdminRole,
7+
hasManagerRole,
8+
hasImpersonationReaderRole,
9+
role,
10+
hasImpersonationFeature,
11+
hasMagicLinksFeature,
12+
}: {
13+
hasImpersonationRead: boolean;
14+
hasMagicLinksCreate: boolean;
15+
hasMagicLinksRead: boolean;
16+
hasImpersonationManage: boolean;
17+
hasAdminRole: boolean;
18+
hasManagerRole: boolean;
19+
hasImpersonationReaderRole: boolean;
20+
role: string | null | undefined;
21+
hasImpersonationFeature: boolean;
22+
hasMagicLinksFeature: boolean;
23+
}) {
24+
return (
25+
<>
26+
<pre>
27+
{`has({ permission: "org:impersonation:read" }) -> `}
28+
{hasImpersonationRead ? 'true' : 'false'}
29+
</pre>
30+
31+
<pre>
32+
{`has({ permission: "org:magic_links:create" }) -> `}
33+
{hasMagicLinksCreate ? 'true' : 'false'}
34+
</pre>
35+
36+
<pre>
37+
{`has({ permission: "org:magic_links:read" }) -> `}
38+
{hasMagicLinksRead ? 'true' : 'false'}
39+
</pre>
40+
41+
<pre>
42+
{`has({ permission: "org:impersonation:manage" }) -> `}
43+
{hasImpersonationManage ? 'true' : 'false'}
44+
</pre>
45+
46+
<pre>
47+
{`has({ role: "org:admin" }) -> `}
48+
{hasAdminRole ? 'true' : 'false'}
49+
</pre>
50+
51+
<pre>
52+
{`has({ role: "org:manager" }) -> `}
53+
{hasManagerRole ? 'true' : 'false'}
54+
</pre>
55+
56+
<pre>
57+
{`has({ role: "org:impersonation_reader" }) -> `}
58+
{hasImpersonationReaderRole ? 'true' : 'false'}
59+
</pre>
60+
61+
<pre>
62+
{`role -> `}
63+
{role}
64+
</pre>
65+
66+
<pre>
67+
{`has({ feature: "org:impersonation" }) -> `}
68+
{hasImpersonationFeature ? 'true' : 'false'}
69+
</pre>
70+
71+
<pre>
72+
{`has({ feature: "org:magic_links" }) -> `}
73+
{hasMagicLinksFeature ? 'true' : 'false'}
74+
</pre>
75+
</>
76+
);
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use client';
2+
import { useAuth } from '@clerk/nextjs';
3+
import { Conditionals } from '../conditionals';
4+
5+
export default function Page() {
6+
const { has, orgRole } = useAuth();
7+
8+
if (!has) {
9+
return <div>Loading...</div>;
10+
}
11+
12+
return (
13+
<>
14+
<h1>Has Client</h1>
15+
<Conditionals
16+
hasImpersonationRead={has({ permission: 'org:impersonation:read' })}
17+
hasMagicLinksCreate={has({ permission: 'org:magic_links:create' })}
18+
hasMagicLinksRead={has({ permission: 'org:magic_links:read' })}
19+
hasImpersonationManage={has({ permission: 'org:impersonation:manage' })}
20+
hasAdminRole={has({ role: 'org:admin' })}
21+
hasManagerRole={has({ role: 'org:manager' })}
22+
hasImpersonationReaderRole={has({ role: 'org:impersonation_reader' })}
23+
role={orgRole}
24+
hasImpersonationFeature={has({ feature: 'org:impersonation' })}
25+
hasMagicLinksFeature={has({ feature: 'org:magic_links' })}
26+
/>
27+
</>
28+
);
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { auth } from '@clerk/nextjs/server';
2+
import { Conditionals } from '../conditionals';
3+
4+
export default async function Page() {
5+
const { has, orgRole } = await auth();
6+
7+
return (
8+
<>
9+
<h1>Has Server</h1>
10+
<Conditionals
11+
hasImpersonationRead={has({ permission: 'org:impersonation:read' })}
12+
hasMagicLinksCreate={has({ permission: 'org:magic_links:create' })}
13+
hasMagicLinksRead={has({ permission: 'org:magic_links:read' })}
14+
hasImpersonationManage={has({ permission: 'org:impersonation:manage' })}
15+
hasAdminRole={has({ role: 'org:admin' })}
16+
hasManagerRole={has({ role: 'org:manager' })}
17+
hasImpersonationReaderRole={has({ role: 'org:impersonation_reader' })}
18+
role={orgRole}
19+
hasImpersonationFeature={has({ feature: 'org:impersonation' })}
20+
hasMagicLinksFeature={has({ feature: 'org:magic_links' })}
21+
/>
22+
</>
23+
);
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use client';
2+
import { useAuth } from '@clerk/nextjs';
3+
import { Conditionals } from '../conditionals';
4+
5+
export function SSR() {
6+
const { has, orgRole } = useAuth();
7+
8+
if (!has) {
9+
return <div>Loading...</div>;
10+
}
11+
12+
return (
13+
<>
14+
<h1>Has SSR</h1>
15+
<Conditionals
16+
hasImpersonationRead={has({ permission: 'org:impersonation:read' })}
17+
hasMagicLinksCreate={has({ permission: 'org:magic_links:create' })}
18+
hasMagicLinksRead={has({ permission: 'org:magic_links:read' })}
19+
hasImpersonationManage={has({ permission: 'org:impersonation:manage' })}
20+
hasAdminRole={has({ role: 'org:admin' })}
21+
hasManagerRole={has({ role: 'org:manager' })}
22+
hasImpersonationReaderRole={has({ role: 'org:impersonation_reader' })}
23+
role={orgRole}
24+
hasImpersonationFeature={has({ feature: 'org:impersonation' })}
25+
hasMagicLinksFeature={has({ feature: 'org:magic_links' })}
26+
/>
27+
</>
28+
);
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { ClerkProvider } from '@clerk/nextjs';
2+
import { SSR } from './client';
3+
4+
export default function Page() {
5+
return (
6+
<ClerkProvider dynamic>
7+
<SSR />
8+
</ClerkProvider>
9+
);
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ClientJWT } from '../client-jwt';
2+
import { ServerJWT } from '../server-jwt';
3+
4+
export default function Layout({ children }: React.PropsWithChildren) {
5+
return (
6+
<div
7+
style={{
8+
display: 'flex',
9+
width: '100%',
10+
}}
11+
>
12+
<aside
13+
style={{
14+
flex: '0 0 300px',
15+
}}
16+
>
17+
<ServerJWT />
18+
<ClientJWT />
19+
</aside>
20+
<div style={{ flex: '1 1 auto' }}>{children}</div>
21+
</div>
22+
);
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Home() {
2+
return (
3+
<div>
4+
<main />
5+
</div>
6+
);
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use client';
2+
3+
import { useAuth } from '@clerk/nextjs';
4+
5+
export function ClientJWT() {
6+
const { sessionClaims } = useAuth();
7+
return (
8+
<>
9+
<h1>Client JWT</h1>
10+
<pre
11+
data-testid='client-jwt'
12+
style={{
13+
maxWidth: '32rem', // equivalent to max-w-lg
14+
textWrap: 'wrap',
15+
wordBreak: 'break-word',
16+
}}
17+
>
18+
{JSON.stringify(sessionClaims, null, 4)}
19+
</pre>
20+
</>
21+
);
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { auth } from '@clerk/nextjs/server';
2+
3+
export async function ServerJWT() {
4+
return (
5+
<>
6+
<h1>Server JWT</h1>
7+
<pre data-testid='server-jwt'>{JSON.stringify((await auth()).sessionClaims, null, 4)}</pre>
8+
</>
9+
);
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { ClerkLoaded, ClerkLoading, ClerkFailed, ClerkDegraded, useClerk } from '@clerk/clerk-react';
2+
3+
export default function ClerkStatusPage() {
4+
const { loaded, status } = useClerk();
5+
6+
return (
7+
<>
8+
<p>Status: {status}</p>
9+
<p>{status === 'loading' ? 'Clerk is loading' : null}</p>
10+
<p>{status === 'error' ? 'Clerk is out' : null}</p>
11+
<p>{status === 'degraded' ? 'Clerk is degraded' : null}</p>
12+
<p>{status === 'ready' ? 'Clerk is ready' : null}</p>
13+
<p>{status === 'ready' || status === 'degraded' ? 'Clerk is ready or degraded (loaded)' : null}</p>
14+
<p>{loaded ? 'Clerk is loaded' : null}</p>
15+
<p>{!loaded ? 'Clerk is NOT loaded' : null}</p>
16+
17+
<ClerkDegraded>
18+
<p>(comp) Clerk is degraded</p>
19+
</ClerkDegraded>
20+
21+
<ClerkLoaded>
22+
<p>(comp) Clerk is loaded,(ready or degraded)</p>
23+
</ClerkLoaded>
24+
25+
<ClerkFailed>
26+
<p>(comp) Something went wrong with Clerk, refresh your page.</p>
27+
</ClerkFailed>
28+
29+
<ClerkLoading>
30+
<p>(comp) Waiting for clerk to fail, ready or regraded.</p>
31+
</ClerkLoading>
32+
</>
33+
);
34+
}

integration/templates/react-vite/src/main.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import OrganizationList from './organization-list';
2020
import CreateOrganization from './create-organization';
2121
import OrganizationSwitcher from './organization-switcher';
2222
import Buttons from './buttons';
23+
import ClerkStatusPage from './clerk-status';
2324

2425
const Root = () => {
2526
const navigate = useNavigate();
@@ -114,6 +115,10 @@ const router = createBrowserRouter([
114115
path: '/create-organization',
115116
element: <CreateOrganization />,
116117
},
118+
{
119+
path: '/clerk-status',
120+
element: <ClerkStatusPage />,
121+
},
117122
],
118123
},
119124
]);

0 commit comments

Comments
 (0)