@youversion/platform-core 0.8.1 → 0.9.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.
@@ -1,8 +1,6 @@
1
1
  import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
2
  import { SignInWithYouVersionPKCEAuthorizationRequestBuilder } from '../SignInWithYouVersionPKCE';
3
3
  import { YouVersionPlatformConfiguration } from '../YouVersionPlatformConfiguration';
4
- import { SignInWithYouVersionPermission } from '../SignInWithYouVersionResult';
5
- import type { SignInWithYouVersionPermissionValues } from '../types/auth';
6
4
  import { setupBrowserMocks, cleanupBrowserMocks } from './mocks/browser';
7
5
 
8
6
  describe('SignInWithYouVersionPKCEAuthorizationRequestBuilder', () => {
@@ -61,15 +59,10 @@ describe('SignInWithYouVersionPKCEAuthorizationRequestBuilder', () => {
61
59
  .mockReturnValueOnce('stateBase64==') // State
62
60
  .mockReturnValueOnce('nonceBase64=='); // Nonce
63
61
 
64
- const permissions = new Set<SignInWithYouVersionPermissionValues>([
65
- SignInWithYouVersionPermission.bibles,
66
- SignInWithYouVersionPermission.highlights,
67
- ]);
68
62
  const redirectURL = new URL('https://example.com/callback');
69
63
 
70
64
  const result = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
71
65
  'test-app-key',
72
- permissions,
73
66
  redirectURL,
74
67
  );
75
68
 
@@ -101,17 +94,14 @@ describe('SignInWithYouVersionPKCEAuthorizationRequestBuilder', () => {
101
94
  mocks.crypto.subtle.digest.mockResolvedValue(new Uint8Array(32).buffer);
102
95
  mocks.btoa.mockImplementation((str: string) => `base64_${callCount}_${str.length}`);
103
96
 
104
- const permissions = new Set<SignInWithYouVersionPermissionValues>();
105
97
  const redirectURL = new URL('https://example.com/callback');
106
98
 
107
99
  const result1 = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
108
100
  'app-key',
109
- permissions,
110
101
  redirectURL,
111
102
  );
112
103
  const result2 = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
113
104
  'app-key',
114
- permissions,
115
105
  redirectURL,
116
106
  );
117
107
 
@@ -136,14 +126,10 @@ describe('SignInWithYouVersionPKCEAuthorizationRequestBuilder', () => {
136
126
  });
137
127
 
138
128
  it('should build authorization URL with all required OAuth2 parameters', async () => {
139
- const permissions = new Set<SignInWithYouVersionPermissionValues>([
140
- SignInWithYouVersionPermission.bibles,
141
- ]);
142
129
  const redirectURL = new URL('https://example.com/callback');
143
130
 
144
131
  const result = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
145
132
  'test-app-key',
146
- permissions,
147
133
  redirectURL,
148
134
  );
149
135
 
@@ -163,12 +149,10 @@ describe('SignInWithYouVersionPKCEAuthorizationRequestBuilder', () => {
163
149
  });
164
150
 
165
151
  it('should handle redirect URL with trailing slash', async () => {
166
- const permissions = new Set<SignInWithYouVersionPermissionValues>();
167
152
  const redirectURL = new URL('https://example.com/callback/');
168
153
 
169
154
  const result = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
170
155
  'test-app-key',
171
- permissions,
172
156
  redirectURL,
173
157
  );
174
158
 
@@ -177,12 +161,10 @@ describe('SignInWithYouVersionPKCEAuthorizationRequestBuilder', () => {
177
161
  });
178
162
 
179
163
  it('should include x-yvp-installation-id param', async () => {
180
- const permissions = new Set<SignInWithYouVersionPermissionValues>();
181
164
  const redirectURL = new URL('https://example.com/callback');
182
165
 
183
166
  const result = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
184
167
  'test-app-key',
185
- permissions,
186
168
  redirectURL,
187
169
  );
188
170
 
@@ -190,73 +172,41 @@ describe('SignInWithYouVersionPKCEAuthorizationRequestBuilder', () => {
190
172
  expect(params.get('x-yvp-installation-id')).not.toBeFalsy();
191
173
  });
192
174
 
193
- it('should create scope string with permissions and openid', async () => {
194
- const permissions = new Set<SignInWithYouVersionPermissionValues>([
195
- SignInWithYouVersionPermission.bibles,
196
- SignInWithYouVersionPermission.highlights,
197
- ]);
175
+ it('should create scope string with profile and openid', async () => {
198
176
  const redirectURL = new URL('https://example.com/callback');
199
177
 
200
178
  const result = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
201
179
  'test-app-key',
202
- permissions,
203
180
  redirectURL,
181
+ ['profile'],
204
182
  );
205
183
 
206
184
  const params = new URLSearchParams(result.url.search);
207
185
  const scope = params.get('scope');
208
186
 
209
- expect(scope).toContain('bibles');
210
- expect(scope).toContain('highlights');
211
- expect(scope).toContain('openid');
212
- });
213
-
214
- it('should sort permissions alphabetically', async () => {
215
- const permissions = new Set<SignInWithYouVersionPermissionValues>([
216
- SignInWithYouVersionPermission.votd,
217
- SignInWithYouVersionPermission.bibles,
218
- SignInWithYouVersionPermission.demographics,
219
- ]);
220
- const redirectURL = new URL('https://example.com/callback');
221
-
222
- const result = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
223
- 'test-app-key',
224
- permissions,
225
- redirectURL,
226
- );
227
-
228
- const params = new URLSearchParams(result.url.search);
229
- const scope = params.get('scope');
230
-
231
- // Should be sorted: bibles demographics votd openid
232
- expect(scope).toBe('bibles demographics votd openid');
187
+ expect(scope).toContain('profile');
233
188
  });
234
189
 
235
190
  it('should add openid when not present', async () => {
236
- const permissions = new Set<SignInWithYouVersionPermissionValues>([
237
- SignInWithYouVersionPermission.bibles,
238
- ]);
239
191
  const redirectURL = new URL('https://example.com/callback');
240
192
 
241
193
  const result = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
242
194
  'test-app-key',
243
- permissions,
244
195
  redirectURL,
196
+ ['profile'],
245
197
  );
246
198
 
247
199
  const params = new URLSearchParams(result.url.search);
248
200
  const scope = params.get('scope');
249
201
 
250
- expect(scope).toBe('bibles openid');
202
+ expect(scope).toBe('profile openid');
251
203
  });
252
204
 
253
- it('should handle empty permissions set', async () => {
254
- const permissions = new Set<SignInWithYouVersionPermissionValues>();
205
+ it('should handle empty scope params', async () => {
255
206
  const redirectURL = new URL('https://example.com/callback');
256
207
 
257
208
  const result = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
258
209
  'test-app-key',
259
- permissions,
260
210
  redirectURL,
261
211
  );
262
212
 
@@ -267,16 +217,10 @@ describe('SignInWithYouVersionPKCEAuthorizationRequestBuilder', () => {
267
217
  });
268
218
 
269
219
  it('should not duplicate openid if already present', async () => {
270
- // This test simulates if openid was somehow in the permissions set
271
- const permissions = new Set<SignInWithYouVersionPermissionValues>([
272
- 'openid' as SignInWithYouVersionPermissionValues,
273
- SignInWithYouVersionPermission.bibles,
274
- ]);
275
220
  const redirectURL = new URL('https://example.com/callback');
276
221
 
277
222
  const result = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
278
223
  'test-app-key',
279
- permissions,
280
224
  redirectURL,
281
225
  );
282
226
 
@@ -347,14 +291,9 @@ describe('SignInWithYouVersionPKCEAuthorizationRequestBuilder', () => {
347
291
  });
348
292
 
349
293
  it('should use crypto.getRandomValues for secure random generation', async () => {
350
- const permissions = new Set<SignInWithYouVersionPermissionValues>();
351
294
  const redirectURL = new URL('https://example.com/callback');
352
295
 
353
- await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
354
- 'test-app-key',
355
- permissions,
356
- redirectURL,
357
- );
296
+ await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make('test-app-key', redirectURL);
358
297
 
359
298
  // Should call crypto.getRandomValues for code verifier, state, and nonce
360
299
  expect(mocks.crypto.getRandomValues).toHaveBeenCalledTimes(3);
@@ -367,14 +306,9 @@ describe('SignInWithYouVersionPKCEAuthorizationRequestBuilder', () => {
367
306
  });
368
307
 
369
308
  it('should use SHA-256 for code challenge', async () => {
370
- const permissions = new Set<SignInWithYouVersionPermissionValues>();
371
309
  const redirectURL = new URL('https://example.com/callback');
372
310
 
373
- await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
374
- 'test-app-key',
375
- permissions,
376
- redirectURL,
377
- );
311
+ await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make('test-app-key', redirectURL);
378
312
 
379
313
  expect(mocks.crypto.subtle.digest).toHaveBeenCalledWith('SHA-256', expect.any(Uint8Array));
380
314
  });
@@ -383,17 +317,14 @@ describe('SignInWithYouVersionPKCEAuthorizationRequestBuilder', () => {
383
317
  // Use real crypto for this test to verify actual randomness
384
318
  cleanupBrowserMocks();
385
319
 
386
- const permissions = new Set<SignInWithYouVersionPermissionValues>();
387
320
  const redirectURL = new URL('https://example.com/callback');
388
321
 
389
322
  const result1 = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
390
323
  'test-app-key',
391
- permissions,
392
324
  redirectURL,
393
325
  );
394
326
  const result2 = await SignInWithYouVersionPKCEAuthorizationRequestBuilder.make(
395
327
  'test-app-key',
396
- permissions,
397
328
  redirectURL,
398
329
  );
399
330
 
@@ -9,7 +9,6 @@ describe('SignInWithYouVersionResult', () => {
9
9
  accessToken: 'test-access-token',
10
10
  expiresIn: 3600,
11
11
  refreshToken: 'test-refresh-token',
12
- permissions: ['votd', 'bibles'],
13
12
  yvpUserId: 'test-user-id',
14
13
  name: 'test user',
15
14
  profilePicture: 'https://this-is-a-test-picture.com',
@@ -19,7 +18,6 @@ describe('SignInWithYouVersionResult', () => {
19
18
  expect(result.accessToken).toBe('test-access-token');
20
19
  expect(result.expiryDate).toStrictEqual(new Date(fixedDate.getTime() + 60 * 60 * 1000));
21
20
  expect(result.refreshToken).toBe('test-refresh-token');
22
- expect(result.permissions).toStrictEqual(['votd', 'bibles']);
23
21
  expect(result.yvpUserId).toBe('test-user-id');
24
22
  expect(result.name).toBe('test user');
25
23
  expect(result.profilePicture).toBe('https://this-is-a-test-picture.com');
@@ -1,9 +1,7 @@
1
1
  import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
2
  import { YouVersionAPIUsers } from '../Users';
3
3
  import { YouVersionPlatformConfiguration } from '../YouVersionPlatformConfiguration';
4
- import { SignInWithYouVersionPermission } from '../SignInWithYouVersionResult';
5
4
  import { YouVersionUserInfo } from '../YouVersionUserInfo';
6
- import type { SignInWithYouVersionPermissionValues } from '../types/auth';
7
5
  import { setupBrowserMocks, cleanupBrowserMocks } from './mocks/browser';
8
6
 
9
7
  const mockFetch = vi.fn();
@@ -40,13 +38,9 @@ describe('YouVersionAPIUsers', () => {
40
38
  it('should throw error when appKey is not set', async () => {
41
39
  YouVersionPlatformConfiguration.appKey = null;
42
40
 
43
- const permissions = new Set<SignInWithYouVersionPermissionValues>([
44
- SignInWithYouVersionPermission.bibles,
45
- ]);
46
-
47
- await expect(
48
- YouVersionAPIUsers.signIn(permissions, 'https://example.com/callback'),
49
- ).rejects.toThrow('YouVersionPlatformConfiguration.appKey must be set before calling signIn');
41
+ await expect(YouVersionAPIUsers.signIn('https://example.com/callback')).rejects.toThrow(
42
+ 'YouVersionPlatformConfiguration.appKey must be set before calling signIn',
43
+ );
50
44
  });
51
45
 
52
46
  it('should create authorization request and redirect on successful signIn', async () => {
@@ -62,13 +56,9 @@ describe('YouVersionAPIUsers', () => {
62
56
  vi.spyOn(crypto.subtle, 'digest').mockResolvedValue(new Uint8Array(32).buffer);
63
57
  mocks.btoa.mockReturnValue('mockBase64Value');
64
58
 
65
- const permissions = new Set<SignInWithYouVersionPermissionValues>([
66
- SignInWithYouVersionPermission.bibles,
67
- SignInWithYouVersionPermission.highlights,
68
- ]);
69
59
  const redirectURL = 'https://example.com/callback';
70
60
 
71
- await YouVersionAPIUsers.signIn(permissions, redirectURL);
61
+ await YouVersionAPIUsers.signIn(redirectURL);
72
62
 
73
63
  // Verify localStorage items stored
74
64
  expect(mocks.localStorage.setItem).toHaveBeenCalledWith(
@@ -210,7 +200,6 @@ describe('YouVersionAPIUsers', () => {
210
200
  expect(result).toBeTruthy();
211
201
  expect(result?.accessToken).toBe('access-token-123');
212
202
  expect(result?.refreshToken).toBe('refresh-token-456');
213
- expect(result?.permissions).toEqual(['bibles', 'highlights']);
214
203
  expect(result?.yvpUserId).toBe('1234567890');
215
204
  expect(result?.name).toBe('John Doe');
216
205
  expect(result?.email).toBe('john@example.com');
@@ -323,32 +312,11 @@ describe('YouVersionAPIUsers', () => {
323
312
  expect(result.accessToken).toBe('access-token-123');
324
313
  expect(result.expiryDate).toStrictEqual(new Date(fixedDate.getTime() + 60 * 60 * 1000));
325
314
  expect(result.refreshToken).toBe('refresh-token-456');
326
- expect(result.permissions).toEqual(['bibles', 'highlights']);
327
315
  expect(result.yvpUserId).toBe('1234567890');
328
316
  expect(result.name).toBe('John Doe');
329
317
  expect(result.email).toBe('john@example.com');
330
318
  expect(result.profilePicture).toBe('https://example.com/avatar.jpg');
331
319
  });
332
-
333
- it('should filter out unknown permissions', () => {
334
- const tokens = {
335
- access_token: 'token',
336
- expires_in: 3600,
337
- id_token:
338
- 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e30.Et9HFtf9R3GEMA0IICOfFMVXY7kkTX1wr4qCyhIf58U',
339
- refresh_token: 'refresh',
340
- scope: 'bibles unknown_permission highlights invalid_scope',
341
- token_type: 'Bearer',
342
- };
343
-
344
- // Mock JWT decoding
345
- vi.mocked(atob).mockReturnValue(JSON.stringify({ sub: 'user123' }));
346
-
347
- // @ts-expect-error - accessing private method for testing
348
- const result = YouVersionAPIUsers.extractSignInResult(tokens);
349
-
350
- expect(result.permissions).toEqual(['bibles', 'highlights']);
351
- });
352
320
  });
353
321
 
354
322
  describe('decodeJWT', () => {
@@ -557,8 +525,6 @@ describe('YouVersionAPIUsers', () => {
557
525
  // Assert that id_token is preserved (same as original)
558
526
  expect(result?.idToken).toBe(existingIdToken);
559
527
 
560
- expect(result?.permissions).toEqual(['bibles', 'highlights']);
561
-
562
528
  // Verify the refresh token request was made correctly
563
529
  expect(mockFetch).toHaveBeenCalledTimes(1);
564
530
  expect(mockFetch).toHaveBeenCalledWith(
@@ -75,8 +75,10 @@ describe('BibleClient', () => {
75
75
  expect(books.data).toHaveLength(66);
76
76
  expect(books.data[0]).toHaveProperty('id', 'GEN');
77
77
  expect(books.data[0]).toHaveProperty('title', 'Genesis');
78
+ expect(books.data[0]).toHaveProperty('full_title', 'The First Book of Moses, Called Genesis');
78
79
  expect(books.data[0]).toHaveProperty('abbreviation', 'Gen');
79
- expect(books.data[0]).toHaveProperty('canon', 'ot');
80
+ expect(books.data[0]).toHaveProperty('canon', 'old_testament');
81
+ expect(books.data[0]?.chapters).toHaveLength(50);
80
82
  });
81
83
  });
82
84
 
@@ -91,7 +93,7 @@ describe('BibleClient', () => {
91
93
  expect(book).toHaveProperty('id', 'GEN');
92
94
  expect(book).toHaveProperty('title', 'Genesis');
93
95
  expect(book).toHaveProperty('abbreviation', 'Gen');
94
- expect(book).toHaveProperty('canon', 'ot');
96
+ expect(book).toHaveProperty('canon', 'old_testament');
95
97
  });
96
98
 
97
99
  it('should throw an error for invalid inputs', async () => {
@@ -116,7 +118,6 @@ describe('BibleClient', () => {
116
118
 
117
119
  expect(chapters.data).toHaveLength(50);
118
120
  expect(chapters.data[0]).toHaveProperty('id', '1');
119
- expect(chapters.data[0]).toHaveProperty('book_id', 'GEN');
120
121
  expect(chapters.data[0]).toHaveProperty('passage_id', 'GEN.1');
121
122
  expect(chapters.data[0]).toHaveProperty('title', '1');
122
123
  expect(chapters.data[0]?.verses).toHaveLength(31);
@@ -131,7 +132,6 @@ describe('BibleClient', () => {
131
132
  expect(success).toBe(true);
132
133
 
133
134
  expect(chapter).toHaveProperty('id', '1');
134
- expect(chapter).toHaveProperty('book_id', 'GEN');
135
135
  expect(chapter).toHaveProperty('passage_id', 'GEN.1');
136
136
  expect(chapter).toHaveProperty('title', '1');
137
137
  expect(chapter.verses).toHaveLength(31);
@@ -154,11 +154,9 @@ describe('BibleClient', () => {
154
154
  const { success } = BibleVerseSchema.safeParse(verses.data[0]);
155
155
  expect(success).toBe(true);
156
156
 
157
- expect(verses.data).toHaveLength(24);
157
+ expect(verses.data).toHaveLength(31);
158
158
  expect(verses.data[0]).toHaveProperty('id', '1');
159
- expect(verses.data[0]).toHaveProperty('reference', 'Genesis 1:1');
160
- expect(verses.data[0]).toHaveProperty('book_id', 'GEN');
161
- expect(verses.data[0]).toHaveProperty('chapter_id', '1');
159
+ expect(verses.data[0]).toHaveProperty('title', '1');
162
160
  expect(verses.data[0]).toHaveProperty('passage_id', 'GEN.1.1');
163
161
  });
164
162
  });
@@ -171,9 +169,7 @@ describe('BibleClient', () => {
171
169
  expect(success).toBe(true);
172
170
 
173
171
  expect(verse).toHaveProperty('id', '1');
174
- expect(verse).toHaveProperty('reference', 'Genesis 1:1');
175
- expect(verse).toHaveProperty('book_id', 'GEN');
176
- expect(verse).toHaveProperty('chapter_id', '1');
172
+ expect(verse).toHaveProperty('title', '1');
177
173
  expect(verse).toHaveProperty('passage_id', 'GEN.1.1');
178
174
  });
179
175
 
@@ -210,8 +206,7 @@ describe('BibleClient', () => {
210
206
  id: 'GEN.1.1',
211
207
  content:
212
208
  '<div><div class="pi"><span class="yv-v" v="1"></span><span class="yv-vlbl">1</span>In the beginning God created the heavens and the earth. </div></div>',
213
- bible_id: 111,
214
- human_reference: 'Genesis 1:1',
209
+ reference: 'Genesis 1:1',
215
210
  });
216
211
  });
217
212
 
@@ -222,8 +217,7 @@ describe('BibleClient', () => {
222
217
  expect(success).toBe(true);
223
218
 
224
219
  expect(passage).toHaveProperty('id', 'GEN.1');
225
- expect(passage).toHaveProperty('bible_id', 111);
226
- expect(passage).toHaveProperty('human_reference', 'Genesis 1');
220
+ expect(passage).toHaveProperty('reference', 'Genesis 1');
227
221
  });
228
222
 
229
223
  it('should fetch a passage with html format by default', async () => {
@@ -242,7 +236,6 @@ describe('BibleClient', () => {
242
236
  const passage = await bibleClient.getPassage(111, 'ROM.1', 'html', true);
243
237
 
244
238
  expect(passage.id).toBe('ROM.1');
245
- expect(passage.bible_id).toBe(111);
246
239
  expect(passage.content).toContain('yv-h');
247
240
  expect(passage.content).not.toContain('yv-n');
248
241
  });
@@ -251,7 +244,6 @@ describe('BibleClient', () => {
251
244
  const passage = await bibleClient.getPassage(111, 'ROM.1', 'html', undefined, true);
252
245
 
253
246
  expect(passage.id).toBe('ROM.1');
254
- expect(passage.bible_id).toBe(111);
255
247
  expect(passage.content).toContain('yv-n');
256
248
  expect(passage.content).not.toContain('yv-h');
257
249
  });
@@ -260,7 +252,6 @@ describe('BibleClient', () => {
260
252
  const passage = await bibleClient.getPassage(111, 'ROM.1', 'html', true, true);
261
253
 
262
254
  expect(passage.id).toBe('ROM.1');
263
- expect(passage.bible_id).toBe(111);
264
255
  expect(passage.content).toContain('yv-n');
265
256
  expect(passage.content).toContain('yv-h');
266
257
  });
@@ -1,8 +1,17 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
1
+ import { describe, it, expect, beforeEach, beforeAll, afterEach, afterAll } from 'vitest';
2
2
  import { ApiClient } from '../client';
3
3
  import { http, HttpResponse } from 'msw';
4
4
  import { server } from './setup';
5
5
 
6
+ // We always want this test to hit msw since this is only testing
7
+ // the setup of and ApiClient instance and not actually hitting
8
+ // any real APIs.
9
+ if (process.env.INTEGRATION_TESTS) {
10
+ beforeAll(() => server.listen());
11
+ afterEach(() => server.resetHandlers());
12
+ afterAll(() => server.close());
13
+ }
14
+
6
15
  describe('ApiClient', () => {
7
16
  let apiClient: ApiClient;
8
17
 
@@ -4,14 +4,17 @@ import { handlers } from './handlers';
4
4
 
5
5
  export const server = setupServer(...handlers);
6
6
 
7
- beforeAll(() => {
8
- server.listen();
9
- });
7
+ // Only setup MSW if INTEGRATION_TESTS env var is not set
8
+ if (!process.env.INTEGRATION_TESTS) {
9
+ beforeAll(() => {
10
+ server.listen();
11
+ });
10
12
 
11
- afterEach(() => {
12
- server.resetHandlers();
13
- });
13
+ afterEach(() => {
14
+ server.resetHandlers();
15
+ });
14
16
 
15
- afterAll(() => {
16
- server.close();
17
- });
17
+ afterAll(() => {
18
+ server.close();
19
+ });
20
+ }
package/src/bible.ts CHANGED
@@ -81,7 +81,7 @@ export class BibleClient {
81
81
  /**
82
82
  * Fetches all books for a given Bible version.
83
83
  * @param versionId The version ID.
84
- * @param canon Optional canon filter ('ot', 'nt', 'deuterocanon').
84
+ * @param canon Optional canon filter ("old_testament", 'new_testament', 'deuterocanon').
85
85
  * @returns An array of BibleBook objects.
86
86
  */
87
87
  async getBooks(versionId: number, canon?: CANON): Promise<Collection<BibleBook>> {
package/src/index.ts CHANGED
@@ -11,7 +11,6 @@ export * from './Users';
11
11
  export * from './YouVersionUserInfo';
12
12
  export * from './SignInWithYouVersionResult';
13
13
  export * from './YouVersionAPI';
14
- export * from './URLBuilder';
15
14
  export * from './YouVersionPlatformConfiguration';
16
15
  export * from './types';
17
16
  export * from './utils/constants';
@@ -1,11 +1,8 @@
1
1
  import { z } from 'zod';
2
2
  import { BOOK_IDS } from '../utils/constants';
3
+ import { BibleChapterSchema } from './chapter';
3
4
 
4
- export const CanonSchema = z.enum([
5
- 'ot', // Old Testament
6
- 'nt', // New Testament
7
- 'dc', // Deuterocanon (Apocrypha)
8
- ]);
5
+ export const CanonSchema = z.enum(['old_testament', 'new_testament', 'deuterocanon']);
9
6
  export type Canon = z.infer<typeof CanonSchema>;
10
7
 
11
8
  // https://github.com/colinhacks/zod/discussions/4934#discussioncomment-13858053
@@ -20,14 +17,14 @@ export const BibleBookSchema = z.object({
20
17
  id: BookUsfmSchema,
21
18
  /** Book title (e.g., "Genesis") */
22
19
  title: z.string(),
20
+ /** Full Book title (e.g., "The First Book of Moses, Commonly Called Genesis") */
21
+ full_title: z.string(),
23
22
  /** Book abbreviation (e.g., "Gen") */
24
23
  abbreviation: z.string().optional(),
25
24
  /** Canonical section (new_testament, old_testament, deuterocanon) */
26
25
  canon: CanonSchema,
27
26
  /** Array of chapter identifiers (e.g., ["GEN.1", "GEN.2", "GEN.3"]) */
28
- chapters: z
29
- .array(z.string().regex(/^\w{3}\.\d+$/, { message: 'Chapter must be an integer string' }))
30
- .optional(),
27
+ chapters: z.array(BibleChapterSchema).optional(),
31
28
  });
32
29
 
33
30
  export type BibleBook = z.infer<typeof BibleBookSchema>;
@@ -1,24 +1,15 @@
1
1
  import { z } from 'zod';
2
- import { BookUsfmSchema } from './book';
2
+ import { BibleVerseSchema } from './verse';
3
3
 
4
4
  export const BibleChapterSchema = z.object({
5
5
  /** Chapter identifier (e.g., "1") */
6
6
  id: z.string(),
7
- /** Book identifier (e.g., "MAT") */
8
- book_id: BookUsfmSchema,
9
7
  /** Passage identifier (e.g., "MAT.1") */
10
8
  passage_id: z.string(),
11
9
  /** Chapter title (e.g., "1") */
12
10
  title: z.string(),
13
- /** Array of verse identifiers (e.g., ["1", "2", "3"]) */
14
- verses: z
15
- .array(
16
- z
17
- .string()
18
- .regex(/^\d+$/, { message: 'Verse must be an integer string' })
19
- .transform((val) => val as `${number}`),
20
- )
21
- .optional(),
11
+ /** Array of verses */
12
+ verses: z.array(BibleVerseSchema).optional(),
22
13
  });
23
14
 
24
15
  export type BibleChapter = z.infer<typeof BibleChapterSchema>;
@@ -5,10 +5,8 @@ export const BiblePassageSchema = z.object({
5
5
  id: z.string(),
6
6
  /** Passage content text */
7
7
  content: z.string(),
8
- /** Bible version identifier */
9
- bible_id: z.number().int().positive(),
10
8
  /** Human-readable reference (e.g., "Matthew 1:1") */
11
- human_reference: z.string(),
9
+ reference: z.string(),
12
10
  });
13
11
 
14
12
  export type BiblePassage = z.infer<typeof BiblePassageSchema>;
@@ -1,17 +1,12 @@
1
1
  import { z } from 'zod';
2
- import { BookUsfmSchema } from './book';
3
2
 
4
3
  export const BibleVerseSchema = z.object({
5
4
  /** Verse identifier (e.g., "1") */
6
5
  id: z.string(),
7
- /** Book identifier (e.g., "MAT") */
8
- book_id: BookUsfmSchema,
9
- /** Chapter identifier (e.g., "1") */
10
- chapter_id: z.string(),
11
6
  /** Passage identifier (e.g., "MAT.1.1") */
12
7
  passage_id: z.string(),
13
- /** Human-readable reference (e.g., "Matthew 1:1") */
14
- reference: z.string(),
8
+ /** Verse Number (e.g., "1") */
9
+ title: z.string(),
15
10
  });
16
11
 
17
12
  export type BibleVerse = z.infer<typeof BibleVerseSchema>;
@@ -17,9 +17,9 @@ export const BibleVersionSchema = z.object({
17
17
  /** Language tag (e.g., "en") */
18
18
  language_tag: z.string(),
19
19
  /** Localized abbreviation */
20
- local_abbreviation: z.string(),
20
+ localized_abbreviation: z.string(),
21
21
  /** Localized title */
22
- local_title: z.string(),
22
+ localized_title: z.string(),
23
23
  /** Organization ID of publisher */
24
24
  organization_id: z.string().nullable().optional(),
25
25
  /** Full title */
package/src/types/auth.ts CHANGED
@@ -14,3 +14,5 @@ export interface AuthenticationState {
14
14
  result: SignInWithYouVersionResult | null;
15
15
  error: Error | null;
16
16
  }
17
+
18
+ export type AuthenticationScopes = 'profile' | 'email';
@@ -18,5 +18,9 @@ export type { Collection } from '../schemas/collection';
18
18
 
19
19
  // Re-export internal/non-API types
20
20
  export type { ApiConfig } from './api-config';
21
- export type { AuthenticationState, SignInWithYouVersionPermissionValues } from './auth';
21
+ export type {
22
+ AuthenticationState,
23
+ SignInWithYouVersionPermissionValues,
24
+ AuthenticationScopes,
25
+ } from './auth';
22
26
  export type { HighlightColor } from './highlight';