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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@jukasdrj/bookstrack-api-client

v1.0.1

Published

TypeScript SDK for BooksTrack API - Auto-generated from OpenAPI specification

Readme

@bookstrack/api-client

Official TypeScript SDK for BooksTrack API

Auto-generated from OpenAPI specification using openapi-typescript + openapi-fetch.

Features

  • Type-Safe - Full TypeScript support with auto-generated types
  • Lightweight - ~2KB gzipped client (tree-shakable)
  • Modern - Uses native fetch API (works in browser, Node.js, Cloudflare Workers)
  • Auto-Generated - Always in sync with API contract via OpenAPI spec

Installation

npm install @bookstrack/api-client

Note: This package is published to GitHub Packages. Ensure your .npmrc is configured:

@bookstrack:registry=https://npm.pkg.github.com

Quick Start

Basic Usage

import { createBooksTrackClient } from '@bookstrack/api-client'

const client = createBooksTrackClient({
  baseUrl: 'https://api.oooefam.net'
})

// Search by ISBN
const { data, error } = await client.GET('/v1/search/isbn', {
  params: { query: { isbn: '9780439708180' } }
})

if (error) {
  console.error('API Error:', error)
} else {
  console.log('Book:', data.data)
}

Advanced Usage

Custom Headers (Authentication)

const client = createBooksTrackClient({
  baseUrl: 'https://api.oooefam.net',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN'
  }
})

POST Requests (Batch Enrichment)

const { data, error } = await client.POST('/v1/enrich/batch', {
  body: {
    workIds: ['OL123W', 'OL456W'],
    force: false
  }
})

if (error) {
  console.error('Failed to enrich:', error)
} else {
  console.log('Job ID:', data.data.jobId)
}

WebSocket Progress Tracking

// Start batch job
const { data: job } = await client.POST('/v1/enrich/batch', {
  body: { workIds: [...] }
})

const jobId = job.data.jobId

// Connect to WebSocket
const ws = new WebSocket(`wss://api.oooefam.net/ws/progress?jobId=${jobId}`)

ws.onmessage = (event) => {
  const progress = JSON.parse(event.data)
  console.log(`Progress: ${progress.progress * 100}%`)
}

Polling Job Status (Fallback)

const { data } = await client.GET('/v1/jobs/{jobId}/status', {
  params: { path: { jobId: 'job-uuid' } }
})

console.log('Job Status:', data.data.status)
console.log('Progress:', data.data.progress)

API Reference

Client Creation

createBooksTrackClient(options?: {
  baseUrl?: string
  headers?: HeadersInit
  credentials?: RequestCredentials
})

Available Endpoints

All endpoints are fully typed. Use TypeScript autocomplete to explore:

  • GET /health - Health check
  • GET /v1/search/isbn - Search by ISBN
  • GET /v1/search/title - Search by title
  • POST /v1/enrich/batch - Batch enrichment
  • POST /v2/import/workflow - CSV import workflow
  • GET /v1/jobs/{jobId}/status - Job status polling
  • GET /ws/progress - WebSocket progress (upgrade)

Response Format

All API responses follow the canonical ResponseEnvelope format:

Success:

{
  success: true,
  data: { /* canonical book object */ },
  metadata: {
    source: 'google_books',
    cached: true,
    timestamp: '2025-01-10T12:00:00Z'
  }
}

Error:

{
  success: false,
  error: {
    code: 'RATE_LIMIT_EXCEEDED',
    message: 'Too many requests',
    statusCode: 429,
    retryable: true,
    retryAfterMs: 60000
  }
}

Error Handling

Circuit Breaker Errors

The API uses circuit breakers for external providers. Handle these gracefully:

const { data, error } = await client.GET('/v1/search/isbn', {
  params: { query: { isbn: '...' } }
})

if (error) {
  if (error.code === 'CIRCUIT_OPEN') {
    // Provider is temporarily unavailable
    console.warn('Provider down, try again in 60s')
  } else if (error.retryable) {
    // Safe to retry after retryAfterMs
    setTimeout(() => retry(), error.retryAfterMs)
  }
}

Common Error Codes

  • MISSING_ISBN - Required parameter missing
  • RATE_LIMIT_EXCEEDED - Too many requests
  • CIRCUIT_OPEN - External provider circuit breaker open
  • NOT_FOUND - Book not found
  • API_ERROR - External API failure
  • INTERNAL_ERROR - Server error

Development

Generating Types

The SDK is auto-generated from docs/openapi.yaml:

npm run generate

This creates src/schema.ts with all API types.

Building

npm run build

Output: dist/index.js and dist/index.d.ts

Publishing (Automated via CI/CD)

Publishing is handled automatically by GitHub Actions when:

  • Pushing to main branch
  • Creating a new release/tag

Manual publish:

npm run prepublishOnly
npm publish

Framework Examples

React (with React Query)

import { useQuery } from '@tanstack/react-query'
import { client } from '@bookstrack/api-client'

function BookSearch({ isbn }: { isbn: string }) {
  const { data, error, isLoading } = useQuery({
    queryKey: ['book', isbn],
    queryFn: async () => {
      const res = await client.GET('/v1/search/isbn', {
        params: { query: { isbn } }
      })
      if (res.error) throw new Error(res.error.message)
      return res.data.data
    }
  })

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>

  return <div>{data?.title}</div>
}

Vue 3 (with Composition API)

import { ref } from 'vue'
import { client } from '@bookstrack/api-client'

export function useBookSearch(isbn: string) {
  const book = ref(null)
  const error = ref(null)
  const loading = ref(false)

  async function search() {
    loading.value = true
    const res = await client.GET('/v1/search/isbn', {
      params: { query: { isbn } }
    })

    if (res.error) {
      error.value = res.error
    } else {
      book.value = res.data.data
    }
    loading.value = false
  }

  return { book, error, loading, search }
}

Svelte

import { writable } from 'svelte/store'
import { client } from '@bookstrack/api-client'

export function createBookStore() {
  const { subscribe, set, update } = writable({
    book: null,
    loading: false,
    error: null
  })

  async function searchByISBN(isbn: string) {
    update(s => ({ ...s, loading: true }))

    const { data, error } = await client.GET('/v1/search/isbn', {
      params: { query: { isbn } }
    })

    if (error) {
      set({ book: null, loading: false, error })
    } else {
      set({ book: data.data, loading: false, error: null })
    }
  }

  return { subscribe, searchByISBN }
}

Migration from Legacy API

If you're migrating from the old /search/* endpoints (deprecated March 1, 2026):

Before (Legacy)

const res = await fetch('https://api.oooefam.net/search/isbn?isbn=123')
const book = await res.json()

After (SDK)

import { client } from '@bookstrack/api-client'

const { data } = await client.GET('/v1/search/isbn', {
  params: { query: { isbn: '123' } }
})

const book = data.data

Key Changes:

  • All responses now use ResponseEnvelope format
  • Success/error discriminator via success field
  • Metadata includes source, cache status, timestamp
  • Error responses include structured error codes

Support

  • Documentation: https://github.com/yourusername/bendv3/tree/main/docs
  • API Contract: https://github.com/yourusername/bendv3/blob/main/docs/API_CONTRACT.md
  • Issues: https://github.com/yourusername/bendv3/issues

License

MIT


Generated from OpenAPI Spec Version: (auto-updated on publish) Last Updated: 2025-11-28