- Updated CONFIGURATION.md to improve navigation and added mermaid diagrams for better visualization of processes. - Revised IPC_ARCHITECTURE.md to clarify the security model and added diagrams to illustrate the architecture. - Improved README.md files in core-api, core-events, core-i18n, and core-storage for consistency and clarity, including better descriptions and structural enhancements.
Storage Engine (@repo/core-storage)
@repo/core-storage is the Enterprise-grade, multi-tool storage engine for the Eigen Monorepo.
It provides a unified set of strictly-typed, secure, and fault-tolerant storage mechanisms tailored for React applications. It enforces strict Inversion of Control (IoC)βthe core engine knows absolutely nothing about your application's business domains; instead, the consuming apps inject their own configurations and types.
This package provides three primary storage solutions:
- Secure Local Storage (Strict Key-Gatekeeping & AES encryption)
- Secure IndexedDB (For larger key-value payloads)
- Offline-First PouchDB (For document-oriented, bi-directional sync data)
π Secure Key-Value Storage (LocalStorage & IndexedDB)
Browser storage is notoriously vulnerable to XSS attacks and pollution. The LocalStorageService and IndexedDBService implement a strict Gatekeeper pattern to solve this.
By forcing developers to register every key explicitly into either plainTextKeys or encryptedKeys, the engine guarantees:
- No unapproved or rogue keys can ever be written or read (throws a
Security Exception). - Highly sensitive tokens (e.g., JWTs) are automatically routed through the
@repo/utilsAES Encryption pipeline before touching the disk.
Architecture
graph TD
%% βββ Styling Definitions (Dark-Mode Friendly Enterprise Palette) βββ
classDef appEntity fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e3a8a
classDef coreEntity fill:#e2e8f0,stroke:#64748b,stroke-width:2px,color:#0f172a
classDef gatekeeper fill:#10b981,stroke:#047857,stroke-width:2px,color:#ffffff
classDef encrypt fill:#f59e0b,stroke:#b45309,stroke-width:2px,color:#ffffff
classDef error fill:#f43f5e,stroke:#be123c,stroke-width:2px,color:#ffffff
classDef database fill:#6366f1,stroke:#4338ca,stroke-width:2px,color:#ffffff
%% βββ Subgraphs βββ
subgraph Apps ["apps/* (App Autonomy)"]
REG[[AppStorageKey Config]]
UI([React Components / API])
INST{{Storage Instances}}
end
subgraph Core ["@repo/core-storage"]
FAC[Factory: createStorage]
API[IStorageService API]
VAL{Runtime Gatekeeper}
ERR>Throws Security Exception]
ENC{{AES Encryption Pipeline}}
LOCAL[(LocalStorage Adapter)]
IDB[(IndexedDB Adapter)]
end
%% βββ Flow & Relationships βββ
%% 1. Initialization Flow
REG -.->|Injects Keys & Config| FAC
FAC -.->|Returns| INST
%% 2. Runtime Execution Flow
UI ===>|getItem / setItem| INST
INST ---> API
API ---> VAL
%% 3. Gatekeeper Decision Tree
VAL -.->|Invalid Key| ERR
VAL ===>|Sensitive Key| ENC
VAL --->|Plain-text Key| LOCAL
VAL --->|Plain-text Key| IDB
%% 4. Post-Encryption Storage
ENC ===>|Encrypted Data| LOCAL
ENC ===>|Encrypted Data| IDB
%% βββ Apply Styles βββ
class REG,UI,INST appEntity;
class FAC,API coreEntity;
class VAL gatekeeper;
class ENC encrypt;
class ERR error;
class LOCAL,IDB database;
%% βββ Subgraph Backgrounds (Transparent for Native GitHub Support) βββ
style Apps fill:transparent,stroke:#818cf8,stroke-width:2px,stroke-dasharray: 5 5
style Core fill:transparent,stroke:#94a3b8,stroke-width:2px,stroke-dasharray: 5 5
Usage & Implementation
import { createLocalStorage, createIndexedDB } from '@repo/core-storage';
// 1. Define allowed keys (Strict Type Safety)
export type AppStorageKey = 'THEME' | 'ACCESS_TOKEN' | 'OFFLINE_CACHE';
// 2. Instantiate Local Storage
export const appStorage = createLocalStorage<AppStorageKey>({
plainTextKeys: new Set(['THEME']),
encryptedKeys: new Set(['ACCESS_TOKEN']), // Auto AES encrypted
});
// 3. Usage
await appStorage.setItem('ACCESS_TOKEN', 'ey...'); // Encrypted on disk
const theme = await appStorage.getItem('THEME'); // Plaintext on disk
β Do's and β Don'ts
- β
DO use TypeScript Literal Types for your storage keys (
type Keys = 'A' | 'B') to get full IntelliSense. - β
DO place Session/Auth tokens exclusively inside the
encryptedKeysSet. - β DON'T use native
window.localStoragedirectly anywhere in your React components. It bypasses our encryption and gatekeeper logic. - β DON'T mix domain data. Keep UI preferences (Theme, Sidebar state) in LocalStorage, and large datasets (Offline Caches) in IndexedDB.
π Offline-First Document Storage (PouchDB & CouchDB)
For complex, document-oriented data that requires fault-tolerance, offline support, and bi-directional cloud synchronization, we use the PouchDatabaseManager.
Architecture
graph TD
%% βββ Styling Definitions (Dark-Mode Friendly Enterprise Palette) βββ
classDef appEntity fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e3a8a
classDef coreEntity fill:#e2e8f0,stroke:#64748b,stroke-width:2px,color:#0f172a
classDef localDb fill:#6366f1,stroke:#4338ca,stroke-width:2px,color:#ffffff
classDef remoteDb fill:#f59e0b,stroke:#b45309,stroke-width:2px,color:#ffffff
%% βββ Subgraphs βββ
subgraph UI ["Consuming App (apps/*)"]
COMP([React Components / Forms])
end
subgraph CoreStorage ["@repo/core-storage Engine"]
MGR[PouchDatabaseManager Factory]
L_SALES[(Local PouchDB: Sales)]
L_INV[(Local PouchDB: Inventory)]
end
subgraph RemoteServer ["CouchDB Cluster"]
R_SALES[(Remote CouchDB: sales_db)]
R_INV[(Remote CouchDB: inventory_db)]
end
%% βββ Flow & Relationships βββ
COMP ===>|Read / Write| L_SALES
COMP ===>|Read / Write| L_INV
MGR -.->|Instantiates Multi-DB| L_SALES
MGR -.->|Instantiates Multi-DB| L_INV
L_SALES <===>|Native Sync Live and Retry| R_SALES
L_INV <===>|Native Sync Live and Retry| R_INV
%% βββ Apply Styles βββ
class COMP appEntity;
class MGR coreEntity;
class L_SALES,L_INV localDb;
class R_SALES,R_INV remoteDb;
%% βββ Subgraph Backgrounds (Transparent for Native GitHub Support) βββ
style UI fill:transparent,stroke:#3b82f6,stroke-width:2px,stroke-dasharray: 5 5
style CoreStorage fill:transparent,stroke:#94a3b8,stroke-width:2px,stroke-dasharray: 5 5
style RemoteServer fill:transparent,stroke:#f59e0b,stroke-width:2px,stroke-dasharray: 5 5
1. Initialization (IoC Factory)
The PouchDatabaseManager acts as a central singleton. It registers and manages all database instances. If a remote URL is provided, it automatically handles background synchronization.
import { PouchDatabaseManager } from '@repo/core-storage';
import type { Item } from './types';
export const dbManager = new PouchDatabaseManager();
export const itemDB = dbManager.register<Item>({
localName: 'items_db',
remoteUrl: 'http://admin:password@localhost:5984/items_db'
});
2. CRUD & MongoDB-style Queries
The registered database returns a PouchDatabaseWrapper. This wrapper fully abstracts the raw PouchDB API into clean, Promise-based helpers, auto-handling _rev conflicts.
| Method | Description |
|---|---|
create(data) |
Inserts a new document. Auto-generates _id if omitted. |
update(id, data) |
Auto-fetches the latest _rev to merge payloads cleanly. |
delete(id) |
Auto-fetches the latest _rev to safely remove the document. |
getAll() |
Retrieves all documents (filters out internal _design/ docs). |
find(options) |
Queries using MongoDB-style selectors (via pouchdb-find). |
// Example: Querying data using selectors
const expensiveItems = await itemDB.find({
selector: { price: { $gt: 100 }, category: 'electronics' }
});
3. Real-Time Reactivity (onChange Pub/Sub)
We implemented a Publisher-Subscriber (Pub/Sub) pattern inside the wrapper to handle real-time data changes efficiently. The wrapper maintains a single background connection to the changes feed and broadcasts events to all React subscribers.
import { useEffect, useCallback, useState } from 'react';
import { itemDB } from '../core/db';
export function InventoryList() {
const [items, setItems] = useState([]);
const loadData = useCallback(async () => {
const data = await itemDB.getAll();
setItems(data);
}, []);
useEffect(() => {
loadData();
// Subscribe to background sync mutations
const unsubscribe = itemDB.onChange(() => {
loadData();
});
// CRITICAL: Prevent memory leaks
return () => unsubscribe();
}, [loadData]);
}
β Do's and β Don'ts for PouchDB
- β
DO use
.onChange()to make your UI reactive to background cloud syncs. - β
DO return the
unsubscribefunction in youruseEffectcleanup block to prevent severe memory leaks. - β DON'T use
db.raw.changes()inside your React components. It creates zombie WebSocket connections and tightly couples your UI to PouchDB's specific API. - β DON'T pass the
_revproperty manually when updating or deleting. The wrapper'supdate()anddelete()methods handle revision fetching automatically.
β οΈ Troubleshooting
CouchDB CORS Infinite Retries
By providing a remoteUrl, the engine runs bi-directional sync in the background (live: true, retry: true). Fault tolerance is guaranteed: if CouchDB crashes, local reads/writes continue uninterrupted.
However, if your browser blocks CouchDB sync with a CORS error, PouchDB will misinterpret this as a network failure and enter an infinite retry loop, flooding your Network tab.
DO NOT try to fix this in the frontend Vite config or proxy! This is strictly a CouchDB server policy issue. You must enable CORS directly on the CouchDB cluster (editing its
local.inior via its dashboard) to alloworigins,credentials, andheaders.