Skip to content

Repository files navigation

i45

Type-safe browser storage wrapper for localStorage and sessionStorage

npm versionTypeScriptLicense: MIT

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

npm install i45

Quick Start

TypeScript (Recommended)

import{DataContext,StorageLocations,Logger}from"i45";// Define your data typeinterfaceUser{id: number;name: string;email: string;}// Create a type-safe context with config object (modern approach)constcontext=newDataContext<User>({storageKey: "Users",storageLocation: StorageLocations.LocalStorage,trackTimestamps: true,// Automatic metadata (default: true)loggingEnabled: true,logger: newLogger(),});// Or use legacy constructor (still supported)constlegacyContext=newDataContext<User>("Users",StorageLocations.LocalStorage);// Store data (fully typed!)awaitcontext.store([{id: 1,name: "Alice",email: "alice@example.com"},{id: 2,name: "Bob",email: "bob@example.com"},]);// Retrieve data (returns User[])constusers=awaitcontext.retrieve();console.log(users);// Get metadata (timestamps, version, count)constmetadata=awaitcontext.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 localStorageconstcontext=newDataContext();// Store data using sample dataawaitcontext.store(SampleData.Lists.Astronomy);// Retrieve dataconstdata=awaitcontext.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

i45 v3.0 is built with TypeScript and provides full type safety.

📖 See typescript.md for comprehensive TypeScript usage guide

import{DataContext,StorageLocations,typeStorageItem}from"i45";// Generic type for your datainterfaceProduct{id: string;name: string;price: number;}// Type-safe contextconstcontext=newDataContext<Product>("products",StorageLocations.SessionStorage);// Store - TypeScript ensures correct typesawaitcontext.store([{id: "1",name: "Widget",price: 9.99},{id: "2",name: "Gadget",price: 19.99},]);// Retrieve - returns Product[]constproducts=awaitcontext.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"constcontext=newDataContext();// Store dataawaitcontext.store(SampleData.Lists.Astronomy);// Retrieve dataconstdata=awaitcontext.retrieve();console.log(data);

Custom Storage Settings

Modern Config Object (Recommended)

import{DataContext,StorageLocations,Logger}from"i45";// Create context with configuration objectconstcontext=newDataContext<BookType>({storageKey: "Books",storageLocation: StorageLocations.SessionStorage,loggingEnabled: true,logger: newLogger(),});// Store books collectionawaitcontext.store(SampleData.JsonData.Books);// Retrieve dataconstbooks=awaitcontext.retrieve();console.log(books);

Legacy Constructor (Still Supported)

import{DataContext,StorageLocations,SampleData}from"i45";// Create context with positional parametersconstcontext=newDataContext("Books",StorageLocations.SessionStorage);// Store books collectionawaitcontext.store(SampleData.JsonData.Books);// Retrieve dataconstbooks=awaitcontext.retrieve();console.log(books);

Retrieving Data

import{DataContext,SampleData}from"i45";// Create contextconstcontext=newDataContext();// Store dataawaitcontext.store(SampleData.JsonData.States);// Retrieve and useconststates=awaitcontext.retrieve();console.log("State data:",states);

Explicit Method Signatures

v3.0.0 provides clear, explicit methods (no confusing overloads):

import{DataContext,StorageLocations}from"i45";constcontext=newDataContext<MyType>();// Store with different scopesawaitcontext.store(items);// Default key/locationawaitcontext.storeAs("customKey",items);// Custom keyawaitcontext.storeAt("key",StorageLocations.SessionStorage,items);// Full control// Retrieve with different scopesconstdata1=awaitcontext.retrieve();// Defaultconstdata2=awaitcontext.retrieveFrom("customKey");// Custom keyconstdata3=awaitcontext.retrieveAt("key",StorageLocations.SessionStorage);// Full control// Remove with different scopesawaitcontext.remove();// Defaultawaitcontext.removeFrom("customKey");// Custom keyawaitcontext.removeAt("key",StorageLocations.SessionStorage);// Full control

Retrieving Data from Custom Data Stores

import{DataContext,StorageLocations,SampleData}from"i45";// Create context with custom settingsconstcontext=newDataContext("Questions",StorageLocations.SessionStorage);// Store questionsawaitcontext.store(SampleData.JsonData.TriviaQuestions);// Retrieve by keyconstquestions=awaitcontext.retrieve("Questions");console.log(questions);// Retrieve with specific locationconstdata=awaitcontext.retrieve("MyItems",StorageLocations.LocalStorage);

Removing Items and Clearing the Data Store

// Delete a specific data store by keyawaitcontext.remove("Questions");// Clear all data from current storage locationawaitcontext.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";vardataContext=newDataContext();// create an array of countries using sample data.varcountries=SampleData.KeyValueLists.Countries;// save the collectiondataContext.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 optionsStorageLocations.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 constructorconstcontext=newDataContext("MyItems",StorageLocations.SessionStorage);// Or use propertiescontext.storageLocation=StorageLocations.LocalStorage;// Use IndexedDB for larger datasetsconstlargeDataContext=newDataContext({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 datasetsconstbooks=SampleData.JsonData.Books;conststates=SampleData.JsonData.States;constastronomy=SampleData.Lists.Astronomy;constcountries=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";constcontext=newDataContext();// Enable loggingcontext.loggingEnabled=true;// Operations will now be loggedawaitcontext.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 loggerconstcustomLogger=newLogger({logToConsole: true,logToStorage: false,});// Add to contextconstcontext=newDataContext();context.addClient(customLogger);// Multiple loggers supportedcontext.addClient(fileSystemLogger);context.addClient(apiLogger);

API Reference

📖 Complete API documentation:api.md

DataContext

Main class for managing browser storage operations.

classDataContext<T=any>{// Constructor - Config object (recommended)constructor(config?: DataContextConfig);// Constructor - Legacy (still supported)constructor(storageKey?: string,storageLocation?: StorageLocation);// PropertiesstorageKey: string;storageLocation: StorageLocation;loggingEnabled: boolean;logger: Logger|null;// Store methodsasyncstore(items: T[]): Promise<DataContext<T>>;asyncstoreAs(storageKey: string,items: T[]): Promise<DataContext<T>>;asyncstoreAt(storageKey: string,storageLocation: StorageLocation,items: T[]): Promise<DataContext<T>>;// Retrieve methodsasyncretrieve(): Promise<T[]>;asyncretrieveFrom(storageKey: string): Promise<T[]>;asyncretrieveAt(storageKey: string,storageLocation: StorageLocation): Promise<T[]>;// Remove methodsasyncremove(): Promise<DataContext<T>>;asyncremoveFrom(storageKey: string): Promise<DataContext<T>>;asyncremoveAt(storageKey: string,storageLocation: StorageLocation): Promise<DataContext<T>>;// Other methodsasyncclear(): 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+):

interfaceDataContextConfig{storageKey?: string;// Default: "Items"storageLocation?: StorageLocation;// Default: localStoragelogger?: Logger|null;// Optional logger instanceloggingEnabled?: boolean;// Default: false}

Types

// Storage location typeexportenumStorageLocations{SessionStorage="sessionStorage",LocalStorage="localStorage",}exporttypeStorageLocation= `${StorageLocations}`;// Storage item interfaceexportinterfaceStorageItem{name: string;value: string;}// Database settingsexportinterfaceDatabaseSettings{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{awaitcontext.store(data);}catch(error){if(errorinstanceofStorageKeyError){console.error("Invalid storage key:",error.key);}elseif(errorinstanceofStorageQuotaError){console.error("Storage full:",error.key,error.storageType);}elseif(errorinstanceofDataRetrievalError){console.error("Failed to retrieve:",error.key,"Cause:",error.cause);}elseif(errorinstanceofStorageLocationError){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

  1. New Architecture: Modular design with service orchestration (December 2025)
  2. Config Object Pattern: New recommended way to initialize DataContext
  3. TypeScript First: Full TypeScript rewrite with generic types
  4. Explicit Methods: store(), storeAs(), storeAt() instead of overloaded signatures
  5. 6 Custom Errors: Specific error classes for better error handling
  6. Centralized Validation: ValidationUtils for consistent validation
  7. Zero Duplication: 300+ lines of duplicate code eliminated
  8. Property Names: StorageItem.Namename, StorageItem.Valuevalue (camelCase)
  9. Async Operations: All storage operations return Promises

Quick Migration Example

// v2.x (Old)constcontext=newDataContext();context.setStorageKey("MyData");context.store(data);// May not be async// v3.x (New - Config Object)constcontext=newDataContext({storageKey: "MyData",loggingEnabled: true,});awaitcontext.store(data);// Always async// v3.x (New - Legacy Constructor)constcontext=newDataContext("MyData");awaitcontext.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

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

License

MIT © CIS Guru

Links

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

Changelog

See revisions.md for version history and release notes.

About

A wrapper for browser storage.

Topics

Resources

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages