fix: v0.7.1 API 修复与防御统一 — static create / 未 open 防护 / 原型污染 / lint 清零
CI / test (22.x) (push) Successful in 17m24s
CI / e2e (push) Successful in 10m39s
CI / test (18.x) (push) Successful in 19m5s
CI / test (20.x) (push) Successful in 18m4s
CI / test (24.x) (push) Successful in 23m19s

- MetonaSqlark.create 静态工厂(README 示例在 ESM/Node 下此前 TypeError),
  独立 create 函数委托静态实现
- close 未初始化防御(engine undefined 不再崩溃)
- AriaEngine hasTable/getTableNames/getTableSchema 统一 ensureOpen
- 事务回滚失败不掩盖原始错误
- __proto__ 列名防护:Executor 列映射 Object.create(null) + schema 校验拒绝
- lint 清零(移除 5 处未使用导入)

测试 1147 → 1155(73 套件);行覆盖率 89.8%;版本 0.7.1
This commit is contained in:
thzxx
2026-08-13 11:14:42 +08:00
parent 57415975ea
commit cbe407eb49
25 changed files with 1620 additions and 1375 deletions
+35 -8
View File
@@ -30,7 +30,7 @@ class DatabaseError extends Error {
// ---------------------------------------------------------------------------
// 版本
// ---------------------------------------------------------------------------
const VERSION = '0.7.0';
const VERSION = '0.7.1';
/**
* metona-sqlark Shared WHERE Matcher 统一的条件匹配逻辑
@@ -2452,6 +2452,11 @@ function validateColumns(columns) {
}
let primaryKeyCount = 0;
for (const [colName, colDef] of Object.entries(columns)) {
// v0.7.1: '__proto__' 作为列名会触发对象原型 setter(列静默丢失);
// 显式拒绝避免原型污染类攻击面
if (colName === '__proto__') {
throw new DatabaseError('Column name "__proto__" is not allowed', 'SCHEMA_ERROR');
}
// 类型校验
if (!FIELD_TYPES.includes(colDef.type)) {
throw new DatabaseError(`Invalid type "${colDef.type}" for column "${colName}". Valid types: ${FIELD_TYPES.join(', ')}`, 'SCHEMA_ERROR');
@@ -6695,12 +6700,16 @@ class AriaEngine {
}
}
async hasTable(tableName) {
// v0.7.1: 未 open 防护统一(此前与 KVStoreEngine 不一致:返回空而非报错)
this.ensureOpen();
return this.schemas.has(tableName);
}
async getTableNames() {
this.ensureOpen();
return Array.from(this.schemas.keys());
}
async getTableSchema(tableName) {
this.ensureOpen();
return this.schemas.get(tableName) ?? null;
}
// =======================================================================
@@ -10957,7 +10966,9 @@ class QueryExecutor {
if (exists)
return;
}
const columns = {};
// v0.7.1: Object.create(null) —— 防止 '__proto__' 列名触发原型 setter 静默丢列
// createSchema 校验会显式拒绝该列名)
const columns = Object.create(null);
for (const col of stmt.columns)
columns[col.name] = astColumnToColumnDef(col);
return this.engine.createTable(createSchema(stmt.name, columns));
@@ -11735,8 +11746,12 @@ class TransactionManager {
return result;
}
catch (error) {
// 失败 → 回滚
await this.engine.rollbackTransaction();
// 失败 → 回滚。v0.7.1: 回滚自身失败不掩盖原始事务错误
// (此前 rollback 抛错会替换掉真正导致失败的异常,定位困难)
try {
await this.engine.rollbackTransaction();
}
catch { /* 回滚失败保留原始错误 */ }
if (error instanceof DatabaseError)
throw error;
throw new DatabaseError(`Transaction failed: ${error.message}`, 'TRANSACTION_ERROR', error);
@@ -11834,6 +11849,16 @@ class PluginManager {
// MetonaSqlark
// ---------------------------------------------------------------------------
class MetonaSqlark {
/**
* v0.7.1: 静态工厂 connect/disconnect 同一入口风格
* 此前 create 仅存在于 api 对象 / window 挂载 README/站点示例的
* `MetonaSqlark.create({...})` ESM/Node 下是 undefinedTypeError
*/
static async create(config) {
const db = new MetonaSqlark(config);
await db.init();
return db;
}
/** 获取版本号 */
get version() { return this._version; }
/** 查询结果行数上限 */
@@ -12281,7 +12306,10 @@ class MetonaSqlark {
this.channel = null;
}
this.pluginManager.destroy();
await this.engine.close();
// v0.7.1: init 失败/未调用时 close 不应崩溃(此前 this.engine undefined → TypeError
if (this.engine) {
await this.engine.close();
}
this.tableCache.clear();
this.ready = false;
}
@@ -12475,9 +12503,8 @@ M.getActiveConnections = () => manager.getActiveConnections();
* ```
*/
async function create(config) {
const db = new MetonaSqlark(config);
await db.init();
return db;
// v0.7.1: 委托静态 create(类与独立函数行为一致,README 两种写法均可)
return MetonaSqlark.create(config);
}
// ---------------------------------------------------------------------------
// 全局 API