npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

lsbible

v0.3.0

Published

LSBible API client - structured, type-safe Bible SDK for TypeScript

Readme

LSBible SDK for TypeScript

Structured, type-safe Bible API client for the Legacy Standard Bible (LSB).

Features

  • 100% Type-Safe - Full TypeScript support with Zod validation
  • Structured Parameters - No string parsing! Use client.getVerse(BookName.JOHN, 3, 16)
  • Complete Validation - All 66 books with chapter/verse validation
  • Rich Formatting - Preserves red-letter text, italics, bold, small-caps
  • Built-in Caching - Configurable TTL-based response cache
  • Modern Runtime Support - Works with Node.js 18+, Bun, and Deno

Installation

# Using npm
npm install lsbible

# Using pnpm
pnpm add lsbible

# Using bun
bun add lsbible

MCP Server

This SDK also includes a Model Context Protocol (MCP) server for integration with AI coding assistants like Cursor, Claude Code, VS Code, and more.

🤖 For MCP Server Installation: To use LSBible with AI coding tools, see the MCP Installation Guide → with step-by-step instructions for 20+ supported clients.

Quick Install Examples:

# Claude Code (remote)
claude mcp add --transport http lsbible https://lsbible.kdco.dev/mcp

# Claude Code (local)
claude mcp add lsbible -- npx -y lsbible-mcp

# Cursor/VS Code/etc. (see full guide)
# Add to your MCP config file - see mcp/README.md for details

Quick Start

import { LSBibleClient, BookName } from "lsbible";

const client = new LSBibleClient();

// Get a single verse (type-safe with enum)
const verse = await client.getVerse(BookName.JOHN, 3, 16);
console.log(verse.verses[0].plainText);
// Output: "For God so loved the world, that He gave His only Son..."

// Get a passage range
const passage = await client.getPassage(
  BookName.JOHN, 3, 16,
  BookName.JOHN, 3, 18
);
console.log(`Got ${passage.verseCount} verses`);

// Get an entire chapter
const chapter = await client.getChapter(BookName.PSALMS, 23);
console.log(`Psalm 23 has ${chapter.verseCount} verses`);

// Search for text
const results = await client.search("love");
console.log(`Found ${results.matchCount} matches in ${results.passageCount} passages`);

Design Philosophy

Structured Parameters Over String Parsing

This SDK prioritizes explicit, structured parameters over string-based references:

// ✅ GOOD - Structured, type-safe, validated
client.getVerse(BookName.JOHN, 3, 16);

// ❌ AVOID - String parsing (error-prone, less type-safe)
client.getVerse("John 3:16"); // NOT SUPPORTED

Why Structured Parameters?

  1. Type Safety - IDE autocomplete for all 66 books
  2. Early Validation - Catch errors before API calls
  3. No Parsing Ambiguity - Clear, explicit parameters
  4. Better Testing - Easy to generate test cases programmatically
  5. Language Agnostic - Works consistently across all SDKs

API Reference

LSBibleClient

import { LSBibleClient, MemoryCacheProvider } from "lsbible";

const client = new LSBibleClient({
  cache: {
    provider: new MemoryCacheProvider(), // Optional cache provider
    ttl: {
      verse: 2592000,   // 30 days (default)
      passage: 2592000, // 30 days (default)
      chapter: 2592000, // 30 days (default)
      search: 604800,   // 7 days (default)
    }
  },
  timeout: 30,        // Request timeout in seconds (default: 30)
  buildId: undefined, // Optional build ID (auto-detected if not provided)
  headers: {},        // Optional custom headers
});

Methods

getVerse(book, chapter, verse)

Get a specific verse with validated parameters.

// Using enum (recommended)
const verse = await client.getVerse(BookName.JOHN, 3, 16);

// Using string (validated at runtime)
const verse = await client.getVerse("John", 3, 16);

// Access structured data
for (const v of verse.verses) {
  console.log(`${v.reference.toString()}: ${v.plainText}`);

  // Access formatting
  for (const segment of v.segments) {
    if (segment.isRedLetter) {
      console.log(`Jesus said: "${segment.text}"`);
    }
  }
}

getPassage(fromBook, fromChapter, fromVerse, toBook, toChapter, toVerse)

Get a passage spanning multiple verses.

// Get John 3:16-18
const passage = await client.getPassage(
  BookName.JOHN, 3, 16,
  BookName.JOHN, 3, 18
);

console.log(`Title: ${passage.title}`);
console.log(`Verses: ${passage.verseCount}`);
console.log(`Single verse? ${passage.isSingleVerse}`);

getChapter(book, chapter)

Get an entire chapter.

// Get all of Psalm 23
const chapter = await client.getChapter(BookName.PSALMS, 23);

console.log(`Psalm 23 has ${chapter.verseCount} verses`);

for (const verse of chapter.verses) {
  console.log(`  ${verse.verseNumber}. ${verse.plainText}`);
}

search(query)

Search for passages containing text.

const results = await client.search("love");

console.log(`Found ${results.matchCount} matches`);
console.log(`Passages: ${results.passageCount}`);
console.log(`Total verses: ${results.totalVerses}`);

// Search results include metadata (for text searches)
if (results.hasSearchMetadata) {
  console.log(`Total results: ${results.totalCount}`);
  console.log(`Filtered: ${results.filteredCount}`);
  console.log(`By book:`, results.countsByBook);
}

// Iterate through results
for (const passage of results.passages) {
  console.log(`\n${passage.title}`);
  for (const verse of passage.verses) {
    console.log(`  ${verse.plainText}`);
  }
}

clearCache()

Clear the response cache (only available with MemoryCacheProvider).

client.clearCache();

Caching

The SDK supports pluggable caching through the CacheProvider interface, allowing you to choose the right caching strategy for your deployment environment.

Built-in Cache Providers

MemoryCacheProvider - In-memory caching with TTL support

  • ✅ Local development, testing, single-process apps
  • ❌ Multi-process apps, Cloudflare Workers, high-traffic production

NoopCacheProvider - Disables caching entirely

  • Useful for debugging or when caching isn't beneficial

CloudflareCacheProvider - Edge caching for Cloudflare Workers

Cache Configuration

import { LSBibleClient, MemoryCacheProvider, CacheTTL } from "lsbible";

const client = new LSBibleClient({
  cache: {
    provider: new MemoryCacheProvider(),
    ttl: {
      verse: CacheTTL.BIBLE_CONTENT,   // 30 days
      passage: CacheTTL.BIBLE_CONTENT, // 30 days
      chapter: CacheTTL.BIBLE_CONTENT, // 30 days
      search: CacheTTL.SEARCH_RESULTS, // 7 days
    }
  }
});

Recommended TTL constants:

  • CacheTTL.BIBLE_CONTENT (30 days) - Bible text is immutable
  • CacheTTL.SEARCH_RESULTS (7 days) - May change with API updates
  • CacheTTL.STATIC (1 year) - Never changes

Custom Cache Providers

Implement the CacheProvider interface for custom backends like Redis, Memcached, or DynamoDB:

interface CacheProvider {
  get<T>(key: string): Promise<T | undefined>;
  set<T>(key: string, value: T, ttl: number): Promise<void>;
}

Complete examples:

  • Memory Cache - Demonstrates cache hits/misses and manual clearing
  • Redis Cache - Full Redis implementation with ioredis
  • Custom Cache - Template for building your own cache provider

Cache Keys

The SDK uses these cache key patterns:

  • verse:{book} {chapter}:{verse} - Single verses
  • passage:{normalized query} - Passage ranges
  • chapter:{book} {chapter} - Full chapters
  • search:{query} - Text searches

Type System

BookName Enum

All 66 Bible books available as enum values:

import { BookName } from "lsbible";

// Old Testament
BookName.GENESIS
BookName.EXODUS
// ... through ...
BookName.MALACHI

// New Testament
BookName.MATTHEW
BookName.MARK
// ... through ...
BookName.REVELATION

VerseReference

interface VerseReference {
  bookNumber: number;      // 1-66
  chapter: number;         // Validated against book
  verse: number;           // Validated against chapter
  readonly bookName: BookName;   // Computed property
  toString(): string;      // e.g., "John 3:16"
}

TextSegment

interface TextSegment {
  text: string;
  isRedLetter: boolean;    // Words of Jesus
  isItalic: boolean;       // Italicized clarifications
  isBold: boolean;         // Bold text
  isSmallCaps: boolean;    // LORD (Yahweh)
}

VerseContent

interface VerseContent {
  reference: VerseReference;
  verseNumber: number;
  segments: TextSegment[];
  hasSubheading: boolean;
  subheadingText: string | null;
  isPoetry: boolean;
  isProse: boolean;
  chapterStart: boolean;
  readonly plainText: string;      // Computed property
  readonly formattedText: string;  // Computed property
}

Passage

interface Passage {
  fromRef: VerseReference;
  toRef: VerseReference;
  title: string;
  verses: VerseContent[];
  readonly isSingleVerse: boolean;  // Computed property
  readonly verseCount: number;      // Computed property
}

SearchResponse

interface SearchResponse {
  query: string;
  matchCount: number;
  passages: Passage[];
  durationMs: number;
  timestamp: number;

  // Optional (for text searches only)
  totalCount?: number;
  filteredCount?: number;
  countsByBook?: Record<string, number>;
  countsBySection?: Record<string, number>;

  // Computed properties
  readonly passageCount: number;
  readonly totalVerses: number;
  readonly hasSearchMetadata: boolean;
}

Error Handling

import { LSBibleClient, BookName, InvalidReferenceError, APIError } from "lsbible";

const client = new LSBibleClient();

try {
  // Invalid chapter (John only has 21 chapters)
  await client.getVerse(BookName.JOHN, 99, 1);
} catch (error) {
  if (error instanceof InvalidReferenceError) {
    console.error(`Invalid reference: ${error.message}`);
    // Output: "John only has 21 chapters, but chapter 99 was requested"
  }
}

try {
  // Invalid verse (John 3 only has 36 verses)
  await client.getVerse(BookName.JOHN, 3, 999);
} catch (error) {
  if (error instanceof InvalidReferenceError) {
    console.error(`Invalid reference: ${error.message}`);
    // Output: "John 3 only has 36 verses, but verse 999 was requested"
  }
}

try {
  // Invalid book name
  await client.getVerse("NotABook", 1, 1);
} catch (error) {
  if (error instanceof InvalidReferenceError) {
    console.error(`Invalid reference: ${error.message}`);
    // Output: "Unknown book: NotABook"
  }
}

try {
  // API failure
  await client.search("something");
} catch (error) {
  if (error instanceof APIError) {
    console.error(`API error: ${error.message}`);
  }
}

Bible Structure Data

The SDK includes complete validation data for all 66 books:

  • Old Testament: 39 books (Genesis through Malachi)
  • New Testament: 27 books (Matthew through Revelation)
  • Total: 1,189 chapters, 31,102 verses
import { BIBLE_STRUCTURE, BOOK_NAMES, BOOK_NUMBERS } from "lsbible";

// Get book info
const johnInfo = BIBLE_STRUCTURE[43];
console.log(johnInfo.name);        // "John"
console.log(johnInfo.chapters);    // 21
console.log(johnInfo.verses[2]);   // Chapter 3 has 36 verses

// Lookup book number
const bookNum = BOOK_NUMBERS["john"];  // 43

// Lookup book name
const bookName = BOOK_NAMES[43];  // "John"

Runtime Support

The SDK is designed to work across modern JavaScript runtimes:

  • Node.js 18.0.0 or higher (native fetch support)
  • Bun (optimized build performance)
  • Deno (works out of the box)

Examples

Example 1: Display a Verse with Formatting

import { LSBibleClient, BookName } from "lsbible";

const client = new LSBibleClient();
const verse = await client.getVerse(BookName.JOHN, 3, 16);

for (const v of verse.verses) {
  console.log(`\n${v.reference.toString()}\n`);

  for (const segment of v.segments) {
    let text = segment.text;
    if (segment.isRedLetter) text = `\x1b[31m${text}\x1b[0m`; // Red
    if (segment.isItalic) text = `\x1b[3m${text}\x1b[0m`;     // Italic
    if (segment.isSmallCaps) text = text.toUpperCase();      // Small caps -> uppercase
    process.stdout.write(text + " ");
  }
  console.log("\n");
}

Example 2: Find All Occurrences of a Word

import { LSBibleClient } from "lsbible";

const client = new LSBibleClient();
const results = await client.search("faith");

console.log(`Found "${results.query}" ${results.matchCount} times\n`);

for (const passage of results.passages) {
  for (const verse of passage.verses) {
    console.log(`${verse.reference.toString()}: ${verse.plainText}`);
  }
}

Example 3: Read an Entire Book

import { LSBibleClient, BookName, BIBLE_STRUCTURE } from "lsbible";

const client = new LSBibleClient();
const bookNum = 57; // Philemon
const bookInfo = BIBLE_STRUCTURE[bookNum];

console.log(`Reading ${bookInfo.name}\n`);

for (let chapter = 1; chapter <= bookInfo.chapters; chapter++) {
  const passage = await client.getChapter(BookName.PHILEMON, chapter);

  console.log(`\nChapter ${chapter}\n`);

  for (const verse of passage.verses) {
    console.log(`${verse.verseNumber}. ${verse.plainText}`);
  }
}

Contributing

This SDK is part of the LSBible monorepo. Contributions are welcome!

See the main repository for contribution guidelines: https://github.com/kdcokenny/lsbible

License

MIT License - See LICENSE file for details.

Related