npm package: @ralalabs/otp-manager-client
Description: Client for otp-manager — OTP extraction API on Cloudflare Workers.
Latest version: 1.0.0

# @ralalabs/otp-manager-client

TypeScript client for [otp-manager](../worker/) — an OTP extraction API running on Cloudflare Workers.

Zero dependencies. Uses global `fetch`. Works on Node.js 20+, Cloudflare Workers, Deno, Bun, and any edge runtime.

## Install

```bash
npm install @ralalabs/otp-manager-client
# or
pnpm add @ralalabs/otp-manager-client
```

## Quick start

```ts
import { OtpManagerClient } from '@ralalabs/otp-manager-client';

const client = new OtpManagerClient({
  baseUrl: 'https://otp-manager.your-sub.workers.dev',
  token: 'YOUR_API_TOKEN'
});

const { otp } = await client.getLatestOtp({ profile: 'your-profile' });
console.log('OTP:', otp);
```

## Poll for OTP

Wait for an OTP to arrive after triggering a login flow:

```ts
const after = new Date().toISOString();

// ... trigger login ...

const result = await client.pollOtp({
  profile: 'your-profile',
  after,
  timeoutMs: 60_000,  // wait up to 60s
  intervalMs: 3_000,  // poll every 3s
  deleteAfter: true    // clean up after retrieval
});

if (result) {
  console.log('OTP:', result.otp);
} else {
  console.log('Timed out');
}
```

## API

### Constructor

```ts
new OtpManagerClient({
  baseUrl: string;    // Worker URL
  token: string;      // Bearer token
  fetch?: typeof fetch; // Custom fetch (optional, defaults to globalThis.fetch)
})
```

### OTP methods

```ts
client.getLatestOtp(params?)    // GET /otp/latest
client.getOtpHistory(params?)   // GET /otp/history
client.extractLatest(params?)   // POST /otp/extract/latest
client.listProfiles()           // GET /otp/profiles
client.getProfile(name)         // GET /otp/profiles/:name
client.pollOtp(params?)         // Poll until OTP arrives or timeout
```

### Email methods

```ts
client.listEmails(params?)          // GET /emails
client.getEmail(id)                 // GET /emails/:id
client.getRawEmail(id)              // GET /emails/:id/raw (returns Response)
client.extractFromEmail(id, params?) // POST /emails/:id/extract-otp
client.deleteEmail(id)              // DELETE /emails/:id
```

### Health

```ts
client.health()  // GET /health
```

### Parameters

**`getLatestOtp`** — `profile`, `from`, `to`, `subject`, `q`, `after`, `lang`, `label`, `codeLength`, `sourcePart`, `strict`

**`getOtpHistory`** — `limit`, `before`, `after`, `from`, `to`, `subject`, `q`, `profile`

**`extractLatest`** — `profile`, `lang`, `codeLength`, `label`, `sourcePart`, `strict`, `from`, `to`, `subject`, `q`, `after`

**`listEmails`** — `limit`, `before`, `after`, `from`, `to`, `subject`, `q`, `hasOtp`

**`pollOtp`** — all `getLatestOtp` params plus `timeoutMs` (default 60000), `intervalMs` (default 3000), `deleteAfter` (default false)

### Error handling

```ts
import { OtpManagerClient, OtpManagerError } from '@ralalabs/otp-manager-client';

try {
  const res = await client.getLatestOtp({ profile: 'your-profile' });
} catch (err) {
  if (err instanceof OtpManagerError) {
    console.log(err.status);       // HTTP status code
    console.log(err.body.message); // Error message from API
  }
}
```

### Custom fetch

Pass a custom `fetch` for testing or environments without global fetch:

```ts
import { OtpManagerClient } from '@ralalabs/otp-manager-client';
import { fetch } from 'undici';

const client = new OtpManagerClient({
  baseUrl: 'https://otp-manager.your-sub.workers.dev',
  token: 'YOUR_TOKEN',
  fetch
});
```

## Responses

All methods return typed responses. Key types:

```ts
import type {
  OtpLatestResponse,
  OtpExtraction,
  EmailRecord,
  ExtractionResult
} from '@ralalabs/otp-manager-client';
```

The `getLatestOtp` response includes:
- `otp` — the code (null if expired)
- `expired` — boolean
- `extraction` — full extraction record with confidence, matched pattern, context
- `email` — email metadata (or null)

Status `410` (expired OTP) is not thrown as an error — it returns normally with `expired: true` and `otp: null`.

## License

MIT