codexparser 0.3.1 → 0.4.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 +36 -0
- package/README.md +94 -2
- package/RELEASE_NOTES_v0.4.0.md +94 -0
- package/package.json +11 -1
- package/src/core/CodexParser.js +21 -0
- package/src/core/ReferenceParser.js +129 -26
- package/src/data/chapter_verses/daniel.js +4 -0
- package/src/data/chapter_verses/esther.js +12 -1
- package/src/data/lxx-editions.js +58 -0
- package/src/data/versifications/1samuel.js +1 -1
- package/src/data/versifications/2kings.js +117 -0
- package/src/data/versifications/2samuel.js +1 -1
- package/src/data/versifications/daniel.js +28 -0
- package/src/data/versifications/esther.js +114 -0
- package/src/data/versifications/ezekiel.js +37 -37
- package/src/data/versifications/genesis.js +37 -39
- package/src/data/versifications/micah.js +19 -16
- package/src/data/versifications/numbers.js +83 -1
- package/src/data/versifications/psalms.js +12 -12
- package/src/data/versified.js +6 -1
- package/.trunk/configs/.markdownlint.yaml +0 -2
- package/.trunk/trunk.yaml +0 -32
- package/REFACTORING.md +0 -214
- package/RELEASE_NOTES_v0.2.0.md +0 -5
- package/RELEASE_NOTES_v0.3.0.md +0 -32
- package/bibles/kjv.json +0 -194080
- package/bibles/updated_kjv.json +0 -194080
- package/passage-generator.js +0 -25
- package/src/CodexParser.js.backup +0 -1713
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented here. For full details, see the Release Notes in README and the GitHub Releases page.
|
|
4
4
|
|
|
5
|
+
## 0.4.0 — 2026-04-28
|
|
6
|
+
|
|
7
|
+
LXX versification audit + structural fixes. Major data-correctness pass against authoritative sources (Hanhart's Göttingen Esther, Rahlfs-Hanhart 2006, Göttingen Theodotion Daniel/Susanna/Bel) verified via Logos library.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `parser.edition("rahlfs" | "auto")` — selects LXX edition. Default `"auto"` uses Göttingen where attested per `src/data/lxx-editions.js` and Rahlfs elsewhere.
|
|
12
|
+
- `passage.convertVersion(target, { edition })` — per-call edition override.
|
|
13
|
+
- `passage.getLXXRahlfs()` helper.
|
|
14
|
+
- `ReferenceParser.expandVersificationValue(value)` — parses `"ch:v"`, `"ch:v1-v2"`, `"ch:v[a-z]"`, returns empty array for malformed/empty input. Handles all shapes used in the data files without producing NaN.
|
|
15
|
+
- `verseSuffix` propagated through `scripture.cv`, `verses[]`, `to.verses[]`, `original`, and `abbr` so converting `Esther 11:2 ENG → LXX` outputs `Esther 1:1a` (was previously losing the letter suffix).
|
|
16
|
+
- `cloned.missingPassages` array on conversions when verses don't exist in the target version (e.g., 1 Kgs 4:21 LXX="").
|
|
17
|
+
- `src/data/lxx-editions.js` — registry of which OT books are in Göttingen vs. Rahlfs-only. Update as new Göttingen volumes ship.
|
|
18
|
+
- New versification files: `2kings.js` (ENG 11:21 = MT/LXX 12:1; ENG 12:1-21 = MT/LXX 12:2-22), `esther.js` (Hanhart-verified Vulgate ↔ Rahlfs letter-suffix mapping for Additions A-F).
|
|
19
|
+
- New `chapter_verses` extensions: Daniel 13 (Susanna, 64 v) + 14 (Bel, 42 v); Esther 10 extended through 16 for Vulgate/RSV-Apocrypha numbering of additions.
|
|
20
|
+
- `Song of Solomon` registered as an alias of `Song of Songs` in `versified.js`.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **MT-side versification bugs (116 entries):** entries that incorrectly left `mt = eng` when MT actually shifts. Verified by cross-checking against BHS in MongoDB.
|
|
25
|
+
- `Genesis 31:55-32:32` (33 entries): ENG 31:55 = MT 32:1, ENG 32:N = MT 32:N+1
|
|
26
|
+
- `1 Samuel 23:29` (1 entry): ENG 23:29 = MT 24:1
|
|
27
|
+
- `2 Samuel 18:33-19:43` (44 entries): ENG 18:33 = MT 19:1, ENG 19:N = MT 19:N+1
|
|
28
|
+
- `Psalms 92:0` (1 entry): ENG title = MT 92:1
|
|
29
|
+
- `Ezekiel 20:45-49` (5 entries): ENG 20:N = MT 21:N-44
|
|
30
|
+
- `Ezekiel 21:1-32` (32 entries): ENG 21:N = MT 21:N+5
|
|
31
|
+
- `numbers.js`: replaced corrupted final entry (`29:40 → lxx:"26:48"`); added Num 30:1-16 ENG = MT/LXX 30:2-17.
|
|
32
|
+
- `micah.js`: full rewrite. Was off by one on MT and unshifted on LXX. ENG 5:1 = MT/LXX 4:14, ENG 5:2-15 = MT/LXX 5:1-14.
|
|
33
|
+
- `psalms.js`: fixed Ps 147:10-20 boundary. Was at 9/10; correct boundary is at 11/12 (ENG 147:1-11 = LXX 146:1-11; ENG 147:12-20 = LXX 147:1-9).
|
|
34
|
+
- `genesis.js`: removed no-op `35:16` entry; corrected `35:21` to `lxx: ""` (verse missing in LXX).
|
|
35
|
+
- `expandVersificationValue` correctly handles range strings (e.g., Gen 31:48 LXX `"31:47-48"`) by emitting one sub-passage per verse.
|
|
36
|
+
|
|
37
|
+
### Tests
|
|
38
|
+
|
|
39
|
+
- 77 assertion-based tests in `tests/lxx-versification-audit.test.js` covering `expandVersificationValue`, all the fixed entries, suffix preservation, edition switching, and the MT-side fixes. All passing.
|
|
40
|
+
|
|
5
41
|
## 0.3.0 — 2026-01-10
|
|
6
42
|
|
|
7
43
|
- Added `convertVersion(targetVersion)` method on passage objects for versification conversion.
|
package/README.md
CHANGED
|
@@ -10,10 +10,12 @@ Built with precision and passion, CodexParser handles single verses, ranges, mul
|
|
|
10
10
|
|
|
11
11
|
## Features 🌟
|
|
12
12
|
|
|
13
|
-
- **Parse Any Reference**: From "Jn 3:16" to "Psalm 115:5,7,10", it
|
|
13
|
+
- **Parse Any Reference**: From "Jn 3:16" to "Psalm 115:5,7,10", it's got you covered.
|
|
14
14
|
- **Structured Output**: Get book, chapter, verses, testament, start/end points, SBL abbreviations, and versification data in a clean object.
|
|
15
15
|
- **SBL Abbreviations**: Formatted references (e.g., "Ps. 115:5, 7, 10", "Gen. 1:1–3") with periods, en dashes for ranges, and commas with spaces for separated verses.
|
|
16
|
-
- **Versification Support**: Handles differences between English, LXX, and MT texts
|
|
16
|
+
- **Versification Support**: Handles differences between English, LXX, and MT texts — including the Greek additions to Esther (A–F) and Daniel (Susanna ch 13, Bel ch 14).
|
|
17
|
+
- **Göttingen vs. Rahlfs Editions**: Default to Göttingen where attested, fall back to Rahlfs elsewhere; switch with `parser.edition("rahlfs")`.
|
|
18
|
+
- **Letter-Suffixed Verses**: Hanhart's Esther additions (`1:1a`–`1:1s`, `4:17a`–`4:17z`, etc.) round-trip through `convertVersion` with `verseSuffix` carried through `scripture.cv`.
|
|
17
19
|
- **Validation**: Checks if verses exist, with detailed error messages for invalid references.
|
|
18
20
|
- **Combine Passages**: Merge multiple references into a single, cohesive range.
|
|
19
21
|
- **Chainable API**: Fluent, intuitive method chaining for a smooth workflow.
|
|
@@ -157,6 +159,55 @@ Notes:
|
|
|
157
159
|
- `getVersion("eng"|"lxx"|"mt"|"bhs")` is available; `getBHS()` aliases `MT`.
|
|
158
160
|
- `.scripture.hash` is OSIS textual (e.g., `John.3.16`), `.osisNumeric` uses pythonbible-style integer IDs.
|
|
159
161
|
|
|
162
|
+
### Editions: Göttingen vs. Rahlfs
|
|
163
|
+
|
|
164
|
+
CodexParser defaults to **Göttingen** versification where the critical edition exists and falls back to **Rahlfs** where it doesn't (per `src/data/lxx-editions.js`). Switch with `.edition()`:
|
|
165
|
+
|
|
166
|
+
```javascript
|
|
167
|
+
const parser = new CodexParser()
|
|
168
|
+
|
|
169
|
+
// Default: "auto" (Göttingen-where-attested, Rahlfs-elsewhere)
|
|
170
|
+
const [a] = parser.parse("Genesis 31:55").getPassages()
|
|
171
|
+
console.log(a.getLXX().scripture.cv) // "32:1" (Wevers Genesis)
|
|
172
|
+
|
|
173
|
+
// Force Rahlfs across the whole parser
|
|
174
|
+
parser.edition("rahlfs")
|
|
175
|
+
const [b] = parser.parse("Genesis 31:55").getPassages()
|
|
176
|
+
console.log(b.getLXX().scripture.cv) // same here; would differ in books with `lxxRahlfs` overrides
|
|
177
|
+
|
|
178
|
+
// Per-call override (no need to switch the whole parser)
|
|
179
|
+
const [c] = parser.parse("Esther 1:1").getPassages()
|
|
180
|
+
console.log(c.convertVersion("lxx", { edition: "rahlfs" }).scripture.cv)
|
|
181
|
+
console.log(c.getLXXRahlfs().scripture.cv) // helper equivalent
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Greek additions: Esther A–F and Daniel 13/14
|
|
185
|
+
|
|
186
|
+
`Esther 11:2`–`16:24` (Vulgate / RSV-Apocrypha numbering) and `Daniel 13`/`14` (Susanna, Bel & the Dragon) parse and convert. Hanhart's letter-suffixed positions (`1:1a`, `4:17a`, `8:12x`, …) are carried through `verseSuffix`.
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
const parser = new CodexParser()
|
|
190
|
+
|
|
191
|
+
// Esther Addition A: ENG 11:2 -> LXX 1:1a
|
|
192
|
+
const [e] = parser.parse("Esther 11:2").getPassages()
|
|
193
|
+
const eLxx = e.convertVersion("lxx")
|
|
194
|
+
console.log(eLxx.scripture.cv) // "1:1a"
|
|
195
|
+
console.log(eLxx.passages[0].verseSuffix) // "a"
|
|
196
|
+
console.log(eLxx.passages[0].chapter, eLxx.passages[0].verse) // 1, 1
|
|
197
|
+
|
|
198
|
+
// Susanna (Daniel 13) parses canonically
|
|
199
|
+
const [s] = parser.parse("Daniel 13:1").getPassages()
|
|
200
|
+
console.log(s.valid) // true
|
|
201
|
+
const sMt = s.convertVersion("mt")
|
|
202
|
+
console.log(sMt.missingPassages?.length) // 1 (LXX-only chapter)
|
|
203
|
+
|
|
204
|
+
// Range / missing-in-target verses don't NaN-out
|
|
205
|
+
const [g] = parser.parse("Genesis 31:48").getPassages()
|
|
206
|
+
console.log(g.getLXX().scripture.cv) // "31:47-48" (range)
|
|
207
|
+
const [k] = parser.parse("1 Kings 4:21").getPassages()
|
|
208
|
+
console.log(k.getLXX().missingPassages[0].missingIn) // "lxx"
|
|
209
|
+
```
|
|
210
|
+
|
|
160
211
|
---
|
|
161
212
|
|
|
162
213
|
## API: Your Codex Arsenal 🛠️
|
|
@@ -189,6 +240,24 @@ Here’s the breakdown of CodexParser’s key methods—your tools for mastering
|
|
|
189
240
|
- **Returns**: The parser instance for chaining.
|
|
190
241
|
- **Example**: `parser.bibleVersion("lxx").parse("Psalm 115:5,7,10");`
|
|
191
242
|
|
|
243
|
+
### `.edition(edition)`
|
|
244
|
+
|
|
245
|
+
- **What it does**: Selects the LXX edition. `"auto"` (default) uses Göttingen where attested per `src/data/lxx-editions.js` and Rahlfs elsewhere. `"rahlfs"` forces Rahlfs versification universally and consults `lxxRahlfs` overrides where present.
|
|
246
|
+
- **Args**: `edition` (string) - `"auto"` or `"rahlfs"`.
|
|
247
|
+
- **Returns**: The parser instance for chaining.
|
|
248
|
+
- **Example**: `parser.edition("rahlfs").parse("Esther 11:2");`
|
|
249
|
+
|
|
250
|
+
### `passage.convertVersion(target, options?)` / `passage.getLXXRahlfs()`
|
|
251
|
+
|
|
252
|
+
- **What it does**: Converts a parsed passage to another versification. `target` is `"eng"`, `"lxx"`, `"mt"`, or `"bhs"`. The optional `options.edition` (`"rahlfs"` or `"auto"`) overrides the parser-level edition for this single call. `getLXXRahlfs()` is a shortcut for `convertVersion("lxx", { edition: "rahlfs" })`.
|
|
253
|
+
- **Returns**: A cloned passage with `chapter` / `verse` / `verseSuffix` remapped, plus `cloned.missingPassages` listing any sub-passages that don't exist in the target version.
|
|
254
|
+
- **Example**:
|
|
255
|
+
```javascript
|
|
256
|
+
const [p] = parser.parse("Esther 11:2").getPassages()
|
|
257
|
+
p.convertVersion("lxx").scripture.cv // "1:1a"
|
|
258
|
+
p.convertVersion("mt").missingPassages.length // 1 (Add A is LXX-only)
|
|
259
|
+
```
|
|
260
|
+
|
|
192
261
|
### `.getPassages()`
|
|
193
262
|
|
|
194
263
|
- **What it does**: Returns an array of parsed passage objects with handy methods like `.first()`, `.oldTestament()`, `.newTestament()`, and `.combine()`.
|
|
@@ -304,6 +373,29 @@ Let’s parse the scriptures together—happy coding! ✝️📚
|
|
|
304
373
|
|
|
305
374
|
## Release Notes
|
|
306
375
|
|
|
376
|
+
### 0.4.0 (2026-04-28)
|
|
377
|
+
|
|
378
|
+
LXX versification audit + structural fixes. Major data-correctness pass against authoritative sources (Hanhart's Göttingen Esther, Rahlfs-Hanhart 2006, Göttingen Theodotion Susanna/Daniel/Bel) verified against a Logos library extract.
|
|
379
|
+
|
|
380
|
+
- New `parser.edition("rahlfs"|"auto")` setter and `passage.convertVersion(target, { edition })` per-call override; `passage.getLXXRahlfs()` helper.
|
|
381
|
+
- `ReferenceParser.expandVersificationValue()` parses `"ch:v"`, `"ch:v1-v2"`, `"ch:v[a-z]"`, and rejects empty/malformed input cleanly. The previously-broken cases (Gen 31:48 LXX `"31:47-48"`, 1 Kgs 4:21 LXX `""`, Isa 64:1 MT `"63:19b"`, Dan 3:24a) all parse correctly.
|
|
382
|
+
- `verseSuffix` is now carried through `scripture.cv`, `verses[]`, `original`, and `abbr` so `Esther 11:2 ENG → LXX` produces `Esther 1:1a` (not `1:1`).
|
|
383
|
+
- `cloned.missingPassages` is populated when a verse doesn't exist in the conversion target (e.g., 1 Kgs 4:21 LXX, Daniel 13 in MT).
|
|
384
|
+
- New `src/data/lxx-editions.js` registry; new `versifications/2kings.js`, `versifications/esther.js` (Hanhart-verified Vulgate ↔ Rahlfs Add A–F mapping); `chapter_verses` extended for Daniel 13/14 (Susanna + Bel) and Esther 10–16 (Vulgate apocrypha layout); `Song of Solomon` aliased to `Song of Songs`.
|
|
385
|
+
- **116 MT-side bugs fixed** across Genesis 31:55–32:32, 1 Samuel 23:29, 2 Samuel 18:33–19:43, Psalms 92:0, Ezekiel 20:45–21:32 — entries that incorrectly left `mt = eng` when MT actually shifts. Verified against BHS data.
|
|
386
|
+
- `numbers.js` corrupted final entry replaced + Num 30 added; `micah.js` rewritten; `psalms.js` Ps 147 boundary corrected from 9/10 to 11/12; `genesis.js` 35:16 no-op removed.
|
|
387
|
+
- 77 assertion-based tests in `tests/lxx-versification-audit.test.js`. All passing.
|
|
388
|
+
- Published to npm as `codexparser@0.4.0`.
|
|
389
|
+
|
|
390
|
+
### 0.3.0 (2026-01-10)
|
|
391
|
+
|
|
392
|
+
- Added `convertVersion(targetVersion)` method on passage objects for versification conversion.
|
|
393
|
+
- Accepts version string: `"eng"`, `"lxx"`, `"mt"`, or `"bhs"` (alias for MT).
|
|
394
|
+
- Automatically converts chapter/verse references between versifications when versification data exists.
|
|
395
|
+
- Returns same reference with updated version metadata if no versification exists.
|
|
396
|
+
- Tested with Psalms, Zechariah, and NT passages.
|
|
397
|
+
- Published to npm as `codexparser@0.3.0`.
|
|
398
|
+
|
|
307
399
|
### 0.2.0 (2026-01-10)
|
|
308
400
|
|
|
309
401
|
- Refactored internal architecture into clear folders:
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# CodexParser 0.4.0
|
|
2
|
+
|
|
3
|
+
LXX versification audit + structural fixes. Major data-correctness pass against authoritative sources (Hanhart's Göttingen Esther 1983, Rahlfs-Hanhart 2006, Göttingen Theodotion Susanna/Daniel/Bel 1999) verified against a Logos library extract.
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
|
|
7
|
+
- **Göttingen vs. Rahlfs editions are now selectable.** Default is "auto" (Göttingen where attested, Rahlfs elsewhere); call `parser.edition("rahlfs")` to force Rahlfs everywhere, or pass `{ edition: "rahlfs" }` to `convertVersion`.
|
|
8
|
+
- **Esther's Greek additions A–F now round-trip cleanly.** ENG `Esther 11:2` → LXX `Esther 1:1a` (with `verseSuffix: "a"` carried through `scripture.cv`, `verses[]`, `original`, and `abbr`).
|
|
9
|
+
- **Daniel 13 (Susanna) and 14 (Bel & the Dragon) added** to `chapter_verses` and `versifications/daniel.js` (Theodotion).
|
|
10
|
+
- **Letter-suffixed verses no longer produce NaN** in `convertVersion`. The previously-broken cases (Gen 31:48 LXX `"31:47-48"`, 1 Kgs 4:21 LXX `""`, Isa 64:1 MT `"63:19b"`, Dan 3:24a) all parse correctly via the new `ReferenceParser.expandVersificationValue()` helper.
|
|
11
|
+
- **116 MT-side versification bugs fixed** across Genesis, 1 Samuel, 2 Samuel, Psalms, and Ezekiel — entries that incorrectly left `mt = eng` when MT actually shifts. Cross-verified against BHS data.
|
|
12
|
+
- **Ps 147 boundary fixed** at 11/12 (was 9/10): ENG 147:1-11 = LXX 146:1-11; ENG 147:12-20 = LXX 147:1-9.
|
|
13
|
+
- **Micah versification rewritten** (was off by one on MT and unshifted on LXX): ENG 5:1 = MT/LXX 4:14, ENG 5:2-15 = MT/LXX 5:1-14.
|
|
14
|
+
|
|
15
|
+
## What's new in the API
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
const CodexParser = require("codexparser")
|
|
19
|
+
|
|
20
|
+
// 1. Edition selection
|
|
21
|
+
const parser = new CodexParser().edition("rahlfs") // force Rahlfs
|
|
22
|
+
const auto = new CodexParser() // auto (default)
|
|
23
|
+
|
|
24
|
+
// 2. Per-call edition override
|
|
25
|
+
const [p] = parser.parse("Genesis 31:55").getPassages()
|
|
26
|
+
const lxxR = p.convertVersion("lxx", { edition: "rahlfs" })
|
|
27
|
+
const lxxR2 = p.getLXXRahlfs() // helper
|
|
28
|
+
|
|
29
|
+
// 3. Esther additions with letter suffix
|
|
30
|
+
const [e] = parser.parse("Esther 11:2").getPassages() // Add A
|
|
31
|
+
const lxx = e.convertVersion("lxx")
|
|
32
|
+
console.log(lxx.scripture.cv) // "1:1a"
|
|
33
|
+
console.log(lxx.passages[0].verseSuffix) // "a"
|
|
34
|
+
|
|
35
|
+
// 4. Daniel 13 (Susanna) parses canonically
|
|
36
|
+
const [s] = parser.parse("Daniel 13:1").getPassages() // valid: true
|
|
37
|
+
const mt = s.convertVersion("mt") // missingPassages: chapter LXX-only
|
|
38
|
+
|
|
39
|
+
// 5. Range/letter values handled cleanly
|
|
40
|
+
const [g] = parser.parse("Genesis 31:48").getPassages()
|
|
41
|
+
const gLxx = g.convertVersion("lxx")
|
|
42
|
+
console.log(gLxx.scripture.cv) // "31:47-48"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## What got fixed in the data
|
|
46
|
+
|
|
47
|
+
### MT-side bugs (116 entries)
|
|
48
|
+
|
|
49
|
+
Each of these entries previously had `mt: "<ENG-key>"` (treating MT = ENG) when MT actually has an offset. Verified by checking against BHS texts.
|
|
50
|
+
|
|
51
|
+
| Book | Range | Pattern |
|
|
52
|
+
|------|-------|---------|
|
|
53
|
+
| Genesis | 31:55-32:32 | ENG 31:55 = MT 32:1; ENG 32:N = MT 32:N+1 |
|
|
54
|
+
| 1 Samuel | 23:29 | ENG 23:29 = MT 24:1 |
|
|
55
|
+
| 2 Samuel | 18:33-19:43 | ENG 18:33 = MT 19:1; ENG 19:N = MT 19:N+1 |
|
|
56
|
+
| Psalms | 92:0 | ENG title = MT 92:1 |
|
|
57
|
+
| Ezekiel | 20:45-49 | ENG 20:N = MT 21:N-44 |
|
|
58
|
+
| Ezekiel | 21:1-32 | ENG 21:N = MT 21:N+5 |
|
|
59
|
+
|
|
60
|
+
### Other corrections
|
|
61
|
+
|
|
62
|
+
- `numbers.js`: replaced corrupted final entry; added Num 30:1-16 ENG = MT/LXX 30:2-17.
|
|
63
|
+
- `micah.js`: full rewrite (every entry was wrong).
|
|
64
|
+
- `psalms.js`: fixed Ps 147 boundary 9/10 → 11/12.
|
|
65
|
+
- `genesis.js`: removed no-op `35:16` entry; corrected `35:21` to `lxx: ""` (verse missing in LXX).
|
|
66
|
+
|
|
67
|
+
## New data files
|
|
68
|
+
|
|
69
|
+
- `src/data/lxx-editions.js` — registry mapping each OT book to its current Göttingen attestation (`"gottingen"` or `"rahlfs"`). Update as new Göttingen volumes ship.
|
|
70
|
+
- `src/data/versifications/2kings.js` — ENG 11:21 = MT/LXX 12:1; ENG 12:1-21 = MT/LXX 12:2-22.
|
|
71
|
+
- `src/data/versifications/esther.js` — Hanhart-verified Vulgate ↔ Rahlfs letter-suffix mapping for Additions A–F.
|
|
72
|
+
- `src/data/chapter_verses/daniel.js` extended with Susanna (ch 13, 64 v) and Bel & Dragon (ch 14, 42 v).
|
|
73
|
+
- `src/data/chapter_verses/esther.js` extended through chapter 16 (Vulgate / RSV-Apocrypha layout).
|
|
74
|
+
- `Song of Solomon` registered as an alias of `Song of Songs` in `versified.js`.
|
|
75
|
+
|
|
76
|
+
## New return-shape fields
|
|
77
|
+
|
|
78
|
+
- **`passage.passages[i].verseSuffix`** — letter suffix when present (`"a"`, `"s"`, `"ee"`, etc.).
|
|
79
|
+
- **`passage.edition`** — `"auto"` or `"rahlfs"`, the resolved edition for this passage.
|
|
80
|
+
- **`cloned.missingPassages`** — array of sub-passages that don't exist in the conversion target (e.g., 1 Kgs 4:21 LXX, Daniel 13 in MT).
|
|
81
|
+
|
|
82
|
+
## Tests
|
|
83
|
+
|
|
84
|
+
77 assertion-based tests in `tests/lxx-versification-audit.test.js` covering `expandVersificationValue`, every fixed entry, suffix preservation, edition switching, and the MT-side fixes. All passing. Run with:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
node tests/lxx-versification-audit.test.js
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Breaking changes
|
|
91
|
+
|
|
92
|
+
- `convertVersion` now returns `cloned.missingPassages` for verses that don't exist in the target version, where previously those would surface as NaN-valued `chapter`/`verse`. Callers that relied on NaN will need to handle the new `missingPassages` array instead.
|
|
93
|
+
|
|
94
|
+
See full CHANGELOG: https://github.com/jeremyam/CodexParser/blob/main/CHANGELOG.md
|
package/package.json
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codexparser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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
|
+
"files": [
|
|
7
|
+
"index.js",
|
|
8
|
+
"src/index.js",
|
|
9
|
+
"src/core/",
|
|
10
|
+
"src/data/",
|
|
11
|
+
"src/format/",
|
|
12
|
+
"src/utils/",
|
|
13
|
+
"CHANGELOG.md",
|
|
14
|
+
"RELEASE_NOTES_v0.4.0.md"
|
|
15
|
+
],
|
|
6
16
|
"scripts": {
|
|
7
17
|
"test": "echo \"Error: no automated test runner configured\" && exit 0"
|
|
8
18
|
},
|
package/src/core/CodexParser.js
CHANGED
|
@@ -39,6 +39,7 @@ class CodexParser {
|
|
|
39
39
|
booksOnly: config.booksOnly ?? false,
|
|
40
40
|
invalid_sequence_strategy: config.invalid_sequence_strategy ?? "include",
|
|
41
41
|
invalid_passage_strategy: config.invalid_passage_strategy ?? "include",
|
|
42
|
+
edition: CodexParser.#normalizeEdition(config.edition),
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
this.#scanner = new ScriptureScanner(this.#config)
|
|
@@ -134,6 +135,7 @@ class CodexParser {
|
|
|
134
135
|
booksOnly: config.booksOnly ?? this.#config.booksOnly,
|
|
135
136
|
invalid_sequence_strategy: config.invalid_sequence_strategy ?? this.#config.invalid_sequence_strategy,
|
|
136
137
|
invalid_passage_strategy: config.invalid_passage_strategy ?? this.#config.invalid_passage_strategy,
|
|
138
|
+
edition: config.edition !== undefined ? CodexParser.#normalizeEdition(config.edition) : this.#config.edition,
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
// Update scanner and parser configs
|
|
@@ -143,6 +145,25 @@ class CodexParser {
|
|
|
143
145
|
return this
|
|
144
146
|
}
|
|
145
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Sets the LXX edition preference. "auto" (default) uses Göttingen where
|
|
150
|
+
* attested per src/data/lxx-editions.js and falls back to Rahlfs elsewhere.
|
|
151
|
+
* "rahlfs" forces Rahlfs versification universally.
|
|
152
|
+
* @param {string} edition - "auto" | "rahlfs"
|
|
153
|
+
* @returns {CodexParser}
|
|
154
|
+
*/
|
|
155
|
+
edition(edition) {
|
|
156
|
+
this.#config.edition = CodexParser.#normalizeEdition(edition)
|
|
157
|
+
this.#parser = new ReferenceParser(this.#config)
|
|
158
|
+
return this
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
static #normalizeEdition(value) {
|
|
162
|
+
if (value == null) return "auto"
|
|
163
|
+
const v = String(value).toLowerCase()
|
|
164
|
+
return v === "rahlfs" ? "rahlfs" : "auto"
|
|
165
|
+
}
|
|
166
|
+
|
|
146
167
|
/**
|
|
147
168
|
* Retrieves available verses for a given book and chapter (legacy method)
|
|
148
169
|
* @param {string} book - The book name
|
|
@@ -31,6 +31,39 @@ class ReferenceParser {
|
|
|
31
31
|
this.#config = config
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Parses a versification value string into one or more {chapter, verse, suffix?} entries.
|
|
36
|
+
* Accepts:
|
|
37
|
+
* - "ch:v" → [{chapter, verse}]
|
|
38
|
+
* - "ch:v1-v2" → expanded to one entry per verse in range
|
|
39
|
+
* - "ch:v[a-z]" → [{chapter, verse, suffix}]
|
|
40
|
+
* Returns [] for unparseable input.
|
|
41
|
+
*/
|
|
42
|
+
static expandVersificationValue(value) {
|
|
43
|
+
if (typeof value !== "string" || !value.includes(":")) return []
|
|
44
|
+
const [chPart, vPart] = value.split(":")
|
|
45
|
+
const chapter = Number(chPart)
|
|
46
|
+
if (!Number.isFinite(chapter)) return []
|
|
47
|
+
|
|
48
|
+
// Range form "v1-v2" (no letter suffixes inside ranges)
|
|
49
|
+
if (vPart.includes("-")) {
|
|
50
|
+
const [a, b] = vPart.split("-").map((s) => Number(s.trim()))
|
|
51
|
+
if (!Number.isFinite(a) || !Number.isFinite(b) || b < a) return []
|
|
52
|
+
const out = []
|
|
53
|
+
for (let v = a; v <= b; v++) out.push({ chapter, verse: v })
|
|
54
|
+
return out
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Letter-suffix form "19b"
|
|
58
|
+
const match = vPart.match(/^(\d+)([a-zA-Z]+)?$/)
|
|
59
|
+
if (!match) return []
|
|
60
|
+
const verse = Number(match[1])
|
|
61
|
+
if (!Number.isFinite(verse)) return []
|
|
62
|
+
const entry = { chapter, verse }
|
|
63
|
+
if (match[2]) entry.suffix = match[2]
|
|
64
|
+
return [entry]
|
|
65
|
+
}
|
|
66
|
+
|
|
34
67
|
/**
|
|
35
68
|
* Parses found references into structured passage objects
|
|
36
69
|
* @param {Array} foundReferences - Array of found references from scanner
|
|
@@ -53,6 +86,7 @@ class ReferenceParser {
|
|
|
53
86
|
endIndex: reference.endIndex,
|
|
54
87
|
originalText: reference.originalText,
|
|
55
88
|
version: VersionHandler.getVersion(reference.version || currentVersion, testament),
|
|
89
|
+
edition: this.#config.edition || "auto",
|
|
56
90
|
passages: [],
|
|
57
91
|
scripture: null,
|
|
58
92
|
valid: true,
|
|
@@ -128,19 +162,65 @@ class ReferenceParser {
|
|
|
128
162
|
*/
|
|
129
163
|
#attachVersionHelpers(passage) {
|
|
130
164
|
const self = this
|
|
131
|
-
const computeConverted = (srcPassage, targetAbbr) => {
|
|
165
|
+
const computeConverted = (srcPassage, targetAbbr, options = {}) => {
|
|
132
166
|
const versionObj = VersionHandler.getVersionObject(targetAbbr)
|
|
133
167
|
const cloned = JSON.parse(JSON.stringify(srcPassage))
|
|
134
168
|
cloned.version = versionObj
|
|
135
169
|
|
|
136
|
-
//
|
|
170
|
+
// Resolve which LXX edition to consult. "rahlfs" prefers
|
|
171
|
+
// versification.lxxRahlfs when present; "auto" (default) uses the
|
|
172
|
+
// canonical lxx field, which by convention holds Göttingen where
|
|
173
|
+
// attested and Rahlfs elsewhere (see src/data/lxx-editions.js).
|
|
174
|
+
const edition =
|
|
175
|
+
options.edition != null
|
|
176
|
+
? String(options.edition).toLowerCase()
|
|
177
|
+
: srcPassage.edition || "auto"
|
|
178
|
+
|
|
179
|
+
const resolveTargetKey = (versification) => {
|
|
180
|
+
if (targetAbbr === "lxx" && edition === "rahlfs" && versification.lxxRahlfs !== undefined) {
|
|
181
|
+
return "lxxRahlfs"
|
|
182
|
+
}
|
|
183
|
+
return targetAbbr
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Remap chapters/verses according to versification, if present.
|
|
187
|
+
// Versification values may be strict "ch:v", a range "ch:v1-v2",
|
|
188
|
+
// a letter-suffixed verse "ch:v[a-z]", or "" / null when the verse
|
|
189
|
+
// does not exist in the target version. We expand ranges and split
|
|
190
|
+
// missing verses into cloned.missingPassages so summary fields
|
|
191
|
+
// never contain NaN.
|
|
192
|
+
const remapped = []
|
|
193
|
+
const missing = []
|
|
137
194
|
cloned.passages.forEach((sub) => {
|
|
138
|
-
if (sub.versification
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
sub.verse = v
|
|
195
|
+
if (!sub.versification) {
|
|
196
|
+
remapped.push(sub)
|
|
197
|
+
return
|
|
142
198
|
}
|
|
199
|
+
const key = resolveTargetKey(sub.versification)
|
|
200
|
+
if (!(key in sub.versification)) {
|
|
201
|
+
remapped.push(sub)
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
const target = sub.versification[key]
|
|
205
|
+
if (target == null || target === "") {
|
|
206
|
+
missing.push({ ...sub, missingIn: targetAbbr })
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
const expanded = ReferenceParser.expandVersificationValue(target)
|
|
210
|
+
if (expanded.length === 0) {
|
|
211
|
+
remapped.push(sub)
|
|
212
|
+
return
|
|
213
|
+
}
|
|
214
|
+
expanded.forEach((entry, idx) => {
|
|
215
|
+
const next = idx === 0 ? sub : JSON.parse(JSON.stringify(sub))
|
|
216
|
+
next.chapter = entry.chapter
|
|
217
|
+
next.verse = entry.verse
|
|
218
|
+
if (entry.suffix) next.verseSuffix = entry.suffix
|
|
219
|
+
remapped.push(next)
|
|
220
|
+
})
|
|
143
221
|
})
|
|
222
|
+
cloned.passages = remapped
|
|
223
|
+
if (missing.length > 0) cloned.missingPassages = missing
|
|
144
224
|
|
|
145
225
|
// Sort and recompute summary fields
|
|
146
226
|
cloned.passages.sort((a, b) => a.chapter - b.chapter || a.verse - b.verse)
|
|
@@ -158,13 +238,13 @@ class ReferenceParser {
|
|
|
158
238
|
}
|
|
159
239
|
}
|
|
160
240
|
|
|
161
|
-
const
|
|
241
|
+
const chapterEntries = {}
|
|
162
242
|
cloned.passages.forEach((p) => {
|
|
163
|
-
if (!
|
|
164
|
-
|
|
243
|
+
if (!chapterEntries[p.chapter]) chapterEntries[p.chapter] = []
|
|
244
|
+
chapterEntries[p.chapter].push({ verse: p.verse, suffix: p.verseSuffix || "" })
|
|
165
245
|
})
|
|
166
246
|
|
|
167
|
-
const sortedChs = Object.keys(
|
|
247
|
+
const sortedChs = Object.keys(chapterEntries)
|
|
168
248
|
.map(Number)
|
|
169
249
|
.sort((a, b) => a - b)
|
|
170
250
|
const chapterStrs = []
|
|
@@ -187,13 +267,32 @@ class ReferenceParser {
|
|
|
187
267
|
return merged
|
|
188
268
|
}
|
|
189
269
|
|
|
270
|
+
const formatChapterVerses = (entries) => {
|
|
271
|
+
const usable = entries.filter((e) => e.verse > 0)
|
|
272
|
+
if (usable.length === 0) return []
|
|
273
|
+
const sorted = [...usable].sort(
|
|
274
|
+
(a, b) => a.verse - b.verse || a.suffix.localeCompare(b.suffix)
|
|
275
|
+
)
|
|
276
|
+
if (sorted.some((e) => e.suffix)) {
|
|
277
|
+
// Letter-suffixed verses (Esther additions, Isa 63:19b, Dan 3:24a):
|
|
278
|
+
// do not range-merge - emit each as "<verse><suffix>" individually.
|
|
279
|
+
const seen = new Set()
|
|
280
|
+
const out = []
|
|
281
|
+
for (const e of sorted) {
|
|
282
|
+
const tag = `${e.verse}${e.suffix}`
|
|
283
|
+
if (seen.has(tag)) continue
|
|
284
|
+
seen.add(tag)
|
|
285
|
+
out.push(tag)
|
|
286
|
+
}
|
|
287
|
+
return out
|
|
288
|
+
}
|
|
289
|
+
return mergeRanges([...new Set(sorted.map((e) => e.verse))])
|
|
290
|
+
}
|
|
291
|
+
|
|
190
292
|
sortedChs.forEach((ch) => {
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
.
|
|
194
|
-
if (vs.length > 0) {
|
|
195
|
-
const merged = mergeRanges(vs)
|
|
196
|
-
chapterStrs.push(`${ch}:${merged.join(",")}`)
|
|
293
|
+
const formatted = formatChapterVerses(chapterEntries[ch])
|
|
294
|
+
if (formatted.length > 0) {
|
|
295
|
+
chapterStrs.push(`${ch}:${formatted.join(",")}`)
|
|
197
296
|
}
|
|
198
297
|
})
|
|
199
298
|
|
|
@@ -205,20 +304,21 @@ class ReferenceParser {
|
|
|
205
304
|
const lastCh = sortedChs[sortedChs.length - 1]
|
|
206
305
|
cloned.chapter = firstCh
|
|
207
306
|
|
|
208
|
-
const
|
|
209
|
-
cloned.verses =
|
|
307
|
+
const formattedFirst = formatChapterVerses(chapterEntries[firstCh] || [])
|
|
308
|
+
cloned.verses = formattedFirst
|
|
210
309
|
|
|
211
310
|
if (firstCh !== lastCh) {
|
|
212
311
|
cloned.type = ReferenceParser.REFERENCE_TYPES.MULTI_CHAPTER_RANGE
|
|
213
312
|
cloned.to = {
|
|
214
313
|
book: cloned.book,
|
|
215
314
|
chapter: lastCh,
|
|
216
|
-
verses:
|
|
315
|
+
verses: formatChapterVerses(chapterEntries[lastCh] || []),
|
|
217
316
|
}
|
|
218
317
|
cloned.original = `${cloned.book} ${chapterStrs.join("; ")}`
|
|
219
318
|
} else {
|
|
220
319
|
const hasRangeOrMultiple =
|
|
221
|
-
|
|
320
|
+
formattedFirst.length > 1 ||
|
|
321
|
+
(formattedFirst.length === 1 && formattedFirst[0].includes("-"))
|
|
222
322
|
cloned.type = hasRangeOrMultiple
|
|
223
323
|
? ReferenceParser.REFERENCE_TYPES.CHAPTER_VERSE_RANGE
|
|
224
324
|
: ReferenceParser.REFERENCE_TYPES.CHAPTER_VERSE
|
|
@@ -251,12 +351,15 @@ class ReferenceParser {
|
|
|
251
351
|
return cloned
|
|
252
352
|
}
|
|
253
353
|
|
|
254
|
-
passage.getVersion = function (targetVersion) {
|
|
354
|
+
passage.getVersion = function (targetVersion, options = {}) {
|
|
255
355
|
const targetAbbr = targetVersion.toLowerCase() === "bhs" ? "mt" : targetVersion.toLowerCase()
|
|
256
|
-
return computeConverted(this, targetAbbr)
|
|
356
|
+
return computeConverted(this, targetAbbr, options)
|
|
357
|
+
}
|
|
358
|
+
passage.getLXX = function (options = {}) {
|
|
359
|
+
return this.getVersion("lxx", options)
|
|
257
360
|
}
|
|
258
|
-
passage.
|
|
259
|
-
return this.getVersion("lxx")
|
|
361
|
+
passage.getLXXRahlfs = function () {
|
|
362
|
+
return this.getVersion("lxx", { edition: "rahlfs" })
|
|
260
363
|
}
|
|
261
364
|
passage.getMT = function () {
|
|
262
365
|
return this.getVersion("mt")
|
|
@@ -267,7 +370,7 @@ class ReferenceParser {
|
|
|
267
370
|
passage.getEnglish = function () {
|
|
268
371
|
return this.getVersion("eng")
|
|
269
372
|
}
|
|
270
|
-
passage.convertVersion = function (targetVersion) {
|
|
373
|
+
passage.convertVersion = function (targetVersion, options = {}) {
|
|
271
374
|
const targetAbbr = targetVersion.toLowerCase() === "bhs" ? "mt" : targetVersion.toLowerCase()
|
|
272
375
|
|
|
273
376
|
// Check if any passages have versification data
|
|
@@ -281,7 +384,7 @@ class ReferenceParser {
|
|
|
281
384
|
}
|
|
282
385
|
|
|
283
386
|
// Has versification, use the full conversion
|
|
284
|
-
return computeConverted(this, targetAbbr)
|
|
387
|
+
return computeConverted(this, targetAbbr, options)
|
|
285
388
|
}
|
|
286
389
|
}
|
|
287
390
|
|
|
@@ -11,4 +11,8 @@ module.exports = {
|
|
|
11
11
|
10: Array.from({ length: 21 }, (_, i) => i + 1), // Daniel 10 has 21 verses
|
|
12
12
|
11: Array.from({ length: 45 }, (_, i) => i + 1), // Daniel 11 has 45 verses
|
|
13
13
|
12: Array.from({ length: 13 }, (_, i) => i + 1), // Daniel 12 has 13 verses
|
|
14
|
+
// Greek (deuterocanonical) Daniel: Susanna and Bel & the Dragon, present
|
|
15
|
+
// in LXX (both Theodotion and Old Greek) and absent from MT.
|
|
16
|
+
13: Array.from({ length: 64 }, (_, i) => i + 1), // Susanna (Theodotion)
|
|
17
|
+
14: Array.from({ length: 42 }, (_, i) => i + 1), // Bel and the Dragon (Theodotion)
|
|
14
18
|
}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// Esther chapter:verse counts using the Vulgate / RSV-with-Apocrypha layout,
|
|
2
|
+
// which appends the six Greek additions (A-F) as chapters 10:4-16:24.
|
|
3
|
+
// Hebrew Esther runs through 10:3; verses 10:4 onward are deuterocanonical
|
|
4
|
+
// (LXX-only). Note: this changes validation - "Esther 11:2" will now resolve
|
|
5
|
+
// even in eng mode. Canonical-only validation is a higher-level concern.
|
|
1
6
|
module.exports = {
|
|
2
7
|
1: Array.from({ length: 22 }, (_, i) => i + 1), // Esther 1 has 22 verses
|
|
3
8
|
2: Array.from({ length: 23 }, (_, i) => i + 1), // Esther 2 has 23 verses
|
|
@@ -8,5 +13,11 @@ module.exports = {
|
|
|
8
13
|
7: Array.from({ length: 10 }, (_, i) => i + 1), // Esther 7 has 10 verses
|
|
9
14
|
8: Array.from({ length: 17 }, (_, i) => i + 1), // Esther 8 has 17 verses
|
|
10
15
|
9: Array.from({ length: 32 }, (_, i) => i + 1), // Esther 9 has 32 verses
|
|
11
|
-
10: Array.from({ length:
|
|
16
|
+
10: Array.from({ length: 13 }, (_, i) => i + 1), // 10:1-3 Hebrew + 10:4-13 Addition F
|
|
17
|
+
11: Array.from({ length: 12 }, (_, i) => i + 1), // 11:1 colophon + 11:2-12 Addition A pt 1
|
|
18
|
+
12: Array.from({ length: 6 }, (_, i) => i + 1), // 12:1-6 Addition A pt 2
|
|
19
|
+
13: Array.from({ length: 18 }, (_, i) => i + 1), // 13:1-7 Add B + 13:8-18 Add C (Mordecai's prayer)
|
|
20
|
+
14: Array.from({ length: 19 }, (_, i) => i + 1), // 14:1-19 Add C (Esther's prayer)
|
|
21
|
+
15: Array.from({ length: 16 }, (_, i) => i + 1), // 15:1-16 Add D (Esther approaches the king)
|
|
22
|
+
16: Array.from({ length: 24 }, (_, i) => i + 1), // 16:1-24 Add E (king's second decree)
|
|
12
23
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Tracks which OT books have a published Göttingen Septuaginta critical
|
|
2
|
+
// edition. Books listed as "gottingen" are ones the parser treats as
|
|
3
|
+
// Göttingen-attested by default; everything else falls back to Rahlfs.
|
|
4
|
+
//
|
|
5
|
+
// Update this list when new Göttingen volumes ship. The `lxx` field in each
|
|
6
|
+
// versification entry should reflect this default (Göttingen where attested,
|
|
7
|
+
// Rahlfs elsewhere); add an `lxxRahlfs` override only when the two editions
|
|
8
|
+
// disagree on numbering for a specific verse.
|
|
9
|
+
//
|
|
10
|
+
// Sources:
|
|
11
|
+
// https://adw-goe.de/forschung/forschungsprojekte-akademienprogramm/septuaginta-unternehmen/
|
|
12
|
+
// Hanhart, R. (1983). Esther. Septuaginta VIII.3.
|
|
13
|
+
// Ziegler, J. & Munnich, O. (1999). Susanna - Daniel - Bel et Draco. Septuaginta XVI.2.
|
|
14
|
+
//
|
|
15
|
+
// As of 2026-04: published. Pentateuch is partial (Genesis published as
|
|
16
|
+
// Wevers 1974; Exodus, Leviticus, Numbers, Deuteronomy via Wevers).
|
|
17
|
+
module.exports = {
|
|
18
|
+
Genesis: "gottingen",
|
|
19
|
+
Exodus: "gottingen",
|
|
20
|
+
Leviticus: "gottingen",
|
|
21
|
+
Numbers: "gottingen",
|
|
22
|
+
Deuteronomy: "gottingen",
|
|
23
|
+
Joshua: "rahlfs",
|
|
24
|
+
Judges: "rahlfs",
|
|
25
|
+
Ruth: "gottingen",
|
|
26
|
+
"1 Samuel": "rahlfs",
|
|
27
|
+
"2 Samuel": "rahlfs",
|
|
28
|
+
"1 Kings": "rahlfs",
|
|
29
|
+
"2 Kings": "rahlfs",
|
|
30
|
+
"1 Chronicles": "rahlfs",
|
|
31
|
+
"2 Chronicles": "rahlfs",
|
|
32
|
+
Ezra: "gottingen", // 1 Esdras / 2 Esdras (Hanhart)
|
|
33
|
+
Nehemiah: "gottingen",
|
|
34
|
+
Esther: "gottingen", // Hanhart 1983
|
|
35
|
+
Job: "gottingen", // Ziegler 1982
|
|
36
|
+
Psalms: "gottingen", // Rahlfs 1931
|
|
37
|
+
Proverbs: "rahlfs",
|
|
38
|
+
Ecclesiastes: "gottingen", // Gentry 2019
|
|
39
|
+
"Song of Songs": "rahlfs",
|
|
40
|
+
"Song of Solomon": "rahlfs",
|
|
41
|
+
Isaiah: "gottingen", // Ziegler 1939
|
|
42
|
+
Jeremiah: "gottingen", // Ziegler 1957
|
|
43
|
+
Lamentations: "gottingen",
|
|
44
|
+
Ezekiel: "gottingen", // Ziegler 1952
|
|
45
|
+
Daniel: "gottingen", // Ziegler & Munnich 1999 (incl. Susanna, Bel)
|
|
46
|
+
Hosea: "gottingen", // Ziegler 1943 (Duodecim Prophetae)
|
|
47
|
+
Joel: "gottingen",
|
|
48
|
+
Amos: "gottingen",
|
|
49
|
+
Obadiah: "gottingen",
|
|
50
|
+
Jonah: "gottingen",
|
|
51
|
+
Micah: "gottingen",
|
|
52
|
+
Nahum: "gottingen",
|
|
53
|
+
Habakkuk: "gottingen",
|
|
54
|
+
Zephaniah: "gottingen",
|
|
55
|
+
Haggai: "gottingen",
|
|
56
|
+
Zechariah: "gottingen",
|
|
57
|
+
Malachi: "gottingen",
|
|
58
|
+
}
|