@ambry/scripture 0.0.0-stage → 0.1.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.
@@ -0,0 +1,79 @@
1
+ import { scriptureBooks } from './books.js';
2
+ function positiveLimit(value, name) {
3
+ if (!Number.isSafeInteger(value) || value < 1)
4
+ throw new RangeError(`${name} must be a positive safe integer.`);
5
+ return value;
6
+ }
7
+ export const scriptureLanguages = {
8
+ english: { label: 'English', field: 'english_text' },
9
+ greek: { label: 'Greek', field: 'greek_text' },
10
+ latin: { label: 'Latin', field: 'latin_text' },
11
+ };
12
+ const languagePattern = 'ENGLISH|ENG|EN|GREEK|GRK|GRC|LATIN|LAT|LA';
13
+ const languageSuffixes = {
14
+ ENGLISH: 'english',
15
+ ENG: 'english',
16
+ EN: 'english',
17
+ GREEK: 'greek',
18
+ GRK: 'greek',
19
+ GRC: 'greek',
20
+ LATIN: 'latin',
21
+ LAT: 'latin',
22
+ LA: 'latin',
23
+ };
24
+ const normalizeBook = (name) => name.toLowerCase().replace(/[\s.]/g, '');
25
+ const bookNames = scriptureBooks.flatMap((book) => [book.name, book.code, ...book.aliases].map((name) => ({ name, book })));
26
+ const escapePattern = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
27
+ const bookPattern = bookNames
28
+ .map(({ name }) => escapePattern(name).replace(/ /g, '\\s*'))
29
+ .sort((a, b) => b.length - a.length)
30
+ .join('|');
31
+ const embeddedReferencePattern = new RegExp(String.raw `(?<![\p{L}\p{N}_])(${bookPattern})\.?\s*(\d{1,3})\s*:\s*(\d{1,3})(?:\s*[-–]\s*(\d{1,3}))?(?:\s+(${languagePattern}))?(?![\p{L}\p{N}_]|\s*[:–-]\s*\d)`, 'giu');
32
+ export function parseScriptureReference(input, options = {}) {
33
+ const maxVerses = positiveLimit(options.maxVerses ?? 176, 'maxVerses');
34
+ const match = input
35
+ .trim()
36
+ .match(new RegExp(String.raw `^(.+?)\s*(\d{1,3})(?:\s*:\s*(\d{1,3})(?:\s*[-–]\s*(\d{1,3}))?)?(?:\s+(${languagePattern}))?$`, 'i'));
37
+ if (!match)
38
+ return;
39
+ const book = bookNames.find(({ name }) => normalizeBook(name) === normalizeBook(match[1]))?.book;
40
+ const chapter = Number(match[2]);
41
+ const firstVerse = match[3] ? Number(match[3]) : undefined;
42
+ const lastVerse = match[4] ? Number(match[4]) : firstVerse;
43
+ if (!book || chapter < 1 || chapter > 150)
44
+ return;
45
+ if (firstVerse != null &&
46
+ (firstVerse < 1 || lastVerse > 176 || lastVerse < firstVerse || lastVerse - firstVerse + 1 > maxVerses))
47
+ return;
48
+ return {
49
+ book: book.name,
50
+ bookCode: book.code,
51
+ chapter,
52
+ firstVerse,
53
+ lastVerse,
54
+ language: languageSuffixes[match[5]?.toUpperCase()] ?? 'english',
55
+ };
56
+ }
57
+ export function findScriptureReferences(content, options = {}) {
58
+ const maxReferences = positiveLimit(options.maxReferences ?? 10, 'maxReferences');
59
+ positiveLimit(options.maxVerses ?? 176, 'maxVerses');
60
+ const trailingLanguage = content.match(new RegExp(String.raw `\b(${languagePattern})\s*[.!?]?\s*$`, 'i'));
61
+ const defaultLanguage = languageSuffixes[trailingLanguage?.[1].toUpperCase() ?? ''] ?? 'english';
62
+ const references = [];
63
+ const seen = new Set();
64
+ for (const match of content.matchAll(embeddedReferencePattern)) {
65
+ const reference = parseScriptureReference(match[0], options);
66
+ if (!reference)
67
+ continue;
68
+ if (!match[5])
69
+ reference.language = defaultLanguage;
70
+ const key = `${reference.bookCode}:${reference.chapter}:${reference.firstVerse}:${reference.lastVerse}:${reference.language}`;
71
+ if (seen.has(key))
72
+ continue;
73
+ seen.add(key);
74
+ references.push(reference);
75
+ if (references.length === maxReferences)
76
+ break;
77
+ }
78
+ return references;
79
+ }
package/package.json CHANGED
@@ -1,6 +1,67 @@
1
1
  {
2
2
  "name": "@ambry/scripture",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Ambry Bible books, aliases, reference parsing, language suffixes, and scripture API client.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "engines": {
8
+ "node": ">=20"
9
+ },
10
+ "sideEffects": false,
11
+ "main": "./dist/cjs/index.js",
12
+ "module": "./dist/esm/index.js",
13
+ "types": "./dist/esm/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": {
17
+ "types": "./dist/esm/index.d.ts",
18
+ "default": "./dist/esm/index.js"
19
+ },
20
+ "require": {
21
+ "types": "./dist/cjs/index.d.ts",
22
+ "default": "./dist/cjs/index.js"
23
+ }
24
+ },
25
+ "./client": {
26
+ "import": {
27
+ "types": "./dist/esm/client.d.ts",
28
+ "default": "./dist/esm/client.js"
29
+ },
30
+ "require": {
31
+ "types": "./dist/cjs/client.d.ts",
32
+ "default": "./dist/cjs/client.js"
33
+ }
34
+ },
35
+ "./package.json": "./package.json"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "README.md",
40
+ "LICENSE"
41
+ ],
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "registry": "https://registry.npmjs.org/"
45
+ },
46
+ "keywords": [
47
+ "ambry",
48
+ "bible",
49
+ "scripture",
50
+ "catholic",
51
+ "reference-parser"
52
+ ],
53
+ "devDependencies": {
54
+ "typescript": "^5.9.3"
55
+ },
56
+ "typesVersions": {
57
+ "*": {
58
+ "client": [
59
+ "dist/cjs/client.d.ts"
60
+ ]
61
+ }
62
+ },
63
+ "scripts": {
64
+ "build": "node scripts/build.mjs",
65
+ "typecheck": "tsc -p tsconfig.json --noEmit"
66
+ }
6
67
  }