Files
biblical-guide.com/scripts/old/validate-bsb-md.js
Andrei 95070e5369 Add comprehensive page management system to admin dashboard
Features added:
- Database schema for pages and media files with content types (Rich Text, HTML, Markdown)
- Admin API routes for full page CRUD operations
- Image upload functionality with file management
- Rich text editor using TinyMCE with image insertion
- Admin interface for creating/editing pages with SEO options
- Dynamic navigation and footer integration
- Public page display routes with proper SEO metadata
- Support for featured images and content excerpts

Admin features:
- Create/edit/delete pages with rich content editor
- Upload and manage images through media library
- Configure pages to appear in navigation or footer
- Set page status (Draft, Published, Archived)
- SEO title and description management
- Real-time preview of content changes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-24 07:26:25 +00:00

144 lines
5.4 KiB
JavaScript

#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
const SRC = process.env.BSB_MD_PATH || path.join('bibles', 'bible-bsb.md')
function canon() {
const OT = [
['Genesis', ['Genesis'], 50],
['Exodus', ['Exodus'], 40],
['Leviticus', ['Leviticus'], 27],
['Numbers', ['Numbers'], 36],
['Deuteronomy', ['Deuteronomy'], 34],
['Joshua', ['Joshua'], 24],
['Judges', ['Judges'], 21],
['Ruth', ['Ruth'], 4],
['1 Samuel', ['1 Samuel','1 Samuel'], 31],
['2 Samuel', ['2 Samuel','2 Samuel'], 24],
['1 Kings', ['1 Kings','1 Kings'], 22],
['2 Kings', ['2 Kings','2 Kings'], 25],
['1 Chronicles', ['1 Chronicles','1 Chronicles'], 29],
['2 Chronicles', ['2 Chronicles','2 Chronicles'], 36],
['Ezra', ['Ezra'], 10],
['Nehemiah', ['Nehemiah'], 13],
['Esther', ['Esther'], 10],
['Job', ['Job'], 42],
['Psalms', ['Psalms','Psalm'], 150],
['Proverbs', ['Proverbs'], 31],
['Ecclesiastes', ['Ecclesiastes'], 12],
['Song of Songs', ['Song of Songs','Song of Solomon'], 8],
['Isaiah', ['Isaiah'], 66],
['Jeremiah', ['Jeremiah'], 52],
['Lamentations', ['Lamentations'], 5],
['Ezekiel', ['Ezekiel'], 48],
['Daniel', ['Daniel'], 12],
['Hosea', ['Hosea'], 14],
['Joel', ['Joel'], 3],
['Amos', ['Amos'], 9],
['Obadiah', ['Obadiah'], 1],
['Jonah', ['Jonah'], 4],
['Micah', ['Micah'], 7],
['Nahum', ['Nahum'], 3],
['Habakkuk', ['Habakkuk'], 3],
['Zephaniah', ['Zephaniah'], 3],
['Haggai', ['Haggai'], 2],
['Zechariah', ['Zechariah'], 14],
['Malachi', ['Malachi'], 4]
]
const NT = [
['Matthew', ['Matthew'], 28],
['Mark', ['Mark'], 16],
['Luke', ['Luke'], 24],
['John', ['John'], 21],
['Acts', ['Acts'], 28],
['Romans', ['Romans'], 16],
['1 Corinthians', ['1 Corinthians','1 Corinthians'], 16],
['2 Corinthians', ['2 Corinthians','2 Corinthians'], 13],
['Galatians', ['Galatians'], 6],
['Ephesians', ['Ephesians'], 6],
['Philippians', ['Philippians'], 4],
['Colossians', ['Colossians'], 4],
['1 Thessalonians', ['1 Thessalonians','1 Thessalonians'], 5],
['2 Thessalonians', ['2 Thessalonians','2 Thessalonians'], 3],
['1 Timothy', ['1 Timothy','1 Timothy'], 6],
['2 Timothy', ['2 Timothy','2 Timothy'], 4],
['Titus', ['Titus'], 3],
['Philemon', ['Philemon'], 1],
['Hebrews', ['Hebrews'], 13],
['James', ['James'], 5],
['1 Peter', ['1 Peter','1 Peter'], 5],
['2 Peter', ['2 Peter','2 Peter'], 3],
['1 John', ['1 John','1 John'], 5],
['2 John', ['2 John','2 John'], 1],
['3 John', ['3 John','3 John'], 1],
['Jude', ['Jude'], 1],
['Revelation', ['Revelation'], 22]
]
return [
...OT.map(([n,v,c]) => ({ name:n, variants:v, expectedChapters:c, testament:'OT' })),
...NT.map(([n,v,c]) => ({ name:n, variants:v, expectedChapters:c, testament:'NT' })),
]
}
function main() {
if (!fs.existsSync(SRC)) {
console.error('Missing source file:', SRC)
process.exit(1)
}
const md = fs.readFileSync(SRC, 'utf-8')
const books = canon()
const report = { file: SRC, totals: { versesTagged: 0 }, books: [] }
for (const b of books) {
const patterns = b.variants.map(v => v.replace(/\s+/g, '\\s+'))
const names = patterns.join('|')
const re = new RegExp(`(?:^|[\n\r\f\s\|\(])(?:${names})\\s+(\\d+):(\\d+)`, 'gi')
const chapters = new Set()
let m
let verseCount = 0
while ((m = re.exec(md)) !== null) {
const nums = m.slice(1).filter(Boolean)
const ch = parseInt(nums[0] || '0', 10)
const vs = parseInt(nums[1] || '0', 10)
if (Number.isFinite(ch) && ch > 0) chapters.add(ch)
if (Number.isFinite(vs) && vs > 0) verseCount++
}
// Heuristic: some one-chapter books may lack inline verse references; accept header presence
const oneChapterBooks = new Set(['Obadiah','Philemon','2 John','3 John','Jude'])
if (chapters.size === 0 && oneChapterBooks.has(b.name)) {
const headerRe = new RegExp(`[\f\n\r]\s*${b.variants.map(v=>v.replace(/\s+/g,'\\s+')).join('|')}\s*[\n\r]`, 'i')
if (headerRe.test(md)) {
chapters.add(1)
}
}
report.totals.versesTagged += verseCount
report.books.push({
name: b.name,
testament: b.testament,
expectedChapters: b.expectedChapters,
detectedChapters: Array.from(chapters).sort((a,b)=>a-b),
detectedCount: chapters.size,
coverage: b.expectedChapters > 0 ? +(100 * chapters.size / b.expectedChapters).toFixed(2) : null,
verseMarkers: verseCount
})
}
const missingBooks = report.books.filter(x => x.detectedCount === 0).map(x=>x.name)
const partialBooks = report.books.filter(x => x.detectedCount > 0 && x.detectedCount < x.expectedChapters).map(x=>({name:x.name, det:x.detectedCount, exp:x.expectedChapters}))
console.log('Validation summary for', SRC)
console.log('Total verse markers found:', report.totals.versesTagged)
console.log('Books missing markers:', missingBooks.length ? missingBooks.join(', ') : 'None')
console.log('Books partially detected (chapters):', partialBooks.length ? JSON.stringify(partialBooks.slice(0,10)) : 'None')
const outDir = path.join('data','en_bible','BSB_VALIDATION')
fs.mkdirSync(outDir, { recursive: true })
fs.writeFileSync(path.join(outDir,'report.json'), JSON.stringify(report, null, 2), 'utf-8')
console.log('Wrote detailed report to', path.join(outDir,'report.json'))
}
main()