@youversion/platform-core 0.6.0 → 0.8.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/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +46 -0
- package/dist/index.cjs +473 -346
- package/dist/index.d.cts +106 -134
- package/dist/index.d.ts +106 -134
- package/dist/index.js +473 -343
- package/package.json +2 -1
- package/src/SignInWithYouVersionPKCE.ts +122 -0
- package/src/SignInWithYouVersionResult.ts +40 -39
- package/src/URLBuilder.ts +0 -21
- package/src/Users.ts +375 -94
- package/src/YouVersionPlatformConfiguration.ts +69 -25
- package/src/YouVersionUserInfo.ts +6 -6
- package/src/__tests__/SignInWithYouVersionPKCE.test.ts +418 -0
- package/src/__tests__/SignInWithYouVersionResult.test.ts +28 -0
- package/src/__tests__/StorageStrategy.test.ts +0 -72
- package/src/__tests__/URLBuilder.test.ts +0 -100
- package/src/__tests__/Users.test.ts +737 -0
- package/src/__tests__/YouVersionPlatformConfiguration.test.ts +192 -30
- package/src/__tests__/YouVersionUserInfo.test.ts +347 -0
- package/src/__tests__/highlights.test.ts +12 -12
- package/src/__tests__/mocks/browser.ts +90 -0
- package/src/__tests__/mocks/configuration.ts +53 -0
- package/src/__tests__/mocks/jwt.ts +93 -0
- package/src/__tests__/mocks/tokens.ts +69 -0
- package/src/index.ts +0 -3
- package/src/types/auth.ts +1 -0
- package/tsconfig.build.json +1 -1
- package/tsconfig.json +1 -1
- package/src/AuthenticationStrategy.ts +0 -78
- package/src/WebAuthenticationStrategy.ts +0 -127
- package/src/__tests__/authentication.test.ts +0 -185
- package/src/authentication.ts +0 -27
package/dist/index.cjs
CHANGED
|
@@ -21,8 +21,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
ApiClient: () => ApiClient,
|
|
24
|
-
AuthClient: () => AuthClient,
|
|
25
|
-
AuthenticationStrategyRegistry: () => AuthenticationStrategyRegistry,
|
|
26
24
|
BOOK_CANON: () => BOOK_CANON,
|
|
27
25
|
BOOK_IDS: () => BOOK_IDS,
|
|
28
26
|
BibleClient: () => BibleClient,
|
|
@@ -33,7 +31,6 @@ __export(index_exports, {
|
|
|
33
31
|
SignInWithYouVersionPermission: () => SignInWithYouVersionPermission,
|
|
34
32
|
SignInWithYouVersionResult: () => SignInWithYouVersionResult,
|
|
35
33
|
URLBuilder: () => URLBuilder,
|
|
36
|
-
WebAuthenticationStrategy: () => WebAuthenticationStrategy,
|
|
37
34
|
YouVersionAPI: () => YouVersionAPI,
|
|
38
35
|
YouVersionAPIUsers: () => YouVersionAPIUsers,
|
|
39
36
|
YouVersionPlatformConfiguration: () => YouVersionPlatformConfiguration,
|
|
@@ -444,10 +441,9 @@ var import_zod3 = require("zod");
|
|
|
444
441
|
var YouVersionPlatformConfiguration = class {
|
|
445
442
|
static _appKey = null;
|
|
446
443
|
static _installationId = null;
|
|
447
|
-
static _accessToken = null;
|
|
448
444
|
static _apiHost = "api.youversion.com";
|
|
449
|
-
static
|
|
450
|
-
static
|
|
445
|
+
static _refreshTokenKey = null;
|
|
446
|
+
static _expiryDateKey = null;
|
|
451
447
|
static getOrSetInstallationId() {
|
|
452
448
|
if (typeof window === "undefined") {
|
|
453
449
|
return "";
|
|
@@ -460,6 +456,44 @@ var YouVersionPlatformConfiguration = class {
|
|
|
460
456
|
localStorage.setItem("x-yvp-installation-id", newId);
|
|
461
457
|
return newId;
|
|
462
458
|
}
|
|
459
|
+
static saveAuthData(accessToken, refreshToken, idToken, expiryDate) {
|
|
460
|
+
if (accessToken !== null) {
|
|
461
|
+
localStorage.setItem("accessToken", accessToken);
|
|
462
|
+
} else {
|
|
463
|
+
localStorage.removeItem("accessToken");
|
|
464
|
+
}
|
|
465
|
+
if (refreshToken !== null) {
|
|
466
|
+
localStorage.setItem("refreshToken", refreshToken);
|
|
467
|
+
} else {
|
|
468
|
+
localStorage.removeItem("refreshToken");
|
|
469
|
+
}
|
|
470
|
+
if (idToken !== null) {
|
|
471
|
+
localStorage.setItem("idToken", idToken);
|
|
472
|
+
} else {
|
|
473
|
+
localStorage.removeItem("idToken");
|
|
474
|
+
}
|
|
475
|
+
if (expiryDate !== null) {
|
|
476
|
+
localStorage.setItem("expiryDate", expiryDate.toISOString());
|
|
477
|
+
} else {
|
|
478
|
+
localStorage.removeItem("expiryDate");
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
static clearAuthTokens() {
|
|
482
|
+
this.saveAuthData(null, null, null, null);
|
|
483
|
+
}
|
|
484
|
+
static get accessToken() {
|
|
485
|
+
return localStorage.getItem("accessToken");
|
|
486
|
+
}
|
|
487
|
+
static get refreshToken() {
|
|
488
|
+
return localStorage.getItem("refreshToken");
|
|
489
|
+
}
|
|
490
|
+
static get idToken() {
|
|
491
|
+
return localStorage.getItem("idToken");
|
|
492
|
+
}
|
|
493
|
+
static get tokenExpiryDate() {
|
|
494
|
+
const dateString = localStorage.getItem("expiryDate");
|
|
495
|
+
return dateString ? new Date(dateString) : null;
|
|
496
|
+
}
|
|
463
497
|
static get appKey() {
|
|
464
498
|
return this._appKey;
|
|
465
499
|
}
|
|
@@ -475,32 +509,23 @@ var YouVersionPlatformConfiguration = class {
|
|
|
475
509
|
static set installationId(value) {
|
|
476
510
|
this._installationId = value || this.getOrSetInstallationId();
|
|
477
511
|
}
|
|
478
|
-
static setAccessToken(token) {
|
|
479
|
-
if (token !== null && (typeof token !== "string" || token.trim().length === 0)) {
|
|
480
|
-
throw new Error("Access token must be a non-empty string or null");
|
|
481
|
-
}
|
|
482
|
-
this._accessToken = token;
|
|
483
|
-
}
|
|
484
|
-
static get accessToken() {
|
|
485
|
-
return this._accessToken;
|
|
486
|
-
}
|
|
487
512
|
static get apiHost() {
|
|
488
513
|
return this._apiHost;
|
|
489
514
|
}
|
|
490
515
|
static set apiHost(value) {
|
|
491
516
|
this._apiHost = value;
|
|
492
517
|
}
|
|
493
|
-
static get
|
|
494
|
-
return this.
|
|
518
|
+
static get refreshTokenKey() {
|
|
519
|
+
return this._refreshTokenKey;
|
|
495
520
|
}
|
|
496
|
-
static set
|
|
497
|
-
this.
|
|
521
|
+
static set refreshTokenKey(value) {
|
|
522
|
+
this._refreshTokenKey = value;
|
|
498
523
|
}
|
|
499
|
-
static get
|
|
500
|
-
return this.
|
|
524
|
+
static get expiryDateKey() {
|
|
525
|
+
return this._expiryDateKey;
|
|
501
526
|
}
|
|
502
|
-
static set
|
|
503
|
-
this.
|
|
527
|
+
static set expiryDateKey(value) {
|
|
528
|
+
this._expiryDateKey = value;
|
|
504
529
|
}
|
|
505
530
|
};
|
|
506
531
|
|
|
@@ -625,78 +650,6 @@ var HighlightsClient = class {
|
|
|
625
650
|
}
|
|
626
651
|
};
|
|
627
652
|
|
|
628
|
-
// src/authentication.ts
|
|
629
|
-
var AuthClient = class {
|
|
630
|
-
client;
|
|
631
|
-
/**
|
|
632
|
-
* Creates an instance of AuthClient.
|
|
633
|
-
* @param client - The ApiClient instance to use for requests.
|
|
634
|
-
*/
|
|
635
|
-
constructor(client) {
|
|
636
|
-
this.client = client;
|
|
637
|
-
}
|
|
638
|
-
/**
|
|
639
|
-
* Retrieves the current authenticated user.
|
|
640
|
-
*
|
|
641
|
-
* @param lat - The long access token (LAT) used for authentication.
|
|
642
|
-
* @returns A promise that resolves to the authenticated User.
|
|
643
|
-
*/
|
|
644
|
-
async getUser(lat) {
|
|
645
|
-
return this.client.get(`/auth/me`, { lat });
|
|
646
|
-
}
|
|
647
|
-
};
|
|
648
|
-
|
|
649
|
-
// src/AuthenticationStrategy.ts
|
|
650
|
-
var AuthenticationStrategyRegistry = class {
|
|
651
|
-
static strategy = null;
|
|
652
|
-
/**
|
|
653
|
-
* Registers a platform-specific authentication strategy
|
|
654
|
-
*
|
|
655
|
-
* @param strategy - The authentication strategy to register
|
|
656
|
-
* @throws Error if strategy is null, undefined, or missing required methods
|
|
657
|
-
*/
|
|
658
|
-
static register(strategy) {
|
|
659
|
-
if (!strategy) {
|
|
660
|
-
throw new Error("Authentication strategy cannot be null or undefined");
|
|
661
|
-
}
|
|
662
|
-
if (typeof strategy.authenticate !== "function") {
|
|
663
|
-
throw new Error("Authentication strategy must implement authenticate method");
|
|
664
|
-
}
|
|
665
|
-
this.strategy = strategy;
|
|
666
|
-
}
|
|
667
|
-
/**
|
|
668
|
-
* Gets the currently registered authentication strategy
|
|
669
|
-
*
|
|
670
|
-
* @returns The registered authentication strategy
|
|
671
|
-
* @throws Error if no strategy has been registered
|
|
672
|
-
*/
|
|
673
|
-
static get() {
|
|
674
|
-
if (!this.strategy) {
|
|
675
|
-
throw new Error(
|
|
676
|
-
"No authentication strategy registered. Please register a platform-specific strategy using AuthenticationStrategyRegistry.register()"
|
|
677
|
-
);
|
|
678
|
-
}
|
|
679
|
-
return this.strategy;
|
|
680
|
-
}
|
|
681
|
-
/**
|
|
682
|
-
* Checks if a strategy is currently registered
|
|
683
|
-
*
|
|
684
|
-
* @returns true if a strategy is registered, false otherwise
|
|
685
|
-
*/
|
|
686
|
-
static isRegistered() {
|
|
687
|
-
return this.strategy !== null;
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* Resets the registry by removing the current strategy
|
|
691
|
-
*
|
|
692
|
-
* This method is primarily intended for testing scenarios
|
|
693
|
-
* where you need to clean up between test cases.
|
|
694
|
-
*/
|
|
695
|
-
static reset() {
|
|
696
|
-
this.strategy = null;
|
|
697
|
-
}
|
|
698
|
-
};
|
|
699
|
-
|
|
700
653
|
// src/StorageStrategy.ts
|
|
701
654
|
var SessionStorageStrategy = class {
|
|
702
655
|
setItem(key, value) {
|
|
@@ -741,96 +694,122 @@ var MemoryStorageStrategy = class {
|
|
|
741
694
|
}
|
|
742
695
|
};
|
|
743
696
|
|
|
744
|
-
// src/
|
|
745
|
-
var
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
constructor(options) {
|
|
754
|
-
this.callbackPath = options?.callbackPath ?? "/auth/callback";
|
|
755
|
-
this.redirectUri = options?.redirectUri ?? window.location.origin + this.callbackPath;
|
|
756
|
-
this.timeout = options?.timeout ?? 3e5;
|
|
757
|
-
this.storage = options?.storage ?? new SessionStorageStrategy();
|
|
758
|
-
}
|
|
759
|
-
async authenticate(authUrl) {
|
|
760
|
-
authUrl.searchParams.set("redirect_uri", this.redirectUri);
|
|
761
|
-
return this.authenticateWithRedirect(authUrl);
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* Call this method when your app loads to handle the redirect callback
|
|
765
|
-
*/
|
|
766
|
-
static handleCallback(callbackPath = "/auth/callback") {
|
|
767
|
-
const currentUrl = new URL(window.location.href);
|
|
768
|
-
if (currentUrl.pathname === callbackPath && currentUrl.searchParams.has("status")) {
|
|
769
|
-
const callbackUrl = new URL(currentUrl.toString());
|
|
770
|
-
if (_WebAuthenticationStrategy.pendingAuthResolve) {
|
|
771
|
-
_WebAuthenticationStrategy.pendingAuthResolve(callbackUrl);
|
|
772
|
-
_WebAuthenticationStrategy.cleanup();
|
|
773
|
-
} else {
|
|
774
|
-
const storageStrategy2 = new SessionStorageStrategy();
|
|
775
|
-
storageStrategy2.setItem("youversion-auth-callback", callbackUrl.toString());
|
|
776
|
-
}
|
|
777
|
-
const storageStrategy = new SessionStorageStrategy();
|
|
778
|
-
const returnUrl = storageStrategy.getItem("youversion-auth-return") ?? "/";
|
|
779
|
-
storageStrategy.removeItem("youversion-auth-return");
|
|
780
|
-
window.history.replaceState({}, "", returnUrl);
|
|
781
|
-
return true;
|
|
697
|
+
// src/YouVersionUserInfo.ts
|
|
698
|
+
var YouVersionUserInfo = class {
|
|
699
|
+
name;
|
|
700
|
+
userId;
|
|
701
|
+
email;
|
|
702
|
+
avatarUrlFormat;
|
|
703
|
+
constructor(data) {
|
|
704
|
+
if (!data || typeof data !== "object") {
|
|
705
|
+
throw new Error("Invalid user data provided");
|
|
782
706
|
}
|
|
783
|
-
|
|
707
|
+
this.name = data.name;
|
|
708
|
+
this.userId = data.id;
|
|
709
|
+
this.email = data.email;
|
|
710
|
+
this.avatarUrlFormat = data.avatar_url;
|
|
784
711
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
712
|
+
getAvatarUrl(width = 200, height = 200) {
|
|
713
|
+
if (!this.avatarUrlFormat) {
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
716
|
+
let urlString = this.avatarUrlFormat;
|
|
717
|
+
if (urlString.startsWith("//")) {
|
|
718
|
+
urlString = "https:" + urlString;
|
|
719
|
+
}
|
|
720
|
+
urlString = urlString.replace("{width}", width.toString());
|
|
721
|
+
urlString = urlString.replace("{height}", height.toString());
|
|
722
|
+
try {
|
|
723
|
+
return new URL(urlString);
|
|
724
|
+
} catch {
|
|
725
|
+
return null;
|
|
792
726
|
}
|
|
793
|
-
_WebAuthenticationStrategy.pendingAuthResolve = null;
|
|
794
|
-
_WebAuthenticationStrategy.pendingAuthReject = null;
|
|
795
727
|
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
728
|
+
get avatarUrl() {
|
|
729
|
+
return this.getAvatarUrl();
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
// src/SignInWithYouVersionPKCE.ts
|
|
734
|
+
var SignInWithYouVersionPKCEAuthorizationRequestBuilder = class {
|
|
735
|
+
static async make(appKey, permissions, redirectURL) {
|
|
736
|
+
const codeVerifier = this.randomURLSafeString(32);
|
|
737
|
+
const codeChallenge = await this.codeChallenge(codeVerifier);
|
|
738
|
+
const state = this.randomURLSafeString(24);
|
|
739
|
+
const nonce = this.randomURLSafeString(24);
|
|
740
|
+
const parameters = {
|
|
741
|
+
codeVerifier,
|
|
742
|
+
codeChallenge,
|
|
743
|
+
state,
|
|
744
|
+
nonce
|
|
745
|
+
};
|
|
746
|
+
const url = this.authorizeURL(appKey, permissions, redirectURL, parameters);
|
|
747
|
+
return { url, parameters };
|
|
748
|
+
}
|
|
749
|
+
static authorizeURL(appKey, permissions, redirectURL, parameters) {
|
|
750
|
+
const components = new URL(`https://${YouVersionPlatformConfiguration.apiHost}/auth/authorize`);
|
|
751
|
+
const redirectUrlString = redirectURL.toString().endsWith("/") ? redirectURL.toString().slice(0, -1) : redirectURL.toString();
|
|
752
|
+
const queryParams = new URLSearchParams({
|
|
753
|
+
response_type: "code",
|
|
754
|
+
client_id: appKey,
|
|
755
|
+
redirect_uri: redirectUrlString,
|
|
756
|
+
nonce: parameters.nonce,
|
|
757
|
+
state: parameters.state,
|
|
758
|
+
code_challenge: parameters.codeChallenge,
|
|
759
|
+
code_challenge_method: "S256"
|
|
760
|
+
});
|
|
761
|
+
const installId = YouVersionPlatformConfiguration.installationId;
|
|
762
|
+
if (installId) {
|
|
763
|
+
queryParams.set("x-yvp-installation-id", installId);
|
|
809
764
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
return
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
765
|
+
const scopeValue = this.scopeValue(permissions);
|
|
766
|
+
if (scopeValue) {
|
|
767
|
+
queryParams.set("scope", scopeValue);
|
|
768
|
+
}
|
|
769
|
+
components.search = queryParams.toString();
|
|
770
|
+
return components;
|
|
771
|
+
}
|
|
772
|
+
static tokenURLRequest(code, codeVerifier, redirectUri) {
|
|
773
|
+
const apiHost = YouVersionPlatformConfiguration.apiHost;
|
|
774
|
+
const appKey = YouVersionPlatformConfiguration.appKey;
|
|
775
|
+
const url = new URL(`https://${apiHost}/auth/token`);
|
|
776
|
+
const parameters = new URLSearchParams({
|
|
777
|
+
grant_type: "authorization_code",
|
|
778
|
+
code,
|
|
779
|
+
redirect_uri: redirectUri,
|
|
780
|
+
client_id: appKey ?? "",
|
|
781
|
+
code_verifier: codeVerifier
|
|
782
|
+
});
|
|
783
|
+
return new Request(url, {
|
|
784
|
+
method: "POST",
|
|
785
|
+
body: parameters,
|
|
786
|
+
headers: {
|
|
787
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
831
788
|
}
|
|
832
789
|
});
|
|
833
790
|
}
|
|
791
|
+
static randomURLSafeString(byteCount) {
|
|
792
|
+
const bytes = new Uint8Array(byteCount);
|
|
793
|
+
crypto.getRandomValues(bytes);
|
|
794
|
+
return this.base64URLEncodedString(bytes);
|
|
795
|
+
}
|
|
796
|
+
static async codeChallenge(verifier) {
|
|
797
|
+
const data = new TextEncoder().encode(verifier);
|
|
798
|
+
const digest = await crypto.subtle.digest("SHA-256", data);
|
|
799
|
+
return this.base64URLEncodedString(new Uint8Array(digest));
|
|
800
|
+
}
|
|
801
|
+
static base64URLEncodedString(data) {
|
|
802
|
+
const base64 = btoa(String.fromCharCode.apply(null, Array.from(data)));
|
|
803
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
804
|
+
}
|
|
805
|
+
static scopeValue(permissions) {
|
|
806
|
+
const scopeArray = Array.from(permissions).sort();
|
|
807
|
+
let scopeWithOpenID = scopeArray.join(" ");
|
|
808
|
+
if (!scopeWithOpenID.split(" ").includes("openid")) {
|
|
809
|
+
scopeWithOpenID += (scopeWithOpenID === "" ? "" : " ") + "openid";
|
|
810
|
+
}
|
|
811
|
+
return scopeWithOpenID || null;
|
|
812
|
+
}
|
|
834
813
|
};
|
|
835
814
|
|
|
836
815
|
// src/SignInWithYouVersionResult.ts
|
|
@@ -843,72 +822,336 @@ var SignInWithYouVersionPermission = {
|
|
|
843
822
|
};
|
|
844
823
|
var SignInWithYouVersionResult = class {
|
|
845
824
|
accessToken;
|
|
825
|
+
expiryDate;
|
|
826
|
+
refreshToken;
|
|
827
|
+
idToken;
|
|
846
828
|
permissions;
|
|
847
|
-
errorMsg;
|
|
848
829
|
yvpUserId;
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
this.permissions = [];
|
|
873
|
-
this.errorMsg = "Authentication failed";
|
|
874
|
-
this.yvpUserId = null;
|
|
875
|
-
}
|
|
830
|
+
name;
|
|
831
|
+
profilePicture;
|
|
832
|
+
email;
|
|
833
|
+
constructor({
|
|
834
|
+
accessToken,
|
|
835
|
+
expiresIn,
|
|
836
|
+
refreshToken,
|
|
837
|
+
idToken,
|
|
838
|
+
permissions,
|
|
839
|
+
yvpUserId,
|
|
840
|
+
name,
|
|
841
|
+
profilePicture,
|
|
842
|
+
email
|
|
843
|
+
}) {
|
|
844
|
+
this.accessToken = accessToken;
|
|
845
|
+
this.expiryDate = expiresIn ? new Date(Date.now() + expiresIn * 1e3) : /* @__PURE__ */ new Date();
|
|
846
|
+
this.refreshToken = refreshToken;
|
|
847
|
+
this.idToken = idToken;
|
|
848
|
+
this.permissions = permissions;
|
|
849
|
+
this.yvpUserId = yvpUserId;
|
|
850
|
+
this.name = name;
|
|
851
|
+
this.profilePicture = profilePicture;
|
|
852
|
+
this.email = email;
|
|
876
853
|
}
|
|
877
854
|
};
|
|
878
855
|
|
|
879
|
-
// src/
|
|
880
|
-
var
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
856
|
+
// src/Users.ts
|
|
857
|
+
var YouVersionAPIUsers = class {
|
|
858
|
+
/**
|
|
859
|
+
* Presents the YouVersion login flow to the user and returns the login result upon completion.
|
|
860
|
+
*
|
|
861
|
+
* This function authenticates the user with YouVersion, requesting the specified required and optional permissions.
|
|
862
|
+
* The function redirects to the YouVersion authorization URL and expects the callback to be handled separately.
|
|
863
|
+
*
|
|
864
|
+
* @param permissions - The set of permissions that must be granted by the user for successful login.
|
|
865
|
+
* @param redirectURL - The URL to redirect back to after authentication.
|
|
866
|
+
* @throws An error if authentication fails or configuration is invalid.
|
|
867
|
+
*/
|
|
868
|
+
static async signIn(permissions, redirectURL) {
|
|
869
|
+
const appKey = YouVersionPlatformConfiguration.appKey;
|
|
870
|
+
if (!appKey) {
|
|
871
|
+
throw new Error("YouVersionPlatformConfiguration.appKey must be set before calling signIn");
|
|
888
872
|
}
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
873
|
+
const authorizationRequest = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
|
|
874
|
+
appKey,
|
|
875
|
+
permissions,
|
|
876
|
+
new URL(redirectURL)
|
|
877
|
+
);
|
|
878
|
+
localStorage.setItem(
|
|
879
|
+
"youversion-auth-code-verifier",
|
|
880
|
+
authorizationRequest.parameters.codeVerifier
|
|
881
|
+
);
|
|
882
|
+
const redirectUrlString = redirectURL.toString().endsWith("/") ? redirectURL.toString().slice(0, -1) : redirectURL.toString();
|
|
883
|
+
localStorage.setItem("youversion-auth-redirect-uri", redirectUrlString);
|
|
884
|
+
localStorage.setItem("youversion-auth-state", authorizationRequest.parameters.state);
|
|
885
|
+
window.location.href = authorizationRequest.url.toString();
|
|
893
886
|
}
|
|
894
|
-
|
|
895
|
-
|
|
887
|
+
/**
|
|
888
|
+
* Handles the OAuth callback after user authentication.
|
|
889
|
+
*
|
|
890
|
+
* Call this method when your app loads to check if the current URL contains
|
|
891
|
+
* an OAuth callback with authorization code. If found, it exchanges the code
|
|
892
|
+
* for tokens and stores them.
|
|
893
|
+
*
|
|
894
|
+
* @returns Promise<SignInWithYouVersionResult | null> - SignInWithYouVersionResult if callback was handled, null otherwise
|
|
895
|
+
* @throws An error if token exchange fails
|
|
896
|
+
*/
|
|
897
|
+
static async handleAuthCallback() {
|
|
898
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
899
|
+
const code = urlParams.get("code");
|
|
900
|
+
const state = urlParams.get("state");
|
|
901
|
+
const error = urlParams.get("error");
|
|
902
|
+
if (!state && !error) {
|
|
896
903
|
return null;
|
|
897
904
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
905
|
+
if (error) {
|
|
906
|
+
const errorDescription = urlParams.get("error_description") || error;
|
|
907
|
+
throw new Error(`OAuth authentication failed: ${errorDescription}`);
|
|
908
|
+
}
|
|
909
|
+
const storedState = localStorage.getItem("youversion-auth-state");
|
|
910
|
+
if (state !== storedState) {
|
|
911
|
+
throw new Error("Invalid state parameter - possible CSRF attack");
|
|
912
|
+
}
|
|
913
|
+
if (!code && state) {
|
|
914
|
+
this.obtainLocation(window.location.href, state);
|
|
915
|
+
}
|
|
916
|
+
const codeVerifier = localStorage.getItem("youversion-auth-code-verifier");
|
|
917
|
+
const redirectUri = localStorage.getItem("youversion-auth-redirect-uri");
|
|
918
|
+
if (!code || !codeVerifier || !redirectUri) {
|
|
919
|
+
throw new Error("Missing required authentication parameters");
|
|
901
920
|
}
|
|
902
|
-
urlString = urlString.replace("{width}", width.toString());
|
|
903
|
-
urlString = urlString.replace("{height}", height.toString());
|
|
904
921
|
try {
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
922
|
+
const tokenRequest = SignInWithYouVersionPKCEAuthorizationRequestBuilder.tokenURLRequest(
|
|
923
|
+
code,
|
|
924
|
+
codeVerifier,
|
|
925
|
+
redirectUri
|
|
926
|
+
);
|
|
927
|
+
const response = await fetch(tokenRequest);
|
|
928
|
+
if (!response.ok) {
|
|
929
|
+
throw new Error(`Token exchange failed: ${response.status} ${response.statusText}`);
|
|
930
|
+
}
|
|
931
|
+
const responseText = await response.text();
|
|
932
|
+
const tokens = JSON.parse(responseText);
|
|
933
|
+
const result = this.extractSignInResult(tokens);
|
|
934
|
+
YouVersionPlatformConfiguration.saveAuthData(
|
|
935
|
+
result.accessToken || null,
|
|
936
|
+
result.refreshToken || null,
|
|
937
|
+
result.idToken || null,
|
|
938
|
+
result.expiryDate || null
|
|
939
|
+
);
|
|
940
|
+
localStorage.removeItem("youversion-auth-code-verifier");
|
|
941
|
+
localStorage.removeItem("youversion-auth-redirect-uri");
|
|
942
|
+
localStorage.removeItem("youversion-auth-state");
|
|
943
|
+
const cleanUrl = new URL(window.location.href);
|
|
944
|
+
cleanUrl.search = "";
|
|
945
|
+
window.history.replaceState({}, "", cleanUrl.toString());
|
|
946
|
+
return result;
|
|
947
|
+
} catch (error2) {
|
|
948
|
+
localStorage.removeItem("youversion-auth-code-verifier");
|
|
949
|
+
localStorage.removeItem("youversion-auth-redirect-uri");
|
|
950
|
+
localStorage.removeItem("youversion-auth-state");
|
|
951
|
+
throw error2;
|
|
908
952
|
}
|
|
909
953
|
}
|
|
910
|
-
|
|
911
|
-
|
|
954
|
+
/**
|
|
955
|
+
* Redirects to the server callback endpoint to obtain authorization code
|
|
956
|
+
*/
|
|
957
|
+
static obtainLocation(callbackURL, state) {
|
|
958
|
+
const url = new URL(callbackURL);
|
|
959
|
+
const params = new URLSearchParams(url.search);
|
|
960
|
+
if (params.get("state") !== state) {
|
|
961
|
+
throw new Error("Invalid state parameter");
|
|
962
|
+
}
|
|
963
|
+
const serverCallbackUrl = new URL(
|
|
964
|
+
`https://${YouVersionPlatformConfiguration.apiHost}/auth/callback`
|
|
965
|
+
);
|
|
966
|
+
params.forEach((value, key) => {
|
|
967
|
+
serverCallbackUrl.searchParams.set(key, value);
|
|
968
|
+
});
|
|
969
|
+
window.location.href = serverCallbackUrl.toString();
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* Extracts sign-in result from token response
|
|
973
|
+
*/
|
|
974
|
+
static extractSignInResult(tokens) {
|
|
975
|
+
const idClaims = this.decodeJWT(tokens.id_token);
|
|
976
|
+
const permissions = tokens.scope.split(" ").map((p) => p.trim()).filter((p) => p.length > 0).filter(
|
|
977
|
+
(p) => Object.values(SignInWithYouVersionPermission).includes(
|
|
978
|
+
p
|
|
979
|
+
)
|
|
980
|
+
);
|
|
981
|
+
const resultData = {
|
|
982
|
+
accessToken: tokens.access_token,
|
|
983
|
+
expiresIn: tokens.expires_in,
|
|
984
|
+
refreshToken: tokens.refresh_token,
|
|
985
|
+
idToken: tokens.id_token,
|
|
986
|
+
permissions,
|
|
987
|
+
yvpUserId: idClaims.sub,
|
|
988
|
+
name: idClaims.name,
|
|
989
|
+
profilePicture: idClaims.profile_picture,
|
|
990
|
+
email: idClaims.email
|
|
991
|
+
};
|
|
992
|
+
return new SignInWithYouVersionResult(resultData);
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Decodes JWT payload for UI display purposes.
|
|
996
|
+
*
|
|
997
|
+
* Note: We intentionally do not verify the JWT signature here because:
|
|
998
|
+
*
|
|
999
|
+
* 1. YouVersion's backend verifies all tokens on API requests
|
|
1000
|
+
* 2. This decoded data is only used for UI display
|
|
1001
|
+
* 3. No security decisions are made based on these claims
|
|
1002
|
+
*
|
|
1003
|
+
* @private
|
|
1004
|
+
*/
|
|
1005
|
+
static decodeJWT(token) {
|
|
1006
|
+
const segments = token.split(".");
|
|
1007
|
+
if (segments.length !== 3) {
|
|
1008
|
+
return {};
|
|
1009
|
+
}
|
|
1010
|
+
let base64 = segments[1]?.replace(/-/g, "+").replace(/_/g, "/");
|
|
1011
|
+
while (base64 && base64.length % 4 !== 0) {
|
|
1012
|
+
base64 += "=";
|
|
1013
|
+
}
|
|
1014
|
+
try {
|
|
1015
|
+
if (base64) {
|
|
1016
|
+
const data = atob(base64);
|
|
1017
|
+
return JSON.parse(data);
|
|
1018
|
+
} else {
|
|
1019
|
+
return {};
|
|
1020
|
+
}
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
if (process.env.NODE_ENV === "development") {
|
|
1023
|
+
console.error("JWT decode failed:", error);
|
|
1024
|
+
}
|
|
1025
|
+
return {};
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
static signOut() {
|
|
1029
|
+
YouVersionPlatformConfiguration.clearAuthTokens();
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Retrieves user information for the authenticated user by decoding the provided JWT access token.
|
|
1033
|
+
*
|
|
1034
|
+
* This function extracts the user's profile information directly from the JWT token payload.
|
|
1035
|
+
*
|
|
1036
|
+
* @param accessToken - The JWT access token obtained from the login process.
|
|
1037
|
+
* @returns A Promise resolving to a YouVersionUserInfo object containing the user's profile information.
|
|
1038
|
+
* @throws An error if the access token is invalid or cannot be decoded.
|
|
1039
|
+
*/
|
|
1040
|
+
static userInfo(idToken) {
|
|
1041
|
+
if (!idToken || typeof idToken !== "string") {
|
|
1042
|
+
throw new Error("Invalid access token: must be a non-empty string");
|
|
1043
|
+
}
|
|
1044
|
+
try {
|
|
1045
|
+
const claims = this.decodeJWT(idToken);
|
|
1046
|
+
if (!claims || Object.keys(claims).length === 0) {
|
|
1047
|
+
throw new Error("Invalid JWT token: Unable to decode token payload");
|
|
1048
|
+
}
|
|
1049
|
+
const userInfoData = {
|
|
1050
|
+
id: claims.sub,
|
|
1051
|
+
name: claims.name,
|
|
1052
|
+
avatar_url: claims.profile_picture,
|
|
1053
|
+
email: claims.email
|
|
1054
|
+
};
|
|
1055
|
+
return new YouVersionUserInfo(userInfoData);
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
if (error instanceof Error) {
|
|
1058
|
+
throw new Error(`Failed to decode user information from JWT: ${error.message}`);
|
|
1059
|
+
} else {
|
|
1060
|
+
throw new Error("Failed to decode user information from JWT: Unknown error");
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Refreshes the access token using the stored refresh token.
|
|
1066
|
+
*
|
|
1067
|
+
* @returns Promise<SignInWithYouVersionResult | null> - New tokens if refresh succeeds, null otherwise
|
|
1068
|
+
* @throws An error if refresh fails or no refresh token is available
|
|
1069
|
+
*/
|
|
1070
|
+
static async refreshTokens() {
|
|
1071
|
+
const refreshToken = YouVersionPlatformConfiguration.refreshToken;
|
|
1072
|
+
const appKey = YouVersionPlatformConfiguration.appKey;
|
|
1073
|
+
const existingIdToken = YouVersionPlatformConfiguration.idToken;
|
|
1074
|
+
if (!refreshToken || !existingIdToken) {
|
|
1075
|
+
throw new Error("No refresh token or id token available");
|
|
1076
|
+
}
|
|
1077
|
+
if (!appKey) {
|
|
1078
|
+
throw new Error(
|
|
1079
|
+
"YouVersionPlatformConfiguration.appKey must be set before refreshing tokens"
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
try {
|
|
1083
|
+
const url = new URL(`https://${YouVersionPlatformConfiguration.apiHost}/auth/token`);
|
|
1084
|
+
const parameters = new URLSearchParams({
|
|
1085
|
+
grant_type: "refresh_token",
|
|
1086
|
+
refresh_token: refreshToken,
|
|
1087
|
+
client_id: appKey
|
|
1088
|
+
});
|
|
1089
|
+
const request = new Request(url, {
|
|
1090
|
+
method: "POST",
|
|
1091
|
+
body: parameters,
|
|
1092
|
+
headers: {
|
|
1093
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
const response = await fetch(request);
|
|
1097
|
+
if (!response.ok) {
|
|
1098
|
+
throw new Error(`Token refresh failed: ${response.status} ${response.statusText}`);
|
|
1099
|
+
}
|
|
1100
|
+
const tokens = await response.json();
|
|
1101
|
+
const result = new SignInWithYouVersionResult({
|
|
1102
|
+
accessToken: tokens.access_token,
|
|
1103
|
+
expiresIn: tokens.expires_in,
|
|
1104
|
+
refreshToken: tokens.refresh_token,
|
|
1105
|
+
idToken: existingIdToken,
|
|
1106
|
+
permissions: tokens.scope.split(" ").map((p) => p.trim()).filter((p) => p.length > 0).filter(
|
|
1107
|
+
(p) => Object.values(SignInWithYouVersionPermission).includes(
|
|
1108
|
+
p
|
|
1109
|
+
)
|
|
1110
|
+
)
|
|
1111
|
+
});
|
|
1112
|
+
YouVersionPlatformConfiguration.saveAuthData(
|
|
1113
|
+
result.accessToken || null,
|
|
1114
|
+
result.refreshToken || null,
|
|
1115
|
+
result.idToken || null,
|
|
1116
|
+
result.expiryDate || null
|
|
1117
|
+
);
|
|
1118
|
+
return result;
|
|
1119
|
+
} catch (error) {
|
|
1120
|
+
if (error instanceof Error) {
|
|
1121
|
+
throw new Error(`Token refresh failed: ${error.message}`);
|
|
1122
|
+
} else {
|
|
1123
|
+
throw new Error("Token refresh failed: Unknown error");
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
/**
|
|
1128
|
+
* Checks if the current access token is expired or about to expire.
|
|
1129
|
+
*
|
|
1130
|
+
* @returns true if token is expired or about to expire
|
|
1131
|
+
*/
|
|
1132
|
+
static isTokenExpired() {
|
|
1133
|
+
const expiryDate = YouVersionPlatformConfiguration.tokenExpiryDate;
|
|
1134
|
+
if (!expiryDate) {
|
|
1135
|
+
return true;
|
|
1136
|
+
}
|
|
1137
|
+
return (/* @__PURE__ */ new Date()).getTime() >= expiryDate.getTime();
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Refreshes the access token if it's expired or about to expire.
|
|
1141
|
+
*
|
|
1142
|
+
* @returns Promise<boolean> - true if refresh was successful or not needed, false if failed
|
|
1143
|
+
*/
|
|
1144
|
+
static async refreshTokenIfNeeded() {
|
|
1145
|
+
if (!this.isTokenExpired()) {
|
|
1146
|
+
return true;
|
|
1147
|
+
}
|
|
1148
|
+
try {
|
|
1149
|
+
const result = await this.refreshTokens();
|
|
1150
|
+
return !!result;
|
|
1151
|
+
} catch {
|
|
1152
|
+
YouVersionPlatformConfiguration.clearAuthTokens();
|
|
1153
|
+
return false;
|
|
1154
|
+
}
|
|
912
1155
|
}
|
|
913
1156
|
};
|
|
914
1157
|
|
|
@@ -969,119 +1212,6 @@ var URLBuilder = class {
|
|
|
969
1212
|
);
|
|
970
1213
|
}
|
|
971
1214
|
}
|
|
972
|
-
static userURL(accessToken) {
|
|
973
|
-
if (typeof accessToken !== "string" || accessToken.trim().length === 0) {
|
|
974
|
-
throw new Error("accessToken must be a non-empty string");
|
|
975
|
-
}
|
|
976
|
-
try {
|
|
977
|
-
const url = new URL(this.baseURL);
|
|
978
|
-
url.pathname = "/auth/me";
|
|
979
|
-
const searchParams = new URLSearchParams();
|
|
980
|
-
searchParams.append("lat", accessToken);
|
|
981
|
-
url.search = searchParams.toString();
|
|
982
|
-
return url;
|
|
983
|
-
} catch (error) {
|
|
984
|
-
throw new Error(
|
|
985
|
-
`Failed to construct user URL: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
986
|
-
);
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
};
|
|
990
|
-
|
|
991
|
-
// src/Users.ts
|
|
992
|
-
var MAX_RETRY_ATTEMPTS = 3;
|
|
993
|
-
var RETRY_DELAY_MS = 1e3;
|
|
994
|
-
var YouVersionAPIUsers = class {
|
|
995
|
-
/**
|
|
996
|
-
* Presents the YouVersion login flow to the user and returns the login result upon completion.
|
|
997
|
-
*
|
|
998
|
-
* This function authenticates the user with YouVersion, requesting the specified required and optional permissions.
|
|
999
|
-
* The function returns a promise that resolves when the user completes or cancels the login flow,
|
|
1000
|
-
* returning the login result containing the authorization code and granted permissions.
|
|
1001
|
-
*
|
|
1002
|
-
* @param requiredPermissions - The set of permissions that must be granted by the user for successful login.
|
|
1003
|
-
* @param optionalPermissions - The set of permissions that will be requested from the user but are not required for successful login.
|
|
1004
|
-
* @returns A Promise resolving to a SignInWithYouVersionResult containing the authorization code and granted permissions upon successful login.
|
|
1005
|
-
* @throws An error if authentication fails or is cancelled by the user.
|
|
1006
|
-
*/
|
|
1007
|
-
static async signIn(requiredPermissions, optionalPermissions) {
|
|
1008
|
-
if (!requiredPermissions || !(requiredPermissions instanceof Set)) {
|
|
1009
|
-
throw new Error("Invalid requiredPermissions: must be a Set");
|
|
1010
|
-
}
|
|
1011
|
-
if (!optionalPermissions || !(optionalPermissions instanceof Set)) {
|
|
1012
|
-
throw new Error("Invalid optionalPermissions: must be a Set");
|
|
1013
|
-
}
|
|
1014
|
-
const appKey = YouVersionPlatformConfiguration.appKey;
|
|
1015
|
-
if (!appKey) {
|
|
1016
|
-
throw new Error("YouVersionPlatformConfiguration.appKey must be set before calling signIn");
|
|
1017
|
-
}
|
|
1018
|
-
const url = URLBuilder.authURL(appKey, requiredPermissions, optionalPermissions);
|
|
1019
|
-
const strategy = AuthenticationStrategyRegistry.get();
|
|
1020
|
-
const callbackUrl = await strategy.authenticate(url);
|
|
1021
|
-
const result = new SignInWithYouVersionResult(callbackUrl);
|
|
1022
|
-
if (result.accessToken) {
|
|
1023
|
-
YouVersionPlatformConfiguration.setAccessToken(result.accessToken);
|
|
1024
|
-
}
|
|
1025
|
-
return result;
|
|
1026
|
-
}
|
|
1027
|
-
static signOut() {
|
|
1028
|
-
YouVersionPlatformConfiguration.setAccessToken(null);
|
|
1029
|
-
}
|
|
1030
|
-
/**
|
|
1031
|
-
* Retrieves user information for the authenticated user using the provided access token.
|
|
1032
|
-
*
|
|
1033
|
-
* This function fetches the user's profile information from the YouVersion API, decoding it into a YouVersionUserInfo model.
|
|
1034
|
-
*
|
|
1035
|
-
* @param accessToken - The access token obtained from the login process.
|
|
1036
|
-
* @returns A Promise resolving to a YouVersionUserInfo object containing the user's profile information.
|
|
1037
|
-
* @throws An error if the URL is invalid, the network request fails, or the response cannot be decoded.
|
|
1038
|
-
*/
|
|
1039
|
-
static async userInfo(accessToken) {
|
|
1040
|
-
if (!accessToken || typeof accessToken !== "string") {
|
|
1041
|
-
throw new Error("Invalid access token: must be a non-empty string");
|
|
1042
|
-
}
|
|
1043
|
-
if (YouVersionPlatformConfiguration.isPreviewMode && accessToken === "preview") {
|
|
1044
|
-
return YouVersionPlatformConfiguration.previewUserInfo || new YouVersionUserInfo({
|
|
1045
|
-
first_name: "Preview",
|
|
1046
|
-
last_name: "User",
|
|
1047
|
-
id: "preview-user",
|
|
1048
|
-
avatar_url: void 0
|
|
1049
|
-
});
|
|
1050
|
-
}
|
|
1051
|
-
const url = URLBuilder.userURL(accessToken);
|
|
1052
|
-
const request = YouVersionAPI.addStandardHeaders(url);
|
|
1053
|
-
let lastError = null;
|
|
1054
|
-
for (let attempt = 1; attempt <= MAX_RETRY_ATTEMPTS; attempt++) {
|
|
1055
|
-
try {
|
|
1056
|
-
const response = await fetch(request);
|
|
1057
|
-
if (response.status === 401) {
|
|
1058
|
-
throw new Error(
|
|
1059
|
-
"Authentication failed: Invalid or expired access token. Please sign in again."
|
|
1060
|
-
);
|
|
1061
|
-
}
|
|
1062
|
-
if (response.status === 403) {
|
|
1063
|
-
throw new Error("Access denied: Insufficient permissions to retrieve user information");
|
|
1064
|
-
}
|
|
1065
|
-
if (response.status !== 200) {
|
|
1066
|
-
throw new Error(
|
|
1067
|
-
`Failed to retrieve user information: Server responded with status ${response.status}`
|
|
1068
|
-
);
|
|
1069
|
-
}
|
|
1070
|
-
const data = await response.json();
|
|
1071
|
-
return data;
|
|
1072
|
-
} catch (error) {
|
|
1073
|
-
lastError = error instanceof Error ? error : new Error("Failed to parse server response");
|
|
1074
|
-
if (error instanceof Error && (error.message.includes("401") || error.message.includes("403"))) {
|
|
1075
|
-
throw error;
|
|
1076
|
-
}
|
|
1077
|
-
if (attempt < MAX_RETRY_ATTEMPTS) {
|
|
1078
|
-
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS * attempt));
|
|
1079
|
-
continue;
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
throw lastError || new Error("Failed to retrieve user information after multiple attempts");
|
|
1084
|
-
}
|
|
1085
1215
|
};
|
|
1086
1216
|
|
|
1087
1217
|
// src/utils/constants.ts
|
|
@@ -1301,8 +1431,6 @@ var BOOK_CANON = {
|
|
|
1301
1431
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1302
1432
|
0 && (module.exports = {
|
|
1303
1433
|
ApiClient,
|
|
1304
|
-
AuthClient,
|
|
1305
|
-
AuthenticationStrategyRegistry,
|
|
1306
1434
|
BOOK_CANON,
|
|
1307
1435
|
BOOK_IDS,
|
|
1308
1436
|
BibleClient,
|
|
@@ -1313,7 +1441,6 @@ var BOOK_CANON = {
|
|
|
1313
1441
|
SignInWithYouVersionPermission,
|
|
1314
1442
|
SignInWithYouVersionResult,
|
|
1315
1443
|
URLBuilder,
|
|
1316
|
-
WebAuthenticationStrategy,
|
|
1317
1444
|
YouVersionAPI,
|
|
1318
1445
|
YouVersionAPIUsers,
|
|
1319
1446
|
YouVersionPlatformConfiguration,
|