codexparser 0.1.97 → 0.2.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.
Files changed (117) hide show
  1. package/.trunk/trunk.yaml +3 -3
  2. package/REFACTORING.md +214 -0
  3. package/index.js +1 -1
  4. package/package.json +4 -24
  5. package/src/{CodexParser.js → CodexParser.js.backup} +303 -247
  6. package/src/core/CodexParser.js +353 -0
  7. package/src/core/PassageCollection.js +404 -0
  8. package/src/core/PassageValidator.js +107 -0
  9. package/src/core/ReferenceParser.js +622 -0
  10. package/src/core/ScriptureScanner.js +235 -0
  11. package/src/core/VersificationHandler.js +143 -0
  12. package/src/core/VersionHandler.js +77 -0
  13. package/src/data/toc.js +7 -0
  14. package/src/format/osis.js +210 -0
  15. package/src/index.js +12 -0
  16. package/src/utils/PassageUtils.js +117 -0
  17. package/src/utils/chapterVerseCombine.js +65 -0
  18. package/.babelrc +0 -5
  19. package/src/chapterVerseCombine.js +0 -63
  20. package/src/toc.js +0 -2
  21. package/webpack.config.js +0 -38
  22. /package/src/{abbr → data/abbr}/sbl.js +0 -0
  23. /package/src/{bible.js → data/bible.js} +0 -0
  24. /package/src/{chapter_verses → data/chapter_verses}/1chronicles.js +0 -0
  25. /package/src/{chapter_verses → data/chapter_verses}/1corinthians.js +0 -0
  26. /package/src/{chapter_verses → data/chapter_verses}/1john.js +0 -0
  27. /package/src/{chapter_verses → data/chapter_verses}/1kings.js +0 -0
  28. /package/src/{chapter_verses → data/chapter_verses}/1peter.js +0 -0
  29. /package/src/{chapter_verses → data/chapter_verses}/1samuel.js +0 -0
  30. /package/src/{chapter_verses → data/chapter_verses}/1thessalonians.js +0 -0
  31. /package/src/{chapter_verses → data/chapter_verses}/1timothy.js +0 -0
  32. /package/src/{chapter_verses → data/chapter_verses}/2chronicles.js +0 -0
  33. /package/src/{chapter_verses → data/chapter_verses}/2corinthians.js +0 -0
  34. /package/src/{chapter_verses → data/chapter_verses}/2kings.js +0 -0
  35. /package/src/{chapter_verses → data/chapter_verses}/2peter.js +0 -0
  36. /package/src/{chapter_verses → data/chapter_verses}/2samuel.js +0 -0
  37. /package/src/{chapter_verses → data/chapter_verses}/2thessalonians.js +0 -0
  38. /package/src/{chapter_verses → data/chapter_verses}/2timothy.js +0 -0
  39. /package/src/{chapter_verses → data/chapter_verses}/acts.js +0 -0
  40. /package/src/{chapter_verses → data/chapter_verses}/amos.js +0 -0
  41. /package/src/{chapter_verses → data/chapter_verses}/colossians.js +0 -0
  42. /package/src/{chapter_verses → data/chapter_verses}/daniel.js +0 -0
  43. /package/src/{chapter_verses → data/chapter_verses}/deuteronomy.js +0 -0
  44. /package/src/{chapter_verses → data/chapter_verses}/ecclesiastes.js +0 -0
  45. /package/src/{chapter_verses → data/chapter_verses}/ephesians.js +0 -0
  46. /package/src/{chapter_verses → data/chapter_verses}/esther.js +0 -0
  47. /package/src/{chapter_verses → data/chapter_verses}/exodus.js +0 -0
  48. /package/src/{chapter_verses → data/chapter_verses}/ezekiel.js +0 -0
  49. /package/src/{chapter_verses → data/chapter_verses}/ezra.js +0 -0
  50. /package/src/{chapter_verses → data/chapter_verses}/galatians.js +0 -0
  51. /package/src/{chapter_verses → data/chapter_verses}/genesis.js +0 -0
  52. /package/src/{chapter_verses → data/chapter_verses}/habakkuk.js +0 -0
  53. /package/src/{chapter_verses → data/chapter_verses}/haggai.js +0 -0
  54. /package/src/{chapter_verses → data/chapter_verses}/hebrews.js +0 -0
  55. /package/src/{chapter_verses → data/chapter_verses}/hosea.js +0 -0
  56. /package/src/{chapter_verses → data/chapter_verses}/isaiah.js +0 -0
  57. /package/src/{chapter_verses → data/chapter_verses}/james.js +0 -0
  58. /package/src/{chapter_verses → data/chapter_verses}/jeremiah.js +0 -0
  59. /package/src/{chapter_verses → data/chapter_verses}/job.js +0 -0
  60. /package/src/{chapter_verses → data/chapter_verses}/joel.js +0 -0
  61. /package/src/{chapter_verses → data/chapter_verses}/john.js +0 -0
  62. /package/src/{chapter_verses → data/chapter_verses}/jonah.js +0 -0
  63. /package/src/{chapter_verses → data/chapter_verses}/joshua.js +0 -0
  64. /package/src/{chapter_verses → data/chapter_verses}/judges.js +0 -0
  65. /package/src/{chapter_verses → data/chapter_verses}/lamentations.js +0 -0
  66. /package/src/{chapter_verses → data/chapter_verses}/leviticus.js +0 -0
  67. /package/src/{chapter_verses → data/chapter_verses}/luke.js +0 -0
  68. /package/src/{chapter_verses → data/chapter_verses}/malachi.js +0 -0
  69. /package/src/{chapter_verses → data/chapter_verses}/mark.js +0 -0
  70. /package/src/{chapter_verses → data/chapter_verses}/matthew.js +0 -0
  71. /package/src/{chapter_verses → data/chapter_verses}/micah.js +0 -0
  72. /package/src/{chapter_verses → data/chapter_verses}/nahum.js +0 -0
  73. /package/src/{chapter_verses → data/chapter_verses}/nehemiah.js +0 -0
  74. /package/src/{chapter_verses → data/chapter_verses}/numbers.js +0 -0
  75. /package/src/{chapter_verses → data/chapter_verses}/philippians.js +0 -0
  76. /package/src/{chapter_verses → data/chapter_verses}/proverbs.js +0 -0
  77. /package/src/{chapter_verses → data/chapter_verses}/psalms.js +0 -0
  78. /package/src/{chapter_verses → data/chapter_verses}/revelation.js +0 -0
  79. /package/src/{chapter_verses → data/chapter_verses}/romans.js +0 -0
  80. /package/src/{chapter_verses → data/chapter_verses}/ruth.js +0 -0
  81. /package/src/{chapter_verses → data/chapter_verses}/songs.js +0 -0
  82. /package/src/{chapter_verses → data/chapter_verses}/titus.js +0 -0
  83. /package/src/{chapter_verses → data/chapter_verses}/zechariah.js +0 -0
  84. /package/src/{chapter_verses → data/chapter_verses}/zephaniah.js +0 -0
  85. /package/src/{esv.js → data/esv.js} +0 -0
  86. /package/src/{versifications → data/versifications}/1chronicles.js +0 -0
  87. /package/src/{versifications → data/versifications}/1kings.js +0 -0
  88. /package/src/{versifications → data/versifications}/1samuel.js +0 -0
  89. /package/src/{versifications → data/versifications}/2chronicles.js +0 -0
  90. /package/src/{versifications → data/versifications}/2samuel.js +0 -0
  91. /package/src/{versifications → data/versifications}/daniel.js +0 -0
  92. /package/src/{versifications → data/versifications}/deuteronomy.js +0 -0
  93. /package/src/{versifications → data/versifications}/ecclesiastes.js +0 -0
  94. /package/src/{versifications → data/versifications}/exodus.js +0 -0
  95. /package/src/{versifications → data/versifications}/ezekiel.js +0 -0
  96. /package/src/{versifications → data/versifications}/genesis.js +0 -0
  97. /package/src/{versifications → data/versifications}/hosea.js +0 -0
  98. /package/src/{versifications → data/versifications}/isaiah.js +0 -0
  99. /package/src/{versifications → data/versifications}/jeremiah.js +0 -0
  100. /package/src/{versifications → data/versifications}/job.js +0 -0
  101. /package/src/{versifications → data/versifications}/joel.js +0 -0
  102. /package/src/{versifications → data/versifications}/jonah.js +0 -0
  103. /package/src/{versifications → data/versifications}/joshua.js +0 -0
  104. /package/src/{versifications → data/versifications}/leviticus.js +0 -0
  105. /package/src/{versifications → data/versifications}/malachi.js +0 -0
  106. /package/src/{versifications → data/versifications}/micah.js +0 -0
  107. /package/src/{versifications → data/versifications}/nahum.js +0 -0
  108. /package/src/{versifications → data/versifications}/nehemiah.js +0 -0
  109. /package/src/{versifications → data/versifications}/numbers.js +0 -0
  110. /package/src/{versifications → data/versifications}/proverbs.js +0 -0
  111. /package/src/{versifications → data/versifications}/psalms.js +0 -0
  112. /package/src/{versifications → data/versifications}/song.js +0 -0
  113. /package/src/{versifications → data/versifications}/zechariah.js +0 -0
  114. /package/src/{versified.js → data/versified.js} +0 -0
  115. /package/src/{abbr.js → format/abbr.js} +0 -0
  116. /package/src/{functions.js → utils/functions.js} +0 -0
  117. /package/src/{regex.js → utils/regex.js} +0 -0
@@ -0,0 +1,353 @@
1
+ /**
2
+ * CodexParser.js
3
+ * A modern ES6+ class for scanning and parsing scripture references from text.
4
+ * Supports various formats (single verses, ranges, multi-chapter references) with
5
+ * validation and version-specific versification.
6
+ */
7
+
8
+ const bible = require("../data/bible")
9
+ const { bookRegex, chapterRegex, verseRegex, scripturesRegex } = require("../utils/regex")
10
+ const ScriptureScanner = require("./ScriptureScanner")
11
+ const ReferenceParser = require("./ReferenceParser")
12
+ const VersificationHandler = require("./VersificationHandler")
13
+ const PassageCollection = require("./PassageCollection")
14
+ const PassageUtils = require("../utils/PassageUtils")
15
+ const VersionHandler = require("./VersionHandler")
16
+ const chapter_verses = require("../utils/chapterVerseCombine")
17
+ const abbreviations = require("../format/abbr")
18
+
19
+ /**
20
+ * Main class for parsing and validating scripture references
21
+ * @class
22
+ */
23
+ class CodexParser {
24
+ // Private fields using ES6+ syntax
25
+ #scanner
26
+ #parser
27
+ #versificationHandler
28
+ #config
29
+ #version
30
+ #found
31
+ #passages
32
+
33
+ /**
34
+ * Initializes the parser with configuration
35
+ * @param {Object} config - Configuration options
36
+ */
37
+ constructor(config = {}) {
38
+ this.#config = {
39
+ booksOnly: config.booksOnly ?? false,
40
+ invalid_sequence_strategy: config.invalid_sequence_strategy ?? "include",
41
+ invalid_passage_strategy: config.invalid_passage_strategy ?? "include",
42
+ }
43
+
44
+ this.#scanner = new ScriptureScanner(this.#config)
45
+ this.#parser = new ReferenceParser(this.#config)
46
+ this.#versificationHandler = new VersificationHandler()
47
+ this.#version = null
48
+ this.#found = []
49
+ this.#passages = []
50
+
51
+ // Legacy public properties for backward compatibility
52
+ this.bible = bible
53
+ this.bookRegex = bookRegex
54
+ this.chapterRegex = chapterRegex
55
+ this.verseRegex = verseRegex
56
+ this.scripturesRegex = scripturesRegex
57
+ this.chapterVerses = chapter_verses
58
+ this.abbreviations = abbreviations
59
+ this.error = false
60
+
61
+ // Legacy constants
62
+ this.SINGLE_CHAPTER = "single_chapter"
63
+ this.CHAPTER_VERSE = "chapter_verse"
64
+ this.CHAPTER_VERSE_RANGE = "chapter_verse_range"
65
+ this.COMMA_SEPARATED = "comma_separated_verses"
66
+ this.CHAPTER_RANGE = "chapter_range"
67
+ this.MULTI_CHAPTER_RANGE = "multi_chapter_verse_range"
68
+ }
69
+
70
+ /**
71
+ * Gets the found references (legacy getter)
72
+ */
73
+ get found() {
74
+ return this.#found
75
+ }
76
+
77
+ /**
78
+ * Sets the found references (legacy setter)
79
+ */
80
+ set found(value) {
81
+ this.#found = value
82
+ }
83
+
84
+ /**
85
+ * Gets the parsed passages (legacy getter)
86
+ */
87
+ get passages() {
88
+ return this.#passages
89
+ }
90
+
91
+ /**
92
+ * Sets the parsed passages (legacy setter)
93
+ */
94
+ set passages(value) {
95
+ this.#passages = value
96
+ }
97
+
98
+ /**
99
+ * Gets the current version (legacy getter)
100
+ */
101
+ get version() {
102
+ return this.#version
103
+ }
104
+
105
+ /**
106
+ * Sets the current version (legacy setter)
107
+ */
108
+ set version(value) {
109
+ this.#version = value
110
+ }
111
+
112
+ /**
113
+ * Gets the current config (legacy getter)
114
+ */
115
+ get config() {
116
+ return this.#config
117
+ }
118
+
119
+ /**
120
+ * Sets the current config (legacy setter)
121
+ */
122
+ set config(value) {
123
+ this.#config = value
124
+ }
125
+
126
+ /**
127
+ * Sets configuration options for the parser
128
+ * @param {Object} config - Configuration options
129
+ * @param {boolean} [config.booksOnly=false] - Whether to capture book names without references
130
+ * @returns {CodexParser} The parser instance for method chaining
131
+ */
132
+ options(config) {
133
+ this.#config = {
134
+ booksOnly: config.booksOnly ?? this.#config.booksOnly,
135
+ invalid_sequence_strategy: config.invalid_sequence_strategy ?? this.#config.invalid_sequence_strategy,
136
+ invalid_passage_strategy: config.invalid_passage_strategy ?? this.#config.invalid_passage_strategy,
137
+ }
138
+
139
+ // Update scanner and parser configs
140
+ this.#scanner = new ScriptureScanner(this.#config)
141
+ this.#parser = new ReferenceParser(this.#config)
142
+
143
+ return this
144
+ }
145
+
146
+ /**
147
+ * Retrieves available verses for a given book and chapter (legacy method)
148
+ * @param {string} book - The book name
149
+ * @param {number} chapter - The chapter number
150
+ * @returns {number[]} Array of valid verse numbers
151
+ */
152
+ getChapterVerses(book, chapter) {
153
+ return PassageUtils.getChapterVerses(book, chapter)
154
+ }
155
+
156
+ /**
157
+ * Scans text for scripture references
158
+ * @param {string} text - The text to scan
159
+ * @returns {CodexParser} The parser instance for method chaining
160
+ */
161
+ scan(text) {
162
+ this.#found = this.#scanner.scan(text)
163
+ return this
164
+ }
165
+
166
+ /**
167
+ * Sets the Bible version for parsing
168
+ * @param {string} version - The version (e.g., "lxx", "mt", "eng")
169
+ * @returns {CodexParser} The parser instance
170
+ */
171
+ bibleVersion(version) {
172
+ this.#version = VersionHandler.normalizeVersion(version)
173
+ return this
174
+ }
175
+
176
+ /**
177
+ * Parses a scripture reference into structured passage objects
178
+ * @param {string} reference - The reference to parse (e.g., "John 3:16")
179
+ * @returns {CodexParser} The parser instance
180
+ */
181
+ parse(reference) {
182
+ this.scan(reference)
183
+ this.#passages = this.#parser.parse(this.#found, this.#version)
184
+ this.#passages = this.#versificationHandler.applyVersification(this.#passages)
185
+ return this
186
+ }
187
+
188
+ /**
189
+ * Returns parsed passages with utility methods
190
+ * @returns {PassageCollection} Collection of passages with utility methods
191
+ */
192
+ getPassages() {
193
+ return PassageCollection.from(this.#passages)
194
+ }
195
+
196
+ /**
197
+ * Returns the first parsed passage
198
+ * @returns {Object|null} The first passage or null
199
+ */
200
+ first() {
201
+ return this.#passages.length > 0 ? this.#passages[0] : null
202
+ }
203
+
204
+ /**
205
+ * Normalizes book names using abbreviations or full names (legacy method)
206
+ * @param {string|Array} book - The book name or array
207
+ * @returns {string} Normalized book name
208
+ */
209
+ bookify(book) {
210
+ if (typeof book !== "string") {
211
+ book = book[0]
212
+ }
213
+ book = book.toLowerCase()
214
+
215
+ const bookified = abbreviations[Object.keys(abbreviations).find((abbr) => abbr.toLowerCase() === book)]
216
+ if (bookified) {
217
+ return bookified
218
+ }
219
+
220
+ return (
221
+ bible.new.find((b) => b.toLowerCase() === book) || bible.old.find((b) => b.toLowerCase() === book) || book
222
+ )
223
+ }
224
+
225
+ /**
226
+ * Combines multiple passages into a single reference
227
+ * @param {Object[]} [passages=this.passages] - Array of passages to combine
228
+ * @returns {Object} Combined passage object
229
+ */
230
+ combine(passages = this.#passages) {
231
+ return PassageCollection.combinePassages(passages)
232
+ }
233
+
234
+ /**
235
+ * Merges verses into ranges or comma-separated lists (legacy method)
236
+ * @param {number[]} verses - Array of verse numbers
237
+ * @returns {string[]} Array of verse strings
238
+ */
239
+ mergeRanges(verses) {
240
+ return PassageUtils.mergeRanges(verses)
241
+ }
242
+
243
+ /**
244
+ * Generates a table of contents for the Bible
245
+ * @param {string} [version="ESV"] - The Bible version
246
+ * @returns {Object} TOC with book-chapter-verse mappings
247
+ */
248
+ getToc(version = "ESV") {
249
+ const toc = {}
250
+
251
+ bible.old.forEach((book) => {
252
+ if (chapter_verses[book]) {
253
+ toc[book] = chapter_verses[book]
254
+ }
255
+ })
256
+
257
+ bible.new.forEach((book) => {
258
+ if (chapter_verses[book]) {
259
+ toc[book] = chapter_verses[book]
260
+ }
261
+ })
262
+
263
+ PassageUtils.SINGLE_CHAPTER_BOOKS.forEach((item) => {
264
+ Object.keys(item).forEach((book) => {
265
+ if (!toc[book]) {
266
+ toc[book] = item[book]
267
+ }
268
+ })
269
+ })
270
+
271
+ const orderedToc = {}
272
+ const canonicalOrder = [...bible.old, ...bible.new]
273
+ canonicalOrder.forEach((book) => {
274
+ if (toc[book]) {
275
+ orderedToc[book] = toc[book]
276
+ }
277
+ })
278
+
279
+ return orderedToc
280
+ }
281
+
282
+ /**
283
+ * Replaces scripture references in text with formatted references
284
+ * @param {string} text - The original text
285
+ * @param {boolean} useAbbreviations - Whether to use abbreviated book names
286
+ * @returns {string} Text with replaced references
287
+ */
288
+ replace(text, useAbbreviations = true) {
289
+ if (!this.#passages.length) {
290
+ return text
291
+ }
292
+
293
+ let result = text
294
+ for (let i = this.#passages.length - 1; i >= 0; i--) {
295
+ const passage = this.#passages[i]
296
+ const { originalText, abbr, original } = passage
297
+ const newReference = useAbbreviations ? abbr : original
298
+
299
+ const regex = new RegExp(`${originalText.replace(/([.*+?^${}()|[\]\\])/g, "\\$1")}`, "g")
300
+
301
+ const matches = [...result.matchAll(regex)]
302
+ if (matches.length > 0) {
303
+ for (let j = matches.length - 1; j >= 0; j--) {
304
+ const match = matches[j]
305
+ const startIndex = match.index
306
+ const endIndex = startIndex + match[0].length
307
+ const leadingSpace = match[1] || ""
308
+ const hasOpeningParen = match[2] === "("
309
+ const hasClosingParen = match[3] === ")"
310
+ const trailingSpace = match[4] || " "
311
+ const replacement =
312
+ hasOpeningParen && hasClosingParen
313
+ ? `${leadingSpace}(${newReference})${trailingSpace}`
314
+ : `${leadingSpace}${newReference}${trailingSpace}`
315
+ result = result.slice(0, startIndex) + replacement + result.slice(endIndex)
316
+ }
317
+ }
318
+ }
319
+
320
+ return result
321
+ }
322
+
323
+ /**
324
+ * Checks if all references in the passages array are from the same book
325
+ * @returns {boolean} True if all passages are from the same book
326
+ */
327
+ same() {
328
+ if (this.#passages.length <= 1) {
329
+ return true
330
+ }
331
+
332
+ const firstBook = this.#passages[0].book.toLowerCase()
333
+ return this.#passages.every((passage) => passage.book.toLowerCase() === firstBook)
334
+ }
335
+
336
+ // Legacy methods for backward compatibility
337
+
338
+ /**
339
+ * Expands verse references into individual verse objects (legacy)
340
+ */
341
+ expandVerses(book, chapter, verses) {
342
+ return PassageUtils.expandVerses(book, chapter, verses)
343
+ }
344
+
345
+ /**
346
+ * Applies versification differences to parsed passages (legacy)
347
+ */
348
+ versification() {
349
+ this.#passages = this.#versificationHandler.applyVersification(this.#passages)
350
+ }
351
+ }
352
+
353
+ module.exports = CodexParser