strata-storage
Version:
Zero-dependency universal storage plugin providing a unified API for all storage operations across web, Android, and iOS platforms
201 lines (200 loc) • 6.38 kB
JavaScript
/**
* Storage strategy implementation for automatic adapter selection
*/
import { StorageStrategy } from '@/types';
import { AdapterRegistry } from "./AdapterRegistry.js";
/**
* Strategy manager for selecting appropriate storage adapters
*/
export class StrategyManager {
registry;
strategy;
constructor(registry, strategy = StorageStrategy.PERFORMANCE_FIRST) {
this.registry = registry;
this.strategy = strategy;
}
/**
* Get the best adapter based on current strategy
*/
async getBestAdapter(preferredTypes, requirements) {
const available = await this.registry.getAvailable();
// Filter by preferred types if specified
let candidates = preferredTypes
? available.filter((adapter) => preferredTypes.includes(adapter.name))
: available;
// Filter by requirements
if (requirements) {
candidates = candidates.filter((adapter) => this.meetsRequirements(adapter.capabilities, requirements));
}
// Sort by strategy
candidates.sort((a, b) => this.compareAdapters(a, b));
return candidates[0] || null;
}
/**
* Get multiple adapters sorted by preference
*/
async getAdapterChain(count, preferredTypes, requirements) {
const available = await this.registry.getAvailable();
let candidates = preferredTypes
? available.filter((adapter) => preferredTypes.includes(adapter.name))
: available;
if (requirements) {
candidates = candidates.filter((adapter) => this.meetsRequirements(adapter.capabilities, requirements));
}
candidates.sort((a, b) => this.compareAdapters(a, b));
return candidates.slice(0, count);
}
/**
* Check if capabilities meet requirements
*/
meetsRequirements(capabilities, requirements) {
for (const [key, value] of Object.entries(requirements)) {
const capKey = key;
if (typeof value === 'boolean' && capabilities[capKey] !== value) {
return false;
}
if (capKey === 'maxSize' && typeof value === 'number') {
const capSize = capabilities.maxSize;
if (capSize !== -1 && capSize < value) {
return false;
}
}
}
return true;
}
/**
* Compare two adapters based on current strategy
*/
compareAdapters(a, b) {
switch (this.strategy) {
case StorageStrategy.PERFORMANCE_FIRST:
return this.comparePerformance(a, b);
case StorageStrategy.PERSISTENCE_FIRST:
return this.comparePersistence(a, b);
case StorageStrategy.SECURITY_FIRST:
return this.compareSecurity(a, b);
case StorageStrategy.CAPACITY_FIRST:
return this.compareCapacity(a, b);
default:
return 0;
}
}
/**
* Compare adapters by performance
*/
comparePerformance(a, b) {
// Priority order for performance
const performanceOrder = [
'memory',
'sessionStorage',
'localStorage',
'cache',
'indexedDB',
'preferences',
'sqlite',
'filesystem',
'cookies',
'secure',
];
const aIndex = performanceOrder.indexOf(a.name);
const bIndex = performanceOrder.indexOf(b.name);
// Prefer synchronous adapters for performance
if (a.capabilities.synchronous !== b.capabilities.synchronous) {
return a.capabilities.synchronous ? -1 : 1;
}
return aIndex - bIndex;
}
/**
* Compare adapters by persistence
*/
comparePersistence(a, b) {
// Prefer persistent storage
if (a.capabilities.persistent !== b.capabilities.persistent) {
return a.capabilities.persistent ? -1 : 1;
}
// Priority order for persistence
const persistenceOrder = [
'sqlite',
'filesystem',
'secure',
'indexedDB',
'preferences',
'localStorage',
'cache',
'cookies',
'sessionStorage',
'memory',
];
const aIndex = persistenceOrder.indexOf(a.name);
const bIndex = persistenceOrder.indexOf(b.name);
return aIndex - bIndex;
}
/**
* Compare adapters by security
*/
compareSecurity(a, b) {
// Prefer encrypted storage
if (a.capabilities.encrypted !== b.capabilities.encrypted) {
return a.capabilities.encrypted ? -1 : 1;
}
// Priority order for security
const securityOrder = [
'secure',
'preferences',
'sqlite',
'indexedDB',
'filesystem',
'localStorage',
'sessionStorage',
'cache',
'memory',
'cookies',
];
const aIndex = securityOrder.indexOf(a.name);
const bIndex = securityOrder.indexOf(b.name);
return aIndex - bIndex;
}
/**
* Compare adapters by capacity
*/
compareCapacity(a, b) {
// Compare max sizes (-1 means unlimited)
const aSize = a.capabilities.maxSize;
const bSize = b.capabilities.maxSize;
if (aSize === -1 && bSize !== -1)
return -1;
if (bSize === -1 && aSize !== -1)
return 1;
if (aSize !== -1 && bSize !== -1) {
return bSize - aSize; // Higher capacity first
}
// Priority order for capacity
const capacityOrder = [
'filesystem',
'sqlite',
'indexedDB',
'cache',
'preferences',
'secure',
'localStorage',
'memory',
'sessionStorage',
'cookies',
];
const aIndex = capacityOrder.indexOf(a.name);
const bIndex = capacityOrder.indexOf(b.name);
return aIndex - bIndex;
}
/**
* Update strategy
*/
setStrategy(strategy) {
this.strategy = strategy;
}
/**
* Get current strategy
*/
getStrategy() {
return this.strategy;
}
}