Evermark SDK
A TypeScript SDK for robust image handling with intelligent fallbacks and storage orchestration. Built specifically for blockchain-based content systems that need reliable image display across multiple storage providers.
๐ฏ Problem Statement
Displaying images in modern web applications is complex when you have:
- Multiple storage sources (Supabase, IPFS, CDNs)
- Network reliability issues
- CORS authentication requirements
- Performance optimization needs
- Automatic fallback strategies for failed loads
This SDK provides battle-tested solutions for these challenges in a single, unified package.
๐ Quick Start
Installation
npm install evermark-sdk
yarn add evermark-sdk
pnpm add evermark-sdk
Basic Usage
import { resolveImageSources } from 'evermark-sdk';
const sources = resolveImageSources({
supabaseUrl: 'https://supabase.storage.com/image.jpg',
thumbnailUrl: 'https://supabase.storage.com/thumb.jpg',
ipfsHash: 'QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG'
});
console.log('Sources to try:', sources);
๐ฆ What's Included
Core Functions
resolveImageSources()
- Smart source prioritization and fallback logic
ImageLoader
- Browser image loading with retries and CORS handling
StorageOrchestrator
- Automatic IPFS โ Supabase transfer flow
- React Components -
EvermarkImage
, ImageDisplay
, ImageUpload
- React Hooks -
useImageLoader
, useStorageFlow
, useImageUpload
3-Step Storage Flow
The SDK implements an intelligent 3-step process:
- Check Supabase - Try loading from Supabase Storage first
- Auto-Transfer - If missing, automatically transfer from IPFS to Supabase
- Graceful Fallback - Fall back to thumbnails or alternative sources
import { EnhancedImageLoader } from 'evermark-sdk';
const loader = new EnhancedImageLoader({
autoTransfer: true,
storageConfig: {
supabase: {
url: 'https://your-project.supabase.co',
anonKey: 'your-anon-key',
bucketName: 'images'
},
ipfs: {
gateway: 'https://gateway.pinata.cloud/ipfs'
}
}
});
const result = await loader.loadImageWithStorageFlow({
supabaseUrl: 'https://supabase.co/storage/image.jpg',
ipfsHash: 'QmHash...'
});
โ๏ธ React Integration
Simple Image Display
import { EvermarkImage } from 'evermark-sdk';
function MyComponent({ evermark }) {
return (
<EvermarkImage
evermark={evermark}
variant="hero"
enableAutoTransfer={true}
storageConfig={storageConfig}
/>
);
}
Custom Hook Usage
import { useImageLoader } from 'evermark-sdk';
function CustomImage({ evermark }) {
const { imageUrl, isLoading, hasError, retry } = useImageLoader({
supabaseUrl: evermark.supabaseImageUrl,
ipfsHash: evermark.ipfsHash,
thumbnailUrl: evermark.thumbnailUrl
});
if (isLoading) return <div>Loading...</div>;
if (hasError) return <button onClick={retry}>Retry</button>;
return <img src={imageUrl} alt={evermark.title} />;
}
Storage Operations
import { useStorageFlow } from 'evermark-sdk';
function TransferComponent({ ipfsHash }) {
const { status, progress, startFlow } = useStorageFlow({
storageConfig,
autoStart: false
});
return (
<div>
<button onClick={() => startFlow({ ipfsHash })}>
Transfer to Supabase
</button>
{progress && <div>Progress: {progress.percentage}%</div>}
</div>
);
}
๐ง Configuration
Storage Configuration
import { createStorageOrchestrator } from 'evermark-sdk';
const orchestrator = createStorageOrchestrator(
'https://your-project.supabase.co',
'your-anon-key',
'your-bucket-name',
existingSupabaseClient
);
Advanced Image Loading
import { ImageLoader } from 'evermark-sdk';
const loader = new ImageLoader({
maxRetries: 3,
timeout: 10000,
useCORS: true,
debug: true
});
๐๏ธ Architecture
Unified Package Structure
evermark-sdk/
โโโ core/ # Pure TypeScript logic, zero dependencies
โโโ browser/ # Browser-specific image loading & CORS
โโโ storage/ # Supabase + IPFS orchestration
โโโ react/ # React hooks and components
Key Design Principles
- Zero external dependencies in core logic
- Intelligent fallbacks for network issues
- TypeScript strict mode throughout
- Real data sources - no mocks or placeholders
- Existing client support - works with your Supabase setup
๐ API Reference
Core Types
interface ImageSourceInput {
supabaseUrl?: string;
thumbnailUrl?: string;
ipfsHash?: string;
processedUrl?: string;
externalUrls?: string[];
preferThumbnail?: boolean;
}
interface StorageConfig {
supabase: {
url: string;
anonKey: string;
bucketName?: string;
client?: SupabaseClient;
};
ipfs: {
gateway: string;
fallbackGateways?: string[];
timeout?: number;
};
}
Core Functions
resolveImageSources(input, config?)
- Main source resolution
isValidUrl(url)
- URL validation
isValidIpfsHash(hash)
- IPFS hash validation
createIpfsUrl(hash, gateway?)
- IPFS URL generation
createDefaultStorageConfig()
- Quick config setup
๐งช Error Handling
The SDK provides comprehensive error handling with detailed debugging:
import { useImageLoader } from 'evermark-sdk';
const { imageUrl, hasError, error, attempts } = useImageLoader(sources, {
debug: true
});
if (hasError) {
console.log('Failed attempts:', attempts);
}
๐ง Migration from Monorepo
If you're migrating from the old @evermark-sdk/*
packages:
import { resolveImageSources } from '@evermark-sdk/core';
import { ImageLoader } from '@evermark-sdk/browser';
import { EvermarkImage } from '@evermark-sdk/react';
import {
resolveImageSources,
ImageLoader,
EvermarkImage
} from 'evermark-sdk';
๐ค Contributing
We welcome contributions! The codebase is organized for clarity:
- Core logic in
/src/core
- Pure functions, no side effects
- Browser APIs in
/src/browser
- DOM, fetch, CORS handling
- Storage ops in
/src/storage
- Supabase + IPFS integration
- React layer in
/src/react
- Hooks and components
Development
git clone https://github.com/ipfsnut/evermark-sdk.git
cd evermark-sdk
npm install
npm run build
npm test
๐ License
MIT License - see LICENSE for details.
๐ Links
Built to solve real image loading challenges in decentralized applications. ๐