npm package: @stoar/sdk
Description: JavaScript/TypeScript SDK for STOAR - Decentralized file storage on Arweave
Author: STOAR Team
Latest version: 0.1.2

# STOAR SDK

[![npm version](https://badge.fury.io/js/%40stoar%2Fsdk.svg)](https://www.npmjs.com/package/@stoar/sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
[![Bundle Size](https://img.shields.io/bundlephobia/minzip/@stoar/sdk)](https://bundlephobia.com/package/@stoar/sdk)

A TypeScript/JavaScript SDK for interacting with STOAR - a decentralized file storage system built on Arweave.

## Features

- 🚀 **Simple API**: Easy-to-use client for file uploads and management
- 🔐 **Wallet Support**: Compatible with ArConnect browser extension and JSON wallet files
- 📦 **Batch Uploads**: Efficient batch file uploads using AR bundles
- 🔄 **S3 Compatibility**: Familiar S3-like API for easy migration
- 💾 **Permanent Storage**: Leverage Arweave's permanent, decentralized storage
- 🧪 **Well Tested**: Comprehensive unit and integration tests
- 📘 **TypeScript**: Full TypeScript support with complete type definitions

## Installation

```bash
npm install @stoar/sdk
# or
yarn add @stoar/sdk
# or
bun add @stoar/sdk
```

## Quick Start

### Basic File Upload

```typescript
import { StoarClient } from '@stoar/sdk';

// Initialize client
const client = new StoarClient({
  appName: 'My App',
  appVersion: '1.0.0'
});

// Initialize with ArConnect wallet (browser)
await client.init();

// Upload a file
const file = new Uint8Array([1, 2, 3, 4]);
const result = await client.uploadFile(file, {
  name: 'my-file.bin',
  size: file.length,
  contentType: 'application/octet-stream'
});

console.log('Uploaded!', result.url);
```

### Using JSON Wallet

```typescript
import { StoarClient } from '@stoar/sdk';
import fs from 'fs';

// Load wallet from file
const walletData = fs.readFileSync('path/to/wallet.json', 'utf8');

const client = new StoarClient();
await client.init(walletData);

// Upload file
const result = await client.uploadFile(
  Buffer.from('Hello, Arweave!'),
  {
    name: 'hello.txt',
    size: 15,
    contentType: 'text/plain'
  }
);
```

### Batch Upload

STOAR SDK now supports advanced batching capabilities that significantly reduce transaction costs:

#### Method 1: Auto-batching
```typescript
// Enable auto-batching - all subsequent uploads are automatically bundled
client.enableBatching({
  timeout: 30000,  // Auto-commit after 30 seconds
  maxFiles: 100,   // Or when 100 files are added
  maxBytes: 100 * 1024 * 1024  // Or when 100MB is reached
});

// These uploads are automatically batched together
await client.uploadFile(data1, metadata1);
await client.uploadFile(data2, metadata2);
await client.uploadFile(data3, metadata3);

// Disable and commit any remaining files
await client.disableBatching();
```

#### Method 2: Manual batch control
```typescript
// Create a batch
const batchId = client.createBatch({
  maxFiles: 50,
  autoCommit: false  // Manual control
});

// Add files to the batch
await client.uploadFile(data1, metadata1, { batch: batchId });
await client.uploadFile(data2, metadata2, { batch: batchId });

// Check batch status
const status = client.getBatchStatus(batchId);
console.log(`Files in batch: ${status.fileCount}`);

// Commit when ready
const result = await client.commitBatch(batchId);
console.log(`Bundle created: ${result.bundleId}`);
console.log(`Cost savings: ${result.fileCount - 1} transaction fees saved!`);
```

#### Method 3: Direct batch upload
```typescript
const files = [
  {
    data: Buffer.from('File 1 content'),
    metadata: { name: 'file1.txt', size: 14, contentType: 'text/plain' }
  },
  {
    data: Buffer.from('File 2 content'),
    metadata: { name: 'file2.txt', size: 14, contentType: 'text/plain' }
  }
];

const batchResult = await client.uploadBatch(files, {
  bundleTags: { 'Batch-Name': 'My Batch Upload' },
  progress: (status) => {
    console.log(`Progress: ${status.completed}/${status.total}`);
  }
});

console.log('Batch uploaded!', batchResult.bundleUrl);
```

**Benefits of Batching:**
- 💰 **90%+ cost reduction** for multi-file uploads
- 🚀 **Faster uploads** with single network transaction
- ⚡ **Atomic operations** - all files succeed or fail together
- 📊 **Perfect for NFT collections** and bulk data

## S3 Compatibility Layer

For easy migration from S3-based applications:

```typescript
import { StoarClient, StoarS3Client } from '@stoar/sdk';

const client = new StoarClient();
await client.init();

const s3 = new StoarS3Client(client, {
  bucket: 'my-bucket',
  region: 'us-east-1'
});

// Use familiar S3 API
await s3.putObject({
  Key: 'path/to/file.txt',
  Body: 'Hello, S3 compatibility!',
  ContentType: 'text/plain',
  Metadata: {
    author: 'John Doe',
    version: '1.0'
  }
});

// Retrieve object
const object = await s3.getObject({ Key: 'path/to/file.txt' });
console.log(new TextDecoder().decode(object.Body));

// List objects
const list = await s3.listObjects({ Prefix: 'path/' });
console.log(list.Contents);
```

## API Reference

### StoarClient

#### Constructor

```typescript
new StoarClient(config?: StoarConfig)
```

**StoarConfig Options:**
- `arweave?: Arweave` - Custom Arweave instance
- `gateway?: string` - Arweave gateway URL (default: 'https://arweave.net')
- `wallet?: string | ArrayBuffer | object` - Wallet data
- `appName?: string` - Application name for tagging
- `appVersion?: string` - Application version for tagging

#### Methods

##### `init(walletSource?)`

Initialize the client with a wallet.

```typescript
// Browser wallet (ArConnect)
await client.init();

// JSON wallet string
await client.init(jsonWalletString);

// JWK object
await client.init(jwkObject);

// ArrayBuffer
await client.init(walletArrayBuffer);
```

##### `uploadFile(data, metadata, options?)`

Upload a single file (supports batching).

```typescript
await client.uploadFile(
  data: Buffer | Uint8Array | string,
  metadata: {
    name: string;
    size: number;
    contentType: string;
    lastModified?: number;
  },
  options?: {
    tags?: Record<string, string>;
    contentType?: string;
    encrypt?: boolean;
    progress?: (progress: number) => void;
    batch?: boolean | string;  // Enable batching or specify batch ID
  }
);
```

##### `uploadBatch(files, options?)`

Upload multiple files as a bundle.

```typescript
await client.uploadBatch(
  files: Array<{
    data: Buffer | Uint8Array;
    metadata: FileMetadata;
  }>,
  options?: {
    tags?: Record<string, string>;
    bundleTags?: Record<string, string>;
    progress?: (progress: {
      completed: number;
      total: number;
      current?: string;
    }) => void;
    concurrent?: number;
  }
);
```

##### `query(options?)`

Query transactions using Arweave's GraphQL endpoint. This method has been updated to use GraphQL instead of the deprecated ArQL, providing better performance and more reliable results.

```typescript
// Basic query
const results = await client.query({
  limit: 20,
  owner: 'wallet-address'
});

// Query with tags
const taggedResults = await client.query({
  tags: {
    'App-Name': 'STOAR SDK',
    'Content-Type': 'image/png'
  },
  limit: 50
});

// Pagination with cursor
const page2 = await client.query({
  limit: 20,
  after: 'cursor-from-previous-query'
});

// Filter by block height
const recentTxs = await client.query({
  minBlock: 1000000,
  maxBlock: 1100000
});
```

**Query Options:**
- `limit`: Number of results to return (default: 10)
- `after`: Cursor for pagination
- `tags`: Filter by transaction tags
- `owner`: Filter by wallet address
- `minBlock`: Minimum block height
- `maxBlock`: Maximum block height

**Returns:** Array of `QueryResult` objects containing:
- `id`: Transaction ID
- `owner`: Owner wallet address
- `tags`: Transaction tags as key-value pairs
- `block`: Block information (height and timestamp)
- `fee`: Transaction fee in winston
- `quantity`: Transfer amount in winston

##### `getFile(transactionId)`

Retrieve file data by transaction ID.

```typescript
const data: Uint8Array = await client.getFile('transaction-id');
```

##### `getAddress()`

Get wallet address.

```typescript
const address: string = client.getAddress();
```

##### `getBalance()`

Get wallet balance in AR.

```typescript
const balance: string = await client.getBalance();
```

##### `createBatch(options?)`

Create a new batch for bundling files.

```typescript
const batchId: string = client.createBatch({
  maxFiles?: number;        // Maximum files in batch
  maxBytes?: number;        // Maximum total size
  timeout?: number;         // Auto-commit timeout in ms
  autoCommit?: boolean;     // Enable auto-commit
});
```

##### `commitBatch(batchId)`

Commit a batch and upload as bundle.

```typescript
const result: BatchCommitResult = await client.commitBatch(batchId);
// Returns: { batchId, bundleId, bundleUrl, fileCount, totalSize, totalCost, files }
```

##### `getBatchStatus(batchId)`

Get the current status of a batch.

```typescript
const status: BatchStatus = client.getBatchStatus(batchId);
// Returns: { batchId, fileCount, totalSize, status, createdAt, error? }
```

##### `enableBatching(options?)`

Enable auto-batching for all subsequent uploads.

```typescript
client.enableBatching({
  timeout?: number;     // Auto-commit timeout
  maxFiles?: number;    // Max files before auto-commit
  maxBytes?: number;    // Max size before auto-commit
});
```

##### `disableBatching()`

Disable auto-batching and commit any pending files.

```typescript
const result = await client.disableBatching();
// Returns BatchCommitResult if files were pending, void otherwise
```

### StoarS3Client

S3-compatible interface for STOAR.

#### Constructor

```typescript
new StoarS3Client(stoarClient: StoarClient, config: S3CompatibleConfig)
```

#### Methods

- `putObject(params)` - Upload an object
- `getObject(params)` - Retrieve an object
- `deleteObject(params)` - Delete an object (creates delete marker)
- `headObject(params)` - Get object metadata
- `listObjects(params)` - List objects in bucket
- `copyObject(params)` - Copy an object

## Error Handling

The SDK provides specific error types:

```typescript
import { 
  StoarError, 
  UploadError, 
  BundleError, 
  WalletError,
  InsufficientBalanceError 
} from '@stoar/sdk';

try {
  await client.uploadFile(data, metadata);
} catch (error) {
  if (error instanceof InsufficientBalanceError) {
    console.error('Insufficient balance:', error.message);
    console.error('Required:', error.required, 'AR');
    console.error('Available:', error.available, 'AR');
  } else if (error instanceof UploadError) {
    console.error('Upload failed:', error.message);
  } else if (error instanceof WalletError) {
    console.error('Wallet error:', error.message);
  } else if (error instanceof BundleError) {
    console.error('Bundle error:', error.message);
  }
}
```

## Environment Configuration

You can configure the SDK using environment variables:

```bash
# Custom Arweave gateway
ARWEAVE_GATEWAY=https://arweave.net

# Application info
APP_NAME=MyApp
APP_VERSION=1.0.0
```

## Development

### Building

```bash
bun run build
```

### Testing

```bash
# Run tests
bun run test

# Run tests with UI
bun run test:ui

# Run tests with coverage
bun run test:coverage
```

### Linting

```bash
bun run lint
bun run lint:fix
```

## Examples

Check out the `examples/` directory for more usage examples:

- [Batch Upload](examples/batch-upload.ts) - Comprehensive batch upload examples
- [NFT Batch Explained](examples/nft-batch-explained.ts) - How to upload NFT collections efficiently
- [S3 Storage Test](examples/s3-storage-test.ts) - S3 compatibility layer examples
- [S3 Upload Test](examples/s3-test-upload.ts) - S3-style uploads
- [S3 Query Test](examples/s3-test-query.ts) - Querying with S3 interface

## Contributing

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Support

- 📖 [Documentation](https://stoar.io/sdk)
- 🐛 [Report Issues](https://github.com/micovi/stoar/issues)