Introduces cacheableModels.js as the source of truth for which models the client caches in IDB and how to dispatch their revalidate actions. DataManager derives modelsToSync from the registry and creates object stores idempotently, so adding new cached models needs only one place to change plus a one-time DATA_VERSION bump. Also fixes two latent bugs that the upcoming boot-hydration relies on: setCacheKeys now awaits all per-key writes (was async-forEach with no await), and replace awaits clear before push (avoids ConstraintError on rapid refetches).
86 lines
2.1 KiB
JavaScript
86 lines
2.1 KiB
JavaScript
import { openDB } from 'idb';
|
|
import { DATA_VERSION } from './version';
|
|
import { cacheableModels, cacheableModelNames } from './cacheableModels';
|
|
|
|
export class DataManager {
|
|
constructor(accountId) {
|
|
this.modelsToSync = cacheableModelNames;
|
|
this.accountId = accountId;
|
|
this.db = null;
|
|
}
|
|
|
|
async initDb() {
|
|
if (this.db) return this.db;
|
|
const dbName = `cw-store-${this.accountId}`;
|
|
this.db = await openDB(dbName, DATA_VERSION, {
|
|
upgrade(db) {
|
|
if (!db.objectStoreNames.contains('cache-keys')) {
|
|
db.createObjectStore('cache-keys');
|
|
}
|
|
cacheableModels.forEach(model => {
|
|
if (!db.objectStoreNames.contains(model.name)) {
|
|
db.createObjectStore(model.name, { keyPath: 'id' });
|
|
}
|
|
});
|
|
},
|
|
});
|
|
|
|
// Store the database name in LocalStorage
|
|
const dbNames = JSON.parse(localStorage.getItem('cw-idb-names') || '[]');
|
|
if (!dbNames.includes(dbName)) {
|
|
dbNames.push(dbName);
|
|
localStorage.setItem('cw-idb-names', JSON.stringify(dbNames));
|
|
}
|
|
|
|
return this.db;
|
|
}
|
|
|
|
validateModel(name) {
|
|
if (!name) throw new Error('Model name is not defined');
|
|
if (!this.modelsToSync.includes(name)) {
|
|
throw new Error(`Model ${name} is not defined`);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
async replace({ modelName, data }) {
|
|
this.validateModel(modelName);
|
|
|
|
await this.db.clear(modelName);
|
|
return this.push({ modelName, data });
|
|
}
|
|
|
|
async push({ modelName, data }) {
|
|
this.validateModel(modelName);
|
|
|
|
if (Array.isArray(data)) {
|
|
const tx = this.db.transaction(modelName, 'readwrite');
|
|
data.forEach(item => {
|
|
tx.store.add(item);
|
|
});
|
|
await tx.done;
|
|
} else {
|
|
await this.db.add(modelName, data);
|
|
}
|
|
}
|
|
|
|
async get({ modelName }) {
|
|
this.validateModel(modelName);
|
|
return this.db.getAll(modelName);
|
|
}
|
|
|
|
async setCacheKeys(cacheKeys) {
|
|
await Promise.all(
|
|
Object.entries(cacheKeys).map(([modelName, value]) =>
|
|
this.db.put('cache-keys', value, modelName)
|
|
)
|
|
);
|
|
}
|
|
|
|
async getCacheKey(modelName) {
|
|
this.validateModel(modelName);
|
|
|
|
return this.db.get('cache-keys', modelName);
|
|
}
|
|
}
|