GitHub - xnodeoncode/i45: A wrapper for browser storage.
Type-safe browser storage wrapper for localStorage and sessionStorage
NodeJS package | GitHub Repository
A powerful, type-safe wrapper for browser storage (localStorage and sessionStorage) with built-in logging, validation, and error handling. Built with TypeScript for maximum type safety and developer experience.
Version 3.0.0-alpha.1 - Complete TypeScript rewrite with architectural refactoring (December 2025)
Features
- โจ Full TypeScript support with generic types:
DataContext<T> - ๐ Type-safe operations - catch errors at compile time
- ๐๏ธ Modern architecture - modular design with service orchestration
- ๏ฟฝ Three storage options - localStorage, sessionStorage, and IndexedDB (~50MB+)
- โฑ๏ธ Automatic timestamp tracking - transparent metadata with createdAt, updatedAt, version
- ๐ Time-based patterns - sync-since-timestamp, cache freshness, conflict resolution- ๐ Cross-tab synchronization (v3.2.0+) - automatic data sync between browser tabs- ๐พ Storage quota checking - monitor capacity and usage across storage types
- ๐ฆ Simple API - config object pattern or legacy constructor
- ๐ฏ Zero code duplication - 300+ lines eliminated through refactoring
- โ
Comprehensive validation - centralized with
ValidationUtils - ๐จ 6 custom error classes - specific, actionable error handling
- ๐ชต Built-in logging via i45-jslogger
- ๐งช Well tested - 272 tests with excellent coverage
- ๐ฏ Zero dependencies (except i45-jslogger and i45-sample-data)
- ๐ Sample data included via i45-sample-data
- ๐ณ Tree-shakeable ESM build
- ๐ Comprehensive type definitions (.d.ts)
๐ Documentation: Migration Guide | API Reference | TypeScript Guide | Examples | Offline Sync Guide | Cross-Tab Sync Guide
Installation
Quick Start
TypeScript (Recommended)
import { DataContext, StorageLocations, Logger } from "i45"; // Define your data type interface User { id: number; name: string; email: string; } // Create a type-safe context with config object (modern approach) const context = new DataContext<User>({ storageKey: "Users", storageLocation: StorageLocations.LocalStorage, trackTimestamps: true, // Automatic metadata (default: true) loggingEnabled: true, logger: new Logger(), }); // Or use legacy constructor (still supported) const legacyContext = new DataContext<User>( "Users", StorageLocations.LocalStorage ); // Store data (fully typed!) await context.store([ { id: 1, name: "Alice", email: "alice@example.com" }, { id: 2, name: "Bob", email: "bob@example.com" }, ]); // Retrieve data (returns User[]) const users = await context.retrieve(); console.log(users); // Get metadata (timestamps, version, count) const metadata = await context.getMetadata(); console.log(`Created: ${metadata.createdAt}, Version: ${metadata.version}`);
๐ More examples: examples.md | TypeScript Guide
JavaScript
import { DataContext, SampleData } from "i45"; // Create an instance of the datacontext // The default storage location is localStorage const context = new DataContext(); // Store data using sample data await context.store(SampleData.Lists.Astronomy); // Retrieve data const data = await context.retrieve(); console.log("Astronomy terms:", data);
Architecture
i45 v3.0.0 features a completely refactored, modular architecture (December 2025).
๐ See also: Migration Guide - Architecture | API Reference
/src
/core # Core application logic
DataContext.ts # Main storage context
StorageManager.ts # Service orchestration
/services
/base # Abstract base classes
IStorageService.ts # Service interface
BaseStorageService.ts # Shared service logic
LocalStorageService.ts
SessionStorageService.ts
/errors # Custom error classes
StorageKeyError.ts
StorageLocationError.ts
DataRetrievalError.ts
StorageQuotaError.ts
PersistenceServiceNotEnabled.ts
DataServiceUnavailable.ts
/models # Data models
DataContextConfig.ts
storageItem.ts
storageLocations.ts
/utils # Shared utilities
ValidationUtils.ts # Centralized validation
ErrorHandler.ts # Error management
Architecture Benefits
- Single Responsibility: Each module has one clear purpose
- Zero Duplication: 300+ lines of duplicate code eliminated
- Easy Testing: Isolated modules with 92% test coverage
- Type Safe: Strong typing throughout
- Extensible: Add new storage services by implementing interface
Usage
- TypeScript Usage
- Default Storage Settings
- Custom Storage Settings
- Retrieving Data
- Retrieving Data from Custom Data Stores
- Removing Items and Clearing the Data Store
- Storage Locations
- Using Sample Data
- Logging
TypeScript Usage
i45 v3.0 is built with TypeScript and provides full type safety.
๐ See typescript.md for comprehensive TypeScript usage guide
import { DataContext, StorageLocations, type StorageItem } from "i45"; // Generic type for your data interface Product { id: string; name: string; price: number; } // Type-safe context const context = new DataContext<Product>( "products", StorageLocations.SessionStorage ); // Store - TypeScript ensures correct types await context.store([ { id: "1", name: "Widget", price: 9.99 }, { id: "2", name: "Gadget", price: 19.99 }, ]); // Retrieve - returns Product[] const products = await context.retrieve(); products.forEach((p) => console.log(`${p.name}: $${p.price}`));
Default Storage Settings
import { DataContext, SampleData } from "i45"; // Create an instance - uses localStorage by default with key "i45" const context = new DataContext(); // Store data await context.store(SampleData.Lists.Astronomy); // Retrieve data const data = await context.retrieve(); console.log(data);
Custom Storage Settings
Modern Config Object (Recommended)
import { DataContext, StorageLocations, Logger } from "i45"; // Create context with configuration object const context = new DataContext<BookType>({ storageKey: "Books", storageLocation: StorageLocations.SessionStorage, loggingEnabled: true, logger: new Logger(), }); // Store books collection await context.store(SampleData.JsonData.Books); // Retrieve data const books = await context.retrieve(); console.log(books);
Legacy Constructor (Still Supported)
import { DataContext, StorageLocations, SampleData } from "i45"; // Create context with positional parameters const context = new DataContext("Books", StorageLocations.SessionStorage); // Store books collection await context.store(SampleData.JsonData.Books); // Retrieve data const books = await context.retrieve(); console.log(books);
Retrieving Data
import { DataContext, SampleData } from "i45"; // Create context const context = new DataContext(); // Store data await context.store(SampleData.JsonData.States); // Retrieve and use const states = await context.retrieve(); console.log("State data:", states);
Explicit Method Signatures
v3.0.0 provides clear, explicit methods (no confusing overloads):
import { DataContext, StorageLocations } from "i45"; const context = new DataContext<MyType>(); // Store with different scopes await context.store(items); // Default key/location await context.storeAs("customKey", items); // Custom key await context.storeAt("key", StorageLocations.SessionStorage, items); // Full control // Retrieve with different scopes const data1 = await context.retrieve(); // Default const data2 = await context.retrieveFrom("customKey"); // Custom key const data3 = await context.retrieveAt("key", StorageLocations.SessionStorage); // Full control // Remove with different scopes await context.remove(); // Default await context.removeFrom("customKey"); // Custom key await context.removeAt("key", StorageLocations.SessionStorage); // Full control
Retrieving Data from Custom Data Stores
import { DataContext, StorageLocations, SampleData } from "i45"; // Create context with custom settings const context = new DataContext("Questions", StorageLocations.SessionStorage); // Store questions await context.store(SampleData.JsonData.TriviaQuestions); // Retrieve by key const questions = await context.retrieve("Questions"); console.log(questions); // Retrieve with specific location const data = await context.retrieve("MyItems", StorageLocations.LocalStorage);
Removing Items and Clearing the Data Store
// Delete a specific data store by key await context.remove("Questions"); // Clear all data from current storage location await context.clear();
To clear all entries in all storage locations, call the clear() method.
Warning: Calling the clear() method will clear all entries in all storage locations.
import { DataContext } from "i45"; var dataContext = new DataContext(); // create an array of countries using sample data. var countries = SampleData.KeyValueLists.Countries; // save the collection dataContext.store("Countries", countries); // removes the item from storage. dataContext.remove("Countries"); // removes all items from all storage locations. // *** WARNING *** calling clear() will clears all entries. datacontext.clear();
Storage Locations
StorageLocations is an enum of available storage options:
import { StorageLocations } from "i45"; // Available options StorageLocations.LocalStorage; // Uses window.localStorage (default, ~5-10MB) StorageLocations.SessionStorage; // Uses window.sessionStorage (~5-10MB) StorageLocations.IndexedDB; // Uses IndexedDB (~50MB+, async database)
Using StorageLocations
import { DataContext, StorageLocations } from "i45"; // Specify storage location in constructor const context = new DataContext("MyItems", StorageLocations.SessionStorage); // Or use properties context.storageLocation = StorageLocations.LocalStorage; // Use IndexedDB for larger datasets const largeDataContext = new DataContext({ storageKey: "LargeDataset", storageLocation: StorageLocations.IndexedDB, });
Using Sample Data
The i45-sample-data package provides sample datasets for development and testing:
import { SampleData } from "i45"; // Access various sample datasets const books = SampleData.JsonData.Books; const states = SampleData.JsonData.States; const astronomy = SampleData.Lists.Astronomy; const countries = SampleData.KeyValueLists.Countries; console.log(books);
Logging
i45 integrates i45-jslogger for comprehensive logging support.
๐ See also: examples.md - Custom Logger
Built-In Logging
import { DataContext } from "i45"; const context = new DataContext(); // Enable logging context.loggingEnabled = true; // Operations will now be logged await context.store([{ id: 1, name: "Test" }]);
When enabled, log messages are written to the console and stored in localStorage.
Using a Custom Logger
Add custom logging clients to receive DataContext events:
import { DataContext, Logger } from "i45"; // Create or use your existing logger const customLogger = new Logger({ logToConsole: true, logToStorage: false, }); // Add to context const context = new DataContext(); context.addClient(customLogger); // Multiple loggers supported context.addClient(fileSystemLogger); context.addClient(apiLogger);
API Reference
๐ Complete API documentation: api.md
DataContext
Main class for managing browser storage operations.
class DataContext<T = any> { // Constructor - Config object (recommended) constructor(config?: DataContextConfig); // Constructor - Legacy (still supported) constructor(storageKey?: string, storageLocation?: StorageLocation); // Properties storageKey: string; storageLocation: StorageLocation; loggingEnabled: boolean; logger: Logger | null; // Store methods async store(items: T[]): Promise<DataContext<T>>; async storeAs(storageKey: string, items: T[]): Promise<DataContext<T>>; async storeAt( storageKey: string, storageLocation: StorageLocation, items: T[] ): Promise<DataContext<T>>; // Retrieve methods async retrieve(): Promise<T[]>; async retrieveFrom(storageKey: string): Promise<T[]>; async retrieveAt( storageKey: string, storageLocation: StorageLocation ): Promise<T[]>; // Remove methods async remove(): Promise<DataContext<T>>; async removeFrom(storageKey: string): Promise<DataContext<T>>; async removeAt( storageKey: string, storageLocation: StorageLocation ): Promise<DataContext<T>>; // Other methods async clear(): Promise<DataContext<T>>; addClient(logger: Logger): DataContext<T>; getCurrentSettings(): { storageKey: string; storageLocation: StorageLocation; }; getData(): any[]; printLog(): any[]; }
DataContextConfig
Configuration object for DataContext (v3.0.0+):
interface DataContextConfig { storageKey?: string; // Default: "Items" storageLocation?: StorageLocation; // Default: localStorage logger?: Logger | null; // Optional logger instance loggingEnabled?: boolean; // Default: false }
Types
// Storage location type export enum StorageLocations { SessionStorage = "sessionStorage", LocalStorage = "localStorage", } export type StorageLocation = `${StorageLocations}`; // Storage item interface export interface StorageItem { name: string; value: string; } // Database settings export interface DatabaseSettings { storageKey: string; storageLocation: StorageLocation; loggingEnabled: boolean; }
Error Types
v3.0.0 provides 6 custom error classes for specific error handling.
๐ Full error documentation: api.md - Error Classes | Examples
import { PersistenceServiceNotEnabled, DataServiceUnavailable, StorageKeyError, StorageLocationError, DataRetrievalError, StorageQuotaError, // NEW in December 2025 } from "i45"; try { await context.store(data); } catch (error) { if (error instanceof StorageKeyError) { console.error("Invalid storage key:", error.key); } else if (error instanceof StorageQuotaError) { console.error("Storage full:", error.key, error.storageType); } else if (error instanceof DataRetrievalError) { console.error("Failed to retrieve:", error.key, "Cause:", error.cause); } else if (error instanceof StorageLocationError) { console.error( "Invalid location:", error.location, "Valid:", error.validLocations ); } }
Migration from v2.x
v3.0.0 includes breaking changes and major architectural improvements. See migration.md for the complete migration guide.
Key Changes
- New Architecture: Modular design with service orchestration (December 2025)
- Config Object Pattern: New recommended way to initialize DataContext
- TypeScript First: Full TypeScript rewrite with generic types
- Explicit Methods:
store(),storeAs(),storeAt()instead of overloaded signatures - 6 Custom Errors: Specific error classes for better error handling
- Centralized Validation:
ValidationUtilsfor consistent validation - Zero Duplication: 300+ lines of duplicate code eliminated
- Property Names:
StorageItem.Nameโname,StorageItem.Valueโvalue(camelCase) - Async Operations: All storage operations return Promises
Quick Migration Example
// v2.x (Old) const context = new DataContext(); context.setStorageKey("MyData"); context.store(data); // May not be async // v3.x (New - Config Object) const context = new DataContext({ storageKey: "MyData", loggingEnabled: true, }); await context.store(data); // Always async // v3.x (New - Legacy Constructor) const context = new DataContext("MyData"); await context.store(data); // Always async
For detailed migration steps, error handling examples, and troubleshooting, see migration.md.
Browser Support
- Chrome/Edge: Latest 2 versions
- Firefox: Latest 2 versions
- Safari: Latest 2 versions
- Modern browsers with ES2015+ support
Requirements
- Node.js 16+ (for development)
- Modern browser with localStorage/sessionStorage support
Framework Integration
- React: See examples.md - React Integration
- Vue: See examples.md - Vue Integration
- TypeScript: See typescript.md for type-safe integration patterns
Testing
i45 v3.0.0 includes comprehensive testing:
- 205 tests with Jest
- 91.7% statement coverage
- Unit tests for all components including IndexedDBService
- Type safety tests
- Error handling tests
- Browser storage mocking with fake-indexeddb
# Run tests npm test # Run tests with coverage npm run test:coverage # Watch mode npm run test:watch
๐ Testing examples: examples.md - Testing Examples
Documentation
Core Documentation
- README.md - This file (getting started and quick reference)
- api.md - Complete API reference with all methods, properties, and error classes
- typescript.md - TypeScript usage guide with patterns and best practices
- examples.md - 20+ comprehensive examples including React/Vue integration
- offline-sync.md - Comprehensive offline sync patterns, conflict resolution, and queue management
- migration.md - Complete v2.x โ v3.x migration guide
Additional Resources
- revisions.md - Version history and changelog
- REFACTORING-SUMMARY.md - December 2025 refactoring details
License
MIT ยฉ CIS Guru
Links
- npm package
- GitHub Repository
- Issue Tracker
- i45-jslogger - Logging support
- i45-sample-data - Sample datasets
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Changelog
See revisions.md for version history and release notes.