@sendoracloud/sdk-react-native 1.26.0 → 1.29.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/dist/index.cjs +288 -47
- package/dist/index.d.cts +178 -6
- package/dist/index.d.ts +178 -6
- package/dist/index.js +288 -47
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -221,10 +221,19 @@ declare class Storage {
|
|
|
221
221
|
* user_id is preserved on success. Throws
|
|
222
222
|
* EmailAlreadyTakenError on 409. Otherwise calls
|
|
223
223
|
* /auth-service/signup as a fresh account.
|
|
224
|
-
* signIn(email, password) — POST /auth-service/login.
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
224
|
+
* signIn(email, password) — POST /auth-service/login.
|
|
225
|
+
*
|
|
226
|
+
* HARD INVARIANT — a failed auth attempt leaves the caller exactly as
|
|
227
|
+
* it found them. Every credentialed path captures the anonymous
|
|
228
|
+
* refresh token into a local (for the device-takeover hint), performs
|
|
229
|
+
* its network call, validates the response, and only THEN clears the
|
|
230
|
+
* old identity and persists the new one. Clearing first would drop the
|
|
231
|
+
* refresh token — for an anonymous user the ONLY durable handle on the
|
|
232
|
+
* account — so any failure after that point (offline, 5xx, a mistyped
|
|
233
|
+
* OTP, a cancelled IdP callback) would orphan the account permanently.
|
|
234
|
+
* A track() fired during the round-trip therefore attaches to the
|
|
235
|
+
* still-anonymous identity, which is correct: the user is not
|
|
236
|
+
* identified yet, and the backend reattributes it via the takeover.
|
|
228
237
|
*
|
|
229
238
|
* All public auth ops are serialized through a single-flight chain
|
|
230
239
|
* so a UI double-submit can't mint two anonymous users or interleave
|
|
@@ -310,9 +319,51 @@ declare class AlreadyIdentifiedError extends Error {
|
|
|
310
319
|
declare class CredentialInUseError extends Error {
|
|
311
320
|
constructor(message?: string);
|
|
312
321
|
}
|
|
322
|
+
/** One credential linked to an account (ADR-030 read side, s58.270). */
|
|
323
|
+
type LinkedIdentity = {
|
|
324
|
+
provider: string;
|
|
325
|
+
email: string | null;
|
|
326
|
+
linkedAt: string;
|
|
327
|
+
};
|
|
328
|
+
/** Response of `auth.listLinkedIdentities()` — the full connected-account set. */
|
|
329
|
+
type LinkedIdentitiesResponse = {
|
|
330
|
+
identities: LinkedIdentity[];
|
|
331
|
+
hasPassword: boolean;
|
|
332
|
+
};
|
|
333
|
+
/**
|
|
334
|
+
* Closed set of auth failure categories (1.28.0). Branch on `kind` instead of
|
|
335
|
+
* string-matching the ~20 raw `code`s the backend and transport can produce.
|
|
336
|
+
*
|
|
337
|
+
* Pairs with the hard guarantee this release introduces: **a failed auth
|
|
338
|
+
* attempt never changes the local session.** An offline sign-in, a mistyped
|
|
339
|
+
* OTP, a dismissed Game Center sheet and a 429 all leave `getUserSync()` and
|
|
340
|
+
* the stored refresh token exactly as they were.
|
|
341
|
+
*/
|
|
342
|
+
type AuthErrorKind = "network" | "server" | "rate_limited" | "invalid_credential" | "account_locked" | "credential_in_use" | "already_identified" | "cancelled" | "config" | "unknown";
|
|
313
343
|
declare class AuthError extends Error {
|
|
314
344
|
code: string;
|
|
315
|
-
|
|
345
|
+
/**
|
|
346
|
+
* Category of the failure — the field to branch on. See `AuthErrorKind`.
|
|
347
|
+
* Derived from `code` (+ HTTP status when known); never affects `code`,
|
|
348
|
+
* which stays exactly what shipped before 1.28.0.
|
|
349
|
+
*/
|
|
350
|
+
readonly kind: AuthErrorKind;
|
|
351
|
+
/** True when retrying the SAME input can plausibly succeed later. */
|
|
352
|
+
readonly retryable: boolean;
|
|
353
|
+
/**
|
|
354
|
+
* Seconds to wait before retrying, when the server told us (429 backoff, or
|
|
355
|
+
* an ACCOUNT_LOCKED cool-off). Absent means "no server-provided hint" — for
|
|
356
|
+
* `account_locked` specifically, absent means the lock does NOT expire on
|
|
357
|
+
* its own and needs support.
|
|
358
|
+
*/
|
|
359
|
+
readonly retryAfterSeconds?: number;
|
|
360
|
+
/** HTTP status when the failure came from a response (absent when offline). */
|
|
361
|
+
readonly status?: number;
|
|
362
|
+
constructor(code: string, message: string, opts?: {
|
|
363
|
+
status?: number;
|
|
364
|
+
retryAfterSeconds?: number;
|
|
365
|
+
kind?: AuthErrorKind;
|
|
366
|
+
});
|
|
316
367
|
}
|
|
317
368
|
interface AuthHooks {
|
|
318
369
|
/** Storage shared with the parent SDK so re-hydration is consistent. */
|
|
@@ -355,6 +406,40 @@ interface DeletionCancelledEvent {
|
|
|
355
406
|
/** Local clock at the time the SDK observed the event. */
|
|
356
407
|
at: Date;
|
|
357
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* Why the local session ended (1.28.0). Before this, a session that died in
|
|
411
|
+
* the background emitted NOTHING — an app could not tell a deliberate sign-out
|
|
412
|
+
* apart from an expired refresh token, and only discovered the difference when
|
|
413
|
+
* `getAccessToken()` started returning null.
|
|
414
|
+
*
|
|
415
|
+
* - `user` — the app called `signOut()`.
|
|
416
|
+
* - `session_expired` — the server rejected the stored refresh token (revoked,
|
|
417
|
+
* expired, rotated away). Transient network failures and
|
|
418
|
+
* rate limits do NOT produce this.
|
|
419
|
+
* - `account_deleted` — `deleteAccount()` succeeded.
|
|
420
|
+
*/
|
|
421
|
+
type AuthSignedOutReason = "user" | "session_expired" | "account_deleted";
|
|
422
|
+
/**
|
|
423
|
+
* A single auth-state transition (1.28.0). Subscribe with
|
|
424
|
+
* `onAuthStateChanged` — one stream covering everything that used to need
|
|
425
|
+
* separate listeners (or had none at all).
|
|
426
|
+
*/
|
|
427
|
+
type AuthStateChange = {
|
|
428
|
+
event: "signed_in";
|
|
429
|
+
user: AuthUser;
|
|
430
|
+
} | {
|
|
431
|
+
event: "signed_out";
|
|
432
|
+
user: null;
|
|
433
|
+
reason: AuthSignedOutReason;
|
|
434
|
+
} | {
|
|
435
|
+
event: "device_takeover";
|
|
436
|
+
user: AuthUser | null;
|
|
437
|
+
retiredAnonUserId: string;
|
|
438
|
+
} | {
|
|
439
|
+
event: "deletion_cancelled";
|
|
440
|
+
user: AuthUser | null;
|
|
441
|
+
};
|
|
442
|
+
type AuthStateListener = (change: AuthStateChange) => void;
|
|
358
443
|
declare class Auth {
|
|
359
444
|
private hooks;
|
|
360
445
|
private user;
|
|
@@ -377,6 +462,7 @@ declare class Auth {
|
|
|
377
462
|
private refreshCooldownUntil;
|
|
378
463
|
private takeoverListeners;
|
|
379
464
|
private deletionCancelledListeners;
|
|
465
|
+
private authStateListeners;
|
|
380
466
|
private lastDeletionCancelled;
|
|
381
467
|
private lastTakeover;
|
|
382
468
|
/**
|
|
@@ -402,6 +488,11 @@ declare class Auth {
|
|
|
402
488
|
*/
|
|
403
489
|
private readyPromise;
|
|
404
490
|
private resolveReady;
|
|
491
|
+
/**
|
|
492
|
+
* True once `hydrate()` has run. Distinguishes "no session restored YET"
|
|
493
|
+
* from "genuinely signed out" for `onAuthStateChanged`'s replay-on-subscribe.
|
|
494
|
+
*/
|
|
495
|
+
private hydrated;
|
|
405
496
|
constructor(hooks: AuthHooks);
|
|
406
497
|
/**
|
|
407
498
|
* Re-hydrate session from storage. Called by the parent SDK during
|
|
@@ -438,6 +529,31 @@ declare class Auth {
|
|
|
438
529
|
* + logged in dev mode. Auth state never depends on listener success.
|
|
439
530
|
*/
|
|
440
531
|
onDeviceTakeover(listener: (evt: DeviceTakeoverEvent) => void): () => void;
|
|
532
|
+
/**
|
|
533
|
+
* Subscribe to every auth-state transition (1.28.0) — the single stream that
|
|
534
|
+
* answers "what happened to my session?". Firebase `onAuthStateChanged` /
|
|
535
|
+
* Supabase `onAuthStateChange` parity.
|
|
536
|
+
*
|
|
537
|
+
* The reason it exists: a session that dies in the BACKGROUND (the server
|
|
538
|
+
* rejected the stored refresh token) used to emit no signal whatsoever, so
|
|
539
|
+
* an app could not distinguish it from a deliberate sign-out and only
|
|
540
|
+
* noticed when `getAccessToken()` began returning null. That transition is
|
|
541
|
+
* now `{ event: "signed_out", reason: "session_expired" }`.
|
|
542
|
+
*
|
|
543
|
+
* Fires the CURRENT state immediately on subscribe once `init()` has
|
|
544
|
+
* hydrated (a `signed_in` when a session was restored from disk — including
|
|
545
|
+
* offline, since hydrate is disk-only), so a subscriber never misses the
|
|
546
|
+
* state it started in. Nothing is emitted for a signed-out cold start.
|
|
547
|
+
*
|
|
548
|
+
* A failed sign-in emits NOTHING: the session is unchanged, so there is no
|
|
549
|
+
* transition to report — the rejected promise is the whole story.
|
|
550
|
+
*
|
|
551
|
+
* Listeners are best-effort; a throwing listener is swallowed (dev-logged)
|
|
552
|
+
* and never affects auth state. Returns an unsubscribe function.
|
|
553
|
+
*/
|
|
554
|
+
onAuthStateChanged(listener: AuthStateListener): () => void;
|
|
555
|
+
/** Fan a state change out to subscribers. Never throws. */
|
|
556
|
+
private emitAuthState;
|
|
441
557
|
/**
|
|
442
558
|
* Inspect the most recent device-takeover event observed by the
|
|
443
559
|
* SDK. Cleared on signOut. Returns null when no takeover has
|
|
@@ -495,9 +611,25 @@ declare class Auth {
|
|
|
495
611
|
getAccessToken(): Promise<string | null>;
|
|
496
612
|
/** Sync read — returns whatever's cached even if expired. Prefer the async variant. */
|
|
497
613
|
getAccessTokenSync(): string | null;
|
|
614
|
+
/**
|
|
615
|
+
* Mint (or return) an anonymous session.
|
|
616
|
+
*
|
|
617
|
+
* Reuses the existing anonymous user when there already is one — the same
|
|
618
|
+
* short-circuit Firebase's `signInAnonymously` performs. Without it, an app
|
|
619
|
+
* that calls this defensively on every cold launch (the most natural thing
|
|
620
|
+
* to write) mints a fresh `user_id` each time, and `persist()` overwrites
|
|
621
|
+
* the stored refresh token — the previous anonymous account's only durable
|
|
622
|
+
* handle — with no takeover, no webhook and no state event. That is the same
|
|
623
|
+
* silent progress loss as a failed sign-in wiping the session, except it
|
|
624
|
+
* happens on a *healthy* network.
|
|
625
|
+
*
|
|
626
|
+
* Pass `forceNew: true` to deliberately abandon the current anonymous
|
|
627
|
+
* session and mint a separate one.
|
|
628
|
+
*/
|
|
498
629
|
signInAnonymously(opts?: {
|
|
499
630
|
name?: string;
|
|
500
631
|
metadata?: Record<string, unknown>;
|
|
632
|
+
forceNew?: boolean;
|
|
501
633
|
}): Promise<AuthUser>;
|
|
502
634
|
/**
|
|
503
635
|
* Create an email/password account.
|
|
@@ -676,6 +808,17 @@ declare class Auth {
|
|
|
676
808
|
* user object + its storage copy are updated (no token/identity rotation).
|
|
677
809
|
*/
|
|
678
810
|
private updateLocalUser;
|
|
811
|
+
/**
|
|
812
|
+
* List the auth methods/identities linked to the current account (ADR-030
|
|
813
|
+
* read side, s58.270). Returns every social/gaming identity on this account
|
|
814
|
+
* plus a `hasPassword` flag — the cross-device / reinstall-durable source of
|
|
815
|
+
* truth for a "Connected: Game Center · Google" UI (an on-device tracker
|
|
816
|
+
* misses a link made on another device). Bearer-gated network read (NOT a
|
|
817
|
+
* cache read — `getUserSync()` only holds the primary `signupMethod`/
|
|
818
|
+
* `lastLoginMethod`). Resolves a FRESH access token first (mirrors
|
|
819
|
+
* `deleteAccount`). Firebase `user.providerData` / Supabase `user.identities`.
|
|
820
|
+
*/
|
|
821
|
+
listLinkedIdentities(): Promise<LinkedIdentitiesResponse>;
|
|
679
822
|
/**
|
|
680
823
|
* Shared gaming-identity sign-in. Mirrors `loginSocial`'s anon-takeover +
|
|
681
824
|
* ADR-025 link-in-place handling: reads the anon refresh BEFORE wiping local
|
|
@@ -860,6 +1003,29 @@ declare class Auth {
|
|
|
860
1003
|
}>;
|
|
861
1004
|
private callAuth;
|
|
862
1005
|
private persist;
|
|
1006
|
+
/**
|
|
1007
|
+
* Drop the local session.
|
|
1008
|
+
*
|
|
1009
|
+
* `reason` drives the `onAuthStateChanged` emission: a real sign-out reason
|
|
1010
|
+
* emits `signed_out`, while `"replaced"` (the internal pre-`persist()` clear
|
|
1011
|
+
* on a SUCCESSFUL sign-in) emits nothing — subscribers see one `signed_in`,
|
|
1012
|
+
* not a spurious logout/login pair.
|
|
1013
|
+
*
|
|
1014
|
+
* ⚠ Never call this before a fallible network request. A failed sign-in must
|
|
1015
|
+
* leave the caller exactly as it found them; for an anonymous session the
|
|
1016
|
+
* refresh token dropped here is the ONLY durable handle on the account, so a
|
|
1017
|
+
* pre-call wipe that is not followed by a successful `persist()` orphans the
|
|
1018
|
+
* account permanently (offline: deterministically). Capture the anon refresh
|
|
1019
|
+
* into a local, make the call, and wipe only after the response validates.
|
|
1020
|
+
*/
|
|
1021
|
+
/**
|
|
1022
|
+
* True when `sent` is still the refresh token on disk — i.e. nothing
|
|
1023
|
+
* replaced or cleared the session while a request carrying it was in
|
|
1024
|
+
* flight. Guards both directions of the refresh race: installing a rotated
|
|
1025
|
+
* token over a session that has since been signed out or replaced, and
|
|
1026
|
+
* wiping a fresh session because an OLD token was rejected.
|
|
1027
|
+
*/
|
|
1028
|
+
private tokenStillCurrent;
|
|
863
1029
|
private wipeLocalIdentity;
|
|
864
1030
|
}
|
|
865
1031
|
|
|
@@ -1642,6 +1808,12 @@ declare class SendoraSDK {
|
|
|
1642
1808
|
* Clear identity + rotate the anonymous id. Call on logout. Awaits
|
|
1643
1809
|
* the AsyncStorage writes so a caller who kills the app immediately
|
|
1644
1810
|
* after doesn't leave stale identity on disk.
|
|
1811
|
+
*
|
|
1812
|
+
* Signs out through `auth` rather than just clearing storage: `clearAll()`
|
|
1813
|
+
* removes the auth keys too, so doing it directly left `Auth` holding an
|
|
1814
|
+
* in-memory user and reporting itself signed in while its refresh token was
|
|
1815
|
+
* already gone — with the server-side session never revoked and no
|
|
1816
|
+
* `onAuthStateChanged` emission telling the app the session had ended.
|
|
1645
1817
|
*/
|
|
1646
1818
|
reset(): Promise<void>;
|
|
1647
1819
|
/** Tear down auto-track listeners. Call in test harnesses. */
|
|
@@ -1651,4 +1823,4 @@ declare class SendoraSDK {
|
|
|
1651
1823
|
}
|
|
1652
1824
|
declare const SendoraCloud: SendoraSDK;
|
|
1653
1825
|
|
|
1654
|
-
export { AlreadyIdentifiedError, type AttachLinkingApiOptions, Auth, AuthError, type AuthTokens, type AuthUser, CredentialInUseError, EmailAlreadyTakenError, type IdentifyTraits, type LinkCreateInput, type LinkCreateResult, type LinkData, LinkError, type LinkErrorCode, type LinkMatchInput, type LinkOpenedEvent, type LinkOpenedHandler, type LinkStats, Links, type PushTokenReceipt, type PushTokenRegistration, SendoraCloud, type SendoraConfig, type TrackProperties, computeDeviceFingerprint, SendoraCloud as default, extractShortcodeFromUrl, getPlayInstallReferrer };
|
|
1826
|
+
export { AlreadyIdentifiedError, type AttachLinkingApiOptions, Auth, AuthError, type AuthErrorKind, type AuthSignedOutReason, type AuthStateChange, type AuthTokens, type AuthUser, CredentialInUseError, type DeletionCancelledEvent, type DeviceTakeoverEvent, EmailAlreadyTakenError, type IdentifyTraits, type LinkCreateInput, type LinkCreateResult, type LinkData, LinkError, type LinkErrorCode, type LinkMatchInput, type LinkOpenedEvent, type LinkOpenedHandler, type LinkStats, type LinkedIdentitiesResponse, type LinkedIdentity, Links, type PushTokenReceipt, type PushTokenRegistration, SendoraCloud, type SendoraConfig, type TrackProperties, computeDeviceFingerprint, SendoraCloud as default, extractShortcodeFromUrl, getPlayInstallReferrer };
|
package/dist/index.d.ts
CHANGED
|
@@ -221,10 +221,19 @@ declare class Storage {
|
|
|
221
221
|
* user_id is preserved on success. Throws
|
|
222
222
|
* EmailAlreadyTakenError on 409. Otherwise calls
|
|
223
223
|
* /auth-service/signup as a fresh account.
|
|
224
|
-
* signIn(email, password) — POST /auth-service/login.
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
224
|
+
* signIn(email, password) — POST /auth-service/login.
|
|
225
|
+
*
|
|
226
|
+
* HARD INVARIANT — a failed auth attempt leaves the caller exactly as
|
|
227
|
+
* it found them. Every credentialed path captures the anonymous
|
|
228
|
+
* refresh token into a local (for the device-takeover hint), performs
|
|
229
|
+
* its network call, validates the response, and only THEN clears the
|
|
230
|
+
* old identity and persists the new one. Clearing first would drop the
|
|
231
|
+
* refresh token — for an anonymous user the ONLY durable handle on the
|
|
232
|
+
* account — so any failure after that point (offline, 5xx, a mistyped
|
|
233
|
+
* OTP, a cancelled IdP callback) would orphan the account permanently.
|
|
234
|
+
* A track() fired during the round-trip therefore attaches to the
|
|
235
|
+
* still-anonymous identity, which is correct: the user is not
|
|
236
|
+
* identified yet, and the backend reattributes it via the takeover.
|
|
228
237
|
*
|
|
229
238
|
* All public auth ops are serialized through a single-flight chain
|
|
230
239
|
* so a UI double-submit can't mint two anonymous users or interleave
|
|
@@ -310,9 +319,51 @@ declare class AlreadyIdentifiedError extends Error {
|
|
|
310
319
|
declare class CredentialInUseError extends Error {
|
|
311
320
|
constructor(message?: string);
|
|
312
321
|
}
|
|
322
|
+
/** One credential linked to an account (ADR-030 read side, s58.270). */
|
|
323
|
+
type LinkedIdentity = {
|
|
324
|
+
provider: string;
|
|
325
|
+
email: string | null;
|
|
326
|
+
linkedAt: string;
|
|
327
|
+
};
|
|
328
|
+
/** Response of `auth.listLinkedIdentities()` — the full connected-account set. */
|
|
329
|
+
type LinkedIdentitiesResponse = {
|
|
330
|
+
identities: LinkedIdentity[];
|
|
331
|
+
hasPassword: boolean;
|
|
332
|
+
};
|
|
333
|
+
/**
|
|
334
|
+
* Closed set of auth failure categories (1.28.0). Branch on `kind` instead of
|
|
335
|
+
* string-matching the ~20 raw `code`s the backend and transport can produce.
|
|
336
|
+
*
|
|
337
|
+
* Pairs with the hard guarantee this release introduces: **a failed auth
|
|
338
|
+
* attempt never changes the local session.** An offline sign-in, a mistyped
|
|
339
|
+
* OTP, a dismissed Game Center sheet and a 429 all leave `getUserSync()` and
|
|
340
|
+
* the stored refresh token exactly as they were.
|
|
341
|
+
*/
|
|
342
|
+
type AuthErrorKind = "network" | "server" | "rate_limited" | "invalid_credential" | "account_locked" | "credential_in_use" | "already_identified" | "cancelled" | "config" | "unknown";
|
|
313
343
|
declare class AuthError extends Error {
|
|
314
344
|
code: string;
|
|
315
|
-
|
|
345
|
+
/**
|
|
346
|
+
* Category of the failure — the field to branch on. See `AuthErrorKind`.
|
|
347
|
+
* Derived from `code` (+ HTTP status when known); never affects `code`,
|
|
348
|
+
* which stays exactly what shipped before 1.28.0.
|
|
349
|
+
*/
|
|
350
|
+
readonly kind: AuthErrorKind;
|
|
351
|
+
/** True when retrying the SAME input can plausibly succeed later. */
|
|
352
|
+
readonly retryable: boolean;
|
|
353
|
+
/**
|
|
354
|
+
* Seconds to wait before retrying, when the server told us (429 backoff, or
|
|
355
|
+
* an ACCOUNT_LOCKED cool-off). Absent means "no server-provided hint" — for
|
|
356
|
+
* `account_locked` specifically, absent means the lock does NOT expire on
|
|
357
|
+
* its own and needs support.
|
|
358
|
+
*/
|
|
359
|
+
readonly retryAfterSeconds?: number;
|
|
360
|
+
/** HTTP status when the failure came from a response (absent when offline). */
|
|
361
|
+
readonly status?: number;
|
|
362
|
+
constructor(code: string, message: string, opts?: {
|
|
363
|
+
status?: number;
|
|
364
|
+
retryAfterSeconds?: number;
|
|
365
|
+
kind?: AuthErrorKind;
|
|
366
|
+
});
|
|
316
367
|
}
|
|
317
368
|
interface AuthHooks {
|
|
318
369
|
/** Storage shared with the parent SDK so re-hydration is consistent. */
|
|
@@ -355,6 +406,40 @@ interface DeletionCancelledEvent {
|
|
|
355
406
|
/** Local clock at the time the SDK observed the event. */
|
|
356
407
|
at: Date;
|
|
357
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* Why the local session ended (1.28.0). Before this, a session that died in
|
|
411
|
+
* the background emitted NOTHING — an app could not tell a deliberate sign-out
|
|
412
|
+
* apart from an expired refresh token, and only discovered the difference when
|
|
413
|
+
* `getAccessToken()` started returning null.
|
|
414
|
+
*
|
|
415
|
+
* - `user` — the app called `signOut()`.
|
|
416
|
+
* - `session_expired` — the server rejected the stored refresh token (revoked,
|
|
417
|
+
* expired, rotated away). Transient network failures and
|
|
418
|
+
* rate limits do NOT produce this.
|
|
419
|
+
* - `account_deleted` — `deleteAccount()` succeeded.
|
|
420
|
+
*/
|
|
421
|
+
type AuthSignedOutReason = "user" | "session_expired" | "account_deleted";
|
|
422
|
+
/**
|
|
423
|
+
* A single auth-state transition (1.28.0). Subscribe with
|
|
424
|
+
* `onAuthStateChanged` — one stream covering everything that used to need
|
|
425
|
+
* separate listeners (or had none at all).
|
|
426
|
+
*/
|
|
427
|
+
type AuthStateChange = {
|
|
428
|
+
event: "signed_in";
|
|
429
|
+
user: AuthUser;
|
|
430
|
+
} | {
|
|
431
|
+
event: "signed_out";
|
|
432
|
+
user: null;
|
|
433
|
+
reason: AuthSignedOutReason;
|
|
434
|
+
} | {
|
|
435
|
+
event: "device_takeover";
|
|
436
|
+
user: AuthUser | null;
|
|
437
|
+
retiredAnonUserId: string;
|
|
438
|
+
} | {
|
|
439
|
+
event: "deletion_cancelled";
|
|
440
|
+
user: AuthUser | null;
|
|
441
|
+
};
|
|
442
|
+
type AuthStateListener = (change: AuthStateChange) => void;
|
|
358
443
|
declare class Auth {
|
|
359
444
|
private hooks;
|
|
360
445
|
private user;
|
|
@@ -377,6 +462,7 @@ declare class Auth {
|
|
|
377
462
|
private refreshCooldownUntil;
|
|
378
463
|
private takeoverListeners;
|
|
379
464
|
private deletionCancelledListeners;
|
|
465
|
+
private authStateListeners;
|
|
380
466
|
private lastDeletionCancelled;
|
|
381
467
|
private lastTakeover;
|
|
382
468
|
/**
|
|
@@ -402,6 +488,11 @@ declare class Auth {
|
|
|
402
488
|
*/
|
|
403
489
|
private readyPromise;
|
|
404
490
|
private resolveReady;
|
|
491
|
+
/**
|
|
492
|
+
* True once `hydrate()` has run. Distinguishes "no session restored YET"
|
|
493
|
+
* from "genuinely signed out" for `onAuthStateChanged`'s replay-on-subscribe.
|
|
494
|
+
*/
|
|
495
|
+
private hydrated;
|
|
405
496
|
constructor(hooks: AuthHooks);
|
|
406
497
|
/**
|
|
407
498
|
* Re-hydrate session from storage. Called by the parent SDK during
|
|
@@ -438,6 +529,31 @@ declare class Auth {
|
|
|
438
529
|
* + logged in dev mode. Auth state never depends on listener success.
|
|
439
530
|
*/
|
|
440
531
|
onDeviceTakeover(listener: (evt: DeviceTakeoverEvent) => void): () => void;
|
|
532
|
+
/**
|
|
533
|
+
* Subscribe to every auth-state transition (1.28.0) — the single stream that
|
|
534
|
+
* answers "what happened to my session?". Firebase `onAuthStateChanged` /
|
|
535
|
+
* Supabase `onAuthStateChange` parity.
|
|
536
|
+
*
|
|
537
|
+
* The reason it exists: a session that dies in the BACKGROUND (the server
|
|
538
|
+
* rejected the stored refresh token) used to emit no signal whatsoever, so
|
|
539
|
+
* an app could not distinguish it from a deliberate sign-out and only
|
|
540
|
+
* noticed when `getAccessToken()` began returning null. That transition is
|
|
541
|
+
* now `{ event: "signed_out", reason: "session_expired" }`.
|
|
542
|
+
*
|
|
543
|
+
* Fires the CURRENT state immediately on subscribe once `init()` has
|
|
544
|
+
* hydrated (a `signed_in` when a session was restored from disk — including
|
|
545
|
+
* offline, since hydrate is disk-only), so a subscriber never misses the
|
|
546
|
+
* state it started in. Nothing is emitted for a signed-out cold start.
|
|
547
|
+
*
|
|
548
|
+
* A failed sign-in emits NOTHING: the session is unchanged, so there is no
|
|
549
|
+
* transition to report — the rejected promise is the whole story.
|
|
550
|
+
*
|
|
551
|
+
* Listeners are best-effort; a throwing listener is swallowed (dev-logged)
|
|
552
|
+
* and never affects auth state. Returns an unsubscribe function.
|
|
553
|
+
*/
|
|
554
|
+
onAuthStateChanged(listener: AuthStateListener): () => void;
|
|
555
|
+
/** Fan a state change out to subscribers. Never throws. */
|
|
556
|
+
private emitAuthState;
|
|
441
557
|
/**
|
|
442
558
|
* Inspect the most recent device-takeover event observed by the
|
|
443
559
|
* SDK. Cleared on signOut. Returns null when no takeover has
|
|
@@ -495,9 +611,25 @@ declare class Auth {
|
|
|
495
611
|
getAccessToken(): Promise<string | null>;
|
|
496
612
|
/** Sync read — returns whatever's cached even if expired. Prefer the async variant. */
|
|
497
613
|
getAccessTokenSync(): string | null;
|
|
614
|
+
/**
|
|
615
|
+
* Mint (or return) an anonymous session.
|
|
616
|
+
*
|
|
617
|
+
* Reuses the existing anonymous user when there already is one — the same
|
|
618
|
+
* short-circuit Firebase's `signInAnonymously` performs. Without it, an app
|
|
619
|
+
* that calls this defensively on every cold launch (the most natural thing
|
|
620
|
+
* to write) mints a fresh `user_id` each time, and `persist()` overwrites
|
|
621
|
+
* the stored refresh token — the previous anonymous account's only durable
|
|
622
|
+
* handle — with no takeover, no webhook and no state event. That is the same
|
|
623
|
+
* silent progress loss as a failed sign-in wiping the session, except it
|
|
624
|
+
* happens on a *healthy* network.
|
|
625
|
+
*
|
|
626
|
+
* Pass `forceNew: true` to deliberately abandon the current anonymous
|
|
627
|
+
* session and mint a separate one.
|
|
628
|
+
*/
|
|
498
629
|
signInAnonymously(opts?: {
|
|
499
630
|
name?: string;
|
|
500
631
|
metadata?: Record<string, unknown>;
|
|
632
|
+
forceNew?: boolean;
|
|
501
633
|
}): Promise<AuthUser>;
|
|
502
634
|
/**
|
|
503
635
|
* Create an email/password account.
|
|
@@ -676,6 +808,17 @@ declare class Auth {
|
|
|
676
808
|
* user object + its storage copy are updated (no token/identity rotation).
|
|
677
809
|
*/
|
|
678
810
|
private updateLocalUser;
|
|
811
|
+
/**
|
|
812
|
+
* List the auth methods/identities linked to the current account (ADR-030
|
|
813
|
+
* read side, s58.270). Returns every social/gaming identity on this account
|
|
814
|
+
* plus a `hasPassword` flag — the cross-device / reinstall-durable source of
|
|
815
|
+
* truth for a "Connected: Game Center · Google" UI (an on-device tracker
|
|
816
|
+
* misses a link made on another device). Bearer-gated network read (NOT a
|
|
817
|
+
* cache read — `getUserSync()` only holds the primary `signupMethod`/
|
|
818
|
+
* `lastLoginMethod`). Resolves a FRESH access token first (mirrors
|
|
819
|
+
* `deleteAccount`). Firebase `user.providerData` / Supabase `user.identities`.
|
|
820
|
+
*/
|
|
821
|
+
listLinkedIdentities(): Promise<LinkedIdentitiesResponse>;
|
|
679
822
|
/**
|
|
680
823
|
* Shared gaming-identity sign-in. Mirrors `loginSocial`'s anon-takeover +
|
|
681
824
|
* ADR-025 link-in-place handling: reads the anon refresh BEFORE wiping local
|
|
@@ -860,6 +1003,29 @@ declare class Auth {
|
|
|
860
1003
|
}>;
|
|
861
1004
|
private callAuth;
|
|
862
1005
|
private persist;
|
|
1006
|
+
/**
|
|
1007
|
+
* Drop the local session.
|
|
1008
|
+
*
|
|
1009
|
+
* `reason` drives the `onAuthStateChanged` emission: a real sign-out reason
|
|
1010
|
+
* emits `signed_out`, while `"replaced"` (the internal pre-`persist()` clear
|
|
1011
|
+
* on a SUCCESSFUL sign-in) emits nothing — subscribers see one `signed_in`,
|
|
1012
|
+
* not a spurious logout/login pair.
|
|
1013
|
+
*
|
|
1014
|
+
* ⚠ Never call this before a fallible network request. A failed sign-in must
|
|
1015
|
+
* leave the caller exactly as it found them; for an anonymous session the
|
|
1016
|
+
* refresh token dropped here is the ONLY durable handle on the account, so a
|
|
1017
|
+
* pre-call wipe that is not followed by a successful `persist()` orphans the
|
|
1018
|
+
* account permanently (offline: deterministically). Capture the anon refresh
|
|
1019
|
+
* into a local, make the call, and wipe only after the response validates.
|
|
1020
|
+
*/
|
|
1021
|
+
/**
|
|
1022
|
+
* True when `sent` is still the refresh token on disk — i.e. nothing
|
|
1023
|
+
* replaced or cleared the session while a request carrying it was in
|
|
1024
|
+
* flight. Guards both directions of the refresh race: installing a rotated
|
|
1025
|
+
* token over a session that has since been signed out or replaced, and
|
|
1026
|
+
* wiping a fresh session because an OLD token was rejected.
|
|
1027
|
+
*/
|
|
1028
|
+
private tokenStillCurrent;
|
|
863
1029
|
private wipeLocalIdentity;
|
|
864
1030
|
}
|
|
865
1031
|
|
|
@@ -1642,6 +1808,12 @@ declare class SendoraSDK {
|
|
|
1642
1808
|
* Clear identity + rotate the anonymous id. Call on logout. Awaits
|
|
1643
1809
|
* the AsyncStorage writes so a caller who kills the app immediately
|
|
1644
1810
|
* after doesn't leave stale identity on disk.
|
|
1811
|
+
*
|
|
1812
|
+
* Signs out through `auth` rather than just clearing storage: `clearAll()`
|
|
1813
|
+
* removes the auth keys too, so doing it directly left `Auth` holding an
|
|
1814
|
+
* in-memory user and reporting itself signed in while its refresh token was
|
|
1815
|
+
* already gone — with the server-side session never revoked and no
|
|
1816
|
+
* `onAuthStateChanged` emission telling the app the session had ended.
|
|
1645
1817
|
*/
|
|
1646
1818
|
reset(): Promise<void>;
|
|
1647
1819
|
/** Tear down auto-track listeners. Call in test harnesses. */
|
|
@@ -1651,4 +1823,4 @@ declare class SendoraSDK {
|
|
|
1651
1823
|
}
|
|
1652
1824
|
declare const SendoraCloud: SendoraSDK;
|
|
1653
1825
|
|
|
1654
|
-
export { AlreadyIdentifiedError, type AttachLinkingApiOptions, Auth, AuthError, type AuthTokens, type AuthUser, CredentialInUseError, EmailAlreadyTakenError, type IdentifyTraits, type LinkCreateInput, type LinkCreateResult, type LinkData, LinkError, type LinkErrorCode, type LinkMatchInput, type LinkOpenedEvent, type LinkOpenedHandler, type LinkStats, Links, type PushTokenReceipt, type PushTokenRegistration, SendoraCloud, type SendoraConfig, type TrackProperties, computeDeviceFingerprint, SendoraCloud as default, extractShortcodeFromUrl, getPlayInstallReferrer };
|
|
1826
|
+
export { AlreadyIdentifiedError, type AttachLinkingApiOptions, Auth, AuthError, type AuthErrorKind, type AuthSignedOutReason, type AuthStateChange, type AuthTokens, type AuthUser, CredentialInUseError, type DeletionCancelledEvent, type DeviceTakeoverEvent, EmailAlreadyTakenError, type IdentifyTraits, type LinkCreateInput, type LinkCreateResult, type LinkData, LinkError, type LinkErrorCode, type LinkMatchInput, type LinkOpenedEvent, type LinkOpenedHandler, type LinkStats, type LinkedIdentitiesResponse, type LinkedIdentity, Links, type PushTokenReceipt, type PushTokenRegistration, SendoraCloud, type SendoraConfig, type TrackProperties, computeDeviceFingerprint, SendoraCloud as default, extractShortcodeFromUrl, getPlayInstallReferrer };
|