UNPKG

@allemandi/bible-validate

Version:

Fast, type-safe utilities for parsing, validating, and normalizing Bible references.

2,348 lines (2,327 loc) 37.4 kB
/** * Normalizes a Bible book name or alias by trimming spaces, removing common prefixes, * converting ordinal prefixes to digits, and stripping non-alphanumeric characters. * @public * @param {string} name - The raw book name or alias to normalize, possibly with prefixes, punctuation, and mixed case. * @returns {string|null} - A cleaned lowercase alphanumeric string with numeric prefixes for ordinals, or null if input is null or undefined. * @example * // Converts ordinal prefix to digit and removes punctuation * normalizeBookName('1st John'); // '1john' * // Removes unified prefixes and lowercases the name * normalizeBookName('The Epistle to the Romans'); // 'romans' * // Strips non-alphanumeric characters and trims spaces * normalizeBookName(' Book of *EX* '); // 'ex' * // Returns null if input is null or undefined * normalizeBookName(null); // null */ function normalizeBookName(name) { if (!name) return null; /** @type {Record<string, string>} */ const prefixMap = { '1st': '1', first: '1', '2nd': '2', second: '2', '3rd': '3', third: '3', iii: '3', ii: '2', i: '1', }; let cleaned = name.trim().toLowerCase().replace(/\s+/g, ' '); cleaned = cleaned.replace( /^(?:the\s+)?(?:(book|epistle|gospel|letter)\s+(?:according\s+)?(?:to|for|of)(?:\s+the)?\s*)?/, '' ); const prefixMatch = cleaned.match(/^(first|1st|second|2nd|third|3rd|iii|ii|i)\b/); if (prefixMatch) { const key = prefixMatch[0]; if (key in prefixMap) { const prefix = prefixMap[key]; cleaned = prefix + ' ' + cleaned.slice(key.length).trim(); } } return cleaned.replace(/[^a-z0-9]/g, ''); } /** * Parses a string representing a chapter and optional verse or verse range * into an object with numeric values. * @public * @param {string} str - The string containing chapter and verse references, which may include words, punctuation, and ranges. * @returns {{chapter: number, verseStart: number|null, verseEnd: number|null}|null} - An object with the chapter number and optional verse start and end numbers, or null if no valid numbers are found. * @example * // Parses chapter only reference * parseChapterVerse('12'); // { chapter: 12, verseStart: null, verseEnd: null } * // Parses chapter and single verse reference * parseChapterVerse('5:3'); // { chapter: 5, verseStart: 3, verseEnd: null } * // Parses chapter with verse range including words and punctuation * parseChapterVerse('Chapter 13 Verses 4–7'); // { chapter: 13, verseStart: 4, verseEnd: 7 } * // Parses chapter with verse range using "to" as a separator * parseChapterVerse('chap. 13, v3 to 8'); // { chapter: 13, verseStart: 3, verseEnd: 8 } * // Returns null if no numeric chapter is present * parseChapterVerse('nonsense'); // null * // Parses chapter and verse range with extra whitespace and punctuation * parseChapterVerse(' 10 : 2 - 6 '); // { chapter: 10, verseStart: 2, verseEnd: 6 } * // Handles pure whitespace and number combination * parseChapterVerse(' 11 1 2 '); // { chapter: 11, verseStart: 1, verseEnd: 2 } */ function parseChapterVerse(str) { if (!str) return null; const cleaned = str .toLowerCase() .replace(/[–—]/g, '-') .replace(/\bto\b/g, '-') .replace(/[a-z.,]/g, '') .replace(/\s+/g, ' ') .trim(); const tokens = cleaned.split(/[\s\-:]+/).filter(Boolean); const nums = tokens.map((t) => parseInt(t, 10)).filter((n) => !isNaN(n)); if (nums.length === 0) return null; switch (nums.length) { case 1: return { chapter: nums[0], verseStart: null, verseEnd: null }; case 2: return { chapter: nums[0], verseStart: nums[1], verseEnd: null }; default: return { chapter: nums[0], verseStart: nums[1], verseEnd: nums[2] }; } } /** * Splits a Bible reference string into the book name and chapter/verse range parts, * trimming empty space. No further normalization. * @public * @param {string} ref - The Bible reference string containing a book name optionally followed by a chapter/verse range. * @returns {[string|null, string|null]} - A tuple where the first element is the extracted book name and the second is the range; returns [null, null] if input is invalid or empty. * @example * // Extracts book and range from a standard reference * extractBookAndRange('1st John 3:16'); // ['1st John', '3:16'] * // Extracts book name with punctuation and range * extractBookAndRange("The Revelation 4:5"); // ["The Revelation", '4:5'] * // Returns book name with empty range when no range is given * extractBookAndRange('Genesis'); // ['Genesis', ''] * // Returns [null, null] for empty or invalid input * extractBookAndRange(''); // [null, null] * // Handles leading spaces and complex ranges with simple chapter abbreviations * extractBookAndRange(' Exodus 12. 1 to 3'); // ['Exodus', '12. 1 to 3'] * extractBookAndRange('Exodus Chapter 12:1-3'); // ['Exodus', 'Chapter 12:1-3'] * extractBookAndRange(' Exodus Ch. 12. 1 to 3'); // ['Exodus', 'Ch. 12. 1 to 3'] * extractBookAndRange('second Kings Chape 1 to 3'); // ['second Kings Chape', '1 to 3'] */ function extractBookAndRange(ref) { if (!ref || typeof ref !== 'string') return [null, null]; const cleaned = ref.trim().replace(/\s+/g, ' '); const pattern = /^([\d\w\s.']+?)\s*(?=\b(ch(?:apter)?|chap\.?)\b|\d)/i; const match = cleaned.match(pattern); if (match) { const book = match[1].trim(); const range = cleaned.slice(match[0].length).trim(); return [book, range]; } return [cleaned, '']; } /** * @import { ParsedReference } from './types.js' */ /** * Parses a Bible reference string into its book, chapter, and verse components, supporting various formats and spacing. * @public * @param { string } ref - The Bible reference string to parse, which may include ordinal prefixes, varying case, punctuation, and verse ranges. * @returns {ParsedReference|null} - An object with normalized book name, chapter, verseStart, and verseEnd fields, or null if the input is not a string. * @example * // Parses ordinal prefix and returns structured reference * parseBibleReference('2nd Kings 4:2'); * // { book: '2kings', chapter: 4, verseStart: 2, verseEnd: null } * // Handles mixed casing, chapter/verse labels, and verse range * parseBibleReference(' Iii JohN Chap. 1 verses 9 to 11'); * // { book: '3john', chapter: 1, verseStart: 9, verseEnd: 11 } * // Returns null fields when chapter and verse are omitted * parseBibleReference('Genesis'); * // { book: 'genesis', chapter: null, verseStart: null, verseEnd: null } * // Cleans and parses input with excessive spacing * parseBibleReference(' 1st Samuel 17 : 4-9 '); * // { book: '1samuel', chapter: 17, verseStart: 4, verseEnd: 9 } * // Returns null for invalid or non-string input * parseBibleReference('!!!'); * // { book: null, chapter: null, verseStart: null, verseEnd: null } * parseBibleReference(42); // null */ function parseBibleReference(ref) { if (!ref || typeof ref !== 'string') return null; const cleanedRef = ref.trim().replace(/\s+/g, ' '); const [bookRaw, chapterVerseRaw] = extractBookAndRange(cleanedRef); const book = bookRaw && normalizeBookName(bookRaw); if (!book) { return { book: null, chapter: null, verseStart: null, verseEnd: null, }; } const chapVerse = chapterVerseRaw ? parseChapterVerse(chapterVerseRaw) : null; return { book, chapter: chapVerse?.chapter ?? null, verseStart: chapVerse?.verseStart ?? null, verseEnd: chapVerse?.verseEnd ?? null, }; }var bibleCounts = [ { book: "Genesis", aliases: [ "Gen", "Ge", "Gn" ], chapters: [ 31, 25, 24, 26, 32, 22, 24, 22, 29, 32, 32, 20, 18, 24, 21, 16, 27, 33, 38, 18, 34, 24, 20, 67, 34, 35, 46, 22, 35, 43, 55, 32, 20, 31, 29, 43, 36, 30, 23, 23, 57, 38, 34, 34, 28, 34, 31, 22, 33, 26 ] }, { book: "Exodus", aliases: [ "Ex", "Exod", "Exo" ], chapters: [ 22, 25, 22, 31, 23, 30, 25, 32, 35, 29, 10, 51, 22, 31, 27, 36, 16, 27, 25, 26, 36, 31, 33, 18, 40, 37, 21, 43, 46, 38, 18, 35, 23, 35, 35, 38, 29, 31, 43, 38 ] }, { book: "Leviticus", aliases: [ "Lev", "Le", "Lv" ], chapters: [ 17, 16, 17, 35, 19, 30, 38, 36, 24, 20, 47, 8, 59, 57, 33, 34, 16, 30, 37, 27, 24, 33, 44, 23, 55, 46, 34 ] }, { book: "Numbers", aliases: [ "Num", "Nu", "Nm", "Nb" ], chapters: [ 54, 34, 51, 49, 31, 27, 89, 26, 23, 36, 35, 16, 33, 45, 41, 50, 13, 32, 22, 29, 35, 41, 30, 25, 18, 65, 23, 31, 40, 16, 54, 42, 56, 29, 34, 13 ] }, { book: "Deuteronomy", aliases: [ "Deut", "De", "Dt" ], chapters: [ 46, 37, 29, 49, 33, 25, 26, 20, 29, 22, 32, 32, 18, 29, 23, 22, 20, 22, 21, 20, 23, 30, 25, 22, 19, 19, 26, 68, 29, 20, 30, 52, 29, 12 ] }, { book: "Joshua", aliases: [ "Josh", "Jos", "Jsh" ], chapters: [ 18, 24, 17, 24, 15, 27, 26, 35, 27, 43, 23, 24, 33, 15, 63, 10, 18, 28, 51, 9, 45, 34, 16, 33 ] }, { book: "Judges", aliases: [ "Judg", "Jdgs", "Jdg", "Jg" ], chapters: [ 36, 23, 31, 24, 31, 40, 25, 35, 57, 18, 40, 15, 25, 20, 20, 31, 13, 31, 30, 48, 25 ] }, { book: "Ruth", aliases: [ "Rth", "Ru" ], chapters: [ 22, 23, 18, 22 ] }, { book: "1 Samuel", aliases: [ "1 Sam", "1 Sm", "1 Sa", "1 S" ], chapters: [ 28, 36, 21, 22, 12, 21, 17, 22, 27, 27, 15, 25, 23, 52, 35, 23, 58, 30, 24, 42, 15, 23, 29, 22, 44, 25, 12, 25, 11, 31, 13 ] }, { book: "2 Samuel", aliases: [ "2 Sam", "2 Sm", "2 Sa", "2 S" ], chapters: [ 27, 32, 39, 12, 25, 23, 29, 18, 13, 19, 27, 31, 39, 33, 37, 23, 29, 33, 43, 26, 22, 51, 39, 25 ] }, { book: "1 Kings", aliases: [ "1 Kgs", "1 Kin", "1 Ki", "1 K" ], chapters: [ 53, 46, 28, 34, 18, 38, 51, 66, 28, 29, 43, 33, 34, 31, 34, 34, 24, 46, 21, 43, 29, 53 ] }, { book: "2 Kings", aliases: [ "2 Kgs", "2 Kin", "2 Ki", "2 K" ], chapters: [ 18, 25, 27, 44, 27, 33, 20, 29, 37, 36, 21, 21, 25, 29, 38, 20, 41, 37, 37, 21, 26, 20, 37, 20, 30 ] }, { book: "1 Chronicles", aliases: [ "1 Chron", "1 Chr", "1 Ch" ], chapters: [ 54, 55, 24, 43, 26, 81, 40, 40, 44, 14, 47, 40, 14, 17, 29, 43, 27, 17, 19, 8, 30, 19, 32, 31, 31, 32, 34, 21, 30 ] }, { book: "2 Chronicles", aliases: [ "2 Chron", "2 Chr", "2 Ch" ], chapters: [ 17, 18, 17, 22, 14, 42, 22, 18, 31, 19, 23, 16, 22, 15, 19, 14, 19, 34, 11, 37, 20, 12, 21, 27, 28, 23, 9, 27, 36, 27, 21, 33, 25, 33, 27, 23 ] }, { book: "Ezra", aliases: [ "Ezr", "Ez" ], chapters: [ 11, 70, 13, 24, 17, 22, 28, 36, 15, 44 ] }, { book: "Nehemiah", aliases: [ "Neh", "Ne" ], chapters: [ 11, 20, 32, 23, 19, 19, 73, 18, 38, 39, 36, 47, 31 ] }, { book: "Esther", aliases: [ "Est", "Esth", "Es" ], chapters: [ 22, 23, 15, 17, 14, 14, 10, 17, 32, 3 ] }, { book: "Job", aliases: [ "Jb" ], chapters: [ 22, 13, 26, 21, 27, 30, 21, 22, 35, 22, 20, 25, 28, 22, 35, 22, 16, 21, 29, 29, 34, 30, 17, 25, 6, 14, 23, 28, 25, 31, 40, 22, 33, 37, 16, 33, 24, 41, 30, 24, 34, 17 ] }, { book: "Psalms", aliases: [ "Ps", "Psalm", "Pslm", "Psa", "Psm", "Pss" ], chapters: [ 6, 12, 8, 8, 12, 10, 17, 9, 20, 18, 7, 8, 6, 7, 5, 11, 15, 50, 14, 9, 13, 31, 6, 10, 22, 12, 14, 9, 11, 12, 24, 11, 22, 22, 28, 12, 40, 22, 13, 17, 13, 11, 5, 26, 17, 11, 9, 14, 20, 23, 19, 9, 6, 7, 23, 13, 11, 11, 17, 12, 8, 12, 11, 10, 13, 20, 7, 35, 36, 5, 24, 20, 28, 23, 10, 12, 20, 72, 13, 19, 16, 8, 18, 12, 13, 17, 7, 18, 52, 17, 16, 15, 5, 23, 11, 13, 12, 9, 9, 5, 8, 28, 22, 35, 45, 48, 43, 13, 31, 7, 10, 10, 9, 8, 18, 19, 2, 29, 176, 7, 8, 9, 4, 8, 5, 6, 5, 6, 8, 8, 3, 18, 3, 3, 21, 26, 9, 8, 24, 13, 10, 7, 12, 15, 21, 10, 20, 14, 9, 6 ] }, { book: "Proverbs", aliases: [ "Prov", "Pro", "Prv", "Pr" ], chapters: [ 33, 22, 35, 27, 23, 35, 27, 36, 18, 32, 31, 28, 25, 35, 33, 33, 28, 24, 29, 30, 31, 29, 35, 34, 28, 28, 27, 28, 27, 33, 31 ] }, { book: "Ecclesiastes", aliases: [ "Eccles", "Eccle", "Ecc", "Ec", "Qoh" ], chapters: [ 18, 26, 22, 16, 20, 12, 29, 17, 18, 20, 10, 14 ] }, { book: "Song of Solomon", aliases: [ "Song", "Song of Songs", "Sos", "So", "Canticle of Canticles", "Canticles", "Cant" ], chapters: [ 17, 17, 11, 16, 16, 13, 13, 14 ] }, { book: "Isaiah", aliases: [ "Isa", "Is" ], chapters: [ 31, 22, 26, 6, 30, 13, 25, 22, 21, 34, 16, 6, 22, 32, 9, 14, 14, 7, 25, 6, 17, 25, 18, 23, 12, 21, 13, 29, 24, 33, 9, 20, 24, 17, 10, 22, 38, 22, 8, 31, 29, 25, 28, 28, 25, 13, 15, 22, 26, 11, 23, 15, 12, 17, 13, 12, 21, 14, 21, 22, 11, 12, 19, 12, 25, 24 ] }, { book: "Jeremiah", aliases: [ "Jer", "Je", "Jr" ], chapters: [ 19, 37, 25, 31, 31, 30, 34, 22, 26, 25, 23, 17, 27, 22, 21, 21, 27, 23, 15, 18, 14, 30, 40, 10, 38, 24, 22, 17, 32, 24, 40, 44, 26, 22, 19, 32, 21, 28, 18, 16, 18, 22, 13, 30, 5, 28, 7, 47, 39, 46, 64, 34 ] }, { book: "Lamentations", aliases: [ "Lam", "La" ], chapters: [ 22, 22, 66, 22, 22 ] }, { book: "Ezekiel", aliases: [ "Ezek", "Eze", "Ezk" ], chapters: [ 28, 10, 27, 17, 17, 14, 27, 18, 11, 22, 25, 28, 23, 23, 8, 63, 24, 32, 14, 49, 32, 31, 49, 27, 17, 21, 36, 26, 21, 26, 18, 32, 33, 31, 15, 38, 28, 23, 29, 49, 26, 20, 27, 31, 25, 24, 23, 35 ] }, { book: "Daniel", aliases: [ "Dan", "Da", "Dn" ], chapters: [ 21, 49, 30, 37, 31, 28, 28, 27, 27, 21, 45, 13 ] }, { book: "Hosea", aliases: [ "Hos", "Ho" ], chapters: [ 11, 23, 5, 19, 15, 11, 16, 14, 17, 15, 12, 14, 16, 9 ] }, { book: "Joel", aliases: [ "Jl" ], chapters: [ 20, 32, 21 ] }, { book: "Amos", aliases: [ "Am" ], chapters: [ 15, 16, 15, 13, 27, 14, 17, 14, 15 ] }, { book: "Obadiah", aliases: [ "Obad", "Ob" ], chapters: [ 21 ] }, { book: "Jonah", aliases: [ "Jnh", "Jon" ], chapters: [ 17, 10, 10, 11 ] }, { book: "Micah", aliases: [ "Mic", "Mc" ], chapters: [ 16, 13, 12, 13, 15, 16, 20 ] }, { book: "Nahum", aliases: [ "Nah", "Na" ], chapters: [ 15, 13, 19 ] }, { book: "Habakkuk", aliases: [ "Hab", "Hb" ], chapters: [ 17, 20, 19 ] }, { book: "Zephaniah", aliases: [ "Zeph", "Zep", "Zp" ], chapters: [ 18, 15, 20 ] }, { book: "Haggai", aliases: [ "Hag", "Hg" ], chapters: [ 15, 23 ] }, { book: "Zechariah", aliases: [ "Zech", "Zec", "Zc" ], chapters: [ 21, 13, 10, 14, 11, 15, 14, 23, 17, 12, 17, 14, 9, 21 ] }, { book: "Malachi", aliases: [ "Mal", "Ml" ], chapters: [ 14, 17, 18, 6 ] }, { book: "Matthew", aliases: [ "Matt", "Mt" ], chapters: [ 25, 23, 17, 25, 48, 34, 29, 34, 38, 42, 30, 50, 58, 36, 39, 28, 27, 35, 30, 34, 46, 46, 39, 51, 46, 75, 66, 20 ] }, { book: "Mark", aliases: [ "Mrk", "Mar", "Mk", "Mr" ], chapters: [ 45, 28, 35, 41, 43, 56, 37, 38, 50, 52, 33, 44, 37, 72, 47, 20 ] }, { book: "Luke", aliases: [ "Luk", "Lk" ], chapters: [ 80, 52, 38, 44, 39, 49, 50, 56, 62, 42, 54, 59, 35, 35, 32, 31, 37, 43, 48, 47, 38, 71, 56, 53 ] }, { book: "John", aliases: [ "Joh", "Jhn", "Jn" ], chapters: [ 51, 25, 36, 54, 47, 71, 53, 59, 41, 42, 57, 50, 38, 31, 27, 33, 26, 40, 42, 31, 25 ] }, { book: "Acts", aliases: [ "Acts", "Ac" ], chapters: [ 26, 47, 26, 37, 42, 15, 60, 40, 43, 48, 30, 25, 52, 28, 41, 40, 34, 28, 41, 38, 40, 30, 35, 27, 27, 32, 44, 31 ] }, { book: "Romans", aliases: [ "Rom", "Ro", "Rm" ], chapters: [ 32, 29, 31, 25, 21, 23, 25, 39, 33, 21, 36, 21, 14, 23, 33, 27 ] }, { book: "1 Corinthians", aliases: [ "1 Cor", "1 Co" ], chapters: [ 31, 16, 23, 21, 13, 20, 40, 13, 27, 33, 34, 31, 13, 40, 58, 24 ] }, { book: "2 Corinthians", aliases: [ "2 Cor", "2 Co" ], chapters: [ 24, 17, 18, 18, 21, 18, 16, 24, 15, 18, 33, 21, 14 ] }, { book: "Galatians", aliases: [ "Gal", "Ga" ], chapters: [ 24, 21, 29, 31, 26, 18 ] }, { book: "Ephesians", aliases: [ "Ephes", "Eph" ], chapters: [ 23, 22, 21, 32, 33, 24 ] }, { book: "Philippians", aliases: [ "Phil", "Php", "Pp" ], chapters: [ 30, 30, 21, 23 ] }, { book: "Colossians", aliases: [ "Col", "Co" ], chapters: [ 29, 23, 25, 18 ] }, { book: "1 Thessalonians", aliases: [ "1 Thess", "1 Thes", "1 Th" ], chapters: [ 10, 20, 13, 18, 28 ] }, { book: "2 Thessalonians", aliases: [ "2 Thess", "2 Thes", "2 Th" ], chapters: [ 12, 17, 18 ] }, { book: "1 Timothy", aliases: [ "1 Tim", "1 Ti" ], chapters: [ 20, 15, 16, 16, 25, 21 ] }, { book: "2 Timothy", aliases: [ "2 Tim", "2 Ti" ], chapters: [ 18, 26, 17, 22 ] }, { book: "Titus", aliases: [ "Tit", "Ti" ], chapters: [ 16, 15, 15 ] }, { book: "Philemon", aliases: [ "Philem", "Phm", "Pm" ], chapters: [ 25 ] }, { book: "Hebrews", aliases: [ "Heb" ], chapters: [ 14, 18, 19, 16, 14, 20, 28, 13, 28, 39, 40, 29, 25 ] }, { book: "James", aliases: [ "Jas", "Jm" ], chapters: [ 27, 26, 18, 17, 20 ] }, { book: "1 Peter", aliases: [ "1 Pet", "1 Pe", "1 Pt", "1 P" ], chapters: [ 25, 25, 22, 19, 14 ] }, { book: "2 Peter", aliases: [ "2 Pet", "2 Pe", "2 Pt", "2 P" ], chapters: [ 21, 22, 18 ] }, { book: "1 John", aliases: [ "1 Jn", "1 Jo", "1 Joh", "1 Jhn", "1 J" ], chapters: [ 10, 29, 24, 21, 21 ] }, { book: "2 John", aliases: [ "2 Jn", "2 Jo", "2 Joh", "2 Jhn", "2 J" ], chapters: [ 13 ] }, { book: "3 John", aliases: [ "3 Jn", "3 Jo", "3 Joh", "3 Jhn", "3 J" ], chapters: [ 15 ] }, { book: "Jude", aliases: [ "Jud", "Jd" ], chapters: [ 25 ] }, { book: "Revelation", aliases: [ "Rev", "Re", "Revelation to John" ], chapters: [ 20, 29, 22, 11, 14, 17, 17, 13, 21, 11, 19, 17, 18, 20, 8, 21, 18, 24, 21, 15, 27, 21 ] } ];/** * @import { BibleBook } from './types.js' */ // Build a Map at load time for fast lookup by normalized book name or alias const bookCache = new Map(); for (const b of bibleCounts) { const normalizedBook = normalizeBookName(b.book); if (normalizedBook) bookCache.set(normalizedBook, b); for (const alias of b.aliases) { const normalizedAlias = normalizeBookName(alias); if (normalizedAlias) bookCache.set(normalizedAlias, b); } } /** * Retrieves a book object from the Bible collection matching the given book name or its aliases, ignoring case and special characters. * @public * @param {string} book - The name or alias of the book to lookup, which will be normalized internally. * @returns {BibleBook|null} - The matched book object containing book name, aliases, and chapters, or null if no match is found. * @example * // Returns the Genesis book object with its aliases and 50 chapters * getBook('Genesis'); // { book: 'Genesis', aliases: ['Gen', 'Ge', 'Gn'], chapters: [...] } * // Returns the Song of Solomon book object when queried with a normalized alias ignoring punctuation and case * getBook('The CANticle of CantiClEs !!?*'); // { book: 'Song of Solomon', aliases: [...], chapters: [...] } * // Returns null for an unknown or invalid book name * getBook('Judas'); // null */ function getBook(book) { if (!book) return null; const normalized = normalizeBookName(book); return bookCache.get(normalized) || null; } /** * Returns the number of chapters for a given Bible book name or alias, or null if the book is not found. * @public * @param {string} name - The name or alias of the book to lookup, which will be normalized internally. * @returns {number|null} - The total number of chapters in the matched book, or null if no book is found. * @example * // Returns 50 chapters for Genesis * getChapterCount('Genesis'); // 50 * // Returns null for an unknown or invalid book name * getChapterCount('Judas'); */ function getChapterCount(name) { const book = getBook(name); return book ? book.chapters.length : null; } /** * Returns the number of verses in a specified chapter of a given Bible book, or null if the book or chapter is invalid. * @public * @param {string} name - The name or alias of the book to lookup, which will be normalized internally. * @param {number} chapter - The chapter number to retrieve the verse count for; must be within valid range. * @returns {number|null} - The count of verses in the specified chapter, or null if the book is unknown or chapter is out of bounds. * @example * // Returns 25, the number of verses in Genesis chapter 2 * getVerseCount('GeN. ', 2); // 25 * // Returns null for an invalid book name * getVerseCount('Judas', 1); * // Returns null for a chapter number that is too high * getVerseCount('Genesis', 999); * // Returns null for a chapter number less than 1 * getVerseCount('Genesis', 0); */ function getVerseCount(name, chapter) { const book = getBook(name); if (!book || chapter < 1 || chapter > book.chapters.length) return null; return book.chapters[chapter - 1]; } /** * Returns an array of all Bible book names in their canonical order. * @public * @returns {string[]} - An array containing 66 book names starting with Genesis and ending with Revelation. * @example * // Returns an array of 66 Bible books * listBibleBooks(); * // The first and last elements are Genesis and Revelation respectively * const books = listBibleBooks(); * console.log(books[0]); // "Genesis" * console.log(books[books.length - 1]); // "Revelation" */ function listBibleBooks() { return bibleCounts.map((b) => b.book); } /** * Returns all aliases for a given book name, including the official book title, optionally normalized. * @public * @param {string} bookName - The name or alias of the book to lookup, which will be normalized internally. * @param {Object} [options] - Optional settings. * @param {boolean} [options.normalized=false] - If true, returns all aliases normalized (lowercased and stripped of special characters). * @returns {string[]|null} - An array of aliases including the official book name, either normalized or in original form, or null if no matching book is found. * @example * // Returns non-normalized aliases for "Second Corinthians" * listAliases('Second Corinthians'); * // Expected output: ["2 Corinthians", "2 Co", ...other aliases] * // Returns normalized aliases for "Song" with normalization enabled * listAliases('Song', { normalized: true }); * // Expected output: ["songofsolomon", "canticleofcanticles", "sos", ...] * // Returns null for unrecognized or empty book names * listAliases('UnknownBook'); // null * listAliases(null); // null * listAliases(''); // null */ function listAliases(bookName, { normalized = false } = {}) { const book = getBook(bookName); if (!book) return null; if (normalized) { return [book.book, ...book.aliases].map(normalizeBookName).filter((s) => s != null); } return [book.book, ...book.aliases]; } /** * Returns an array of chapter numbers for a given Bible book, starting from 1 up to the total chapter count. * @public * @param {string} bookName - The name or alias of the book to lookup, which will be normalized internally. * @returns {number[]|null} - An array of chapter numbers from 1 to the book's chapter count, or null if the book is invalid or not found. * @example * // Returns an array [1, 2, ..., 40] for Exodus, which has 40 chapters * listChapters('Exodus'); // [1, 2, 3, ..., 40] * // Returns null for an invalid or unknown book * listChapters('UnknownBook'); // null */ function listChapters(bookName) { const count = getChapterCount(bookName); if (count == null) return null; return Array.from({ length: count }, (_, i) => i + 1); } /** * Lists all verse numbers for a given book and chapter as a sequential array starting from 1. * @public * @param {string} bookName - The name or alias of the book to lookup, which will be normalized internally. * @param {number} chapter - The chapter number within the book. * @returns {number[]|null} - An array of verse numbers from 1 up to the chapter's verse count, or null if the book or chapter is invalid or out of range. * @example * // Returns an array of verses [1, 2, ..., 31] for Genesis chapter 1 * listVerses('Genesis', 1); // [1, 2, 3, ..., 31] * // Returns null for a missing chapter parameter * listVerses('Genesis'); // null * // Returns null for an invalid chapter number or unknown book * listVerses('Genesis', 0); // null * listVerses('Genesis', 999); // null * listVerses('UnknownBook', 1); // null */ function listVerses(bookName, chapter) { const verseCount = getVerseCount(bookName, chapter); if (verseCount == null) return null; return Array.from({ length: verseCount }, (_, i) => i + 1); }/** * Checks if a given book name or alias corresponds to a valid Bible book. * @public * @param {string|null|undefined} book - The name or alias of the book to lookup, which will be normalized internally. * @returns {boolean} - True if the book exists in the Bible collection, false otherwise. * @example * // Valid full book name returns true * isValidBook('Genesis'); // true * // Valid alias, case-insensitive, returns true * isValidBook('gEn'); // true * // Unknown book returns false * isValidBook('Judas'); // false * // Empty string returns false * isValidBook(''); // false * // Null or undefined input returns false * isValidBook(null); // false * isValidBook(undefined); // false */ function isValidBook(book) { if (typeof book !== 'string' || book.trim() === '') return false; return getBook(book) !== null; } /** * Checks if the given chapter number is valid for the specified Bible book. * @public * @param {string} book - The name or alias of the book to lookup, which will be normalized internally. * @param {number} chapter - The chapter number to check, expected to be a positive integer. * @returns {boolean} - True if the chapter is within the valid range for the book; otherwise, false. * @example * // Valid chapters for Genesis include 1 and 50 * isValidChapter('Genesis', 1); // true * isValidChapter('Genesis', 50); // true * // Invalid chapters are below 1 or above the book's chapter count * isValidChapter('Genesis', 0); // false * isValidChapter('Genesis', 51); // false * isValidChapter('Genesis', -1); // false * // Returns false if the book is unknown or input is null/undefined * isValidChapter('Judas', 1); // false * isValidChapter(null, 1); // false * isValidChapter('Genesis', null); // false */ function isValidChapter(book, chapter) { const bookObj = getBook(book); if (!bookObj) return false; return chapter >= 1 && chapter <= bookObj.chapters.length; } /** * Validates whether a given Bible reference consisting of book, chapter, and verse(s) is valid. * @public * @param {string} book - The name or alias of the book to lookup, which will be normalized internally. * @param {number} chapter - The chapter number, must be a positive integer within the book's chapter count. * @param {number} verseStart - The starting verse number, must be a positive integer within the chapter's verse count. * @param {number|null} [verseEnd=null] - Optional ending verse number, must be greater than or equal to verseStart and within the chapter's verse count if provided. * @returns {boolean} True if the reference is valid within the book's chapter and verse bounds, otherwise false. * @example * // Valid single verse in Genesis chapter 1 * isValidReference('Genesis', 1, 1); // true * // Valid last verse in Genesis chapter 1 * isValidReference('Genesis', 1, 31); // true * // Invalid verse number exceeding the number of verses in chapter 1 of Genesis * isValidReference('Genesis', 1, 32); // false * // Invalid chapter number (0) in Genesis * isValidReference('Genesis', 0, 1); // false * // Invalid verse number (0) in Genesis chapter 1 * isValidReference('Genesis', 1, 0); // false * // Invalid unknown book name returns false * isValidReference('Blah', 1, 1); // false * // Reference with a verse range, valid only if verseEnd >= verseStart and within chapter verse count * isValidReference('Genesis', 1, 1, 5); // true * // Case-insensitive book name input is accepted * isValidReference('gEnEsIs', 1, 1); // true */ function isValidReference(book, chapter, verseStart, verseEnd = null) { const bookObj = getBook(book); if (!bookObj || !isValidChapter(book, chapter)) return false; const maxVerses = bookObj.chapters[chapter - 1]; if (verseStart < 1 || verseStart > maxVerses) return false; if (verseEnd !== null) { if (verseEnd < verseStart || verseEnd > maxVerses) return false; } return true; }/** * @import { ParseReferenceOptions, ParsedReference, SimpleResult, StructuredResult } from './types.js' */ /** * Formats a scripture reference string based on the provided input. Input is not normalized. * @public * @param {Object} reference - The reference object. * @param {string} reference.book - The name of the book (e.g., "Genesis"). * @param {number} [reference.chapter] - The chapter number. * @param {number} [reference.verseStart] - The starting verse number. * @param {number|null} [reference.verseEnd] - The ending verse number (optional, used for ranges). * @returns {string} - A formatted Bible reference (e.g., "Genesis 1:1-5"). Returns an empty string if no input is provided. * @example * formatReference({}); // '' * formatReference({ book: 'Genesis' }); // 'Genesis' * formatReference({ book: 'Genesis', chapter: 1 }); // 'Genesis 1' * formatReference({ book: 'Genesis', chapter: 1, verseStart: 1 }); // 'Genesis 1:1' * formatReference({ book: 'Genesis', chapter: 1, verseStart: 1, verseEnd: 5 }); // 'Genesis 1:1-5' * formatReference({ book: 'Genesis', chapter: 1, verseStart: 3, verseEnd: 3 }); // 'Genesis 1:3' */ function formatReference({ book, chapter, verseStart, verseEnd }) { if (!book || !chapter) return book || ''; if (verseStart == null) return `${book} ${chapter}`; if (verseEnd == null || verseEnd === verseStart) return `${book} ${chapter}:${verseStart}`; return `${book} ${chapter}:${verseStart}-${verseEnd}`; } /** * Parses and validates a Bible reference string. * @public * @param {string} reference - The raw Bible reference string to be parsed, normalized, and formatted (e.g., "Genesis 1:1", "Letter to the Romans. Ch 2 , 1 to 3"). * @param {ParseReferenceOptions} [options] - Optional configuration, return structured object or just the formatted result. * @returns {SimpleResult|StructuredResult} - Result object depending on options.structured. * @example * parseAndValidateReference(' GN. Ch 1 , 1 to 3'); * // → { isValid: true, formatted: 'Genesis 1:1-3', error: null, original: ' GN. Ch 1 , 1 to 3' } * parseAndValidateReference('gEnEsIs 1 verse 1', { structured: true }); * // → { * // isValid: true, * // book: 'Genesis', * // chapter: 1, * // verseStart: 1, * // verseEnd: null, * // formatted: 'Genesis 1:1', * // error: null, * // original: 'gEnEsIs 1 verse 1' * // } * parseAndValidateReference('Book of Judas 1:1'); * // → { isValid: false, error: 'Invalid book name', original: 'Book of Judas 1:1' } */ function parseAndValidateReference(reference, { structured = false } = {}) { /** * @private * @param {string} msg * @returns {{ isValid: false, error: string, original: string }} */ const fail = (msg) => ({ isValid: false, error: msg, original: reference }); if (typeof reference !== 'string' || !reference.trim()) { return fail('Empty or invalid input'); } /** * @private * @type {ParsedReference|null} */ const parsed = parseBibleReference(reference); if (!parsed?.book) return fail('Could not parse reference'); const bookObj = getBook(parsed.book); if (!bookObj) return fail('Invalid book name'); const chapter = parsed.chapter ?? null; const verseStart = parsed.verseStart ?? null; const verseEnd = parsed.verseEnd ?? null; if (chapter === null || verseStart === null) { return fail('Missing chapter or verse'); } if (!isValidReference(bookObj.book, chapter, verseStart, verseEnd)) { return fail('Invalid chapter or verse'); } const formatted = formatReference({ book: bookObj.book, chapter, verseStart, verseEnd }); const base = { isValid: true, formatted, error: null, original: reference, }; return structured ? { ...base, book: bookObj.book, chapter, verseStart, verseEnd, } : base; }export{extractBookAndRange,formatReference,getBook,getChapterCount,getVerseCount,isValidBook,isValidChapter,isValidReference,listAliases,listBibleBooks,listChapters,listVerses,normalizeBookName,parseAndValidateReference,parseBibleReference,parseChapterVerse};//# sourceMappingURL=index.module.js.map