@youversion/platform-react-hooks 0.5.8 → 0.7.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.
Files changed (84) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +49 -0
  3. package/README.md +3 -3
  4. package/dist/__tests__/mocks/auth.d.ts +12 -0
  5. package/dist/__tests__/mocks/auth.d.ts.map +1 -0
  6. package/dist/__tests__/mocks/auth.js +44 -0
  7. package/dist/__tests__/mocks/auth.js.map +1 -0
  8. package/dist/__tests__/utils/test-utils.d.ts +15 -0
  9. package/dist/__tests__/utils/test-utils.d.ts.map +1 -0
  10. package/dist/__tests__/utils/test-utils.js +24 -0
  11. package/dist/__tests__/utils/test-utils.js.map +1 -0
  12. package/dist/context/YouVersionAuthContext.d.ts +4 -0
  13. package/dist/context/YouVersionAuthContext.d.ts.map +1 -0
  14. package/dist/context/YouVersionAuthContext.js +13 -0
  15. package/dist/context/YouVersionAuthContext.js.map +1 -0
  16. package/dist/context/YouVersionAuthProvider.d.ts +8 -0
  17. package/dist/context/YouVersionAuthProvider.d.ts.map +1 -0
  18. package/dist/context/YouVersionAuthProvider.js +80 -0
  19. package/dist/context/YouVersionAuthProvider.js.map +1 -0
  20. package/dist/context/YouVersionContext.d.ts +8 -0
  21. package/dist/context/YouVersionContext.d.ts.map +1 -0
  22. package/dist/context/YouVersionContext.js +4 -0
  23. package/dist/context/YouVersionContext.js.map +1 -0
  24. package/dist/context/YouVersionProvider.d.ts +17 -0
  25. package/dist/context/YouVersionProvider.d.ts.map +1 -0
  26. package/dist/context/YouVersionProvider.js +25 -0
  27. package/dist/context/YouVersionProvider.js.map +1 -0
  28. package/dist/context/index.d.ts +2 -2
  29. package/dist/context/index.d.ts.map +1 -1
  30. package/dist/context/index.js +2 -2
  31. package/dist/context/index.js.map +1 -1
  32. package/dist/index.d.ts +4 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +5 -0
  35. package/dist/index.js.map +1 -1
  36. package/dist/types/auth.d.ts +18 -0
  37. package/dist/types/auth.d.ts.map +1 -0
  38. package/dist/types/auth.js +2 -0
  39. package/dist/types/auth.js.map +1 -0
  40. package/dist/useBibleClient.js +3 -3
  41. package/dist/useBibleClient.js.map +1 -1
  42. package/dist/useHighlights.js +3 -3
  43. package/dist/useHighlights.js.map +1 -1
  44. package/dist/useLanguages.js +3 -3
  45. package/dist/useLanguages.js.map +1 -1
  46. package/dist/usePassage.d.ts +3 -3
  47. package/dist/usePassage.d.ts.map +1 -1
  48. package/dist/usePassage.js +5 -5
  49. package/dist/usePassage.js.map +1 -1
  50. package/dist/useYVAuth.d.ts +97 -0
  51. package/dist/useYVAuth.d.ts.map +1 -0
  52. package/dist/useYVAuth.js +135 -0
  53. package/dist/useYVAuth.js.map +1 -0
  54. package/package.json +5 -5
  55. package/src/__tests__/mocks/auth.ts +48 -0
  56. package/src/__tests__/utils/test-utils.tsx +43 -0
  57. package/src/context/YouVersionAuthContext.test.tsx +88 -0
  58. package/src/context/YouVersionAuthContext.tsx +20 -0
  59. package/src/context/YouVersionAuthProvider.test.tsx +373 -0
  60. package/src/context/YouVersionAuthProvider.tsx +90 -0
  61. package/src/context/{BibleSDKContext.tsx → YouVersionContext.tsx} +2 -2
  62. package/src/context/YouVersionProvider.tsx +65 -0
  63. package/src/context/index.ts +2 -2
  64. package/src/index.ts +6 -0
  65. package/src/types/auth.ts +20 -0
  66. package/src/useBibleClient.test.tsx +14 -14
  67. package/src/useBibleClient.ts +3 -3
  68. package/src/useHighlights.test.tsx +6 -6
  69. package/src/useHighlights.ts +3 -3
  70. package/src/useLanguages.test.tsx +6 -6
  71. package/src/useLanguages.ts +3 -3
  72. package/src/usePassage.ts +8 -15
  73. package/src/useVOTD.test.tsx +6 -6
  74. package/src/useYVAuth.test.tsx +378 -0
  75. package/src/useYVAuth.ts +179 -0
  76. package/dist/context/BibleSDKContext.d.ts +0 -8
  77. package/dist/context/BibleSDKContext.d.ts.map +0 -1
  78. package/dist/context/BibleSDKContext.js +0 -4
  79. package/dist/context/BibleSDKContext.js.map +0 -1
  80. package/dist/context/BibleSDKProvider.d.ts +0 -9
  81. package/dist/context/BibleSDKProvider.d.ts.map +0 -1
  82. package/dist/context/BibleSDKProvider.js +0 -18
  83. package/dist/context/BibleSDKProvider.js.map +0 -1
  84. package/src/context/BibleSDKProvider.tsx +0 -35
@@ -1,10 +1,10 @@
1
- "use client";
2
- import { useBibleClient } from "./useBibleClient";
3
- import { useApiData } from "./useApiData";
4
- export function usePassage({ versionId, usfm, format = "html", include_headings = false, include_notes = false, options, }) {
1
+ 'use client';
2
+ import { useBibleClient } from './useBibleClient';
3
+ import { useApiData } from './useApiData';
4
+ export function usePassage({ versionId, usfm, format = 'html', include_headings = false, include_notes = false, options, }) {
5
5
  const bibleClient = useBibleClient();
6
6
  // Don't attempt to fetch if usfm is invalid
7
- const isValidUsfm = Boolean(usfm) && usfm !== "undefined" && usfm !== "null";
7
+ const isValidUsfm = Boolean(usfm) && usfm !== 'undefined' && usfm !== 'null';
8
8
  const { data, loading, error, refetch } = useApiData(() => bibleClient.getPassage(versionId, usfm, format, include_headings, include_notes), [bibleClient, versionId, usfm, format, include_headings, include_notes], { enabled: options?.enabled !== false && isValidUsfm });
9
9
  return { passage: data, loading, error, refetch };
10
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"usePassage.js","sourceRoot":"","sources":["../src/usePassage.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAA0B,MAAM,cAAc,CAAC;AAYlE,MAAM,UAAU,UAAU,CAAC,EACzB,SAAS,EACT,IAAI,EACJ,MAAM,GAAG,MAAM,EACf,gBAAgB,GAAG,KAAK,EACxB,aAAa,GAAG,KAAK,EACrB,OAAO,GACS;IAMhB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,4CAA4C;IAC5C,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,MAAM,CAAC;IAE7E,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,CAClD,GAAG,EAAE,CACH,WAAW,CAAC,UAAU,CACpB,SAAS,EACT,IAAI,EACJ,MAAM,EACN,gBAAgB,EAChB,aAAa,CACd,EACH,CAAC,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,aAAa,CAAC,EACvE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,KAAK,IAAI,WAAW,EAAE,CACvD,CAAC;IAEF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"usePassage.js","sourceRoot":"","sources":["../src/usePassage.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAA0B,MAAM,cAAc,CAAC;AAYlE,MAAM,UAAU,UAAU,CAAC,EACzB,SAAS,EACT,IAAI,EACJ,MAAM,GAAG,MAAM,EACf,gBAAgB,GAAG,KAAK,EACxB,aAAa,GAAG,KAAK,EACrB,OAAO,GACS;IAMhB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,4CAA4C;IAC5C,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,MAAM,CAAC;IAE7E,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,CAClD,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,aAAa,CAAC,EACtF,CAAC,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,aAAa,CAAC,EACvE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,KAAK,IAAI,WAAW,EAAE,CACvD,CAAC;IAEF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACpD,CAAC"}
@@ -0,0 +1,97 @@
1
+ import { type AuthenticationState, type SignInWithYouVersionPermissionValues, type SignInWithYouVersionResult, type YouVersionUserInfo } from '@youversion/platform-core';
2
+ export interface UseYVAuthReturn {
3
+ auth: AuthenticationState;
4
+ signIn: (params: {
5
+ redirectUrl: string;
6
+ permissions?: SignInWithYouVersionPermissionValues[];
7
+ }) => Promise<void>;
8
+ signOut: () => void;
9
+ processCallback: () => Promise<SignInWithYouVersionResult | null>;
10
+ userInfo: YouVersionUserInfo | null;
11
+ }
12
+ /**
13
+ * Comprehensive YouVersion authentication hook using vanilla React.
14
+ *
15
+ * Provides complete auth functionality without external dependencies.
16
+ * Sign-in redirects to YouVersion, callback processing caches user info.
17
+ *
18
+ * @returns Complete auth state and functionality
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * // Sign-in page
23
+ * import { useYVAuth, SignInWithYouVersionPermission } from '@youversion/platform-react-hooks';
24
+ *
25
+ * function SignInPage() {
26
+ * const { auth, signIn } = useYVAuth();
27
+ *
28
+ * const handleSignIn = async () => {
29
+ * try {
30
+ * await signIn({
31
+ * redirectUrl: 'https://myapp.com/callback',
32
+ * permissions: [SignInWithYouVersionPermission.bibles]
33
+ * });
34
+ * } catch (error) {
35
+ * console.error('Sign in failed:', error);
36
+ * }
37
+ * };
38
+ *
39
+ * if (auth.isAuthenticated) {
40
+ * return <div>Already signed in!</div>;
41
+ * }
42
+ *
43
+ * return (
44
+ * <button onClick={handleSignIn} disabled={auth.isLoading}>
45
+ * {auth.isLoading ? 'Signing in...' : 'Sign In'}
46
+ * </button>
47
+ * );
48
+ * }
49
+ *
50
+ * // Callback page
51
+ * function CallbackPage() {
52
+ * const { processCallback } = useYVAuth();
53
+ * const [isProcessing, setIsProcessing] = useState(true);
54
+ *
55
+ * useEffect(() => {
56
+ * processCallback()
57
+ * .then(result => {
58
+ * if (result) {
59
+ * console.log('Auth success:', result.name);
60
+ * // User info is now cached
61
+ * // Redirect to app
62
+ * }
63
+ * })
64
+ * .catch(error => {
65
+ * console.error('Auth failed:', error);
66
+ * })
67
+ * .finally(() => {
68
+ * setIsProcessing(false);
69
+ * });
70
+ * }, [processCallback]);
71
+ *
72
+ * return (
73
+ * <div>
74
+ * {isProcessing ? 'Processing authentication...' : 'Authentication complete'}
75
+ * </div>
76
+ * );
77
+ * }
78
+ *
79
+ * // App with cached user info
80
+ * function App() {
81
+ * const { auth, userInfo, signOut } = useYVAuth();
82
+ *
83
+ * if (!auth.isAuthenticated) {
84
+ * return <SignInPage />;
85
+ * }
86
+ *
87
+ * return (
88
+ * <div>
89
+ * <p>Welcome, {userInfo?.name || 'User'}!</p>
90
+ * <button onClick={signOut}>Sign Out</button>
91
+ * </div>
92
+ * );
93
+ * }
94
+ * ```
95
+ */
96
+ export declare function useYVAuth(): UseYVAuthReturn;
97
+ //# sourceMappingURL=useYVAuth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useYVAuth.d.ts","sourceRoot":"","sources":["../src/useYVAuth.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,oCAAoC,EACzC,KAAK,0BAA0B,EAC/B,KAAK,kBAAkB,EACxB,MAAM,2BAA2B,CAAC;AAGnC,MAAM,WAAW,eAAe;IAE9B,IAAI,EAAE,mBAAmB,CAAC;IAG1B,MAAM,EAAE,CAAC,MAAM,EAAE;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,oCAAoC,EAAE,CAAC;KACtD,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;IAGpB,eAAe,EAAE,MAAM,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC,CAAC;IAGlE,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AACH,wBAAgB,SAAS,IAAI,eAAe,CAiE3C"}
@@ -0,0 +1,135 @@
1
+ import { useMemo, useCallback } from 'react';
2
+ import { YouVersionAPIUsers, YouVersionPlatformConfiguration, } from '@youversion/platform-core';
3
+ import { useYouVersionAuthContext } from './context/YouVersionAuthContext';
4
+ /**
5
+ * Comprehensive YouVersion authentication hook using vanilla React.
6
+ *
7
+ * Provides complete auth functionality without external dependencies.
8
+ * Sign-in redirects to YouVersion, callback processing caches user info.
9
+ *
10
+ * @returns Complete auth state and functionality
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * // Sign-in page
15
+ * import { useYVAuth, SignInWithYouVersionPermission } from '@youversion/platform-react-hooks';
16
+ *
17
+ * function SignInPage() {
18
+ * const { auth, signIn } = useYVAuth();
19
+ *
20
+ * const handleSignIn = async () => {
21
+ * try {
22
+ * await signIn({
23
+ * redirectUrl: 'https://myapp.com/callback',
24
+ * permissions: [SignInWithYouVersionPermission.bibles]
25
+ * });
26
+ * } catch (error) {
27
+ * console.error('Sign in failed:', error);
28
+ * }
29
+ * };
30
+ *
31
+ * if (auth.isAuthenticated) {
32
+ * return <div>Already signed in!</div>;
33
+ * }
34
+ *
35
+ * return (
36
+ * <button onClick={handleSignIn} disabled={auth.isLoading}>
37
+ * {auth.isLoading ? 'Signing in...' : 'Sign In'}
38
+ * </button>
39
+ * );
40
+ * }
41
+ *
42
+ * // Callback page
43
+ * function CallbackPage() {
44
+ * const { processCallback } = useYVAuth();
45
+ * const [isProcessing, setIsProcessing] = useState(true);
46
+ *
47
+ * useEffect(() => {
48
+ * processCallback()
49
+ * .then(result => {
50
+ * if (result) {
51
+ * console.log('Auth success:', result.name);
52
+ * // User info is now cached
53
+ * // Redirect to app
54
+ * }
55
+ * })
56
+ * .catch(error => {
57
+ * console.error('Auth failed:', error);
58
+ * })
59
+ * .finally(() => {
60
+ * setIsProcessing(false);
61
+ * });
62
+ * }, [processCallback]);
63
+ *
64
+ * return (
65
+ * <div>
66
+ * {isProcessing ? 'Processing authentication...' : 'Authentication complete'}
67
+ * </div>
68
+ * );
69
+ * }
70
+ *
71
+ * // App with cached user info
72
+ * function App() {
73
+ * const { auth, userInfo, signOut } = useYVAuth();
74
+ *
75
+ * if (!auth.isAuthenticated) {
76
+ * return <SignInPage />;
77
+ * }
78
+ *
79
+ * return (
80
+ * <div>
81
+ * <p>Welcome, {userInfo?.name || 'User'}!</p>
82
+ * <button onClick={signOut}>Sign Out</button>
83
+ * </div>
84
+ * );
85
+ * }
86
+ * ```
87
+ */
88
+ export function useYVAuth() {
89
+ // Get auth state from provider context
90
+ const { userInfo, setUserInfo, isLoading, error } = useYouVersionAuthContext();
91
+ // Derive authentication state
92
+ const isAuthenticated = !!userInfo;
93
+ // Get current tokens for actions
94
+ const authTokens = useMemo(() => {
95
+ if (typeof window !== 'undefined') {
96
+ return {
97
+ accessToken: YouVersionPlatformConfiguration.accessToken,
98
+ idToken: YouVersionPlatformConfiguration.idToken,
99
+ };
100
+ }
101
+ return { accessToken: null, idToken: null };
102
+ }, []);
103
+ // Sign in function
104
+ const signIn = useCallback(async ({ redirectUrl, permissions = [], }) => {
105
+ await YouVersionAPIUsers.signIn(new Set(permissions), redirectUrl);
106
+ // Note: This will redirect, so code after this won't execute
107
+ }, []);
108
+ // Process callback function
109
+ const processCallback = useCallback(async () => {
110
+ const result = await YouVersionAPIUsers.handleAuthCallback();
111
+ return result;
112
+ }, []);
113
+ // Derive auth state
114
+ const auth = useMemo(() => ({
115
+ isAuthenticated,
116
+ isLoading,
117
+ accessToken: authTokens.accessToken,
118
+ idToken: authTokens.idToken,
119
+ result: null,
120
+ error,
121
+ }), [isAuthenticated, isLoading, authTokens.accessToken, authTokens.idToken, error]);
122
+ // Sign out function
123
+ const signOut = useCallback(() => {
124
+ YouVersionPlatformConfiguration.clearAuthTokens();
125
+ setUserInfo(null);
126
+ }, []);
127
+ return {
128
+ auth,
129
+ signIn,
130
+ signOut,
131
+ processCallback,
132
+ userInfo,
133
+ };
134
+ }
135
+ //# sourceMappingURL=useYVAuth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useYVAuth.js","sourceRoot":"","sources":["../src/useYVAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EACL,kBAAkB,EAClB,+BAA+B,GAKhC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAoB3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AACH,MAAM,UAAU,SAAS;IACvB,uCAAuC;IACvC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,wBAAwB,EAAE,CAAC;IAE/E,8BAA8B;IAC9B,MAAM,eAAe,GAAG,CAAC,CAAC,QAAQ,CAAC;IAEnC,iCAAiC;IACjC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE;QAC9B,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,OAAO;gBACL,WAAW,EAAE,+BAA+B,CAAC,WAAW;gBACxD,OAAO,EAAE,+BAA+B,CAAC,OAAO;aACjD,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC9C,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,mBAAmB;IACnB,MAAM,MAAM,GAAG,WAAW,CACxB,KAAK,EAAE,EACL,WAAW,EACX,WAAW,GAAG,EAAE,GAIjB,EAAE,EAAE;QACH,MAAM,kBAAkB,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC;QACnE,6DAA6D;IAC/D,CAAC,EACD,EAAE,CACH,CAAC;IAEF,4BAA4B;IAC5B,MAAM,eAAe,GAAG,WAAW,CAAC,KAAK,IAAgD,EAAE;QACzF,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,kBAAkB,EAAE,CAAC;QAC7D,OAAO,MAAM,CAAC;IAChB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,oBAAoB;IACpB,MAAM,IAAI,GAAwB,OAAO,CACvC,GAAG,EAAE,CAAC,CAAC;QACL,eAAe;QACf,SAAS;QACT,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,MAAM,EAAE,IAAI;QACZ,KAAK;KACN,CAAC,EACF,CAAC,eAAe,EAAE,SAAS,EAAE,UAAU,CAAC,WAAW,EAAE,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAChF,CAAC;IAEF,oBAAoB;IACpB,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE;QAC/B,+BAA+B,CAAC,eAAe,EAAE,CAAC;QAClD,WAAW,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO;QACL,IAAI;QACJ,MAAM;QACN,OAAO;QACP,eAAe;QACf,QAAQ;KACT,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youversion/platform-react-hooks",
3
- "version": "0.5.8",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -22,7 +22,7 @@
22
22
  }
23
23
  },
24
24
  "dependencies": {
25
- "@youversion/platform-core": "0.5.8"
25
+ "@youversion/platform-core": "0.7.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "react": ">=19.1.0 <20.0.0"
@@ -36,14 +36,14 @@
36
36
  "eslint": "9.38.0",
37
37
  "typescript": "5.9.3",
38
38
  "vitest": "4.0.4",
39
- "@internal/eslint-config": "0.0.0",
40
- "@internal/tsconfig": "0.0.0"
39
+ "@internal/tsconfig": "0.0.0",
40
+ "@internal/eslint-config": "0.0.0"
41
41
  },
42
42
  "scripts": {
43
43
  "dev": "tsc --watch",
44
44
  "build": "tsc -p tsconfig.build.json",
45
45
  "lint": "eslint . --max-warnings 0",
46
- "check-types": "tsc --noEmit",
46
+ "typecheck": "tsc --noEmit",
47
47
  "test": "vitest run",
48
48
  "test:watch": "vitest",
49
49
  "test:coverage": "vitest run --coverage"
@@ -0,0 +1,48 @@
1
+ import { vi } from 'vitest';
2
+ import { SignInWithYouVersionResult, YouVersionUserInfo } from '@youversion/platform-core';
3
+
4
+ /**
5
+ * Creates a mock user info object for testing
6
+ * @param overrides Optional properties to override the default values
7
+ */
8
+ export const createMockUserInfo = (overrides: Record<string, any> = {}): YouVersionUserInfo => {
9
+ const mockUserInfo = new YouVersionUserInfo({
10
+ id: '123',
11
+ name: 'John Doe',
12
+ email: 'john@example.com',
13
+ avatar_url: 'https://example.com/avatar.jpg?w={width}&h={height}',
14
+ ...overrides,
15
+ });
16
+
17
+ // Mock the getAvatarUrl method for testing
18
+ vi.spyOn(mockUserInfo, 'getAvatarUrl').mockImplementation((width = 200, height = 200) => {
19
+ try {
20
+ return new URL(`https://example.com/avatar.jpg?w=${width}&h=${height}`);
21
+ } catch {
22
+ return null;
23
+ }
24
+ });
25
+
26
+ return mockUserInfo;
27
+ };
28
+
29
+ /**
30
+ * Creates a mock auth result for testing
31
+ * @param overrides Optional properties to override the default values
32
+ */
33
+ export const createMockAuthResult = (
34
+ overrides: Record<string, unknown> = {},
35
+ ): SignInWithYouVersionResult => {
36
+ const mockUserInfo = createMockUserInfo();
37
+ return new SignInWithYouVersionResult({
38
+ ...mockUserInfo,
39
+ accessToken: 'access-token',
40
+ idToken: 'id-token',
41
+ refreshToken: 'refresh-token',
42
+ expiresIn: 3600,
43
+ permissions: ['bibles', 'highlights'],
44
+ yvpUserId: 'test-yvp-user-id',
45
+ profilePicture: 'https://example.com/profile.jpg',
46
+ ...overrides,
47
+ });
48
+ };
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import { YouVersionProvider } from '../../context/YouVersionProvider';
3
+ import { useYouVersionAuthContext } from '../../context/YouVersionAuthContext';
4
+
5
+ /**
6
+ * Creates a test wrapper component with YouVersionAuthProvider
7
+ * @param config Optional auth configuration
8
+ */
9
+ export const createAuthProviderWrapper = (): React.ComponentType<{ children: React.ReactNode }> => {
10
+ return ({ children }: { children: React.ReactNode }) => (
11
+ <YouVersionProvider
12
+ appKey="test-app-key"
13
+ apiHost="test-api.example.com"
14
+ includeAuth={true}
15
+ authRedirectUrl="http://test.example.com"
16
+ >
17
+ {children}
18
+ </YouVersionProvider>
19
+ );
20
+ };
21
+
22
+ /**
23
+ * Test component to access auth context for testing
24
+ */
25
+ export function TestAuthChild({ onRender }: { onRender?: (data: any) => void }): React.JSX.Element {
26
+ const context = useYouVersionAuthContext();
27
+
28
+ React.useEffect(() => {
29
+ if (onRender) {
30
+ onRender(context);
31
+ }
32
+ }, [context, onRender]);
33
+
34
+ return (
35
+ <div>
36
+ <div data-testid="user-info">
37
+ {context.userInfo ? JSON.stringify(context.userInfo) : 'null'}
38
+ </div>
39
+ <div data-testid="is-loading">{context.isLoading.toString()}</div>
40
+ <div data-testid="error">{context.error ? context.error.message : 'null'}</div>
41
+ </div>
42
+ );
43
+ }
@@ -0,0 +1,88 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { renderHook } from '@testing-library/react';
3
+ import { type ReactNode } from 'react';
4
+ import { YouVersionAuthContext, useYouVersionAuthContext } from './YouVersionAuthContext';
5
+ import type { AuthContextValue } from '../types/auth';
6
+
7
+ const mockContextValue: AuthContextValue = {
8
+ userInfo: {
9
+ userId: '123',
10
+ name: 'John Doe',
11
+ email: 'john@example.com',
12
+ avatarUrlFormat: 'https://example.com/avatar.jpg',
13
+ getAvatarUrl: () => new URL('https://example.com/avatar.jpg'),
14
+ avatarUrl: new URL('https://example.com/avatar.jpg'),
15
+ },
16
+ setUserInfo: () => {
17
+ // Mock function for testing
18
+ },
19
+ isLoading: false,
20
+ error: null,
21
+ };
22
+
23
+ function TestWrapper({
24
+ children,
25
+ value = mockContextValue,
26
+ }: {
27
+ children: ReactNode;
28
+ value?: AuthContextValue | null;
29
+ }) {
30
+ return <YouVersionAuthContext.Provider value={value}>{children}</YouVersionAuthContext.Provider>;
31
+ }
32
+
33
+ describe('YouVersionAuthContext', () => {
34
+ describe('useYouVersionAuthContext', () => {
35
+ it('should return context value when used within provider', () => {
36
+ const { result } = renderHook(() => useYouVersionAuthContext(), {
37
+ wrapper: ({ children }) => <TestWrapper>{children}</TestWrapper>,
38
+ });
39
+
40
+ expect(result.current).toEqual(mockContextValue);
41
+ expect(result.current.userInfo).toEqual(mockContextValue.userInfo);
42
+ expect(result.current.isLoading).toBe(false);
43
+ expect(result.current.error).toBe(null);
44
+ expect(typeof result.current.setUserInfo).toBe('function');
45
+ });
46
+
47
+ it('should throw error when used outside provider', () => {
48
+ expect(() => {
49
+ renderHook(() => useYouVersionAuthContext());
50
+ }).toThrow(
51
+ 'useYouVersionAuthContext must be used within an auth provider. ' +
52
+ 'Make sure your app is wrapped with <YouVersionAuthProvider> from @youversion/platform-react-hooks ' +
53
+ 'or create your own provider using YouVersionAuthContext from @youversion/platform-react-hooks.',
54
+ );
55
+ });
56
+
57
+ it('should throw error when context value is null', () => {
58
+ expect(() => {
59
+ renderHook(() => useYouVersionAuthContext(), {
60
+ wrapper: ({ children }) => <TestWrapper value={null}>{children}</TestWrapper>,
61
+ });
62
+ }).toThrow(
63
+ 'useYouVersionAuthContext must be used within an auth provider. ' +
64
+ 'Make sure your app is wrapped with <YouVersionAuthProvider> from @youversion/platform-react-hooks ' +
65
+ 'or create your own provider using YouVersionAuthContext from @youversion/platform-react-hooks.',
66
+ );
67
+ });
68
+
69
+ it('should work with different context values', () => {
70
+ const customContextValue: AuthContextValue = {
71
+ userInfo: null,
72
+ setUserInfo: () => {
73
+ // Mock function for testing
74
+ },
75
+ isLoading: true,
76
+ error: new Error('Test error'),
77
+ };
78
+
79
+ const { result } = renderHook(() => useYouVersionAuthContext(), {
80
+ wrapper: ({ children }) => <TestWrapper value={customContextValue}>{children}</TestWrapper>,
81
+ });
82
+
83
+ expect(result.current.userInfo).toBe(null);
84
+ expect(result.current.isLoading).toBe(true);
85
+ expect(result.current.error?.message).toBe('Test error');
86
+ });
87
+ });
88
+ });
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+
3
+ import { createContext, useContext } from 'react';
4
+ import type { AuthContextValue } from '../types/auth';
5
+
6
+ export const YouVersionAuthContext = createContext<AuthContextValue | null>(null);
7
+
8
+ export function useYouVersionAuthContext(): AuthContextValue {
9
+ const context = useContext(YouVersionAuthContext);
10
+
11
+ if (!context) {
12
+ throw new Error(
13
+ 'useYouVersionAuthContext must be used within an auth provider. ' +
14
+ 'Make sure your app is wrapped with <YouVersionAuthProvider> from @youversion/platform-react-hooks ' +
15
+ 'or create your own provider using YouVersionAuthContext from @youversion/platform-react-hooks.',
16
+ );
17
+ }
18
+
19
+ return context;
20
+ }