Skip to content

Commit 79bbbed

Browse files
authored
feat(clerk-js): Reuse SignIn/SignUp instances (#7803)
1 parent 4ddb821 commit 79bbbed

5 files changed

Lines changed: 104 additions & 2 deletions

File tree

.changeset/calm-pillows-slide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': minor
3+
---
4+
5+
Reuse SignIn and SignUp instances on Client when processing Client response JSON.

packages/clerk-js/src/core/resources/Client.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,19 @@ export class Client extends BaseResource implements ClientResource {
134134
if (data) {
135135
this.id = data.id;
136136
this.sessions = (data.sessions || []).map(s => new Session(s));
137-
this.signUp = new SignUp(data.sign_up);
138-
this.signIn = new SignIn(data.sign_in);
137+
138+
if (data.sign_up && this.signUp instanceof SignUp && this.signUp.id === data.sign_up.id) {
139+
this.signUp.__internal_updateFromJSON(data.sign_up);
140+
} else {
141+
this.signUp = new SignUp(data.sign_up);
142+
}
143+
144+
if (data.sign_in && this.signIn instanceof SignIn && this.signIn.id === data.sign_in.id) {
145+
this.signIn.__internal_updateFromJSON(data.sign_in);
146+
} else {
147+
this.signIn = new SignIn(data.sign_in);
148+
}
149+
139150
this.lastActiveSessionId = data.last_active_session_id;
140151
this.captchaBypass = data.captcha_bypass || false;
141152
this.cookieExpiresAt = data.cookie_expires_at ? unixEpochToDate(data.cookie_expires_at) : null;

packages/clerk-js/src/core/resources/SignIn.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,10 @@ export class SignIn extends BaseResource implements SignInResource {
574574
return this;
575575
}
576576

577+
public __internal_updateFromJSON(data: SignInJSON | SignInJSONSnapshot | null): this {
578+
return this.fromJSON(data);
579+
}
580+
577581
public __internal_toSnapshot(): SignInJSONSnapshot {
578582
return {
579583
object: 'sign_in',

packages/clerk-js/src/core/resources/SignUp.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,10 @@ export class SignUp extends BaseResource implements SignUpResource {
521521
return this;
522522
}
523523

524+
public __internal_updateFromJSON(data: SignUpJSON | SignUpJSONSnapshot | null): this {
525+
return this.fromJSON(data);
526+
}
527+
524528
public __internal_toSnapshot(): SignUpJSONSnapshot {
525529
return {
526530
object: 'sign_up',

packages/clerk-js/src/core/resources/__tests__/Client.test.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,84 @@ describe('Client Singleton', () => {
148148
});
149149
});
150150

151+
it('preserves sign up and sign in identity when fromJSON receives matching ids', () => {
152+
const user = createUser({ first_name: 'John', last_name: 'Doe', id: 'user_1' });
153+
const session = createSession({ id: 'session_1' }, user);
154+
const initialClientJSON: ClientJSON = {
155+
object: 'client',
156+
id: 'test_id',
157+
status: 'active',
158+
last_active_session_id: 'test_session_id',
159+
sign_in: createSignIn({ id: 'test_sign_in_id', status: 'needs_first_factor' }, user),
160+
sign_up: createSignUp({ id: 'test_sign_up_id', status: 'missing_requirements' }),
161+
sessions: [session],
162+
created_at: Date.now() - 1000,
163+
updated_at: Date.now(),
164+
} as any;
165+
166+
// @ts-expect-error We cannot mess with the singleton when tests are running in parallel
167+
const client = new Client(initialClientJSON);
168+
const initialSignUp = client.signUp;
169+
const initialSignIn = client.signIn;
170+
171+
client.fromJSON({
172+
...initialClientJSON,
173+
sign_in: createSignIn(
174+
{
175+
id: 'test_sign_in_id',
176+
status: 'needs_second_factor',
177+
identifier: 'updated@example.com',
178+
},
179+
user,
180+
),
181+
sign_up: createSignUp({
182+
id: 'test_sign_up_id',
183+
status: 'missing_requirements',
184+
email_address: 'updated@example.com',
185+
}),
186+
updated_at: Date.now() + 1000,
187+
});
188+
189+
expect(client.signUp).toBe(initialSignUp);
190+
expect(client.signIn).toBe(initialSignIn);
191+
expect(client.signUp.emailAddress).toBe('updated@example.com');
192+
expect(client.signIn.identifier).toBe('updated@example.com');
193+
expect(client.signIn.status).toBe('needs_second_factor');
194+
});
195+
196+
it('replaces sign up and sign in identity when fromJSON receives new ids', () => {
197+
const user = createUser({ first_name: 'John', last_name: 'Doe', id: 'user_1' });
198+
const session = createSession({ id: 'session_1' }, user);
199+
const initialClientJSON: ClientJSON = {
200+
object: 'client',
201+
id: 'test_id',
202+
status: 'active',
203+
last_active_session_id: 'test_session_id',
204+
sign_in: createSignIn({ id: 'test_sign_in_id', status: 'needs_first_factor' }, user),
205+
sign_up: createSignUp({ id: 'test_sign_up_id', status: 'missing_requirements' }),
206+
sessions: [session],
207+
created_at: Date.now() - 1000,
208+
updated_at: Date.now(),
209+
} as any;
210+
211+
// @ts-expect-error We cannot mess with the singleton when tests are running in parallel
212+
const client = new Client(initialClientJSON);
213+
const initialSignUp = client.signUp;
214+
const initialSignIn = client.signIn;
215+
216+
client.fromJSON({
217+
...initialClientJSON,
218+
sign_in: createSignIn({ id: 'test_sign_in_id_v2', status: 'needs_first_factor' }, user),
219+
sign_up: createSignUp({ id: 'test_sign_up_id_v2', status: 'missing_requirements' }),
220+
updated_at: Date.now() + 1000,
221+
});
222+
223+
expect(client.signUp).not.toBe(initialSignUp);
224+
expect(client.signIn).not.toBe(initialSignIn);
225+
expect(client.signUp.id).toBe('test_sign_up_id_v2');
226+
expect(client.signIn.id).toBe('test_sign_in_id_v2');
227+
});
228+
151229
it('has the same initial properties', () => {
152230
const clientJSON = {
153231
object: 'client',

0 commit comments

Comments
 (0)