@sendoracloud/sdk-react-native 1.41.0 → 1.41.1
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 +26 -0
- package/README.md +1 -1
- package/dist/index.cjs +5 -15
- package/dist/index.d.cts +14 -6
- package/dist/index.d.ts +14 -6
- package/dist/index.js +5 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.41.1 — a dead session is `session_expired` again; the server's detail moves to `endReason`
|
|
4
|
+
|
|
5
|
+
**Fix to a 1.41.0 behaviour change (ADR-023: a new value is additive only for a NEW situation).**
|
|
6
|
+
A session that died on a server-rejected refresh was reported as `session_expired` through
|
|
7
|
+
1.40.0. 1.41.0 reported the server's own verdict as the reason instead (`revoked` / `deleted`),
|
|
8
|
+
which gave two old situations new names: "account deleted on another device" and "sessions
|
|
9
|
+
revoked". Adding a value to a union is additive for the compiler, not for a listener that
|
|
10
|
+
matched the old value. A listener that acted only on `session_expired` (the documented pattern, and
|
|
11
|
+
the RopoLabs template's session-death handler) silently ignored both, leaving the app on a
|
|
12
|
+
signed-in screen where every request fails. Reported by RopoLabs against the published
|
|
13
|
+
tarball before any production install.
|
|
14
|
+
|
|
15
|
+
- `signed_out.reason` is `session_expired` for EVERY server-rejected refresh, exactly as 1.40.0.
|
|
16
|
+
- New optional `signed_out.endReason`: the server's verdict (`revoked` | `deleted` | `expired` |
|
|
17
|
+
`reuse` | `unknown`; `evicted` reserved). Present only when the server sent one; absent on
|
|
18
|
+
`user` / `account_deleted` and from an older backend.
|
|
19
|
+
- `revoked` / `evicted` / `deleted` stay in `AuthSignedOutReason` so code written against 1.41.0
|
|
20
|
+
compiles; they are never emitted again. If you shipped 1.41.0 with a listener matching them,
|
|
21
|
+
it keeps compiling and stops firing — match `session_expired` (and read `endReason`).
|
|
22
|
+
- Nothing else changed: same storage, same wipe effects (no anon-id rotation on a session death),
|
|
23
|
+
same held-slot release rules.
|
|
24
|
+
|
|
25
|
+
Parity: web 3.24.1 / iOS 5.4.1 / Android 4.26.1. Guard: `test/auth-machine-conformance.test.ts`
|
|
26
|
+
`refresh_dead.current` (every endReason incl. `evicted` / `unknown` / absent → `session_expired` +
|
|
27
|
+
the exact `endReason`, key absent when the server sent none).
|
|
28
|
+
|
|
3
29
|
## 1.41.0 — sign-in no longer retires the guest: it is held on the device (ADR-033 wave 1)
|
|
4
30
|
|
|
5
31
|
**Two semantic changes, both strictly less destructive than before; everything else is additive
|
package/README.md
CHANGED
|
@@ -303,7 +303,7 @@ This SDK is on the **1.x line**. Patch bumps (1.x.y → 1.x.z) are backwards-com
|
|
|
303
303
|
|
|
304
304
|
### Auth state is a transition stream, not a health signal
|
|
305
305
|
|
|
306
|
-
`auth.onAuthStateChanged()` reports confirmed transitions only — `signed_in` (`cause`: `mint` | `sign_in` | `restore` | `link`), `signed_out` (reason `user` | `session_expired` | `account_deleted`
|
|
306
|
+
`auth.onAuthStateChanged()` reports confirmed transitions only — `signed_in` (`cause`: `mint` | `sign_in` | `restore` | `link`), `signed_out` (reason `user` | `session_expired` | `account_deleted`; a session the server killed is always `session_expired`, with the server's verdict in `endReason` — `revoked` | `deleted` | `expired` | `reuse` | `unknown` — since 1.41.1), `user_updated`, `guest_held`, `guest_released`, `session_withheld`, `device_takeover`, `deletion_cancelled`. Each subscriber first receives one `initial{status, user}` (1.41.0); with a live session it is followed by the same `signed_in{user}` replay 1.28–1.40 sent (no `cause`), so existing listeners keep working. New code: read `initial` and treat a cause-less `signed_in` right after it as the replay. A transient failure of the background token refresh (offline, timeout, HTTP 429, 5xx) emits **nothing**: the stored refresh token is kept and the refresh retried with backoff, so no state changed. "No event" means "no confirmed transition", never "the session is fine". Only a server-rejected refresh token produces `signed_out(session_expired)`. For liveness, read `getAccessToken()` — `null` means no usable token right now.
|
|
307
307
|
|
|
308
308
|
`signInAnonymously()` returns the **current** session when one exists, anonymous or identified — it never replaces a signed-in account with a guest. Pass `forceNew: true` to deliberately leave the current session and mint a fresh anonymous one (a guest left this way is held, not lost).
|
|
309
309
|
|
package/dist/index.cjs
CHANGED
|
@@ -263,7 +263,7 @@ var Storage = class {
|
|
|
263
263
|
// package.json
|
|
264
264
|
var package_default = {
|
|
265
265
|
name: "@sendoracloud/sdk-react-native",
|
|
266
|
-
version: "1.41.
|
|
266
|
+
version: "1.41.1",
|
|
267
267
|
description: "Sendora Cloud React Native + Expo SDK \u2014 analytics, identity, push-token registration, auth (incl. Game Center / Play Games), deep links (Branch / Firebase Dynamic Links parity). Auth + analytics + deep links work in Expo Go; push-token registration and the no-arg Game Center / Play Games sign-in require a Dev Client (EAS Build or `npx expo prebuild`).",
|
|
268
268
|
type: "module",
|
|
269
269
|
main: "./dist/index.cjs",
|
|
@@ -2931,7 +2931,7 @@ var Auth = class {
|
|
|
2931
2931
|
} catch (err) {
|
|
2932
2932
|
if (err instanceof AuthError && isDeadRefreshError(err.code)) {
|
|
2933
2933
|
if (this.hooks.storage.get(REFRESH_KEY) === sent) {
|
|
2934
|
-
await this.commitAsync({ kind: "clear", reason:
|
|
2934
|
+
await this.commitAsync({ kind: "clear", reason: "session_expired", endReason: err.endReason, expectRefresh: sent });
|
|
2935
2935
|
} else {
|
|
2936
2936
|
const i = this.held.findIndex((h) => h?.refreshToken === sent);
|
|
2937
2937
|
if (i !== -1 && err.endReason !== void 0 && RELEASE_HELD_ON_END_REASONS.includes(err.endReason)) {
|
|
@@ -3207,7 +3207,9 @@ var Auth = class {
|
|
|
3207
3207
|
if (reason === "stale_blob" || reason === "replaced") return { status: "applied", held: false };
|
|
3208
3208
|
if (reason === "user" && prevRefresh) this.revokeFireAndForget(prevRefresh);
|
|
3209
3209
|
this.pendingWipeHook = this.wipeLocalIdentity(reason);
|
|
3210
|
-
this.emitAuthState(
|
|
3210
|
+
this.emitAuthState(
|
|
3211
|
+
tx.endReason !== void 0 ? { event: "signed_out", user: null, reason, endReason: tx.endReason } : { event: "signed_out", user: null, reason }
|
|
3212
|
+
);
|
|
3211
3213
|
return { status: "applied", held: false };
|
|
3212
3214
|
}
|
|
3213
3215
|
/**
|
|
@@ -3390,18 +3392,6 @@ var Auth = class {
|
|
|
3390
3392
|
else this.hooks.storage.remove(key);
|
|
3391
3393
|
}
|
|
3392
3394
|
};
|
|
3393
|
-
function signedOutReasonFor(endReason) {
|
|
3394
|
-
switch (endReason) {
|
|
3395
|
-
case "revoked":
|
|
3396
|
-
return "revoked";
|
|
3397
|
-
case "deleted":
|
|
3398
|
-
return "deleted";
|
|
3399
|
-
case "evicted":
|
|
3400
|
-
return "evicted";
|
|
3401
|
-
default:
|
|
3402
|
-
return "session_expired";
|
|
3403
|
-
}
|
|
3404
|
-
}
|
|
3405
3395
|
function isDeadRefreshError(code) {
|
|
3406
3396
|
if (!code) return false;
|
|
3407
3397
|
return DEAD_CODES.includes(code);
|
package/dist/index.d.cts
CHANGED
|
@@ -761,12 +761,17 @@ interface DeletionCancelledEvent {
|
|
|
761
761
|
* Why the local session ended.
|
|
762
762
|
*
|
|
763
763
|
* - `user` — the app called `signOut()` (or `discardWithheld()`).
|
|
764
|
-
* - `session_expired` — the server rejected the stored refresh token
|
|
765
|
-
*
|
|
764
|
+
* - `session_expired` — the server rejected the stored refresh token. EVERY
|
|
765
|
+
* dead verdict, whatever the server's detail: read the
|
|
766
|
+
* event's `endReason` (`revoked` / `deleted` / `expired` /
|
|
767
|
+
* `reuse` / `unknown`) for that.
|
|
766
768
|
* - `account_deleted` — `deleteAccount()` succeeded.
|
|
767
|
-
* - `revoked` / `evicted` / `deleted` —
|
|
768
|
-
*
|
|
769
|
-
*
|
|
769
|
+
* - `revoked` / `evicted` / `deleted` — ⚠ emitted by 1.41.0 ONLY, never since
|
|
770
|
+
* 1.41.1. 1.41.0 put the server's `endReason` here, which
|
|
771
|
+
* renamed "account deleted elsewhere" / "sessions revoked"
|
|
772
|
+
* (both `session_expired` through 1.40.0) and silently
|
|
773
|
+
* broke listeners matching `session_expired`. Kept in the
|
|
774
|
+
* type so code written against 1.41.0 still compiles.
|
|
770
775
|
*
|
|
771
776
|
* Transient network failures and rate limits never produce any of these.
|
|
772
777
|
* Consumers default-branch (§3.5 NORMATIVE).
|
|
@@ -792,10 +797,13 @@ type AuthStateChange = {
|
|
|
792
797
|
event: "signed_in";
|
|
793
798
|
user: AuthUser;
|
|
794
799
|
cause?: AuthSignedInCause;
|
|
795
|
-
}
|
|
800
|
+
}
|
|
801
|
+
/** `endReason` (1.41.1) = the server's own verdict on a dead refresh token; absent on `user` / `account_deleted` and from an older backend. */
|
|
802
|
+
| {
|
|
796
803
|
event: "signed_out";
|
|
797
804
|
user: null;
|
|
798
805
|
reason: AuthSignedOutReason;
|
|
806
|
+
endReason?: RefreshEndReason;
|
|
799
807
|
} | {
|
|
800
808
|
event: "device_takeover";
|
|
801
809
|
user: AuthUser | null;
|
package/dist/index.d.ts
CHANGED
|
@@ -761,12 +761,17 @@ interface DeletionCancelledEvent {
|
|
|
761
761
|
* Why the local session ended.
|
|
762
762
|
*
|
|
763
763
|
* - `user` — the app called `signOut()` (or `discardWithheld()`).
|
|
764
|
-
* - `session_expired` — the server rejected the stored refresh token
|
|
765
|
-
*
|
|
764
|
+
* - `session_expired` — the server rejected the stored refresh token. EVERY
|
|
765
|
+
* dead verdict, whatever the server's detail: read the
|
|
766
|
+
* event's `endReason` (`revoked` / `deleted` / `expired` /
|
|
767
|
+
* `reuse` / `unknown`) for that.
|
|
766
768
|
* - `account_deleted` — `deleteAccount()` succeeded.
|
|
767
|
-
* - `revoked` / `evicted` / `deleted` —
|
|
768
|
-
*
|
|
769
|
-
*
|
|
769
|
+
* - `revoked` / `evicted` / `deleted` — ⚠ emitted by 1.41.0 ONLY, never since
|
|
770
|
+
* 1.41.1. 1.41.0 put the server's `endReason` here, which
|
|
771
|
+
* renamed "account deleted elsewhere" / "sessions revoked"
|
|
772
|
+
* (both `session_expired` through 1.40.0) and silently
|
|
773
|
+
* broke listeners matching `session_expired`. Kept in the
|
|
774
|
+
* type so code written against 1.41.0 still compiles.
|
|
770
775
|
*
|
|
771
776
|
* Transient network failures and rate limits never produce any of these.
|
|
772
777
|
* Consumers default-branch (§3.5 NORMATIVE).
|
|
@@ -792,10 +797,13 @@ type AuthStateChange = {
|
|
|
792
797
|
event: "signed_in";
|
|
793
798
|
user: AuthUser;
|
|
794
799
|
cause?: AuthSignedInCause;
|
|
795
|
-
}
|
|
800
|
+
}
|
|
801
|
+
/** `endReason` (1.41.1) = the server's own verdict on a dead refresh token; absent on `user` / `account_deleted` and from an older backend. */
|
|
802
|
+
| {
|
|
796
803
|
event: "signed_out";
|
|
797
804
|
user: null;
|
|
798
805
|
reason: AuthSignedOutReason;
|
|
806
|
+
endReason?: RefreshEndReason;
|
|
799
807
|
} | {
|
|
800
808
|
event: "device_takeover";
|
|
801
809
|
user: AuthUser | null;
|
package/dist/index.js
CHANGED
|
@@ -212,7 +212,7 @@ var Storage = class {
|
|
|
212
212
|
// package.json
|
|
213
213
|
var package_default = {
|
|
214
214
|
name: "@sendoracloud/sdk-react-native",
|
|
215
|
-
version: "1.41.
|
|
215
|
+
version: "1.41.1",
|
|
216
216
|
description: "Sendora Cloud React Native + Expo SDK \u2014 analytics, identity, push-token registration, auth (incl. Game Center / Play Games), deep links (Branch / Firebase Dynamic Links parity). Auth + analytics + deep links work in Expo Go; push-token registration and the no-arg Game Center / Play Games sign-in require a Dev Client (EAS Build or `npx expo prebuild`).",
|
|
217
217
|
type: "module",
|
|
218
218
|
main: "./dist/index.cjs",
|
|
@@ -2880,7 +2880,7 @@ var Auth = class {
|
|
|
2880
2880
|
} catch (err) {
|
|
2881
2881
|
if (err instanceof AuthError && isDeadRefreshError(err.code)) {
|
|
2882
2882
|
if (this.hooks.storage.get(REFRESH_KEY) === sent) {
|
|
2883
|
-
await this.commitAsync({ kind: "clear", reason:
|
|
2883
|
+
await this.commitAsync({ kind: "clear", reason: "session_expired", endReason: err.endReason, expectRefresh: sent });
|
|
2884
2884
|
} else {
|
|
2885
2885
|
const i = this.held.findIndex((h) => h?.refreshToken === sent);
|
|
2886
2886
|
if (i !== -1 && err.endReason !== void 0 && RELEASE_HELD_ON_END_REASONS.includes(err.endReason)) {
|
|
@@ -3156,7 +3156,9 @@ var Auth = class {
|
|
|
3156
3156
|
if (reason === "stale_blob" || reason === "replaced") return { status: "applied", held: false };
|
|
3157
3157
|
if (reason === "user" && prevRefresh) this.revokeFireAndForget(prevRefresh);
|
|
3158
3158
|
this.pendingWipeHook = this.wipeLocalIdentity(reason);
|
|
3159
|
-
this.emitAuthState(
|
|
3159
|
+
this.emitAuthState(
|
|
3160
|
+
tx.endReason !== void 0 ? { event: "signed_out", user: null, reason, endReason: tx.endReason } : { event: "signed_out", user: null, reason }
|
|
3161
|
+
);
|
|
3160
3162
|
return { status: "applied", held: false };
|
|
3161
3163
|
}
|
|
3162
3164
|
/**
|
|
@@ -3339,18 +3341,6 @@ var Auth = class {
|
|
|
3339
3341
|
else this.hooks.storage.remove(key);
|
|
3340
3342
|
}
|
|
3341
3343
|
};
|
|
3342
|
-
function signedOutReasonFor(endReason) {
|
|
3343
|
-
switch (endReason) {
|
|
3344
|
-
case "revoked":
|
|
3345
|
-
return "revoked";
|
|
3346
|
-
case "deleted":
|
|
3347
|
-
return "deleted";
|
|
3348
|
-
case "evicted":
|
|
3349
|
-
return "evicted";
|
|
3350
|
-
default:
|
|
3351
|
-
return "session_expired";
|
|
3352
|
-
}
|
|
3353
|
-
}
|
|
3354
3344
|
function isDeadRefreshError(code) {
|
|
3355
3345
|
if (!code) return false;
|
|
3356
3346
|
return DEAD_CODES.includes(code);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendoracloud/sdk-react-native",
|
|
3
|
-
"version": "1.41.
|
|
3
|
+
"version": "1.41.1",
|
|
4
4
|
"description": "Sendora Cloud React Native + Expo SDK — analytics, identity, push-token registration, auth (incl. Game Center / Play Games), deep links (Branch / Firebase Dynamic Links parity). Auth + analytics + deep links work in Expo Go; push-token registration and the no-arg Game Center / Play Games sign-in require a Dev Client (EAS Build or `npx expo prebuild`).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|