npm package: @sirv/rest-api-js
Description: Official SDK for Sirv REST API - Works with JavaScript, Node.js, and TypeScript
Author: Sirv
Latest version: 1.0.1

# @sirv/rest-api-js

Official SDK for the [Sirv REST API](https://apidocs.sirv.com/). Works with JavaScript, Node.js, and TypeScript for image management, 360 spins, CDN delivery, and more.

## Features

- Works in **Browser**, **Node.js**, and **TypeScript**
- Full TypeScript type definitions included
- Automatic token management with refresh
- Async iterators for pagination (Node.js/TypeScript)
- Retry logic with exponential backoff
- All 40+ API endpoints implemented

## Installation

```bash
npm install @sirv/rest-api-js
```

Or with yarn:

```bash
yarn add @sirv/rest-api-js
```

### CDN (Browser)

```html
<script src="https://unpkg.com/@sirv/rest-api-js/dist/sirv.min.js"></script>
```

## Quick Start

### JavaScript (Browser)

```html
<script src="https://unpkg.com/@sirv/rest-api-js/dist/sirv.min.js"></script>
<script>
const sirv = new SirvClient({
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret'
});

sirv.connect().then(() => {
  sirv.getAccountInfo().then(account => {
    console.log('CDN URL:', account.cdnURL);
  });
});
</script>
```

### Node.js

```javascript
const { SirvClient } = require('@sirv/rest-api-js');

const sirv = new SirvClient({
  clientId: process.env.SIRV_CLIENT_ID,
  clientSecret: process.env.SIRV_CLIENT_SECRET
});

async function main() {
  await sirv.connect();

  const account = await sirv.getAccountInfo();
  console.log('CDN URL:', account.cdnURL);

  // Upload from file path (Node.js only)
  await sirv.uploadFile('/images/photo.jpg', './local-photo.jpg');
}

main();
```

### TypeScript

```typescript
import { SirvClient, AccountInfo, SearchResult } from '@sirv/rest-api-js';

const client = new SirvClient({
  clientId: process.env.SIRV_CLIENT_ID!,
  clientSecret: process.env.SIRV_CLIENT_SECRET!
});

async function main(): Promise<void> {
  await client.connect();

  const account: AccountInfo = await client.getAccountInfo();
  console.log('CDN URL:', account.cdnURL);

  // Use async iterators for pagination
  for await (const file of client.iterateSearchResults({ query: 'product' })) {
    console.log(file.filename);
  }
}

main();
```

## Configuration

```javascript
const sirv = new SirvClient({
  // Required
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',

  // Optional
  baseUrl: 'https://api.sirv.com',  // API base URL
  autoRefreshToken: true,            // Auto-refresh token before expiry
  tokenRefreshBuffer: 60,            // Seconds before expiry to refresh
  timeout: 30000,                    // Request timeout in ms
  maxRetries: 3                      // Retry failed requests
});
```

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `clientId` | string | - | Your Sirv API client ID (required) |
| `clientSecret` | string | - | Your Sirv API client secret (required) |
| `baseUrl` | string | https://api.sirv.com | API base URL |
| `autoRefreshToken` | boolean | true | Automatically refresh tokens |
| `tokenRefreshBuffer` | number | 60 | Seconds before expiry to refresh |
| `timeout` | number | 30000 | Request timeout in milliseconds |
| `maxRetries` | number | 3 | Max retries for failed requests |

## API Reference

### Authentication

```javascript
// Connect and obtain token (called automatically when needed)
await sirv.connect();

// Optional: Request a token with custom expiry time (5-604800 seconds)
await sirv.connect(3600); // Token valid for 1 hour

// Check if connected
if (sirv.isConnected()) {
  console.log('Connected!');
}

// Get current token
const token = sirv.getAccessToken();
```

The `connect()` method accepts an optional `expiresIn` parameter to customize token lifetime:
- **Range**: 5 to 604800 seconds (7 days)
- **Default**: 1200 seconds (20 minutes)

### Account API

```javascript
// Get account info
const account = await sirv.getAccountInfo();

// Update account settings
await sirv.updateAccount({ minify: { enabled: true } });

// Get rate limits
const limits = await sirv.getAccountLimits();

// Get storage info
const storage = await sirv.getStorageInfo();

// Get account users
const users = await sirv.getAccountUsers();

// Get billing plan
const plan = await sirv.getBillingPlan();

// Search events
const events = await sirv.searchEvents({ level: 'error' });

// Mark events seen
await sirv.markEventsSeen(['event-id-1', 'event-id-2']);
```

### User API

```javascript
// Get current user
const user = await sirv.getUserInfo();

// Get specific user
const user = await sirv.getUserInfo('user-id');
```

### Files API

```javascript
// Get file info
const info = await sirv.getFileInfo('/images/photo.jpg');

// Read folder contents
const folder = await sirv.readFolderContents('/images');

// Iterate folder (Node.js/TypeScript - handles pagination)
for await (const file of sirv.iterateFolderContents('/images')) {
  console.log(file.filename);
}

// Search files
const results = await sirv.searchFiles({
  query: 'product',
  size: 20,
  sort: { field: 'mtime', order: 'desc' },
  filters: { extension: ['jpg', 'png'] }
});

// Iterate search results (Node.js/TypeScript)
for await (const file of sirv.iterateSearchResults({ query: 'product' })) {
  console.log(file.filename);
}

// Scroll through results manually
const moreResults = await sirv.scrollSearch(results.scrollId);
```

### Upload & Download

```javascript
// Upload - Browser (File or Blob)
const fileInput = document.getElementById('fileInput');
await sirv.uploadFile('/uploads/photo.jpg', fileInput.files[0]);

// Upload - Node.js (file path)
await sirv.uploadFile('/images/photo.jpg', './local-file.jpg');

// Upload - Node.js (Buffer)
const buffer = fs.readFileSync('./photo.jpg');
await sirv.uploadFile('/images/photo.jpg', buffer);

// Upload - Node.js (Stream)
const stream = fs.createReadStream('./large-file.jpg');
await sirv.uploadFile('/images/large.jpg', stream);

// Download to Buffer/ArrayBuffer
const data = await sirv.downloadFile('/images/photo.jpg');

// Download to file (Node.js only)
await sirv.downloadFileTo('/images/photo.jpg', './downloaded.jpg');
```

### File Operations

```javascript
// Create folder
await sirv.createFolder('/new-folder');

// Delete file
await sirv.deleteFile('/images/old-photo.jpg');

// Batch delete
const result = await sirv.batchDelete(['/img1.jpg', '/img2.jpg']);

// Copy file
await sirv.copyFile({ from: '/a.jpg', to: '/b.jpg' });

// Rename/move
await sirv.renameFile({ from: '/old.jpg', to: '/new.jpg' });

// Fetch from URL
await sirv.fetchUrl({
  url: 'https://example.com/image.jpg',
  filename: '/fetched/image.jpg',
  wait: true
});

// Create ZIP
const zipResult = await sirv.batchZip({
  filenames: ['/img1.jpg', '/img2.jpg'],
  filename: '/archive.zip'
});
```

### Metadata API

```javascript
// Get all metadata
const meta = await sirv.getFileMeta('/images/photo.jpg');

// Set metadata
await sirv.setFileMeta('/images/photo.jpg', {
  title: 'Product Photo',
  description: 'Main image',
  tags: ['product', 'featured']
});

// Title
const { title } = await sirv.getFileTitle('/images/photo.jpg');
await sirv.setFileTitle('/images/photo.jpg', 'New Title');

// Description
const { description } = await sirv.getFileDescription('/images/photo.jpg');
await sirv.setFileDescription('/images/photo.jpg', 'New description');

// Tags
const { tags } = await sirv.getFileTags('/images/photo.jpg');
await sirv.addFileTags('/images/photo.jpg', ['new', 'tags']);
await sirv.removeFileTags('/images/photo.jpg', ['old']);

// Product metadata
const product = await sirv.getProductMeta('/products/item.jpg');
await sirv.setProductMeta('/products/item.jpg', {
  id: 'SKU-123',
  name: 'Product Name',
  brand: 'Brand'
});

// Approval flag
const { approved } = await sirv.getApprovalFlag('/images/photo.jpg');
await sirv.setApprovalFlag('/images/photo.jpg', true);
```

### Folder Options

```javascript
const options = await sirv.getFolderOptions('/products');

await sirv.setFolderOptions('/products', {
  scanSpins: true,
  allowListing: false
});
```

### JWT API

```javascript
const jwt = await sirv.generateJwt({
  filename: '/private/document.pdf',
  expiresIn: 3600  // 1 hour
});
console.log('Protected URL:', jwt.url);
```

### Spins/360 API

```javascript
// Convert spin to video
const video = await sirv.spin2Video({
  filename: '/spins/product.spin',
  options: { width: 1920, height: 1080, loops: 2 }
});

// Convert video to spin
const spin = await sirv.video2Spin({
  filename: '/videos/product.mp4',
  targetFilename: '/spins/product.spin',
  options: { frames: 36 }
});

// Export to marketplaces
await sirv.exportSpinToAmazon({ filename: '/spins/p.spin', asin: 'B08XXX' });
await sirv.exportSpinToWalmart({ filename: '/spins/p.spin', productId: 'xxx' });
await sirv.exportSpinToHomeDepot({ filename: '/spins/p.spin', productId: 'xxx' });
await sirv.exportSpinToLowes({ filename: '/spins/p.spin', productId: 'xxx' });
await sirv.exportSpinToGrainger({ filename: '/spins/p.spin', productId: 'xxx' });
```

### Points of Interest API

```javascript
// Get POIs
const pois = await sirv.getPointsOfInterest('/spins/product.spin');

// Set POI
await sirv.setPointOfInterest('/spins/product.spin', {
  name: 'Feature 1',
  x: 0.5,
  y: 0.3,
  frame: 10
});

// Delete POI
await sirv.deletePointOfInterest('/spins/product.spin', 'Feature 1');
```

### Statistics API

```javascript
// HTTP stats
const httpStats = await sirv.getHttpStats({
  from: '2024-01-01',
  to: '2024-01-31'
});

// Spin views (max 5-day range)
const spinStats = await sirv.getSpinViewsStats({
  from: '2024-01-01',
  to: '2024-01-05'
});

// Storage stats
const storageStats = await sirv.getStorageStats({
  from: '2024-01-01',
  to: '2024-01-31'
});
```

## Error Handling

```javascript
import { SirvApiError } from '@sirv/rest-api-js';

try {
  await sirv.uploadFile('/images/photo.jpg', file);
} catch (error) {
  if (error instanceof SirvApiError) {
    console.error('API Error:', error.message);
    console.error('Status:', error.statusCode);
    console.error('Code:', error.errorCode);
  } else {
    throw error;
  }
}
```

## TypeScript Types

All types are exported for use in TypeScript projects:

```typescript
import {
  SirvClient,
  // Configuration
  SirvClientConfig,
  TokenResponse,
  // Account
  AccountInfo,
  StorageInfo,
  AccountLimits,
  AccountUser,
  BillingPlan,
  AccountEvent,
  // Files
  FileInfo,
  FileMeta,
  ProductMeta,
  FolderContents,
  SearchParams,
  SearchResult,
  // Spins
  SpinConvertParams,
  PointOfInterest,
  // Stats
  HttpStats,
  SpinViewStats,
  StorageStats,
  // Errors
  SirvApiError
} from '@sirv/rest-api-js';
```

## Requirements

- **Browser**: Modern browser with Fetch API support
- **Node.js**: Version 14.0.0 or higher
- **TypeScript**: Version 4.7+ (recommended)

## Getting API Credentials

1. Log in to your [Sirv account](https://my.sirv.com)
2. Go to **Settings > API**
3. Create a new API client
4. Copy your **Client ID** and **Client Secret**

## Security Note

Never expose your Client Secret in client-side code in production. For browser applications, consider using a backend proxy to handle API authentication.

## License

MIT License - see [LICENSE](LICENSE) for details.

## Links

- [Sirv Website](https://sirv.com)
- [API Documentation](https://apidocs.sirv.com)
- [Help Center](https://sirv.com/help)
- [GitHub Repository](https://github.com/sirv/@sirv/rest-api-js)