codexparser 0.1.83 → 0.1.85
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/.trunk/trunk.yaml +3 -3
- package/package.json +1 -1
- package/src/CodexParser.js +110 -32
- package/src/versifications/malachi.js +7 -3
package/.trunk/trunk.yaml
CHANGED
|
@@ -7,7 +7,7 @@ cli:
|
|
|
7
7
|
plugins:
|
|
8
8
|
sources:
|
|
9
9
|
- id: trunk
|
|
10
|
-
ref: v1.7.
|
|
10
|
+
ref: v1.7.1
|
|
11
11
|
uri: https://github.com/trunk-io/plugins
|
|
12
12
|
# Many linters and tools depend on runtimes - configure them here. (https://docs.trunk.io/runtimes)
|
|
13
13
|
runtimes:
|
|
@@ -17,11 +17,11 @@ runtimes:
|
|
|
17
17
|
# This is the section where you manage your linters. (https://docs.trunk.io/check/configuration)
|
|
18
18
|
lint:
|
|
19
19
|
enabled:
|
|
20
|
-
- checkov@3.2.
|
|
20
|
+
- checkov@3.2.446
|
|
21
21
|
- git-diff-check
|
|
22
22
|
- markdownlint@0.45.0
|
|
23
23
|
- osv-scanner@2.0.3
|
|
24
|
-
- prettier@3.6.
|
|
24
|
+
- prettier@3.6.1
|
|
25
25
|
- trufflehog@3.89.2
|
|
26
26
|
actions:
|
|
27
27
|
disabled:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codexparser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.85",
|
|
4
4
|
"description": "This is a Javascript Bible parser and text scanner. It will search through texts and collate all scripture references into an array and parse them into objects, and it will parse passages into objects by book, chapter, verse, and testament. ",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
package/src/CodexParser.js
CHANGED
|
@@ -85,11 +85,6 @@ class CodexParser {
|
|
|
85
85
|
return singleChapterBook ? singleChapterBook[book][chapter] || [] : this.chapterVerses[book]?.[chapter] || []
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
/**
|
|
89
|
-
* Scans text for scripture references and stores them in `this.found`.
|
|
90
|
-
* @param {string} text - The text to scan.
|
|
91
|
-
* @returns {CodexParser} The parser instance for method chaining.
|
|
92
|
-
*/
|
|
93
88
|
/**
|
|
94
89
|
* Scans text for scripture references and stores them in `this.found`.
|
|
95
90
|
* @param {string} text - The text to scan.
|
|
@@ -101,6 +96,8 @@ class CodexParser {
|
|
|
101
96
|
this.found = []
|
|
102
97
|
// Minimal normalization: fix periods before numbers, remove trailing periods
|
|
103
98
|
let normalizedText = text.replace(/\.(?=\d)/g, ":").replace(/(\b[A-Za-z]+)\.(?=\s|$)/g, "$1")
|
|
99
|
+
console.log(`Input text: ${text}`)
|
|
100
|
+
console.log(`Normalized text: ${normalizedText}`)
|
|
104
101
|
const lowercaseBibleFullNames = fullNames.map((book) => book.toLowerCase())
|
|
105
102
|
const lowercaseBibleAbbreviations = abbreviations.map((abbr) => abbr.toLowerCase())
|
|
106
103
|
const lowerCaseText = normalizedText.toLowerCase()
|
|
@@ -121,7 +118,7 @@ class CodexParser {
|
|
|
121
118
|
|
|
122
119
|
while (i < lowerCaseText.length) {
|
|
123
120
|
let foundBook = null
|
|
124
|
-
let
|
|
121
|
+
let bookStartIndex = -1
|
|
125
122
|
let matchedLength = 0
|
|
126
123
|
|
|
127
124
|
// Skip whitespace and special characters before checking for book
|
|
@@ -130,11 +127,13 @@ class CodexParser {
|
|
|
130
127
|
}
|
|
131
128
|
if (i >= lowerCaseText.length) break
|
|
132
129
|
|
|
130
|
+
console.log(`Scanning at index ${i}: ${lowerCaseText.slice(i, i + 10)}...`)
|
|
131
|
+
|
|
133
132
|
for (let j = 0; j < lowercaseBibleFullNames.length; j++) {
|
|
134
133
|
const book = lowercaseBibleFullNames[j]
|
|
135
134
|
if (lowerCaseText.startsWith(book, i) && book.length > matchedLength) {
|
|
136
135
|
foundBook = fullNames[j]
|
|
137
|
-
|
|
136
|
+
bookStartIndex = i
|
|
138
137
|
matchedLength = book.length
|
|
139
138
|
}
|
|
140
139
|
}
|
|
@@ -144,24 +143,42 @@ class CodexParser {
|
|
|
144
143
|
const abbreviation = lowercaseBibleAbbreviations[k]
|
|
145
144
|
if (lowerCaseText.startsWith(abbreviation, i) && abbreviation.length > matchedLength) {
|
|
146
145
|
foundBook = this.abbreviations[abbreviations[k]]
|
|
147
|
-
|
|
146
|
+
bookStartIndex = i
|
|
148
147
|
matchedLength = abbreviation.length
|
|
149
148
|
}
|
|
150
149
|
}
|
|
151
150
|
}
|
|
152
151
|
|
|
153
152
|
if (foundBook) {
|
|
153
|
+
console.log(`Found book: ${foundBook} at index ${bookStartIndex}, length ${matchedLength}`)
|
|
154
154
|
i += matchedLength
|
|
155
155
|
let chapterVerse = ""
|
|
156
156
|
const references = []
|
|
157
|
-
|
|
157
|
+
let refStartIndex = bookStartIndex // Start of reference (including book) in normalizedText
|
|
158
|
+
let originalRefStartIndex = bookStartIndex // Start in original text
|
|
158
159
|
|
|
159
160
|
while (i < normalizedText.length && isValidChapterVerseChar(normalizedText[i])) {
|
|
160
|
-
if (isNextBibleBook(i))
|
|
161
|
+
if (isNextBibleBook(i)) {
|
|
162
|
+
console.log(`Next book detected at index ${i}, stopping reference parsing`)
|
|
163
|
+
break
|
|
164
|
+
}
|
|
161
165
|
if (normalizedText[i] === ";") {
|
|
162
|
-
const formattedReference = chapterVerse.trim()
|
|
163
|
-
if (formattedReference)
|
|
166
|
+
const formattedReference = chapterVerse.trim()
|
|
167
|
+
if (formattedReference) {
|
|
168
|
+
const refEndIndex = i
|
|
169
|
+
references.push({
|
|
170
|
+
ref: formattedReference,
|
|
171
|
+
start: refStartIndex,
|
|
172
|
+
end: refEndIndex,
|
|
173
|
+
})
|
|
174
|
+
console.log(
|
|
175
|
+
`Reference found: ${formattedReference}, normalized indices ${refStartIndex}-${refEndIndex}`
|
|
176
|
+
)
|
|
177
|
+
}
|
|
164
178
|
chapterVerse = ""
|
|
179
|
+
refStartIndex = i + 1
|
|
180
|
+
const semicolonIndex = text.indexOf(";", originalRefStartIndex)
|
|
181
|
+
originalRefStartIndex = semicolonIndex !== -1 ? semicolonIndex + 1 : refStartIndex
|
|
165
182
|
i++
|
|
166
183
|
continue
|
|
167
184
|
}
|
|
@@ -170,25 +187,29 @@ class CodexParser {
|
|
|
170
187
|
}
|
|
171
188
|
|
|
172
189
|
if (chapterVerse.trim().length > 0) {
|
|
173
|
-
const formattedReference = chapterVerse.trim()
|
|
174
|
-
if (formattedReference)
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
190
|
+
const formattedReference = chapterVerse.trim()
|
|
191
|
+
if (formattedReference) {
|
|
192
|
+
const refEndIndex = i
|
|
193
|
+
references.push({
|
|
194
|
+
ref: formattedReference,
|
|
195
|
+
start: refStartIndex,
|
|
196
|
+
end: refEndIndex,
|
|
197
|
+
})
|
|
198
|
+
console.log(
|
|
199
|
+
`Final reference found: ${formattedReference}, normalized indices ${refStartIndex}-${refEndIndex}`
|
|
200
|
+
)
|
|
201
|
+
}
|
|
184
202
|
}
|
|
185
203
|
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
204
|
+
// Align indices with original text
|
|
205
|
+
const originalBookText = text.slice(bookStartIndex, bookStartIndex + matchedLength)
|
|
206
|
+
const originalBookStartIndex =
|
|
207
|
+
text.indexOf(originalBookText, bookStartIndex) !== -1
|
|
208
|
+
? text.indexOf(originalBookText, bookStartIndex)
|
|
209
|
+
: bookStartIndex
|
|
210
|
+
console.log(`Original book text: ${originalBookText}, original start index: ${originalBookStartIndex}`)
|
|
190
211
|
|
|
191
|
-
references.forEach((ref) => {
|
|
212
|
+
references.forEach(({ ref, start, end }, refIndex) => {
|
|
192
213
|
let type
|
|
193
214
|
if (ref.includes(":")) {
|
|
194
215
|
if (ref.includes("-")) {
|
|
@@ -212,14 +233,56 @@ class CodexParser {
|
|
|
212
233
|
type = "single_chapter"
|
|
213
234
|
}
|
|
214
235
|
|
|
236
|
+
// Construct full reference text for original text
|
|
237
|
+
const fullRefText = `${originalBookText} ${ref.replace(":", ".")}`
|
|
238
|
+
const suffixData = detectSuffix(end)
|
|
239
|
+
const suffix = suffixData ? suffixData.suffix : null
|
|
240
|
+
let refEndIndex = end
|
|
241
|
+
if (suffixData) {
|
|
242
|
+
refEndIndex += suffixData.length
|
|
243
|
+
i += suffixData.length // Skip suffix
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Map to original text
|
|
247
|
+
let originalStartIndex =
|
|
248
|
+
text.indexOf(fullRefText, originalRefStartIndex) !== -1
|
|
249
|
+
? text.indexOf(fullRefText, originalRefStartIndex)
|
|
250
|
+
: originalBookStartIndex
|
|
251
|
+
console.log(
|
|
252
|
+
`Searching for fullRefText: ${fullRefText} at index ${originalRefStartIndex}, found at ${originalStartIndex}`
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
let originalEndIndex = originalStartIndex + fullRefText.length
|
|
256
|
+
let originalText = text.slice(originalStartIndex, originalEndIndex)
|
|
257
|
+
|
|
258
|
+
// Adjust for suffix in original text
|
|
259
|
+
if (suffixData) {
|
|
260
|
+
originalEndIndex += suffixData.length
|
|
261
|
+
originalText = text.slice(originalStartIndex, originalEndIndex)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Trim trailing whitespace from originalText
|
|
265
|
+
while (originalEndIndex > originalStartIndex && /[\s]/.test(text[originalEndIndex - 1])) {
|
|
266
|
+
originalEndIndex--
|
|
267
|
+
originalText = text.slice(originalStartIndex, originalEndIndex)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
console.log(
|
|
271
|
+
`Reference ${
|
|
272
|
+
refIndex + 1
|
|
273
|
+
}: ${originalText}, original indices ${originalStartIndex}-${originalEndIndex}, type: ${type}, suffix: ${
|
|
274
|
+
suffix || "none"
|
|
275
|
+
}, search text: ${fullRefText}`
|
|
276
|
+
)
|
|
277
|
+
|
|
215
278
|
this.found.push({
|
|
216
279
|
book: foundBook,
|
|
217
280
|
reference: ref,
|
|
218
|
-
startIndex:
|
|
219
|
-
endIndex:
|
|
281
|
+
startIndex: originalStartIndex,
|
|
282
|
+
endIndex: originalEndIndex,
|
|
220
283
|
version: suffix || null,
|
|
221
284
|
type,
|
|
222
|
-
originalText:
|
|
285
|
+
originalText: originalText,
|
|
223
286
|
})
|
|
224
287
|
})
|
|
225
288
|
} else {
|
|
@@ -227,6 +290,7 @@ class CodexParser {
|
|
|
227
290
|
}
|
|
228
291
|
}
|
|
229
292
|
|
|
293
|
+
console.log(`Found references: ${JSON.stringify(this.found, null, 2)}`)
|
|
230
294
|
return this
|
|
231
295
|
}
|
|
232
296
|
|
|
@@ -276,6 +340,7 @@ class CodexParser {
|
|
|
276
340
|
|
|
277
341
|
// Clean reference for parsing
|
|
278
342
|
let cleanReference = passage.reference.replace(/\s*(LXX|MT)$/i, "").trim()
|
|
343
|
+
console.log(`Parsing reference: ${cleanReference}, type: ${passage.type}`)
|
|
279
344
|
if (cleanReference.endsWith(",")) {
|
|
280
345
|
cleanReference = cleanReference.slice(0, -1).trim()
|
|
281
346
|
}
|
|
@@ -283,6 +348,7 @@ class CodexParser {
|
|
|
283
348
|
// Handle book-only or empty references
|
|
284
349
|
if (!cleanReference && this.config.booksOnly) {
|
|
285
350
|
parsedPassage.type = "book_only"
|
|
351
|
+
console.log(`Book-only reference: ${book}`)
|
|
286
352
|
} else if (!cleanReference || cleanReference.match(/^\d+\s*[:;]?\s*$/)) {
|
|
287
353
|
const chapterMatch = cleanReference.match(/\d+/) || ["1"]
|
|
288
354
|
const chapter = Number(chapterMatch[0])
|
|
@@ -294,8 +360,18 @@ class CodexParser {
|
|
|
294
360
|
const endVerse = chapterVerses[chapterVerses.length - 1]
|
|
295
361
|
parsedPassage.verses = [`${startVerse}-${endVerse}`]
|
|
296
362
|
}
|
|
363
|
+
console.log(`Single chapter: ${chapter}, verses: ${parsedPassage.verses}`)
|
|
364
|
+
} else if (passage.type === "comma_separated_verses") {
|
|
365
|
+
// Handle comma-separated verses (e.g., "1:7,18")
|
|
366
|
+
const [chapter, verses] = cleanReference.split(":")
|
|
367
|
+
parsedPassage.chapter = Number(chapter)
|
|
368
|
+
parsedPassage.verses = verses.split(",").map((v) => v.trim())
|
|
369
|
+
console.log(`Comma-separated verses: chapter ${chapter}, verses ${parsedPassage.verses}`)
|
|
297
370
|
} else {
|
|
298
371
|
this.parseReferenceParts(parsedPassage, cleanReference)
|
|
372
|
+
console.log(
|
|
373
|
+
`Parsed with parseReferenceParts: chapter ${parsedPassage.chapter}, verses ${parsedPassage.verses}`
|
|
374
|
+
)
|
|
299
375
|
}
|
|
300
376
|
|
|
301
377
|
parsedPassage.passages = this.populate(parsedPassage)
|
|
@@ -315,6 +391,7 @@ class CodexParser {
|
|
|
315
391
|
} else {
|
|
316
392
|
parsedPassage.abbr = parsedPassage.original
|
|
317
393
|
}
|
|
394
|
+
console.log(`Abbreviation set: ${parsedPassage.abbr}`)
|
|
318
395
|
|
|
319
396
|
if (parsedPassage.type === this.MULTI_CHAPTER_RANGE) {
|
|
320
397
|
this.handleMultiChapterRange(parsedPassage, cleanReference)
|
|
@@ -340,6 +417,7 @@ class CodexParser {
|
|
|
340
417
|
chapter: lastPassage.chapter,
|
|
341
418
|
verse: lastPassage.verse,
|
|
342
419
|
}
|
|
420
|
+
console.log(`Start: ${JSON.stringify(parsedPassage.start)}, End: ${JSON.stringify(parsedPassage.end)}`)
|
|
343
421
|
}
|
|
344
422
|
|
|
345
423
|
if (!parsedPassage.version) {
|
|
@@ -354,9 +432,9 @@ class CodexParser {
|
|
|
354
432
|
})
|
|
355
433
|
|
|
356
434
|
this.versification()
|
|
435
|
+
console.log(`Final passages: ${JSON.stringify(this.passages, null, 2)}`)
|
|
357
436
|
return this
|
|
358
437
|
}
|
|
359
|
-
|
|
360
438
|
/**
|
|
361
439
|
* Parses reference parts into chapter and verse components.
|
|
362
440
|
* @param {Object} passage - The passage object to populate.
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// Versification mappings for Malachi, aligning ESV (English), BHS (Masoretic Text), and Göttingen LXX (Septuagint).
|
|
2
|
+
// ESV uses 4 chapters, with 4:1–6 corresponding to BHS 3:19–24 and LXX 3:19–24.
|
|
3
|
+
// Note: LXX reorders verses 4–6: ESV 4:4 = LXX 3:24, ESV 4:5 = LXX 3:22, ESV 4:6 = LXX 3:23.
|
|
4
|
+
// Chapters 1–3 have identical versification across all three (e.g., 1:1 = 1:1).
|
|
1
5
|
module.exports = {
|
|
2
6
|
"4:1": {
|
|
3
7
|
lxx: "3:19",
|
|
@@ -15,17 +19,17 @@ module.exports = {
|
|
|
15
19
|
eng: "4:3",
|
|
16
20
|
},
|
|
17
21
|
"4:4": {
|
|
18
|
-
lxx: "3:
|
|
22
|
+
lxx: "3:24", // Law of Moses (matches BHS 3:22 content)
|
|
19
23
|
mt: "3:22",
|
|
20
24
|
eng: "4:4",
|
|
21
25
|
},
|
|
22
26
|
"4:5": {
|
|
23
|
-
lxx: "3:
|
|
27
|
+
lxx: "3:22", // Elijah’s coming (matches BHS 3:23 content)
|
|
24
28
|
mt: "3:23",
|
|
25
29
|
eng: "4:5",
|
|
26
30
|
},
|
|
27
31
|
"4:6": {
|
|
28
|
-
lxx: "3:
|
|
32
|
+
lxx: "3:23", // Hearts restored (matches BHS 3:24 content)
|
|
29
33
|
mt: "3:24",
|
|
30
34
|
eng: "4:6",
|
|
31
35
|
},
|