release: v0.1.14 生产加固 — IDB事务原子性/多标签页感知/幂等init
This commit is contained in:
Vendored
+34
-9
@@ -31,7 +31,7 @@ class DatabaseError extends Error {
|
||||
// ---------------------------------------------------------------------------
|
||||
// 版本
|
||||
// ---------------------------------------------------------------------------
|
||||
const VERSION = '0.1.13';
|
||||
const VERSION = '0.1.14';
|
||||
|
||||
/**
|
||||
* metona-sqlark Shared WHERE Matcher — 统一的条件匹配逻辑
|
||||
@@ -204,7 +204,13 @@ class MemoryEngine {
|
||||
this.snapshot = null;
|
||||
}
|
||||
// ---- 生命周期 ----
|
||||
async open(_dbName, _version) { this.opened = true; }
|
||||
async open(_dbName, _version) {
|
||||
if (this.opened) {
|
||||
// 幂等:已打开则忽略
|
||||
return;
|
||||
}
|
||||
this.opened = true;
|
||||
}
|
||||
async close() {
|
||||
this.tables.clear();
|
||||
this.schemas.clear();
|
||||
@@ -553,13 +559,26 @@ class IndexedDBEngine {
|
||||
await this.memoryCache.open(dbName, version);
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(dbName, version);
|
||||
request.onsuccess = () => { this.db = request.result; resolve(); };
|
||||
request.onsuccess = () => {
|
||||
this.db = request.result;
|
||||
// 多标签页冲突处理:其他标签页升级版本时自动关闭当前连接
|
||||
this.db.onversionchange = () => {
|
||||
if (this.db) {
|
||||
this.db.close();
|
||||
this.db = null;
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`[metona-sqlark] Database "${dbName}" was upgraded in another tab. Connection closed. Please re-open.`);
|
||||
}
|
||||
};
|
||||
resolve();
|
||||
};
|
||||
request.onerror = () => reject(new DatabaseError(`Failed to open IndexedDB "${dbName}"`, 'IDB_OPEN_ERROR', request.error));
|
||||
request.onblocked = () => reject(new DatabaseError(`IndexedDB "${dbName}" is blocked`, 'IDB_BLOCKED'));
|
||||
});
|
||||
}
|
||||
async close() {
|
||||
if (this.db) {
|
||||
this.db.onversionchange = null; // 清理监听器
|
||||
this.db.close();
|
||||
this.db = null;
|
||||
}
|
||||
@@ -765,16 +784,22 @@ class IndexedDBEngine {
|
||||
tx.onerror = () => reject(new DatabaseError(`Clear failed for "${tableName}"`, 'IDB_TX_ERROR', tx.error));
|
||||
});
|
||||
}
|
||||
/** 将内存缓存中的所有表数据刷新到 IndexedDB */
|
||||
/** 将内存缓存中的所有表数据原子性刷新到 IndexedDB */
|
||||
async flushToIDB() {
|
||||
const tableNames = await this.memoryCache.getTableNames();
|
||||
const db = this.ensureDB();
|
||||
// 每个表在一个单独的 IDB 事务中完成 clear+insert,保证原子性
|
||||
for (const tableName of tableNames) {
|
||||
const rows = await this.memoryCache.find(tableName, { table: tableName });
|
||||
// Clear + bulk insert for simplicity
|
||||
await this.idbClear(tableName);
|
||||
if (rows.length > 0) {
|
||||
await this.idbInsert(tableName, rows);
|
||||
}
|
||||
await new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(tableName, 'readwrite');
|
||||
const store = tx.objectStore(tableName);
|
||||
store.clear(); // 清空
|
||||
for (const row of rows)
|
||||
store.add(row); // 批量写入
|
||||
tx.oncomplete = () => resolve();
|
||||
tx.onerror = () => reject(new DatabaseError(`Flush failed for "${tableName}"`, 'IDB_FLUSH_ERROR', tx.error));
|
||||
});
|
||||
}
|
||||
}
|
||||
ensureDB() {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -110,7 +110,7 @@ interface MetonaPlugin {
|
||||
/** 销毁 */
|
||||
destroy(): void;
|
||||
}
|
||||
declare const VERSION = "0.1.13";
|
||||
declare const VERSION = "0.1.14";
|
||||
|
||||
/**
|
||||
* metona-sqlark Plugin — 插件系统
|
||||
@@ -567,7 +567,7 @@ declare class IndexedDBEngine implements IStorageEngine {
|
||||
private idbUpdate;
|
||||
private idbDelete;
|
||||
private idbClear;
|
||||
/** 将内存缓存中的所有表数据刷新到 IndexedDB */
|
||||
/** 将内存缓存中的所有表数据原子性刷新到 IndexedDB */
|
||||
private flushToIDB;
|
||||
private ensureDB;
|
||||
}
|
||||
|
||||
Vendored
+34
-9
@@ -27,7 +27,7 @@ class DatabaseError extends Error {
|
||||
// ---------------------------------------------------------------------------
|
||||
// 版本
|
||||
// ---------------------------------------------------------------------------
|
||||
const VERSION = '0.1.13';
|
||||
const VERSION = '0.1.14';
|
||||
|
||||
/**
|
||||
* metona-sqlark Shared WHERE Matcher — 统一的条件匹配逻辑
|
||||
@@ -200,7 +200,13 @@ class MemoryEngine {
|
||||
this.snapshot = null;
|
||||
}
|
||||
// ---- 生命周期 ----
|
||||
async open(_dbName, _version) { this.opened = true; }
|
||||
async open(_dbName, _version) {
|
||||
if (this.opened) {
|
||||
// 幂等:已打开则忽略
|
||||
return;
|
||||
}
|
||||
this.opened = true;
|
||||
}
|
||||
async close() {
|
||||
this.tables.clear();
|
||||
this.schemas.clear();
|
||||
@@ -549,13 +555,26 @@ class IndexedDBEngine {
|
||||
await this.memoryCache.open(dbName, version);
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(dbName, version);
|
||||
request.onsuccess = () => { this.db = request.result; resolve(); };
|
||||
request.onsuccess = () => {
|
||||
this.db = request.result;
|
||||
// 多标签页冲突处理:其他标签页升级版本时自动关闭当前连接
|
||||
this.db.onversionchange = () => {
|
||||
if (this.db) {
|
||||
this.db.close();
|
||||
this.db = null;
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`[metona-sqlark] Database "${dbName}" was upgraded in another tab. Connection closed. Please re-open.`);
|
||||
}
|
||||
};
|
||||
resolve();
|
||||
};
|
||||
request.onerror = () => reject(new DatabaseError(`Failed to open IndexedDB "${dbName}"`, 'IDB_OPEN_ERROR', request.error));
|
||||
request.onblocked = () => reject(new DatabaseError(`IndexedDB "${dbName}" is blocked`, 'IDB_BLOCKED'));
|
||||
});
|
||||
}
|
||||
async close() {
|
||||
if (this.db) {
|
||||
this.db.onversionchange = null; // 清理监听器
|
||||
this.db.close();
|
||||
this.db = null;
|
||||
}
|
||||
@@ -761,16 +780,22 @@ class IndexedDBEngine {
|
||||
tx.onerror = () => reject(new DatabaseError(`Clear failed for "${tableName}"`, 'IDB_TX_ERROR', tx.error));
|
||||
});
|
||||
}
|
||||
/** 将内存缓存中的所有表数据刷新到 IndexedDB */
|
||||
/** 将内存缓存中的所有表数据原子性刷新到 IndexedDB */
|
||||
async flushToIDB() {
|
||||
const tableNames = await this.memoryCache.getTableNames();
|
||||
const db = this.ensureDB();
|
||||
// 每个表在一个单独的 IDB 事务中完成 clear+insert,保证原子性
|
||||
for (const tableName of tableNames) {
|
||||
const rows = await this.memoryCache.find(tableName, { table: tableName });
|
||||
// Clear + bulk insert for simplicity
|
||||
await this.idbClear(tableName);
|
||||
if (rows.length > 0) {
|
||||
await this.idbInsert(tableName, rows);
|
||||
}
|
||||
await new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(tableName, 'readwrite');
|
||||
const store = tx.objectStore(tableName);
|
||||
store.clear(); // 清空
|
||||
for (const row of rows)
|
||||
store.add(row); // 批量写入
|
||||
tx.oncomplete = () => resolve();
|
||||
tx.onerror = () => reject(new DatabaseError(`Flush failed for "${tableName}"`, 'IDB_FLUSH_ERROR', tx.error));
|
||||
});
|
||||
}
|
||||
}
|
||||
ensureDB() {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+34
-9
@@ -33,7 +33,7 @@
|
||||
// ---------------------------------------------------------------------------
|
||||
// 版本
|
||||
// ---------------------------------------------------------------------------
|
||||
const VERSION = '0.1.13';
|
||||
const VERSION = '0.1.14';
|
||||
|
||||
/**
|
||||
* metona-sqlark Shared WHERE Matcher — 统一的条件匹配逻辑
|
||||
@@ -206,7 +206,13 @@
|
||||
this.snapshot = null;
|
||||
}
|
||||
// ---- 生命周期 ----
|
||||
async open(_dbName, _version) { this.opened = true; }
|
||||
async open(_dbName, _version) {
|
||||
if (this.opened) {
|
||||
// 幂等:已打开则忽略
|
||||
return;
|
||||
}
|
||||
this.opened = true;
|
||||
}
|
||||
async close() {
|
||||
this.tables.clear();
|
||||
this.schemas.clear();
|
||||
@@ -555,13 +561,26 @@
|
||||
await this.memoryCache.open(dbName, version);
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(dbName, version);
|
||||
request.onsuccess = () => { this.db = request.result; resolve(); };
|
||||
request.onsuccess = () => {
|
||||
this.db = request.result;
|
||||
// 多标签页冲突处理:其他标签页升级版本时自动关闭当前连接
|
||||
this.db.onversionchange = () => {
|
||||
if (this.db) {
|
||||
this.db.close();
|
||||
this.db = null;
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`[metona-sqlark] Database "${dbName}" was upgraded in another tab. Connection closed. Please re-open.`);
|
||||
}
|
||||
};
|
||||
resolve();
|
||||
};
|
||||
request.onerror = () => reject(new DatabaseError(`Failed to open IndexedDB "${dbName}"`, 'IDB_OPEN_ERROR', request.error));
|
||||
request.onblocked = () => reject(new DatabaseError(`IndexedDB "${dbName}" is blocked`, 'IDB_BLOCKED'));
|
||||
});
|
||||
}
|
||||
async close() {
|
||||
if (this.db) {
|
||||
this.db.onversionchange = null; // 清理监听器
|
||||
this.db.close();
|
||||
this.db = null;
|
||||
}
|
||||
@@ -767,16 +786,22 @@
|
||||
tx.onerror = () => reject(new DatabaseError(`Clear failed for "${tableName}"`, 'IDB_TX_ERROR', tx.error));
|
||||
});
|
||||
}
|
||||
/** 将内存缓存中的所有表数据刷新到 IndexedDB */
|
||||
/** 将内存缓存中的所有表数据原子性刷新到 IndexedDB */
|
||||
async flushToIDB() {
|
||||
const tableNames = await this.memoryCache.getTableNames();
|
||||
const db = this.ensureDB();
|
||||
// 每个表在一个单独的 IDB 事务中完成 clear+insert,保证原子性
|
||||
for (const tableName of tableNames) {
|
||||
const rows = await this.memoryCache.find(tableName, { table: tableName });
|
||||
// Clear + bulk insert for simplicity
|
||||
await this.idbClear(tableName);
|
||||
if (rows.length > 0) {
|
||||
await this.idbInsert(tableName, rows);
|
||||
}
|
||||
await new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(tableName, 'readwrite');
|
||||
const store = tx.objectStore(tableName);
|
||||
store.clear(); // 清空
|
||||
for (const row of rows)
|
||||
store.add(row); // 批量写入
|
||||
tx.oncomplete = () => resolve();
|
||||
tx.onerror = () => reject(new DatabaseError(`Flush failed for "${tableName}"`, 'IDB_FLUSH_ERROR', tx.error));
|
||||
});
|
||||
}
|
||||
}
|
||||
ensureDB() {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user