@sendoracloud/sdk-react-native 1.24.0 → 1.26.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 +144 -3
- package/dist/index.d.cts +101 -1
- package/dist/index.d.ts +101 -1
- package/dist/index.js +142 -3
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -30,8 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
AlreadyIdentifiedError: () => AlreadyIdentifiedError,
|
|
33
34
|
Auth: () => Auth,
|
|
34
35
|
AuthError: () => AuthError,
|
|
36
|
+
CredentialInUseError: () => CredentialInUseError,
|
|
35
37
|
EmailAlreadyTakenError: () => EmailAlreadyTakenError,
|
|
36
38
|
LinkError: () => LinkError,
|
|
37
39
|
Links: () => Links,
|
|
@@ -200,7 +202,7 @@ var Storage = class {
|
|
|
200
202
|
// package.json
|
|
201
203
|
var package_default = {
|
|
202
204
|
name: "@sendoracloud/sdk-react-native",
|
|
203
|
-
version: "1.
|
|
205
|
+
version: "1.26.0",
|
|
204
206
|
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`).",
|
|
205
207
|
type: "module",
|
|
206
208
|
main: "./dist/index.cjs",
|
|
@@ -256,7 +258,7 @@ var package_default = {
|
|
|
256
258
|
scripts: {
|
|
257
259
|
build: "tsup src/index.ts src/contact-widget.tsx --format esm,cjs --dts --clean --external react --external react-native --external react-native-webview",
|
|
258
260
|
typecheck: "tsc --noEmit",
|
|
259
|
-
test: "tsx test/secure-storage.test.ts && tsx test/integration-storage.test.ts && tsx test/plugin.test.ts && tsx test/game-auth.test.ts"
|
|
261
|
+
test: "tsx test/secure-storage.test.ts && tsx test/integration-storage.test.ts && tsx test/plugin.test.ts && tsx test/game-auth.test.ts && tsx test/link.test.ts"
|
|
260
262
|
},
|
|
261
263
|
devDependencies: {
|
|
262
264
|
"@types/react": "^19.2.16",
|
|
@@ -426,6 +428,18 @@ var EmailAlreadyTakenError = class extends Error {
|
|
|
426
428
|
this.name = "EmailAlreadyTakenError";
|
|
427
429
|
}
|
|
428
430
|
};
|
|
431
|
+
var AlreadyIdentifiedError = class extends Error {
|
|
432
|
+
constructor(message = "Already signed in with an identity; use link*() to add a credential, or sign out first") {
|
|
433
|
+
super(message);
|
|
434
|
+
this.name = "AlreadyIdentifiedError";
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
var CredentialInUseError = class extends Error {
|
|
438
|
+
constructor(message = "This credential is already linked to a different account") {
|
|
439
|
+
super(message);
|
|
440
|
+
this.name = "CredentialInUseError";
|
|
441
|
+
}
|
|
442
|
+
};
|
|
429
443
|
var AuthError = class extends Error {
|
|
430
444
|
constructor(code, message) {
|
|
431
445
|
super(message);
|
|
@@ -496,6 +510,8 @@ var Auth = class {
|
|
|
496
510
|
this.refreshFailureCount = 0;
|
|
497
511
|
this.refreshCooldownUntil = 0;
|
|
498
512
|
this.takeoverListeners = /* @__PURE__ */ new Set();
|
|
513
|
+
this.deletionCancelledListeners = /* @__PURE__ */ new Set();
|
|
514
|
+
this.lastDeletionCancelled = null;
|
|
499
515
|
this.lastTakeover = null;
|
|
500
516
|
/**
|
|
501
517
|
* Anon refresh token captured at `signIn()` time, stashed keyed to the
|
|
@@ -608,6 +624,36 @@ var Auth = class {
|
|
|
608
624
|
}
|
|
609
625
|
}
|
|
610
626
|
}
|
|
627
|
+
/**
|
|
628
|
+
* Subscribe to deletion-cancelled events (s58.269): fires when a sign-in
|
|
629
|
+
* cancelled a pending self-service account deletion within its grace window
|
|
630
|
+
* (the account is restored, same user_id). Use it to show "your account
|
|
631
|
+
* deletion was cancelled" + reconcile local state. Fires from signIn /
|
|
632
|
+
* loginSocial / signInWithGameCenter / signInWithPlayGames. Returns an
|
|
633
|
+
* unsubscribe function; listener errors are swallowed (best-effort).
|
|
634
|
+
*/
|
|
635
|
+
onDeletionCancelled(listener) {
|
|
636
|
+
this.deletionCancelledListeners.add(listener);
|
|
637
|
+
return () => {
|
|
638
|
+
this.deletionCancelledListeners.delete(listener);
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
/** The most recent deletion-cancelled event this session, or null. Cleared on signOut. */
|
|
642
|
+
getLastDeletionCancelled() {
|
|
643
|
+
return this.lastDeletionCancelled;
|
|
644
|
+
}
|
|
645
|
+
/** Internal — fan out deletion-cancelled to subscribers + cache the latest. */
|
|
646
|
+
fireDeletionCancelled(userId) {
|
|
647
|
+
const evt = { userId, at: /* @__PURE__ */ new Date() };
|
|
648
|
+
this.lastDeletionCancelled = evt;
|
|
649
|
+
for (const fn of this.deletionCancelledListeners) {
|
|
650
|
+
try {
|
|
651
|
+
fn(evt);
|
|
652
|
+
} catch (err) {
|
|
653
|
+
if (this.hooks.debug) console.warn("[sendoracloud] onDeletionCancelled listener threw", err);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
611
657
|
/**
|
|
612
658
|
* Returns a VALID (non-expired) access token, or null.
|
|
613
659
|
*
|
|
@@ -696,10 +742,13 @@ var Auth = class {
|
|
|
696
742
|
this.persist(data);
|
|
697
743
|
return data.user;
|
|
698
744
|
} catch (err) {
|
|
745
|
+
if (err instanceof AuthError && (err.code === "NOT_ANONYMOUS" || err.code === "FORBIDDEN_NON_ANONYMOUS")) {
|
|
746
|
+
throw new AlreadyIdentifiedError(err.message);
|
|
747
|
+
}
|
|
699
748
|
if (err instanceof AuthError && (err.code === "CONFLICT" || err.code === "EMAIL_ALREADY_TAKEN")) {
|
|
700
749
|
throw new EmailAlreadyTakenError(err.message);
|
|
701
750
|
}
|
|
702
|
-
if (err instanceof AuthError && (err.code === "
|
|
751
|
+
if (err instanceof AuthError && (err.code === "INVALID_REFRESH" || err.code === "UNAUTHORIZED" || err.code === "REFRESH_EXPIRED")) {
|
|
703
752
|
await this.wipeLocalIdentity();
|
|
704
753
|
} else {
|
|
705
754
|
throw err;
|
|
@@ -849,6 +898,92 @@ var Auth = class {
|
|
|
849
898
|
link
|
|
850
899
|
);
|
|
851
900
|
}
|
|
901
|
+
// ============================================================
|
|
902
|
+
// IDENTITY LINKING (ADR-030) — attach a SECOND credential to the
|
|
903
|
+
// CURRENT signed-in account, preserving the same user id (sub).
|
|
904
|
+
// ============================================================
|
|
905
|
+
//
|
|
906
|
+
// Unlike signUp() / loginSocial({link}) — which only preserve the sub from
|
|
907
|
+
// an ANONYMOUS session — these operate on an ALREADY-IDENTIFIED account.
|
|
908
|
+
// No token rotation (the session is unchanged); they refresh the cached user
|
|
909
|
+
// object in place. Require a signed-in user (throws NOT_SIGNED_IN otherwise).
|
|
910
|
+
//
|
|
911
|
+
// Primary use: ONE account across platforms. A Game Center player on iOS
|
|
912
|
+
// links email/Google; on Android they sign in with that same credential and
|
|
913
|
+
// land on the SAME sub, progress intact. Collision (the credential already
|
|
914
|
+
// belongs to another account) → CredentialInUseError (never merges).
|
|
915
|
+
/** Link email + password to the current account (sub preserved). */
|
|
916
|
+
linkEmailPassword(email, password) {
|
|
917
|
+
return this.linkCredential("/api/v1/auth-service/me/link/email", { email, password });
|
|
918
|
+
}
|
|
919
|
+
/** Link an OAuth social identity (verified idToken or code) to the current account. */
|
|
920
|
+
linkSocial(input) {
|
|
921
|
+
return this.linkCredential("/api/v1/auth-service/me/link/social", input);
|
|
922
|
+
}
|
|
923
|
+
/** Convenience: link a Google identity (native idToken, or web code+redirectUri). */
|
|
924
|
+
linkGoogle(input) {
|
|
925
|
+
return this.linkSocial({ provider: "google", ...input });
|
|
926
|
+
}
|
|
927
|
+
/** Convenience: link an Apple identity (native idToken, or web code+redirectUri). */
|
|
928
|
+
linkApple(input) {
|
|
929
|
+
return this.linkSocial({ provider: "apple", ...input });
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Link an Apple Game Center identity to the current account. No-arg drives the
|
|
933
|
+
* native GameKit fetch (needs `@sendoracloud/sdk-react-native-gaming`); or pass
|
|
934
|
+
* an explicit payload obtained from your own GameKit bridge.
|
|
935
|
+
*/
|
|
936
|
+
async linkGameCenter(payload) {
|
|
937
|
+
const body = payload ?? await fetchGameCenterPayloadNative();
|
|
938
|
+
return this.linkCredential("/api/v1/auth-service/me/link/game-center", { ...body });
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Link a Google Play Games identity to the current account. No-arg drives the
|
|
942
|
+
* native Play Games fetch; or pass an explicit `{ serverAuthCode }`.
|
|
943
|
+
*/
|
|
944
|
+
async linkPlayGames(payload) {
|
|
945
|
+
const body = payload ?? await fetchPlayGamesAuthCodeNative();
|
|
946
|
+
return this.linkCredential("/api/v1/auth-service/me/link/play-games", { ...body });
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Shared link executor. POSTs the verified credential with the caller's
|
|
950
|
+
* end-user Bearer token, then refreshes the cached user object IN PLACE (no
|
|
951
|
+
* token rotation — the sub is unchanged). Deliberately NOT wrapped in
|
|
952
|
+
* serialize(): getAccessToken() serializes internally, so re-entering would
|
|
953
|
+
* deadlock — same non-serialized posture as deleteAccount().
|
|
954
|
+
*/
|
|
955
|
+
async linkCredential(path, body) {
|
|
956
|
+
const token = await this.getAccessToken();
|
|
957
|
+
if (!token) throw new AuthError("NOT_SIGNED_IN", "Sign in before linking a credential");
|
|
958
|
+
const res = await post(
|
|
959
|
+
{ apiUrl: this.hooks.apiUrl, publicKey: this.hooks.publicKey, debug: this.hooks.debug },
|
|
960
|
+
path,
|
|
961
|
+
body,
|
|
962
|
+
{ Authorization: `Bearer ${token}` }
|
|
963
|
+
);
|
|
964
|
+
if (!res) throw new AuthError("NETWORK_ERROR", "Network request failed");
|
|
965
|
+
let parsed;
|
|
966
|
+
try {
|
|
967
|
+
parsed = await res.json();
|
|
968
|
+
} catch {
|
|
969
|
+
throw new AuthError("PARSE_ERROR", `HTTP ${res.status}`);
|
|
970
|
+
}
|
|
971
|
+
if (!res.ok || !parsed.success || !parsed.data) {
|
|
972
|
+
const code = parsed.error?.code ?? `HTTP_${res.status}`;
|
|
973
|
+
if (code === "CREDENTIAL_IN_USE") throw new CredentialInUseError(parsed.error?.message);
|
|
974
|
+
throw new AuthError(code, parsed.error?.message ?? "Failed to link credential");
|
|
975
|
+
}
|
|
976
|
+
if (parsed.data.user) this.updateLocalUser(parsed.data.user);
|
|
977
|
+
return parsed.data.user ?? this.user;
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Refresh the cached user after a link — the sub is unchanged, so only the
|
|
981
|
+
* user object + its storage copy are updated (no token/identity rotation).
|
|
982
|
+
*/
|
|
983
|
+
updateLocalUser(user) {
|
|
984
|
+
this.user = user;
|
|
985
|
+
this.hooks.storage.set(USER_KEY, JSON.stringify(user));
|
|
986
|
+
}
|
|
852
987
|
/**
|
|
853
988
|
* Shared gaming-identity sign-in. Mirrors `loginSocial`'s anon-takeover +
|
|
854
989
|
* ADR-025 link-in-place handling: reads the anon refresh BEFORE wiping local
|
|
@@ -1496,12 +1631,16 @@ var Auth = class {
|
|
|
1496
1631
|
if (data.retiredAnonUserId) {
|
|
1497
1632
|
this.fireDeviceTakeover(data.retiredAnonUserId, data.user.id);
|
|
1498
1633
|
}
|
|
1634
|
+
if (data.reactivatedFromDeletion) {
|
|
1635
|
+
this.fireDeletionCancelled(data.user.id);
|
|
1636
|
+
}
|
|
1499
1637
|
}
|
|
1500
1638
|
async wipeLocalIdentity() {
|
|
1501
1639
|
this.user = null;
|
|
1502
1640
|
this.accessToken = null;
|
|
1503
1641
|
this.accessExpiresAt = 0;
|
|
1504
1642
|
this.lastTakeover = null;
|
|
1643
|
+
this.lastDeletionCancelled = null;
|
|
1505
1644
|
this.pendingAnonTakeover = null;
|
|
1506
1645
|
this.hooks.storage.remove(USER_KEY);
|
|
1507
1646
|
this.hooks.storage.remove(TOKEN_KEY);
|
|
@@ -3214,8 +3353,10 @@ var SendoraCloud = new SendoraSDK();
|
|
|
3214
3353
|
var index_default = SendoraCloud;
|
|
3215
3354
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3216
3355
|
0 && (module.exports = {
|
|
3356
|
+
AlreadyIdentifiedError,
|
|
3217
3357
|
Auth,
|
|
3218
3358
|
AuthError,
|
|
3359
|
+
CredentialInUseError,
|
|
3219
3360
|
EmailAlreadyTakenError,
|
|
3220
3361
|
LinkError,
|
|
3221
3362
|
Links,
|
package/dist/index.d.cts
CHANGED
|
@@ -292,6 +292,24 @@ interface GameCenterPayload {
|
|
|
292
292
|
declare class EmailAlreadyTakenError extends Error {
|
|
293
293
|
constructor(message?: string);
|
|
294
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* `signUp()` was called on a session that is ALREADY signed in with an
|
|
297
|
+
* identity (not anonymous). Creating a second account would orphan the
|
|
298
|
+
* current one — use `linkEmailPassword()` / `linkGoogle()` / … (ADR-030) to
|
|
299
|
+
* add the credential to THIS account, or sign out first. Distinct from
|
|
300
|
+
* `EmailAlreadyTakenError`: the email isn't the problem, the live session is.
|
|
301
|
+
*/
|
|
302
|
+
declare class AlreadyIdentifiedError extends Error {
|
|
303
|
+
constructor(message?: string);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* A credential you tried to LINK (ADR-030) is already attached to a DIFFERENT
|
|
307
|
+
* account. Sendora never auto-merges two real accounts — sign in to that
|
|
308
|
+
* account instead, or link a different credential.
|
|
309
|
+
*/
|
|
310
|
+
declare class CredentialInUseError extends Error {
|
|
311
|
+
constructor(message?: string);
|
|
312
|
+
}
|
|
295
313
|
declare class AuthError extends Error {
|
|
296
314
|
code: string;
|
|
297
315
|
constructor(code: string, message: string);
|
|
@@ -326,6 +344,17 @@ interface DeviceTakeoverEvent {
|
|
|
326
344
|
/** Local clock at the time the SDK observed the event. */
|
|
327
345
|
at: Date;
|
|
328
346
|
}
|
|
347
|
+
/**
|
|
348
|
+
* Detail handed to `onDeletionCancelled` subscribers (s58.269). Fires once
|
|
349
|
+
* per sign-in that cancelled a pending self-service deletion within grace —
|
|
350
|
+
* the account is restored with the SAME user_id.
|
|
351
|
+
*/
|
|
352
|
+
interface DeletionCancelledEvent {
|
|
353
|
+
/** The restored user_id (unchanged — this is a status flip, not a remap). */
|
|
354
|
+
userId: string;
|
|
355
|
+
/** Local clock at the time the SDK observed the event. */
|
|
356
|
+
at: Date;
|
|
357
|
+
}
|
|
329
358
|
declare class Auth {
|
|
330
359
|
private hooks;
|
|
331
360
|
private user;
|
|
@@ -347,6 +376,8 @@ declare class Auth {
|
|
|
347
376
|
private refreshFailureCount;
|
|
348
377
|
private refreshCooldownUntil;
|
|
349
378
|
private takeoverListeners;
|
|
379
|
+
private deletionCancelledListeners;
|
|
380
|
+
private lastDeletionCancelled;
|
|
350
381
|
private lastTakeover;
|
|
351
382
|
/**
|
|
352
383
|
* Anon refresh token captured at `signIn()` time, stashed keyed to the
|
|
@@ -416,6 +447,19 @@ declare class Auth {
|
|
|
416
447
|
getLastDeviceTakeover(): DeviceTakeoverEvent | null;
|
|
417
448
|
/** Internal — fan out to subscribers + cache the latest. */
|
|
418
449
|
private fireDeviceTakeover;
|
|
450
|
+
/**
|
|
451
|
+
* Subscribe to deletion-cancelled events (s58.269): fires when a sign-in
|
|
452
|
+
* cancelled a pending self-service account deletion within its grace window
|
|
453
|
+
* (the account is restored, same user_id). Use it to show "your account
|
|
454
|
+
* deletion was cancelled" + reconcile local state. Fires from signIn /
|
|
455
|
+
* loginSocial / signInWithGameCenter / signInWithPlayGames. Returns an
|
|
456
|
+
* unsubscribe function; listener errors are swallowed (best-effort).
|
|
457
|
+
*/
|
|
458
|
+
onDeletionCancelled(listener: (evt: DeletionCancelledEvent) => void): () => void;
|
|
459
|
+
/** The most recent deletion-cancelled event this session, or null. Cleared on signOut. */
|
|
460
|
+
getLastDeletionCancelled(): DeletionCancelledEvent | null;
|
|
461
|
+
/** Internal — fan out deletion-cancelled to subscribers + cache the latest. */
|
|
462
|
+
private fireDeletionCancelled;
|
|
419
463
|
/**
|
|
420
464
|
* Returns a VALID (non-expired) access token, or null.
|
|
421
465
|
*
|
|
@@ -576,6 +620,62 @@ declare class Auth {
|
|
|
576
620
|
}, opts?: {
|
|
577
621
|
link?: boolean;
|
|
578
622
|
}): Promise<AuthUser>;
|
|
623
|
+
/** Link email + password to the current account (sub preserved). */
|
|
624
|
+
linkEmailPassword(email: string, password: string): Promise<AuthUser>;
|
|
625
|
+
/** Link an OAuth social identity (verified idToken or code) to the current account. */
|
|
626
|
+
linkSocial(input: {
|
|
627
|
+
provider: "google" | "github" | "apple" | "microsoft" | "linkedin" | "facebook" | "twitter" | "discord";
|
|
628
|
+
code?: string;
|
|
629
|
+
idToken?: string;
|
|
630
|
+
redirectUri?: string;
|
|
631
|
+
codeVerifier?: string;
|
|
632
|
+
appleName?: {
|
|
633
|
+
firstName?: string;
|
|
634
|
+
lastName?: string;
|
|
635
|
+
};
|
|
636
|
+
}): Promise<AuthUser>;
|
|
637
|
+
/** Convenience: link a Google identity (native idToken, or web code+redirectUri). */
|
|
638
|
+
linkGoogle(input: {
|
|
639
|
+
idToken?: string;
|
|
640
|
+
code?: string;
|
|
641
|
+
redirectUri?: string;
|
|
642
|
+
}): Promise<AuthUser>;
|
|
643
|
+
/** Convenience: link an Apple identity (native idToken, or web code+redirectUri). */
|
|
644
|
+
linkApple(input: {
|
|
645
|
+
idToken?: string;
|
|
646
|
+
code?: string;
|
|
647
|
+
redirectUri?: string;
|
|
648
|
+
appleName?: {
|
|
649
|
+
firstName?: string;
|
|
650
|
+
lastName?: string;
|
|
651
|
+
};
|
|
652
|
+
}): Promise<AuthUser>;
|
|
653
|
+
/**
|
|
654
|
+
* Link an Apple Game Center identity to the current account. No-arg drives the
|
|
655
|
+
* native GameKit fetch (needs `@sendoracloud/sdk-react-native-gaming`); or pass
|
|
656
|
+
* an explicit payload obtained from your own GameKit bridge.
|
|
657
|
+
*/
|
|
658
|
+
linkGameCenter(payload?: GameCenterPayload): Promise<AuthUser>;
|
|
659
|
+
/**
|
|
660
|
+
* Link a Google Play Games identity to the current account. No-arg drives the
|
|
661
|
+
* native Play Games fetch; or pass an explicit `{ serverAuthCode }`.
|
|
662
|
+
*/
|
|
663
|
+
linkPlayGames(payload?: {
|
|
664
|
+
serverAuthCode: string;
|
|
665
|
+
}): Promise<AuthUser>;
|
|
666
|
+
/**
|
|
667
|
+
* Shared link executor. POSTs the verified credential with the caller's
|
|
668
|
+
* end-user Bearer token, then refreshes the cached user object IN PLACE (no
|
|
669
|
+
* token rotation — the sub is unchanged). Deliberately NOT wrapped in
|
|
670
|
+
* serialize(): getAccessToken() serializes internally, so re-entering would
|
|
671
|
+
* deadlock — same non-serialized posture as deleteAccount().
|
|
672
|
+
*/
|
|
673
|
+
private linkCredential;
|
|
674
|
+
/**
|
|
675
|
+
* Refresh the cached user after a link — the sub is unchanged, so only the
|
|
676
|
+
* user object + its storage copy are updated (no token/identity rotation).
|
|
677
|
+
*/
|
|
678
|
+
private updateLocalUser;
|
|
579
679
|
/**
|
|
580
680
|
* Shared gaming-identity sign-in. Mirrors `loginSocial`'s anon-takeover +
|
|
581
681
|
* ADR-025 link-in-place handling: reads the anon refresh BEFORE wiping local
|
|
@@ -1551,4 +1651,4 @@ declare class SendoraSDK {
|
|
|
1551
1651
|
}
|
|
1552
1652
|
declare const SendoraCloud: SendoraSDK;
|
|
1553
1653
|
|
|
1554
|
-
export { type AttachLinkingApiOptions, Auth, AuthError, type AuthTokens, type AuthUser, 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 };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -292,6 +292,24 @@ interface GameCenterPayload {
|
|
|
292
292
|
declare class EmailAlreadyTakenError extends Error {
|
|
293
293
|
constructor(message?: string);
|
|
294
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* `signUp()` was called on a session that is ALREADY signed in with an
|
|
297
|
+
* identity (not anonymous). Creating a second account would orphan the
|
|
298
|
+
* current one — use `linkEmailPassword()` / `linkGoogle()` / … (ADR-030) to
|
|
299
|
+
* add the credential to THIS account, or sign out first. Distinct from
|
|
300
|
+
* `EmailAlreadyTakenError`: the email isn't the problem, the live session is.
|
|
301
|
+
*/
|
|
302
|
+
declare class AlreadyIdentifiedError extends Error {
|
|
303
|
+
constructor(message?: string);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* A credential you tried to LINK (ADR-030) is already attached to a DIFFERENT
|
|
307
|
+
* account. Sendora never auto-merges two real accounts — sign in to that
|
|
308
|
+
* account instead, or link a different credential.
|
|
309
|
+
*/
|
|
310
|
+
declare class CredentialInUseError extends Error {
|
|
311
|
+
constructor(message?: string);
|
|
312
|
+
}
|
|
295
313
|
declare class AuthError extends Error {
|
|
296
314
|
code: string;
|
|
297
315
|
constructor(code: string, message: string);
|
|
@@ -326,6 +344,17 @@ interface DeviceTakeoverEvent {
|
|
|
326
344
|
/** Local clock at the time the SDK observed the event. */
|
|
327
345
|
at: Date;
|
|
328
346
|
}
|
|
347
|
+
/**
|
|
348
|
+
* Detail handed to `onDeletionCancelled` subscribers (s58.269). Fires once
|
|
349
|
+
* per sign-in that cancelled a pending self-service deletion within grace —
|
|
350
|
+
* the account is restored with the SAME user_id.
|
|
351
|
+
*/
|
|
352
|
+
interface DeletionCancelledEvent {
|
|
353
|
+
/** The restored user_id (unchanged — this is a status flip, not a remap). */
|
|
354
|
+
userId: string;
|
|
355
|
+
/** Local clock at the time the SDK observed the event. */
|
|
356
|
+
at: Date;
|
|
357
|
+
}
|
|
329
358
|
declare class Auth {
|
|
330
359
|
private hooks;
|
|
331
360
|
private user;
|
|
@@ -347,6 +376,8 @@ declare class Auth {
|
|
|
347
376
|
private refreshFailureCount;
|
|
348
377
|
private refreshCooldownUntil;
|
|
349
378
|
private takeoverListeners;
|
|
379
|
+
private deletionCancelledListeners;
|
|
380
|
+
private lastDeletionCancelled;
|
|
350
381
|
private lastTakeover;
|
|
351
382
|
/**
|
|
352
383
|
* Anon refresh token captured at `signIn()` time, stashed keyed to the
|
|
@@ -416,6 +447,19 @@ declare class Auth {
|
|
|
416
447
|
getLastDeviceTakeover(): DeviceTakeoverEvent | null;
|
|
417
448
|
/** Internal — fan out to subscribers + cache the latest. */
|
|
418
449
|
private fireDeviceTakeover;
|
|
450
|
+
/**
|
|
451
|
+
* Subscribe to deletion-cancelled events (s58.269): fires when a sign-in
|
|
452
|
+
* cancelled a pending self-service account deletion within its grace window
|
|
453
|
+
* (the account is restored, same user_id). Use it to show "your account
|
|
454
|
+
* deletion was cancelled" + reconcile local state. Fires from signIn /
|
|
455
|
+
* loginSocial / signInWithGameCenter / signInWithPlayGames. Returns an
|
|
456
|
+
* unsubscribe function; listener errors are swallowed (best-effort).
|
|
457
|
+
*/
|
|
458
|
+
onDeletionCancelled(listener: (evt: DeletionCancelledEvent) => void): () => void;
|
|
459
|
+
/** The most recent deletion-cancelled event this session, or null. Cleared on signOut. */
|
|
460
|
+
getLastDeletionCancelled(): DeletionCancelledEvent | null;
|
|
461
|
+
/** Internal — fan out deletion-cancelled to subscribers + cache the latest. */
|
|
462
|
+
private fireDeletionCancelled;
|
|
419
463
|
/**
|
|
420
464
|
* Returns a VALID (non-expired) access token, or null.
|
|
421
465
|
*
|
|
@@ -576,6 +620,62 @@ declare class Auth {
|
|
|
576
620
|
}, opts?: {
|
|
577
621
|
link?: boolean;
|
|
578
622
|
}): Promise<AuthUser>;
|
|
623
|
+
/** Link email + password to the current account (sub preserved). */
|
|
624
|
+
linkEmailPassword(email: string, password: string): Promise<AuthUser>;
|
|
625
|
+
/** Link an OAuth social identity (verified idToken or code) to the current account. */
|
|
626
|
+
linkSocial(input: {
|
|
627
|
+
provider: "google" | "github" | "apple" | "microsoft" | "linkedin" | "facebook" | "twitter" | "discord";
|
|
628
|
+
code?: string;
|
|
629
|
+
idToken?: string;
|
|
630
|
+
redirectUri?: string;
|
|
631
|
+
codeVerifier?: string;
|
|
632
|
+
appleName?: {
|
|
633
|
+
firstName?: string;
|
|
634
|
+
lastName?: string;
|
|
635
|
+
};
|
|
636
|
+
}): Promise<AuthUser>;
|
|
637
|
+
/** Convenience: link a Google identity (native idToken, or web code+redirectUri). */
|
|
638
|
+
linkGoogle(input: {
|
|
639
|
+
idToken?: string;
|
|
640
|
+
code?: string;
|
|
641
|
+
redirectUri?: string;
|
|
642
|
+
}): Promise<AuthUser>;
|
|
643
|
+
/** Convenience: link an Apple identity (native idToken, or web code+redirectUri). */
|
|
644
|
+
linkApple(input: {
|
|
645
|
+
idToken?: string;
|
|
646
|
+
code?: string;
|
|
647
|
+
redirectUri?: string;
|
|
648
|
+
appleName?: {
|
|
649
|
+
firstName?: string;
|
|
650
|
+
lastName?: string;
|
|
651
|
+
};
|
|
652
|
+
}): Promise<AuthUser>;
|
|
653
|
+
/**
|
|
654
|
+
* Link an Apple Game Center identity to the current account. No-arg drives the
|
|
655
|
+
* native GameKit fetch (needs `@sendoracloud/sdk-react-native-gaming`); or pass
|
|
656
|
+
* an explicit payload obtained from your own GameKit bridge.
|
|
657
|
+
*/
|
|
658
|
+
linkGameCenter(payload?: GameCenterPayload): Promise<AuthUser>;
|
|
659
|
+
/**
|
|
660
|
+
* Link a Google Play Games identity to the current account. No-arg drives the
|
|
661
|
+
* native Play Games fetch; or pass an explicit `{ serverAuthCode }`.
|
|
662
|
+
*/
|
|
663
|
+
linkPlayGames(payload?: {
|
|
664
|
+
serverAuthCode: string;
|
|
665
|
+
}): Promise<AuthUser>;
|
|
666
|
+
/**
|
|
667
|
+
* Shared link executor. POSTs the verified credential with the caller's
|
|
668
|
+
* end-user Bearer token, then refreshes the cached user object IN PLACE (no
|
|
669
|
+
* token rotation — the sub is unchanged). Deliberately NOT wrapped in
|
|
670
|
+
* serialize(): getAccessToken() serializes internally, so re-entering would
|
|
671
|
+
* deadlock — same non-serialized posture as deleteAccount().
|
|
672
|
+
*/
|
|
673
|
+
private linkCredential;
|
|
674
|
+
/**
|
|
675
|
+
* Refresh the cached user after a link — the sub is unchanged, so only the
|
|
676
|
+
* user object + its storage copy are updated (no token/identity rotation).
|
|
677
|
+
*/
|
|
678
|
+
private updateLocalUser;
|
|
579
679
|
/**
|
|
580
680
|
* Shared gaming-identity sign-in. Mirrors `loginSocial`'s anon-takeover +
|
|
581
681
|
* ADR-025 link-in-place handling: reads the anon refresh BEFORE wiping local
|
|
@@ -1551,4 +1651,4 @@ declare class SendoraSDK {
|
|
|
1551
1651
|
}
|
|
1552
1652
|
declare const SendoraCloud: SendoraSDK;
|
|
1553
1653
|
|
|
1554
|
-
export { type AttachLinkingApiOptions, Auth, AuthError, type AuthTokens, type AuthUser, 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 };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -157,7 +157,7 @@ var Storage = class {
|
|
|
157
157
|
// package.json
|
|
158
158
|
var package_default = {
|
|
159
159
|
name: "@sendoracloud/sdk-react-native",
|
|
160
|
-
version: "1.
|
|
160
|
+
version: "1.26.0",
|
|
161
161
|
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`).",
|
|
162
162
|
type: "module",
|
|
163
163
|
main: "./dist/index.cjs",
|
|
@@ -213,7 +213,7 @@ var package_default = {
|
|
|
213
213
|
scripts: {
|
|
214
214
|
build: "tsup src/index.ts src/contact-widget.tsx --format esm,cjs --dts --clean --external react --external react-native --external react-native-webview",
|
|
215
215
|
typecheck: "tsc --noEmit",
|
|
216
|
-
test: "tsx test/secure-storage.test.ts && tsx test/integration-storage.test.ts && tsx test/plugin.test.ts && tsx test/game-auth.test.ts"
|
|
216
|
+
test: "tsx test/secure-storage.test.ts && tsx test/integration-storage.test.ts && tsx test/plugin.test.ts && tsx test/game-auth.test.ts && tsx test/link.test.ts"
|
|
217
217
|
},
|
|
218
218
|
devDependencies: {
|
|
219
219
|
"@types/react": "^19.2.16",
|
|
@@ -383,6 +383,18 @@ var EmailAlreadyTakenError = class extends Error {
|
|
|
383
383
|
this.name = "EmailAlreadyTakenError";
|
|
384
384
|
}
|
|
385
385
|
};
|
|
386
|
+
var AlreadyIdentifiedError = class extends Error {
|
|
387
|
+
constructor(message = "Already signed in with an identity; use link*() to add a credential, or sign out first") {
|
|
388
|
+
super(message);
|
|
389
|
+
this.name = "AlreadyIdentifiedError";
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
var CredentialInUseError = class extends Error {
|
|
393
|
+
constructor(message = "This credential is already linked to a different account") {
|
|
394
|
+
super(message);
|
|
395
|
+
this.name = "CredentialInUseError";
|
|
396
|
+
}
|
|
397
|
+
};
|
|
386
398
|
var AuthError = class extends Error {
|
|
387
399
|
constructor(code, message) {
|
|
388
400
|
super(message);
|
|
@@ -453,6 +465,8 @@ var Auth = class {
|
|
|
453
465
|
this.refreshFailureCount = 0;
|
|
454
466
|
this.refreshCooldownUntil = 0;
|
|
455
467
|
this.takeoverListeners = /* @__PURE__ */ new Set();
|
|
468
|
+
this.deletionCancelledListeners = /* @__PURE__ */ new Set();
|
|
469
|
+
this.lastDeletionCancelled = null;
|
|
456
470
|
this.lastTakeover = null;
|
|
457
471
|
/**
|
|
458
472
|
* Anon refresh token captured at `signIn()` time, stashed keyed to the
|
|
@@ -565,6 +579,36 @@ var Auth = class {
|
|
|
565
579
|
}
|
|
566
580
|
}
|
|
567
581
|
}
|
|
582
|
+
/**
|
|
583
|
+
* Subscribe to deletion-cancelled events (s58.269): fires when a sign-in
|
|
584
|
+
* cancelled a pending self-service account deletion within its grace window
|
|
585
|
+
* (the account is restored, same user_id). Use it to show "your account
|
|
586
|
+
* deletion was cancelled" + reconcile local state. Fires from signIn /
|
|
587
|
+
* loginSocial / signInWithGameCenter / signInWithPlayGames. Returns an
|
|
588
|
+
* unsubscribe function; listener errors are swallowed (best-effort).
|
|
589
|
+
*/
|
|
590
|
+
onDeletionCancelled(listener) {
|
|
591
|
+
this.deletionCancelledListeners.add(listener);
|
|
592
|
+
return () => {
|
|
593
|
+
this.deletionCancelledListeners.delete(listener);
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
/** The most recent deletion-cancelled event this session, or null. Cleared on signOut. */
|
|
597
|
+
getLastDeletionCancelled() {
|
|
598
|
+
return this.lastDeletionCancelled;
|
|
599
|
+
}
|
|
600
|
+
/** Internal — fan out deletion-cancelled to subscribers + cache the latest. */
|
|
601
|
+
fireDeletionCancelled(userId) {
|
|
602
|
+
const evt = { userId, at: /* @__PURE__ */ new Date() };
|
|
603
|
+
this.lastDeletionCancelled = evt;
|
|
604
|
+
for (const fn of this.deletionCancelledListeners) {
|
|
605
|
+
try {
|
|
606
|
+
fn(evt);
|
|
607
|
+
} catch (err) {
|
|
608
|
+
if (this.hooks.debug) console.warn("[sendoracloud] onDeletionCancelled listener threw", err);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
568
612
|
/**
|
|
569
613
|
* Returns a VALID (non-expired) access token, or null.
|
|
570
614
|
*
|
|
@@ -653,10 +697,13 @@ var Auth = class {
|
|
|
653
697
|
this.persist(data);
|
|
654
698
|
return data.user;
|
|
655
699
|
} catch (err) {
|
|
700
|
+
if (err instanceof AuthError && (err.code === "NOT_ANONYMOUS" || err.code === "FORBIDDEN_NON_ANONYMOUS")) {
|
|
701
|
+
throw new AlreadyIdentifiedError(err.message);
|
|
702
|
+
}
|
|
656
703
|
if (err instanceof AuthError && (err.code === "CONFLICT" || err.code === "EMAIL_ALREADY_TAKEN")) {
|
|
657
704
|
throw new EmailAlreadyTakenError(err.message);
|
|
658
705
|
}
|
|
659
|
-
if (err instanceof AuthError && (err.code === "
|
|
706
|
+
if (err instanceof AuthError && (err.code === "INVALID_REFRESH" || err.code === "UNAUTHORIZED" || err.code === "REFRESH_EXPIRED")) {
|
|
660
707
|
await this.wipeLocalIdentity();
|
|
661
708
|
} else {
|
|
662
709
|
throw err;
|
|
@@ -806,6 +853,92 @@ var Auth = class {
|
|
|
806
853
|
link
|
|
807
854
|
);
|
|
808
855
|
}
|
|
856
|
+
// ============================================================
|
|
857
|
+
// IDENTITY LINKING (ADR-030) — attach a SECOND credential to the
|
|
858
|
+
// CURRENT signed-in account, preserving the same user id (sub).
|
|
859
|
+
// ============================================================
|
|
860
|
+
//
|
|
861
|
+
// Unlike signUp() / loginSocial({link}) — which only preserve the sub from
|
|
862
|
+
// an ANONYMOUS session — these operate on an ALREADY-IDENTIFIED account.
|
|
863
|
+
// No token rotation (the session is unchanged); they refresh the cached user
|
|
864
|
+
// object in place. Require a signed-in user (throws NOT_SIGNED_IN otherwise).
|
|
865
|
+
//
|
|
866
|
+
// Primary use: ONE account across platforms. A Game Center player on iOS
|
|
867
|
+
// links email/Google; on Android they sign in with that same credential and
|
|
868
|
+
// land on the SAME sub, progress intact. Collision (the credential already
|
|
869
|
+
// belongs to another account) → CredentialInUseError (never merges).
|
|
870
|
+
/** Link email + password to the current account (sub preserved). */
|
|
871
|
+
linkEmailPassword(email, password) {
|
|
872
|
+
return this.linkCredential("/api/v1/auth-service/me/link/email", { email, password });
|
|
873
|
+
}
|
|
874
|
+
/** Link an OAuth social identity (verified idToken or code) to the current account. */
|
|
875
|
+
linkSocial(input) {
|
|
876
|
+
return this.linkCredential("/api/v1/auth-service/me/link/social", input);
|
|
877
|
+
}
|
|
878
|
+
/** Convenience: link a Google identity (native idToken, or web code+redirectUri). */
|
|
879
|
+
linkGoogle(input) {
|
|
880
|
+
return this.linkSocial({ provider: "google", ...input });
|
|
881
|
+
}
|
|
882
|
+
/** Convenience: link an Apple identity (native idToken, or web code+redirectUri). */
|
|
883
|
+
linkApple(input) {
|
|
884
|
+
return this.linkSocial({ provider: "apple", ...input });
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Link an Apple Game Center identity to the current account. No-arg drives the
|
|
888
|
+
* native GameKit fetch (needs `@sendoracloud/sdk-react-native-gaming`); or pass
|
|
889
|
+
* an explicit payload obtained from your own GameKit bridge.
|
|
890
|
+
*/
|
|
891
|
+
async linkGameCenter(payload) {
|
|
892
|
+
const body = payload ?? await fetchGameCenterPayloadNative();
|
|
893
|
+
return this.linkCredential("/api/v1/auth-service/me/link/game-center", { ...body });
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Link a Google Play Games identity to the current account. No-arg drives the
|
|
897
|
+
* native Play Games fetch; or pass an explicit `{ serverAuthCode }`.
|
|
898
|
+
*/
|
|
899
|
+
async linkPlayGames(payload) {
|
|
900
|
+
const body = payload ?? await fetchPlayGamesAuthCodeNative();
|
|
901
|
+
return this.linkCredential("/api/v1/auth-service/me/link/play-games", { ...body });
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Shared link executor. POSTs the verified credential with the caller's
|
|
905
|
+
* end-user Bearer token, then refreshes the cached user object IN PLACE (no
|
|
906
|
+
* token rotation — the sub is unchanged). Deliberately NOT wrapped in
|
|
907
|
+
* serialize(): getAccessToken() serializes internally, so re-entering would
|
|
908
|
+
* deadlock — same non-serialized posture as deleteAccount().
|
|
909
|
+
*/
|
|
910
|
+
async linkCredential(path, body) {
|
|
911
|
+
const token = await this.getAccessToken();
|
|
912
|
+
if (!token) throw new AuthError("NOT_SIGNED_IN", "Sign in before linking a credential");
|
|
913
|
+
const res = await post(
|
|
914
|
+
{ apiUrl: this.hooks.apiUrl, publicKey: this.hooks.publicKey, debug: this.hooks.debug },
|
|
915
|
+
path,
|
|
916
|
+
body,
|
|
917
|
+
{ Authorization: `Bearer ${token}` }
|
|
918
|
+
);
|
|
919
|
+
if (!res) throw new AuthError("NETWORK_ERROR", "Network request failed");
|
|
920
|
+
let parsed;
|
|
921
|
+
try {
|
|
922
|
+
parsed = await res.json();
|
|
923
|
+
} catch {
|
|
924
|
+
throw new AuthError("PARSE_ERROR", `HTTP ${res.status}`);
|
|
925
|
+
}
|
|
926
|
+
if (!res.ok || !parsed.success || !parsed.data) {
|
|
927
|
+
const code = parsed.error?.code ?? `HTTP_${res.status}`;
|
|
928
|
+
if (code === "CREDENTIAL_IN_USE") throw new CredentialInUseError(parsed.error?.message);
|
|
929
|
+
throw new AuthError(code, parsed.error?.message ?? "Failed to link credential");
|
|
930
|
+
}
|
|
931
|
+
if (parsed.data.user) this.updateLocalUser(parsed.data.user);
|
|
932
|
+
return parsed.data.user ?? this.user;
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Refresh the cached user after a link — the sub is unchanged, so only the
|
|
936
|
+
* user object + its storage copy are updated (no token/identity rotation).
|
|
937
|
+
*/
|
|
938
|
+
updateLocalUser(user) {
|
|
939
|
+
this.user = user;
|
|
940
|
+
this.hooks.storage.set(USER_KEY, JSON.stringify(user));
|
|
941
|
+
}
|
|
809
942
|
/**
|
|
810
943
|
* Shared gaming-identity sign-in. Mirrors `loginSocial`'s anon-takeover +
|
|
811
944
|
* ADR-025 link-in-place handling: reads the anon refresh BEFORE wiping local
|
|
@@ -1453,12 +1586,16 @@ var Auth = class {
|
|
|
1453
1586
|
if (data.retiredAnonUserId) {
|
|
1454
1587
|
this.fireDeviceTakeover(data.retiredAnonUserId, data.user.id);
|
|
1455
1588
|
}
|
|
1589
|
+
if (data.reactivatedFromDeletion) {
|
|
1590
|
+
this.fireDeletionCancelled(data.user.id);
|
|
1591
|
+
}
|
|
1456
1592
|
}
|
|
1457
1593
|
async wipeLocalIdentity() {
|
|
1458
1594
|
this.user = null;
|
|
1459
1595
|
this.accessToken = null;
|
|
1460
1596
|
this.accessExpiresAt = 0;
|
|
1461
1597
|
this.lastTakeover = null;
|
|
1598
|
+
this.lastDeletionCancelled = null;
|
|
1462
1599
|
this.pendingAnonTakeover = null;
|
|
1463
1600
|
this.hooks.storage.remove(USER_KEY);
|
|
1464
1601
|
this.hooks.storage.remove(TOKEN_KEY);
|
|
@@ -3175,8 +3312,10 @@ var SendoraSDK = class {
|
|
|
3175
3312
|
var SendoraCloud = new SendoraSDK();
|
|
3176
3313
|
var index_default = SendoraCloud;
|
|
3177
3314
|
export {
|
|
3315
|
+
AlreadyIdentifiedError,
|
|
3178
3316
|
Auth,
|
|
3179
3317
|
AuthError,
|
|
3318
|
+
CredentialInUseError,
|
|
3180
3319
|
EmailAlreadyTakenError,
|
|
3181
3320
|
LinkError,
|
|
3182
3321
|
Links,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendoracloud/sdk-react-native",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.0",
|
|
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",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "tsup src/index.ts src/contact-widget.tsx --format esm,cjs --dts --clean --external react --external react-native --external react-native-webview",
|
|
58
58
|
"typecheck": "tsc --noEmit",
|
|
59
|
-
"test": "tsx test/secure-storage.test.ts && tsx test/integration-storage.test.ts && tsx test/plugin.test.ts && tsx test/game-auth.test.ts"
|
|
59
|
+
"test": "tsx test/secure-storage.test.ts && tsx test/integration-storage.test.ts && tsx test/plugin.test.ts && tsx test/game-auth.test.ts && tsx test/link.test.ts"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/react": "^19.2.16",
|