UNPKG

@stoar/sdk

Version:

JavaScript/TypeScript SDK for STOAR - Decentralized file storage on Arweave

811 lines (799 loc) 22.6 kB
import Arweave from 'arweave/web'; interface StoarConfig { arweave?: Arweave; gateway?: string; wallet?: string | ArrayBuffer | object; appName?: string; appVersion?: string; } interface UploadOptions { tags?: Record<string, string>; contentType?: string; encrypt?: boolean; progress?: (progress: number) => void; batch?: boolean | string; } interface UploadResult { id: string; url: string; size: number; contentType: string; tags: Record<string, string>; timestamp: number; status?: 'completed' | 'pending'; batchId?: string; } interface FileMetadata { name: string; size: number; contentType: string; lastModified?: number; } interface BatchUploadOptions { tags?: Record<string, string>; bundleTags?: Record<string, string>; progress?: (progress: { completed: number; total: number; current?: string; }) => void; concurrent?: number; } interface BatchUploadResult { bundleId?: string; bundleUrl?: string; files: Array<{ name: string; id: string; url: string; size: number; index: number; }>; totalSize: number; totalCost: number; } interface S3CompatibleConfig { bucket: string; region?: string; endpoint?: string; accessKeyId?: string; secretAccessKey?: string; } interface S3PutObjectParams { Key: string; Body: Buffer | Uint8Array | string; ContentType?: string; Metadata?: Record<string, string>; ACL?: string; } interface S3GetObjectParams { Key: string; } interface S3ListObjectsParams { Prefix?: string; MaxKeys?: number; Marker?: string; } interface QueryOptions { limit?: number; after?: string; tags?: Record<string, string>; owner?: string; minBlock?: number; maxBlock?: number; } interface QueryResult { id: string; owner: string; tags: Record<string, string>; block: { height: number; timestamp: number; }; fee: string; quantity: string; } interface WalletInterface { address: string; sign(data: Uint8Array): Promise<Uint8Array>; encrypt?(data: Uint8Array, publicKey: string): Promise<Uint8Array>; decrypt?(data: Uint8Array): Promise<Uint8Array>; } declare class StoarError extends Error { code: string; details?: unknown; constructor(message: string, code: string, details?: unknown); } declare class UploadError extends StoarError { constructor(message: string, details?: unknown); } declare class BundleError extends StoarError { constructor(message: string, details?: unknown); } declare class WalletError extends StoarError { constructor(message: string, details?: unknown); } declare class InsufficientBalanceError extends StoarError { required: string; available: string; constructor(required: string, available: string, details?: unknown); } interface BatchOptions { maxFiles?: number; maxBytes?: number; timeout?: number; autoCommit?: boolean; tags?: Record<string, string>; bundleTags?: Record<string, string>; } interface BatchCommitResult { batchId: string; bundleId: string; bundleUrl: string; fileCount: number; totalSize: number; totalCost: number; files: Array<{ name: string; id: string; url: string; size: number; index: number; }>; } interface BatchStatus { batchId: string; fileCount: number; totalSize: number; status: 'open' | 'processing' | 'completed' | 'failed'; createdAt: number; error?: string; } declare class StoarClient$1 { private arweave; private wallet?; private config; private batchQueues; private batchStatuses; private defaultBatchId?; constructor(config?: StoarConfig); /** * Initialize the client with a wallet */ init(walletSource?: string | ArrayBuffer | object): Promise<void>; /** * Upload a single file to Arweave */ uploadFile(data: Buffer | Uint8Array | string, metadata: FileMetadata, options?: UploadOptions): Promise<UploadResult>; /** * Upload multiple files as an AR bundle */ uploadBatch(files: Array<{ data: Buffer | Uint8Array; metadata: FileMetadata; }>, options?: BatchUploadOptions): Promise<BatchUploadResult>; /** * Query transactions using GraphQL */ query(options?: QueryOptions): Promise<QueryResult[]>; /** * Get file data by transaction ID */ getFile(transactionId: string): Promise<Uint8Array>; /** * Get wallet address */ getAddress(): string; /** * Get wallet balance in AR */ getBalance(): Promise<string>; /** * Create a new batch for bundling files */ createBatch(options?: BatchOptions): string; /** * Add a file to a specific batch */ addToBatch(batchId: string, data: Buffer | Uint8Array | string, metadata: FileMetadata, options?: UploadOptions): Promise<string>; /** * Commit a batch and upload as bundle */ commitBatch(batchId: string): Promise<BatchCommitResult>; /** * Get batch status */ getBatchStatus(batchId: string): BatchStatus | null; /** * Enable auto-batching for all uploads */ enableBatching(options?: BatchOptions): void; /** * Disable auto-batching and commit any pending files */ disableBatching(): Promise<BatchCommitResult | void>; /** * Check if wallet has sufficient balance for upload * @throws InsufficientBalanceError if balance is 0 or negative */ private checkBalance; /** * Helper method to extract tags from transaction */ private extractTags; /** * Helper method to build tag query for ArQL */ private buildTagQuery; } /** * Arweave manifest structure according to spec */ interface ArweaveManifest { manifest: 'arweave/paths'; version: '0.2.0'; index: { path: string; id: string; }; paths: Record<string, { id: string; }>; fallback?: { id: string; }; } /** * Transaction record stored locally */ interface TransactionRecord { txId: string; timestamp: number; type: 'file' | 'manifest' | 'metadata' | 'versions' | 'index'; bucketName?: string; key?: string; size: number; contentType?: string; tags?: Record<string, string>; data?: any; } /** * Bucket state tracked locally */ interface BucketState { bucketName: string; currentManifestId: string; manifestVersion: number; previousManifests: string[]; files: Record<string, { txId: string; version: number; size: number; uploadedAt: number; }>; metadata: { owner: string; createdAt: number; visibility: 'public' | 'private'; }; } /** * Local storage configuration */ interface LocalStorageConfig { baseDir?: string; } /** * Manages local storage for development and testing * Tracks all transactions and allows state reconstruction */ declare class LocalStorageManager { private baseDir; private transactionsDir; private bucketsDir; private manifestsDir; constructor(config?: LocalStorageConfig); /** * Ensure all required directories exist */ private ensureDirectories; /** * Record a transaction */ recordTransaction(record: TransactionRecord): void; /** * Get a transaction record */ getTransaction(txId: string): TransactionRecord | null; /** * Record a manifest */ recordManifest(manifestId: string, manifest: ArweaveManifest, bucketName: string): void; /** * Get a manifest */ getManifest(manifestId: string): { manifest: ArweaveManifest; bucketName: string; } | null; /** * Update bucket state based on transaction */ private updateBucketState; /** * Get bucket state */ getBucketState(bucketName: string): BucketState | null; /** * List all buckets */ listBuckets(): string[]; /** * Get manifest history for a bucket */ getManifestHistory(bucketName: string): { version: number; manifestId: string; timestamp: number; }[]; /** * Get file icon based on extension/type */ private getFileIcon; /** * Export bucket state as HTML for visualization */ exportBucketAsHtml(bucketName: string): string; /** * Update bucket with manifest changes */ updateBucketFromManifest(bucketName: string, manifestId: string, updates: Array<{ operation: 'add' | 'remove'; key: string; txId?: string; size?: number; }>, owner?: string): void; /** * Clear all local storage */ clear(): void; } /** * S3-compatible API layer for STOAR * Provides familiar S3-like methods for interacting with Arweave storage */ declare class StoarS3Client { private stoarClient; private config; private localStorage; private manifestManager; private versionManager; private bucketRegistry; private costCalculator; constructor(stoarClient: StoarClient$1, config: S3CompatibleConfig, localStorageConfig?: LocalStorageConfig); /** * Put an object (equivalent to S3 putObject) * Now with version support */ putObject(params: S3PutObjectParams): Promise<{ ETag: string; Location: string; Bucket: string; Key: string; VersionId: string; }>; /** * Get an object (equivalent to S3 getObject) * Now supports version retrieval */ getObject(params: S3GetObjectParams & { VersionId?: string; }): Promise<{ Body: Uint8Array; ContentType?: string; ContentLength: number; ETag: string; LastModified: Date; VersionId: string; Metadata: Record<string, string>; }>; /** * List objects (equivalent to S3 listObjects) */ listObjects(params?: S3ListObjectsParams): Promise<{ IsTruncated: boolean; Contents: Array<{ Key: string; LastModified: Date; ETag: string; Size: number; StorageClass: string; }>; Name: string; Prefix?: string; MaxKeys: number; Marker?: string; }>; /** * Delete an object (equivalent to S3 deleteObject) * Note: In Arweave, data is permanent and cannot be deleted */ deleteObject(params: S3GetObjectParams): Promise<never>; /** * Check if an object exists (equivalent to S3 headObject) */ headObject(params: S3GetObjectParams): Promise<{ ContentType?: string; ContentLength: number; ETag: string; LastModified: Date; Metadata: Record<string, string>; }>; /** * Copy an object (equivalent to S3 copyObject) */ copyObject(params: { CopySource: string; Key: string; Metadata?: Record<string, string>; MetadataDirective?: 'COPY' | 'REPLACE'; }): Promise<{ ETag: string; LastModified: Date; }>; /** * Helper method to get data size */ private getDataSize; /** * Helper method to convert S3 metadata to Arweave tags */ private convertMetadataToTags; /** * Helper method to extract S3 metadata from Arweave tags */ private extractS3Metadata; /** * Create a new bucket with manifest support */ createBucket(bucketName: string): Promise<{ manifestId: string; bucketUrl: string; }>; /** * Update a bucket manifest with new files */ updateBucketManifest(params: { bucketName: string; manifestId: string; updates: Array<{ operation: 'add' | 'remove'; key: string; txId?: string; size?: number; }>; }): Promise<{ manifestId: string; bucketUrl: string; version: number; }>; /** * Get bucket info from local storage */ getBucketInfo(bucketName: string): { state: ReturnType<LocalStorageManager['getBucketState']>; manifestHistory: ReturnType<LocalStorageManager['getManifestHistory']>; }; /** * List all buckets from local storage */ listLocalBuckets(): string[]; /** * Export bucket as HTML */ exportBucketHtml(bucketName: string): string; /** * List all versions of objects in a bucket */ listObjectVersions(params: { Bucket: string; Prefix?: string; KeyMarker?: string; VersionIdMarker?: string; MaxKeys?: number; }): Promise<{ IsTruncated: boolean; KeyMarker?: string; VersionIdMarker?: string; Versions: Array<{ Key: string; VersionId: string; IsLatest: boolean; LastModified: Date; ETag: string; Size: number; Owner: string; }>; }>; /** * Get bucket history by traversing manifest chain */ getBucketHistory(bucket: string, limit?: number): Promise<{ History: Array<{ ManifestId: string; ManifestVersion: number; Timestamp: Date; Operation: string; ChangedKey?: string; Actor: string; PreviousManifest?: string; }>; }>; /** * Estimate costs for operations */ estimateCost(operations: Array<{ type: 'putObject' | 'createBucket' | 'updateManifest'; size?: number; }>): Promise<{ breakdown: Array<{ operation: string; costAR: string; costUSD: string; }>; totalAR: string; totalUSD: string; warning?: string; }>; /** * Format bytes to human readable format */ private formatBytes; /** * List all buckets (S3-compatible) * Discovers buckets from the blockchain */ listBuckets(): Promise<{ Buckets: Array<{ Name: string; CreationDate: Date; }>; Owner: { ID: string; DisplayName: string; }; }>; /** * Remove an object from manifest (soft delete) * The file data still exists on Arweave but won't appear in bucket listings */ removeFromManifest(bucket: string, key: string): Promise<{ ManifestVersion: number; ManifestId: string; RemovedKey: string; Note: string; }>; /** * Get file icon based on extension/type */ private getFileIcon; /** * Generate index.html for a bucket */ private generateIndexHtml; } declare abstract class BaseBrowserWallet<TArweave> implements WalletInterface { protected arweave: TArweave; address: string; protected constructor(arweave: TArweave); protected connect(): Promise<void>; sign(data: Uint8Array): Promise<Uint8Array>; getPublicKey(): Promise<string>; encrypt(data: Uint8Array, publicKey: string): Promise<Uint8Array>; decrypt(data: Uint8Array): Promise<Uint8Array>; } declare abstract class BaseJSONWallet<TArweave> implements WalletInterface { protected arweave: TArweave; readonly jwk: any; address: string; protected constructor(arweave: TArweave, jwk: any); protected abstract jwkToAddress(jwk: any): Promise<string>; protected abstract cryptoSign(jwk: any, data: Uint8Array): Promise<Uint8Array>; protected abstract cryptoEncrypt(data: Uint8Array, publicKey: string): Promise<Uint8Array>; protected abstract cryptoDecrypt(jwk: any, data: Uint8Array): Promise<Uint8Array>; sign(data: Uint8Array): Promise<Uint8Array>; getPublicKey(): Promise<string>; encrypt(data: Uint8Array, publicKey: string): Promise<Uint8Array>; decrypt(data: Uint8Array): Promise<Uint8Array>; } declare global { interface Window { arweaveWallet?: { connect(permissions: string[]): Promise<void>; disconnect(): Promise<void>; getActiveAddress(): Promise<string>; getActivePublicKey(): Promise<string>; getAllAddresses(): Promise<string[]>; signature(data: Uint8Array, algorithm: any): Promise<ArrayBuffer>; encrypt(data: Uint8Array, publicKey: string): Promise<ArrayBuffer>; decrypt(data: Uint8Array): Promise<ArrayBuffer>; sign(transaction: any): Promise<any>; }; } } declare class BrowserWallet extends BaseBrowserWallet<Arweave> implements WalletInterface { static create(arweave: Arweave): Promise<BrowserWallet>; } declare class JSONWallet extends BaseJSONWallet<Arweave> implements WalletInterface { static create(arweave: Arweave, jwkOrString: any | string): Promise<JSONWallet>; static fromJWK(arweave: Arweave, jwk: any): Promise<JSONWallet>; static fromJSONString(arweave: Arweave, jsonString: string): Promise<JSONWallet>; protected jwkToAddress(jwk: any): Promise<string>; protected cryptoSign(jwk: any, data: Uint8Array): Promise<Uint8Array>; protected cryptoEncrypt(data: Uint8Array, publicKey: string): Promise<Uint8Array>; protected cryptoDecrypt(jwk: any, data: Uint8Array): Promise<Uint8Array>; } /** * Get MIME type for a file based on its name or extension * @param fileName - The file name or path * @returns The MIME type or 'application/octet-stream' as default */ declare function getMimeType(fileName: string): string; /** * Get file extension for a MIME type * @param mimeType - The MIME type * @returns The file extension (without dot) or false if not found */ declare function getExtension(mimeType: string): string | false; /** * Get content type with charset for text-based MIME types * @param fileName - The file name or path * @returns The content type with charset if applicable */ declare function getContentType(fileName: string): string; /** * Check if a file is likely to be text-based * @param fileName - The file name or path * @returns True if the file is likely text-based */ declare function isTextFile(fileName: string): boolean; /** * Check if a file is an image * @param fileName - The file name or path * @returns True if the file is an image */ declare function isImageFile(fileName: string): boolean; /** * Check if a file is a video * @param fileName - The file name or path * @returns True if the file is a video */ declare function isVideoFile(fileName: string): boolean; /** * Check if a file is audio * @param fileName - The file name or path * @returns True if the file is audio */ declare function isAudioFile(fileName: string): boolean; /** * Get a human-readable file type category * @param fileName - The file name or path * @returns A category string like 'image', 'video', 'audio', 'text', 'document', or 'other' */ declare function getFileCategory(fileName: string): string; /** * GraphQL utilities for querying Arweave */ interface GraphQLEdge { cursor: string; node: { id: string; owner: { address: string; }; fee: { winston: string; }; quantity: { winston: string; }; tags: Array<{ name: string; value: string; }>; block?: { height: number; timestamp: number; }; }; } interface GraphQLResponse { data: { transactions: { edges: GraphQLEdge[]; pageInfo: { hasNextPage: boolean; }; }; }; } interface GraphQLQueryOptions { owners?: string[]; tags?: Array<{ name: string; values: string[]; }>; first?: number; after?: string; block?: { min?: number; max?: number; }; } /** * Build a GraphQL query for transactions */ declare function buildTransactionQuery(options?: GraphQLQueryOptions): string; /** * Execute a GraphQL query */ declare function executeGraphQLQuery(query: string, gateway?: string): Promise<GraphQLResponse>; /** * Convert tags from array format to object format */ declare function tagsArrayToObject(tags: Array<{ name: string; value: string; }>): Record<string, string>; declare class StoarClient { private arweave; private wallet?; private config; constructor(config?: StoarConfig); /** * Initialize the client with a wallet */ init(walletSource?: string | ArrayBuffer | object): Promise<void>; /** * Upload a single file to Arweave */ uploadFile(data: Buffer | Uint8Array | string, metadata: FileMetadata, options?: UploadOptions): Promise<UploadResult>; /** * Upload multiple files as an AR bundle */ uploadBatch(files: Array<{ data: Buffer | Uint8Array; metadata: FileMetadata; }>, options?: BatchUploadOptions): Promise<BatchUploadResult>; /** * Query transactions */ query(options?: QueryOptions): Promise<QueryResult[]>; /** * Get file data by transaction ID */ getFile(transactionId: string): Promise<Uint8Array>; /** * Get wallet address */ getAddress(): string; /** * Get wallet balance in AR */ getBalance(): Promise<string>; /** * Check if wallet has sufficient balance for upload * @throws InsufficientBalanceError if balance is 0 or negative */ private checkBalance; /** * Helper method to extract tags from transaction */ private extractTags; /** * Helper method to build tag query for ArQL */ private buildTagQuery; } export { BrowserWallet, BundleError, InsufficientBalanceError, JSONWallet, StoarClient$1 as StoarClient, StoarClient as StoarClientNode, StoarError, StoarS3Client, UploadError, WalletError, buildTransactionQuery, StoarClient$1 as default, executeGraphQLQuery, getContentType, getExtension, getFileCategory, getMimeType, isAudioFile, isImageFile, isTextFile, isVideoFile, tagsArrayToObject }; export type { BatchCommitResult, BatchOptions, BatchStatus, BatchUploadOptions, BatchUploadResult, FileMetadata, GraphQLEdge, GraphQLQueryOptions, GraphQLResponse, QueryOptions, QueryResult, S3CompatibleConfig, S3GetObjectParams, S3ListObjectsParams, S3PutObjectParams, StoarConfig, UploadOptions, UploadResult, WalletInterface };