Phase 3 expand machine catalogs

This commit is contained in:
2026-07-11 22:10:34 -05:00
parent afeb2c4d3b
commit 564eaea5a0
7 changed files with 271 additions and 15 deletions
+7 -1
View File
@@ -2,10 +2,16 @@
## Current ## Current
- Added generated human-facing resource docs for Gitea and future ChristIT.com catalog work. - Expanded machine catalogs so ChristIT.com and apps can read rich resource metadata directly from JSON.
## 0.1.3 - 2026-07-12
- Phase 3 machine catalog foundation: expanded aggregate and per-resource JSON catalogs with schema version, resource identity, translation metadata, contributors, features, attachments, source/license details, package paths, checksums, and file sizes.
## 0.1.2 - 2026-07-12 ## 0.1.2 - 2026-07-12
- Added generated human-facing resource docs for Gitea and future ChristIT.com catalog work.
- Phase 2 generated public docs: added a docs generator that writes `RESOURCE_INDEX.md` and per-resource detail pages from source manifests and generated package catalogs. - Phase 2 generated public docs: added a docs generator that writes `RESOURCE_INDEX.md` and per-resource detail pages from source manifests and generated package catalogs.
## 0.1.1 - 2026-07-12 ## 0.1.1 - 2026-07-12
+8
View File
@@ -99,6 +99,14 @@ Attachment metadata should identify:
- Build/import scripts should fail when required source, license, checksum, or package metadata is missing. - Build/import scripts should fail when required source, license, checksum, or package metadata is missing.
- The public catalog must not silently disagree with the source manifest. - The public catalog must not silently disagree with the source manifest.
## Machine Catalogs
- `packages/json/catalog.json` is the machine-readable index for ChristIT.com, Libre Study, GracePress Bible Tooltip, and future consumers.
- Per-resource package catalogs, such as `packages/json/<resource-id>/catalog.json`, should carry the full resource metadata needed to render a public detail page without scraping Markdown.
- Machine catalogs should include schema version, project name, resource id, resource type, title, abbreviation, language, script, canon, translation/edition metadata, contributors, features, attachments, source, license, display summary, package paths, counts, file checksums, and source check metadata.
- Markdown docs are a human-readable projection of the same metadata. They must not become a separate truth source.
- Public ChristIT.com pages and app/plugin package discovery should read machine catalogs first, then link to Markdown docs for explanation.
## Update Workflow ## Update Workflow
- Update checks must be mechanical and repeatable. - Update checks must be mechanical and repeatable.
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "libre-bible-data", "name": "libre-bible-data",
"version": "0.1.2", "version": "0.1.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "libre-bible-data", "name": "libre-bible-data",
"version": "0.1.2", "version": "0.1.3",
"dependencies": { "dependencies": {
"adm-zip": "^0.5.16" "adm-zip": "^0.5.16"
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "libre-bible-data", "name": "libre-bible-data",
"version": "0.1.2", "version": "0.1.3",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
+121 -1
View File
@@ -1,15 +1,51 @@
{ {
"generated_at": "2026-07-12T02:51:51.354Z", "schema_version": "librebible.index-catalog.v1",
"project": "LibreBible",
"generated_at": "2026-07-12T03:10:02.881Z",
"resources": [ "resources": [
{ {
"id": "kjv-eng-kjv2006", "id": "kjv-eng-kjv2006",
"resource_type": "translation",
"title": "King James (Authorized) Version", "title": "King James (Authorized) Version",
"short_title": "King James Version",
"abbreviation": "KJV", "abbreviation": "KJV",
"alternate_ids": [
"ENGKJV",
"eng-kjv2006"
],
"language": { "language": {
"code": "eng", "code": "eng",
"name": "English", "name": "English",
"dialect": "archaic British" "dialect": "archaic British"
}, },
"script": "Latn",
"canon": {
"scope": "protocanon",
"notes": "eBible describes this package as protocanon only."
},
"translation": {
"base_title": "King James Version",
"edition": "1769 standardized text with Strong's numbers added",
"translation_year": 1611,
"edition_year": 1769,
"source_text_basis": "Authorized Version / King James Version standardized text of 1769",
"tradition": "Protestant",
"public_description": "The King James Version or Authorized Version of the Holy Bible, using the standardized text of 1769, protocanon only, with Strong's numbers added."
},
"contributors": [
{
"name": "King James Version translators",
"role": "translation"
},
{
"name": "CrossWire Bible Society",
"role": "source text provider"
},
{
"name": "eBible.org",
"role": "publisher and package provider"
}
],
"features": [ "features": [
{ {
"id": "bible-text", "id": "bible-text",
@@ -43,6 +79,64 @@
"embedded": false "embedded": false
} }
], ],
"attachments": {
"included": [
{
"id": "kjv-eng-kjv2006-strongs-links",
"resource_type": "concordance_links",
"label": "Strong's links",
"relationship": "word-to-strongs",
"anchor_types": [
"verse",
"word",
"strongs_number"
],
"languages": [
"hebrew",
"aramaic",
"greek"
],
"systems": [
"strongs"
],
"source": "Embedded in the eBible USFM package.",
"package": "packages/json/kjv-eng-kjv2006/strongs-links.jsonl"
}
],
"supported_future_types": [
"study_notes",
"commentary",
"cross_references",
"morphology",
"lemma_links",
"lexicon",
"dictionary",
"maps",
"timeline",
"media"
]
},
"source": {
"provider": "eBible.org",
"url": "https://ebible.org/find/show.php?id=eng-kjv2006",
"download_url": "https://ebible.org/Scriptures/eng-kjv2006_usfm.zip",
"format": "usfm-zip",
"upstream_id": "eng-kjv2006",
"upstream_last_updated": "2026-05-16",
"available_formats": [
"browser",
"mobile-html",
"epub",
"pdf",
"sword",
"plain-text",
"vpl-sql",
"browser-bible",
"usfx",
"usfm",
"xetex"
]
},
"license": { "license": {
"name": "Public Domain", "name": "Public Domain",
"redistribution": true, "redistribution": true,
@@ -54,11 +148,37 @@
], ],
"restricted_notes": "Printing this translation in the United Kingdom or importing printed copies into the UK may require permission from the Crown patent holders." "restricted_notes": "Printing this translation in the United Kingdom or importing printed copies into the UK may require permission from the Crown patent holders."
}, },
"catalog_display": {
"primary_features": [
"Bible text",
"Strong's-linked words",
"USFM source"
],
"summary": "Public-domain KJV package based on the 1769 standardized text, with Strong's numbers embedded in the source USFM."
},
"package_path": "packages/json/kjv-eng-kjv2006/catalog.json", "package_path": "packages/json/kjv-eng-kjv2006/catalog.json",
"source_manifest_path": "sources/kjv-eng-kjv2006.json",
"detail_doc_path": "docs/resources/kjv-eng-kjv2006.md",
"counts": { "counts": {
"books": 66, "books": 66,
"verses": 31102, "verses": 31102,
"strongs_links": 348884 "strongs_links": 348884
},
"files": {
"verses_jsonl": {
"path": "verses.jsonl",
"bytes": 8700726,
"sha256": "e3102f854a99c4788ed259ec61cb93fea830845c0788c8c0929bc77e9db6ca10"
},
"strongs_jsonl": {
"path": "strongs-links.jsonl",
"bytes": 62690394,
"sha256": "fa9702224776509275ea17fb245cb72150f1c262c4fbe44c7161e8e83866c779"
}
},
"checks": {
"expected_sha256": "4ea6952590d070bfa22985aded48a49581e31b568a60aa09e25f73462e700e7d",
"last_checked_at": "2026-07-12T02:18:30Z"
} }
} }
] ]
+88 -1
View File
@@ -1,12 +1,48 @@
{ {
"schema_version": "librebible.resource-catalog.v1",
"project": "LibreBible",
"id": "kjv-eng-kjv2006", "id": "kjv-eng-kjv2006",
"resource_type": "translation",
"title": "King James (Authorized) Version", "title": "King James (Authorized) Version",
"short_title": "King James Version",
"abbreviation": "KJV", "abbreviation": "KJV",
"alternate_ids": [
"ENGKJV",
"eng-kjv2006"
],
"language": { "language": {
"code": "eng", "code": "eng",
"name": "English", "name": "English",
"dialect": "archaic British" "dialect": "archaic British"
}, },
"script": "Latn",
"canon": {
"scope": "protocanon",
"notes": "eBible describes this package as protocanon only."
},
"translation": {
"base_title": "King James Version",
"edition": "1769 standardized text with Strong's numbers added",
"translation_year": 1611,
"edition_year": 1769,
"source_text_basis": "Authorized Version / King James Version standardized text of 1769",
"tradition": "Protestant",
"public_description": "The King James Version or Authorized Version of the Holy Bible, using the standardized text of 1769, protocanon only, with Strong's numbers added."
},
"contributors": [
{
"name": "King James Version translators",
"role": "translation"
},
{
"name": "CrossWire Bible Society",
"role": "source text provider"
},
{
"name": "eBible.org",
"role": "publisher and package provider"
}
],
"features": [ "features": [
{ {
"id": "bible-text", "id": "bible-text",
@@ -40,6 +76,43 @@
"embedded": false "embedded": false
} }
], ],
"attachments": {
"included": [
{
"id": "kjv-eng-kjv2006-strongs-links",
"resource_type": "concordance_links",
"label": "Strong's links",
"relationship": "word-to-strongs",
"anchor_types": [
"verse",
"word",
"strongs_number"
],
"languages": [
"hebrew",
"aramaic",
"greek"
],
"systems": [
"strongs"
],
"source": "Embedded in the eBible USFM package.",
"package": "packages/json/kjv-eng-kjv2006/strongs-links.jsonl"
}
],
"supported_future_types": [
"study_notes",
"commentary",
"cross_references",
"morphology",
"lemma_links",
"lexicon",
"dictionary",
"maps",
"timeline",
"media"
]
},
"source": { "source": {
"provider": "eBible.org", "provider": "eBible.org",
"url": "https://ebible.org/find/show.php?id=eng-kjv2006", "url": "https://ebible.org/find/show.php?id=eng-kjv2006",
@@ -72,12 +145,24 @@
], ],
"restricted_notes": "Printing this translation in the United Kingdom or importing printed copies into the UK may require permission from the Crown patent holders." "restricted_notes": "Printing this translation in the United Kingdom or importing printed copies into the UK may require permission from the Crown patent holders."
}, },
"catalog_display": {
"primary_features": [
"Bible text",
"Strong's-linked words",
"USFM source"
],
"summary": "Public-domain KJV package based on the 1769 standardized text, with Strong's numbers embedded in the source USFM."
},
"importer": { "importer": {
"name": "scripts/import-usfm.js", "name": "scripts/import-usfm.js",
"version": "0.1.0" "version": "0.1.0"
}, },
"generated_at": "2026-07-12T02:51:51.354Z", "generated_at": "2026-07-12T03:10:02.881Z",
"source_sha256": "4ea6952590d070bfa22985aded48a49581e31b568a60aa09e25f73462e700e7d", "source_sha256": "4ea6952590d070bfa22985aded48a49581e31b568a60aa09e25f73462e700e7d",
"checks": {
"expected_sha256": "4ea6952590d070bfa22985aded48a49581e31b568a60aa09e25f73462e700e7d",
"last_checked_at": "2026-07-12T02:18:30Z"
},
"counts": { "counts": {
"books": 66, "books": 66,
"verses": 31102, "verses": 31102,
@@ -86,10 +171,12 @@
"files": { "files": {
"verses_jsonl": { "verses_jsonl": {
"path": "verses.jsonl", "path": "verses.jsonl",
"bytes": 8700726,
"sha256": "e3102f854a99c4788ed259ec61cb93fea830845c0788c8c0929bc77e9db6ca10" "sha256": "e3102f854a99c4788ed259ec61cb93fea830845c0788c8c0929bc77e9db6ca10"
}, },
"strongs_jsonl": { "strongs_jsonl": {
"path": "strongs-links.jsonl", "path": "strongs-links.jsonl",
"bytes": 62690394,
"sha256": "fa9702224776509275ea17fb245cb72150f1c262c4fbe44c7161e8e83866c779" "sha256": "fa9702224776509275ea17fb245cb72150f1c262c4fbe44c7161e8e83866c779"
} }
} }
+44 -9
View File
@@ -1,5 +1,5 @@
import AdmZip from 'adm-zip' import AdmZip from 'adm-zip'
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises' import { mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises'
import path from 'node:path' import path from 'node:path'
import { import {
cleanUsfmText, cleanUsfmText,
@@ -110,50 +110,85 @@ await writeFile(
'utf8', 'utf8',
) )
const versesPackage = await packageFile(outputDir, 'verses.jsonl')
const strongsPackage = await packageFile(outputDir, 'strongs-links.jsonl')
const catalog = { const catalog = {
schema_version: 'librebible.resource-catalog.v1',
project: manifest.project_name ?? 'LibreBible',
id: manifest.id, id: manifest.id,
resource_type: manifest.resource_type,
title: manifest.title, title: manifest.title,
short_title: manifest.short_title,
abbreviation: manifest.abbreviation, abbreviation: manifest.abbreviation,
alternate_ids: manifest.alternate_ids ?? [],
language: manifest.language, language: manifest.language,
script: manifest.script,
canon: manifest.canon,
translation: manifest.translation,
contributors: manifest.contributors ?? [],
features: manifest.features, features: manifest.features,
attachments: manifest.attachments,
source: manifest.source, source: manifest.source,
license: manifest.license, license: manifest.license,
catalog_display: manifest.catalog_display,
importer: manifest.importer, importer: manifest.importer,
generated_at: new Date().toISOString(), generated_at: new Date().toISOString(),
source_sha256: archiveSha, source_sha256: archiveSha,
checks: manifest.checks,
counts: { counts: {
books: new Set(verses.map((verse) => verse.book_id)).size, books: new Set(verses.map((verse) => verse.book_id)).size,
verses: verses.length, verses: verses.length,
strongs_links: strongsLinks.length, strongs_links: strongsLinks.length,
}, },
files: { files: {
verses_jsonl: { verses_jsonl: versesPackage,
path: 'verses.jsonl', strongs_jsonl: strongsPackage,
sha256: await sha256File(path.join(outputDir, 'verses.jsonl')),
},
strongs_jsonl: {
path: 'strongs-links.jsonl',
sha256: await sha256File(path.join(outputDir, 'strongs-links.jsonl')),
},
}, },
} }
await writeJson(path.join(outputDir, 'catalog.json'), catalog) await writeJson(path.join(outputDir, 'catalog.json'), catalog)
await writeJson(path.join(root, 'packages', 'json', 'catalog.json'), { await writeJson(path.join(root, 'packages', 'json', 'catalog.json'), {
schema_version: 'librebible.index-catalog.v1',
project: manifest.project_name ?? 'LibreBible',
generated_at: catalog.generated_at, generated_at: catalog.generated_at,
resources: [ resources: [
{ {
id: catalog.id, id: catalog.id,
resource_type: catalog.resource_type,
title: catalog.title, title: catalog.title,
short_title: catalog.short_title,
abbreviation: catalog.abbreviation, abbreviation: catalog.abbreviation,
alternate_ids: catalog.alternate_ids,
language: catalog.language, language: catalog.language,
script: catalog.script,
canon: catalog.canon,
translation: catalog.translation,
contributors: catalog.contributors,
features: catalog.features, features: catalog.features,
attachments: catalog.attachments,
source: catalog.source,
license: catalog.license, license: catalog.license,
catalog_display: catalog.catalog_display,
package_path: `packages/json/${catalog.id}/catalog.json`, package_path: `packages/json/${catalog.id}/catalog.json`,
source_manifest_path: `sources/${path.basename(manifestPath)}`,
detail_doc_path: `docs/resources/${catalog.id}.md`,
counts: catalog.counts, counts: catalog.counts,
files: catalog.files,
checks: catalog.checks,
}, },
], ],
}) })
console.log(`${manifest.id}: imported ${catalog.counts.verses} verses`) console.log(`${manifest.id}: imported ${catalog.counts.verses} verses`)
console.log(`${manifest.id}: imported ${catalog.counts.strongs_links} Strong's links`) console.log(`${manifest.id}: imported ${catalog.counts.strongs_links} Strong's links`)
async function packageFile(outputDir, fileName) {
const filePath = path.join(outputDir, fileName)
const fileStat = await stat(filePath)
return {
path: fileName,
bytes: fileStat.size,
sha256: await sha256File(filePath),
}
}