Skip to content

Commit 01fd9c8

Browse files
committed
fix(*): address Protect timeout review feedback
1 parent ef1c365 commit 01fd9c8

7 files changed

Lines changed: 43 additions & 11 deletions

File tree

‎.changeset/protect-check-load-timeout.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
'@clerk/clerk-js': patch
3+
'@clerk/react': patch
34
'@clerk/shared': patch
45
'@clerk/ui': patch
56
---

‎packages/clerk-js/src/core/__tests__/protectChallengeLoadTimeout.test.ts‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,29 @@ describe('Protect.challengeLoadTimeoutMs', () => {
6363
expect(protect.challengeLoadTimeoutMs).toBe(25_000);
6464
});
6565

66-
it('ignores a non-positive or non-numeric value rather than passing it on', () => {
66+
it('ignores a non-positive, non-finite, or non-numeric value rather than passing it on', () => {
6767
const protect = new Protect();
6868
protect.load(
6969
environment([
7070
loader({ challenge_load_timeout_ms: 0 }),
71+
loader({ challenge_load_timeout_ms: Number.POSITIVE_INFINITY }),
7172
loader({ challenge_load_timeout_ms: 'soon' as unknown as number }),
7273
]),
7374
);
7475

7576
expect(protect.challengeLoadTimeoutMs).toBeUndefined();
7677
});
78+
79+
it('reports the first finite, positive value from the applied loaders', () => {
80+
const protect = new Protect();
81+
protect.load(
82+
environment([
83+
loader({ challenge_load_timeout_ms: 0 }),
84+
loader({ challenge_load_timeout_ms: 25_000 }),
85+
loader({ challenge_load_timeout_ms: 45_000 }),
86+
]),
87+
);
88+
89+
expect(protect.challengeLoadTimeoutMs).toBe(25_000);
90+
});
7791
});

‎packages/clerk-js/src/core/protect.ts‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,9 @@ export class Protect {
5555
// loaders we are actually going to apply.
5656
const loaders = configured.filter(loader => isLoader(loader) && inRollout(loader));
5757

58-
// Read off the applied set, before any of them run: this only describes config, so a loader
59-
// that later fails to be placed has still spoken for the browser it was assigned to.
60-
//
61-
// First one that asks for it wins, and the instance-wide value applies only when none does —
62-
// the precedence is across the applied set rather than per loader, because there is no single
63-
// "the" loader once more than one is live. Documented on the field.
58+
// Load timeout applies to a single event and we only need one, so we pick the first valid one.
59+
// This happens before applying the loaders since we want to use the value even if the loader
60+
// later fails to apply.
6461
this.#challengeLoadTimeoutMs = loaders.find(
6562
loader =>
6663
typeof loader.challenge_load_timeout_ms === 'number' &&

‎packages/react/src/__tests__/isomorphicClerk.test.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,20 @@ describe('isomorphicClerk', () => {
7373
expect(isomorphicClerk.__internal_moduleManager).toBe(mm);
7474
});
7575

76+
it('exposes the inner clerk-js Protect challenge load timeout', () => {
77+
const isomorphicClerk = new IsomorphicClerk({ publishableKey: 'pk_test_XXX' });
78+
79+
expect(isomorphicClerk.__internal_protectChallengeLoadTimeoutMs).toBeUndefined();
80+
81+
const innerClerk: any = {
82+
addListener: vi.fn(),
83+
__internal_protectChallengeLoadTimeoutMs: 25_000,
84+
};
85+
(isomorphicClerk as any).replayInterceptedInvocations(innerClerk);
86+
87+
expect(isomorphicClerk.__internal_protectChallengeLoadTimeoutMs).toBe(25_000);
88+
});
89+
7690
it('updates props asynchronously after clerkjs has loaded', async () => {
7791
const propsHistory: any[] = [];
7892
const dummyClerkJS = {

‎packages/react/src/isomorphicClerk.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,10 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk {
302302
return this.clerkjs?.__internal_moduleManager;
303303
}
304304

305+
public get __internal_protectChallengeLoadTimeoutMs(): number | undefined {
306+
return this.clerkjs?.__internal_protectChallengeLoadTimeoutMs;
307+
}
308+
305309
constructor(options: IsomorphicClerkOptions) {
306310
this.#publishableKey = options?.publishableKey;
307311
this.#proxyUrl = options?.proxyUrl;

‎packages/shared/src/types/protectConfig.ts‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ export interface ProtectLoader {
4444
* Per loader because loaders roll out gradually: while a new one ramps, two are live for the
4545
* same instance at once, and the new one may need a different value from the one it replaces.
4646
*
47-
* Precedence is across the APPLIED SET, not per loader: the first applied loader that specifies
48-
* one wins, and the instance-wide value applies only when none of them does. An instance running
49-
* two loaders at once should therefore either set this on both or on neither — setting it on one
50-
* makes it apply to browsers that got the other, which is rarely what is meant.
47+
* Precedence is across the APPLIED SET, not per loader: the first applied loader with a finite,
48+
* positive value wins, and the instance-wide value applies only when no applied loader specifies
49+
* such a value. An instance running two loaders at once should therefore either set this on both
50+
* or on neither — setting it on one makes it apply to browsers that got the other, which is rarely
51+
* what is meant.
5152
*/
5253
challenge_load_timeout_ms?: number;
5354
}

‎packages/ui/src/hooks/useProtectCheckRunner.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export function useProtectCheckRunner<TResource>(params: ProtectCheckRunnerParam
7272
// changing anything for browsers still on the loader it replaces. Undefined at both levels
7373
// leaves the SDK default in force. Read here rather than inside the effect so the effect keeps
7474
// depending on primitives.
75+
// Older clerk-js versions omit this getter; undefined preserves the instance/default fallback.
7576
const loaderTimeoutMs = useClerk().__internal_protectChallengeLoadTimeoutMs;
7677
const instanceTimeoutMs = useEnvironment().protectConfig?.challenge_load_timeout_ms;
7778
const loadTimeoutMs = loaderTimeoutMs ?? instanceTimeoutMs;

0 commit comments

Comments
 (0)