Overview
Ecosistema completo para organización y gestión de contenido con interfaces espaciales
Flash Persistence - Overview
Flash Persistence is an auto-persistent signal system that eliminates the boilerplate of manual state management and storage synchronization.
Table of Contents
What is Flash Persistence?
Flash Persistence combines Angular signals with intelligent storage to create persistent state that:
- ⚡ Reads instantly - 0.3ms from RAM cache
- 💾 Persists automatically - Saves on every change (debounced)
- 🧠 Stores intelligently - Adapters chosen automatically
- 🔄 Syncs optionally - MongoDB, Firebase, or API
- 🎯 Types safely - Full TypeScript support
The Problem It Solves
Traditional Approach:
// ❌ Manual storage management
class PreferencesService {
private preferences = signal({ theme: 'dark' });
async loadPreferences() {
const stored = await storage.get('preferences');
if (stored) this.preferences.set(stored);
}
async savePreferences(prefs: Preferences) {
this.preferences.set(prefs);
await storage.put('preferences', prefs);
}
}
// ❌ Must call manually everywhere
await prefsService.loadPreferences(); // On init
await prefsService.savePreferences(newPrefs); // On change
Flash Persistence:
// ✅ Automatic persistence
const preferences = autoFlashState({
key: 'preferences',
initialValue: { theme: 'dark' }
});
// ✅ Just use it - persistence is automatic
preferences.set({ theme: 'light' }); // Saved automatically!
const theme = preferences().theme; // Instant read from cache
Architecture
High-Level Overview
┌─────────────────────────────────────────────────────────────┐
│ Angular Component │
│ │
│ const userPrefs = autoFlashState({...}); │
│ const theme = userPrefs().theme; ← 0.3ms read │
│ userPrefs.set({...}); ← Triggers persistence │
└──────────────────┬──────────────────────────────────────────┘
│
┌──────────────────▼───────────────────────────────────────────┐
│ FlashStateHydratorService │
│ • Pre-loads values on app init │
│ • Manages persistence queue │
│ • Debounces writes (300ms default) │
│ • Provides instant cache access │
└──────────────────┬───────────────────────────────────────────┘
│
┌──────────────────▼───────────────────────────────────────────┐
│ GgStorage Engine │
│ • RAM Cache (0.3ms reads) │
│ • Multi-adapter selection │
│ • Intelligent redundancy │
└──────────────────┬───────────────────────────────────────────┘
│
┌──────────┼──────────┐
│ │ │
┌───────▼────┐ ┌──▼──────┐ ┌─▼─────────┐
│ OPFS │ │ Dexie │ │localStorage│
│ (file-like)│ │(indexed)│ │ (simple) │
└────────────┘ └─────────┘ └───────────┘
│ │ │
└──────────┼──────────┘
│ (optional)
┌──────────▼──────────┐
│ MongoDB Sync │
│ (background) │
└─────────────────────┘
Component Interaction
// 1. Signal Creation
const state = autoFlashState({
key: 'user-prefs',
initialValue: { theme: 'dark' }
});
// 2. Value is immediately available (pre-loaded or initial)
console.log(state()); // { theme: 'dark' }
// 3. Read triggers cache lookup (0.3ms)
const theme = state().theme;
// 4. Write triggers persistence pipeline
state.set({ theme: 'light' });
↓
FlashStateHydrator (debounce 300ms)
↓
GgStorage (intelligent adapter)
↓
Storage Backend (OPFS/Dexie/localStorage)
↓
(Optional) MongoDB Sync
Core Concepts
1. Auto-Persistent Signals
Flash Persistence signals are regular Angular signals with persistence built-in:
// It's still a signal!
const counter = autoFlashState({
key: 'counter',
initialValue: 0
});
// All signal features work
console.log(counter()); // Read
counter.set(5); // Write
counter.update(n => n + 1); // Update
// But also persisted!
// Refresh page → value is still 6
2. Hierarchical Keys
Keys can be organized hierarchically for better organization:
const userPrefs = autoFlashState({
key: 'preferences',
database: 'app-data',
collection: 'user-settings',
namespace: 'profile'
// Full key: app-data:profile:user-settings:preferences
});
Benefits:
- Logical grouping
- Easy to find related data
- Supports per-collection sync strategies
- Clear data ownership
3. Intelligent Storage
The system automatically selects the best storage adapter:
// System decides based on:
// - Data size
// - Access patterns
// - Data type
// - Priority level
const smallData = autoFlashState({
key: 'theme',
initialValue: 'dark'
}); // → localStorage (fast, simple)
const largeData = autoFlashState({
key: 'large-dataset',
initialValue: hugeArray
}); // → OPFS (best for large data)
const criticalData = autoFlashState({
key: 'auth-token',
initialValue: null,
priority: 'critical'
}); // → Multi-adapter redundancy
4. Optional Sync
Sync is opt-in and configurable:
// No sync (offline-only)
const localState = autoFlashState({
key: 'draft',
initialValue: ''
});
// MongoDB sync with priority
const syncedState = autoFlashState({
key: 'shared-doc',
database: 'collaboration',
collection: 'documents',
initialValue: '',
priority: 'high' // Sync every 5 minutes
});
// Real-time sync
const realtimeState = autoFlashState({
key: 'chat-messages',
database: 'chat',
collection: 'messages',
initialValue: [],
priority: 'critical' // Immediate sync
});
How It Works
Initialization Flow
// 1. App starts → initialization runs
provideFlashPersistence() // in app.config.ts
↓
FlashStateHydratorService.preload()
↓
// Pre-loads all discovered states from storage
AUTO_DISCOVERED_STATES.forEach(state => {
const value = await storage.get(state.fullKey);
if (value) cache.set(state.fullKey, value);
});
Read Flow
// User reads value
const value = userPrefs();
↓
// Signal getter called
signal.value
↓
// Value already in memory (pre-loaded)
// Return time: ~0.3ms (RAM access)
Write Flow
// User updates value
userPrefs.set({ theme: 'light' });
↓
// Signal setter called
signal.set(newValue)
↓
// UI updates immediately (signal reactivity)
↓
// Hydrator notified
FlashStateHydrator.onStateChange()
↓
// Debounce 300ms (avoid excessive writes)
↓
// Persist to storage
await storage.put(key, value)
↓
// (Optional) Queue for MongoDB sync
if (priority) syncEngine.enqueue({
collection,
data: value,
priority
});
Performance
Benchmarks
| Operation | Time | Notes |
|---|---|---|
| Read (cached) | 0.3ms | RAM cache hit |
| Read (storage) | 1-2ms | localStorage/Dexie |
| Write (local) | 1-5ms | Debounced, batched |
| Sync (background) | 50-200ms | Non-blocking |
| Real-time update | 10-50ms | Change Streams |
Memory Efficiency
- Cache Budget: Configurable (default 100KB)
- LRU Eviction: Least recently used
- Smart Pre-loading: Only discovered states
- Lazy Loading: Heavy data loaded on-demand
Network Efficiency
- Batching: Multiple operations combined
- Compression: Automatic for large data
- Priority Queue: Critical first
- Offline Queue: Syncs when online
Comparison
vs. NGXS
| Feature | Flash Persistence | NGXS |
|---|---|---|
| Setup | One function call | Store, actions, state, selectors |
| Boilerplate | Zero | High (actions, reducers) |
| Persistence | Automatic | Manual plugin configuration |
| TypeScript | Full inference | Requires decorators |
| Performance | 0.3ms cache | Depends on implementation |
| Sync | Built-in (MongoDB) | External solution needed |
| Learning Curve | Minimal (signals) | Moderate (Redux pattern) |
Migration:
// NGXS
@State({
name: 'preferences',
defaults: { theme: 'dark' }
})
export class PreferencesState {
@Action(SetTheme)
setTheme(ctx: StateContext<Preferences>, action: SetTheme) {
ctx.patchState({ theme: action.theme });
}
}
// Flash Persistence
const preferences = autoFlashState({
key: 'preferences',
initialValue: { theme: 'dark' }
});
preferences.update(p => ({ ...p, theme: 'light' }));
vs. RxJS + LocalStorage
| Feature | Flash Persistence | RxJS + Storage |
|---|---|---|
| Reactivity | Signals (built-in) | BehaviorSubject |
| Persistence | Automatic | Manual save() |
| Loading | Pre-loaded | Manual load() |
| Caching | Built-in (0.3ms) | Must implement |
| Sync | Built-in | Must implement |
| Memory Leaks | Auto-managed | Manual unsubscribe |
Comparison:
// RxJS + LocalStorage
class PrefsService {
private prefs$ = new BehaviorSubject<Preferences>(null);
async load() {
const stored = localStorage.getItem('prefs');
if (stored) this.prefs$.next(JSON.parse(stored));
}
async save(prefs: Preferences) {
this.prefs$.next(prefs);
localStorage.setItem('prefs', JSON.stringify(prefs));
}
ngOnDestroy() {
this.prefs$.complete(); // Must remember!
}
}
// Flash Persistence
const prefs = autoFlashState({
key: 'prefs',
initialValue: null
});
// That's it! No load(), save(), or cleanup needed
vs. IndexedDB Direct
| Feature | Flash Persistence | Direct IndexedDB |
|---|---|---|
| API | Simple signals | Complex async API |
| Caching | Automatic | Manual |
| Reactivity | Built-in | Must implement |
| Multi-adapter | Automatic | Single storage |
| Sync | Built-in | Must implement |
| Error Handling | Automatic | Manual try/catch everywhere |
Next Steps
- 🚀 Quick Start - Get started in 5 minutes
- 🎯 Component Integration - Use in components
- 📚 API Reference - Complete API docs
- 🎓 Best Practices - Recommended patterns
Ready to eliminate state management boilerplate? → Quick Start