Last Updated: December 20, 2025
Current Version: v3.0.0-alpha.1
This guide helps you migrate from i45 v2.x to v3.0.0, which includes breaking changes and major improvements.
i45 v3.0.0 is a complete TypeScript rewrite with significant API improvements and architectural refactoring:
- ✅ TypeScript-first with full type safety
- ✅ New architecture - modular design with clear separation of concerns
- ✅ Simplified API - removed deprecated methods
- ✅ Better property naming - use getters/setters instead of methods
- ✅ Improved validation - centralized validation utilities with descriptive errors
- ✅ Enhanced error handling - 6 custom error classes
- ✅ Zero code duplication - 300+ lines of duplicate code eliminated
- ✅ 100% ESM - no CommonJS support
- ✅ Updated dependencies - i45-jslogger v2.0.1+
- ✅ Comprehensive testing - 172 tests with 92% coverage
v3.0.0 introduces a completely refactored architecture:
/src
/core # Core application logic
DataContext.ts # Main class (refactored, 32% smaller)
StorageManager.ts # Service orchestration (NEW)
/services
/base # Base abstractions
IStorageService.ts # Service interface (NEW)
BaseStorageService.ts # Abstract base class (refactored)
LocalStorageService.ts # Refactored implementation
SessionStorageService.ts # Refactored implementation
/errors # Individual error classes (NEW)
PersistenceServiceNotEnabled.ts
DataServiceUnavailable.ts
StorageKeyError.ts
StorageLocationError.ts
DataRetrievalError.ts
StorageQuotaError.ts # NEW error class
index.ts # Barrel export
/models # Data models
DataContextConfig.ts # Configuration interface (NEW)
storageItem.ts
storageLocations.ts
databaseSettings.ts
/utils # Shared utilities (NEW)
ValidationUtils.ts # Centralized validation
ErrorHandler.ts # Error management
v2.x (Old):
const context = new DataContext("Items", StorageLocations.LocalStorage);v3.0.0 (New - Recommended):
// Modern config object approach
const context = new DataContext<MyType>({
storageKey: "Items",
storageLocation: StorageLocations.LocalStorage,
loggingEnabled: true,
logger: new Logger()
});
// Legacy approach still supported for backward compatibility
const cError Handling - 6 Custom Error Classes
**v3.0.0 introduces custom error classes** for better error handling:
```typescript
import {
PersistenceServiceNotEnabled,
DataServiceUnavailable,
StorageKeyError,
StorageLocationError,
DataRetrievalError,
StorageQuotaError // NEW in December 2025
} from 'i45';
try {
await context.store(items);
} catch (error) {
if (error instanceof StorageKeyError) {
console.error('Invalid key:', error.key);
} else if (error instanceof StorageQuotaError) {
console.error('Storage full:', error.key, error.storageType);
} else if (error instanceof DataRetrievalError) {
console.error('Retrieval failed:', error.key, error.cause);
}
}v3.0.0 uses ValidationUtils for consistent validation and throws errors for invalid inputs:
// ❌ These now throw errors:
context.storageKey = ""; // StorageKeyError: Cannot be empty
context.storageKey = " "; // StorageKeyError: Cannot be whitespace only
context.storageKey = "localStorage"; // Warning: Reserved name
await context.store(null); // Error: Items cannot be null
await context.store("not an array"); // Error: Items must be an array
context.storageLocation = "invalid"; // StorageLocationError: Invalid location
context.setStorageKey("MyKey"); // Method call ❌
context.setStorageLocation(StorageLocations.SessionStorage); // Method call ❌v3.0.0 (New):
const context = new DataContext<MyType>();
context.storageKey = "MyKey"; // Property assignment ✅
context.storageLocation = StorageLocations.SessionStorage; // Property assignment ✅The following methods have been completely removed:
- ❌
setStorageKey()- Usecontext.storageKey = valueinstead - ❌
StorageLocation()- Usecontext.storageLocation = valueinstead - ❌
setStorageLocation()- Usecontext.storageLocation = valueinstead
v2.x (Old):
const context = new DataContext("Items", StorageLocations.LocalStorage);
const items = await context.retrieve(); // Returns any[]
```6. Method Signatures - Explicit Methods
**v3.0.0 uses clear, explicit method names** (no complex overloading):
```typescript
// Store operations
await context.store(items); // Default key/location
await context.storeAs(key, items); // Custom key
await context.storeAt(key, location, items); // Full control
// Retrieve operations
const items = await context.retrieve(); // Default
const items = await context.retrieveFrom(key); // Custom key
const items = await context.retrieveAt(key, location); // Full control
// Remove operations
await context.remove(); // Default
await context.removeFrom(key); // Custom key
await context.removeAt(key, location); // Full controlv3.0.0 (New):
interface UserData {
id: number;
name: string;
}
const context = new DataContext<UserData>(
"Items",
StorageLocations.LocalStorage
);
const items = await context.retrieve(); // Returns UserData[] ✅v3.0.0 throws errors for invalid inputs (v2.x only logged warnings):
// ❌ These now throw errors:
context.storageKey = ""; // Error: Cannot be empty
cont8xt.storageKey = " "; // Error: Cannot be whitespace only
await context.store(null); // Error: Items cannot be null
await context.store("not an array"); // Error: Items must be an arrayv2.x (Old):
logger.enableLogging = true; // ❌ Old property name
logger.enableEvents = true; // ❌ Old property namev3.0.0 (New):
logger.loggingEnabled = true; // ✅ New property name
logger.enableDispatchEvents = true; // ✅ New property namev2.x supported both CommonJS and ESM:
const { DataContext } = require("i45"); // ❌ No longer supportedv3.0.0 is ESM only:
import { DataContext } from "i45"; // ✅ Use importnpm install i45@^3.0.0
# Also update i45-jslogger if using logging
npm install i45-jslogger@^2.0.1Before (v2.x):
const { DataContext, StorageLocations } = require("i45");After (v3.0.0):
// Basic imports
import { DataContext, StorageLocations } from 'i45';
// Import error classes
import { 4: Update Constructor Calls
**Before (v2.x)**:
```javascript
const context = new DataContext();
context.setStorageKey("MyData");
context.setStorageLocation(StorageLocations.SessionStorage);After (v3.0.0 - Recommended):
// Use config object (recommended)
const context = new DataContext<MyType>({
storageKey: "MyData",
storageLocation: StorageLocations.SessionStorage,
loggingEnabled: true,
logger: new Logger(),
});
6;
// Or use legacy constructor (still supported)
const context = new DataContext<MyType>(
"MyData",
StorageLocations.SessionStorage
);
context.loggingEnabled = true;
context.logger = new Logger();StorageKeyError, StorageLocationError, DataRetrievalError, StorageQuotaError } from 'i45';
// Import type definitions import type { StorageItem, StorageLocation, DataContextConfig } from 'i45';
### Step 3: Convert to TypeScript (Recommended)
If using JavaScript, consider migrating to TypeScript to take full advantage of type safety:
```typescript
// Create types for your data
interface Product {
id: number;
name: string;
price: number;
}
// Use generic type parameter
const products = new DataContext<Product>("Products");
Before (v2.x):
context.setStorageKey("MyData");
context.setStorageLocation(StorageLocations.SessionStorage);
const key = context.getStorageKey();After (v3.0.0): 7: Add Error Handling
Since v3.0.0 throws errors for invalid inputs and has custom error classes, add proper error handling:
import {
StorageKeyError,
StorageLocationError,
DataRetrievalError,
StorageQuotaError
} from 'i45';
try {
await context.store(items);
} 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 {
console.error("Storage operation failed:", error);
}
**Before (v2.x)**:
```javascript
const logger = new Logger();
logger.enableLogging = true;
logger.enableEvents = true;After (v3.0.0):
const logger = new Logger();
logger.loggingEnabled = true;
logger.en8: Update Method Calls
**Before (v2.x)** - Complex overloading:
```javascript
await context.store(items);
await context.store("customKey", items); // Confusing signature
await context.store("key", StorageLocations.SessionStorage, items);After (v3.0.0) - Explicit methods:
await context.store(items); // Default
await context.storeAs("customKey", items); // Custom key
await context.storeAt("key", StorageLocations.SessionStorage, items); // Full control
### Step 6: Update Imports
**Before (v2.x)**:
```javascript
const { DataContext, StorageLocations } = require("i45");After (v3.0.0):
import { DataContext, StorageLocations } from "i45";
// or
import type { StorageItem, StorageLocation } from "i45";const { DataContext, StorageLocations } = require("i45");
const context = new DataContext();
context.setStorageKey("Users");
context.setStorageLocation(StorageLocations.SessionStorage);
const users = [ (Recommended):
```typescript
import { DataContext, StorageLocations, Logger } from "i45";
import { StorageKeyError, DataRetrievalError } from "i45";
interface User {
id: number;
name: string;
}
// Modern config object approach (recommended)
const context = new DataContext<User>({
storageKey: "Users",
storageLocation: StorageLocations.SessionStorage,
loggingEnabled: true,
logger: new Logger()
});
const users: User[] = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];
try {
await context.store(users);
const data = await context.retrieve();
console.log(data); // Type-safe: User[]
} ca1. Configuration Object Pattern
```typescript
// Clean, self-documenting configuration
const context = new DataContext<MyType>({
storageKey: "MyData",
storageLocation: StorageLocations.LocalStorage,
loggingEnabled: true,
logger: myLogger
});The new StorageManager class eliminates code duplication and provides a clean abstraction:
// Internally handles all storage operations
// No more duplicated switch statements
// Single place for service selection logicAll validation is centralized with consistent error messages:
// validateStorageKey()
// validateStorageLocation()
// validateArray()
// isReservedKey()
// sanitizeKey()- Better Validation
// All of these now throw descriptive errors:
context.storageKey = ""; // StorageKeyError: Cannot be empty
context.storageKey = "localStorage"; // Warning: Reserved name
await context.store(null); // Error: Items cannot be null
await context.storeAs("", items); // StorageKeyError: key cannot be empty
context.storageLocation = "invalid"; // StorageLocationError: Invalid location- Improved Type Exports
// Type imports
import type {
StorageItem,
StorageLocation,
DatabaseSettings,
DataContextConfig, // NEW
} from "i45";
// Factory functions
import {
createStorageItem,
createDatabaseSettings,
createDefaultConfig, // NEW
mergeConfig, // NEW
} from "i45";
// Utility exports
import {
ValidationUtils, // NEW
ErrorHandler, // NEW
StorageManager, // NEW
} from "i45";- 172 tests (up from 0 in v2.x)
- 92.08% statement coverage
- Type safety tests
- Error handling tests
- Integration testsentralized validation with
ValidationUtils - Unified error handling with
ErrorHandler - No repeated switch statements
- 32% reduction in DataContext size (920 → 620 lines)ataServiceUnavailable, StorageKeyError, StorageLocationError, DataRetrievalError, StorageQuotaError // NEW } from 'i45';
### 5. tch (error) {
if (error instanceof StorageKeyError) {
console.error("Invalid key:", error.key);
} else if (error instanceof DataRetrievalError) {
console.error("Retrieval failed:", error.key, error.cause);
} else {
console.error("Storage operation failed:", error);
}
}
import { DataContext, StorageLocations } from "i45";
interface User {
id: number;
name: string;
}
or config object:
```typescript
// ❌ context.setStorageKey("key");
context.storageKey = "key"; // ✅
// Or use config object
const context = new DataContext({ storageKey: "key" }); // ✅Problem: Using old overloaded method signatures.
Solution: Use explicit methods:
// ❌ await context.store("key", location, items);
await context.storeAt("key", location, items);import { DataContext, StorageLocations } from "i45";
interface User {
id: number;
name: string;
}
const context = new DataContext<User>("Users", StorageLocations.SessionStorage);
const users: User[] = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];
try {
await context.store(users);
const data = await context.retrieve();
console.log(data);
} catch (error) {
console.error("Storage operation failed:", error);
}interface Product {
id: number;
name: string;
price: number;
}
const products = new DataContext<Product>("Pr with `ValidationUtils`.
**Solution**: Ensure data is valid before storing:
```typescript
if (Array.isArray(items) && items.length > 0) {
await context.store(items);
} else {
console.warn('No items to store');
}Problem: Generic error catching doesn't provide enough detail.
Solution: Use instanceof checks with custom error classes:
import {
StorageKeyError,
StorageQuotaError,
DataRetrievalError
} from 'i45';
try {
await context.store(items);
} catch (error) {
if (error instanceof StorageQuotaError) {
// Handle storage full
console.error('Storage full for key:', error.key);
// Maybe clear old data
} else if (error instanceof StorageKeyError) {
// Handle invalid key
console.error('Invalid key:', error.key);
} else if (error instanceof DataRetrievalError) {
// Handle retrieval failure
Performance Improvements
The December 2025 refactoring brings significant performance improvements:
- **Faster builds**: <1 second (0.9s average)
- **Smaller codebase**: 32% reduction in main class size
- **Better tree-shaking**: Modular architecture
- **Reduced bundle overhead**: Eliminated duplicate code
## Architecture Benefits
The new modular architecture provides:
1. **Single Responsibility**: Each module has one clear purpose
2. **Easy Testing**: Isolated modules are easier to test
3. **Better Maintainability**: Clear separation of concerns
4. **Easy Extension**: Add new storage services by implementing interface
5. **Type Safety**: Strong typing throughout
```typescript
// Example: Using the new utilities directly
import { ValidationUtils } from 'i45';
// Validate before operations
ValidationUtils.validateStorageKey(userInput);
ValidationUtils.validateArray(items);
// Check for reserved names
if (ValidationUtils.isReservedKey(key)) {
console.warn('Using reserved key:', key);
}After migrating to v3.0.0:
- Review error handling - Use custom error classes
- Enable TypeScript - Full type safety benefits
- Update tests - Test with new validation rules
- Monitor storage - New error classes help catch issues
- Read the docs - Check api.md for complete reference
- GitHub Issues: https://github.com/xnodeoncode/i45/issues
- Discussions: https://github.com/xnodeoncode/i45/discussions
- Documentation:
- README.md - Getting started
- api.md - Complete API reference
- typescript.md - TypeScript usage guide
- examples.md - Comprehensive examples
- Changelog: See revisions.md for detailed version history
- Refactoring Details: See REFACTORING-SUMMARY.md for December 2025 changes
i45 v3.0.0 is a major upgrade focused on:
- ✅ Type safety - Full TypeScript with generics
- ✅ Simpler, cleaner API - Config object pattern
- ✅ Better validation - Centralized with ValidationUtils
- ✅ Modern ESM-only architecture - Tree-shakeable modules
- ✅ Zero code duplication - 300+ lines eliminated
- ✅ Custom error classes - 6 specific error types
- ✅ Comprehensive testing - 172 tests, 92% coverage
- ✅ Modular design - Clear separation of concerns
The migration effort is worthwhile for the improved developer experience, type safety, maintainability, and performanc
import type { StorageItem, StorageLocation, DatabaseSettings } from "i45";
import { createStorageItem, createDatabaseSettings } from "i45";Problem: CommonJS require() no longer works.
Solution: Use ESM imports:
import { DataContext } from "i45";Problem: Using old method names.
Solution: Use property assignment:
// ❌ context.setStorageKey("key");
context.storageKey = "key"; // ✅Problem: v3.0.0 validates inputs strictly.
Solution: Ensure data is valid before storing:
if (Array.isArray(items) && items.length > 0) {
await context.store(items);
}Problem: Missing type definitions.
Solution: i45 v3.0.0 includes full TypeScript definitions. Ensure your tsconfig.json includes:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true
}
}- GitHub Issues: https://github.com/xnodeoncode/i45/issues
- Documentation: See README.md for complete API documentation
- Testing: See testing.md for test examples
i45 v3.0.0 is a major upgrade focused on:
- ✅ Type safety
- ✅ Simpler, cleaner API
- ✅ Better validation
- ✅ Modern ESM-only architecture
While migration requires changes, the improved developer experience and type safety make it worthwhile!