npm package: @kluglabs/ygg-js
Description: YGG client-side file upload SDK
Latest version: 0.1.2

# YGG JavaScript Upload Client

A simple client-side JavaScript SDK for uploading files to YGG storage. Requires a public key and JWT token (validated against Supabase JWKS).

## Installation

```bash
npm install @kluglabs/ygg-js
# or
yarn add @kluglabs/ygg-js
```

## Quick Start

```typescript
import { upload } from '@kluglabs/ygg-js';

const file = document.querySelector('input[type="file"]').files[0];
const { url, fileId } = await upload(file, {
  key: 'ygg_pub_...',
  authorization: `Bearer ${supabaseJwt}`, // Required: JWT from Supabase
  onProgress: (pct) => console.log(`Progress: ${pct}%`),
});

console.log(`File uploaded: ${url}`);
```

## API Reference

### `upload(file, options)`

Uploads a file to YGG storage.

**Parameters:**
- `file` (File | Blob) - The file to upload
- `options` (UploadOptions) - Upload configuration

**Returns:** `Promise<UploadResult>`

### UploadOptions

```typescript
{
  key: string;                    // Required: Public key (ygg_pub_...)
  authorization: string;           // Required: "Bearer <jwt>" - JWT validated against Supabase JWKS
  endpoint?: string;               // Optional: API endpoint (default: http://localhost:9000)
  filename?: string;               // Optional: Override filename
  contentType?: string;            // Optional: Override content type
  path?: string;                   // Optional: Logical path
  metadata?: Record<string, string>; // Optional: File metadata
  onProgress?: (pct: number) => void; // Optional: Progress callback (0-100)
  signal?: AbortSignal;            // Optional: Abort signal for cancellation
}
```

### UploadResult

```typescript
{
  fileId: string;      // Unique file identifier
  url: string;        // URL to access the uploaded file
  size: number;       // File size in bytes
  contentType: string; // MIME type of the file
}
```

## Examples

### Basic Upload

```typescript
import { upload } from '@kluglabs/ygg-js';

const file = document.querySelector('input[type="file"]').files[0];
const { url, fileId } = await upload(file, {
  key: 'ygg_pub_...',
  authorization: `Bearer ${supabaseJwt}`, // Required: JWT from Supabase
});

console.log(`File uploaded: ${url}`);
```

### Upload with Metadata

```typescript
import { upload } from '@kluglabs/ygg-js';

const result = await upload(file, {
  key: 'ygg_pub_...',
  authorization: `Bearer ${supabaseJwt}`, // Required
  filename: 'custom-name.jpg',
  path: 'uploads/images',
  metadata: {
    userId: 'user_123',
    category: 'profile'
  }
});
```

### Upload with Progress Tracking

```typescript
import { upload } from '@kluglabs/ygg-js';

const [progress, setProgress] = useState(0);

await upload(file, {
  key: 'ygg_pub_...',
  authorization: `Bearer ${supabaseJwt}`, // Required
  onProgress: (pct) => {
    setProgress(pct);
    console.log(`Upload progress: ${pct}%`);
  }
});
```

### Upload with Abort Signal

```typescript
import { upload } from '@kluglabs/ygg-js';

const controller = new AbortController();

// Cancel upload
const cancelUpload = () => {
  controller.abort();
};

await upload(file, {
  key: 'ygg_pub_...',
  authorization: `Bearer ${supabaseJwt}`, // Required
  signal: controller.signal
});
```

## Security Considerations

**Important:** All uploads require JWT validation:

1. **JWT is mandatory** - The JWT token is validated against Supabase JWKS for the project/environment associated with the public key
2. **No anonymous uploads** - Every upload must include a valid JWT token
3. **Public keys identify project/env** - The public key determines which Supabase instance's JWKS to validate against
4. **Use scoped keys** - Create public keys with limited permissions when possible
5. **Monitor usage** - Track uploads to detect abuse

## Error Handling

The upload function throws errors that you can catch:

```typescript
try {
  const result = await upload(file, { key: 'ygg_pub_...' });
} catch (error) {
  if (error.message.includes('upload_failed')) {
    console.error('Upload failed');
  } else if (error.message === 'aborted') {
    console.log('Upload was cancelled');
  } else {
    console.error('Upload error:', error.message);
  }
}
```