npm package: @synet/fs-digitalocean
Description: DigitalOcean Spaces filesystem abstraction following FileSystem pattern
Author: Synet
Latest version: 1.0.0

# @synet/fs-digitalocean

**DigitalOcean Spaces FileSystem Adapter** - High-performance object storage with S3-compatible API, built-in CDN, and developer-friendly pricing.

Perfect for applications requiring reliable cloud storage with predictable costs and global edge locations.

---

## Features

- **S3-Compatible API**: Full compatibility with AWS S3 SDKs and tools
- **Built-in CDN**: Global content delivery with 300+ edge locations
- **Developer Pricing**: Simple, predictable pricing starting at $5/month
- **High Performance**: SSD-backed storage with low latency
- **Global Regions**: 8+ regions worldwide for optimal performance
- **TypeScript First**: Complete type safety with full interface definitions

---

## Installation

```bash
npm install @synet/fs-digitalocean
```

---

## Quick Start

```typescript
import { DigitalOceanSpacesFileSystem } from '@synet/fs-digitalocean';

const doFs = new DigitalOceanSpacesFileSystem({
  endpoint: 'https://nyc3.digitaloceanspaces.com',
  accessKeyId: process.env.DO_ACCESS_KEY!,
  secretAccessKey: process.env.DO_SECRET_KEY!,
  bucket: 'my-app-storage',
  region: 'nyc3',
  prefix: 'app-data/' // Optional: acts as root directory
});

// File operations
await doFs.writeFile('/config.json', JSON.stringify({ app: 'myapp' }));
const config = await doFs.readFile('/config.json');

// Directory operations
await doFs.ensureDir('/uploads');
const files = await doFs.readDir('/uploads');
```

---

## Configuration

### Environment Variables

```bash
# .env
DO_ACCESS_KEY=your_access_key
DO_SECRET_KEY=your_secret_key
DO_REGION=nyc3
DO_BUCKET=my-storage
DO_ENDPOINT=https://nyc3.digitaloceanspaces.com
```

### DigitalOcean Setup

1. **Create a Space** in DigitalOcean Control Panel
2. **Generate API Keys**:
   - Go to API → Spaces Keys
   - Generate new key pair
   - Note the access key and secret key

3. **Configure CDN** (Optional):
   - Enable CDN for global distribution
   - Set custom domain if needed

### Regional Endpoints

| Region | Endpoint | Location |
|--------|----------|----------|
| `nyc3` | `https://nyc3.digitaloceanspaces.com` | New York |
| `ams3` | `https://ams3.digitaloceanspaces.com` | Amsterdam |
| `sgp1` | `https://sgp1.digitaloceanspaces.com` | Singapore |
| `sfo3` | `https://sfo3.digitaloceanspaces.com` | San Francisco |
| `fra1` | `https://fra1.digitaloceanspaces.com` | Frankfurt |

---

## Advanced Usage

### Multi-Environment Setup

```typescript
const createSpacesFS = (environment: string) => {
  return new DigitalOceanSpacesFileSystem({
    endpoint: `https://${process.env.DO_REGION}.digitaloceanspaces.com`,
    accessKeyId: process.env.DO_ACCESS_KEY!,
    secretAccessKey: process.env.DO_SECRET_KEY!,
    bucket: process.env.DO_BUCKET!,
    region: process.env.DO_REGION!,
    prefix: `${environment}/`
  });
};

const prodFs = createSpacesFS('production');
const stagingFs = createSpacesFS('staging');
```

### CDN Integration

```typescript
const spacesFs = new DigitalOceanSpacesFileSystem({
  endpoint: 'https://nyc3.digitaloceanspaces.com',
  accessKeyId: process.env.DO_ACCESS_KEY!,
  secretAccessKey: process.env.DO_SECRET_KEY!,
  bucket: 'my-cdn-space',
  region: 'nyc3'
});

// Upload assets
await spacesFs.writeFile('/assets/logo.png', imageBuffer);

// CDN URL (if CDN is enabled)
const cdnUrl = `https://my-cdn-space.nyc3.cdn.digitaloceanspaces.com/assets/logo.png`;
```

### Backup and Sync

```typescript
class SpacesBackup {
  private spaces: DigitalOceanSpacesFileSystem;

  constructor() {
    this.spaces = new DigitalOceanSpacesFileSystem({
      endpoint: 'https://nyc3.digitaloceanspaces.com',
      accessKeyId: process.env.DO_ACCESS_KEY!,
      secretAccessKey: process.env.DO_SECRET_KEY!,
      bucket: 'backups',
      region: 'nyc3',
      prefix: 'daily/'
    });
  }

  async backupData(data: any): Promise<void> {
    const timestamp = new Date().toISOString().split('T')[0];
    await this.spaces.writeFile(`/backup-${timestamp}.json`, JSON.stringify(data));
  }
}
```

---

## API Reference

### Constructor Options

```typescript
interface DigitalOceanSpacesOptions {
  endpoint: string;          // DigitalOcean Spaces endpoint
  accessKeyId: string;       // DO Spaces access key
  secretAccessKey: string;   // DO Spaces secret key
  bucket: string;            // Space name
  region: string;            // DO region
  prefix?: string;           // Optional prefix for all operations
}
```

### File Operations

- `writeFile(path: string, data: string): Promise<void>` - Upload file
- `readFile(path: string): Promise<string>` - Download file content
- `deleteFile(path: string): Promise<void>` - Delete file
- `exists(path: string): Promise<boolean>` - Check if file exists

### Directory Operations

- `ensureDir(path: string): Promise<void>` - Ensure directory exists (no-op)
- `deleteDir(path: string): Promise<void>` - Delete all files with prefix
- `readDir(path: string): Promise<string[]>` - List files with prefix

### Metadata Operations

- `stat(path: string): Promise<FileStats>` - Get file metadata

---

## Testing

```bash
# Run tests
npm test

# Run with coverage
npm run coverage

# Type checking
npm run type-check
```

### Test Configuration

```typescript
import { DigitalOceanSpacesFileSystem } from '@synet/fs-digitalocean';

const testSpaces = new DigitalOceanSpacesFileSystem({
  endpoint: 'https://nyc3.digitaloceanspaces.com',
  accessKeyId: process.env.DO_TEST_ACCESS_KEY!,
  secretAccessKey: process.env.DO_TEST_SECRET_KEY!,
  bucket: 'test-space',
  region: 'nyc3',
  prefix: 'test-data/'
});

describe('DigitalOcean Spaces Integration', () => {
  it('should handle file operations', async () => {
    await testSpaces.writeFile('/test.json', '{"test": true}');
    const content = await testSpaces.readFile('/test.json');
    expect(JSON.parse(content)).toEqual({ test: true });
  });
});
```

---

## Performance

### Concurrent Operations

```typescript
// Parallel uploads
const uploadPromises = files.map(file => 
  spacesFs.writeFile(file.path, file.content)
);
await Promise.all(uploadPromises);

// Batch operations
const contents = await Promise.all(
  paths.map(path => spacesFs.readFile(path))
);
```

### Caching

```typescript
class CachedSpacesFS {
  private spaces: DigitalOceanSpacesFileSystem;
  private cache = new Map<string, { content: string; timestamp: number }>();
  private ttl = 5 * 60 * 1000; // 5 minutes

  async readFile(path: string): Promise<string> {
    const cached = this.cache.get(path);
    if (cached && Date.now() - cached.timestamp < this.ttl) {
      return cached.content;
    }

    const content = await this.spaces.readFile(path);
    this.cache.set(path, { content, timestamp: Date.now() });
    return content;
  }
}
```

---

## Error Handling

```typescript
try {
  await spacesFs.writeFile('/config.json', JSON.stringify(config));
} catch (error: any) {
  if (error.name === 'NoSuchBucket') {
    console.error('Space does not exist');
  } else if (error.name === 'AccessDenied') {
    console.error('Invalid credentials or permissions');
  } else if (error.statusCode === 403) {
    console.error('Access denied - check API keys');
  } else {
    console.error('Upload failed:', error.message);
  }
}
```

### Retry Logic

```typescript
async function writeWithRetry(path: string, content: string, maxRetries = 3): Promise<void> {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      await spacesFs.writeFile(path, content);
      return;
    } catch (error: any) {
      if (attempt === maxRetries || error.statusCode === 403) {
        throw error;
      }
      await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
    }
  }
}
```

---

## Cost Optimization

### Storage Classes

- **Standard**: For frequently accessed data
- **Archive**: For long-term backup (coming soon)

### Transfer Optimization

```typescript
// Minimize API calls with batch operations
const batchSize = 10;
for (let i = 0; i < files.length; i += batchSize) {
  const batch = files.slice(i, i + batchSize);
  await Promise.all(batch.map(file => spacesFs.writeFile(file.path, file.content)));
}
```

---

## Development

```bash
# Install dependencies
npm install

# Build package
npm run build

# Run linting
npm run lint

# Format code
npm run format
```

---

## License

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

---

## Related Packages

- **[@synet/fs](https://www.npmjs.com/package/@synet/fs)** - Core filesystem abstraction
- **[@synet/fs-azure](https://www.npmjs.com/package/@synet/fs-azure)** - Azure Blob Storage adapter
- **[@synet/fs-gcs](https://www.npmjs.com/package/@synet/fs-gcs)** - Google Cloud Storage adapter
- **[@synet/fs-s3](https://www.npmjs.com/package/@synet/fs-s3)** - AWS S3 storage adapter
- **[@synet/fs-linode](https://www.npmjs.com/package/@synet/fs-linode)** - Linode Object Storage adapter

---

**Built with ❤️ by the SYNET Team**