@youversion/platform-core 1.2.1 → 1.4.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 +21 -0
- package/dist/index.cjs +13 -6
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +13 -6
- package/package.json +1 -1
- package/src/__tests__/bible.test.ts +50 -0
- package/src/__tests__/client.test.ts +55 -0
- package/src/__tests__/polyfills.ts +29 -0
- package/src/bible.ts +11 -5
- package/src/client.ts +14 -4
- package/vitest.config.ts +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @youversion/platform-core@1.
|
|
2
|
+
> @youversion/platform-core@1.4.0 build /home/runner/work/platform-sdk-react/platform-sdk-react/packages/core
|
|
3
3
|
> tsup src/index.ts --format cjs,esm --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
[34mCLI[39m Target: es2022
|
|
9
9
|
[34mCJS[39m Build start
|
|
10
10
|
[34mESM[39m Build start
|
|
11
|
-
[32mESM[39m [1mdist/index.js [22m[32m41.
|
|
12
|
-
[32mESM[39m ⚡️ Build success in
|
|
13
|
-
[32mCJS[39m [1mdist/index.cjs [22m[
|
|
14
|
-
[32mCJS[39m ⚡️ Build success in
|
|
11
|
+
[32mESM[39m [1mdist/index.js [22m[32m41.47 KB[39m
|
|
12
|
+
[32mESM[39m ⚡️ Build success in 36ms
|
|
13
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m43.29 KB[39m
|
|
14
|
+
[32mCJS[39m ⚡️ Build success in 36ms
|
|
15
15
|
[34mDTS[39m Build start
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.cts [22m[32m32.
|
|
18
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m32.
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 1771ms
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m32.84 KB[39m
|
|
18
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m32.84 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @youversion/platform-core
|
|
2
2
|
|
|
3
|
+
## 1.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8275a27: feat(ui): add bible reader settings
|
|
8
|
+
- refactor popover component to have consistent styling across
|
|
9
|
+
multiple components and reduce duplication in code.
|
|
10
|
+
- add bible reader settings and save the users settings to localStorage.
|
|
11
|
+
|
|
12
|
+
## 1.3.0
|
|
13
|
+
|
|
14
|
+
### Minor Changes
|
|
15
|
+
|
|
16
|
+
- b2b86c2: Add support for array query parameters in API client and improve language range handling
|
|
17
|
+
- **API Client**: Enhanced query string serialization to support array parameters, properly formatting them as repeated keys (e.g., `?param=one¶m=two`)
|
|
18
|
+
- **Bible Client**: Updated `getVersions()` method to accept either a single language range string or an array of language ranges, providing more flexibility for filtering Bible versions
|
|
19
|
+
- **Schema**: Renamed language range schema to use plural naming convention for consistency
|
|
20
|
+
- **Testing**: Added comprehensive test coverage for query string building with both scalar and array parameters
|
|
21
|
+
|
|
22
|
+
This change maintains backward compatibility while providing more flexible API parameter handling.
|
|
23
|
+
|
|
3
24
|
## 1.2.1
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -69,9 +69,15 @@ var ApiClient = class {
|
|
|
69
69
|
*/
|
|
70
70
|
buildQueryString(params) {
|
|
71
71
|
if (!params) return "";
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
const searchParams = new URLSearchParams();
|
|
73
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
74
|
+
if (Array.isArray(value)) {
|
|
75
|
+
value.forEach((item) => searchParams.append(key, String(item)));
|
|
76
|
+
} else {
|
|
77
|
+
searchParams.append(key, String(value));
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const queryString = searchParams.toString();
|
|
75
81
|
return queryString ? `?${queryString}` : "";
|
|
76
82
|
}
|
|
77
83
|
/**
|
|
@@ -205,14 +211,15 @@ var BibleClient = class {
|
|
|
205
211
|
/**
|
|
206
212
|
* Fetches a collection of Bible versions filtered by language ranges.
|
|
207
213
|
*
|
|
208
|
-
* @param language_ranges -
|
|
214
|
+
* @param language_ranges - One or more language codes or ranges to filter the versions (required).
|
|
209
215
|
* @param license_id - Optional license ID to filter versions by license.
|
|
210
216
|
* @returns A promise that resolves to a collection of BibleVersion objects.
|
|
211
217
|
*/
|
|
212
218
|
async getVersions(language_ranges, license_id) {
|
|
213
|
-
|
|
219
|
+
const languageRangeArray = Array.isArray(language_ranges) ? language_ranges : [language_ranges];
|
|
220
|
+
const parsedLanguageRanges = import_zod.z.array(this.languageRangesSchema).nonempty("At least one language range is required").parse(languageRangeArray);
|
|
214
221
|
const params = {
|
|
215
|
-
"language_ranges[]":
|
|
222
|
+
"language_ranges[]": parsedLanguageRanges
|
|
216
223
|
};
|
|
217
224
|
if (license_id !== void 0) {
|
|
218
225
|
params.license_id = license_id;
|
package/dist/index.d.cts
CHANGED
|
@@ -258,7 +258,8 @@ interface HighlightColor {
|
|
|
258
258
|
label: string;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
type
|
|
261
|
+
type PrimitiveQueryParam = string | number | boolean;
|
|
262
|
+
type QueryParams = Record<string, PrimitiveQueryParam | PrimitiveQueryParam[]>;
|
|
262
263
|
type RequestData = Record<string, string | number | boolean | object>;
|
|
263
264
|
type RequestHeaders = Record<string, string>;
|
|
264
265
|
/**
|
|
@@ -333,11 +334,11 @@ declare class BibleClient {
|
|
|
333
334
|
/**
|
|
334
335
|
* Fetches a collection of Bible versions filtered by language ranges.
|
|
335
336
|
*
|
|
336
|
-
* @param language_ranges -
|
|
337
|
+
* @param language_ranges - One or more language codes or ranges to filter the versions (required).
|
|
337
338
|
* @param license_id - Optional license ID to filter versions by license.
|
|
338
339
|
* @returns A promise that resolves to a collection of BibleVersion objects.
|
|
339
340
|
*/
|
|
340
|
-
getVersions(language_ranges: string, license_id?: string | number): Promise<Collection<BibleVersion>>;
|
|
341
|
+
getVersions(language_ranges: string | string[], license_id?: string | number): Promise<Collection<BibleVersion>>;
|
|
341
342
|
/**
|
|
342
343
|
* Fetches a Bible version by its ID.
|
|
343
344
|
* @param id The version ID.
|
package/dist/index.d.ts
CHANGED
|
@@ -258,7 +258,8 @@ interface HighlightColor {
|
|
|
258
258
|
label: string;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
type
|
|
261
|
+
type PrimitiveQueryParam = string | number | boolean;
|
|
262
|
+
type QueryParams = Record<string, PrimitiveQueryParam | PrimitiveQueryParam[]>;
|
|
262
263
|
type RequestData = Record<string, string | number | boolean | object>;
|
|
263
264
|
type RequestHeaders = Record<string, string>;
|
|
264
265
|
/**
|
|
@@ -333,11 +334,11 @@ declare class BibleClient {
|
|
|
333
334
|
/**
|
|
334
335
|
* Fetches a collection of Bible versions filtered by language ranges.
|
|
335
336
|
*
|
|
336
|
-
* @param language_ranges -
|
|
337
|
+
* @param language_ranges - One or more language codes or ranges to filter the versions (required).
|
|
337
338
|
* @param license_id - Optional license ID to filter versions by license.
|
|
338
339
|
* @returns A promise that resolves to a collection of BibleVersion objects.
|
|
339
340
|
*/
|
|
340
|
-
getVersions(language_ranges: string, license_id?: string | number): Promise<Collection<BibleVersion>>;
|
|
341
|
+
getVersions(language_ranges: string | string[], license_id?: string | number): Promise<Collection<BibleVersion>>;
|
|
341
342
|
/**
|
|
342
343
|
* Fetches a Bible version by its ID.
|
|
343
344
|
* @param id The version ID.
|
package/dist/index.js
CHANGED
|
@@ -30,9 +30,15 @@ var ApiClient = class {
|
|
|
30
30
|
*/
|
|
31
31
|
buildQueryString(params) {
|
|
32
32
|
if (!params) return "";
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
const searchParams = new URLSearchParams();
|
|
34
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
35
|
+
if (Array.isArray(value)) {
|
|
36
|
+
value.forEach((item) => searchParams.append(key, String(item)));
|
|
37
|
+
} else {
|
|
38
|
+
searchParams.append(key, String(value));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
const queryString = searchParams.toString();
|
|
36
42
|
return queryString ? `?${queryString}` : "";
|
|
37
43
|
}
|
|
38
44
|
/**
|
|
@@ -166,14 +172,15 @@ var BibleClient = class {
|
|
|
166
172
|
/**
|
|
167
173
|
* Fetches a collection of Bible versions filtered by language ranges.
|
|
168
174
|
*
|
|
169
|
-
* @param language_ranges -
|
|
175
|
+
* @param language_ranges - One or more language codes or ranges to filter the versions (required).
|
|
170
176
|
* @param license_id - Optional license ID to filter versions by license.
|
|
171
177
|
* @returns A promise that resolves to a collection of BibleVersion objects.
|
|
172
178
|
*/
|
|
173
179
|
async getVersions(language_ranges, license_id) {
|
|
174
|
-
|
|
180
|
+
const languageRangeArray = Array.isArray(language_ranges) ? language_ranges : [language_ranges];
|
|
181
|
+
const parsedLanguageRanges = z.array(this.languageRangesSchema).nonempty("At least one language range is required").parse(languageRangeArray);
|
|
175
182
|
const params = {
|
|
176
|
-
"language_ranges[]":
|
|
183
|
+
"language_ranges[]": parsedLanguageRanges
|
|
177
184
|
};
|
|
178
185
|
if (license_id !== void 0) {
|
|
179
186
|
params.license_id = license_id;
|
package/package.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { http, HttpResponse } from 'msw';
|
|
2
3
|
import { ApiClient } from '../client';
|
|
3
4
|
import { BibleClient } from '../bible';
|
|
4
5
|
import {
|
|
@@ -9,6 +10,8 @@ import {
|
|
|
9
10
|
BibleVersionSchema,
|
|
10
11
|
VOTDSchema,
|
|
11
12
|
} from '../schemas';
|
|
13
|
+
import { server } from './setup';
|
|
14
|
+
import { mockVersions } from './MockVersions';
|
|
12
15
|
|
|
13
16
|
describe('BibleClient', () => {
|
|
14
17
|
let apiClient: ApiClient;
|
|
@@ -34,6 +37,50 @@ describe('BibleClient', () => {
|
|
|
34
37
|
expect(hasNIV).toBe(true);
|
|
35
38
|
});
|
|
36
39
|
|
|
40
|
+
it('should send multiple language ranges when provided', async () => {
|
|
41
|
+
server.use(
|
|
42
|
+
http.get('https://api.youversion.com/v1/bibles', ({ request }) => {
|
|
43
|
+
const url = new URL(request.url);
|
|
44
|
+
const languageRanges = url.searchParams.getAll('language_ranges[]');
|
|
45
|
+
|
|
46
|
+
expect(languageRanges).toEqual(['en*', 'es*']);
|
|
47
|
+
|
|
48
|
+
return HttpResponse.json({
|
|
49
|
+
data: mockVersions,
|
|
50
|
+
next_page_token: null,
|
|
51
|
+
total_size: mockVersions.length,
|
|
52
|
+
});
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const versions = await bibleClient.getVersions(['en*', 'es*']);
|
|
57
|
+
|
|
58
|
+
const { success } = BibleVersionSchema.safeParse(versions.data[0]);
|
|
59
|
+
expect(success).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should accept a wildcard language range', async () => {
|
|
63
|
+
server.use(
|
|
64
|
+
http.get('https://api.youversion.com/v1/bibles', ({ request }) => {
|
|
65
|
+
const url = new URL(request.url);
|
|
66
|
+
const languageRanges = url.searchParams.getAll('language_ranges[]');
|
|
67
|
+
|
|
68
|
+
expect(languageRanges).toEqual(['*']);
|
|
69
|
+
|
|
70
|
+
return HttpResponse.json({
|
|
71
|
+
data: mockVersions,
|
|
72
|
+
next_page_token: null,
|
|
73
|
+
total_size: mockVersions.length,
|
|
74
|
+
});
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const versions = await bibleClient.getVersions('*');
|
|
79
|
+
|
|
80
|
+
const { success } = BibleVersionSchema.safeParse(versions.data[0]);
|
|
81
|
+
expect(success).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
|
|
37
84
|
it('should throw an error for invalid language ranges', async () => {
|
|
38
85
|
await expect(bibleClient.getVersions('')).rejects.toThrow(
|
|
39
86
|
'Language ranges must be a non-empty string',
|
|
@@ -41,6 +88,9 @@ describe('BibleClient', () => {
|
|
|
41
88
|
await expect(bibleClient.getVersions(' ')).rejects.toThrow(
|
|
42
89
|
'Language ranges must be a non-empty string',
|
|
43
90
|
);
|
|
91
|
+
await expect(bibleClient.getVersions([])).rejects.toThrow(
|
|
92
|
+
'At least one language range is required',
|
|
93
|
+
);
|
|
44
94
|
});
|
|
45
95
|
});
|
|
46
96
|
|
|
@@ -43,6 +43,45 @@ describe('ApiClient', () => {
|
|
|
43
43
|
});
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
+
describe('buildQueryString', () => {
|
|
47
|
+
const buildQueryString = (params?: Parameters<ApiClient['get']>[1]) =>
|
|
48
|
+
(
|
|
49
|
+
apiClient as unknown as {
|
|
50
|
+
buildQueryString: (params?: Parameters<ApiClient['get']>[1]) => string;
|
|
51
|
+
}
|
|
52
|
+
).buildQueryString(params);
|
|
53
|
+
|
|
54
|
+
it('should serialize single scalar parameter', () => {
|
|
55
|
+
const query = buildQueryString({ param: 'value' });
|
|
56
|
+
|
|
57
|
+
expect(query).toBe('?param=value');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should serialize an array of length 1 as repeated key', () => {
|
|
61
|
+
const query = buildQueryString({ param: ['only'] });
|
|
62
|
+
|
|
63
|
+
expect(query).toBe('?param=only');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should serialize an array of length 2 as repeated keys', () => {
|
|
67
|
+
const query = buildQueryString({ param: ['one', 'two'] });
|
|
68
|
+
|
|
69
|
+
expect(query).toBe('?param=one¶m=two');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should serialize an array of length 3 as repeated keys', () => {
|
|
73
|
+
const query = buildQueryString({ param: ['one', 'two', 'three'] });
|
|
74
|
+
|
|
75
|
+
expect(query).toBe('?param=one¶m=two¶m=three');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should handle both scalar and array parameters together', () => {
|
|
79
|
+
const query = buildQueryString({ param: 'value', list: ['one', 'two'] });
|
|
80
|
+
|
|
81
|
+
expect(query).toBe('?param=value&list=one&list=two');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
46
85
|
describe('get', () => {
|
|
47
86
|
it('should make GET request and return data', async () => {
|
|
48
87
|
server.use(
|
|
@@ -71,6 +110,22 @@ describe('ApiClient', () => {
|
|
|
71
110
|
|
|
72
111
|
expect(result).toEqual({ param: 'value' });
|
|
73
112
|
});
|
|
113
|
+
|
|
114
|
+
it('should include array query parameters as repeated keys', async () => {
|
|
115
|
+
server.use(
|
|
116
|
+
http.get('https://test_placeholder.youversion.com/test', ({ request }) => {
|
|
117
|
+
const url = new URL(request.url);
|
|
118
|
+
const params = url.searchParams.getAll('param');
|
|
119
|
+
return HttpResponse.json({ params });
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const result = await apiClient.get<{ params: string[] }>('/test', {
|
|
124
|
+
param: ['one', 'two'],
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
expect(result).toEqual({ params: ['one', 'two'] });
|
|
128
|
+
});
|
|
74
129
|
});
|
|
75
130
|
|
|
76
131
|
describe('post', () => {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class LocalStorageMock {
|
|
2
|
+
private store: Record<string, string> = {};
|
|
3
|
+
|
|
4
|
+
getItem(key: string): string | null {
|
|
5
|
+
return this.store[key] ?? null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
setItem(key: string, value: string): void {
|
|
9
|
+
this.store[key] = value;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
removeItem(key: string): void {
|
|
13
|
+
delete this.store[key];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
clear(): void {
|
|
17
|
+
this.store = {};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get length(): number {
|
|
21
|
+
return Object.keys(this.store).length;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
key(index: number): string | null {
|
|
25
|
+
return Object.keys(this.store)[index] ?? null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
globalThis.localStorage = new LocalStorageMock() as Storage;
|
package/src/bible.ts
CHANGED
|
@@ -50,17 +50,23 @@ export class BibleClient {
|
|
|
50
50
|
/**
|
|
51
51
|
* Fetches a collection of Bible versions filtered by language ranges.
|
|
52
52
|
*
|
|
53
|
-
* @param language_ranges -
|
|
53
|
+
* @param language_ranges - One or more language codes or ranges to filter the versions (required).
|
|
54
54
|
* @param license_id - Optional license ID to filter versions by license.
|
|
55
55
|
* @returns A promise that resolves to a collection of BibleVersion objects.
|
|
56
56
|
*/
|
|
57
57
|
async getVersions(
|
|
58
|
-
language_ranges: string,
|
|
58
|
+
language_ranges: string | string[],
|
|
59
59
|
license_id?: string | number,
|
|
60
60
|
): Promise<Collection<BibleVersion>> {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
const languageRangeArray = Array.isArray(language_ranges) ? language_ranges : [language_ranges];
|
|
62
|
+
|
|
63
|
+
const parsedLanguageRanges = z
|
|
64
|
+
.array(this.languageRangesSchema)
|
|
65
|
+
.nonempty('At least one language range is required')
|
|
66
|
+
.parse(languageRangeArray);
|
|
67
|
+
|
|
68
|
+
const params: Record<string, string | number | string[]> = {
|
|
69
|
+
'language_ranges[]': parsedLanguageRanges,
|
|
64
70
|
};
|
|
65
71
|
if (license_id !== undefined) {
|
|
66
72
|
params.license_id = license_id;
|
package/src/client.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ApiConfig } from './types';
|
|
2
2
|
|
|
3
|
-
type
|
|
3
|
+
type PrimitiveQueryParam = string | number | boolean;
|
|
4
|
+
type QueryParams = Record<string, PrimitiveQueryParam | PrimitiveQueryParam[]>;
|
|
4
5
|
type RequestData = Record<string, string | number | boolean | object>;
|
|
5
6
|
type RequestHeaders = Record<string, string>;
|
|
6
7
|
|
|
@@ -41,9 +42,18 @@ export class ApiClient {
|
|
|
41
42
|
*/
|
|
42
43
|
private buildQueryString(params?: QueryParams): string {
|
|
43
44
|
if (!params) return '';
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
|
|
46
|
+
const searchParams = new URLSearchParams();
|
|
47
|
+
|
|
48
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
49
|
+
if (Array.isArray(value)) {
|
|
50
|
+
value.forEach((item) => searchParams.append(key, String(item)));
|
|
51
|
+
} else {
|
|
52
|
+
searchParams.append(key, String(value));
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const queryString = searchParams.toString();
|
|
47
57
|
return queryString ? `?${queryString}` : '';
|
|
48
58
|
}
|
|
49
59
|
|
package/vitest.config.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { defineConfig } from 'vitest/config';
|
|
|
3
3
|
export default defineConfig({
|
|
4
4
|
test: {
|
|
5
5
|
environment: 'node',
|
|
6
|
-
setupFiles: ['./src/__tests__/setup.ts'],
|
|
6
|
+
setupFiles: ['./src/__tests__/polyfills.ts', './src/__tests__/setup.ts'],
|
|
7
7
|
testTimeout: 10_000,
|
|
8
8
|
coverage: {
|
|
9
9
|
provider: 'v8',
|