@youversion/platform-core 0.4.1

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 (59) hide show
  1. package/.env.example +7 -0
  2. package/.env.local +10 -0
  3. package/.turbo/turbo-build.log +18 -0
  4. package/CHANGELOG.md +7 -0
  5. package/LICENSE +201 -0
  6. package/README.md +369 -0
  7. package/dist/index.cjs +1330 -0
  8. package/dist/index.d.cts +737 -0
  9. package/dist/index.d.ts +737 -0
  10. package/dist/index.js +1286 -0
  11. package/package.json +46 -0
  12. package/src/AuthenticationStrategy.ts +78 -0
  13. package/src/SignInWithYouVersionResult.ts +53 -0
  14. package/src/StorageStrategy.ts +81 -0
  15. package/src/URLBuilder.ts +71 -0
  16. package/src/Users.ts +137 -0
  17. package/src/WebAuthenticationStrategy.ts +127 -0
  18. package/src/YouVersionAPI.ts +27 -0
  19. package/src/YouVersionPlatformConfiguration.ts +80 -0
  20. package/src/YouVersionUserInfo.ts +49 -0
  21. package/src/__tests__/StorageStrategy.test.ts +404 -0
  22. package/src/__tests__/URLBuilder.test.ts +289 -0
  23. package/src/__tests__/YouVersionPlatformConfiguration.test.ts +150 -0
  24. package/src/__tests__/authentication.test.ts +174 -0
  25. package/src/__tests__/bible.test.ts +356 -0
  26. package/src/__tests__/client.test.ts +109 -0
  27. package/src/__tests__/handlers.ts +41 -0
  28. package/src/__tests__/highlights.test.ts +485 -0
  29. package/src/__tests__/languages.test.ts +139 -0
  30. package/src/__tests__/setup.ts +17 -0
  31. package/src/authentication.ts +27 -0
  32. package/src/bible.ts +272 -0
  33. package/src/client.ts +162 -0
  34. package/src/highlight.ts +16 -0
  35. package/src/highlights.ts +173 -0
  36. package/src/index.ts +20 -0
  37. package/src/languages.ts +80 -0
  38. package/src/schemas/bible-index.ts +48 -0
  39. package/src/schemas/book.ts +34 -0
  40. package/src/schemas/chapter.ts +24 -0
  41. package/src/schemas/collection.ts +28 -0
  42. package/src/schemas/highlight.ts +23 -0
  43. package/src/schemas/index.ts +11 -0
  44. package/src/schemas/language.ts +38 -0
  45. package/src/schemas/passage.ts +14 -0
  46. package/src/schemas/user.ts +10 -0
  47. package/src/schemas/verse.ts +17 -0
  48. package/src/schemas/version.ts +31 -0
  49. package/src/schemas/votd.ts +10 -0
  50. package/src/types/api-config.ts +9 -0
  51. package/src/types/auth.ts +15 -0
  52. package/src/types/book.ts +116 -0
  53. package/src/types/chapter.ts +5 -0
  54. package/src/types/highlight.ts +9 -0
  55. package/src/types/index.ts +22 -0
  56. package/src/utils/constants.ts +219 -0
  57. package/tsconfig.build.json +11 -0
  58. package/tsconfig.json +12 -0
  59. package/vitest.config.ts +9 -0
package/src/bible.ts ADDED
@@ -0,0 +1,272 @@
1
+ import { z } from 'zod';
2
+ import type { ApiClient } from './client';
3
+ import type {
4
+ BibleBook,
5
+ BibleChapter,
6
+ Collection,
7
+ BibleVerse,
8
+ BibleVersion,
9
+ BiblePassage,
10
+ VOTD,
11
+ BibleIndex,
12
+ CANON,
13
+ } from './types';
14
+
15
+ /**
16
+ * Client for interacting with Bible API endpoints.
17
+ */
18
+ export class BibleClient {
19
+ private client: ApiClient;
20
+
21
+ // Validation schemas
22
+ private versionIdSchema = z.number().int().positive('Version ID must be a positive integer');
23
+ private bookSchema = z
24
+ .string()
25
+ .trim()
26
+ .min(3, 'Book ID must be exactly 3 characters')
27
+ .max(3, 'Book ID must be exactly 3 characters');
28
+ private chapterSchema = z
29
+ .number()
30
+ .int('Chapter must be an integer')
31
+ .positive('Chapter must be a positive integer');
32
+ private verseSchema = z
33
+ .number()
34
+ .int('Verse must be an integer')
35
+ .positive('Verse must be a positive integer');
36
+ private languageRangesSchema = z
37
+ .string()
38
+ .trim()
39
+ .min(1, 'Language ranges must be a non-empty string');
40
+ private booleanSchema = z.boolean();
41
+
42
+ /**
43
+ * Creates a new BibleClient instance.
44
+ * @param client The API client to use for requests.
45
+ */
46
+ constructor(client: ApiClient) {
47
+ this.client = client;
48
+ }
49
+
50
+ private get rootPath(): string {
51
+ return `/${this.client.config.version}`;
52
+ }
53
+
54
+ /**
55
+ * Fetches a collection of Bible versions filtered by language ranges.
56
+ *
57
+ * @param language_ranges - A comma-separated list of language codes or ranges to filter the versions (required).
58
+ * @param license_id - Optional license ID to filter versions by license.
59
+ * @returns A promise that resolves to a collection of BibleVersion objects.
60
+ */
61
+ async getVersions(
62
+ language_ranges: string,
63
+ license_id?: string | number,
64
+ ): Promise<Collection<BibleVersion>> {
65
+ this.languageRangesSchema.parse(language_ranges);
66
+ const params: Record<string, string | number> = {
67
+ 'language_ranges[]': language_ranges,
68
+ };
69
+ if (license_id !== undefined) {
70
+ params.license_id = license_id;
71
+ }
72
+ return this.client.get<Collection<BibleVersion>>(`${this.rootPath}/bibles`, params);
73
+ }
74
+
75
+ /**
76
+ * Fetches a Bible version by its ID.
77
+ * @param id The version ID.
78
+ * @returns The requested BibleVersion object.
79
+ */
80
+ async getVersion(id: number): Promise<BibleVersion> {
81
+ this.versionIdSchema.parse(id);
82
+ return this.client.get<BibleVersion>(`${this.rootPath}/bibles/${id}`);
83
+ }
84
+
85
+ /**
86
+ * Fetches all books for a given Bible version.
87
+ * @param versionId The version ID.
88
+ * @param canon Optional canon filter ('ot', 'nt', 'deuterocanon').
89
+ * @returns An array of BibleBook objects.
90
+ */
91
+ async getBooks(versionId: number, canon?: CANON): Promise<Collection<BibleBook>> {
92
+ this.versionIdSchema.parse(versionId);
93
+ return this.client.get<Collection<BibleBook>>(`${this.rootPath}/bibles/${versionId}/books`, {
94
+ ...(canon && { canon }),
95
+ });
96
+ }
97
+
98
+ /**
99
+ * Fetches a specific book by USFM code for a given version.
100
+ * @param versionId The version ID.
101
+ * @param book The Book Identifier code of the book.
102
+ * @returns The requested BibleBook object.
103
+ */
104
+ async getBook(versionId: number, book: string): Promise<BibleBook> {
105
+ this.versionIdSchema.parse(versionId);
106
+ this.bookSchema.parse(book);
107
+ return this.client.get<BibleBook>(`${this.rootPath}/bibles/${versionId}/books/${book}`);
108
+ }
109
+
110
+ /**
111
+ * Fetches all chapters for a specific book in a version.
112
+ * @param versionId The version ID.
113
+ * @param book The Book Identifier code of the book.
114
+ * @returns An array of BibleChapter objects.
115
+ */
116
+ async getChapters(versionId: number, book: string): Promise<Collection<BibleChapter>> {
117
+ this.versionIdSchema.parse(versionId);
118
+ this.bookSchema.parse(book);
119
+ return this.client.get<Collection<BibleChapter>>(
120
+ `${this.rootPath}/bibles/${versionId}/books/${book}/chapters`,
121
+ );
122
+ }
123
+
124
+ /**
125
+ * Fetches a specific chapter for a book in a version.
126
+ * @param versionId The version ID.
127
+ * @param book The Book Identifier code of the book (3 characters, e.g., "MAT").
128
+ * @param chapter The chapter number
129
+ * @returns The requested BibleChapter object.
130
+ */
131
+ async getChapter(versionId: number, book: string, chapter: number): Promise<BibleChapter> {
132
+ this.versionIdSchema.parse(versionId);
133
+ this.bookSchema.parse(book);
134
+ this.chapterSchema.parse(chapter);
135
+
136
+ return this.client.get<BibleChapter>(
137
+ `${this.rootPath}/bibles/${versionId}/books/${book}/chapters/${chapter}`,
138
+ );
139
+ }
140
+
141
+ /**
142
+ * Fetches all verses for a specific chapter in a book and version.
143
+ * @param versionId The version ID.
144
+ * @param book The Book Identifier code of the book (3 characters, e.g., "MAT").
145
+ * @param chapter The chapter number.
146
+ * @returns An array of BibleVerse objects.
147
+ */
148
+ async getVerses(
149
+ versionId: number,
150
+ book: string,
151
+ chapter: number,
152
+ ): Promise<Collection<BibleVerse>> {
153
+ this.versionIdSchema.parse(versionId);
154
+ this.bookSchema.parse(book);
155
+ this.chapterSchema.parse(chapter);
156
+
157
+ return this.client.get<Collection<BibleVerse>>(
158
+ `${this.rootPath}/bibles/${versionId}/books/${book}/chapters/${chapter}/verses`,
159
+ );
160
+ }
161
+
162
+ /**
163
+ * Fetches a specific verse from a chapter, book, and version.
164
+ * @param versionId The version ID.
165
+ * @param book The Book Identifier code of the book (3 characters, e.g., "MAT").
166
+ * @param chapter The chapter number.
167
+ * @param verse The verse number.
168
+ * @returns The requested BibleVerse object.
169
+ */
170
+ async getVerse(
171
+ versionId: number,
172
+ book: string,
173
+ chapter: number,
174
+ verse: number,
175
+ ): Promise<BibleVerse> {
176
+ this.versionIdSchema.parse(versionId);
177
+ this.bookSchema.parse(book);
178
+ this.chapterSchema.parse(chapter);
179
+ this.verseSchema.parse(verse);
180
+
181
+ return this.client.get<BibleVerse>(
182
+ `${this.rootPath}/bibles/${versionId}/books/${book}/chapters/${chapter}/verses/${verse}`,
183
+ );
184
+ }
185
+
186
+ /**
187
+ * Fetches a passage (range of verses) from the Bible using the passages endpoint.
188
+ * This is the new API format that returns HTML-formatted content.
189
+ * @param versionId The version ID.
190
+ * @param usfm The USFM reference (e.g., "JHN.3.1-2", "GEN.1", "JHN.3.16").
191
+ * @param format The format to return ("html" or "text", default: "html").
192
+ * @param include_headings Whether to include headings in the content.
193
+ * @param include_notes Whether to include notes in the content.
194
+ * @returns The requested BiblePassage object with HTML content.
195
+ * @example
196
+ * ```ts
197
+ * // Get a single verse
198
+ * const verse = await bibleClient.getPassage(111, "JHN.3.16");
199
+ *
200
+ * // Get a range of verses
201
+ * const verses = await bibleClient.getPassage(111, "JHN.3.1-5");
202
+ *
203
+ * // Get an entire chapter
204
+ * const chapter = await bibleClient.getPassage(111, "GEN.1");
205
+ * ```
206
+ */
207
+ async getPassage(
208
+ versionId: number,
209
+ usfm: string,
210
+ format: 'html' | 'text' = 'html',
211
+ include_headings?: boolean,
212
+ include_notes?: boolean,
213
+ ): Promise<BiblePassage> {
214
+ this.versionIdSchema.parse(versionId);
215
+ if (include_headings !== undefined) {
216
+ this.booleanSchema.parse(include_headings);
217
+ }
218
+ if (include_notes !== undefined) {
219
+ this.booleanSchema.parse(include_notes);
220
+ }
221
+ const params: Record<string, string | number | boolean> = {
222
+ format,
223
+ };
224
+ if (include_headings !== undefined) {
225
+ params.include_headings = include_headings;
226
+ }
227
+ if (include_notes !== undefined) {
228
+ params.include_notes = include_notes;
229
+ }
230
+ return this.client.get<BiblePassage>(
231
+ `${this.rootPath}/bibles/${versionId}/passages/${usfm}`,
232
+ params,
233
+ );
234
+ }
235
+
236
+ /**
237
+ * Fetches the indexing structure for a Bible version.
238
+ * @param versionId The version ID.
239
+ * @returns The BibleIndex object containing full hierarchy of books, chapters, and verses.
240
+ */
241
+ async getIndex(versionId: number): Promise<BibleIndex> {
242
+ this.versionIdSchema.parse(versionId);
243
+ return this.client.get<BibleIndex>(`${this.rootPath}/bibles/${versionId}/index`);
244
+ }
245
+
246
+ /**
247
+ * Fetches the verse of the day calendar for an entire year.
248
+ * @returns A collection of VOTD objects for all days of the year.
249
+ */
250
+ async getAllVOTDs(): Promise<Collection<VOTD>> {
251
+ return this.client.get<Collection<VOTD>>(`${this.rootPath}/verse_of_the_days`);
252
+ }
253
+
254
+ /**
255
+ * Fetches the passage_id for the Verse Of The Day.
256
+ * @param day The day of the year (1-366).
257
+ * @returns The day of the year and the passage_id for that day of the year
258
+ * @example
259
+ * ```ts
260
+ * // Get the passageId for the verse of the first day of the year.
261
+ * const passageId = await bibleClient.getVOTD(1);
262
+ *
263
+ * // Get the passageId for the verse of the 100th day of the year.
264
+ * const passageId = await bibleClient.getVOTD(100);
265
+ * ```
266
+ */
267
+ async getVOTD(day: number): Promise<VOTD> {
268
+ const daySchema = z.number().int().min(1).max(366);
269
+ daySchema.parse(day);
270
+ return this.client.get<VOTD>(`${this.rootPath}/verse_of_the_days/${day}`);
271
+ }
272
+ }
package/src/client.ts ADDED
@@ -0,0 +1,162 @@
1
+ import type { ApiConfig } from './types';
2
+
3
+ type QueryParams = Record<string, string | number | boolean>;
4
+ type RequestData = Record<string, string | number | boolean | object>;
5
+ type RequestHeaders = Record<string, string>;
6
+
7
+ /**
8
+ * ApiClient is a lightweight HTTP client for interacting with the API using fetch.
9
+ * It provides convenient methods for GET and POST requests with typed responses.
10
+ */
11
+ export class ApiClient {
12
+ private baseURL: string;
13
+ private timeout: number;
14
+ private defaultHeaders: RequestHeaders;
15
+ public config: ApiConfig;
16
+
17
+ /**
18
+ * Creates an instance of ApiClient.
19
+ *
20
+ * @param config - The API configuration object containing baseUrl, timeout, and appId.
21
+ */
22
+ constructor(config: ApiConfig) {
23
+ this.config = {
24
+ version: config.version || 'v1',
25
+ ...config,
26
+ };
27
+ this.baseURL = config.baseUrl || 'https://api-dev.youversion.com';
28
+ this.timeout = config.timeout || 10000;
29
+ this.defaultHeaders = {
30
+ 'Content-Type': 'application/json',
31
+ 'X-YVP-App-Key': this.config.appId,
32
+ 'X-YVP-Installation-Id': this.config.installationId || 'web-sdk-default',
33
+ };
34
+ }
35
+
36
+ /**
37
+ * Builds the query string from parameters
38
+ */
39
+ private buildQueryString(params?: QueryParams): string {
40
+ if (!params) return '';
41
+ const queryString = new URLSearchParams(
42
+ Object.entries(params).map(([key, value]) => [key, String(value)]),
43
+ ).toString();
44
+ return queryString ? `?${queryString}` : '';
45
+ }
46
+
47
+ /**
48
+ * Makes an HTTP request with timeout support
49
+ */
50
+ private async request<T>(url: string, options: RequestInit = {}): Promise<T> {
51
+ const controller = new AbortController();
52
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
53
+
54
+ try {
55
+ const response = await fetch(url, {
56
+ ...options,
57
+ signal: controller.signal,
58
+ headers: {
59
+ ...this.defaultHeaders,
60
+ ...options.headers,
61
+ },
62
+ });
63
+
64
+ clearTimeout(timeoutId);
65
+
66
+ if (!response.ok) {
67
+ const isDevelopment = process.env.NODE_ENV === 'development';
68
+ let errorMessage = `HTTP error! status: ${response.status}`;
69
+ try {
70
+ const errorBody = await response.text();
71
+ if (errorBody) {
72
+ try {
73
+ const errorJson = JSON.parse(errorBody) as { message?: string; error?: string };
74
+ const detailedMessage = errorJson.message || errorJson.error || errorBody;
75
+ if (isDevelopment) {
76
+ errorMessage = detailedMessage;
77
+ } else {
78
+ errorMessage = `Request failed with status ${response.status}`;
79
+ }
80
+ } catch {
81
+ if (isDevelopment) {
82
+ errorMessage = errorBody;
83
+ }
84
+ }
85
+ }
86
+ } catch (error) {
87
+ if (isDevelopment && error instanceof Error) {
88
+ console.error('Failed to read error response:', error.message);
89
+ }
90
+ }
91
+ const error = Object.assign(new Error(errorMessage), {
92
+ status: response.status,
93
+ statusText: response.statusText,
94
+ });
95
+ throw error;
96
+ }
97
+
98
+ const contentType = response.headers.get('content-type');
99
+ if (contentType?.includes('application/json')) {
100
+ const data = (await response.json()) as T;
101
+ return data;
102
+ } else {
103
+ const text = await response.text();
104
+ return text as unknown as T;
105
+ }
106
+ } catch (error) {
107
+ clearTimeout(timeoutId);
108
+ if (error instanceof Error && error.name === 'AbortError') {
109
+ throw new Error(`Request timeout after ${this.timeout}ms`);
110
+ }
111
+ throw error;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Sends a GET request to the specified API path with optional query parameters.
117
+ *
118
+ * @typeParam T - The expected response type.
119
+ * @param path - The API endpoint path (relative to baseURL).
120
+ * @param params - Optional query parameters to include in the request.
121
+ * @returns A promise resolving to the response data of type T.
122
+ */
123
+ async get<T>(path: string, params?: QueryParams, headers?: RequestHeaders): Promise<T> {
124
+ const url = `${this.baseURL}${path}${this.buildQueryString(params)}`;
125
+ return this.request<T>(url, {
126
+ method: 'GET',
127
+ headers,
128
+ });
129
+ }
130
+
131
+ /**
132
+ * Sends a POST request to the specified API path with optional data and query parameters.
133
+ *
134
+ * @typeParam T - The expected response type.
135
+ * @param path - The API endpoint path (relative to baseURL).
136
+ * @param data - Optional request body data to send.
137
+ * @param params - Optional query parameters to include in the request.
138
+ * @returns A promise resolving to the response data of type T.
139
+ */
140
+ async post<T>(path: string, data?: RequestData, params?: QueryParams): Promise<T> {
141
+ const url = `${this.baseURL}${path}${this.buildQueryString(params)}`;
142
+ return this.request<T>(url, {
143
+ method: 'POST',
144
+ body: data ? JSON.stringify(data) : undefined,
145
+ });
146
+ }
147
+
148
+ /**
149
+ * Sends a DELETE request to the specified API path with optional query parameters.
150
+ *
151
+ * @typeParam T - The expected response type.
152
+ * @param path - The API endpoint path (relative to baseURL).
153
+ * @param params - Optional query parameters to include in the request.
154
+ * @returns A promise resolving to the response data of type T (may be empty for 204 responses).
155
+ */
156
+ async delete<T>(path: string, params?: QueryParams): Promise<T> {
157
+ const url = `${this.baseURL}${path}${this.buildQueryString(params)}`;
158
+ return this.request<T>(url, {
159
+ method: 'DELETE',
160
+ });
161
+ }
162
+ }
@@ -0,0 +1,16 @@
1
+ import type { HighlightColor } from './types';
2
+
3
+ export const HIGHLIGHT_COLORS: HighlightColor[] = [
4
+ { id: 0, color: '#fffe00', label: 'Yellow' },
5
+ { id: 1, color: '#5dff79', label: 'Green' },
6
+ { id: 2, color: '#00d6ff', label: 'Blue' },
7
+ { id: 3, color: '#ffc66f', label: 'Orange' },
8
+ { id: 4, color: '#ff95ef', label: 'Pink' },
9
+ ] as const;
10
+
11
+ export function hexToRgba(hex: string, alpha: number): string {
12
+ const r = parseInt(hex.slice(1, 3), 16);
13
+ const g = parseInt(hex.slice(3, 5), 16);
14
+ const b = parseInt(hex.slice(5, 7), 16);
15
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
16
+ }
@@ -0,0 +1,173 @@
1
+ import { z } from 'zod';
2
+ import type { ApiClient } from './client';
3
+ import type { Collection, Highlight, CreateHighlight } from './types';
4
+ import { YouVersionPlatformConfiguration } from './YouVersionPlatformConfiguration';
5
+
6
+ /**
7
+ * Options for getting highlights.
8
+ */
9
+ export type GetHighlightsOptions = {
10
+ version_id?: number;
11
+ passage_id?: string;
12
+ };
13
+
14
+ /**
15
+ * Options for deleting highlights.
16
+ */
17
+ export type DeleteHighlightOptions = {
18
+ version_id?: number;
19
+ };
20
+
21
+ /**
22
+ * Client for interacting with Highlights API endpoints.
23
+ * Note: All endpoints require OAuth authentication with appropriate scopes.
24
+ */
25
+ export class HighlightsClient {
26
+ private client: ApiClient;
27
+
28
+ private versionIdSchema = z.number().int().positive('Version ID must be a positive integer');
29
+ private passageIdSchema = z.string().trim().min(1, 'Passage ID must be a non-empty string');
30
+ private colorSchema = z
31
+ .string()
32
+ .regex(/^[0-9a-f]{6}$/i, 'Color must be a 6-character hex string without #');
33
+
34
+ /**
35
+ * Creates a new HighlightsClient instance.
36
+ * @param client The API client to use for requests.
37
+ */
38
+ constructor(client: ApiClient) {
39
+ this.client = client;
40
+ }
41
+
42
+ private get rootPath(): string {
43
+ return `/${this.client.config.version}`;
44
+ }
45
+
46
+ /**
47
+ * Gets the authentication token, either from the provided parameter or from the platform configuration.
48
+ * @param lat Optional explicit long access token. If not provided, retrieves from YouVersionPlatformConfiguration.
49
+ * @returns The authentication token.
50
+ * @throws Error if no token is available.
51
+ */
52
+ private getAuthToken(lat?: string): string {
53
+ if (lat) {
54
+ return lat;
55
+ }
56
+ const token = YouVersionPlatformConfiguration.accessToken;
57
+ if (!token) {
58
+ throw new Error(
59
+ 'Authentication required. Please provide a token or sign in before accessing highlights.',
60
+ );
61
+ }
62
+ return token;
63
+ }
64
+
65
+ private validateVersionId(value: number): void {
66
+ try {
67
+ this.versionIdSchema.parse(value);
68
+ } catch (error) {
69
+ if (error instanceof z.ZodError) {
70
+ throw new Error('Version ID must be a positive integer');
71
+ }
72
+ throw error;
73
+ }
74
+ }
75
+
76
+ private validatePassageId(value: string): void {
77
+ try {
78
+ this.passageIdSchema.parse(value);
79
+ } catch (error) {
80
+ if (error instanceof z.ZodError) {
81
+ throw new Error('Passage ID must be a non-empty string');
82
+ }
83
+ throw error;
84
+ }
85
+ }
86
+
87
+ private validateColor(value: string): void {
88
+ try {
89
+ this.colorSchema.parse(value);
90
+ } catch (error) {
91
+ if (error instanceof z.ZodError) {
92
+ throw new Error('Color must be a 6-character hex string without #');
93
+ }
94
+ throw error;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Fetches a collection of highlights for a user.
100
+ * The response will return a color per verse without ranges.
101
+ * Requires OAuth with read_highlights scope.
102
+ * @param options Query parameters for filtering highlights.
103
+ * @param lat Optional long access token. If not provided, retrieves from YouVersionPlatformConfiguration.
104
+ * @returns A collection of Highlight objects.
105
+ */
106
+ async getHighlights(
107
+ options?: GetHighlightsOptions,
108
+ lat?: string,
109
+ ): Promise<Collection<Highlight>> {
110
+ const token = this.getAuthToken(lat);
111
+ const params: Record<string, string | number> = {
112
+ lat: token,
113
+ };
114
+
115
+ if (options?.version_id !== undefined) {
116
+ this.validateVersionId(options.version_id);
117
+ params.version_id = options.version_id;
118
+ }
119
+
120
+ if (options?.passage_id !== undefined) {
121
+ this.validatePassageId(options.passage_id);
122
+ params.passage_id = options.passage_id;
123
+ }
124
+
125
+ return this.client.get<Collection<Highlight>>(`${this.rootPath}/highlights`, params);
126
+ }
127
+
128
+ /**
129
+ * Creates or updates a highlight on a passage.
130
+ * Verse ranges may be used in the passage_id attribute.
131
+ * Requires OAuth with write_highlights scope.
132
+ * @param data The highlight data to create or update.
133
+ * @param lat Optional long access token. If not provided, retrieves from YouVersionPlatformConfiguration.
134
+ * @returns The created or updated Highlight object.
135
+ */
136
+ async createHighlight(data: CreateHighlight, lat?: string): Promise<Highlight> {
137
+ this.validateVersionId(data.version_id);
138
+ this.validatePassageId(data.passage_id);
139
+ this.validateColor(data.color);
140
+
141
+ const token = this.getAuthToken(lat);
142
+
143
+ return this.client.post<Highlight>(`${this.rootPath}/highlights`, data, { lat: token });
144
+ }
145
+
146
+ /**
147
+ * Clears highlights for a passage.
148
+ * Requires OAuth with write_highlights scope.
149
+ * @param passageId The passage identifier (USFM format, e.g., "MAT.1.1" or "MAT.1.1-5").
150
+ * @param options Optional query parameters including bible_id.
151
+ * @param lat Optional long access token. If not provided, retrieves from YouVersionPlatformConfiguration.
152
+ * @returns Promise that resolves when highlights are deleted (204 response).
153
+ */
154
+ async deleteHighlight(
155
+ passageId: string,
156
+ options?: DeleteHighlightOptions,
157
+ lat?: string,
158
+ ): Promise<void> {
159
+ this.validatePassageId(passageId);
160
+
161
+ const token = this.getAuthToken(lat);
162
+ const params: Record<string, string | number> = {
163
+ lat: token,
164
+ };
165
+
166
+ if (options?.version_id !== undefined) {
167
+ this.validateVersionId(options.version_id);
168
+ params.version_id = options.version_id;
169
+ }
170
+
171
+ await this.client.delete<void>(`${this.rootPath}/highlights/${passageId}`, params);
172
+ }
173
+ }
package/src/index.ts ADDED
@@ -0,0 +1,20 @@
1
+ export { ApiClient } from './client';
2
+ export { BibleClient } from './bible';
3
+ export { LanguagesClient, type GetLanguagesOptions } from './languages';
4
+ export {
5
+ HighlightsClient,
6
+ type GetHighlightsOptions,
7
+ type DeleteHighlightOptions,
8
+ } from './highlights';
9
+ export { AuthClient } from './authentication';
10
+ export * from './AuthenticationStrategy';
11
+ export { WebAuthenticationStrategy } from './WebAuthenticationStrategy';
12
+ export * from './StorageStrategy';
13
+ export * from './Users';
14
+ export * from './YouVersionUserInfo';
15
+ export * from './SignInWithYouVersionResult';
16
+ export * from './YouVersionAPI';
17
+ export * from './URLBuilder';
18
+ export * from './YouVersionPlatformConfiguration';
19
+ export * from './types';
20
+ export * from './utils/constants';