npm package: @nulz-rip/host
Description: Official JavaScript/TypeScript SDK for Nulz Host. Upload images, manage folders, links, and presets programmatically.
Author: Andrew P. Harper
Latest version: 0.1.0

# @nulz-rip/host

Node.js SDK for [Nulz Host](https://nulz.host) image hosting. TypeScript, ESM, Node 18+.

Upload images, manage folders, short links, and upload presets programmatically.

## Get an API key

1. Go to **[https://nulz.host](https://nulz.host)** and sign up or log in.
2. Open **Settings** and generate an **API key** (64-character hex).
3. Use it in the SDK as below.

## Install

```bash
npm install @nulz-rip/host
```

## Usage

```ts
import { HostClient } from '@nulz-rip/host'

const client = new HostClient({
  apiKey: process.env.NULZ_HOST_API_KEY!,
  baseUrl: 'https://api.nulz.host', // optional; default
  timeoutMs: 60_000, // optional; uploads can be large
})

// Upload an image (Blob, File, or Node Buffer)
const file = new Blob([...]) // or fs.readFileSync(...) as Buffer, or File in browser
const result = await client.upload(file, {
  fileName: 'photo.png', // optional
  maxViews: 100,         // optional; Premium/Cyber
  expiresIn: '7d',       // optional; e.g. "30m", "24h", "7d"; Premium/Cyber
  folderId: 'folderId',  // optional
  presetId: 'presetId',  // optional
})
console.log(result.id, result.url)

// List images
const { images, total } = await client.listImages({ page: 1, limit: 25, folderId: 'root' })

// Update image (folder, maxViews, expiresIn)
await client.updateImage(result.id, { folderId: null, maxViews: 50 })

// Delete image
await client.deleteImage(result.id)

// Folders (Premium/Cyber)
const folder = await client.createFolder({ name: 'Screenshots', parentFolderId: null })
const { folders } = await client.listFolders({ parentFolderId: null })
await client.updateFolder(folder.id, { name: 'New name' })
await client.deleteFolder(folder.id)

// Short links
const link = await client.createLink({ url: 'https://example.com', slug: 'my-link' })
const { links } = await client.listLinks({ page: 1, limit: 25 })
await client.deleteLink(link.id)

// Presets (Premium/Cyber)
const preset = await client.createPreset({ name: 'Default', maxViews: 100, expiresIn: '24h' })
const { presets } = await client.listPresets()
await client.updatePreset(preset.id, { name: 'Updated' })
await client.deletePreset(preset.id)

// User & API key
const me = await client.me()
const { apiKey } = await client.apiKey.get()
await client.apiKey.create() // regenerate
await client.apiKey.revoke()

// Activity heatmap (last 365 days)
const { activity } = await client.getImageActivity()

// Config & health
const config = await client.config()
const health = await client.health()
```

## API summary

| Method | Description |
|--------|-------------|
| **upload(file, options?)** | Upload image (Blob/File/Buffer). Options: fileName, maxViews, expiresIn, folderId, presetId. |
| **listImages(options?)** | List images. Options: page, limit, folderId ('root' = no folder). |
| **getImageActivity()** | Daily upload counts for heatmap. |
| **updateImage(id, body)** | PATCH folderId, maxViews, expiresIn, expiresAt. |
| **deleteImage(id)** | Delete image. |
| **replaceImage(id, file)** | Replace image file (Cyber plan only). |
| **createFolder(body)** | Create folder (name, parentFolderId?). |
| **listFolders(options?)** | List folders. Options: parentFolderId. |
| **updateFolder(id, body)** | PATCH name, parentFolderId. |
| **deleteFolder(id)** | Delete folder (images move to root). |
| **createLink(body)** | Create short link (url, slug?). |
| **listLinks(options?)** | List links. Options: page, limit. |
| **deleteLink(id)** | Delete link. |
| **createPreset(body)** | Create upload preset (name, maxViews?, expiresIn?, folderId?). |
| **listPresets()** | List presets. |
| **updatePreset(id, body)** | PATCH name, maxViews, expiresIn, folderId. |
| **deletePreset(id)** | Delete preset. |
| **me()** | Current user (plan, storage, limits). |
| **apiKey.get()** | Get current API key. |
| **apiKey.create()** | Regenerate API key. |
| **apiKey.revoke()** | Revoke API key. |
| **config()** | Public config (imageBaseUrl, etc.). |
| **health()** | Public health check. |

Errors: `NulzAPIError` with `.message`, `.status`, `.body`.

## License

MIT