@stoar/sdk
Version:
JavaScript/TypeScript SDK for STOAR - Decentralized file storage on Arweave
511 lines (390 loc) โข 12.3 kB
Markdown
# STOAR SDK
[](https://www.npmjs.com/package/@stoar/sdk)
[](https://opensource.org/licenses/MIT)
[](https://www.typescriptlang.org/)
[](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)