learning_ai_common_plat/packages/sync/src/storage.ts
saravanakumardb1 359d6e18a5 feat: Platform Acceleration + A/B Testing Framework
Platform Acceleration Phase 1:
- @bytelyst/sync package: Offline-first sync engine with conflict resolution
  - Storage adapters: LocalStorage, InMemory, MMKV
  - Deduplication, retry with backoff, auto-flush on reconnect
  - 12 comprehensive tests
- @bytelyst/dashboard-components package: Shared React components
  - ErrorPage, NotFoundPage, LoadingSpinner, LoadingSkeleton, EmptyState, PageHeader
  - Theme-aware with CSS custom properties

A/B Testing Framework (Complete):
- Admin UI at /ops/ab-testing with experiments list, variant performance, AI suggestions
- Sidebar navigation with Beaker icon
- 40 tests passing in ab-testing module

All 909 platform-service tests pass.
2026-03-03 19:47:47 -08:00

128 lines
4.1 KiB
TypeScript

/**
* Storage Adapters
*
* @module @bytelyst/sync/storage
*/
import type { StorageAdapter } from './types.js';
// ─────────────────────────────────────────────────────────────────────────────
// LocalStorage Adapter (Web)
// ─────────────────────────────────────────────────────────────────────────────
export class LocalStorageAdapter implements StorageAdapter {
private prefix: string;
constructor(prefix = 'bytelyst:sync:') {
this.prefix = prefix;
}
getItem<T>(key: string): T | null {
if (typeof localStorage === 'undefined') return null;
const value = localStorage.getItem(this.prefix + key);
if (!value) return null;
try {
return JSON.parse(value) as T;
} catch {
return null;
}
}
setItem<T>(key: string, value: T): void {
if (typeof localStorage === 'undefined') return;
localStorage.setItem(this.prefix + key, JSON.stringify(value));
}
removeItem(key: string): void {
if (typeof localStorage === 'undefined') return;
localStorage.removeItem(this.prefix + key);
}
keys(): string[] {
if (typeof localStorage === 'undefined') return [];
const keys: string[] = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && key.startsWith(this.prefix)) {
keys.push(key.slice(this.prefix.length));
}
}
return keys;
}
}
// ─────────────────────────────────────────────────────────────────────────────
// In-Memory Adapter (Testing / SSR)
// ─────────────────────────────────────────────────────────────────────────────
export class InMemoryAdapter implements StorageAdapter {
private store = new Map<string, unknown>();
getItem<T>(key: string): T | null {
const value = this.store.get(key);
return value !== undefined ? (value as T) : null;
}
setItem<T>(key: string, value: T): void {
this.store.set(key, value);
}
removeItem(key: string): void {
this.store.delete(key);
}
keys(): string[] {
return Array.from(this.store.keys());
}
clear(): void {
this.store.clear();
}
}
// ─────────────────────────────────────────────────────────────────────────────
// MMKV Adapter Interface (React Native)
// ─────────────────────────────────────────────────────────────────────────────
export interface MMKVInstance {
getString(key: string): string | undefined;
set(key: string, value: string): void;
delete(key: string): void;
getAllKeys(): string[];
}
export class MMKVAdapter implements StorageAdapter {
private mmkv: MMKVInstance;
private prefix: string;
constructor(mmkv: MMKVInstance, prefix = 'sync:') {
this.mmkv = mmkv;
this.prefix = prefix;
}
getItem<T>(key: string): T | null {
const value = this.mmkv.getString(this.prefix + key);
if (!value) return null;
try {
return JSON.parse(value) as T;
} catch {
return null;
}
}
setItem<T>(key: string, value: T): void {
this.mmkv.set(this.prefix + key, JSON.stringify(value));
}
removeItem(key: string): void {
this.mmkv.delete(this.prefix + key);
}
keys(): string[] {
return this.mmkv
.getAllKeys()
.filter(k => k.startsWith(this.prefix))
.map(k => k.slice(this.prefix.length));
}
}