Add an enabled prop to KnockProvider#1033
Conversation
Add a subscribable authStore + authStatus getter and a Knock.logout() that tears down all stateful connections (socket, token-expiration timer, and page-visibility listener) and lazily re-creates the API client. Make user-scoped calls quiescent while unauthenticated instead of throwing or firing blind: - Feed markAs*/markAll*/fetchNextPage no-op without an optimistic store write. - Guide fetch/subscribe/step-marks no longer throw (fixes the crash when Guides render before a user is set). - Slack/MS Teams authCheck return a disconnected shape; getChannels/getTeams return empty; messages.batchUpdateStatuses returns []. Also fix two guide bugs the enabled prop exacerbates: re-read the socket from the API client on each subscribe so real-time survives re-auth, and share the history pushState/replaceState patch per window so remounting a guide provider no longer nests patches or leaves the originals unrestored.
Implement `enabled` (default true) as credential-nulling through useAuthenticatedKnockClient: when false the client is created but left unauthenticated and fully quiescent while children still render. A fresh client is built on every enable/disable transition so enabling remounts the feed subtree (and refetches) and disabling clears the previous user's stores. Also guard useFeedSettings against firing GET /v1/users/undefined/.../settings for an unauthenticated user, and tear the client down on provider unmount (StrictMode-safe via a dispose/re-init flag) instead of leaking the socket, token timer, and page-visibility listener.
Add useKnockAuthState(knock), which subscribes to the client's authStore and re-renders on login, logout, or a user switch. Wire it into the integrations that previously latched their state: - Slack/MS Teams connection status resets and re-runs authCheck when the authenticated user changes, and the provider keys now include the userId so a user switch reliably re-renders consumers. - Expo autoRegister waits for an authenticated user before registering a push token, deferring the OS permission prompt for logged-out users and re-registering on sign-in; a notification tapped while logged out no longer fires a message-status update.
Add the readyToTarget x enabled/auth matrix to KnockGuideProvider's JSDoc, and document the enabled prop in the react, react-native, and expo READMEs plus the vanilla logout/quiescence story in the client README.
🦋 Changeset detectedLatest commit: fd3ca65 The changes in this PR will be included in the next version bump. This PR includes changesets to release 13 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.
|
Bundle ReportChanges will increase total bundle size by 11.94kB (2.13%) ⬆️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: @knocklabs/react-react-cjsAssets Changed:
Files in
view changes for bundle: @knocklabs/react-react-esmAssets Changed:
Files in
view changes for bundle: @knocklabs/expo-expo-cjsAssets Changed:
Files in
view changes for bundle: @knocklabs/client-client-esmAssets Changed:
Files in
Files in
Files in
Files in
Files in
Files in
Files in
view changes for bundle: @knocklabs/react-core-react-core-cjsAssets Changed:
Files in
Files in
Files in
Files in
Files in
Files in
Files in
view changes for bundle: @knocklabs/react-core-react-core-esmAssets Changed:
Files in
Files in
Files in
Files in
Files in
Files in
Files in
view changes for bundle: @knocklabs/client-client-cjsAssets Changed:
Files in
Files in
Files in
Files in
Files in
Files in
Files in
view changes for bundle: @knocklabs/expo-expo-esmAssets Changed:
Files in
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b9854aa. Configure here.
Replace the disposedRef/generation StrictMode self-heal with a plain effect cleanup that tears the client down on unmount. This removes the one-render window where a StrictMode simulated remount could return the previous (torn-down) memoized client before the generation bump rebuilt it. Addresses Cursor BugBot: "StrictMode serves torn-down client".
Wait for the connection status to resolve back to "connected" (which only happens after the second authCheck promise resolves) rather than asserting it synchronously right after waitFor(authCheck called twice), which was flaky under full-suite timing.
enabled prop to KnockProvider (SDK-wide quiescence + auth-state signal)enabled prop to KnockProvider
Replace the generation/disposedRef StrictMode self-heal with a plain effect-cleanup teardown. Addresses a Cursor BugBot finding: the self-heal could briefly return a torn-down client on a StrictMode simulated remount. The simpler cleanup still fixes the real unmount leak (socket, token-refresh timer, page-visibility listener) without that path.
Rewrite the changesets, READMEs, and doc comments in plain language, dropping "quiescent"/"quiescence" and similar jargon, so the enabled-prop behavior is easy to read.
Add a KnockSlackProvider render test, an Expo test that fires a notification while unauthenticated (hitting the updateMessageStatus guard), and a guide test that calls the patched history.pushState to exercise the shared location-change patch.
…ed-out instance The onUserTokenExpiring callback re-authenticated after its await with no check that logout()/teardown() had run in the meantime, which rebuilt the API client and rescheduled the timer. Null the timer on teardown and bail in the callback if the timer id is no longer current.
The react package curates its re-exports by name and this one was missing, so it was unimportable despite being advertised. Guard it in the barrel test.
If a user switch happened while the first authCheck was still in flight, the effect never re-ran (status was still "connecting") and the previous users result latched. Depend on userId and ignore superseded results.
… true Uses a real Knock (not the singleton-mocked one) so the fresh-instance + feedProviderKey remount that drives the feed to refetch is actually observed.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1033 +/- ##
==========================================
+ Coverage 64.49% 66.00% +1.51%
==========================================
Files 212 213 +1
Lines 10217 10499 +282
Branches 1389 1506 +117
==========================================
+ Hits 6589 6930 +341
+ Misses 3603 3544 -59
Partials 25 25
|

Description
Adds an
enabledprop to<KnockProvider>. When it'sfalse, children still render but the Knock client sits idle: no identify call, no API requests, no websocket. Set it totrueand it connects like a login; set it back tofalseand it shuts down like a logout. It defaults totrue, so existing code is unaffected.Today, if you don't have a user yet (common when the user token loads asynchronously), your only option is to conditionally mount the provider. This replaces that:
The prop itself is small. Most of the work is that a lot of the SDK used to throw, or fire requests with
userId=undefined, when no user was set. The worst case: rendering Guides before a user was set would throw and crash the app. This makes all of those paths do nothing when there's no user.Code changes
@knocklabs/client— do nothing when signed out, addlogout()+ auth stateknock.ts— newlogout()(disconnects socket + token timer + visibility listener),authStatus, a subscribableauthStore, andsyncAuthState().authenticate()also rewires surviving feeds after alogout()via the newfeeds.hasInstances()check.interfaces.ts—KnockAuthStatus/KnockAuthStatetypes.clients/feed/feed.ts— acanMutate()gate; everymarkAs*/markAll*/fetchNextPageno-ops (and skips the optimistic store update) when signed out.clients/feed/index.ts— thehasInstances()helper.clients/guide/client.ts—fetch/subscribe/ step-marks stop throwing and no-op;subscribe()re-reads the socket from the client (fixes real-time after a re-login); and thehistorymonkey-patch is now shared + idempotent (fixes a bug on provider remount).clients/slack/index.ts·clients/ms-teams/index.ts—authCheckreturns "not connected";getChannels/getTeamsreturn empty.clients/messages/index.ts—batchUpdateStatusesreturns[].@knocklabs/react-core— theenabledpropKnockProvider.tsx— theenabledprop.useAuthenticatedKnockClient.ts— the implementation: when off, pass no user; toggling builds a fresh client (so the feed reloads on login and clears on logout); tear down on unmount.useFeedSettings.ts— skips the branding fetch (no moreGET /users/undefined/...) when signed out.react-core+expo— react to sign-in changesuseKnockAuthState.ts— new hook (subscribes toauthStore), exported viahooks/index.ts→core/index.ts→src/index.ts.useSlackConnectionStatus.ts·useMsTeamsConnectionStatus.ts— reset and re-runauthCheckwhen the user changes (it used to check once and stick).utils.ts,KnockSlackProvider.tsx,KnockMsTeamsProvider.tsx— adduserIdto the provider keys so a user switch re-renders consumers.KnockExpoPushNotificationProvider.tsx— waits for a user before auto-registering (defers the OS permission prompt), and skips the status update for a notification tapped while signed out.Docs
KnockGuideProvider.tsx— JSDoc onreadyToTargetvs auth. Plus README updates and changesets for every affected package.Tests
The bulk of the diff. They cover the transitions (enable = login, disable = logout, user switch), the client-side no-ops per method, the guide history-patch and socket re-read, the Slack/Teams re-check, and the Expo gating. Client + react-core + expo suites pass.
Todos
enabledprop.Checklist