npm package: @pappymall/storage
Description: Official Node.js SDK for PappyMall Storage - Object storage with S3-compatible API
Author: PappyMall
Latest version: 1.0.1

# @pappymall/storage

Official Node.js SDK for PappyMall Storage - Object storage for your applications.

## Installation

```bash
npm install @pappymall/storage
```

## Quick Start

```javascript
const { PappyStorage } = require('@pappymall/storage');

const storage = new PappyStorage({
  accessKey: 'your_access_key',
  secretKey: 'your_secret_key'
});

// Upload a product image
const file = await storage.putObject('product-images', 'products/shoe-123.jpg', imageBuffer, {
  content_type: 'image/jpeg'
});

// Store the URL in your database
const imageUrl = file.downloadUrl;
// Example: "/api/v1/storage/buckets/product-images/objects/products/shoe-123.jpg"
```

## Features

- **Bucket Management** - Create, list, delete buckets
- **Object Operations** - Upload, download, update, delete files
- **Streaming Support** - Upload large files from streams
- **Metadata & Tags** - Organize files with custom tags

## API Reference

### Bucket Operations

```javascript
// List all buckets
const buckets = await storage.listBuckets();
// Returns: [{ name, fileCount, totalSize, ... }]

// Create a bucket
const bucket = await storage.createBucket('product-images');

// Delete bucket (must be empty)
await storage.deleteBucket('my-bucket');
```

### Upload Files

```javascript
// Upload from Buffer
const file = await storage.putObject('bucket-name', 'path/to/file.jpg', buffer, {
  content_type: 'image/jpeg'
});

// Response contains:
// - file.objectKey     - The path/key of the uploaded file
// - file.fileName      - The filename
// - file.fileSize      - Size in bytes
// - file.contentType   - MIME type
// - file.downloadUrl   - URL to access the file (store this in your database)
// - file.eTag          - ETag for caching

// Upload from file path
const file = await storage.putObjectFromFile('bucket', 'images/photo.jpg', '/local/path/photo.jpg');

// Upload from stream (for large files)
const stream = fs.createReadStream('/path/to/large-video.mp4');
const file = await storage.putObjectFromStream('bucket', 'videos/movie.mp4', stream, {
  content_type: 'video/mp4'
});
```

### Download Files

```javascript
// Download file to memory
const obj = await storage.getObject('bucket', 'path/to/file.jpg');
console.log(obj.data);           // Buffer containing file data
console.log(obj.content_type);   // 'image/jpeg'
console.log(obj.content_length); // Size in bytes

// Download to local file
await storage.downloadObject('bucket', 'path/to/file.jpg', '/local/download.jpg');
```

### List Objects

```javascript
// List all objects in bucket
const files = await storage.listObjects('bucket');

// List with prefix filter (like folders)
const productImages = await storage.listObjects('product-images', {
  prefix: 'products/'
});

// Each file contains:
// - objectKey, fileName, fileSize, contentType, uploadedAt
```

### Update Object Metadata

```javascript
// Update tags on an object
const updated = await storage.updateObject('bucket', 'products/shoe.jpg', {
  tags: 'featured,sale,new-arrival'
});
```

### Delete Objects

```javascript
// Delete a single object
await storage.deleteObject('bucket', 'path/to/file.jpg');
```

## Error Handling

```javascript
const { 
  PappyStorage, 
  PappyStorageError, 
  BucketNotFoundError, 
  ObjectNotFoundError,
  QuotaExceededError 
} = require('@pappymall/storage');

try {
  await storage.getObject('bucket', 'missing-file.txt');
} catch (error) {
  if (error instanceof ObjectNotFoundError) {
    console.log('File not found');
  } else if (error instanceof BucketNotFoundError) {
    console.log('Bucket not found');
  } else if (error instanceof QuotaExceededError) {
    console.log('Storage quota exceeded');
  } else if (error instanceof PappyStorageError) {
    console.log(`Error [${error.code}]: ${error.message}`);
  }
}
```

## Configuration

```javascript
const storage = new PappyStorage({
  accessKey: 'your_access_key',      // Required
  secretKey: 'your_secret_key',      // Required
  serviceUrl: 'https://pappymall.com/api/v1/storage',  // Optional, this is the default
  timeout: 60000                     // Optional, default: 30000 (30 seconds)
});
```

## Complete Example: Product Image Management

```javascript
const { PappyStorage } = require('@pappymall/storage');
const fs = require('fs');

const storage = new PappyStorage({
  accessKey: process.env.STORAGE_ACCESS_KEY,
  secretKey: process.env.STORAGE_SECRET_KEY
});

// 1. Create a bucket for product images
await storage.createBucket('product-images');

// 2. Upload a product image
const imageBuffer = fs.readFileSync('./shoe.jpg');
const file = await storage.putObject('product-images', 'products/shoe-123.jpg', imageBuffer, {
  content_type: 'image/jpeg'
});

// 3. Save the URL to your database
const product = {
  id: 123,
  name: 'Running Shoe',
  image_url: file.downloadUrl  // Store this!
};
await db.products.insert(product);

// 4. Later, update the image tags
await storage.updateObject('product-images', 'products/shoe-123.jpg', {
  tags: 'featured,bestseller'
});

// 5. List all product images
const images = await storage.listObjects('product-images', { prefix: 'products/' });
console.log(`Found ${images.length} product images`);

// 6. Delete when product is removed
await storage.deleteObject('product-images', 'products/shoe-123.jpg');
```

## License

MIT