refactor(clerk-js): Consolidate resource events into single event#7980
refactor(clerk-js): Consolidate resource events into single event#7980
Conversation
🦋 Changeset detectedLatest commit: 59c6aa4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
!snapshot |
|
Hey @nikosdouvlis - the snapshot version command generated the following package versions:
Tip: Use the snippet copy button below to quickly install the required packages. npm i @clerk/agent-toolkit@0.3.2-snapshot.v20260305105512 --save-exact
npm i @clerk/astro@3.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/backend@3.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/chrome-extension@3.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/clerk-js@6.0.1-snapshot.v20260305105512 --save-exact
npm i @clerk/dev-cli@0.1.1-snapshot.v20260305105512 --save-exact
npm i @clerk/expo@3.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/expo-passkeys@1.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/express@2.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/fastify@3.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/hono@0.0.4-snapshot.v20260305105512 --save-exact
npm i @clerk/localizations@4.0.1-snapshot.v20260305105512 --save-exact
npm i @clerk/msw@0.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/nextjs@7.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/nuxt@2.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/react@6.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/react-router@3.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/shared@4.0.1-snapshot.v20260305105512 --save-exact
npm i @clerk/tanstack-react-start@1.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/testing@2.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/ui@1.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/upgrade@2.0.2-snapshot.v20260305105512 --save-exact
npm i @clerk/vue@2.0.2-snapshot.v20260305105512 --save-exact |
…:state-change` event
baab30c to
59c6aa4
Compare
📝 WalkthroughWalkthroughThis PR consolidates resource state management in Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/clerk-js/src/core/resources/SignIn.ts (1)
1293-1312:⚠️ Potential issue | 🟠 Major
finalize()can reject unexpectedly and mark the flow discardable before successLine [1296] throws outside the
try, so this method can reject instead of returning{ error }. Also, setting#canBeDiscardedbeforesetActivesucceeds can prematurely allow null-resource replacement on failure.Proposed fix
async finalize(params?: SignInFutureFinalizeParams): Promise<{ error: ClerkError | null }> { const { navigate } = params || {}; - - if (!this.#resource.createdSessionId) { - throw new Error('Cannot finalize sign-in without a created session.'); - } try { + if (!this.#resource.createdSessionId) { + throw new Error('Cannot finalize sign-in without a created session.'); + } + // Reload the client if the created session is not in the client's sessions. This can happen during modal SSO // flows where the in-memory client does not have the created session. if (SignIn.clerk.client && !SignIn.clerk.client.sessions.some(s => s.id === this.#resource.createdSessionId)) { await SignIn.clerk.client.reload(); } - this.#canBeDiscarded = true; await SignIn.clerk.setActive({ session: this.#resource.createdSessionId, navigate }); + this.#canBeDiscarded = true; return { error: null }; } catch (err) { return { error: err as ClerkError }; } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/clerk-js/src/core/resources/SignIn.ts` around lines 1293 - 1312, The finalize method currently throws if `#resource.createdSessionId` is missing and sets `#canBeDiscarded` before setActive succeeds, so change finalize to return { error } instead of throwing and only mark `#canBeDiscarded` true after SignIn.clerk.setActive completes successfully: move the createdSessionId guard and any throws into the try/catch (or convert them to an early return of { error: new ClerkError(...) }) and relocate the assignment of this.#canBeDiscarded to immediately after await SignIn.clerk.setActive(...) succeeds; adjust error handling to return the caught error as ClerkError. Reference finalize, this.#resource.createdSessionId, this.#canBeDiscarded, SignIn.clerk.client.reload, and SignIn.clerk.setActive.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@packages/clerk-js/src/core/resources/SignIn.ts`:
- Around line 1293-1312: The finalize method currently throws if
`#resource.createdSessionId` is missing and sets `#canBeDiscarded` before setActive
succeeds, so change finalize to return { error } instead of throwing and only
mark `#canBeDiscarded` true after SignIn.clerk.setActive completes successfully:
move the createdSessionId guard and any throws into the try/catch (or convert
them to an early return of { error: new ClerkError(...) }) and relocate the
assignment of this.#canBeDiscarded to immediately after await
SignIn.clerk.setActive(...) succeeds; adjust error handling to return the caught
error as ClerkError. Reference finalize, this.#resource.createdSessionId,
this.#canBeDiscarded, SignIn.clerk.client.reload, and SignIn.clerk.setActive.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: d47a6777-69a9-4390-a604-fcc13018fee0
📒 Files selected for processing (13)
.changeset/fiery-games-pick.mdpackages/clerk-js/src/core/__tests__/state.test.tspackages/clerk-js/src/core/events.tspackages/clerk-js/src/core/resources/Client.tspackages/clerk-js/src/core/resources/SignIn.tspackages/clerk-js/src/core/resources/SignUp.tspackages/clerk-js/src/core/resources/Waitlist.tspackages/clerk-js/src/core/resources/__tests__/SignIn.test.tspackages/clerk-js/src/core/resources/__tests__/SignUp.test.tspackages/clerk-js/src/core/resources/__tests__/Waitlist.test.tspackages/clerk-js/src/core/state.tspackages/clerk-js/src/utils/__tests__/runAsyncResourceTask.test.tspackages/clerk-js/src/utils/runAsyncResourceTask.ts
|
!snapshot |
@clerk/agent-toolkit
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/hono
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
|
This looks great! I just have a few clarifying questions before I approve:
I'm pretty sure I understand this, but just to state it again to make sure I understand: with this change we're now able to batch the resource/error/fetchStatus signal updates into a single update when those happen in a single call site. Meaning one event emit results in one signal update, but multiple events aren't batched in any way (which is totally fine, just making sure I understand). So, for example: triggers one signal update. I think I'm fine with this, but it does mean we no longer have the ability to update just the resource, or just the fetch status. As evidenced by this PR that's probably fine? But still worth calling out.
This one I'm the most unsure of, because there's times when the resource identity isn't stable. However I can't think of an instance in which the resource identity will change during a method that's set fetchStatus to
The original intent here was to put the resource into a fetching status since we were doing something that should disable input (waiting for the navigation to be performed). I think it's fine to remove for now, and we can add back if we feel the need or see people doing things like |
Description
Resource state changes were spread across three separate events:
resource:update,resource:error, andresource:fetch. Each one triggered its own signal write inState, which meant React could re-render between them. After an API call completed, React would briefly see the updated resource whilefetchStatuswas still'fetching', because the resource and fetch-status updates arrived independently.Even after consolidating into one event, a second source of inconsistency remained:
fromJSONemits a resource-only event mid-flight (during an API call), which updated the resource signal beforerunAsyncResourceTaskemitted the completion event withfetchStatus: 'idle'.A third source came from
finalize(), which usedrunAsyncResourceTaskand setfetchStatus: 'fetching'on an already-complete resource even though it's just callingsetActive, a session-level operation.How we fixed it
Three changes, each addressing one source of inconsistency:
Consolidated three events into one.
resource:update,resource:error, andresource:fetchare now a singleresource:state-changeevent with optionalerrorandfetchStatusfields. TheStatehandler wraps all signal writes instartBatch()/endBatch()from alien-signals so they flush as one notification.Skip resource-only events while fetching. When
Statereceives a resource-only event (nofetchStatus, noerror) and the currentfetchStatusis'fetching', it skips the signal write entirely. SincefromJSONmutates the resource in place, the completion event carries the same already-updated instance.Removed
runAsyncResourceTaskfromfinalize(). Likereset(),finalize()now handles its own async flow without emittingfetchStatus. The sign-in/sign-up is already complete;setActiveis a session-level operation that shouldn't affect sign-in/sign-up fetch status.Demo
(showcasing the
finalize()fix)resource-events-before.mov
resource-events-after.mov
Sample code used for the demo
Checklist
pnpm testruns as expected.pnpm buildruns as expected.Type of change
Summary by CodeRabbit
Bug Fixes
Improvements