Numbered books in non-English/version-specific naming (e.g. 1 Könige,
2 Corintios, 1 Reyes) failed to return verse text (#355, #342). Root
cause: AllBibleBooksInAllSupportedLanguages dropped startNumber, so the
cross-language ordinal fallback (bookIdFromName) — the path the provider
uses to map a localized name to English — could never match numbered
books. Preserve startNumber; add a regression test.
Clementine Latin Vulgate errored because bible-api.com dropped the
'clementine' translation (404) (#357). Repoint to bolls.life's 'VULG'
(same Biblia Sacra juxta Vulgatam Clementinam); drop the stale entry
from the bible-api translations doc comment.
Claude-Session: https://claude.ai/code/session_01HvA9UdGcEyNyysyRVKKmmN
Co-authored-by: tim-hub <tim-hub@users.noreply.github.com>
Ship the public-domain World English Bible inside the plugin so verses
resolve with zero network. Adds an Offline Mode setting (forces the
bundled version, marked "(Offline)" in the dropdown) and auto-falls back
to bundled WEB text whenever an online fetch fails, without poisoning the
selected version's verse cache.
- offlineBible.web.json: compact string[][][] indexed [book][chapter][verse],
canonical 66-book order (generated from TehShrike/world-english-bible).
- offlineLookup(): language-agnostic book-name normalization + verse expansion.
- OfflineProvider routes the 'offline' apiSource; BaseBibleAPIProvider
degrades gracefully on network failure.
Closes#359
Claude-Session: https://claude.ai/code/session_01HvA9UdGcEyNyysyRVKKmmN
The obsidian mock was preloaded only via the root bunfig.toml, so
`bun test` run from the package (as `--filter ... test` does) skipped it
and provider tests failed to resolve 'obsidian'. Add a package-level
bunfig.toml with a package-relative preload.
Claude-Session: https://claude.ai/code/session_01HvA9UdGcEyNyysyRVKKmmN
Export SupportedLanguages from the toolkit so callers can enumerate
catalogs instead of probing by try/catch. Add a reconciliation test that
fails CI when a version code neither matches a catalog nor is a known
English-fallback code, so a zh_TW-style drift can't silently anglicize
book names again. Also tracks catalogs shipped without a version (da, jp).
Claude-Session: https://claude.ai/code/session_01HvA9UdGcEyNyysyRVKKmmN
Book names for Chinese Bibles rendered in English because the catalog
keys zh_CN/zh_TW were the only mixed-case ones and the toolkit lowercases
every lookup, making them unreachable. Lowercase the two catalog keys to
zh_cn/zh_tw and re-code the 5 Chinese versions to their exact catalog
(Simplified -> zh_cn, Traditional -> zh_tw).
Consolidate the version+setting -> book-name-locale decision and its
English fallback into one bookNameLocalization module; getSuggestionsFromQuery
and BollyLifeProvider now pass a Version instead of juggling a raw code.
Claude-Session: https://claude.ai/code/session_01HvA9UdGcEyNyysyRVKKmmN
Squashes in-progress local work: [verseCache] debug logs in the cache
path, .agents/.claude skill definitions with skills-lock.json, and
.gitignore/.stignore updates.
Claude-Session: https://claude.ai/code/session_01HvA9UdGcEyNyysyRVKKmmN
Bare 'John 1' fired a transient whole-chapter fetch on every keystroke
before ':1' was typed. Add hasExplicitVerse predicate and guard the two
per-keystroke suggesters (editor + lookup modal) so they only fetch once
a colon + verse or ':a' is present. Whole-chapter stays available via
explicit 'John 1:a'. Programmatic API and getSuggestionsFromQuery keep
bare-chapter-whole-chapter behavior.
Claude-Session: https://claude.ai/code/session_01HvA9UdGcEyNyysyRVKKmmN
Cache immutable Bible text per (translation, book+chapter, verse) so
repeated lookups skip the network. BaseBibleAPIProvider.query() reads
cached verses, fetches only the gap (whole chapter for BollyLife), and
fills the cache. main.ts hydrates on load and persists via a single
writer that serializes settings + cache together (no clobber).
Claude-Session: https://claude.ai/code/session_01HvA9UdGcEyNyysyRVKKmmN
The ackee analytics implementation was removed in d5f3b8a; only dead
remnants were left. Drop the unused ackee-tracker / @types/ackee-tracker
dependencies and the unreferenced optOutToEvents setting field/default.
Build + 200 tests pass; lockfile refreshed.
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
The module holds Verse-of-the-Day fetch functions (getVod/getEnhancedVod),
not a BaseBibleAPIProvider subclass - the ...Provider name and provider/
placement implied an adapter it never was. Rename the file; update the
3 import sites. 200 tests pass.
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
- Delete IVerseSuggesting: a single-implementer interface adds surface
without abstraction; VerseSuggesting uses its concrete type.
- Delete BibleAPIDotComProvider.getOriginalVerseReferenceLink: a no-op
override that only called super (base impl is inherited unchanged),
plus its stale commented-out block.
Left as-is (not actually shallow): VerseOfDaySuggesting's ctor is
load-bearing (widens visibility + stores verseTexts accessed in main.ts).
200 tests pass.
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
The four URL builders are genuinely different formats, so they stay as
distinct functions. What was shallow was the dispatch: getUrlForReference
repeated the same try/log/fallback wrapper per source, and its
'biblegateway' case was identical to the Bible Gateway fallback helper.
- Replace the ~95-line switch with a builders map + one try/catch/fallback
(fallback logic now lives in one place); 'biblegateway' and unknown
sources fall through to the fallback.
- Delete getLogosLink (exported but only used by its own tests); replace
those tests with direct getLogosTranslation fallback coverage.
Behaviour unchanged; 200 tests pass.
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
Delete the Gang-of-Four singleton (~40 of 60 lines were scaffolding
guarding a stateless dispatch) in favour of a plain buildProvider()
function. Update the 3 call sites; add a dispatch test asserting the
right provider per apiSource. 201 tests pass.
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
Replace the 159-line hand-kept BIBLE_VERSE_COUNTS table with thin
getVerseCount/getChapterCount adapters over bible-reference-toolkit
(backed by bible-book-names-intl). Bible structure now lives in one
place. Also fixes two off-by-one errors in the old table
(1 Samuel 23: 28->29, 1 Kings 20: 42->43 verses; toolkit matches KJV).
splitBibleReference untouched; 54 range tests + full 197 suite pass.
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
Retire the hardcoded php->50 / isa->23 branches in the plugin's
bookNameReference. Root causes fixed at the source instead:
- add "Php" to Philippians in bible-book-names-intl (data gap)
- toolkit matcher now does an exact name/abbreviation pass before the
ordinal-variation pass, so "Isa" resolves to Isaiah instead of losing
to the generated "I"+"Sa" variant of 1 Samuel
Book-name knowledge now lives only in the libs. Toolkit 38 tests +
full 197 suite pass; en.json snapshot updated for the new abbreviation.
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
Remove ALL_BOOKS, BOOKS_YOUVERSION, OSI_BOOKS, BOOK_NAMES,
LOGOS_BOOK_CODES, matchingBooks - all had zero references and
re-encoded data owned by bible-book-names-intl. Keep only the live
URL-code tables BLB_BOOK_CODES / STEPBIBLE_BOOK_CODES. 723->154 lines,
no behaviour change; 197 tests pass.
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
A single greedy regex kept tripping CodeQL js/polynomial-redos: any
.-class quantifier adjacent to \s+ backtracks polynomially (latest
witness: 'aA !' + many 'aa !'). Replace it with a linear scan for the
last 'letter + whitespace + non-space' boundary using a non-overlapping
pattern, then slice. Behavior unchanged; 38 toolkit tests pass; witness
input now linear (200k reps in ~6ms).
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
The anchored regex still let \s+ and the trailing (.+) both match
spaces, which CodeQL flagged (witness: 'aA ' + many spaces). Require
the chapter/verse group to start with \S so \s+ owns all separator
whitespace - no overlapping quantifiers, linear time. Behavior
unchanged; 38 toolkit tests pass.
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
Anchor the book/chapter-verse parsing regex with ^...$ so it no longer
restarts at every position. Resolves CodeQL js/polynomial-redos
(polynomial backtracking on long uncontrolled input). Behavior
unchanged; 38 toolkit tests pass.
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
- version-bump.mjs now derives today's YY.MM.DD, writes it into the plugin
package.json (source of truth), and syncs manifest.json + versions.json.
- On same-day collision (base version already git-tagged) it appends -1, -2, …
- Optional explicit version override via CLI arg.
- Release skill updated: bump + sync is now a single command.
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
- .gitignore: ignore .vscode/
- version-bump.mjs: read the version from the plugin package.json (single
source of truth) instead of the npm_package_version env var, and sync it
into the root manifest.json + versions.json
- .claude release skill: use bun commands and the new version flow
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
- Switch package manager/runtime/test-runner from pnpm+jest to Bun.
Remove pnpm-workspace.yaml + pnpm-lock.yaml; root package.json is now a
bun workspace ("workspaces": ["packages/*"]); add bun.lock.
- Move the Obsidian plugin source into packages/obsidian-bible-reference/
(src, scripts, tsconfig, eslint, prettier). The repo root remains the
installable plugin folder: manifest.json, versions.json, styles.css and
the built main.js stay at root.
- esbuild + version-bump resolve repo-root paths so the build emits main.js
to root and version-bump edits root manifest/versions from the package.
- Migrate plugin tests from jest to `bun test`. Only the provider test used
jest mocking; convert to bun:test (mock/spyOn). Mock the types-only
`obsidian` module via a bun test preload (bunfig.toml) since mock.module
is not hoisted like jest.mock.
- Root build builds packages in dependency order then the plugin; CI builds
before test because the plugin/tests import bible-reference-toolkit's dist.
- Update CI + release workflows to oven-sh/setup-bun; husky pre-commit,
dev-setup/release docs and VS Code tasks to bun.
- Pin plugin prettier to 3.8.3 (3.9.x reformats unrelated union types).
- Add @types/node to bible-reference-toolkit (was hoisted under pnpm).
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM
- pnpm-workspace.yaml declares packages/* (un-ignore it; now essential config)
- plugin depends on bible-reference-toolkit via workspace:*; toolkit -> bible-book-names-intl via workspace:*
- add rimraf devDep + skipLibCheck so packages build under this toolchain
- exclude packages/ from plugin tsconfig; ignore packages/*/dist build artifacts
- eslint + prettier ignore packages/ (they keep their own bun toolchain)
- README: document the packages contained in this monorepo
Claude-Session: https://claude.ai/code/session_013pCNGDmUyfJia4tE4aWoYM