npm package: @seaspark/utils-sdk
Description: SeaSpark Utils SDK - 通用工具集合，包含文件上传、图片压缩等功能
Author: SeaSpark Team
Latest version: 0.1.0

# @seaspark/utils-sdk

SeaVerse Utils SDK - Collection of utility tools including file upload, image compression, and more

[![npm version](https://img.shields.io/npm/v/@seaspark/utils-sdk.svg)](https://www.npmjs.com/package/@seaspark/utils-sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Features

- 📤 **File Upload** - Two-step upload with presigned URLs, secure and efficient
- 🖼️ **Image Compression** - Automatic compression to 1080p, configurable quality and dimensions
- 📊 **Upload Progress** - Real-time progress callbacks with speed and remaining time calculation
- ✅ **File Validation** - Size and type validation
- ⚡ **TypeScript** - Complete type definitions
- 🌍 **Environment Support** - Multi-environment configuration

## Installation

```bash
npm install @seaspark/utils-sdk
# or
pnpm add @seaspark/utils-sdk
```

## Quick Start

### Basic Usage

```typescript
import { UtilsClient } from '@seaspark/utils-sdk';

// Initialize client
const client = new UtilsClient({
  token: 'your-bearer-token',
});

// Upload file
const file = document.querySelector('input[type="file"]').files[0];
const result = await client.uploadFile(file);

console.log('CDN URL:', result.cdnUrl);
console.log('File size:', result.fileSize);
console.log('Compressed:', result.compressed);
```

### With Progress Callback

```typescript
const result = await client.uploadFile(file, {
  onProgress: (progress) => {
    console.log(`Progress: ${progress.percent}%`);
    console.log(`Stage: ${progress.stage}`);
    console.log(`Speed: ${progress.speed} bytes/sec`);
    console.log(`Remaining time: ${progress.remainingTime} sec`);
  },
});
```

### Disable Image Compression

```typescript
const result = await client.uploadFile(file, {
  compress: false, // Disable compression
});
```

### Custom Compression Configuration

```typescript
const client = new UtilsClient({
  token: 'your-bearer-token',
  compress: {
    enabled: true,
    maxWidth: 1920,    // Custom max width
    maxHeight: 1080,   // Custom max height
    quality: 0.9,      // Custom compression quality
  },
});
```

## API Reference

### UtilsClient

#### Constructor

```typescript
new UtilsClient(options: UtilsClientOptions)
```

**Parameters:**

- `token` (string, required): Bearer Token
- `baseURL` (string, optional): API base URL, default `https://seaspark-resource-hub.dev.seaspark.ai`
- `timeout` (number, optional): Request timeout (milliseconds), default 60000
- `compress` (CompressionConfig, optional): Image compression configuration

**CompressionConfig:**

- `enabled` (boolean): Enable compression, default `true`
- `maxWidth` (number): Maximum width, default `1080`
- `maxHeight` (number): Maximum height, default `1080`
- `quality` (number): Compression quality (0-1), default `0.8`

#### uploadFile()

Upload file with automatic compression, validation, and upload handling.

```typescript
async uploadFile(file: File, options?: UploadOptions): Promise<UploadResult>
```

**Parameters:**

- `file`: File object to upload
- `options` (optional):
  - `onProgress`: Progress callback function
  - `compress`: Whether to compress (overrides global configuration)
  - `metadata`: Custom metadata

**Returns:**

```typescript
{
  fileName: string          // File name
  originalFileName: string  // Original file name
  cdnUrl: string           // CDN access URL
  objectPath: string       // Storage path
  fileSize: number         // File size (bytes)
  originalSize: number     // Original size (bytes)
  compressed: boolean      // Whether compressed
  contentType: string      // MIME type
  duration: number         // Upload duration (milliseconds)
  expiresAt: string        // Expiration time
}
```

**Errors:**

- Throws `UtilsAPIError` exception

**Example:**

```typescript
try {
  const result = await client.uploadFile(file, {
    onProgress: (progress) => {
      updateProgressBar(progress.percent);
    },
  });
  console.log('Upload successful:', result.cdnUrl);
} catch (error) {
  if (error instanceof UtilsAPIError) {
    console.error(`Upload failed [${error.code}]: ${error.message}`);
  }
}
```

#### setToken()

Update Bearer Token.

```typescript
setToken(token: string): void
```

#### setBaseURL()

Update base URL.

```typescript
setBaseURL(baseURL: string): void
```

#### updateCompressionConfig()

Update compression configuration.

```typescript
updateCompressionConfig(config: Partial<CompressionConfig>): void
```

## Utility Functions

### validateFile()

Validate file size and type.

```typescript
import { validateFile } from '@seaspark/utils-sdk';

const result = validateFile(file, {
  maxSize: 10 * 1024 * 1024,  // 10MB
  allowedTypes: ['image/jpeg', 'image/png'],
});

if (!result.valid) {
  console.error('Validation failed:', result.errors);
}
```

### compressImage()

Manually compress images.

```typescript
import { compressImage } from '@seaspark/utils-sdk';

const result = await compressImage(file, {
  maxWidth: 1920,
  maxHeight: 1080,
  quality: 0.8,
});

console.log('Compression ratio:', result.compressionRatio);
console.log('Compressed size:', result.compressedSize);
```

### formatBytes()

Format bytes to readable string.

```typescript
import { formatBytes } from '@seaspark/utils-sdk';

console.log(formatBytes(1024));       // "1 KB"
console.log(formatBytes(1048576));    // "1 MB"
console.log(formatBytes(1073741824)); // "1 GB"
```

## Error Handling

```typescript
import { UtilsAPIError, ErrorCode } from '@seaspark/utils-sdk';

try {
  const result = await client.uploadFile(file);
} catch (error) {
  if (error instanceof UtilsAPIError) {
    switch (error.code) {
      case ErrorCode.FILE_TOO_LARGE:
        alert('File too large, please select a file smaller than 100MB');
        break;
      case ErrorCode.INVALID_FILE_TYPE:
        alert('Unsupported file type');
        break;
      case ErrorCode.NETWORK_ERROR:
        alert('Network error, please check your connection');
        break;
      default:
        alert(`Upload failed: ${error.message}`);
    }
  }
}
```

## Error Codes

| Error Code | Description |
|------------|-------------|
| `FILE_TOO_LARGE` | File size exceeds limit |
| `INVALID_FILE_TYPE` | Unsupported file type |
| `PRESIGNED_URL_FAILED` | Failed to get presigned URL |
| `UPLOAD_FAILED` | Upload failed |
| `COMPRESSION_FAILED` | Image compression failed |
| `NETWORK_ERROR` | Network error |
| `TIMEOUT` | Request timeout |
| `VALIDATION_FAILED` | File validation failed |

## Environment Configuration

Different environments use different baseURLs:

```typescript
// Development environment (default)
const client = new UtilsClient({
  token: devToken,
  // baseURL defaults to 'https://seaspark-resource-hub.dev.seaspark.ai'
});

// Production environment
const client = new UtilsClient({
  baseURL: 'https://resource.sg.seaverse.dev',
  token: prodToken,
});

// Local development
const client = new UtilsClient({
  baseURL: 'http://localhost:8003',
  token: localToken,
});
```

## Integration with Auth SDK

```typescript
import { SeaVerseBackendAPIClient } from '@seaspark/auth-sdk';
import { UtilsClient } from '@seaspark/utils-sdk';

// 1. Login with Auth SDK
const authClient = new SeaVerseBackendAPIClient({
  appId: 'your-app-id',
});

const loginResult = await authClient.login({
  email: 'user@example.com',
  password: 'password',
});

// 2. Initialize Utils SDK with same token
const utilsClient = new UtilsClient({
  token: loginResult.token,
});

// 3. Upload file
const result = await utilsClient.uploadFile(file);
```

## Upload Flow Description

SDK uses presigned URL two-step upload mode:

```
1. Get presigned URL
   POST /api/v1/resources/presign-upload
   → Returns upload_url and cdn_url

2. Upload to GCS
   PUT {upload_url}
   → Direct upload to Google Cloud Storage

3. Return CDN URL
   Use cdn_url to access file
```

**Advantages:**

- ✅ Secure: Token not exposed to third-party storage
- ✅ Efficient: Direct upload to GCS, no backend proxy
- ✅ Reliable: GCS provides high availability guarantee

## Image Compression Details

### Compression Strategy

- Maximum dimensions: 1080×1080 pixels (default configuration)
- Maintains aspect ratio with automatic scaling
- Configurable compression quality (0-1)
- Supports custom max width/height (maxWidth, maxHeight)

### Compression Trigger Conditions

Compression only occurs when all of the following conditions are met:

1. File is an image type (MIME type starts with `image/`)
2. Compression configuration is enabled (`compress.enabled === true`)
3. Not explicitly disabled in upload options (`options.compress !== false`)

### Skip Compression

```typescript
// Method 1: Globally disable
const client = new UtilsClient({
  token: 'xxx',
  compress: { enabled: false },
});

// Method 2: Disable for single upload
const result = await client.uploadFile(file, {
  compress: false,
});
```

## FAQ

### Q1: How to get a token?

**A**: Login via `@seaspark/auth-sdk` to get Bearer Token

### Q2: What file types are supported?

**A**:
- **Images**: JPEG, PNG, GIF, WebP
- **Documents**: PDF, TXT, MD
- **Others**: Depends on backend configuration

### Q3: Upload file size limit?

**A**:
- Default limit: 100MB
- Can customize via validation rules
- Images are automatically compressed to reduce transfer size

### Q4: How to handle upload failures?

**A**:
```typescript
try {
  await client.uploadFile(file);
} catch (error) {
  if (error instanceof UtilsAPIError) {
    // Handle based on error code
    if (error.code === ErrorCode.NETWORK_ERROR) {
      // Retry logic
    }
  }
}
```

### Q5: Does compression affect image quality?

**A**:
- Default quality is 0.8, balancing file size and quality
- Can adjust via `quality` parameter (0-1)
- Recommended value: 0.7-0.9

## Development

```bash
# Install dependencies
pnpm install

# Build
pnpm build

# Development mode
pnpm dev

# Type checking
pnpm typecheck
```

## License

MIT © [SeaSpark Team](mailto:support@seaverse.com)

## Changelog

### v0.1.0 (2026-01-07)

- 🎉 Initial release
- ✨ File upload support (presigned URL mode)
- ✨ Automatic image compression (1080p)
- ✨ Upload progress callbacks
- ✨ File validation support
- ✨ Complete TypeScript type definitions