Skip to content

Commit 90f78cd

Browse files
authored
Merge branch 'main' into brk.feat/redirect-on-session-exists
2 parents 631e48d + 80a634d commit 90f78cd

File tree

202 files changed

+4067
-1101
lines changed

Some content is hidden

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

202 files changed

+4067
-1101
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@clerk/clerk-js": minor
3+
"@clerk/shared": minor
4+
---
5+
6+
Implemented server-side pagination and filtering for API keys

.changeset/moody-parks-scream.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/clerk-js': minor
3+
'@clerk/shared': minor
4+
---
5+
6+
[Experimental] Add types for errors used in new custom flow APIs

.changeset/ripe-ants-carry.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

.changeset/ripe-banks-pay.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/clerk-js': minor
3+
'@clerk/types': minor
4+
---
5+
6+
[Experimental] Add support for modal SSO sign-ins to new APIs
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/shared': patch
3+
---
4+
5+
Add email codes as an option to `PrepareSecondFactorParams`

.typedoc/custom-plugin.mjs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,20 +98,50 @@ function getRelativeLinkReplacements() {
9898
function getCatchAllReplacements() {
9999
return [
100100
{
101-
pattern: /\(setActiveParams\)/g,
102-
replace: '([setActiveParams](/docs/reference/javascript/types/set-active-params))',
101+
pattern: /(?<![\[\w`])`Appearance`\\<`Theme`\\>/g,
102+
replace: '[`Appearance<Theme>`](/docs/guides/customizing-clerk/appearance-prop/overview)',
103+
},
104+
{
105+
pattern: /\(CreateOrganizationParams\)/g,
106+
replace: '([CreateOrganizationParams](#create-organization-params))',
103107
},
104108
{
105109
pattern: /`LoadedClerk`/g,
106110
replace: '[Clerk](/docs/reference/javascript/clerk)',
107111
},
108112
{
109-
pattern: /\(CreateOrganizationParams\)/g,
110-
replace: '([CreateOrganizationParams](#create-organization-params))',
113+
pattern: /(?<![\[\w`])`?LocalizationResource`?(?![\]\w`])/g,
114+
replace: '[`LocalizationResource`](/docs/guides/customizing-clerk/localization)',
115+
},
116+
{
117+
// SessionResource appears in plain text, with an array next to it, with backticks, etc.
118+
// e.g. `SessionResource[]`
119+
pattern: /(?<![`[\]])\bSessionResource(\[\])?\b(?![\]\)`])/g,
120+
replace: '[`SessionResource`](/docs/reference/javascript/session)$1',
121+
},
122+
{
123+
pattern: /(?<![\[\w`])`?SessionStatusClaim`?(?![\]\w`])/g,
124+
replace: '[`SessionStatusClaim`](/docs/reference/javascript/types/session-status)',
111125
},
112126
{
113-
pattern: /\| `SignInResource` \|/,
114-
replace: '| [SignInResource](/docs/reference/javascript/sign-in) |',
127+
pattern: /(?<![`[\]])\bSetActiveParams\b(?![\]\(])/g,
128+
replace: '[SetActiveParams](/docs/reference/javascript/types/set-active-params)',
129+
},
130+
{
131+
pattern: /(?<![\[\w`])`?SignInResource`?(?![\]\w`])/g,
132+
replace: '[`SignInResource`](/docs/reference/javascript/sign-in)',
133+
},
134+
{
135+
pattern: /(?<![\[\w`])`?SignedInSessionResource`?(?![\]\w`])/g,
136+
replace: '[`SignedInSessionResource`](/docs/reference/javascript/session)',
137+
},
138+
{
139+
pattern: /(?<![\[\w`])`?SignUpResource`?(?![\]\w`])/g,
140+
replace: '[`SignUpResource`](/docs/reference/javascript/sign-up)',
141+
},
142+
{
143+
pattern: /(?<![\[\w`])`?OrganizationResource`?(?![\]\w`])/g,
144+
replace: '[`OrganizationResource`](/docs/reference/javascript/organization)',
115145
},
116146
{
117147
pattern: /`OrganizationPrivateMetadata`/g,
@@ -142,6 +172,10 @@ function getCatchAllReplacements() {
142172
replace:
143173
'[`OrganizationMembershipPublicMetadata`](/docs/reference/javascript/types/metadata#organization-membership-public-metadata)',
144174
},
175+
{
176+
pattern: /(?<![\[\w`])`?UserResource`?(?![\]\w`])/g,
177+
replace: '[`UserResource`](/docs/reference/javascript/user)',
178+
},
145179
{
146180
/**
147181
* By default, `@deprecated` is output with `**Deprecated**`. We want to add a full stop to it.

eslint.config.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,8 @@ export default tseslint.config([
367367
},
368368
},
369369
{
370-
name: 'packages/clerk-js - vitest',
371-
files: ['packages/clerk-js/src/**/*.test.{ts,tsx}'],
370+
name: 'packages - vitest',
371+
files: ['packages/*/src/**/*.test.{ts,tsx}'],
372372
rules: {
373373
'jest/unbound-method': 'off',
374374
'@typescript-eslint/unbound-method': 'off',

integration/templates/expo-web/metro.config.js

Lines changed: 90 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* DO NOT EDIT THIS FILE UNLESS YOU DEFINITELY KNWO WHAT YOU ARE DOING.
2+
* DO NOT EDIT THIS FILE UNLESS YOU DEFINITELY KNOW WHAT YOU ARE DOING.
33
* THIS ENSURES THAT INTEGRATION TESTS ARE LOADING THE CORRECT DEPENDENCIES.
44
*/
55
const { getDefaultConfig } = require('expo/metro-config');
@@ -30,25 +30,93 @@ const clerkExpoPath = getClerkExpoPath();
3030
const clerkMonorepoPath = clerkExpoPath?.replace(/\/packages\/expo$/, '');
3131

3232
/** @type {import('expo/metro-config').MetroConfig} */
33-
const config = {
34-
...getDefaultConfig(__dirname),
35-
watchFolders: [clerkMonorepoPath],
36-
resolver: {
37-
sourceExts: ['js', 'json', 'ts', 'tsx', 'cjs', 'mjs'],
38-
nodeModulesPaths: [
39-
path.resolve(__dirname, 'node_modules'),
40-
clerkExpoPath && `${clerkMonorepoPath}/node_modules`,
41-
clerkExpoPath && `${clerkExpoPath}/node_modules`,
42-
],
43-
// This is a workaround for a to prevent multiple versions of react and react-native from being loaded.
44-
// https://github.com/expo/expo/pull/26209
45-
blockList: [
46-
clerkExpoPath && new RegExp(`${clerkMonorepoPath}/node_modules/react`),
47-
clerkExpoPath && new RegExp(`${clerkMonorepoPath}/node_modules/react-native`),
48-
],
49-
},
50-
};
33+
const config = getDefaultConfig(__dirname);
5134

52-
module.exports = {
53-
...config,
54-
};
35+
// Only customize Metro config when running from monorepo
36+
if (clerkMonorepoPath) {
37+
console.log('[Metro Config] Applying monorepo customizations');
38+
config.watchFolders = [clerkMonorepoPath];
39+
40+
// Disable file watching to prevent infinite reload loops in integration tests
41+
config.watchFolders = [clerkMonorepoPath];
42+
config.watcher = {
43+
healthCheck: {
44+
enabled: false,
45+
},
46+
};
47+
48+
// Prioritize local node_modules over monorepo node_modules
49+
config.resolver.nodeModulesPaths = [path.resolve(__dirname, 'node_modules'), `${clerkMonorepoPath}/node_modules`];
50+
51+
// Explicitly map @clerk packages to their source locations
52+
// Point to the root of the package so Metro can properly resolve subpath exports
53+
config.resolver.extraNodeModules = {
54+
'@clerk/clerk-react': path.resolve(clerkMonorepoPath, 'packages/react'),
55+
'@clerk/clerk-expo': path.resolve(clerkMonorepoPath, 'packages/expo'),
56+
'@clerk/shared': path.resolve(clerkMonorepoPath, 'packages/shared'),
57+
'@clerk/types': path.resolve(clerkMonorepoPath, 'packages/types'),
58+
};
59+
60+
// This is a workaround to prevent multiple versions of react and react-native from being loaded.
61+
// Block React/React-Native in both monorepo root and all package node_modules
62+
// Use word boundaries to avoid blocking clerk-react
63+
// https://github.com/expo/expo/pull/26209
64+
const escapedPath = clerkMonorepoPath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
65+
config.resolver.blockList = [
66+
// Block monorepo root node_modules for react/react-native/react-dom
67+
new RegExp(`${escapedPath}/node_modules/react/`),
68+
new RegExp(`${escapedPath}/node_modules/react$`),
69+
new RegExp(`${escapedPath}/node_modules/react-dom/`),
70+
new RegExp(`${escapedPath}/node_modules/react-dom$`),
71+
new RegExp(`${escapedPath}/node_modules/react-native/`),
72+
new RegExp(`${escapedPath}/node_modules/react-native$`),
73+
// Block react in monorepo's pnpm store
74+
new RegExp(`${escapedPath}/node_modules/\\.pnpm/.*/node_modules/react/`),
75+
new RegExp(`${escapedPath}/node_modules/\\.pnpm/.*/node_modules/react$`),
76+
new RegExp(`${escapedPath}/node_modules/\\.pnpm/.*/node_modules/react-dom/`),
77+
new RegExp(`${escapedPath}/node_modules/\\.pnpm/.*/node_modules/react-dom$`),
78+
new RegExp(`${escapedPath}/node_modules/\\.pnpm/.*/node_modules/react-native/`),
79+
new RegExp(`${escapedPath}/node_modules/\\.pnpm/.*/node_modules/react-native$`),
80+
// Block react/react-native/react-dom in all package node_modules
81+
new RegExp(`${escapedPath}/packages/.*/node_modules/react/`),
82+
new RegExp(`${escapedPath}/packages/.*/node_modules/react$`),
83+
new RegExp(`${escapedPath}/packages/.*/node_modules/react-dom/`),
84+
new RegExp(`${escapedPath}/packages/.*/node_modules/react-dom$`),
85+
new RegExp(`${escapedPath}/packages/.*/node_modules/react-native/`),
86+
new RegExp(`${escapedPath}/packages/.*/node_modules/react-native$`),
87+
];
88+
89+
// Custom resolver to handle package.json subpath exports for @clerk packages
90+
// This enables Metro to resolve imports like '@clerk/clerk-react/internal'
91+
const originalResolveRequest = config.resolver.resolveRequest;
92+
config.resolver.resolveRequest = (context, moduleName, platform) => {
93+
// Check if this is a @clerk package with a subpath
94+
const clerkPackageMatch = moduleName.match(/^(@clerk\/[^/]+)\/(.+)$/);
95+
if (clerkPackageMatch && config.resolver.extraNodeModules) {
96+
const [, packageName, subpath] = clerkPackageMatch;
97+
const packageRoot = config.resolver.extraNodeModules[packageName];
98+
99+
if (packageRoot) {
100+
// Try to resolve via the subpath-workaround directory (e.g., internal/package.json)
101+
const subpathDir = path.join(packageRoot, subpath);
102+
try {
103+
const subpathPkg = require(path.join(subpathDir, 'package.json'));
104+
if (subpathPkg.main) {
105+
const resolvedPath = path.join(subpathDir, subpathPkg.main);
106+
return { type: 'sourceFile', filePath: resolvedPath };
107+
}
108+
} catch (e) {
109+
// Subpath directory doesn't exist, continue with default resolution
110+
}
111+
}
112+
}
113+
114+
// Fall back to default resolution
115+
if (originalResolveRequest) {
116+
return originalResolveRequest(context, moduleName, platform);
117+
}
118+
return context.resolveRequest(context, moduleName, platform);
119+
};
120+
}
121+
122+
module.exports = config;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use client';
2+
import { usePlans, useSubscription, useStatements } from '@clerk/nextjs/experimental';
3+
4+
export default function Home() {
5+
const { data: plans, count: planCount } = usePlans();
6+
const { data: subscription } = useSubscription();
7+
const { data: statements, count: statementCount } = useStatements();
8+
return (
9+
<main>
10+
{plans?.map(plan => (
11+
<div key={plan.id}>
12+
<h2>Plan: {plan.name}</h2>
13+
<p>{plan.description}</p>
14+
</div>
15+
))}
16+
17+
{planCount > 0 ? <p>Plans found</p> : <p>No plans found</p>}
18+
19+
{statements?.map(statement => (
20+
<div key={statement.id}>
21+
<p>Statement total: {statement.totals.grandTotal.amountFormatted}</p>
22+
</div>
23+
))}
24+
25+
{statementCount > 0 ? <p>Statements found</p> : <p>No statements found</p>}
26+
27+
{subscription ? (
28+
<div>
29+
<h2>Subscribed to {subscription.subscriptionItems[0].plan.name}</h2>
30+
</div>
31+
) : (
32+
<p>No subscription found</p>
33+
)}
34+
</main>
35+
);
36+
}

integration/testUtils/usersService.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,15 +200,13 @@ export const createUserService = (clerkClient: ClerkClient) => {
200200

201201
const apiKey = await clerkClient.apiKeys.create({
202202
subject: userId,
203-
name: `Integration Test - ${userId}`,
203+
name: `Integration Test - ${faker.string.uuid()}`,
204204
secondsUntilExpiration: TWENTY_MINUTES,
205205
});
206206

207-
const { secret } = await clerkClient.apiKeys.getSecret(apiKey.id);
208-
209207
return {
210208
apiKey,
211-
secret,
209+
secret: apiKey.secret ?? '',
212210
revoke: () => clerkClient.apiKeys.revoke({ apiKeyId: apiKey.id, revocationReason: 'For testing purposes' }),
213211
} satisfies FakeAPIKey;
214212
},

0 commit comments

Comments
 (0)