@passflow/react 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to `@passflow/react` are documented in this file.
4
4
 
5
+ ## [0.17.0] - 2026-11-03
6
+
7
+ ### Changed
8
+
9
+ - Requires `@passflow/core` `^0.21.0`: one token refresh per browser across tabs, with transient failures kept apart from the end of a session. See core's `docs/token-refresh.md`, "Migrating from 0.20.1". What reaches React apps: a network error or 5xx during refresh no longer signs the user out; `SessionExpired` fires only at a real end; `SignOut` fires once per sign-out.
10
+ - `SessionExpiredReason` is core's type, re-exported from the package root as before. It adds `'successor_timeout'` and `'identity_changed'`, so `useSessionExpired` and `AuthProvider`'s `onSessionExpired` can receive them. An exhaustive `switch` over the reason needs the new cases.
11
+
12
+ ### Added
13
+
14
+ - `useRefreshStatus()` returns `{ isFailing, since, nextRetryAt }` while token refresh is failing transiently (`RefreshFailing` until `RefreshRecovered`), for a "reconnecting" notice. Call it in a component that lives as long as the app: a component that mounts mid-episode starts idle.
15
+
16
+ ### Fixed
17
+
18
+ - `PassflowProvider` disposes the `Passflow` instances it replaces (its state changes on app ID discovery and app settings) and the one it holds on unmount. From core 0.21 each instance holds refresh timers, a `BroadcastChannel` and Web Locks, so an undisposed one went on acting as another tab.
19
+
5
20
  ## [0.16.0] - 2026-11-03
6
21
 
7
22
  ### Breaking
package/README.md CHANGED
@@ -46,6 +46,8 @@ This is a SDK for react application.
46
46
  - [useJoinInvite](#usejoininvite)
47
47
  - [useLogout](#uselogout)
48
48
  - [usePasswordlessComplete](#usepasswordlesscomplete)
49
+ - [useSessionExpired](#usesessionexpired)
50
+ - [useRefreshStatus](#userefreshstatus)
49
51
 
50
52
  to install just type:
51
53
 
@@ -947,3 +949,43 @@ const { fetch, isLoading, isError, error } = usePasswordlessComplete();
947
949
  | isLoading | `boolean` | Loading state |
948
950
  | isError | `boolean` | Error state |
949
951
  | error | `string` | Error message |
952
+
953
+ ### useSessionExpired
954
+ Hook for reacting to the end of a session, for example to redirect to your login page.
955
+
956
+ ```typescript
957
+ const { isSessionExpired, expiredReason, resetSessionExpired } = useSessionExpired({
958
+ onSessionExpired: (reason) => navigate('/signin'),
959
+ });
960
+ ```
961
+
962
+ `SessionExpired` fires only when the session has really ended: the last auth strike (`'refresh_failed'`), no successor token pair in time (`'successor_timeout'`), or another user's tokens in storage (`'identity_changed'`). A transient refresh failure (network error, 5xx) keeps the session and shows up as `RefreshFailing` / `RefreshRecovered` instead; use [useRefreshStatus](#userefreshstatus) for that. The state resets on `SignIn` and `Register`.
963
+
964
+ | Parameter | Type | Description |
965
+ |-----------|------|-------------|
966
+ | onSessionExpired | `(reason: SessionExpiredReason) => void` (optional) | Called when the session ends |
967
+
968
+ **Returns:**
969
+
970
+ | Property | Type | Description |
971
+ |----------|------|-------------|
972
+ | isSessionExpired | `boolean` | True once the session has ended |
973
+ | expiredReason | `SessionExpiredReason \| null` | Why it ended (`SessionExpiredReason` is exported from `@passflow/core`) |
974
+ | resetSessionExpired | `() => void` | Clears the expired state |
975
+
976
+ ### useRefreshStatus
977
+ Hook for showing a "connection trouble" notice while token refresh is failing transiently. The session is kept during the episode; show the notice on `RefreshFailing` and hide it on `RefreshRecovered`.
978
+
979
+ ```typescript
980
+ const { isFailing, since, nextRetryAt } = useRefreshStatus();
981
+ ```
982
+
983
+ Set by `RefreshFailing`. Cleared by `RefreshRecovered`, `SessionExpired`, `SignOut`, `SignIn` and `Register`. The episode is known only from events, so a component that mounts after `RefreshFailing` starts idle: call the hook in a component that lives as long as the app, near `PassflowProvider`.
984
+
985
+ **Returns:**
986
+
987
+ | Property | Type | Description |
988
+ |----------|------|-------------|
989
+ | isFailing | `boolean` | True inside a transient-failure episode |
990
+ | since | `number \| null` | Epoch ms of the episode's first failure |
991
+ | nextRetryAt | `number \| null` | Epoch ms of the pending retry, when known |