refactor: share IndexedDB transaction execution
This commit is contained in:
@@ -1,70 +1,46 @@
|
||||
import { createModuleLogger } from "./log_system/log_funcs.js";
|
||||
import { ALL_STORES, IMAGE_STORE, STATE_STORE, createDBRequest, openLayerForgeDB, } from "./db_shared.js";
|
||||
import { ALL_STORES, IMAGE_STORE, STATE_STORE, executeDBStoreRequest, } from "./db_shared.js";
|
||||
const log = createModuleLogger('db');
|
||||
export async function getCanvasState(id) {
|
||||
log.info(`Getting state for id: ${id}`);
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([STATE_STORE.name], 'readonly');
|
||||
const store = transaction.objectStore(STATE_STORE.name);
|
||||
const result = await createDBRequest(store, 'get', id, "Error getting canvas state", log);
|
||||
const result = await executeDBStoreRequest(log, ALL_STORES, STATE_STORE, 'readonly', 'get', id, "Error getting canvas state");
|
||||
log.debug(`Get success for id: ${id}`, result ? 'found' : 'not found');
|
||||
return result ? result.state : null;
|
||||
}
|
||||
export async function setCanvasState(id, state) {
|
||||
log.info(`Setting state for id: ${id}`);
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([STATE_STORE.name], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE.name);
|
||||
await createDBRequest(store, 'put', { id, state }, "Error setting canvas state", log);
|
||||
await executeDBStoreRequest(log, ALL_STORES, STATE_STORE, 'readwrite', 'put', { id, state }, "Error setting canvas state");
|
||||
log.debug(`Set success for id: ${id}`);
|
||||
}
|
||||
export async function removeCanvasState(id) {
|
||||
log.info(`Removing state for id: ${id}`);
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([STATE_STORE.name], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE.name);
|
||||
await createDBRequest(store, 'delete', id, "Error removing canvas state", log);
|
||||
await executeDBStoreRequest(log, ALL_STORES, STATE_STORE, 'readwrite', 'delete', id, "Error removing canvas state");
|
||||
log.debug(`Remove success for id: ${id}`);
|
||||
}
|
||||
export async function saveImage(imageId, imageSrc) {
|
||||
log.info(`Saving image with id: ${imageId}`);
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([IMAGE_STORE.name], 'readwrite');
|
||||
const store = transaction.objectStore(IMAGE_STORE.name);
|
||||
await createDBRequest(store, 'put', { imageId, imageSrc }, "Error saving image", log);
|
||||
await executeDBStoreRequest(log, ALL_STORES, IMAGE_STORE, 'readwrite', 'put', { imageId, imageSrc }, "Error saving image");
|
||||
log.debug(`Image saved successfully for id: ${imageId}`);
|
||||
}
|
||||
export async function getImage(imageId) {
|
||||
log.info(`Getting image with id: ${imageId}`);
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([IMAGE_STORE.name], 'readonly');
|
||||
const store = transaction.objectStore(IMAGE_STORE.name);
|
||||
const result = await createDBRequest(store, 'get', imageId, "Error getting image", log);
|
||||
const result = await executeDBStoreRequest(log, ALL_STORES, IMAGE_STORE, 'readonly', 'get', imageId, "Error getting image");
|
||||
log.debug(`Get image success for id: ${imageId}`, result ? 'found' : 'not found');
|
||||
return result ? result.imageSrc : null;
|
||||
}
|
||||
export async function removeImage(imageId) {
|
||||
log.info(`Removing image with id: ${imageId}`);
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([IMAGE_STORE.name], 'readwrite');
|
||||
const store = transaction.objectStore(IMAGE_STORE.name);
|
||||
await createDBRequest(store, 'delete', imageId, "Error removing image", log);
|
||||
await executeDBStoreRequest(log, ALL_STORES, IMAGE_STORE, 'readwrite', 'delete', imageId, "Error removing image");
|
||||
log.debug(`Remove image success for id: ${imageId}`);
|
||||
}
|
||||
export async function getAllImageIds() {
|
||||
log.info("Getting all image IDs...");
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([IMAGE_STORE.name], 'readonly');
|
||||
const store = transaction.objectStore(IMAGE_STORE.name);
|
||||
const imageIds = await createDBRequest(store, 'getAllKeys', null, "Error getting all image IDs", log);
|
||||
const imageIds = await executeDBStoreRequest(log, ALL_STORES, IMAGE_STORE, 'readonly', 'getAllKeys', null, "Error getting all image IDs");
|
||||
log.debug(`Found ${imageIds.length} image IDs in database`);
|
||||
return imageIds;
|
||||
}
|
||||
export async function clearAllCanvasStates() {
|
||||
log.info("Clearing all canvas states...");
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([STATE_STORE.name], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE.name);
|
||||
await createDBRequest(store, 'clear', null, "Error clearing canvas states", log);
|
||||
await executeDBStoreRequest(log, ALL_STORES, STATE_STORE, 'readwrite', 'clear', null, "Error clearing canvas states");
|
||||
log.info("All canvas states cleared successfully.");
|
||||
}
|
||||
|
||||
@@ -45,6 +45,13 @@ export function createDBRequest(store, operation, data, errorMessage, log) {
|
||||
};
|
||||
});
|
||||
}
|
||||
/** Open the shared database, execute one store operation, and preserve request error handling. */
|
||||
export async function executeDBStoreRequest(log, stores, storeDefinition, mode, operation, data, errorMessage, openOptions = {}) {
|
||||
const database = await openLayerForgeDB(log, stores, openOptions);
|
||||
const transaction = database.transaction([storeDefinition.name], mode);
|
||||
const store = transaction.objectStore(storeDefinition.name);
|
||||
return createDBRequest(store, operation, data, errorMessage, log);
|
||||
}
|
||||
/** Open the shared LayerForge database and create only the stores needed by the caller. */
|
||||
export function openLayerForgeDB(log, stores, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { STATE_STORE, createDBRequest, openLayerForgeDB } from './db_shared.js';
|
||||
import { STATE_STORE, executeDBStoreRequest } from './db_shared.js';
|
||||
console.log('[StateWorker] Worker script loaded and running.');
|
||||
function log(...args) {
|
||||
console.log('[StateWorker]', ...args);
|
||||
@@ -8,15 +8,12 @@ function error(...args) {
|
||||
}
|
||||
const dbLogger = { info: log, error };
|
||||
async function setCanvasState(id, state) {
|
||||
const db = await openLayerForgeDB(dbLogger, [STATE_STORE], {
|
||||
await executeDBStoreRequest(dbLogger, [STATE_STORE], STATE_STORE, 'readwrite', 'put', { id, state }, "Error setting canvas state", {
|
||||
openingMessage: null,
|
||||
upgradingMessage: 'Upgrading IndexedDB in worker...',
|
||||
successMessage: 'IndexedDB opened successfully in worker.',
|
||||
logStoreCreation: false,
|
||||
});
|
||||
const transaction = db.transaction([STATE_STORE.name], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE.name);
|
||||
await createDBRequest(store, 'put', { id, state }, "Error setting canvas state", dbLogger);
|
||||
}
|
||||
self.onmessage = async function (e) {
|
||||
log('Message received from main thread:', e.data ? 'data received' : 'no data');
|
||||
|
||||
@@ -3,8 +3,7 @@ import {
|
||||
ALL_STORES,
|
||||
IMAGE_STORE,
|
||||
STATE_STORE,
|
||||
createDBRequest,
|
||||
openLayerForgeDB,
|
||||
executeDBStoreRequest,
|
||||
} from "./db_shared.js";
|
||||
|
||||
const log = createModuleLogger('db');
|
||||
@@ -21,83 +20,115 @@ interface CanvasImageDB {
|
||||
|
||||
export async function getCanvasState(id: string): Promise<any | null> {
|
||||
log.info(`Getting state for id: ${id}`);
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([STATE_STORE.name], 'readonly');
|
||||
const store = transaction.objectStore(STATE_STORE.name);
|
||||
|
||||
const result = await createDBRequest(store, 'get', id, "Error getting canvas state", log) as CanvasStateDB;
|
||||
const result = await executeDBStoreRequest<CanvasStateDB | undefined>(
|
||||
log,
|
||||
ALL_STORES,
|
||||
STATE_STORE,
|
||||
'readonly',
|
||||
'get',
|
||||
id,
|
||||
"Error getting canvas state"
|
||||
);
|
||||
log.debug(`Get success for id: ${id}`, result ? 'found' : 'not found');
|
||||
return result ? result.state : null;
|
||||
}
|
||||
|
||||
export async function setCanvasState(id: string, state: any): Promise<void> {
|
||||
log.info(`Setting state for id: ${id}`);
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([STATE_STORE.name], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE.name);
|
||||
|
||||
await createDBRequest(store, 'put', {id, state}, "Error setting canvas state", log);
|
||||
await executeDBStoreRequest<void>(
|
||||
log,
|
||||
ALL_STORES,
|
||||
STATE_STORE,
|
||||
'readwrite',
|
||||
'put',
|
||||
{id, state},
|
||||
"Error setting canvas state"
|
||||
);
|
||||
log.debug(`Set success for id: ${id}`);
|
||||
}
|
||||
|
||||
export async function removeCanvasState(id: string): Promise<void> {
|
||||
log.info(`Removing state for id: ${id}`);
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([STATE_STORE.name], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE.name);
|
||||
|
||||
await createDBRequest(store, 'delete', id, "Error removing canvas state", log);
|
||||
await executeDBStoreRequest<void>(
|
||||
log,
|
||||
ALL_STORES,
|
||||
STATE_STORE,
|
||||
'readwrite',
|
||||
'delete',
|
||||
id,
|
||||
"Error removing canvas state"
|
||||
);
|
||||
log.debug(`Remove success for id: ${id}`);
|
||||
}
|
||||
|
||||
export async function saveImage(imageId: string, imageSrc: string | ImageBitmap): Promise<void> {
|
||||
log.info(`Saving image with id: ${imageId}`);
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([IMAGE_STORE.name], 'readwrite');
|
||||
const store = transaction.objectStore(IMAGE_STORE.name);
|
||||
|
||||
await createDBRequest(store, 'put', {imageId, imageSrc}, "Error saving image", log);
|
||||
await executeDBStoreRequest<void>(
|
||||
log,
|
||||
ALL_STORES,
|
||||
IMAGE_STORE,
|
||||
'readwrite',
|
||||
'put',
|
||||
{imageId, imageSrc},
|
||||
"Error saving image"
|
||||
);
|
||||
log.debug(`Image saved successfully for id: ${imageId}`);
|
||||
}
|
||||
|
||||
export async function getImage(imageId: string): Promise<string | ImageBitmap | null> {
|
||||
log.info(`Getting image with id: ${imageId}`);
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([IMAGE_STORE.name], 'readonly');
|
||||
const store = transaction.objectStore(IMAGE_STORE.name);
|
||||
|
||||
const result = await createDBRequest(store, 'get', imageId, "Error getting image", log) as CanvasImageDB;
|
||||
const result = await executeDBStoreRequest<CanvasImageDB | undefined>(
|
||||
log,
|
||||
ALL_STORES,
|
||||
IMAGE_STORE,
|
||||
'readonly',
|
||||
'get',
|
||||
imageId,
|
||||
"Error getting image"
|
||||
);
|
||||
log.debug(`Get image success for id: ${imageId}`, result ? 'found' : 'not found');
|
||||
return result ? result.imageSrc : null;
|
||||
}
|
||||
|
||||
export async function removeImage(imageId: string): Promise<void> {
|
||||
log.info(`Removing image with id: ${imageId}`);
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([IMAGE_STORE.name], 'readwrite');
|
||||
const store = transaction.objectStore(IMAGE_STORE.name);
|
||||
|
||||
await createDBRequest(store, 'delete', imageId, "Error removing image", log);
|
||||
await executeDBStoreRequest<void>(
|
||||
log,
|
||||
ALL_STORES,
|
||||
IMAGE_STORE,
|
||||
'readwrite',
|
||||
'delete',
|
||||
imageId,
|
||||
"Error removing image"
|
||||
);
|
||||
log.debug(`Remove image success for id: ${imageId}`);
|
||||
}
|
||||
|
||||
export async function getAllImageIds(): Promise<string[]> {
|
||||
log.info("Getting all image IDs...");
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([IMAGE_STORE.name], 'readonly');
|
||||
const store = transaction.objectStore(IMAGE_STORE.name);
|
||||
|
||||
const imageIds = await createDBRequest(store, 'getAllKeys', null, "Error getting all image IDs", log) as string[];
|
||||
const imageIds = await executeDBStoreRequest<string[]>(
|
||||
log,
|
||||
ALL_STORES,
|
||||
IMAGE_STORE,
|
||||
'readonly',
|
||||
'getAllKeys',
|
||||
null,
|
||||
"Error getting all image IDs"
|
||||
);
|
||||
log.debug(`Found ${imageIds.length} image IDs in database`);
|
||||
return imageIds;
|
||||
}
|
||||
|
||||
export async function clearAllCanvasStates(): Promise<void> {
|
||||
log.info("Clearing all canvas states...");
|
||||
const db = await openLayerForgeDB(log, ALL_STORES);
|
||||
const transaction = db.transaction([STATE_STORE.name], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE.name);
|
||||
|
||||
await createDBRequest(store, 'clear', null, "Error clearing canvas states", log);
|
||||
await executeDBStoreRequest<void>(
|
||||
log,
|
||||
ALL_STORES,
|
||||
STATE_STORE,
|
||||
'readwrite',
|
||||
'clear',
|
||||
null,
|
||||
"Error clearing canvas states"
|
||||
);
|
||||
log.info("All canvas states cleared successfully.");
|
||||
}
|
||||
|
||||
@@ -78,6 +78,24 @@ export function createDBRequest(
|
||||
});
|
||||
}
|
||||
|
||||
/** Open the shared database, execute one store operation, and preserve request error handling. */
|
||||
export async function executeDBStoreRequest<T>(
|
||||
log: DBLogger,
|
||||
stores: readonly DBStoreDefinition[],
|
||||
storeDefinition: DBStoreDefinition,
|
||||
mode: IDBTransactionMode,
|
||||
operation: DBRequestOperation,
|
||||
data: any,
|
||||
errorMessage: string,
|
||||
openOptions: DBOpenOptions = {},
|
||||
): Promise<T> {
|
||||
const database = await openLayerForgeDB(log, stores, openOptions);
|
||||
const transaction = database.transaction([storeDefinition.name], mode);
|
||||
const store = transaction.objectStore(storeDefinition.name);
|
||||
|
||||
return createDBRequest(store, operation, data, errorMessage, log) as Promise<T>;
|
||||
}
|
||||
|
||||
/** Open the shared LayerForge database and create only the stores needed by the caller. */
|
||||
export function openLayerForgeDB(
|
||||
log: DBLogger,
|
||||
|
||||
+16
-10
@@ -1,4 +1,4 @@
|
||||
import {STATE_STORE, createDBRequest, openLayerForgeDB} from './db_shared.js';
|
||||
import {STATE_STORE, executeDBStoreRequest} from './db_shared.js';
|
||||
|
||||
console.log('[StateWorker] Worker script loaded and running.');
|
||||
|
||||
@@ -13,15 +13,21 @@ function error(...args: any[]): void {
|
||||
const dbLogger = {info: log, error};
|
||||
|
||||
async function setCanvasState(id: string, state: any): Promise<void> {
|
||||
const db = await openLayerForgeDB(dbLogger, [STATE_STORE], {
|
||||
openingMessage: null,
|
||||
upgradingMessage: 'Upgrading IndexedDB in worker...',
|
||||
successMessage: 'IndexedDB opened successfully in worker.',
|
||||
logStoreCreation: false,
|
||||
});
|
||||
const transaction = db.transaction([STATE_STORE.name], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE.name);
|
||||
await createDBRequest(store, 'put', {id, state}, "Error setting canvas state", dbLogger);
|
||||
await executeDBStoreRequest<void>(
|
||||
dbLogger,
|
||||
[STATE_STORE],
|
||||
STATE_STORE,
|
||||
'readwrite',
|
||||
'put',
|
||||
{id, state},
|
||||
"Error setting canvas state",
|
||||
{
|
||||
openingMessage: null,
|
||||
upgradingMessage: 'Upgrading IndexedDB in worker...',
|
||||
successMessage: 'IndexedDB opened successfully in worker.',
|
||||
logStoreCreation: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
self.onmessage = async function(e: MessageEvent<{ state: any, nodeId: string }>): Promise<void> {
|
||||
|
||||
@@ -173,8 +173,24 @@ test('the worker uses shared database infrastructure for canvas-state persistenc
|
||||
assert.match(sharedSource, /export const IMAGE_STORE_NAME = 'CanvasImages'/);
|
||||
assert.match(sharedSource, /export const DB_VERSION = 3/);
|
||||
assert.match(workerSource, /from '\.\/db_shared\.js'/);
|
||||
assert.match(workerSource, /openLayerForgeDB\(dbLogger, \[STATE_STORE\]/);
|
||||
assert.match(workerSource, /executeDBStoreRequest<void>\(\s*dbLogger,\s*\[STATE_STORE\]/s);
|
||||
assert.doesNotMatch(workerSource, /const DB_NAME/);
|
||||
assert.doesNotMatch(workerSource, /function openDB/);
|
||||
assert.doesNotMatch(workerSource, /function createDBRequest/);
|
||||
});
|
||||
|
||||
test('database callers share one transaction helper while preserving store and worker options', async () => {
|
||||
const [dbSource, workerSource] = await Promise.all([
|
||||
readFile(new URL('../src/db.ts', import.meta.url), 'utf8'),
|
||||
readFile(new URL('../src/state-saver.worker.ts', import.meta.url), 'utf8'),
|
||||
]);
|
||||
|
||||
assert.equal((dbSource.match(/executeDBStoreRequest</g) ?? []).length, 8);
|
||||
assert.doesNotMatch(dbSource, /const transaction = db\.transaction\(/);
|
||||
assert.doesNotMatch(dbSource, /const store = transaction\.objectStore\(/);
|
||||
assert.match(workerSource, /executeDBStoreRequest<void>\(/);
|
||||
assert.doesNotMatch(workerSource, /const transaction = db\.transaction\(/);
|
||||
assert.doesNotMatch(workerSource, /const store = transaction\.objectStore\(/);
|
||||
assert.match(workerSource, /openingMessage: null/);
|
||||
assert.match(workerSource, /logStoreCreation: false/);
|
||||
});
|
||||
|
||||
@@ -18,7 +18,10 @@ test('canvas export uses the canonical blob variant union', () => {
|
||||
|
||||
test('image ID lookup uses the shared IndexedDB getAllKeys operation', () => {
|
||||
assert.match(sharedSource, /export type DBRequestOperation = 'get' \| 'put' \| 'delete' \| 'clear' \| 'getAllKeys';/);
|
||||
assert.match(sharedSource, /export async function executeDBStoreRequest<T>\(/);
|
||||
assert.match(sharedSource, /const database = await openLayerForgeDB\(log, stores, openOptions\)/);
|
||||
assert.match(sharedSource, /return createDBRequest\(store, operation, data, errorMessage, log\)/);
|
||||
assert.match(sharedSource, /case 'getAllKeys':[\s\S]*?request = store\.getAllKeys\(\);/);
|
||||
assert.match(dbSource, /createDBRequest\(store, 'getAllKeys', null, "Error getting all image IDs", log\)/);
|
||||
assert.match(dbSource, /executeDBStoreRequest<string\[\]>\([\s\S]*?'getAllKeys',[\s\S]*?"Error getting all image IDs"/);
|
||||
assert.doesNotMatch(dbSource, /const request = store\.getAllKeys\(\);/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user