Skip to content

Commit 12f75b0

Browse files
authored
Merge branch 'main' into changeset-release/main
2 parents cada456 + 2c06a5f commit 12f75b0

67 files changed

Lines changed: 698 additions & 112 deletions

File tree

Some content is hidden

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

.changeset/empty-lights-dress.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@clerk/nuxt': minor
3+
---
4+
5+
API keys is now generally available.
6+
7+
```vue
8+
<script setup>
9+
// Components are automatically imported
10+
</script>
11+
12+
<template>
13+
<APIKeys />
14+
</template>
15+
```

.changeset/fifty-paths-tie.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/ui': patch
3+
---
4+
5+
Adjust padding and display logo on `OrganizationList` header

.changeset/five-poems-drum.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/backend': patch
3+
---
4+
5+
Export `OrganizationInvitationAcceptedWebhookEvent` type.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@clerk/testing": patch
3+
---
4+
5+
Fix `signIn()` timing out with concurrent Playwright workers by de-duplicating route handler registration and adding retry with exponential backoff for transient FAPI errors (429, 502, 503, 504).
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

.changeset/stable-api-keys.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
"@clerk/shared": minor
3+
"@clerk/react": minor
4+
"@clerk/clerk-js": minor
5+
"@clerk/ui": minor
6+
---
7+
8+
API keys is now generally available.
9+
10+
### `<APIKeys />` component
11+
12+
```tsx
13+
import { APIKeys } from '@clerk/react';
14+
15+
export default function Page() {
16+
return <APIKeys />;
17+
}
18+
```
19+
20+
### `useAPIKeys()` hook
21+
22+
```tsx
23+
import { useAPIKeys } from '@clerk/react';
24+
25+
export default function CustomAPIKeys() {
26+
const { data, isLoading, page, pageCount, fetchNext, fetchPrevious } = useAPIKeys({
27+
pageSize: 10,
28+
initialPage: 1,
29+
});
30+
31+
if (isLoading) return <div>Loading...</div>;
32+
33+
return (
34+
<ul>
35+
{data?.map((key) => (
36+
<li key={key.id}>{key.name}</li>
37+
))}
38+
</ul>
39+
);
40+
}
41+
```

.github/workflows/e2e-staging.yml

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ concurrency:
3939
jobs:
4040
permissions-check:
4141
name: Check Permissions
42+
if: ${{ github.event_name != 'repository_dispatch' }}
4243
runs-on: 'blacksmith-8vcpu-ubuntu-2204'
4344
steps:
4445
- name: Check org membership
@@ -70,12 +71,35 @@ jobs:
7071
validate-instances:
7172
name: Validate Staging Instances
7273
needs: [permissions-check]
74+
if: ${{ always() && (needs.permissions-check.result == 'success' || needs.permissions-check.result == 'skipped') }}
7375
runs-on: 'blacksmith-8vcpu-ubuntu-2204'
7476
steps:
77+
- name: Normalize inputs
78+
id: inputs
79+
env:
80+
EVENT_NAME: ${{ github.event_name }}
81+
INPUT_REF: ${{ github.event.inputs.ref }}
82+
PAYLOAD_REF: ${{ github.event.client_payload.ref }}
83+
run: |
84+
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
85+
echo "ref=${INPUT_REF:-main}" >> $GITHUB_OUTPUT
86+
else
87+
echo "ref=${PAYLOAD_REF:-main}" >> $GITHUB_OUTPUT
88+
fi
89+
90+
- name: Validate ref
91+
env:
92+
REF: ${{ steps.inputs.outputs.ref }}
93+
run: |
94+
if [[ ! "$REF" =~ ^(main|release/.*)$ ]]; then
95+
echo "::error::Ref '$REF' is not allowed. Only 'main' and 'release/*' branches are permitted."
96+
exit 1
97+
fi
98+
7599
- name: Checkout Repo
76100
uses: actions/checkout@v4
77101
with:
78-
ref: ${{ github.event.inputs.ref || github.event.client_payload.ref || 'main' }}
102+
ref: ${{ steps.inputs.outputs.ref }}
79103
sparse-checkout: scripts/validate-staging-instances.mjs
80104
fetch-depth: 1
81105

@@ -88,6 +112,7 @@ jobs:
88112
integration-tests:
89113
name: Integration Tests (${{ matrix.test-name }}, ${{ matrix.test-project }})
90114
needs: [permissions-check]
115+
if: ${{ always() && (needs.permissions-check.result == 'success' || needs.permissions-check.result == 'skipped') }}
91116
runs-on: 'blacksmith-8vcpu-ubuntu-2204'
92117
defaults:
93118
run:

integration/testUtils/__tests__/retryableClerkClient.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,29 @@ describe('withRetry', () => {
135135
expect(mock).toHaveBeenCalledTimes(2);
136136
});
137137

138+
it('uses exponential backoff as floor when retryAfter is 0', async () => {
139+
vi.spyOn(Math, 'random').mockReturnValue(0);
140+
const error = makeClerkAPIError(429, { retryAfter: 0 });
141+
const mock = vi
142+
.fn()
143+
.mockImplementationOnce(() => Promise.resolve().then(() => Promise.reject(error)))
144+
.mockResolvedValueOnce({ id: 'user_123' });
145+
const client = makeMockClient({ getUser: mock });
146+
const wrapped = withRetry(client);
147+
148+
const promise = (wrapped.users as any).getUser('user_123');
149+
150+
// retryAfter=0 should NOT cause a 0ms delay; exponential backoff (1000ms for attempt 0) is used as floor
151+
await vi.advanceTimersByTimeAsync(999);
152+
expect(mock).toHaveBeenCalledTimes(1);
153+
154+
await vi.advanceTimersByTimeAsync(1);
155+
await vi.advanceTimersByTimeAsync(0);
156+
157+
await expect(promise).resolves.toEqual({ id: 'user_123' });
158+
expect(mock).toHaveBeenCalledTimes(2);
159+
});
160+
138161
it('caps retryAfter delay at MAX_RETRY_DELAY_MS (30s)', async () => {
139162
const error = makeClerkAPIError(429, { retryAfter: 60 });
140163
const mock = vi

integration/testUtils/retryableClerkClient.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ function sleep(ms: number): Promise<void> {
1414
}
1515

1616
function getRetryDelay(error: unknown, attempt: number): number {
17+
const exponentialDelay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * JITTER_MAX_MS;
1718
if (isClerkAPIResponseError(error) && typeof error.retryAfter === 'number') {
18-
return Math.min(error.retryAfter * 1000, MAX_RETRY_DELAY_MS);
19+
return Math.min(Math.max(error.retryAfter * 1000, exponentialDelay), MAX_RETRY_DELAY_MS);
1920
}
20-
return BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * JITTER_MAX_MS;
21+
return exponentialDelay;
2122
}
2223

2324
function recordRetry(path: string): void {

0 commit comments

Comments
 (0)