From 0e4b986e81f8132a44e61fa8df18860301cc3468 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Thu, 01 Jan 2026 20:31:10 +0000
Subject: [PATCH] =JakeVan preliminary additions

---
 assets/js/concise/DataStore.js |  581 ++++++++++++++++++++++++++++++---------------------------
 1 files changed, 308 insertions(+), 273 deletions(-)

diff --git a/assets/js/concise/DataStore.js b/assets/js/concise/DataStore.js
index 3d46224..95027e3 100644
--- a/assets/js/concise/DataStore.js
+++ b/assets/js/concise/DataStore.js
@@ -6,6 +6,7 @@
  *   this.store = window.jvbStore.register('feed', { config });
  */
 class DataStore {
+
 	constructor() {
 		// Singleton pattern
 		if (DataStore.instance) {
@@ -14,7 +15,7 @@
 		DataStore.instance = this;
 
 		// Shared resources
-		this.dbConfig = new Map();		// Definitions for the databases
+		this.dbConfig = new Map();      // Definitions for the databases
 		this.databases = new Map();     // Shared IndexedDB connections
 		this.stores = new Map();        // Registered store namespaces
 		this.subscribers = new Map();   // Per-store event subscribers
@@ -27,8 +28,6 @@
 		this.loading = document.querySelector('dialog.loading');
 
 		this.init();
-
-		// window.addEventListener('beforeunload', () => this.destroy());
 	}
 
 	async init() {
@@ -69,7 +68,6 @@
 				throw new Error(`Store "${config.storeName}" requires keyPath`);
 			}
 
-
 			const storeKey = `${name}_${config.storeName}`;
 
 			const store = {
@@ -93,16 +91,14 @@
 					// Behavior
 					showLoading: false,
 					delayFetch: true,
-					validateData: true, // Validate data is serializable
+					validateData: true,
 					...config
 				},
 				dbKey: name,
 				storeKey: storeKey,
 				data: new Map(),
 				cache: new Map(),
-				httpHeaders: new Map(),
-				subscribers: new Map(),
-				filters: {...(config.filters || {}) },
+				filters: {...(config.filters || {})},
 				isFetching: false,
 				currentRequest: null,
 				lastResponse: null,
@@ -122,7 +118,6 @@
 			}
 		});
 
-
 		// Initialize database asynchronously
 		this.initDB(name).catch(error => {
 			console.error(`Failed to initialize store "${name}":`, error);
@@ -146,6 +141,8 @@
 			delete: (id) => this.delete(name, id),
 			get: (id) => this.get(name, id),
 			getAll: () => this.getAll(name),
+			getAllByIndex: (indexName, value) => this.getAllByIndex(name, indexName, value),
+			filterByIndex: (criteria) => this.filterByIndex(name, criteria),
 			getFiltered: () => this.getFiltered(name),
 			clear: () => this.clear(name),
 
@@ -157,7 +154,6 @@
 
 			// Cache methods
 			clearCache: () => this.clearCache(name),
-			clearHttpHeaders: (key) => this.clearHttpHeaders(name, key),
 
 			// Event methods
 			subscribe: (callback) => this.subscribe(name, callback),
@@ -242,9 +238,10 @@
 		return formData;
 	}
 
-	/**
-	 * Initialize database for a specific store
-	 */
+	/***********************************************************************
+	 * DATABASE INITIALIZATION
+	 ***********************************************************************/
+
 	async initDB(name) {
 		const db = this.dbConfig.get(name);
 		if (!db || db._initialized) return;
@@ -290,7 +287,7 @@
 					this.loadStoreDataInBackground(storeName);
 					this.notify(storeName, 'db-init');
 				}
-			})
+			});
 
 		} catch (error) {
 			console.error(`Failed to initialize database for store "${name}":`, error);
@@ -332,29 +329,55 @@
 			});
 		}
 
-		// Cache store
+		// Cache store (now includes HTTP headers)
 		if (config.endpoint && !db.objectStoreNames.contains('cache')) {
 			const cacheStore = db.createObjectStore('cache', { keyPath: 'key' });
 			cacheStore.createIndex('timestamp', 'timestamp', { unique: false });
 		}
+	}
 
-		// HTTP headers store
-		if (config.useHttpCaching && !db.objectStoreNames.contains('headers')) {
-			db.createObjectStore('headers', { keyPath: 'key' });
+	/**
+	 * Generic loader for any object store
+	 */
+	async loadFromObjectStore(name, storeName, processItem) {
+		const store = this.stores.get(name);
+		if (!store?.db || !store.db.objectStoreNames.contains(storeName)) {
+			return [];
 		}
+
+		return new Promise((resolve) => {
+			const tx = store.db.transaction([storeName], 'readonly');
+			const objectStore = tx.objectStore(storeName);
+			const request = objectStore.getAll();
+
+			request.onsuccess = (e) => {
+				const items = e.target.result || [];
+				items.forEach(processItem);
+				resolve(items);
+			};
+
+			request.onerror = () => resolve([]);
+		});
 	}
 
 	loadStoreDataInBackground(name) {
 		const store = this.stores.get(name);
 		if (!store?.db) return;
 
-		const tasks = [
-			this.loadStoreData(name),
-			this.loadStoreCache(name),
-			this.loadStoreHeaders(name)
-		];
+		Promise.all([
+			// Load main data
+			this.loadFromObjectStore(name, store.config.storeName, (item) => {
+				const key = this.getItemKey(item, store.config.keyPath);
+				store.data.set(key, item);
+			}),
 
-		Promise.all(tasks)
+			// Load cache (includes HTTP headers now!)
+			this.loadFromObjectStore(name, 'cache', (item) => {
+				if (this.isCacheValid(item, store.config.TTL)) {
+					store.cache.set(item.key, item);
+				}
+			})
+		])
 			.then(() => {
 				this.notify(name, 'data-ready');
 
@@ -407,71 +430,6 @@
 		}
 	}
 
-	async loadStoreData(name) {
-		const store = this.stores.get(name);
-		if (!store?.db) return;
-
-		return new Promise((resolve) => {
-			const tx = store.db.transaction([store.config.storeName], 'readonly');
-			const objectStore = tx.objectStore(store.config.storeName);
-			const request = objectStore.getAll();
-
-			request.onsuccess = (e) => {
-				const items = e.target.result || [];
-				items.forEach(item => {
-					const key = this.getItemKey(item, store.config.keyPath);
-					store.data.set(key, item);
-				});
-				this.notify(name, 'data-loaded', { count: items.length });
-				resolve(items);
-			};
-
-			request.onerror = () => resolve([]);
-		});
-	}
-
-	async loadStoreCache(name) {
-		const store = this.stores.get(name);
-		if (!store?.db || !store.db.objectStoreNames.contains('cache')) return;
-
-		return new Promise((resolve) => {
-			const tx = store.db.transaction(['cache'], 'readonly');
-			const objectStore = tx.objectStore('cache');
-			const request = objectStore.getAll();
-
-			request.onsuccess = (e) => {
-				(e.target.result || []).forEach(item => {
-					if (this.isCacheValid(item, store.config.TTL)) {
-						store.cache.set(item.key, item);
-					}
-				});
-				resolve();
-			};
-
-			request.onerror = () => resolve();
-		});
-	}
-
-	async loadStoreHeaders(name) {
-		const store = this.stores.get(name);
-		if (!store?.db || !store.db.objectStoreNames.contains('headers')) return;
-
-		return new Promise((resolve) => {
-			const tx = store.db.transaction(['headers'], 'readonly');
-			const objectStore = tx.objectStore('headers');
-			const request = objectStore.getAll();
-
-			request.onsuccess = (e) => {
-				(e.target.result || []).forEach(header => {
-					store.httpHeaders.set(header.key, header);
-				});
-				resolve();
-			};
-
-			request.onerror = () => resolve();
-		});
-	}
-
 	async ensureStoreInitialized(name) {
 		const store = this.stores.get(name);
 		if (!store) {
@@ -483,6 +441,42 @@
 		}
 	}
 
+	/***********************************************************************
+	 * TRANSACTION HELPER
+	 ***********************************************************************/
+
+	/**
+	 * Create transaction helper - reduces boilerplate
+	 */
+	async withTransaction(name, storeNames, mode, callback) {
+		const store = this.stores.get(name);
+		if (!store?.db) return null;
+
+		// Ensure storeNames is an array
+		if (typeof storeNames === 'string') storeNames = [storeNames];
+
+		return new Promise((resolve, reject) => {
+			const tx = store.db.transaction(storeNames, mode);
+			const stores = storeNames.map(name => tx.objectStore(name));
+			const objectStore = stores.length === 1 ? stores[0] : stores;
+
+			let result;
+			tx.oncomplete = () => resolve(result);
+			tx.onerror = () => reject(tx.error);
+
+			// Call callback immediately to queue operations
+			try {
+				result = callback(objectStore, tx);
+			} catch (error) {
+				reject(error);
+			}
+		});
+	}
+
+	/***********************************************************************
+	 * FETCH & DATA PROCESSING
+	 ***********************************************************************/
+
 	async fetch(name) {
 		await this.ensureStoreInitialized(name);
 
@@ -524,15 +518,11 @@
 
 			const url = this.buildFetchUrl(name);
 			const headers = { ...store.config.headers };
-			const cachedHeaders = store.httpHeaders.get(cacheKey);
 
-			if (store.config.useHttpCaching && cachedHeaders) {
-				if (cachedHeaders.etag) {
-					headers['If-None-Match'] = cachedHeaders.etag;
-				}
-				if (cachedHeaders.lastModified) {
-					headers['If-Modified-Since'] = cachedHeaders.lastModified;
-				}
+			// Use HTTP cache headers from cache entry
+			if (store.config.useHttpCaching && cached) {
+				if (cached.etag) headers['If-None-Match'] = cached.etag;
+				if (cached.lastModified) headers['If-Modified-Since'] = cached.lastModified;
 			}
 
 			const controller = new AbortController();
@@ -556,7 +546,6 @@
 				}
 
 				// No cached data but server says not modified - return empty result
-				// This can happen on first load when cache headers exist but data doesn't
 				this.notify(name, 'data-loaded', {
 					cached: false,
 					notModified: true,
@@ -581,10 +570,7 @@
 
 			const data = await response.json();
 
-			if (store.config.useHttpCaching) {
-				this.storeResponseHeaders(name, cacheKey, response);
-			}
-			await this.processFetchedData(name, data, cacheKey);
+			await this.processFetchedData(name, data, cacheKey, response);
 
 			this.notify(name, 'data-loaded', {
 				cached: false,
@@ -628,47 +614,53 @@
 		return params.toString() ? `${baseUrl}?${params}` : baseUrl;
 	}
 
-	async processFetchedData(name, data, cacheKey) {
+	/**
+	 * Process fetched data (batch from server)
+	 */
+	async processFetchedData(name, data, cacheKey, response) {
 		const store = this.stores.get(name);
 		const items = data.items || [];
+		const changes = []; // Track all changes
 
-		// Batch process all items in a single transaction
+		// Batch process with single transaction
 		if (store.db && items.length > 0) {
-			const tx = store.db.transaction([store.config.storeName], 'readwrite');
-			const objectStore = tx.objectStore(store.config.storeName);
+			await this.withTransaction(name, store.config.storeName, 'readwrite', (objectStore) => {
+				items.forEach(item => {
+					try {
+						// Use shared save logic
+						const changeInfo = this._saveItem(name, item);
+						changes.push(changeInfo);
 
-			for (const item of items) {
-				const result = this.processForStorage(item, store.config.validateData);
-				if (result.valid) {
-					const key = this.getItemKey(result.data, store.config.keyPath);
-
-					// Store in memory
-					store.data.set(key, item);
-
-					// Queue for batch write
-					await objectStore.put(result.data);
-				}
-			}
-
-			// Wait for transaction to complete
-			await new Promise((resolve, reject) => {
-				tx.oncomplete = () => resolve();
-				tx.onerror = () => reject(tx.error);
+						// Queue for batch write
+						objectStore.put(changeInfo.processed);
+					} catch (error) {
+						console.error(`Error processing item:`, error);
+					}
+				});
 			});
-
 		}
 
+		// Update cache (now includes HTTP headers!)
 		const cacheEntry = {
 			key: cacheKey,
 			items: items.map(item => this.getItemKey(item, store.config.keyPath)),
 			timestamp: Date.now(),
 			endpoint: store.config.endpoint,
-			filters: { ...store.filters }
+			filters: { ...store.filters },
+			etag: response.headers.get('ETag'),
+			lastModified: response.headers.get('Last-Modified')
 		};
 
 		store.cache.set(cacheKey, cacheEntry);
-		await this.saveToCache(name, cacheKey, cacheEntry);
 
+		// Save cache to IndexedDB
+		if (store.db?.objectStoreNames.contains('cache')) {
+			await this.withTransaction(name, 'cache', 'readwrite', (objectStore) => {
+				objectStore.put(cacheEntry);
+			});
+		}
+
+		// Update lastResponse metadata
 		store.lastResponse = {
 			...data,
 			has_more: data.has_more || false,
@@ -676,13 +668,28 @@
 			pages: data.pages || 1,
 			queue_stats: data.queue_stats || {}
 		};
+
+		// Emit events for items with status changes
+		changes.forEach(changeInfo => {
+			if (changeInfo.statusChanged) {
+				this.notify(name, 'item-saved', {
+					item: changeInfo.item,
+					key: changeInfo.key,
+					previousItem: changeInfo.previousItem
+				});
+			}
+		});
 	}
 
+	/***********************************************************************
+	 * SAVE OPERATIONS
+	 ***********************************************************************/
+
 	/**
-	 * Save item to store
-	 * IMPORTANT: Item must be serializable (no DOM, FormData, Blobs)
+	 * Internal method: Save a single item with full tracking
+	 * Returns change info without writing to IndexedDB (caller handles that)
 	 */
-	async save(name, item) {
+	_saveItem(name, item) {
 		const store = this.stores.get(name);
 
 		const result = this.processForStorage(item, store.config.validateData);
@@ -693,18 +700,42 @@
 
 		const key = this.getItemKey(processed, store.config.keyPath);
 
-		// Store the original in memory (with original data intact)
+		// Capture previous state
+		const previousItem = store.data.get(key);
+
+		// Update in-memory store (with original data intact)
 		store.data.set(key, item);
 
-		// Store processed in IndexedDB
-		if (store.db) {
-			const tx = store.db.transaction([store.config.storeName], 'readwrite');
-			const objectStore = tx.objectStore(store.config.storeName);
-			await objectStore.put(processed);
-		}
+		// Return change info for event emission
+		return {
+			item,
+			previousItem,
+			key,
+			processed,
+			statusChanged: previousItem && previousItem.status !== item.status
+		};
+	}
 
-		this.notify(name, 'item-saved', { item, key });
-		return key;
+	/**
+	 * Save single item (public API)
+	 */
+	async save(name, item) {
+		const store = this.stores.get(name);
+		const changeInfo = this._saveItem(name, item);
+
+		// Write to IndexedDB immediately for single saves
+		await this.withTransaction(name, store.config.storeName, 'readwrite', (objectStore) => {
+			objectStore.put(changeInfo.processed);
+		});
+
+		// Always emit for explicit saves
+		this.notify(name, 'item-saved', {
+			item: changeInfo.item,
+			key: changeInfo.key,
+			previousItem: changeInfo.previousItem
+		});
+
+		return changeInfo.key;
 	}
 
 	processForStorage(obj, validate = true, path = 'root') {
@@ -719,30 +750,33 @@
 
 		// Reject functions
 		if (type === 'function') {
-			return validate ? { valid: false, error: `Function at ${path}` } : { valid: true, data: null };
+			if (validate) return { valid: false, error: `Function at ${path}` };
+			console.debug(`[DataStore] Stripped function at ${path}`);
+			return { valid: true, data: undefined };
 		}
 
 		// DOM elements
 		if (obj instanceof HTMLElement || obj.nodeType !== undefined) {
-			return validate ? { valid: false, error: `DOM element at ${path}` } : { valid: true, data: null };
+			if (validate) return { valid: false, error: `DOM element at ${path}` };
+			console.debug(`[DataStore] Stripped DOM element at ${path}`);
+			return { valid: true, data: undefined };
 		}
 
 		// FormData - convert and continue
 		if (obj instanceof FormData) {
-			return validate
-				? { valid: false, error: `FormData at ${path}` }
-				: { valid: true, data: this.formDataToObject(obj) };
+			if (validate) return { valid: false, error: `FormData at ${path}` };
+			console.debug(`[DataStore] Converted FormData at ${path}`);
+			return { valid: true, data: this.formDataToObject(obj) };
 		}
 
 		// Preserve safe types
-		if (obj instanceof Date || obj instanceof ArrayBuffer || ArrayBuffer.isView(obj)) {
+		if (obj instanceof Date || obj instanceof ArrayBuffer || ArrayBuffer.isView(obj) || obj instanceof Blob) {
 			return { valid: true, data: obj };
 		}
 
 		// Convert Sets to Arrays
 		if (obj instanceof Set) {
-			const arr = Array.from(obj);
-			return this.processForStorage(arr, validate, path);
+			return this.processForStorage(Array.from(obj), validate, path);
 		}
 
 		// Convert Maps to Objects
@@ -756,7 +790,7 @@
 			for (let i = 0; i < obj.length; i++) {
 				const result = this.processForStorage(obj[i], validate, `${path}[${i}]`);
 				if (!result.valid) return result;
-				if (result.data !== null) processed.push(result.data);
+				if (result.data !== undefined) processed.push(result.data);
 			}
 			return { valid: true, data: processed };
 		}
@@ -767,25 +801,27 @@
 			for (const [key, value] of Object.entries(obj)) {
 				const result = this.processForStorage(value, validate, `${path}.${key}`);
 				if (!result.valid) return result;
-				if (result.data !== null) processed[key] = result.data;
+				if (result.data !== undefined) processed[key] = result.data;
 			}
 			return { valid: true, data: processed };
 		}
 
-		return validate
-			? { valid: false, error: `Unknown type at ${path}` }
-			: { valid: true, data: null };
+		if (validate) return { valid: false, error: `Unknown type at ${path}` };
+		console.debug(`[DataStore] Stripped unknown type at ${path}`);
+		return { valid: true, data: undefined };
 	}
 
+	/***********************************************************************
+	 * DATA ACCESS
+	 ***********************************************************************/
+
 	async delete(name, id) {
 		const store = this.stores.get(name);
 		store.data.delete(id);
 
-		if (store.db) {
-			const tx = store.db.transaction([store.config.storeName], 'readwrite');
-			const objectStore = tx.objectStore(store.config.storeName);
-			await objectStore.delete(id);
-		}
+		await this.withTransaction(name, store.config.storeName, 'readwrite', (objectStore) => {
+			objectStore.delete(id);
+		});
 
 		this.notify(name, 'item-deleted', { id });
 	}
@@ -799,6 +835,64 @@
 		const store = this.stores.get(name);
 		return Array.from(store.data.values());
 	}
+	/**
+	 * Filter in-memory data by multiple index/value pairs
+	 * @param {string} name - Store name
+	 * @param {Object} criteria - Object of { indexName: acceptedValue(s) }
+	 * @returns {Array} - Items matching ALL criteria
+	 *
+	 * @example
+	 * filterByIndex(name, { field: 'upload_123', status: ['queued', 'uploading'] })
+	 */
+	filterByIndex(name, criteria) {
+		const store = this.stores.get(name);
+		if (!store) return [];
+
+		return Array.from(store.data.values()).filter(item => {
+			return Object.entries(criteria).every(([key, value]) => {
+				const accepted = Array.isArray(value) ? value : [value];
+				return accepted.includes(item[key]);
+			});
+		});
+	}
+	/**
+	 * Get all items matching an index value
+	 * @param {string} name - Store name
+	 * @param {string} indexName - Name of the index to query
+	 * @param {*} value - Value to match
+	 * @returns {Promise<Array>} - Matching items
+	 */
+	async getAllByIndex(name, indexName, value) {
+		const store = this.stores.get(name);
+		const values = Array.isArray(value) ? value : [value];
+
+		// Try IndexedDB index query first (more efficient for large datasets)
+		if (store.db && store.db.objectStoreNames.contains(store.config.storeName)) {
+			try {
+				const tx = store.db.transaction([store.config.storeName], 'readonly');
+				const objectStore = tx.objectStore(store.config.storeName);
+
+				if (objectStore.indexNames.contains(indexName)) {
+					const index = objectStore.index(indexName);
+
+					const results = await Promise.all(
+						values.map(v => new Promise((resolve, reject) => {
+							const request = index.getAll(v);
+							request.onsuccess = () => resolve(request.result || []);
+							request.onerror = () => reject(request.error);
+						}))
+					);
+
+					return results.flat();
+				}
+			} catch (error) {
+				console.warn(`Index query failed for "${indexName}", falling back to filter:`, error);
+			}
+		}
+
+		// Fallback: filter in-memory data
+		return Array.from(store.data.values()).filter(item => values.includes(item[indexName]));
+	}
 
 	getFiltered(name) {
 		const store = this.stores.get(name);
@@ -821,34 +915,48 @@
 		store.data.clear();
 		store.cache.clear();
 
-		if (store.db) {
-			const tx = store.db.transaction([store.config.storeName], 'readwrite');
-			const objectStore = tx.objectStore(store.config.storeName);
-			await objectStore.clear();
-		}
+		await this.withTransaction(name, store.config.storeName, 'readwrite', (objectStore) => {
+			objectStore.clear();
+		});
 
 		this.notify(name, 'data-cleared');
 	}
 
-	setFilter(name, key, value) {
+	/***********************************************************************
+	 * FILTER OPERATIONS
+	 ***********************************************************************/
+	async updateFilters(name, updates, clearAll = false) {
 		const store = this.stores.get(name);
-		const oldValue = store.filters[key];
+		const oldFilters = { ...store.filters };
 
-		if (value === null || value === undefined || value === '') {
-			delete store.filters[key];
+		if (clearAll) {
+			store.filters = { ...store.config.filters };
 		} else {
-			store.filters[key] = value;
+			// Apply updates (null/undefined/'' = delete)
+			Object.entries(updates).forEach(([key, value]) => {
+				if (value === null || value === undefined || value === '') {
+					delete store.filters[key];
+				} else {
+					store.filters[key] = value;
+				}
+			});
 		}
+
 		this.notify(name, 'filters-changed', {
+			oldFilters,
 			filters: store.filters,
-			changed: { key, oldValue, newValue: value }
+			updates
 		});
 
 		if (store.config.endpoint) {
-			this.fetch(name);
+			await this.fetch(name);
 		}
 	}
 
+	setFilter(name, key, value) {
+		return this.updateFilters(name, { [key]: value });
+	}
+
 	async setFilters(name, filters) {
 		const store = this.stores.get(name);
 
@@ -858,87 +966,54 @@
 
 		if (!hasChanges) return;
 
-		store.filters = { ...store.filters, ...filters };
-
-		this.notify(name, 'filters-changed', {
-			filters: store.filters,
-			changed: filters
-		});
-
-		if (store.config.endpoint) {
-			await this.fetch(name);
-		}
+		return this.updateFilters(name, filters);
 	}
 
 	removeFilter(name, key) {
-		const store = this.stores.get(name);
-		const oldValue = store.filters[key];
-
-		if (oldValue !== undefined) {
-			delete store.filters[key];
-
-			this.notify(name, 'filters-changed', {
-				filters: store.filters,
-				removed: { key, oldValue }
-			});
-
-			if (store.config.endpoint) {
-				this.fetch(name);
-			}
-		}
+		return this.updateFilters(name, { [key]: null });
 	}
 
 	clearFilters(name) {
-		const store = this.stores.get(name);
-		const oldFilters = { ...store.filters };
-
-		store.filters = { ...store.config.filters };
-
-		this.notify(name, 'filters-cleared', {
-			oldFilters,
-			filters: store.filters
-		});
-
-		if (store.config.endpoint) {
-			this.fetch(name);
-		}
+		return this.updateFilters(name, {}, true);
 	}
 
+	/***********************************************************************
+	 * CACHE OPERATIONS
+	 ***********************************************************************/
+
 	clearCache(name) {
 		const store = this.stores.get(name);
 		store.cache.clear();
 
-		if (store.db && store.db.objectStoreNames.contains('cache')) {
-			const tx = store.db.transaction(['cache'], 'readwrite');
-			const objectStore = tx.objectStore('cache');
-			objectStore.clear();
+		if (store.db?.objectStoreNames.contains('cache')) {
+			this.withTransaction(name, 'cache', 'readwrite', (objectStore) => {
+				objectStore.clear();
+			});
 		}
 
 		this.notify(name, 'cache-cleared');
 	}
 
-	clearHttpHeaders(name, cacheKey = null) {
-		const store = this.stores.get(name);
-
-		if (cacheKey) {
-			store.httpHeaders.delete(cacheKey);
-
-			if (store.db && store.db.objectStoreNames.contains('headers')) {
-				const tx = store.db.transaction(['headers'], 'readwrite');
-				const objectStore = tx.objectStore('headers');
-				objectStore.delete(cacheKey);
-			}
-		} else {
-			store.httpHeaders.clear();
-
-			if (store.db && store.db.objectStoreNames.contains('headers')) {
-				const tx = store.db.transaction(['headers'], 'readwrite');
-				const objectStore = tx.objectStore('headers');
-				objectStore.clear();
-			}
-		}
+	generateCacheKey(filters) {
+		const normalized = Object.keys(filters)
+			.sort()
+			.reduce((acc, key) => {
+				acc[key] = filters[key];
+				return acc;
+			}, {});
+		return JSON.stringify(normalized);
 	}
 
+	isCacheValid(entry, ttl) {
+		if (!entry || !entry.timestamp) return false;
+		const age = Date.now() - entry.timestamp;
+		return age < ttl;
+	}
+
+	/***********************************************************************
+	 * EVENT SYSTEM
+	 ***********************************************************************/
+
 	subscribe(name, callback) {
 		if (!this.subscribers.has(name)) {
 			this.subscribers.set(name, new Set());
@@ -961,49 +1036,9 @@
 		});
 	}
 
-	storeResponseHeaders(name, key, response) {
-		const store = this.stores.get(name);
-
-		const headers = {
-			key,
-			etag: response.headers.get('ETag'),
-			lastModified: response.headers.get('Last-Modified'),
-			timestamp: Date.now()
-		};
-
-		store.httpHeaders.set(key, headers);
-
-		if (store.db && store.db.objectStoreNames.contains('headers')) {
-			const tx = store.db.transaction(['headers'], 'readwrite');
-			const objectStore = tx.objectStore('headers');
-			objectStore.put(headers);
-		}
-	}
-
-	async saveToCache(name, key, data) {
-		const store = this.stores.get(name);
-		if (!store.db || !store.db.objectStoreNames.contains('cache')) return;
-
-		const tx = store.db.transaction(['cache'], 'readwrite');
-		const objectStore = tx.objectStore('cache');
-		await objectStore.put(data);
-	}
-
-	generateCacheKey(filters) {
-		const normalized = Object.keys(filters)
-			.sort()
-			.reduce((acc, key) => {
-				acc[key] = filters[key];
-				return acc;
-			}, {});
-		return JSON.stringify(normalized);
-	}
-
-	isCacheValid(entry, ttl) {
-		if (!entry || !entry.timestamp) return false;
-		const age = Date.now() - entry.timestamp;
-		return age < ttl;
-	}
+	/***********************************************************************
+	 * UTILITIES
+	 ***********************************************************************/
 
 	getItemKey(item, keyPath) {
 		if (typeof keyPath === 'function') {

--
Gitblit v1.10.0