Introduces comprehensive audit trail system with: - AuditService to track entity changes - SyncManager for background sync of audit entries - New CoreEvents for entity and audit tracking - Simplified sync architecture with event-driven approach Prepares system for enhanced compliance and change tracking
128 lines
3.8 KiB
TypeScript
128 lines
3.8 KiB
TypeScript
import { IStore } from './IStore';
|
|
|
|
/**
|
|
* IndexedDBContext - Database connection manager and provider
|
|
*
|
|
* RESPONSIBILITY:
|
|
* - Opens and manages IDBDatabase connection lifecycle
|
|
* - Creates object stores via injected IStore implementations
|
|
* - Provides shared IDBDatabase instance to all services
|
|
*
|
|
* SEPARATION OF CONCERNS:
|
|
* - This class: Connection management ONLY
|
|
* - OperationQueue: Queue and sync state operations
|
|
* - Entity Services: CRUD operations for specific entities
|
|
*
|
|
* USAGE:
|
|
* Services inject IndexedDBContext and call getDatabase() to access db.
|
|
* This lazy access pattern ensures db is ready when requested.
|
|
*/
|
|
export class IndexedDBContext {
|
|
private static readonly DB_NAME = 'CalendarDB';
|
|
private static readonly DB_VERSION = 3; // Bumped for audit store
|
|
static readonly QUEUE_STORE = 'operationQueue';
|
|
static readonly SYNC_STATE_STORE = 'syncState';
|
|
|
|
private db: IDBDatabase | null = null;
|
|
private initialized: boolean = false;
|
|
private stores: IStore[];
|
|
|
|
/**
|
|
* @param stores - Array of IStore implementations injected via DI
|
|
*/
|
|
constructor(stores: IStore[]) {
|
|
this.stores = stores;
|
|
}
|
|
|
|
/**
|
|
* Initialize and open the database
|
|
* Creates all entity stores, queue store, and sync state store
|
|
*/
|
|
async initialize(): Promise<void> {
|
|
return new Promise((resolve, reject) => {
|
|
const request = indexedDB.open(IndexedDBContext.DB_NAME, IndexedDBContext.DB_VERSION);
|
|
|
|
request.onerror = () => {
|
|
reject(new Error(`Failed to open IndexedDB: ${request.error}`));
|
|
};
|
|
|
|
request.onsuccess = () => {
|
|
this.db = request.result;
|
|
this.initialized = true;
|
|
resolve();
|
|
};
|
|
|
|
request.onupgradeneeded = (event) => {
|
|
const db = (event.target as IDBOpenDBRequest).result;
|
|
|
|
// Create all entity stores via injected IStore implementations
|
|
// Open/Closed Principle: Adding new entity only requires DI registration
|
|
this.stores.forEach(store => {
|
|
if (!db.objectStoreNames.contains(store.storeName)) {
|
|
store.create(db);
|
|
}
|
|
});
|
|
|
|
// Create operation queue store (sync infrastructure)
|
|
if (!db.objectStoreNames.contains(IndexedDBContext.QUEUE_STORE)) {
|
|
const queueStore = db.createObjectStore(IndexedDBContext.QUEUE_STORE, { keyPath: 'id' });
|
|
queueStore.createIndex('timestamp', 'timestamp', { unique: false });
|
|
}
|
|
|
|
// Create sync state store (sync metadata)
|
|
if (!db.objectStoreNames.contains(IndexedDBContext.SYNC_STATE_STORE)) {
|
|
db.createObjectStore(IndexedDBContext.SYNC_STATE_STORE, { keyPath: 'key' });
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Check if database is initialized
|
|
*/
|
|
public isInitialized(): boolean {
|
|
return this.initialized;
|
|
}
|
|
|
|
/**
|
|
* Get IDBDatabase instance
|
|
* Used by services to access the database
|
|
*
|
|
* @throws Error if database not initialized
|
|
* @returns IDBDatabase instance
|
|
*/
|
|
public getDatabase(): IDBDatabase {
|
|
if (!this.db) {
|
|
throw new Error('IndexedDB not initialized. Call initialize() first.');
|
|
}
|
|
return this.db;
|
|
}
|
|
|
|
/**
|
|
* Close database connection
|
|
*/
|
|
close(): void {
|
|
if (this.db) {
|
|
this.db.close();
|
|
this.db = null;
|
|
this.initialized = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete entire database (for testing/reset)
|
|
*/
|
|
static async deleteDatabase(): Promise<void> {
|
|
return new Promise((resolve, reject) => {
|
|
const request = indexedDB.deleteDatabase(IndexedDBContext.DB_NAME);
|
|
|
|
request.onsuccess = () => {
|
|
resolve();
|
|
};
|
|
|
|
request.onerror = () => {
|
|
reject(new Error(`Failed to delete database: ${request.error}`));
|
|
};
|
|
});
|
|
}
|
|
}
|