npm package: @prismfy/prismfy
Description: Official JavaScript/TypeScript SDK for the Prismfy web search API
Author: Prismfy
Latest version: 1.0.0

# Prismfy JavaScript / TypeScript SDK

Official JS/TS client for the [Prismfy](https://prismfy.io) web search API.
Works in **Node.js 18+**, **Bun**, **Deno**, and modern **browsers**.

## Installation

```bash
npm install prismfy
# or
yarn add prismfy
# or
pnpm add prismfy
```

## Quick start

```typescript
import { Prismfy } from 'prismfy'

const client = new Prismfy({ apiKey: 'ss_live_...' })

const result = await client.search('best Python libraries 2026')

for (const r of result.results) {
  console.log(r.title)
  console.log(r.url)
  console.log(r.content.slice(0, 200))
  console.log()
}
```

Use environment variables (recommended):

```bash
export PRISMFY_API_KEY="ss_live_..."
```

```typescript
const client = new Prismfy()  // reads PRISMFY_API_KEY automatically
```

## Features

- ✅ Full TypeScript support — complete type definitions included
- ✅ Zero dependencies — uses native `fetch` (Node 18+, browsers, Bun, Deno)
- ✅ Works in browsers, Node.js, Bun, Deno, and edge runtimes (Vercel, Cloudflare)
- ✅ ESM + CommonJS — both module formats shipped
- ✅ Typed error hierarchy with specific exception classes
- ✅ Overloaded `search()` — accepts string or object

## Usage

### Search

```typescript
// Simple string argument
const result = await client.search('TypeScript generics')

// Full options
const result = await client.search('transformer architecture', {
  engines:   ['arxiv', 'pubmed', 'google'],  // Pro/Enterprise
  timeRange: 'month',
  language:  'en',
  page:      1,
})

// Domain-scoped
const result = await client.search('Array.from()', {
  domain: 'developer.mozilla.org',
})

// Object-style (alternative)
const result = await client.search({
  query:    'rust async programming',
  page:     2,
  engines:  ['google', 'bing'],
})

// Access results
console.log(`${result.results.length} results, cached: ${result.cached}`)
console.log(`Duration: ${result.meta.durationMs}ms`)
console.log(`Engines: ${result.meta.engines.join(', ')}`)
```

### User profile & quota

```typescript
const profile = await client.user.me()
console.log(profile.tier)              // 'free' | 'pro' | 'enterprise'
console.log(profile.quota.remaining)   // requests left this month
console.log(profile.quota.expiresAt)   // reset date
```

### Search history

```typescript
const history = await client.user.searches({ limit: 20 })
for (const item of history.searches) {
  console.log(item.query, item.status, item.durationMs)
}
```

### Usage / billing log

```typescript
const usage = await client.user.usage({ limit: 50 })
for (const event of usage.history) {
  console.log(event.event, event.createdAt)
}
```

### API key management

```typescript
// List all keys
const keys = await client.user.keys()

// Create a new key
const newKey = await client.user.createKey({ name: 'Production' })
console.log(newKey.key)  // ss_live_... — save this, shown only once!

// Delete a key (immediately invalidated)
await client.user.deleteKey(newKey.id)
```

### Plans & engine catalog

```typescript
const { plans, allEngines, engines } = await client.plans()

plans.forEach(p => {
  console.log(`${p.name}: ${p.limits.rpm} rpm, ${p.limits.requestsMonth} req/mo`)
})

console.log('All engines:', allEngines)
// ['google', 'bing', 'brave', 'yahoo', 'yahoo news', 'hackernews', ...]
```

### Health check

```typescript
const health = await client.health()
console.log(health.status, health.checks)
// { status: 'ok', checks: { db: 'ok', redis: 'ok' } }
```

## Error handling

```typescript
import {
  Prismfy,
  PrismfyRateLimitError,
  PrismfyQuotaError,
  PrismfyAuthError,
  PrismfyTimeoutError,
  PrismfyError,
} from 'prismfy'

const client = new Prismfy()

try {
  const result = await client.search('query')
} catch (err) {
  if (err instanceof PrismfyRateLimitError) {
    console.log(`Rate limited. Retry after ${err.retryAfterSeconds}s`)
    await new Promise(r => setTimeout(r, err.retryAfterMs + 100))
  } else if (err instanceof PrismfyQuotaError) {
    console.log('Monthly quota exhausted. Upgrade your plan.')
  } else if (err instanceof PrismfyAuthError) {
    console.log('Invalid API key.')
  } else if (err instanceof PrismfyTimeoutError) {
    console.log('Search timed out. Try again.')
  } else if (err instanceof PrismfyError) {
    console.log(`API error: ${err.statusCode} ${err.code} — ${err.message}`)
  }
}
```

### Retry with exponential backoff

```typescript
async function searchWithRetry(
  client: Prismfy,
  query: string,
  maxRetries = 3,
) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await client.search(query)
    } catch (err) {
      if (err instanceof PrismfyRateLimitError) {
        if (attempt === maxRetries - 1) throw err
        await new Promise(r => setTimeout(r, err.retryAfterMs + 100))
      } else if (err instanceof PrismfyTimeoutError) {
        if (attempt === maxRetries - 1) throw err
        await new Promise(r => setTimeout(r, 1000 * 2 ** attempt))
      } else {
        throw err
      }
    }
  }
}
```

## Next.js / React example

```typescript
// app/api/search/route.ts
import { Prismfy } from 'prismfy'
import { NextResponse } from 'next/server'

const client = new Prismfy()  // reads PRISMFY_API_KEY

export async function POST(req: Request) {
  const { query } = await req.json()
  const result = await client.search(query)
  return NextResponse.json(result)
}
```

```typescript
// components/SearchBox.tsx
'use client'
import { useState } from 'react'
import type { SearchResponse } from 'prismfy'

export function SearchBox() {
  const [results, setResults] = useState<SearchResponse | null>(null)

  async function handleSearch(query: string) {
    const res = await fetch('/api/search', {
      method:  'POST',
      headers: { 'Content-Type': 'application/json' },
      body:    JSON.stringify({ query }),
    })
    setResults(await res.json())
  }

  return (
    <div>
      <input onKeyDown={e => e.key === 'Enter' && handleSearch(e.currentTarget.value)} />
      {results?.results.map(r => (
        <div key={r.url}>
          <a href={r.url}>{r.title}</a>
          <p>{r.content}</p>
        </div>
      ))}
    </div>
  )
}
```

## Engine reference

| Engine | Category | Free | Pro/Enterprise |
|--------|----------|------|----------------|
| `google` | Web | ✅ | ✅ |
| `bing` | Web | ✅ | ✅ |
| `brave` | Web | — | ✅ |
| `yahoo` | Web | — | ✅ |
| `yahoo news` | News | — | ✅ |
| `hackernews` | News | — | ✅ |
| `github` | Code | — | ✅ |
| `pypi` | Code | — | ✅ |
| `mdn` | Code | — | ✅ |
| `arxiv` | Academic | — | ✅ |
| `pubmed` | Academic | — | ✅ |
| `huggingface` | AI | — | ✅ |
| `reddit` | Community | — | ✅ |
| `wikipedia` | Reference | — | ✅ |
| `askubuntu` | Q&A | — | ✅ |

## License

MIT