@joewalker/scripture-ref 0.4.0 → 0.5.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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # 0.5.0 (2026-05-11)
2
+
3
+ * Added a `Translation` class that loads a translation's text on demand via `Translation.create('ESV')` and exposes `getPassageText` and `search` as methods, paving the way for additional translations. The top-level `getPassageText` and `search` functions are retained as thin wrappers around an eagerly-loaded ESV translation and are now marked deprecated. The `BookName` and `VerseText` types are now exported from the public API, and the unused `Book` type has been removed.
4
+ * Expanded reference parsing to recognise a normalised registry of alternate book spellings and abbreviations (for example `Gen`, `1Cor`, `Song of Sol`), with case-insensitive and punctuation-insensitive lookup. Parsing now reports an explicit error when a name resolves to more than one book instead of silently picking the first match.
5
+ * Added a Damerau-Levenshtein fuzzy fallback when an exact book name lookup fails, so common typos such as `Genisis`, `Habakuk`, and `Revelaiton` now resolve to the intended book. The allowed edit distance scales with name length, and very short inputs stay unresolved to avoid colliding with the abbreviation table.
6
+ * Reduced the size of the published package by deriving verse ordinal lookup tables from the per-chapter verse counts at load time instead of shipping a pre-built prefix-sum table.
7
+
1
8
  # 0.4.0 (2026-05-10)
2
9
 
3
10
  * The package is now published to npm as `@joewalker/scripture-ref`, alongside the existing tagged releases on GitLab.
package/README.md CHANGED
@@ -44,7 +44,7 @@ src/
44
44
  __testutil__/index.ts test-only re-exports (parser internals)
45
45
  book/
46
46
  books.ts getPassageText, search
47
- esv.json ESV text (about 10MB)
47
+ data/*.json Bible translations (about 10MB per file)
48
48
  passage/
49
49
  bible-info.ts structural helpers
50
50
  verse.ts Verse and parser
@@ -1,15 +1,26 @@
1
1
  import type { Passage } from '../passage/passage.js';
2
2
  import type { Verse } from '../passage/verse.js';
3
- type BookName = string;
4
- type BibleText = Record<BookName, Chapters>;
5
- type Chapters = ReadonlyArray<Verses>;
6
- type Verses = ReadonlyArray<Words>;
7
- type Words = ReadonlyArray<Word>;
8
- type Word = [string, string] | [string];
9
3
  /**
10
- *
4
+ * VerseText returns the text for a verse in calls to `getPassageText(…)`
11
5
  */
12
- export interface Book {
6
+ export interface VerseText {
7
+ readonly verse: Verse;
8
+ readonly text: string;
9
+ }
10
+ /**
11
+ * Words might have Strong's numbers associated with them
12
+ */
13
+ type WordData = [translation: string] | [translation: string, strongsNumber: string];
14
+ /** A verse is an array of words */
15
+ type VerseData = ReadonlyArray<WordData>;
16
+ /** A chapter is an array of verses */
17
+ type ChapterData = ReadonlyArray<VerseData>;
18
+ /** A chapter is an array of chapters */
19
+ type BookData = ReadonlyArray<ChapterData>;
20
+ /**
21
+ * A book is metadata plus a record of books to arrays of chapterdata
22
+ */
23
+ interface TranslationData {
13
24
  readonly version: string;
14
25
  readonly versionName: string;
15
26
  readonly meta: {
@@ -22,24 +33,36 @@ export interface Book {
22
33
  readonly license: string | null;
23
34
  readonly copyright: string;
24
35
  };
36
+ readonly books: Record<string, BookData>;
37
+ }
38
+ declare const BOOK_NAME: Readonly<{
39
+ ESV: "./data/ESV.json";
40
+ }>;
41
+ export type BookName = keyof typeof BOOK_NAME;
42
+ /**
43
+ * The standard interface to the text of a Translation
44
+ */
45
+ export declare class Translation {
46
+ #private;
47
+ static create(bookName: BookName): Promise<Translation>;
48
+ constructor(translationData: TranslationData);
25
49
  /**
26
- * Record<BookName, Chapters>
27
- * Chapters = ReadonlyArray<Verses>
28
- * Verses = ReadonlyArray<Words>
29
- * Words = [EnglishWords, StrongsNumber];
50
+ * Look up the Bible text for a given passage
30
51
  */
31
- readonly books: BibleText;
32
- }
33
- export interface VerseText {
34
- readonly verse: Verse;
35
- readonly text: string;
52
+ getPassageText(passage: Passage): ReadonlyArray<VerseText>;
53
+ /**
54
+ * Produce the output text needed for a search term
55
+ */
56
+ search(terms: string): string;
36
57
  }
37
58
  /**
38
59
  * Look up the Bible text for a given passage
60
+ * @deprecated Use `Translation.create(bookId).getPassageText(passage)`
39
61
  */
40
62
  export declare function getPassageText(bookId: string, passage: Passage): ReadonlyArray<VerseText>;
41
63
  /**
42
64
  * Produce the output text needed for a search term
65
+ * @deprecated Use `Translation.create(bookId).search(terms)`
43
66
  */
44
67
  export declare function search(bookId: string, terms: string): string;
45
68
  export {};
@@ -1,62 +1,8 @@
1
1
  import { chaptersInBook, verseOrdinal, versesInChapter, } from '../passage/bible-info.js';
2
2
  import { PassageTally } from '../passage/passage-tally.js';
3
- // https://github.com/jestjs/jest/issues/15000#issuecomment-2470602098
4
- const esv = (await import(/* webpackChunkName: "mobile/esv" */ './esv.json', {
5
- with: { type: 'json' },
6
- })).default;
7
- // It's not clear why we need to cast to unknown before Book, I suspect some
8
- // data inconsistency
9
- const esvBibleText = esv.books;
10
- /**
11
- * Look up the Bible text for a given passage
12
- */
13
- export function getPassageText(bookId, passage) {
14
- if (bookId !== 'ESV') {
15
- throw new Error(`Unknown book: '${bookId}'.`);
16
- }
17
- const output = [];
18
- for (const range of passage.verseRanges()) {
19
- for (const verse of range.verses()) {
20
- output.push({ verse, text: renderVerseText(lookupWords(verse)) });
21
- }
22
- }
23
- return output;
24
- }
25
- function lookupWords(verse) {
26
- const bookName = indexLookup[verse.book];
27
- return esvBibleText[bookName][verse.chapter - 1][verse.verse - 1];
28
- }
29
- /**
30
- * Join the per-word entries for a single verse into a display string,
31
- * keeping attaching punctuation (.,;!?:) flush with the preceding word.
32
- */
33
- function renderVerseText(words) {
34
- let text = '';
35
- for (const word of words) {
36
- const head = word[0];
37
- if (text.length === 0 || isAttachingPunctuation(head)) {
38
- text += head;
39
- }
40
- else {
41
- text += ` ${head}`;
42
- }
43
- }
44
- return text;
45
- }
46
- /**
47
- * True for punctuation that should attach to the previous word with no
48
- * intervening space (sentence/clause punctuation).
49
- */
50
- function isAttachingPunctuation(word) {
51
- return (word.startsWith('.') ||
52
- word.startsWith(',') ||
53
- word.startsWith(';') ||
54
- word.startsWith('!') ||
55
- word.startsWith('?') ||
56
- word.startsWith(':'));
57
- }
58
- const indexLookup = [
59
- '',
3
+ /** A lookup table for book names used in the TranslationData.books record */
4
+ const BOOK_NAME_LOOKUP = [
5
+ '#ERROR#',
60
6
  'Genesis',
61
7
  'Exodus',
62
8
  'Leviticus',
@@ -124,73 +70,161 @@ const indexLookup = [
124
70
  'Jude',
125
71
  'Revelation of John',
126
72
  ];
73
+ const BOOK_NAME = Object.freeze({
74
+ ESV: './data/ESV.json',
75
+ });
127
76
  /**
128
- * Escape a string so it can be safely embedded in a regular expression.
77
+ * The standard interface to the text of a Translation
129
78
  */
130
- function escapeRegExp(value) {
131
- return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
79
+ export class Translation {
80
+ #translationData;
81
+ static async create(bookName) {
82
+ const translationData = (await import(BOOK_NAME[bookName], {
83
+ with: { type: 'json' },
84
+ }));
85
+ return new Translation(translationData.default);
86
+ }
87
+ constructor(translationData) {
88
+ this.#translationData = translationData;
89
+ }
90
+ /**
91
+ * Look up the Bible text for a given passage
92
+ */
93
+ getPassageText(passage) {
94
+ const output = [];
95
+ for (const verse of passage.verses()) {
96
+ output.push({ verse, text: renderVerseText(this.#lookupWords(verse)) });
97
+ }
98
+ return output;
99
+ }
100
+ /**
101
+ * Produce the output text needed for a search term
102
+ */
103
+ search(terms) {
104
+ const searchWords = terms
105
+ .split(' ')
106
+ .filter(word => word.trim().length > 0)
107
+ .map(word => {
108
+ const pattern = `\\b(${escapeRegExp(word)})\\b`;
109
+ return {
110
+ // The `g` flag mutates `lastIndex` across `.test()` calls and would
111
+ // make matches on later words start past the beginning of the string.
112
+ test: new RegExp(pattern, 'i'),
113
+ highlight: new RegExp(pattern, 'gi'),
114
+ };
115
+ });
116
+ if (searchWords.length === 0) {
117
+ return '\n\n';
118
+ }
119
+ const tally = new PassageTally();
120
+ const booksData = this.#translationData.books;
121
+ for (let book = 1; book < BOOK_NAME_LOOKUP.length; book++) {
122
+ const bookName = BOOK_NAME_LOOKUP[book];
123
+ const bookData = booksData[bookName];
124
+ const maxChapter = chaptersInBook(book);
125
+ for (let chapter = 1; chapter <= maxChapter; chapter++) {
126
+ const chapterData = bookData[chapter - 1];
127
+ const maxVerse = versesInChapter(book, chapter);
128
+ for (let verse = 1; verse <= maxVerse; verse++) {
129
+ const ordinal = verseOrdinal(book, chapter, verse);
130
+ const words = chapterData[verse - 1];
131
+ for (const searchWord of searchWords) {
132
+ wordLoop: for (const word of words) {
133
+ if (searchWord.test.test(word[0])) {
134
+ tally.incrementByOrdinal(ordinal, 1);
135
+ // Verses with multiple matches for a search term should not
136
+ // get extra weighting
137
+ break wordLoop;
138
+ }
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+ return (
145
+ // eslint-disable-next-line prefer-template
146
+ '\n' +
147
+ tally
148
+ .results({ minScore: searchWords.length })
149
+ .map(verse => {
150
+ let text = renderVerseText(this.#lookupWords(verse));
151
+ for (const searchWord of searchWords) {
152
+ text = text.replace(searchWord.highlight, x => {
153
+ return `**${x}**`;
154
+ });
155
+ }
156
+ return `[${verse.toString()} → ${text}]`;
157
+ })
158
+ .join('\n') +
159
+ '\n');
160
+ }
161
+ /**
162
+ * Convert a verse reference to stored verse data.
163
+ */
164
+ #lookupWords(verse) {
165
+ const bookName = BOOK_NAME_LOOKUP[verse.book];
166
+ const booksData = this.#translationData.books;
167
+ return booksData[bookName][verse.chapter - 1][verse.verse - 1];
168
+ }
169
+ }
170
+ // https://github.com/jestjs/jest/issues/15000#issuecomment-2470602098
171
+ const esvTranslationData = (await import(/* webpackChunkName: "mobile/esv" */ './data/ESV.json', {
172
+ with: { type: 'json' },
173
+ })).default;
174
+ const esvTranslation = new Translation(esvTranslationData);
175
+ /**
176
+ * Look up the Bible text for a given passage
177
+ * @deprecated Use `Translation.create(bookId).getPassageText(passage)`
178
+ */
179
+ export function getPassageText(bookId, passage) {
180
+ if (bookId !== 'ESV') {
181
+ throw new Error(`Unknown book: '${bookId}'.`);
182
+ }
183
+ return esvTranslation.getPassageText(passage);
132
184
  }
133
185
  /**
134
186
  * Produce the output text needed for a search term
187
+ * @deprecated Use `Translation.create(bookId).search(terms)`
135
188
  */
136
189
  export function search(bookId, terms) {
137
190
  if (bookId !== 'ESV') {
138
191
  throw new Error(`Unknown book: '${bookId}'.`);
139
192
  }
140
- const searchWords = terms
141
- .split(' ')
142
- .filter(word => word.trim().length > 0)
143
- .map(word => {
144
- const pattern = `\\b(${escapeRegExp(word)})\\b`;
145
- return {
146
- // The `g` flag mutates `lastIndex` across `.test()` calls and would
147
- // make matches on later words start past the beginning of the string.
148
- test: new RegExp(pattern, 'i'),
149
- highlight: new RegExp(pattern, 'gi'),
150
- };
151
- });
152
- if (searchWords.length === 0) {
153
- return '\n\n';
154
- }
155
- const tally = new PassageTally();
156
- for (let book = 1; book < indexLookup.length; book++) {
157
- const bookName = indexLookup[book];
158
- const bookData = esvBibleText[bookName];
159
- const maxChapter = chaptersInBook(book);
160
- for (let chapter = 1; chapter <= maxChapter; chapter++) {
161
- const chapterData = bookData[chapter - 1];
162
- const maxVerse = versesInChapter(book, chapter);
163
- for (let verse = 1; verse <= maxVerse; verse++) {
164
- const ordinal = verseOrdinal(book, chapter, verse);
165
- const words = chapterData[verse - 1];
166
- for (const searchWord of searchWords) {
167
- wordLoop: for (const word of words) {
168
- if (searchWord.test.test(word[0])) {
169
- tally.incrementByOrdinal(ordinal, 1);
170
- // Verses with multiple matches for a search term should not
171
- // get extra weighting
172
- break wordLoop;
173
- }
174
- }
175
- }
176
- }
193
+ return esvTranslation.search(terms);
194
+ }
195
+ /**
196
+ * Join the per-word entries for a single verse into a display string,
197
+ * keeping attaching punctuation (.,;!?:) flush with the preceding word.
198
+ */
199
+ function renderVerseText(words) {
200
+ let text = '';
201
+ for (const word of words) {
202
+ const head = word[0];
203
+ if (text.length === 0 || isAttachingPunctuation(head)) {
204
+ text += head;
205
+ }
206
+ else {
207
+ text += ` ${head}`;
177
208
  }
178
209
  }
179
- return (
180
- // eslint-disable-next-line prefer-template
181
- '\n' +
182
- tally
183
- .results({ minScore: searchWords.length })
184
- .map(verse => {
185
- let text = renderVerseText(lookupWords(verse));
186
- for (const searchWord of searchWords) {
187
- text = text.replace(searchWord.highlight, x => {
188
- return `**${x}**`;
189
- });
190
- }
191
- return `[${verse.toString()} → ${text}]`;
192
- })
193
- .join('\n') +
194
- '\n');
210
+ return text;
211
+ }
212
+ /**
213
+ * True for punctuation that should attach to the previous word with no
214
+ * intervening space (sentence/clause punctuation).
215
+ */
216
+ function isAttachingPunctuation(word) {
217
+ return (word.startsWith('.') ||
218
+ word.startsWith(',') ||
219
+ word.startsWith(';') ||
220
+ word.startsWith('!') ||
221
+ word.startsWith('?') ||
222
+ word.startsWith(':'));
223
+ }
224
+ /**
225
+ * Escape a string so it can be safely embedded in a regular expression.
226
+ */
227
+ function escapeRegExp(value) {
228
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
195
229
  }
196
230
  //# sourceMappingURL=books.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"books.js","sourceRoot":"","sources":["../../src/book/books.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"books.js","sourceRoot":"","sources":["../../src/book/books.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -3,6 +3,6 @@ export type { Accuracy, VerseParseResult } from './passage/verse.js';
3
3
  export { VerseRange } from './passage/verse-range.js';
4
4
  export { Passage } from './passage/passage.js';
5
5
  export { PassageTally } from './passage/passage-tally.js';
6
- export { getPassageText, search } from './book/books.js';
7
- export type { Book } from './book/books.js';
6
+ export { getPassageText, search, Translation } from './book/books.js';
7
+ export type { BookName, VerseText } from './book/books.js';
8
8
  export { findBookNumber, getShortName, getLongName, chaptersInBook, versesInChapter, versesInBible, chaptersInBible, booksInBible, verseOrdinal, decodeOrdinal, bookNames, shortBookNames, } from './passage/bible-info.js';
package/dist/index.js CHANGED
@@ -2,6 +2,6 @@ export { Verse, ACCURACY } from './passage/verse.js';
2
2
  export { VerseRange } from './passage/verse-range.js';
3
3
  export { Passage } from './passage/passage.js';
4
4
  export { PassageTally } from './passage/passage-tally.js';
5
- export { getPassageText, search } from './book/books.js';
5
+ export { getPassageText, search, Translation } from './book/books.js';
6
6
  export { findBookNumber, getShortName, getLongName, chaptersInBook, versesInChapter, versesInBible, chaptersInBible, booksInBible, verseOrdinal, decodeOrdinal, bookNames, shortBookNames, } from './passage/bible-info.js';
7
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAGzD,OAAO,EACL,cAAc,EACd,YAAY,EACZ,WAAW,EACX,cAAc,EACd,eAAe,EACf,aAAa,EACb,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,SAAS,EACT,cAAc,GACf,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGtE,OAAO,EACL,cAAc,EACd,YAAY,EACZ,WAAW,EACX,cAAc,EACd,eAAe,EACf,aAAa,EACb,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,SAAS,EACT,cAAc,GACf,MAAM,yBAAyB,CAAC"}
@@ -1,3 +1,20 @@
1
+ export type BookLookupResult = {
2
+ readonly type: 'found';
3
+ readonly book: number;
4
+ } | {
5
+ readonly type: 'unknown';
6
+ } | {
7
+ readonly type: 'ambiguous';
8
+ readonly books: string;
9
+ };
10
+ /**
11
+ * Build the normalized book alias lookup table.
12
+ *
13
+ * Throws if the same alias is assigned to two different books, or if an alias
14
+ * is also listed in `ambiguousAliases` (the ambiguous branch in
15
+ * `_lookupBookNumber` would otherwise silently shadow the concrete entry).
16
+ */
17
+ export declare function _buildBookLookup(aliasesByBook: ReadonlyArray<readonly [number, string]>, ambiguousAliases: ReadonlyMap<string, string>): ReadonlyMap<string, number>;
1
18
  /**
2
19
  * Find the (1-based) book number for a given string.
3
20
  * e.g.:
@@ -6,6 +23,10 @@
6
23
  * - findBookNumber('g') === undefined
7
24
  */
8
25
  export declare function findBookNumber(name: string): number | undefined;
26
+ /**
27
+ * Look up a normalized book name and report ambiguous matches separately.
28
+ */
29
+ export declare function _lookupBookNumber(name: string): BookLookupResult;
9
30
  /**
10
31
  * What is the canonical short name for a book?
11
32
  */