Universal storage abstraction for browser-based SDKs with support for multiple backends, TTL/expiration, namespace isolation, and automatic fallback.
- Multiple Backends: localStorage, sessionStorage, cookies, and in-memory
- TTL/Expiration: Automatic expiration of stored values
- Namespace Isolation: Prevent key collisions between apps
- JSON Serialization: Store any JSON-serializable value
- Automatic Fallback: Falls back to memory storage when other backends are unavailable
- Type-Safe: Full TypeScript support
- Event-Driven: Emits events for all storage operations
pnpm add @prosdevlab/sdk-kit @prosdevlab/sdk-kit-pluginsimport { SDK } from '@prosdevlab/sdk-kit';
import { storagePlugin } from '@prosdevlab/sdk-kit-plugins/storage';
const sdk = new SDK({
storage: {
backend: 'localStorage',
namespace: 'myapp'
}
});
sdk.use(storagePlugin);
// Set a value
sdk.storage.set('user', { id: 123, name: 'Alice' });
// Get a value
const user = sdk.storage.get('user');
console.log(user); // { id: 123, name: 'Alice' }
// Remove a value
sdk.storage.remove('user');
// Clear all values
sdk.storage.clear();const sdk = new SDK({
storage: {
// Backend to use (default: 'localStorage')
backend: 'localStorage' | 'sessionStorage' | 'cookie' | 'memory',
// Namespace for key isolation (default: '')
namespace: 'myapp',
// Default TTL in seconds (default: undefined - no expiration)
ttl: 3600,
// Cookie-specific options
domain: '.example.com',
path: '/',
secure: true,
sameSite: 'lax'
}
});Store a value in storage.
// Simple value
sdk.storage.set('name', 'Alice');
// Object
sdk.storage.set('user', { id: 123, name: 'Alice' });
// With TTL (expires in 1 hour)
sdk.storage.set('session', { token: 'abc123' }, { ttl: 3600 });
// With custom backend
sdk.storage.set('temp', 'value', { backend: 'sessionStorage' });
// With custom namespace
sdk.storage.set('key', 'value', { namespace: 'app1' });Options:
backend?: 'localStorage' | 'sessionStorage' | 'cookie' | 'memory'- Override default backendnamespace?: string- Override default namespacettl?: number- Time to live in secondsdomain?: string- Cookie domain (cookie backend only)path?: string- Cookie path (cookie backend only)secure?: boolean- Secure flag (cookie backend only)sameSite?: 'strict' | 'lax' | 'none'- SameSite attribute (cookie backend only)
Retrieve a value from storage. Returns null if the key doesn't exist or the value has expired.
const user = sdk.storage.get<User>('user');
if (user) {
console.log(user.name);
}
// From custom backend
const temp = sdk.storage.get('temp', { backend: 'sessionStorage' });
// From custom namespace
const value = sdk.storage.get('key', { namespace: 'app1' });Remove a value from storage.
sdk.storage.remove('user');
// From custom backend
sdk.storage.remove('temp', { backend: 'sessionStorage' });
// From custom namespace
sdk.storage.remove('key', { namespace: 'app1' });Clear all values from storage.
sdk.storage.clear();
// Clear specific backend
sdk.storage.clear({ backend: 'sessionStorage' });Check if a storage backend is supported in the current environment.
if (sdk.storage.isSupported('localStorage')) {
console.log('localStorage is available');
}
if (!sdk.storage.isSupported('cookie')) {
console.log('Cookies are disabled');
}Persistent storage that survives page refreshes and browser restarts. Data is stored per origin.
sdk.storage.set('key', 'value', { backend: 'localStorage' });Characteristics:
- Persistent across sessions
- ~5-10MB storage limit
- Synchronous API
- Automatically falls back to memory if unavailable or quota exceeded
Session-scoped storage that persists only for the duration of the page session. Data is cleared when the tab/window is closed.
sdk.storage.set('key', 'value', { backend: 'sessionStorage' });Characteristics:
- Cleared when tab/window closes
- ~5-10MB storage limit
- Synchronous API
- Automatically falls back to memory if unavailable
Stores data in browser cookies. Useful for server-side access or cross-subdomain sharing.
sdk.storage.set('key', 'value', {
backend: 'cookie',
domain: '.example.com',
path: '/',
secure: true,
sameSite: 'lax',
ttl: 3600
});Characteristics:
- Sent with every HTTP request
- ~4KB size limit per cookie
- Can be shared across subdomains
- Supports expiration via TTL
- Automatically falls back to memory if cookies are disabled
Cookie Options:
domain- Cookie domain (e.g.,.example.comfor all subdomains)path- Cookie path (default:/)secure- Only send over HTTPS (default:false)sameSite- CSRF protection ('strict','lax', or'none')ttl- Expiration time in seconds
In-memory storage that exists only for the lifetime of the SDK instance. Data is lost on page refresh.
sdk.storage.set('key', 'value', { backend: 'memory' });Characteristics:
- No persistence
- No size limits (limited by available memory)
- Always available (used as fallback)
- Cleared when SDK is destroyed
Values can be set with a time-to-live (TTL) in seconds. Expired values are automatically removed when accessed.
// Expires in 1 hour
sdk.storage.set('session', { token: 'abc123' }, { ttl: 3600 });
// Set default TTL in config
const sdk = new SDK({
storage: {
ttl: 3600 // All values expire in 1 hour by default
}
});
// Override default TTL
sdk.storage.set('permanent', 'value', { ttl: undefined }); // No expirationHow it works:
- When a value is stored with TTL, an expiration timestamp is calculated
- When the value is retrieved, the timestamp is checked
- If expired, the value is removed and
nullis returned - A
storage:expiredevent is emitted
Namespaces prevent key collisions when multiple apps or SDK instances share the same storage backend.
// Set default namespace in config
const sdk1 = new SDK({ storage: { namespace: 'app1' } });
const sdk2 = new SDK({ storage: { namespace: 'app2' } });
sdk1.use(storagePlugin);
sdk2.use(storagePlugin);
sdk1.storage.set('user', 'Alice');
sdk2.storage.set('user', 'Bob');
console.log(sdk1.storage.get('user')); // 'Alice'
console.log(sdk2.storage.get('user')); // 'Bob'
// Or use per-operation namespace
sdk.storage.set('key', 'value1', { namespace: 'app1' });
sdk.storage.set('key', 'value2', { namespace: 'app2' });How it works:
- Keys are prefixed with the namespace:
namespace:key - Empty namespace (default) means no prefix
The storage plugin integrates with the logging plugin for operational visibility.
Load the logging plugin before the storage plugin:
import { SDK } from '@prosdevlab/sdk-kit';
import { loggingPlugin, storagePlugin } from '@prosdevlab/sdk-kit-plugins';
const sdk = new SDK({
logging: { level: 'debug' }
});
sdk.use(loggingPlugin).use(storagePlugin);
await sdk.init();| Operation | Level | Data Logged |
|---|---|---|
set() |
debug | key, backend, ttl |
get() hit |
trace | key, backend |
get() miss |
trace | key, backend |
remove() |
debug | key, backend |
clear() |
info | backend, namespace |
| Backend init | debug | backend |
| Backend fallback | warn | requested backend, fallback backend |
| Parse error | warn | key, backend, error |
The storage plugin NEVER logs stored values - only metadata like keys, backend types, and configuration.
// Safe: Only logs key and metadata
sdk.storage.set('credentials', { password: 'secret123' });
// Logs: { key: 'credentials', backend: 'localStorage', ttl: null }
// Safe: Only logs key
sdk.storage.get('credentials');
// Logs: { key: 'credentials', backend: 'localStorage' }sdk.logging.onLog((entry) => {
if (entry.namespace === 'storage') {
// Send storage operations to analytics
analytics.track('storage_operation', {
operation: entry.message,
backend: entry.data?.backend
});
}
});The logging plugin is optional. If not loaded, all log calls are safely ignored:
✅ sdk.use(loggingPlugin).use(storagePlugin); // Logging enabled
✅ sdk.use(storagePlugin); // No logging (graceful)
❌ sdk.use(storagePlugin).use(loggingPlugin); // No logs (load order)The storage plugin emits events for all operations:
// Value was stored
sdk.on('storage:set', (data) => {
console.log(`Set ${data.key} in ${data.backend}`);
});
// Value was retrieved
sdk.on('storage:get', (data) => {
console.log(`Got ${data.key} from ${data.backend}`);
});
// Value was removed
sdk.on('storage:remove', (data) => {
console.log(`Removed ${data.key} from ${data.backend}`);
});
// Storage was cleared
sdk.on('storage:clear', (data) => {
console.log(`Cleared ${data.backend}`);
});
// Value expired
sdk.on('storage:expired', (data) => {
console.log(`${data.key} expired in ${data.backend}`);
});The storage plugin handles errors gracefully:
- Quota Exceeded: Falls back to memory storage
- Storage Unavailable: Falls back to memory storage
- Corrupted Data: Removes corrupted value and returns
null - Unsupported Backend: Falls back to memory storage with warning
// Check if backend is supported
if (!sdk.storage.isSupported('localStorage')) {
console.warn('localStorage not available, using memory fallback');
}
// Errors are logged but don't throw
sdk.storage.set('key', 'value'); // Never throwsThe storage plugin is fully typed:
interface User {
id: number;
name: string;
email: string;
}
// Type-safe get/set
sdk.storage.set<User>('user', { id: 123, name: 'Alice', email: 'alice@example.com' });
const user = sdk.storage.get<User>('user');
if (user) {
console.log(user.name); // TypeScript knows user is User | null
}// Store session with 1 hour expiration
sdk.storage.set('session', {
token: 'abc123',
userId: 456,
expiresAt: Date.now() + 3600000
}, { ttl: 3600 });
// Check if session exists
const session = sdk.storage.get('session');
if (session) {
console.log('User is logged in');
} else {
console.log('Session expired');
}// Store preferences in localStorage (persistent)
sdk.storage.set('preferences', {
theme: 'dark',
language: 'en',
notifications: true
}, { backend: 'localStorage' });
// Load preferences on init
const prefs = sdk.storage.get('preferences') || {
theme: 'light',
language: 'en',
notifications: false
};// Store visitor ID in cookie accessible across subdomains
sdk.storage.set('visitorId', generateId(), {
backend: 'cookie',
domain: '.example.com',
ttl: 365 * 24 * 3600, // 1 year
secure: true,
sameSite: 'lax'
});// Isolate data by tenant
const tenantId = 'tenant-123';
sdk.storage.set('data', { foo: 'bar' }, {
namespace: tenantId
});
// Each tenant has isolated storage
const data = sdk.storage.get('data', { namespace: tenantId });- localStorage: All modern browsers, IE 8+
- sessionStorage: All modern browsers, IE 8+
- Cookies: All browsers
- Memory: All browsers (always available)
The plugin automatically detects support and falls back to memory storage when needed.
MIT