44 lines
2.0 KiB
JavaScript
44 lines
2.0 KiB
JavaScript
const DB_NAME = 'dww11-account-switch-v3';
|
||
let opening;
|
||
export function openDB() {
|
||
if (!opening) opening = new Promise((resolve, reject) => {
|
||
const req = indexedDB.open(DB_NAME, 1);
|
||
req.onupgradeneeded = () => {
|
||
req.result.createObjectStore('accounts', { keyPath: 'username' });
|
||
req.result.createObjectStore('meta');
|
||
};
|
||
req.onsuccess = () => {
|
||
const db = req.result;
|
||
db.onversionchange = () => { db.close(); opening = null; };
|
||
db.onclose = () => { opening = null; };
|
||
resolve(db);
|
||
};
|
||
req.onerror = () => { opening = null; reject(new Error('无法打开 IndexedDB:' + req.error?.message)); };
|
||
req.onblocked = () => { opening = null; reject(new Error('数据库升级被阻塞,请关闭扩展管理页后重试。')); };
|
||
});
|
||
return opening;
|
||
}
|
||
|
||
// 所有状态变更跨 accounts/meta 使用同一事务。回调必须同步,避免 await 使事务自动提交。
|
||
export async function transact(reducer, write = true) {
|
||
const db = await openDB();
|
||
return new Promise((resolve, reject) => {
|
||
const tx = db.transaction(['accounts', 'meta'], write ? 'readwrite' : 'readonly', write ? { durability: 'strict' } : undefined);
|
||
const accountsStore = tx.objectStore('accounts'), metaStore = tx.objectStore('meta');
|
||
const a = accountsStore.getAll(), m = metaStore.get('state');
|
||
let output, failure, ready = 0;
|
||
const loaded = () => {
|
||
if (++ready !== 2) return;
|
||
try {
|
||
const state = m.result || { schedule: null, flow: null };
|
||
output = reducer(a.result, state, accountsStore);
|
||
if (write) metaStore.put(state, 'state');
|
||
} catch (e) { failure = e; tx.abort(); }
|
||
};
|
||
a.onsuccess = loaded; m.onsuccess = loaded;
|
||
tx.oncomplete = () => resolve(output);
|
||
tx.onabort = () => reject(failure || new Error('数据库事务未提交:' + (tx.error?.message || '已中止')));
|
||
tx.onerror = () => {}; // 由 onabort 统一处理,不在提交之前返回成功。
|
||
});
|
||
}
|