@youversion/platform-core 0.4.2 → 0.4.4
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/.env.example +3 -5
- package/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +12 -0
- package/README.md +62 -322
- package/dist/index.cjs +42 -51
- package/dist/index.d.cts +7 -12
- package/dist/index.d.ts +7 -12
- package/dist/index.js +42 -51
- package/package.json +3 -3
- package/src/URLBuilder.ts +4 -4
- package/src/Users.ts +4 -4
- package/src/YouVersionAPI.ts +4 -6
- package/src/YouVersionPlatformConfiguration.ts +6 -6
- package/src/__tests__/URLBuilder.test.ts +34 -30
- package/src/__tests__/YouVersionPlatformConfiguration.test.ts +22 -13
- package/src/__tests__/authentication.test.ts +2 -3
- package/src/__tests__/bible.test.ts +2 -3
- package/src/__tests__/client.test.ts +12 -16
- package/src/__tests__/handlers.ts +7 -4
- package/src/__tests__/highlights.test.ts +2 -3
- package/src/__tests__/languages.test.ts +2 -3
- package/src/bible.ts +12 -19
- package/src/client.ts +7 -4
- package/src/highlights.ts +3 -7
- package/src/languages.ts +2 -6
- package/src/types/api-config.ts +2 -4
- package/.env.local +0 -10
package/dist/index.js
CHANGED
|
@@ -7,18 +7,21 @@ var ApiClient = class {
|
|
|
7
7
|
/**
|
|
8
8
|
* Creates an instance of ApiClient.
|
|
9
9
|
*
|
|
10
|
-
* @param config - The API configuration object containing baseUrl, timeout, and
|
|
10
|
+
* @param config - The API configuration object containing baseUrl, timeout, and appKey.
|
|
11
11
|
*/
|
|
12
12
|
constructor(config) {
|
|
13
13
|
this.config = {
|
|
14
|
-
version: config.version || "v1",
|
|
15
14
|
...config
|
|
16
15
|
};
|
|
17
|
-
|
|
16
|
+
const apiHost = config.apiHost || "api.youversion.com";
|
|
17
|
+
if (!apiHost) {
|
|
18
|
+
throw new Error("ApiClient requires a host name. Provide an apiHost in the config.");
|
|
19
|
+
}
|
|
20
|
+
this.baseURL = "https://" + apiHost;
|
|
18
21
|
this.timeout = config.timeout || 1e4;
|
|
19
22
|
this.defaultHeaders = {
|
|
20
23
|
"Content-Type": "application/json",
|
|
21
|
-
"X-YVP-App-Key": this.config.
|
|
24
|
+
"X-YVP-App-Key": this.config.appKey,
|
|
22
25
|
"X-YVP-Installation-Id": this.config.installationId || "web-sdk-default"
|
|
23
26
|
};
|
|
24
27
|
}
|
|
@@ -160,9 +163,6 @@ var BibleClient = class {
|
|
|
160
163
|
constructor(client) {
|
|
161
164
|
this.client = client;
|
|
162
165
|
}
|
|
163
|
-
get rootPath() {
|
|
164
|
-
return `/${this.client.config.version}`;
|
|
165
|
-
}
|
|
166
166
|
/**
|
|
167
167
|
* Fetches a collection of Bible versions filtered by language ranges.
|
|
168
168
|
*
|
|
@@ -178,7 +178,7 @@ var BibleClient = class {
|
|
|
178
178
|
if (license_id !== void 0) {
|
|
179
179
|
params.license_id = license_id;
|
|
180
180
|
}
|
|
181
|
-
return this.client.get(
|
|
181
|
+
return this.client.get(`/v1/bibles`, params);
|
|
182
182
|
}
|
|
183
183
|
/**
|
|
184
184
|
* Fetches a Bible version by its ID.
|
|
@@ -187,7 +187,7 @@ var BibleClient = class {
|
|
|
187
187
|
*/
|
|
188
188
|
async getVersion(id) {
|
|
189
189
|
this.versionIdSchema.parse(id);
|
|
190
|
-
return this.client.get(
|
|
190
|
+
return this.client.get(`/v1/bibles/${id}`);
|
|
191
191
|
}
|
|
192
192
|
/**
|
|
193
193
|
* Fetches all books for a given Bible version.
|
|
@@ -197,7 +197,7 @@ var BibleClient = class {
|
|
|
197
197
|
*/
|
|
198
198
|
async getBooks(versionId, canon) {
|
|
199
199
|
this.versionIdSchema.parse(versionId);
|
|
200
|
-
return this.client.get(
|
|
200
|
+
return this.client.get(`/v1/bibles/${versionId}/books`, {
|
|
201
201
|
...canon && { canon }
|
|
202
202
|
});
|
|
203
203
|
}
|
|
@@ -210,7 +210,7 @@ var BibleClient = class {
|
|
|
210
210
|
async getBook(versionId, book) {
|
|
211
211
|
this.versionIdSchema.parse(versionId);
|
|
212
212
|
this.bookSchema.parse(book);
|
|
213
|
-
return this.client.get(
|
|
213
|
+
return this.client.get(`/v1/bibles/${versionId}/books/${book}`);
|
|
214
214
|
}
|
|
215
215
|
/**
|
|
216
216
|
* Fetches all chapters for a specific book in a version.
|
|
@@ -222,7 +222,7 @@ var BibleClient = class {
|
|
|
222
222
|
this.versionIdSchema.parse(versionId);
|
|
223
223
|
this.bookSchema.parse(book);
|
|
224
224
|
return this.client.get(
|
|
225
|
-
|
|
225
|
+
`/v1/bibles/${versionId}/books/${book}/chapters`
|
|
226
226
|
);
|
|
227
227
|
}
|
|
228
228
|
/**
|
|
@@ -237,7 +237,7 @@ var BibleClient = class {
|
|
|
237
237
|
this.bookSchema.parse(book);
|
|
238
238
|
this.chapterSchema.parse(chapter);
|
|
239
239
|
return this.client.get(
|
|
240
|
-
|
|
240
|
+
`/v1/bibles/${versionId}/books/${book}/chapters/${chapter}`
|
|
241
241
|
);
|
|
242
242
|
}
|
|
243
243
|
/**
|
|
@@ -252,7 +252,7 @@ var BibleClient = class {
|
|
|
252
252
|
this.bookSchema.parse(book);
|
|
253
253
|
this.chapterSchema.parse(chapter);
|
|
254
254
|
return this.client.get(
|
|
255
|
-
|
|
255
|
+
`/v1/bibles/${versionId}/books/${book}/chapters/${chapter}/verses`
|
|
256
256
|
);
|
|
257
257
|
}
|
|
258
258
|
/**
|
|
@@ -269,7 +269,7 @@ var BibleClient = class {
|
|
|
269
269
|
this.chapterSchema.parse(chapter);
|
|
270
270
|
this.verseSchema.parse(verse);
|
|
271
271
|
return this.client.get(
|
|
272
|
-
|
|
272
|
+
`/v1/bibles/${versionId}/books/${book}/chapters/${chapter}/verses/${verse}`
|
|
273
273
|
);
|
|
274
274
|
}
|
|
275
275
|
/**
|
|
@@ -310,10 +310,7 @@ var BibleClient = class {
|
|
|
310
310
|
if (include_notes !== void 0) {
|
|
311
311
|
params.include_notes = include_notes;
|
|
312
312
|
}
|
|
313
|
-
return this.client.get(
|
|
314
|
-
`${this.rootPath}/bibles/${versionId}/passages/${usfm}`,
|
|
315
|
-
params
|
|
316
|
-
);
|
|
313
|
+
return this.client.get(`/v1/bibles/${versionId}/passages/${usfm}`, params);
|
|
317
314
|
}
|
|
318
315
|
/**
|
|
319
316
|
* Fetches the indexing structure for a Bible version.
|
|
@@ -322,14 +319,14 @@ var BibleClient = class {
|
|
|
322
319
|
*/
|
|
323
320
|
async getIndex(versionId) {
|
|
324
321
|
this.versionIdSchema.parse(versionId);
|
|
325
|
-
return this.client.get(
|
|
322
|
+
return this.client.get(`/v1/bibles/${versionId}/index`);
|
|
326
323
|
}
|
|
327
324
|
/**
|
|
328
325
|
* Fetches the verse of the day calendar for an entire year.
|
|
329
326
|
* @returns A collection of VOTD objects for all days of the year.
|
|
330
327
|
*/
|
|
331
328
|
async getAllVOTDs() {
|
|
332
|
-
return this.client.get(
|
|
329
|
+
return this.client.get(`/v1/verse_of_the_days`);
|
|
333
330
|
}
|
|
334
331
|
/**
|
|
335
332
|
* Fetches the passage_id for the Verse Of The Day.
|
|
@@ -347,7 +344,7 @@ var BibleClient = class {
|
|
|
347
344
|
async getVOTD(day) {
|
|
348
345
|
const daySchema = z.number().int().min(1).max(366);
|
|
349
346
|
daySchema.parse(day);
|
|
350
|
-
return this.client.get(
|
|
347
|
+
return this.client.get(`/v1/verse_of_the_days/${day}`);
|
|
351
348
|
}
|
|
352
349
|
};
|
|
353
350
|
|
|
@@ -367,9 +364,6 @@ var LanguagesClient = class {
|
|
|
367
364
|
constructor(client) {
|
|
368
365
|
this.client = client;
|
|
369
366
|
}
|
|
370
|
-
get rootPath() {
|
|
371
|
-
return `/${this.client.config.version}`;
|
|
372
|
-
}
|
|
373
367
|
/**
|
|
374
368
|
* Fetches a collection of languages supported in the Platform.
|
|
375
369
|
* @param options Query parameters for pagination and filtering (country is required).
|
|
@@ -387,7 +381,7 @@ var LanguagesClient = class {
|
|
|
387
381
|
if (options.page_token !== void 0) {
|
|
388
382
|
params.page_token = options.page_token;
|
|
389
383
|
}
|
|
390
|
-
return this.client.get(
|
|
384
|
+
return this.client.get(`/v1/languages`, params);
|
|
391
385
|
}
|
|
392
386
|
/**
|
|
393
387
|
* Fetches details about a specific language in the Platform.
|
|
@@ -396,7 +390,7 @@ var LanguagesClient = class {
|
|
|
396
390
|
*/
|
|
397
391
|
async getLanguage(languageId) {
|
|
398
392
|
this.languageIdSchema.parse(languageId);
|
|
399
|
-
return this.client.get(
|
|
393
|
+
return this.client.get(`/v1/languages/${languageId}`);
|
|
400
394
|
}
|
|
401
395
|
};
|
|
402
396
|
|
|
@@ -405,10 +399,10 @@ import { z as z3 } from "zod";
|
|
|
405
399
|
|
|
406
400
|
// src/YouVersionPlatformConfiguration.ts
|
|
407
401
|
var YouVersionPlatformConfiguration = class {
|
|
408
|
-
static
|
|
402
|
+
static _appKey = null;
|
|
409
403
|
static _installationId = null;
|
|
410
404
|
static _accessToken = null;
|
|
411
|
-
static _apiHost = "api
|
|
405
|
+
static _apiHost = "api.youversion.com";
|
|
412
406
|
static _isPreviewMode = false;
|
|
413
407
|
static _previewUserInfo = null;
|
|
414
408
|
static getOrSetInstallationId() {
|
|
@@ -423,11 +417,11 @@ var YouVersionPlatformConfiguration = class {
|
|
|
423
417
|
localStorage.setItem("x-yvp-installation-id", newId);
|
|
424
418
|
return newId;
|
|
425
419
|
}
|
|
426
|
-
static get
|
|
427
|
-
return this.
|
|
420
|
+
static get appKey() {
|
|
421
|
+
return this._appKey;
|
|
428
422
|
}
|
|
429
|
-
static set
|
|
430
|
-
this.
|
|
423
|
+
static set appKey(value) {
|
|
424
|
+
this._appKey = value;
|
|
431
425
|
}
|
|
432
426
|
static get installationId() {
|
|
433
427
|
if (!this._installationId) {
|
|
@@ -480,9 +474,6 @@ var HighlightsClient = class {
|
|
|
480
474
|
constructor(client) {
|
|
481
475
|
this.client = client;
|
|
482
476
|
}
|
|
483
|
-
get rootPath() {
|
|
484
|
-
return `/${this.client.config.version}`;
|
|
485
|
-
}
|
|
486
477
|
/**
|
|
487
478
|
* Gets the authentication token, either from the provided parameter or from the platform configuration.
|
|
488
479
|
* @param lat Optional explicit long access token. If not provided, retrieves from YouVersionPlatformConfiguration.
|
|
@@ -552,7 +543,7 @@ var HighlightsClient = class {
|
|
|
552
543
|
this.validatePassageId(options.passage_id);
|
|
553
544
|
params.passage_id = options.passage_id;
|
|
554
545
|
}
|
|
555
|
-
return this.client.get(
|
|
546
|
+
return this.client.get(`/v1/highlights`, params);
|
|
556
547
|
}
|
|
557
548
|
/**
|
|
558
549
|
* Creates or updates a highlight on a passage.
|
|
@@ -567,7 +558,7 @@ var HighlightsClient = class {
|
|
|
567
558
|
this.validatePassageId(data.passage_id);
|
|
568
559
|
this.validateColor(data.color);
|
|
569
560
|
const token = this.getAuthToken(lat);
|
|
570
|
-
return this.client.post(
|
|
561
|
+
return this.client.post(`/v1/highlights`, data, { lat: token });
|
|
571
562
|
}
|
|
572
563
|
/**
|
|
573
564
|
* Clears highlights for a passage.
|
|
@@ -587,7 +578,7 @@ var HighlightsClient = class {
|
|
|
587
578
|
this.validateVersionId(options.version_id);
|
|
588
579
|
params.version_id = options.version_id;
|
|
589
580
|
}
|
|
590
|
-
await this.client.delete(
|
|
581
|
+
await this.client.delete(`/v1/highlights/${passageId}`, params);
|
|
591
582
|
}
|
|
592
583
|
};
|
|
593
584
|
|
|
@@ -885,13 +876,13 @@ var YouVersionAPI = class {
|
|
|
885
876
|
Accept: "application/json",
|
|
886
877
|
"Content-Type": "application/json"
|
|
887
878
|
};
|
|
888
|
-
const
|
|
889
|
-
if (
|
|
890
|
-
headers["X-App-
|
|
879
|
+
const appKey = YouVersionPlatformConfiguration.appKey;
|
|
880
|
+
if (appKey) {
|
|
881
|
+
headers["X-YVP-App-Key"] = appKey;
|
|
891
882
|
}
|
|
892
883
|
const installationId = YouVersionPlatformConfiguration.installationId;
|
|
893
884
|
if (installationId) {
|
|
894
|
-
headers["
|
|
885
|
+
headers["X-YVP-Installation-ID"] = installationId;
|
|
895
886
|
}
|
|
896
887
|
const request = new Request(url.toString(), {
|
|
897
888
|
headers
|
|
@@ -905,15 +896,15 @@ var URLBuilder = class {
|
|
|
905
896
|
static get baseURL() {
|
|
906
897
|
return new URL(`https://${YouVersionPlatformConfiguration.apiHost}`);
|
|
907
898
|
}
|
|
908
|
-
static authURL(
|
|
909
|
-
if (typeof
|
|
910
|
-
throw new Error("
|
|
899
|
+
static authURL(appKey, requiredPermissions = /* @__PURE__ */ new Set(), optionalPermissions = /* @__PURE__ */ new Set()) {
|
|
900
|
+
if (typeof appKey !== "string" || appKey.trim().length === 0) {
|
|
901
|
+
throw new Error("appKey must be a non-empty string");
|
|
911
902
|
}
|
|
912
903
|
try {
|
|
913
904
|
const url = new URL(this.baseURL);
|
|
914
905
|
url.pathname = "/auth/login";
|
|
915
906
|
const searchParams = new URLSearchParams();
|
|
916
|
-
searchParams.append("
|
|
907
|
+
searchParams.append("APP_KEY", appKey);
|
|
917
908
|
searchParams.append("language", "en");
|
|
918
909
|
if (requiredPermissions.size > 0) {
|
|
919
910
|
const requiredList = Array.from(requiredPermissions).map((p) => p.toString());
|
|
@@ -977,11 +968,11 @@ var YouVersionAPIUsers = class {
|
|
|
977
968
|
if (!optionalPermissions || !(optionalPermissions instanceof Set)) {
|
|
978
969
|
throw new Error("Invalid optionalPermissions: must be a Set");
|
|
979
970
|
}
|
|
980
|
-
const
|
|
981
|
-
if (!
|
|
982
|
-
throw new Error("YouVersionPlatformConfiguration.
|
|
971
|
+
const appKey = YouVersionPlatformConfiguration.appKey;
|
|
972
|
+
if (!appKey) {
|
|
973
|
+
throw new Error("YouVersionPlatformConfiguration.appKey must be set before calling signIn");
|
|
983
974
|
}
|
|
984
|
-
const url = URLBuilder.authURL(
|
|
975
|
+
const url = URLBuilder.authURL(appKey, requiredPermissions, optionalPermissions);
|
|
985
976
|
const strategy = AuthenticationStrategyRegistry.get();
|
|
986
977
|
const callbackUrl = await strategy.authenticate(url);
|
|
987
978
|
const result = new SignInWithYouVersionResult(callbackUrl);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@youversion/platform-core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"msw": "2.11.6",
|
|
29
29
|
"typescript": "5.9.3",
|
|
30
30
|
"vitest": "4.0.4",
|
|
31
|
-
"@internal/
|
|
32
|
-
"@internal/
|
|
31
|
+
"@internal/eslint-config": "0.0.0",
|
|
32
|
+
"@internal/tsconfig": "0.0.0"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"tsup": "8.5.0",
|
package/src/URLBuilder.ts
CHANGED
|
@@ -7,12 +7,12 @@ export class URLBuilder {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
static authURL(
|
|
10
|
-
|
|
10
|
+
appKey: string,
|
|
11
11
|
requiredPermissions: Set<SignInWithYouVersionPermissionValues> = new Set<SignInWithYouVersionPermissionValues>(),
|
|
12
12
|
optionalPermissions: Set<SignInWithYouVersionPermissionValues> = new Set<SignInWithYouVersionPermissionValues>(),
|
|
13
13
|
): URL {
|
|
14
|
-
if (typeof
|
|
15
|
-
throw new Error('
|
|
14
|
+
if (typeof appKey !== 'string' || appKey.trim().length === 0) {
|
|
15
|
+
throw new Error('appKey must be a non-empty string');
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
try {
|
|
@@ -21,7 +21,7 @@ export class URLBuilder {
|
|
|
21
21
|
|
|
22
22
|
// Add query parameters
|
|
23
23
|
const searchParams = new URLSearchParams();
|
|
24
|
-
searchParams.append('
|
|
24
|
+
searchParams.append('APP_KEY', appKey);
|
|
25
25
|
searchParams.append('language', 'en'); // TODO: load from system
|
|
26
26
|
|
|
27
27
|
if (requiredPermissions.size > 0) {
|
package/src/Users.ts
CHANGED
|
@@ -34,12 +34,12 @@ export class YouVersionAPIUsers {
|
|
|
34
34
|
throw new Error('Invalid optionalPermissions: must be a Set');
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const
|
|
38
|
-
if (!
|
|
39
|
-
throw new Error('YouVersionPlatformConfiguration.
|
|
37
|
+
const appKey = YouVersionPlatformConfiguration.appKey;
|
|
38
|
+
if (!appKey) {
|
|
39
|
+
throw new Error('YouVersionPlatformConfiguration.appKey must be set before calling signIn');
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
const url = URLBuilder.authURL(
|
|
42
|
+
const url = URLBuilder.authURL(appKey, requiredPermissions, optionalPermissions);
|
|
43
43
|
|
|
44
44
|
// Use the registered authentication strategy
|
|
45
45
|
const strategy = AuthenticationStrategyRegistry.get();
|
package/src/YouVersionAPI.ts
CHANGED
|
@@ -7,16 +7,14 @@ export class YouVersionAPI {
|
|
|
7
7
|
'Content-Type': 'application/json',
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
headers['X-App-Id'] = appId;
|
|
10
|
+
const appKey = YouVersionPlatformConfiguration.appKey;
|
|
11
|
+
if (appKey) {
|
|
12
|
+
headers['X-YVP-App-Key'] = appKey;
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
// Add installation ID header
|
|
17
15
|
const installationId = YouVersionPlatformConfiguration.installationId;
|
|
18
16
|
if (installationId) {
|
|
19
|
-
headers['
|
|
17
|
+
headers['X-YVP-Installation-ID'] = installationId;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
const request = new Request(url.toString(), {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { YouVersionUserInfo } from './YouVersionUserInfo';
|
|
2
2
|
|
|
3
3
|
export class YouVersionPlatformConfiguration {
|
|
4
|
-
private static
|
|
4
|
+
private static _appKey: string | null = null;
|
|
5
5
|
private static _installationId: string | null = null;
|
|
6
6
|
private static _accessToken: string | null = null;
|
|
7
|
-
private static _apiHost: string = 'api
|
|
7
|
+
private static _apiHost: string = 'api.youversion.com';
|
|
8
8
|
private static _isPreviewMode: boolean = false;
|
|
9
9
|
private static _previewUserInfo: YouVersionUserInfo | null = null;
|
|
10
10
|
|
|
@@ -23,12 +23,12 @@ export class YouVersionPlatformConfiguration {
|
|
|
23
23
|
return newId;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
static get
|
|
27
|
-
return this.
|
|
26
|
+
static get appKey(): string | null {
|
|
27
|
+
return this._appKey;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
static set
|
|
31
|
-
this.
|
|
30
|
+
static set appKey(value: string | null) {
|
|
31
|
+
this._appKey = value;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
static get installationId(): string {
|
|
@@ -5,7 +5,7 @@ import type { SignInWithYouVersionPermissionValues } from '../types';
|
|
|
5
5
|
|
|
6
6
|
describe('URLBuilder - Input Validation', () => {
|
|
7
7
|
let originalApiHost: string;
|
|
8
|
-
let originalInstallationId: string |
|
|
8
|
+
let originalInstallationId: string | null;
|
|
9
9
|
|
|
10
10
|
beforeEach(() => {
|
|
11
11
|
// Store original config
|
|
@@ -13,7 +13,11 @@ describe('URLBuilder - Input Validation', () => {
|
|
|
13
13
|
originalInstallationId = YouVersionPlatformConfiguration.installationId;
|
|
14
14
|
|
|
15
15
|
// Set test config
|
|
16
|
-
|
|
16
|
+
const apiHost = process.env.YVP_API_HOST;
|
|
17
|
+
if (!apiHost) {
|
|
18
|
+
throw new Error('YVP_API_HOST environment variable must be set for URLBuilder tests.');
|
|
19
|
+
}
|
|
20
|
+
YouVersionPlatformConfiguration.apiHost = apiHost;
|
|
17
21
|
YouVersionPlatformConfiguration.installationId = 'test-installation-id';
|
|
18
22
|
});
|
|
19
23
|
|
|
@@ -23,23 +27,23 @@ describe('URLBuilder - Input Validation', () => {
|
|
|
23
27
|
YouVersionPlatformConfiguration.installationId = originalInstallationId;
|
|
24
28
|
});
|
|
25
29
|
|
|
26
|
-
describe('authURL -
|
|
27
|
-
it('should throw error for empty string
|
|
30
|
+
describe('authURL - appKey validation', () => {
|
|
31
|
+
it('should throw error for empty string appKey', () => {
|
|
28
32
|
expect(() => {
|
|
29
33
|
URLBuilder.authURL('');
|
|
30
|
-
}).toThrow('
|
|
34
|
+
}).toThrow('appKey must be a non-empty string');
|
|
31
35
|
});
|
|
32
36
|
|
|
33
|
-
it('should throw error for whitespace-only
|
|
37
|
+
it('should throw error for whitespace-only appKey', () => {
|
|
34
38
|
expect(() => {
|
|
35
39
|
URLBuilder.authURL(' ');
|
|
36
|
-
}).toThrow('
|
|
40
|
+
}).toThrow('appKey must be a non-empty string');
|
|
37
41
|
});
|
|
38
42
|
|
|
39
|
-
it('should throw error for tab/newline-only
|
|
43
|
+
it('should throw error for tab/newline-only appKey', () => {
|
|
40
44
|
expect(() => {
|
|
41
45
|
URLBuilder.authURL('\t\n ');
|
|
42
|
-
}).toThrow('
|
|
46
|
+
}).toThrow('appKey must be a non-empty string');
|
|
43
47
|
});
|
|
44
48
|
|
|
45
49
|
it('should throw descriptive error message', () => {
|
|
@@ -49,34 +53,34 @@ describe('URLBuilder - Input Validation', () => {
|
|
|
49
53
|
expect.fail('Should have thrown an error');
|
|
50
54
|
} catch (error) {
|
|
51
55
|
expect(error).toBeInstanceOf(Error);
|
|
52
|
-
expect((error as Error).message).toContain('
|
|
56
|
+
expect((error as Error).message).toContain('appKey');
|
|
53
57
|
expect((error as Error).message).toContain('non-empty string');
|
|
54
58
|
}
|
|
55
59
|
});
|
|
56
60
|
|
|
57
|
-
it('should accept valid non-empty
|
|
58
|
-
const url = URLBuilder.authURL('valid-app-
|
|
61
|
+
it('should accept valid non-empty appKey', () => {
|
|
62
|
+
const url = URLBuilder.authURL('valid-app-key');
|
|
59
63
|
|
|
60
64
|
expect(url).toBeInstanceOf(URL);
|
|
61
|
-
expect(url.hostname
|
|
65
|
+
expect(url.hostname.endsWith('.youversion.com')).toBe(true);
|
|
62
66
|
expect(url.pathname).toBe('/auth/login');
|
|
63
|
-
expect(url.searchParams.get('
|
|
67
|
+
expect(url.searchParams.get('APP_KEY')).toBe('valid-app-key');
|
|
64
68
|
});
|
|
65
69
|
|
|
66
|
-
it('should trim and accept
|
|
70
|
+
it('should trim and accept appKey with surrounding whitespace', () => {
|
|
67
71
|
// Note: The validation checks trim().length, so this should pass
|
|
68
|
-
const url = URLBuilder.authURL(' valid-app-
|
|
72
|
+
const url = URLBuilder.authURL(' valid-app-key ');
|
|
69
73
|
|
|
70
74
|
expect(url).toBeInstanceOf(URL);
|
|
71
75
|
// The actual value passed has whitespace preserved in the URL
|
|
72
|
-
expect(url.searchParams.get('
|
|
76
|
+
expect(url.searchParams.get('APP_KEY')).toBe(' valid-app-key ');
|
|
73
77
|
});
|
|
74
78
|
|
|
75
|
-
it('should accept
|
|
76
|
-
const
|
|
77
|
-
const url = URLBuilder.authURL(
|
|
79
|
+
it('should accept appKey with special characters', () => {
|
|
80
|
+
const specialAppKey = 'app-key_123.test';
|
|
81
|
+
const url = URLBuilder.authURL(specialAppKey);
|
|
78
82
|
|
|
79
|
-
expect(url.searchParams.get('
|
|
83
|
+
expect(url.searchParams.get('APP_KEY')).toBe(specialAppKey);
|
|
80
84
|
});
|
|
81
85
|
});
|
|
82
86
|
|
|
@@ -85,14 +89,14 @@ describe('URLBuilder - Input Validation', () => {
|
|
|
85
89
|
const url = URLBuilder.authURL('test-app');
|
|
86
90
|
|
|
87
91
|
expect(url.protocol).toBe('https:');
|
|
88
|
-
expect(url.hostname
|
|
92
|
+
expect(url.hostname.endsWith('.youversion.com')).toBe(true);
|
|
89
93
|
expect(url.pathname).toBe('/auth/login');
|
|
90
94
|
});
|
|
91
95
|
|
|
92
|
-
it('should include
|
|
93
|
-
const url = URLBuilder.authURL('my-app-
|
|
96
|
+
it('should include APP_KEY in query parameters', () => {
|
|
97
|
+
const url = URLBuilder.authURL('my-app-key');
|
|
94
98
|
|
|
95
|
-
expect(url.searchParams.get('
|
|
99
|
+
expect(url.searchParams.get('APP_KEY')).toBe('my-app-key');
|
|
96
100
|
});
|
|
97
101
|
|
|
98
102
|
it('should include default language parameter', () => {
|
|
@@ -109,7 +113,7 @@ describe('URLBuilder - Input Validation', () => {
|
|
|
109
113
|
});
|
|
110
114
|
|
|
111
115
|
it('should not include installation ID when not configured', () => {
|
|
112
|
-
YouVersionPlatformConfiguration.installationId =
|
|
116
|
+
YouVersionPlatformConfiguration.installationId = null;
|
|
113
117
|
const url = URLBuilder.authURL('test-app');
|
|
114
118
|
|
|
115
119
|
expect(url.searchParams.get('x-yvp-installation-id')).toBeNull();
|
|
@@ -187,7 +191,7 @@ describe('URLBuilder - Input Validation', () => {
|
|
|
187
191
|
const url = URLBuilder.userURL('valid-access-token-123');
|
|
188
192
|
|
|
189
193
|
expect(url).toBeInstanceOf(URL);
|
|
190
|
-
expect(url.hostname
|
|
194
|
+
expect(url.hostname.endsWith('.youversion.com')).toBe(true);
|
|
191
195
|
expect(url.pathname).toBe('/auth/me');
|
|
192
196
|
expect(url.searchParams.get('lat')).toBe('valid-access-token-123');
|
|
193
197
|
});
|
|
@@ -205,7 +209,7 @@ describe('URLBuilder - Input Validation', () => {
|
|
|
205
209
|
const url = URLBuilder.userURL('test-token');
|
|
206
210
|
|
|
207
211
|
expect(url.protocol).toBe('https:');
|
|
208
|
-
expect(url.hostname
|
|
212
|
+
expect(url.hostname.endsWith('.youversion.com')).toBe(true);
|
|
209
213
|
expect(url.pathname).toBe('/auth/me');
|
|
210
214
|
});
|
|
211
215
|
|
|
@@ -227,7 +231,7 @@ describe('URLBuilder - Input Validation', () => {
|
|
|
227
231
|
});
|
|
228
232
|
|
|
229
233
|
describe('Error handling', () => {
|
|
230
|
-
it('should throw errors instead of returning null for invalid
|
|
234
|
+
it('should throw errors instead of returning null for invalid appKey', () => {
|
|
231
235
|
// Verify that the method throws, not returns null
|
|
232
236
|
let threwError = false;
|
|
233
237
|
let returnValue: any;
|
|
@@ -260,7 +264,7 @@ describe('URLBuilder - Input Validation', () => {
|
|
|
260
264
|
// This is hard to trigger, but we can at least verify the pattern exists
|
|
261
265
|
// by checking that valid inputs don't trigger the catch block
|
|
262
266
|
expect(() => {
|
|
263
|
-
URLBuilder.authURL('valid-app-
|
|
267
|
+
URLBuilder.authURL('valid-app-key');
|
|
264
268
|
}).not.toThrow(/Failed to construct auth URL/);
|
|
265
269
|
});
|
|
266
270
|
|
|
@@ -14,13 +14,20 @@ vi.stubGlobal('localStorage', { getItem: mockGetItem, setItem: mockSetItem });
|
|
|
14
14
|
|
|
15
15
|
import { YouVersionPlatformConfiguration } from '../YouVersionPlatformConfiguration';
|
|
16
16
|
|
|
17
|
+
const envApiHost = process.env.YVP_API_HOST || 'api.youversion.com';
|
|
18
|
+
if (!envApiHost) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
'YVP_API_HOST environment variable must be set for YouVersionPlatformConfiguration tests.',
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
17
24
|
describe('YouVersionPlatformConfiguration', () => {
|
|
18
25
|
beforeEach(() => {
|
|
19
26
|
// Reset all static properties
|
|
20
|
-
YouVersionPlatformConfiguration.
|
|
27
|
+
YouVersionPlatformConfiguration.appKey = null;
|
|
21
28
|
YouVersionPlatformConfiguration.installationId = null;
|
|
22
29
|
YouVersionPlatformConfiguration.setAccessToken(null);
|
|
23
|
-
YouVersionPlatformConfiguration.apiHost =
|
|
30
|
+
YouVersionPlatformConfiguration.apiHost = envApiHost;
|
|
24
31
|
YouVersionPlatformConfiguration.isPreviewMode = false;
|
|
25
32
|
YouVersionPlatformConfiguration.previewUserInfo = null;
|
|
26
33
|
|
|
@@ -37,15 +44,15 @@ describe('YouVersionPlatformConfiguration', () => {
|
|
|
37
44
|
vi.restoreAllMocks();
|
|
38
45
|
});
|
|
39
46
|
|
|
40
|
-
describe('
|
|
41
|
-
it('should get and set
|
|
42
|
-
expect(YouVersionPlatformConfiguration.
|
|
47
|
+
describe('appKey', () => {
|
|
48
|
+
it('should get and set appKey', () => {
|
|
49
|
+
expect(YouVersionPlatformConfiguration.appKey).toBeNull();
|
|
43
50
|
|
|
44
|
-
YouVersionPlatformConfiguration.
|
|
45
|
-
expect(YouVersionPlatformConfiguration.
|
|
51
|
+
YouVersionPlatformConfiguration.appKey = 'test-app-key';
|
|
52
|
+
expect(YouVersionPlatformConfiguration.appKey).toBe('test-app-key');
|
|
46
53
|
|
|
47
|
-
YouVersionPlatformConfiguration.
|
|
48
|
-
expect(YouVersionPlatformConfiguration.
|
|
54
|
+
YouVersionPlatformConfiguration.appKey = null;
|
|
55
|
+
expect(YouVersionPlatformConfiguration.appKey).toBeNull();
|
|
49
56
|
});
|
|
50
57
|
});
|
|
51
58
|
|
|
@@ -104,10 +111,12 @@ describe('YouVersionPlatformConfiguration', () => {
|
|
|
104
111
|
|
|
105
112
|
describe('apiHost', () => {
|
|
106
113
|
it('should get and set apiHost', () => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
YouVersionPlatformConfiguration.apiHost = '
|
|
110
|
-
expect(YouVersionPlatformConfiguration.apiHost).toBe('
|
|
114
|
+
const apiHost = process.env.YVP_API_HOST || 'api.youversion.com';
|
|
115
|
+
expect(YouVersionPlatformConfiguration.apiHost).toBe(apiHost);
|
|
116
|
+
YouVersionPlatformConfiguration.apiHost = 'somethingelse.youversion.com';
|
|
117
|
+
expect(YouVersionPlatformConfiguration.apiHost).toBe('somethingelse.youversion.com');
|
|
118
|
+
YouVersionPlatformConfiguration.apiHost = apiHost;
|
|
119
|
+
expect(YouVersionPlatformConfiguration.apiHost).toBe(apiHost);
|
|
111
120
|
});
|
|
112
121
|
});
|
|
113
122
|
|
|
@@ -13,9 +13,8 @@ describe('AuthClient', () => {
|
|
|
13
13
|
beforeEach(() => {
|
|
14
14
|
global.fetch = vi.fn();
|
|
15
15
|
apiClient = new ApiClient({
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
version: 'v1',
|
|
16
|
+
appKey: 'test-app-key',
|
|
17
|
+
apiHost: process.env.YVP_API_HOST || '',
|
|
19
18
|
});
|
|
20
19
|
authClient = new AuthClient(apiClient);
|
|
21
20
|
});
|
|
@@ -16,9 +16,8 @@ describe('BibleClient', () => {
|
|
|
16
16
|
|
|
17
17
|
beforeEach(() => {
|
|
18
18
|
apiClient = new ApiClient({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
version: 'v1',
|
|
19
|
+
apiHost: process.env.YVP_API_HOST || '',
|
|
20
|
+
appKey: process.env.YVP_APP_KEY || '',
|
|
22
21
|
installationId: 'test-installation',
|
|
23
22
|
});
|
|
24
23
|
bibleClient = new BibleClient(apiClient);
|