@flipflag/persist
v1.1.0
Published
Persistence plugin for FlipFlag SDK - offline resilience with pluggable storage adapters
Downloads
117
Maintainers
Readme
@flipflag/persist
Persistence plugin for FlipFlag SDK - offline resilience with pluggable storage adapters.
Features
- Offline resilience - Flag values persist and restore when SDK is unavailable
- Multiple storage backends - localStorage, sessionStorage, cookies, or in-memory
- TTL support - Automatic expiration of cached values
- Zero config - Sensible defaults, just wrap your FlipFlag instance
- TypeScript - Full type definitions included
- Tree-shakeable - Import only what you need
Installation
npm install @flipflag/persistPeer dependency: Requires @flipflag/sdk >= 1.2.0
Quick Start
import { FlipFlag } from "@flipflag/sdk";
import { withLocalStorage } from "@flipflag/persist";
const flipFlag = new FlipFlag({ apiKey: "your-api-key" });
const persistedFlipFlag = withLocalStorage(flipFlag);
// Flag values are now automatically persisted
const isEnabled = persistedFlipFlag.isEnabled("my-feature");Storage Adapters
localStorage (recommended for web)
import { withLocalStorage } from "@flipflag/persist";
const persistedFlipFlag = withLocalStorage(flipFlag, {
prefix: "ff:", // Storage key prefix (default: "flipflag:")
ttlMs: 3600000, // TTL in ms (default: 24 hours)
});sessionStorage
import { withSessionStorage } from "@flipflag/persist";
const persistedFlipFlag = withSessionStorage(flipFlag, {
prefix: "ff:",
ttlMs: 3600000,
});Cookies
import { withCookies } from "@flipflag/persist";
const persistedFlipFlag = withCookies(flipFlag, {
prefix: "ff:",
ttlMs: 3600000,
// Cookie-specific options:
path: "/",
domain: "example.com",
secure: true,
sameSite: "lax", // "strict" | "lax" | "none"
});Custom Adapter
import { withPersistence, StorageAdapter } from "@flipflag/persist";
const customAdapter: StorageAdapter = {
get(key) {
// Return PersistedFlagEntry or undefined
},
set(key, entry) {
// Store the entry
},
remove(key) {
// Remove the entry
},
isAvailable() {
return true;
},
};
const persistedFlipFlag = withPersistence(flipFlag, {
adapter: customAdapter,
prefix: "ff:",
ttlMs: 3600000,
});API Reference
withPersistence(flipFlag, options)
Wrap a FlipFlag instance with persistence capabilities.
Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| adapter | StorageAdapter | required | Storage adapter to use |
| prefix | string | "flipflag:" | Key prefix for storage |
| ttlMs | number | 86400000 | Time-to-live in milliseconds (24h) |
| onRestore | (flagName, value) => void | - | Called when a flag is restored from cache |
| onPersist | (flagName, value) => void | - | Called when a flag is persisted |
| onError | (error) => void | - | Called on persistence errors |
withLocalStorage(flipFlag, options?)
Convenience wrapper using localStorage.
withSessionStorage(flipFlag, options?)
Convenience wrapper using sessionStorage.
withCookies(flipFlag, options?)
Convenience wrapper using cookies.
Additional cookie options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| path | string | "/" | Cookie path |
| domain | string | - | Cookie domain |
| secure | boolean | true in production | Secure flag |
| sameSite | "strict" \| "lax" \| "none" | "lax" | SameSite attribute |
Built-in Adapters
import {
localStorageAdapter,
sessionStorageAdapter,
cookieAdapter,
memoryAdapter,
} from "@flipflag/persist";localStorageAdapter()- Browser localStoragesessionStorageAdapter()- Browser sessionStoragecookieAdapter(options?)- Browser cookiesmemoryAdapter()- In-memory storage (useful for SSR/testing)
Types
interface PersistedFlagEntry {
value: boolean;
persistedAt: number;
expiresAt?: number;
}
interface StorageAdapter {
get(key: string): PersistedFlagEntry | undefined | Promise<PersistedFlagEntry | undefined>;
set(key: string, entry: PersistedFlagEntry): void | Promise<void>;
remove(key: string): void | Promise<void>;
isAvailable(): boolean;
}
interface PersistenceOptions {
adapter: StorageAdapter;
prefix?: string;
ttlMs?: number;
onRestore?: (flagName: string, value: boolean) => void;
onPersist?: (flagName: string, value: boolean) => void;
onError?: (error: Error) => void;
}
interface CookieAdapterOptions {
path?: string;
domain?: string;
secure?: boolean;
sameSite?: "strict" | "lax" | "none";
}How It Works
The plugin wraps your FlipFlag instance with a Proxy that intercepts isEnabled() calls:
When you call
isEnabled(flagName):- If SDK succeeds: Value is returned and persisted to storage
- If SDK fails: Value is restored from cache (if not expired)
Each flag is stored individually with its own TTL
Storage keys follow the pattern:
{prefix}{flagName}
License
MIT
