@ambry/scripture 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ambry contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,152 @@
1
- # Temporary Holding Version
1
+ # @ambry/scripture
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Ambry's 73 Bible books, canonical API codes, common aliases, reference parser, and scripture API client.
4
+ The package has no runtime dependencies and includes ESM, CommonJS, and TypeScript declarations.
5
+ The root export works offline without credentials; authenticated requests live in `@ambry/scripture/client`.
6
+ Node.js 20 or newer is required for the client. The offline parser also works in modern browsers.
7
+
8
+ ## Install
9
+
10
+ After the package has been published to npm:
11
+
12
+ ```sh
13
+ npm install @ambry/scripture
14
+ ```
15
+
16
+ Before publication, install a packed release directly:
17
+
18
+ ```sh
19
+ npm install /path/to/ambry-scripture-0.1.0.tgz
20
+ ```
21
+
22
+ ## Books and reference parsing
23
+
24
+ ```ts
25
+ import { scriptureBooks, parseScriptureReference, findScriptureReferences } from '@ambry/scripture';
26
+
27
+ scriptureBooks.find((book) => book.code === 'joh');
28
+ // { name: 'John', code: 'joh', aliases: ['Jn'] }
29
+
30
+ parseScriptureReference('Jn. 3:16–18 GRK');
31
+ // {
32
+ // book: 'John', bookCode: 'joh', chapter: 3,
33
+ // firstVerse: 16, lastVerse: 18, language: 'greek'
34
+ // }
35
+
36
+ parseScriptureReference('John 3'); // A chapter reference, without verse bounds.
37
+ parseScriptureReference('Not a Bible reference'); // undefined
38
+
39
+ findScriptureReferences('Read John 3:16 and Romans 8:28 GRK.');
40
+ // Two references, both in Greek.
41
+
42
+ findScriptureReferences('John 3:16 LAT, Romans 8:28 GRK');
43
+ // John in Latin; Romans in Greek.
44
+ ```
45
+
46
+ Each book keeps its `name`, `code`, and `aliases` together in `src/books.ts`.
47
+ Canonical names, Ambry codes, and aliases are case insensitive. Periods and spacing in
48
+ abbreviations are normalized, so `Jn. 3:16` and `1Jn4:8` are accepted.
49
+
50
+ `parseScriptureReference` accepts a chapter, a single verse, or a same-chapter verse range.
51
+ `findScriptureReferences` detects explicit chapter-and-verse references anywhere in text;
52
+ chapter-only references are skipped. Duplicate references in the same language are returned once.
53
+ Results follow their appearance in the input. Cross-chapter ranges and comma-separated verse lists
54
+ are not supported.
55
+
56
+ Both functions validate syntax and broad bounds (chapters 1–150, verses 1–176).
57
+ They do not check whether a specific book/chapter/verse exists in Ambry's database.
58
+ References use Ambry's book and chapter numbering, including its Psalm numbering.
59
+
60
+ ## Languages
61
+
62
+ English is the default. Suffixes are case insensitive:
63
+
64
+ | Language | Accepted suffixes | Ambry field |
65
+ | --- | --- | --- |
66
+ | English | `ENG`, `EN`, `ENGLISH` | `english_text` |
67
+ | Greek | `GRK`, `GRC`, `GREEK` | `greek_text` |
68
+ | Latin | `LAT`, `LA`, `LATIN` | `latin_text` |
69
+
70
+ A suffix directly after a reference applies to that reference. A suffix at the end of a message
71
+ also applies to references without their own suffix. `scriptureLanguages` exports the display labels
72
+ and API text fields. The API supplies the text; the package does not bundle Bible translations.
73
+ An unavailable language field remains unavailable rather than falling back to a different language.
74
+ The API does not identify a particular English edition in its response metadata.
75
+
76
+ ## Consumer limits
77
+
78
+ The parser allows up to 176 verses in a range by default. Detection returns up to 10 distinct references
79
+ by default. Applications can set their own limits, independently of the Ambry dataset:
80
+
81
+ ```ts
82
+ parseScriptureReference('John 3:16-18', { maxVerses: 20 });
83
+ findScriptureReferences('John 3:16, Romans 8:28', { maxVerses: 20, maxReferences: 3 });
84
+ ```
85
+
86
+ Limits must be positive safe integers; invalid options throw `RangeError`.
87
+
88
+ ## Authenticated API client
89
+
90
+ ```ts
91
+ import { parseScriptureReference, scriptureLanguages } from '@ambry/scripture';
92
+ import { fetchScripture } from '@ambry/scripture/client';
93
+
94
+ const reference = parseScriptureReference('John 3:16 GRK');
95
+ if (!reference) throw new Error('Invalid scripture reference.');
96
+
97
+ const result = await fetchScripture(reference, {
98
+ apiKey: process.env.AMBRY_API_KEY!,
99
+ // baseUrl: 'https://api.ambry.church', // Default; override for your own API instance.
100
+ });
101
+
102
+ const language = scriptureLanguages[reference.language ?? 'english'];
103
+ for (const verse of result.verses) {
104
+ console.log(verse[language.field] ?? 'Text unavailable in the requested language.');
105
+ }
106
+ ```
107
+
108
+ Keep API keys on your server. Obtain an Ambry developer key at
109
+ `https://ambry.church/developers/api-keys`. The client uses bearer authentication and
110
+ `GET /api/v1/bible-verses`, validates responses, and reads up to two 100-verse pages to retrieve
111
+ a full chapter. It returns `{ verses, total }`; presentation, preview limits, and message splitting
112
+ belong to the consumer. A single-verse request uses the API's `verse` filter.
113
+
114
+ No results return an empty `verses` array. HTTP failures, malformed responses, and partially returned
115
+ verse ranges throw an error. Requests time out after 15 seconds per page and refuse redirects;
116
+ credentials and response bodies are not included in client-generated errors.
117
+ The third argument accepts a fetch-compatible function for other runtimes or local verification.
118
+
119
+ CommonJS is supported through the same exports:
120
+
121
+ ```js
122
+ const { parseScriptureReference } = require('@ambry/scripture');
123
+ const { fetchScripture } = require('@ambry/scripture/client');
124
+ ```
125
+
126
+ ## Build and release
127
+
128
+ From the Ambry repository root:
129
+
130
+ ```sh
131
+ pnpm install
132
+ pnpm check:scripture
133
+ pnpm build:scripture
134
+ mkdir -p /tmp/ambry-scripture-release
135
+ pnpm --dir packages/scripture pack --pack-destination /tmp/ambry-scripture-release
136
+ ```
137
+
138
+ The build emits `dist/esm` and `dist/cjs`, each with JavaScript and declaration files.
139
+ Only those outputs, this README, the MIT license, and package metadata are packed.
140
+ Verify the tarball in a separate consumer project before publishing.
141
+
142
+ To publish the reviewed tarball, sign into an npm account with access to the `@ambry` scope:
143
+
144
+ ```sh
145
+ npm whoami --registry=https://registry.npmjs.org/
146
+ npm publish /tmp/ambry-scripture-release/ambry-scripture-0.1.0.tgz --access public
147
+ ```
148
+
149
+ Increment `packages/scripture/package.json` before subsequent releases and rebuild/repack.
150
+ `prepack` also runs the build when lifecycle scripts are enabled.
151
+ Keep the book list aligned with the API's OpenAPI book name/code enums; alias and parser changes
152
+ can be released without changing the Go or Laravel applications.
@@ -0,0 +1,295 @@
1
+ export declare const scriptureBooks: readonly [{
2
+ readonly name: "Genesis";
3
+ readonly code: "gen";
4
+ readonly aliases: readonly ["Gen"];
5
+ }, {
6
+ readonly name: "Exodus";
7
+ readonly code: "exo";
8
+ readonly aliases: readonly ["Ex", "Exod"];
9
+ }, {
10
+ readonly name: "Leviticus";
11
+ readonly code: "lev";
12
+ readonly aliases: readonly ["Lev"];
13
+ }, {
14
+ readonly name: "Numbers";
15
+ readonly code: "num";
16
+ readonly aliases: readonly ["Num"];
17
+ }, {
18
+ readonly name: "Deuteronomy";
19
+ readonly code: "deu";
20
+ readonly aliases: readonly ["Deut"];
21
+ }, {
22
+ readonly name: "Joshua";
23
+ readonly code: "jos";
24
+ readonly aliases: readonly ["Josh"];
25
+ }, {
26
+ readonly name: "Judges";
27
+ readonly code: "jdg";
28
+ readonly aliases: readonly ["Judg"];
29
+ }, {
30
+ readonly name: "Ruth";
31
+ readonly code: "rut";
32
+ readonly aliases: readonly ["Ruth"];
33
+ }, {
34
+ readonly name: "1 Samuel";
35
+ readonly code: "1sa";
36
+ readonly aliases: readonly ["1 Sam"];
37
+ }, {
38
+ readonly name: "2 Samuel";
39
+ readonly code: "2sa";
40
+ readonly aliases: readonly ["2 Sam"];
41
+ }, {
42
+ readonly name: "1 Kings";
43
+ readonly code: "1ki";
44
+ readonly aliases: readonly ["1 Kgs"];
45
+ }, {
46
+ readonly name: "2 Kings";
47
+ readonly code: "2ki";
48
+ readonly aliases: readonly ["2 Kgs"];
49
+ }, {
50
+ readonly name: "1 Chronicles";
51
+ readonly code: "1ch";
52
+ readonly aliases: readonly ["1 Chron"];
53
+ }, {
54
+ readonly name: "2 Chronicles";
55
+ readonly code: "2ch";
56
+ readonly aliases: readonly ["2 Chron"];
57
+ }, {
58
+ readonly name: "Ezra";
59
+ readonly code: "ezr";
60
+ readonly aliases: readonly [];
61
+ }, {
62
+ readonly name: "Nehemiah";
63
+ readonly code: "neh";
64
+ readonly aliases: readonly ["Neh"];
65
+ }, {
66
+ readonly name: "Tobit";
67
+ readonly code: "tob";
68
+ readonly aliases: readonly ["Tob"];
69
+ }, {
70
+ readonly name: "Judith";
71
+ readonly code: "jth";
72
+ readonly aliases: readonly ["Jdt"];
73
+ }, {
74
+ readonly name: "Esther";
75
+ readonly code: "est";
76
+ readonly aliases: readonly [];
77
+ }, {
78
+ readonly name: "Job";
79
+ readonly code: "job";
80
+ readonly aliases: readonly [];
81
+ }, {
82
+ readonly name: "Psalms";
83
+ readonly code: "psa";
84
+ readonly aliases: readonly ["Psalm", "Ps"];
85
+ }, {
86
+ readonly name: "Proverbs";
87
+ readonly code: "pro";
88
+ readonly aliases: readonly ["Prov"];
89
+ }, {
90
+ readonly name: "Ecclesiastes";
91
+ readonly code: "ecc";
92
+ readonly aliases: readonly ["Eccl"];
93
+ }, {
94
+ readonly name: "Song of Songs";
95
+ readonly code: "son";
96
+ readonly aliases: readonly ["Song of Solomon", "Canticles"];
97
+ }, {
98
+ readonly name: "Wisdom";
99
+ readonly code: "wis";
100
+ readonly aliases: readonly [];
101
+ }, {
102
+ readonly name: "Sirach";
103
+ readonly code: "sir";
104
+ readonly aliases: readonly ["Ecclesiasticus"];
105
+ }, {
106
+ readonly name: "Isaiah";
107
+ readonly code: "isa";
108
+ readonly aliases: readonly ["Isa"];
109
+ }, {
110
+ readonly name: "Jeremiah";
111
+ readonly code: "jer";
112
+ readonly aliases: readonly ["Jer"];
113
+ }, {
114
+ readonly name: "Lamentations";
115
+ readonly code: "lam";
116
+ readonly aliases: readonly [];
117
+ }, {
118
+ readonly name: "Baruch";
119
+ readonly code: "bar";
120
+ readonly aliases: readonly [];
121
+ }, {
122
+ readonly name: "Ezekiel";
123
+ readonly code: "eze";
124
+ readonly aliases: readonly ["Ezek"];
125
+ }, {
126
+ readonly name: "Daniel";
127
+ readonly code: "dan";
128
+ readonly aliases: readonly ["Dan"];
129
+ }, {
130
+ readonly name: "Hosea";
131
+ readonly code: "hos";
132
+ readonly aliases: readonly [];
133
+ }, {
134
+ readonly name: "Joel";
135
+ readonly code: "joe";
136
+ readonly aliases: readonly [];
137
+ }, {
138
+ readonly name: "Amos";
139
+ readonly code: "amo";
140
+ readonly aliases: readonly [];
141
+ }, {
142
+ readonly name: "Obadiah";
143
+ readonly code: "oba";
144
+ readonly aliases: readonly [];
145
+ }, {
146
+ readonly name: "Jonah";
147
+ readonly code: "jon";
148
+ readonly aliases: readonly [];
149
+ }, {
150
+ readonly name: "Micah";
151
+ readonly code: "mic";
152
+ readonly aliases: readonly [];
153
+ }, {
154
+ readonly name: "Nahum";
155
+ readonly code: "nah";
156
+ readonly aliases: readonly [];
157
+ }, {
158
+ readonly name: "Habakkuk";
159
+ readonly code: "hab";
160
+ readonly aliases: readonly [];
161
+ }, {
162
+ readonly name: "Zephaniah";
163
+ readonly code: "zep";
164
+ readonly aliases: readonly [];
165
+ }, {
166
+ readonly name: "Haggai";
167
+ readonly code: "hag";
168
+ readonly aliases: readonly [];
169
+ }, {
170
+ readonly name: "Zechariah";
171
+ readonly code: "zec";
172
+ readonly aliases: readonly [];
173
+ }, {
174
+ readonly name: "Malachi";
175
+ readonly code: "mal";
176
+ readonly aliases: readonly [];
177
+ }, {
178
+ readonly name: "1 Maccabees";
179
+ readonly code: "1ma";
180
+ readonly aliases: readonly [];
181
+ }, {
182
+ readonly name: "2 Maccabees";
183
+ readonly code: "2ma";
184
+ readonly aliases: readonly [];
185
+ }, {
186
+ readonly name: "Matthew";
187
+ readonly code: "mat";
188
+ readonly aliases: readonly ["Mt", "Matt"];
189
+ }, {
190
+ readonly name: "Mark";
191
+ readonly code: "mar";
192
+ readonly aliases: readonly ["Mk"];
193
+ }, {
194
+ readonly name: "Luke";
195
+ readonly code: "luk";
196
+ readonly aliases: readonly ["Lk"];
197
+ }, {
198
+ readonly name: "John";
199
+ readonly code: "joh";
200
+ readonly aliases: readonly ["Jn"];
201
+ }, {
202
+ readonly name: "Acts";
203
+ readonly code: "act";
204
+ readonly aliases: readonly [];
205
+ }, {
206
+ readonly name: "Romans";
207
+ readonly code: "rom";
208
+ readonly aliases: readonly ["Rom"];
209
+ }, {
210
+ readonly name: "1 Corinthians";
211
+ readonly code: "1co";
212
+ readonly aliases: readonly ["1 Cor"];
213
+ }, {
214
+ readonly name: "2 Corinthians";
215
+ readonly code: "2co";
216
+ readonly aliases: readonly ["2 Cor"];
217
+ }, {
218
+ readonly name: "Galatians";
219
+ readonly code: "gal";
220
+ readonly aliases: readonly ["Gal"];
221
+ }, {
222
+ readonly name: "Ephesians";
223
+ readonly code: "eph";
224
+ readonly aliases: readonly ["Eph"];
225
+ }, {
226
+ readonly name: "Philippians";
227
+ readonly code: "phi";
228
+ readonly aliases: readonly ["Phil"];
229
+ }, {
230
+ readonly name: "Colossians";
231
+ readonly code: "col";
232
+ readonly aliases: readonly [];
233
+ }, {
234
+ readonly name: "1 Thessalonians";
235
+ readonly code: "1th";
236
+ readonly aliases: readonly ["1 Thess"];
237
+ }, {
238
+ readonly name: "2 Thessalonians";
239
+ readonly code: "2th";
240
+ readonly aliases: readonly ["2 Thess"];
241
+ }, {
242
+ readonly name: "1 Timothy";
243
+ readonly code: "1ti";
244
+ readonly aliases: readonly ["1 Tim"];
245
+ }, {
246
+ readonly name: "2 Timothy";
247
+ readonly code: "2ti";
248
+ readonly aliases: readonly ["2 Tim"];
249
+ }, {
250
+ readonly name: "Titus";
251
+ readonly code: "tit";
252
+ readonly aliases: readonly [];
253
+ }, {
254
+ readonly name: "Philemon";
255
+ readonly code: "phm";
256
+ readonly aliases: readonly ["Philem"];
257
+ }, {
258
+ readonly name: "Hebrews";
259
+ readonly code: "heb";
260
+ readonly aliases: readonly [];
261
+ }, {
262
+ readonly name: "James";
263
+ readonly code: "jam";
264
+ readonly aliases: readonly ["Jas"];
265
+ }, {
266
+ readonly name: "1 Peter";
267
+ readonly code: "1pe";
268
+ readonly aliases: readonly ["1 Pet"];
269
+ }, {
270
+ readonly name: "2 Peter";
271
+ readonly code: "2pe";
272
+ readonly aliases: readonly ["2 Pet"];
273
+ }, {
274
+ readonly name: "1 John";
275
+ readonly code: "1jo";
276
+ readonly aliases: readonly ["1 Jn"];
277
+ }, {
278
+ readonly name: "2 John";
279
+ readonly code: "2jo";
280
+ readonly aliases: readonly ["2 Jn"];
281
+ }, {
282
+ readonly name: "3 John";
283
+ readonly code: "3jo";
284
+ readonly aliases: readonly ["3 Jn"];
285
+ }, {
286
+ readonly name: "Jude";
287
+ readonly code: "jud";
288
+ readonly aliases: readonly [];
289
+ }, {
290
+ readonly name: "Revelation";
291
+ readonly code: "rev";
292
+ readonly aliases: readonly ["Apocalypse"];
293
+ }];
294
+ export type ScriptureBook = (typeof scriptureBooks)[number];
295
+ export type ScriptureBookCode = ScriptureBook['code'];
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scriptureBooks = void 0;
4
+ // Canonical names and codes from https://api.ambry.church/openapi.json.
5
+ exports.scriptureBooks = [
6
+ { name: 'Genesis', code: 'gen', aliases: ['Gen'] },
7
+ { name: 'Exodus', code: 'exo', aliases: ['Ex', 'Exod'] },
8
+ { name: 'Leviticus', code: 'lev', aliases: ['Lev'] },
9
+ { name: 'Numbers', code: 'num', aliases: ['Num'] },
10
+ { name: 'Deuteronomy', code: 'deu', aliases: ['Deut'] },
11
+ { name: 'Joshua', code: 'jos', aliases: ['Josh'] },
12
+ { name: 'Judges', code: 'jdg', aliases: ['Judg'] },
13
+ { name: 'Ruth', code: 'rut', aliases: ['Ruth'] },
14
+ { name: '1 Samuel', code: '1sa', aliases: ['1 Sam'] },
15
+ { name: '2 Samuel', code: '2sa', aliases: ['2 Sam'] },
16
+ { name: '1 Kings', code: '1ki', aliases: ['1 Kgs'] },
17
+ { name: '2 Kings', code: '2ki', aliases: ['2 Kgs'] },
18
+ { name: '1 Chronicles', code: '1ch', aliases: ['1 Chron'] },
19
+ { name: '2 Chronicles', code: '2ch', aliases: ['2 Chron'] },
20
+ { name: 'Ezra', code: 'ezr', aliases: [] },
21
+ { name: 'Nehemiah', code: 'neh', aliases: ['Neh'] },
22
+ { name: 'Tobit', code: 'tob', aliases: ['Tob'] },
23
+ { name: 'Judith', code: 'jth', aliases: ['Jdt'] },
24
+ { name: 'Esther', code: 'est', aliases: [] },
25
+ { name: 'Job', code: 'job', aliases: [] },
26
+ { name: 'Psalms', code: 'psa', aliases: ['Psalm', 'Ps'] },
27
+ { name: 'Proverbs', code: 'pro', aliases: ['Prov'] },
28
+ { name: 'Ecclesiastes', code: 'ecc', aliases: ['Eccl'] },
29
+ {
30
+ name: 'Song of Songs',
31
+ code: 'son',
32
+ aliases: ['Song of Solomon', 'Canticles'],
33
+ },
34
+ { name: 'Wisdom', code: 'wis', aliases: [] },
35
+ { name: 'Sirach', code: 'sir', aliases: ['Ecclesiasticus'] },
36
+ { name: 'Isaiah', code: 'isa', aliases: ['Isa'] },
37
+ { name: 'Jeremiah', code: 'jer', aliases: ['Jer'] },
38
+ { name: 'Lamentations', code: 'lam', aliases: [] },
39
+ { name: 'Baruch', code: 'bar', aliases: [] },
40
+ { name: 'Ezekiel', code: 'eze', aliases: ['Ezek'] },
41
+ { name: 'Daniel', code: 'dan', aliases: ['Dan'] },
42
+ { name: 'Hosea', code: 'hos', aliases: [] },
43
+ { name: 'Joel', code: 'joe', aliases: [] },
44
+ { name: 'Amos', code: 'amo', aliases: [] },
45
+ { name: 'Obadiah', code: 'oba', aliases: [] },
46
+ { name: 'Jonah', code: 'jon', aliases: [] },
47
+ { name: 'Micah', code: 'mic', aliases: [] },
48
+ { name: 'Nahum', code: 'nah', aliases: [] },
49
+ { name: 'Habakkuk', code: 'hab', aliases: [] },
50
+ { name: 'Zephaniah', code: 'zep', aliases: [] },
51
+ { name: 'Haggai', code: 'hag', aliases: [] },
52
+ { name: 'Zechariah', code: 'zec', aliases: [] },
53
+ { name: 'Malachi', code: 'mal', aliases: [] },
54
+ { name: '1 Maccabees', code: '1ma', aliases: [] },
55
+ { name: '2 Maccabees', code: '2ma', aliases: [] },
56
+ { name: 'Matthew', code: 'mat', aliases: ['Mt', 'Matt'] },
57
+ { name: 'Mark', code: 'mar', aliases: ['Mk'] },
58
+ { name: 'Luke', code: 'luk', aliases: ['Lk'] },
59
+ { name: 'John', code: 'joh', aliases: ['Jn'] },
60
+ { name: 'Acts', code: 'act', aliases: [] },
61
+ { name: 'Romans', code: 'rom', aliases: ['Rom'] },
62
+ { name: '1 Corinthians', code: '1co', aliases: ['1 Cor'] },
63
+ { name: '2 Corinthians', code: '2co', aliases: ['2 Cor'] },
64
+ { name: 'Galatians', code: 'gal', aliases: ['Gal'] },
65
+ { name: 'Ephesians', code: 'eph', aliases: ['Eph'] },
66
+ { name: 'Philippians', code: 'phi', aliases: ['Phil'] },
67
+ { name: 'Colossians', code: 'col', aliases: [] },
68
+ { name: '1 Thessalonians', code: '1th', aliases: ['1 Thess'] },
69
+ { name: '2 Thessalonians', code: '2th', aliases: ['2 Thess'] },
70
+ { name: '1 Timothy', code: '1ti', aliases: ['1 Tim'] },
71
+ { name: '2 Timothy', code: '2ti', aliases: ['2 Tim'] },
72
+ { name: 'Titus', code: 'tit', aliases: [] },
73
+ { name: 'Philemon', code: 'phm', aliases: ['Philem'] },
74
+ { name: 'Hebrews', code: 'heb', aliases: [] },
75
+ { name: 'James', code: 'jam', aliases: ['Jas'] },
76
+ { name: '1 Peter', code: '1pe', aliases: ['1 Pet'] },
77
+ { name: '2 Peter', code: '2pe', aliases: ['2 Pet'] },
78
+ { name: '1 John', code: '1jo', aliases: ['1 Jn'] },
79
+ { name: '2 John', code: '2jo', aliases: ['2 Jn'] },
80
+ { name: '3 John', code: '3jo', aliases: ['3 Jn'] },
81
+ { name: 'Jude', code: 'jud', aliases: [] },
82
+ { name: 'Revelation', code: 'rev', aliases: ['Apocalypse'] },
83
+ ];
@@ -0,0 +1,18 @@
1
+ import { type ScriptureBookCode } from './books.js';
2
+ import type { ScriptureReference } from './references.js';
3
+ export type AmbryScriptureConfig = {
4
+ apiKey: string;
5
+ baseUrl?: string;
6
+ };
7
+ export type ScriptureVerse = {
8
+ book_code: ScriptureBookCode;
9
+ chapter: number;
10
+ verse: number;
11
+ english_text?: string | null;
12
+ greek_text?: string | null;
13
+ latin_text?: string | null;
14
+ };
15
+ export declare function fetchScripture(reference: ScriptureReference, config: AmbryScriptureConfig, fetcher?: typeof fetch): Promise<{
16
+ verses: ScriptureVerse[];
17
+ total: number;
18
+ }>;
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchScripture = fetchScripture;
4
+ const books_js_1 = require("./books.js");
5
+ function validateReference(reference) {
6
+ if (!books_js_1.scriptureBooks.some((book) => book.code === reference.bookCode) ||
7
+ !Number.isInteger(reference.chapter) ||
8
+ reference.chapter < 1 ||
9
+ reference.chapter > 150) {
10
+ throw new Error('Enter a valid scripture book code and chapter.');
11
+ }
12
+ if (reference.firstVerse == null && reference.lastVerse == null)
13
+ return;
14
+ if (!Number.isInteger(reference.firstVerse) ||
15
+ !Number.isInteger(reference.lastVerse) ||
16
+ reference.firstVerse < 1 ||
17
+ reference.lastVerse > 176 ||
18
+ reference.lastVerse < reference.firstVerse) {
19
+ throw new Error('Enter a valid scripture verse or range.');
20
+ }
21
+ }
22
+ async function fetchScripture(reference, config, fetcher = fetch) {
23
+ validateReference(reference);
24
+ if (typeof config.apiKey !== 'string' || !config.apiKey.trim())
25
+ throw new Error('An Ambry API key is required.');
26
+ const baseUrl = new URL(config.baseUrl ?? 'https://api.ambry.church');
27
+ if (!['https:', 'http:'].includes(baseUrl.protocol) || baseUrl.username || baseUrl.password) {
28
+ throw new Error('Use an HTTP(S) Ambry base URL without embedded credentials.');
29
+ }
30
+ const verses = [];
31
+ let total = 0;
32
+ for (let page = 1; page <= 2; page++) {
33
+ const url = new URL('api/v1/bible-verses', baseUrl.href.replace(/\/?$/, '/'));
34
+ url.searchParams.set('book_code', reference.bookCode);
35
+ url.searchParams.set('chapter', String(reference.chapter));
36
+ if (reference.firstVerse === reference.lastVerse && reference.firstVerse != null)
37
+ url.searchParams.set('verse', String(reference.firstVerse));
38
+ url.searchParams.set('per_page', '100');
39
+ url.searchParams.set('page', String(page));
40
+ const response = await fetcher(url, {
41
+ headers: {
42
+ Authorization: `Bearer ${config.apiKey}`,
43
+ Accept: 'application/json',
44
+ },
45
+ signal: AbortSignal.timeout(15000),
46
+ redirect: 'error',
47
+ });
48
+ if (!response.ok)
49
+ throw new Error(`Ambry scripture request failed (HTTP ${response.status}).`);
50
+ const data = await response.json();
51
+ const result = data;
52
+ if (!result ||
53
+ !result.pagination ||
54
+ result.pagination.page !== page ||
55
+ result.pagination.per_page !== 100 ||
56
+ !Number.isInteger(result.pagination.total) ||
57
+ result.pagination.total < 0 ||
58
+ result.pagination.total > 176 ||
59
+ !Number.isInteger(result.pagination.total_pages) ||
60
+ result.pagination.total_pages < 0 ||
61
+ result.pagination.total_pages > 2 ||
62
+ (result.data != null && !Array.isArray(result.data))) {
63
+ throw new Error('Ambry returned an invalid scripture response.');
64
+ }
65
+ total = result.pagination.total;
66
+ for (const verse of result.data ?? []) {
67
+ if (!verse ||
68
+ verse.book_code !== reference.bookCode ||
69
+ verse.chapter !== reference.chapter ||
70
+ !Number.isInteger(verse.verse) ||
71
+ verse.verse < 1 ||
72
+ verse.verse > 176 ||
73
+ ['english_text', 'greek_text', 'latin_text'].some((field) => verse[field] != null && typeof verse[field] !== 'string') ||
74
+ (reference.firstVerse === reference.lastVerse &&
75
+ reference.firstVerse != null &&
76
+ verse.verse !== reference.firstVerse)) {
77
+ throw new Error('Ambry returned an invalid scripture verse.');
78
+ }
79
+ if (verses.some((saved) => saved.verse === verse.verse))
80
+ throw new Error('Ambry returned duplicate scripture verses.');
81
+ verses.push(verse);
82
+ }
83
+ if (page >= result.pagination.total_pages)
84
+ break;
85
+ }
86
+ if (verses.length !== total)
87
+ throw new Error('Ambry returned an incomplete scripture passage.');
88
+ const selected = verses
89
+ .sort((a, b) => a.verse - b.verse)
90
+ .filter((verse) => reference.firstVerse == null ||
91
+ (verse.verse >= reference.firstVerse && verse.verse <= reference.lastVerse));
92
+ if (reference.firstVerse != null &&
93
+ selected.length &&
94
+ selected.length !== reference.lastVerse - reference.firstVerse + 1) {
95
+ throw new Error('Ambry did not return every verse in the requested range.');
96
+ }
97
+ return { verses: selected, total: selected.length };
98
+ }
@@ -0,0 +1,2 @@
1
+ export { scriptureBooks, type ScriptureBook, type ScriptureBookCode } from './books.js';
2
+ export { parseScriptureReference, findScriptureReferences, scriptureLanguages, type ScriptureLanguage, type ScriptureReference, type ParseScriptureOptions, type FindScriptureOptions, } from './references.js';