📦 安装

MetonaSqlark 支持多种引入方式,覆盖 npm、CDN、ESM、CJS 所有常见场景。

npm 安装(推荐)

# 配置 Gitea Registry(一次性)
npm config set @metona-team:registry https://git.metona.cn/api/packages/MetonaTeam/npm/

# 安装
npm install @metona-team/metona-sqlark

CDN / UMD

<!-- UMD 格式,暴露 window.MetonaSqlark 和 window.MeSqlark -->
<script src="https://git.metona.cn/MetonaTeam/MetonaSqlark/raw/branch/master/dist/metona-sqlark.min.js"></script>
<script>
  const db = await window.MetonaSqlark.create({...});
  // 或 window.MeSqlark.create(...)  — 完全等价
</script>

ESM

import { MetonaSqlark, MeSqlark } from '@metona-team/metona-sqlark';
// MeSqlark 是 MetonaSqlark 的别名,行为完全一致

CJS

const { MetonaSqlark } = require('@metona-team/metona-sqlark');

输出文件说明:

文件格式用途
metona-sqlark.jsUMD浏览器开发版(含 sourcemap)
metona-sqlark.min.jsUMD (minified)生产环境(~105KB / ~27KB gzip)
metona-sqlark.esm.jsES Module现代打包工具 / 浏览器 ESM
metona-sqlark.cjsCommonJSNode.js require()
metona-sqlark.d.tsTypeScript 声明类型提示

🏗 创建数据库

MetonaSqlark.create(config) — 工厂函数,自动创建并初始化数据库实例。

const db = await MetonaSqlark.create({
  name: 'my-app',
  mode: 'hybrid',       // 'memory' | 'disk' | 'hybrid' | 'aria' 🆕
  diskEngine: 'opfs', // 'opfs' | 'memory'(v0.6.0: IndexedDB 已移除)
  version: 1,
  plugins: [],            // MetonaPlugin[]
  onReady: (db) => {},    // 就绪回调
  onError: (err) => {},   // 错误回调
});

// 也可手动实例化
const db2 = new MetonaSqlark(config);
await db2.init();

// 检查状态
db.isReady();  // true

// 关闭
await db.close();

📋 定义表

使用 db.defineTable(name, columns) 定义表结构。

await db.defineTable('users', {
  id:       { type: 'string',  primaryKey: true },
  name:     { type: 'string',  required: true },
  email:    { type: 'string',  unique: true, index: true },
  age:      { type: 'number',  default: 0, min: 0, max: 150 },
  active:   { type: 'boolean', default: true },
  birthday: { type: 'date' },
  meta:     { type: 'json' },
  dept_id:  { type: 'number',  references: 'departments.id' },
});

// 表操作
await db.getTableNames();         // ['users']
await db.dropTable('users');     // 删除表
ColumnDef 属性类型说明
type'string'|'number'|'boolean'|'date'|'json'数据类型
primaryKeyboolean主键(每表至少一个)
requiredboolean是否必填
uniqueboolean唯一约束(自动建索引)
indexboolean创建哈希索引,O(1) 加速查询
defaultunknown默认值
maxLengthnumber字符串最大长度
min/maxnumber数值范围
referencesstring外键引用 'table.column'
onDelete'CASCADE'\|'SET NULL'\|'RESTRICT'删除级联 ✅ v0.4.1
onUpdate'CASCADE'\|'SET NULL'\|'RESTRICT'更新级联(更新主键时触发)✅ v0.4.2

🔍 SQL 查询

db.query(sql) — 执行标准 SQL 字符串,返回查询结果。

db.queryStream(sql, onRow) — 流式查询(v0.4.0):逐行回调不物化结果集,大表友好。支持简单 SELECT(WHERE/LIMIT/OFFSET/列投影);JOIN/GROUP BY/UNION/聚合/ORDER BY 自动回退物化。

// 流式查询 — 大表逐行处理
let count = 0;
await db.queryStream("SELECT * FROM logs WHERE level = 'error'", (row) => {
  count++;
  processRow(row);
});

// 派生表 / COUNT(DISTINCT) / NULLS 排序(v0.4.0)
const top = await db.query(`SELECT dept, total FROM
  (SELECT dept, SUM(salary) AS total FROM emp GROUP BY dept) AS t
  WHERE total > 100 ORDER BY total DESC`);
await db.query("SELECT COUNT(DISTINCT city) AS n FROM users");
await db.query("SELECT name FROM users ORDER BY age ASC NULLS FIRST");

完整 SQL 语法支持

// SELECT — 核心查询
const rows = await db.query(`SELECT * FROM users
  WHERE age > 18
  ORDER BY name ASC
  LIMIT 10 OFFSET 0`);

// INSERT — 插入数据
await db.query("INSERT INTO users (id, name, age) VALUES ('1', 'Alice', 30)");
await db.query("INSERT INTO users VALUES ('2', 'Bob', 25)");
// 支持多行插入
await db.query("INSERT INTO users VALUES ('3', 'C'), ('4', 'D')");

// UPDATE — 更新数据
await db.query("UPDATE users SET age = 31, active = true WHERE id = '1'");

// DELETE — 删除数据
await db.query("DELETE FROM users WHERE id = '1'");

// DDL — 表结构操作
await db.query(`CREATE TABLE products (
  id STRING PRIMARY KEY,
  name STRING NOT NULL,
  price NUMBER DEFAULT 0
)`);
await db.query('DROP TABLE products');

-- ALTER TABLE — 动态修改表结构 (v0.2.5)
await db.query('ALTER TABLE users ADD COLUMN phone STRING');
await db.query('ALTER TABLE users DROP COLUMN phone');

-- TRUNCATE TABLE — 快速清空表数据 (v0.2.5)
await db.query('TRUNCATE TABLE old_logs');

SQL 扩展 (v0.3.0+)

// 多语句 — 分号分隔一次执行(返回最后一条结果)
await db.query(`CREATE TABLE t (id STRING PRIMARY KEY);
  INSERT INTO t VALUES ('1'); INSERT INTO t VALUES ('2')`);

// 事务语句 — BEGIN / COMMIT / ROLLBACK
await db.query('BEGIN');
await db.query("INSERT INTO t VALUES ('3')");
await db.query('ROLLBACK'); // 回滚

// INSERT INTO ... SELECT — 查询结果写入
await db.query('INSERT INTO t SELECT id FROM t2 WHERE x > 1');

// UNION / UNION ALL — 合并查询
const merged = await db.query(
  `SELECT name FROM users WHERE city = 'Beijing'
   UNION SELECT name FROM users WHERE age < 30`);

// EXISTS / NOT EXISTS — 关联子查询
const hasOrders = await db.query(`SELECT * FROM users u
  WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id)`);

// CASE WHEN — SELECT 列 / WHERE / 聚合 (v0.3.1 / v0.3.2)
const labeled = await db.query(`SELECT name,
  CASE WHEN age >= 18 THEN 'adult' ELSE 'minor' END AS status FROM users`);
const adults = await db.query(`SELECT SUM(CASE WHEN age >= 18 THEN 1 ELSE 0 END) FROM users`);

// CREATE / DROP INDEX — 动态二级索引
await db.query('CREATE INDEX idx_users_city ON users (city)');
await db.query('DROP INDEX idx_users_city ON users (city)');

条件表达式

// 比较运算符
`WHERE age > 18 AND name LIKE 'A%'`
`WHERE salary >= 5000 OR dept = 'Engineering'`

// IN / NOT IN
`WHERE dept IN ('Engineering', 'Sales')`

// NULL 检查
`WHERE email IS NULL`
`WHERE email IS NOT NULL`

// NOT 取反
`WHERE NOT (age < 18 OR age > 65)`

// 嵌套条件
`WHERE (age > 18 AND active = true) OR role = 'admin'`

⛓ Query Builder

链式 API,TypeScript 友好,享受 IDE 自动补全。

SELECT

const users = db.table('users');

// 全量查询
await users.select().execute();

// 指定列 + 条件 + 排序 + 分页
const result = await users
  .select(['name', 'age', 'email'])
  .where({
    age: { $gt: 18 },
    name: { $like: 'A%' },
  })
  .orderBy('age', 'desc')
  .limit(10)
  .offset(0)
  .execute();

INSERT

// 单行插入 — 返回主键值
const pk = await users.insert({ id: '1', name: 'Alice', age: 30 });

// 批量插入 — 返回主键值数组
const pks = await users.insertMany([
  { id: '2', name: 'Bob', age: 25 },
  { id: '3', name: 'Charlie', age: 35 },
]);

UPDATE / DELETE

// 更新 — 返回影响行数
const updated = await users
  .update({ age: 31, active: false })
  .where({ id: '1' })
  .execute();  // 1

// 删除 — 返回影响行数
const deleted = await users
  .delete()
  .where({ id: '1' })
  .execute();  // 1

// 全表删除
await users.delete().execute();

🔗 JOIN 查询

支持 INNER / LEFT / RIGHT / CROSS JOIN,SQL 和 Query Builder 两种方式。

// SQL 方式 — 支持表别名
await db.query(`SELECT u.name, d.name AS dept_name
  FROM users u
  INNER JOIN departments d ON u.dept_id = d.id
  LEFT JOIN orders o ON u.id = o.user_id
  WHERE d.name = 'Engineering'
  ORDER BY u.name`);

// Query Builder 方式
await db.table('users').select()
  .as('u')
  .innerJoin('departments', { 'u.dept_id': { $col: 'd.id' } }, 'd')
  .leftJoin('orders', { 'u.id': { $col: 'o.user_id' } }, 'o')
  .execute();

// CROSS JOIN — 笛卡尔积
.crossJoin('metadata', 'm')

📊 GROUP BY & 聚合

五大聚合函数 + GROUP BY + HAVING + DISTINCT,完整的数据分析能力。

// GROUP BY + 聚合
await db.query(`SELECT dept, COUNT(*) as cnt, SUM(salary) as total,
       AVG(salary) as avg_sal, MIN(age), MAX(age)
  FROM employees
  GROUP BY dept
  HAVING COUNT(*) > 1
  ORDER BY total DESC
  LIMIT 5`);

// DISTINCT 去重
await db.query('SELECT DISTINCT dept FROM employees');

// 聚合函数别名
await db.query('SELECT COUNT(*) AS total_users, AVG(age) AS avg_age FROM users');

🎯 WHERE 操作符

Query Builder 使用 $ 前缀操作符,支持逻辑组合。

操作符含义示例
直接值 / $eq等于{ age: 30 } / { age: { $eq: 30 } }
$ne不等于{ age: { $ne: 30 } }
$gt / $gte大于 / 大于等于{ age: { $gt: 18 } }
$lt / $lte小于 / 小于等于{ age: { $lt: 65 } }
$in / $nin在列表中 / 不在{ dept: { $in: ['IT','HR'] } }
$like模糊匹配{ name: { $like: 'A%' } }
$and逻辑与{ $and: [{...}, {...}] }
$or逻辑或{ $or: [{...}, {...}] }
$not逻辑非{ age: { $not: { $gt: 18 } } }
$col列引用(JOIN ON){ 'a.id': { $col: 'b.a_id' } }
// 复合条件
.where({
  $or: [
    { age: { $lt: 18 } },
    { age: { $gt: 65 } },
  ],
  active: true,
  name: { $like: 'A%', $ne: 'Admin' },
})

🔒 事务 & 回滚

v0.1.13 起支持真正的自动回滚:事务内任何一步失败,所有变更自动撤销。

await db.transaction(async (trx) => {
  // trx.table() 获取事务内表操作对象
  await trx.table('users').insert({ id: '3', name: 'Charlie' });
  await trx.table('orders').insert({ id: 'o1', userId: '3', amount: 99 });
  // ✅ 全部成功 → 自动 commit
  // ❌ 任何一步失败 → 自动 rollback,数据恢复原状
  // Memory 引擎:快照回滚 | KVStore 引擎:原子日志 flush
});

🔍 子查询

v0.1.13 新增子查询支持,可在 WHERE 条件中嵌套 SELECT。

// IN 子查询 — 查询有高额订单的用户
await db.query(`SELECT * FROM users
  WHERE id IN (SELECT user_id FROM orders WHERE amount > 100)`);

// 标量子查询 — 查询年龄等于平均年龄的用户
await db.query(`SELECT * FROM users
  WHERE age = (SELECT AVG(age) FROM users)`);

// NOT IN 子查询
await db.query(`SELECT * FROM users
  WHERE id NOT IN (SELECT user_id FROM orders)`);

🏗 ALTER TABLE (🆕 v0.2.5)

v0.2.5 新增 ALTER TABLE 语法,支持动态添加和删除列。

ADD COLUMN

-- 添加新列
await db.query('ALTER TABLE users ADD COLUMN phone STRING');

-- 带约束的添加
await db.query('ALTER TABLE users ADD COLUMN email STRING UNIQUE');

-- 带可选 COLUMN 关键字
await db.query('ALTER TABLE users ADD COLUMN age NUMBER DEFAULT 0');

DROP COLUMN

-- 删除列
await db.query('ALTER TABLE users DROP COLUMN phone');

-- 带可选 COLUMN 关键字
await db.query('ALTER TABLE users DROP COLUMN email');
语法说明
ALTER TABLE name ADD COLUMN col type [constraints]添加列(COLUMN 可选)
ALTER TABLE name DROP COLUMN col删除列(COLUMN 可选)

🗑 TRUNCATE TABLE (🆕 v0.2.5)

v0.2.5 新增 TRUNCATE TABLE 语法,快速清空表数据(保留表结构)。

-- 快速清空表数据
await db.query('TRUNCATE TABLE old_logs');

-- 等价于 DELETE FROM old_logs,但语义更清晰
语法说明
TRUNCATE TABLE name清空表数据,保留表结构

🔗 外键级联

v0.1.13 支持外键级联操作,定义表时可指定 ON DELETE / ON UPDATE 行为。

// 定义时指定外键 + 级联策略
await db.defineTable('orders', {
  id: { type: 'string', primaryKey: true },
  user_id: {
    type: 'string',
    references: 'users.id',
    onDelete: 'CASCADE',  // 删除用户时级联删除订单
    onUpdate: 'RESTRICT', // 禁止更新被引用的用户 ID
  },
  amount: { type: 'number' },
});

// SQL DDL 同样支持
await db.query(`CREATE TABLE orders (
  id STRING PRIMARY KEY,
  user_id STRING REFERENCES users(id) ON DELETE CASCADE ON UPDATE RESTRICT,
  amount NUMBER
)`);

// 删除用户 → 其所有订单自动删除
await db.query("DELETE FROM users WHERE id = '1'");
级联选项行为
CASCADE级联删除/更新子表中的匹配行
SET NULL将子表中的外键列设为 NULL
RESTRICT禁止操作(默认行为)

🏊 连接池

v0.1.13 新增连接池管理器,避免重复创建同名数据库实例。

// connect() — 获取或创建实例(单例复用)
const db1 = await MetonaSqlark.connect({ name: 'my-app', mode: 'hybrid' });
const db2 = await MetonaSqlark.connect({ name: 'my-app' });
// db1 === db2 — 复用已有实例,避免重复打开底层存储

// disconnect() — 释放连接(引用计数 -1)
await db2.disconnect(); // 引用计数: 2 → 1
await db1.disconnect(); // 引用计数: 1 → 0,自动 close()

// disconnectAll() — 强制关闭所有连接
await MetonaSqlark.disconnectAll();

// getActiveConnections() — 查看活跃连接
MetonaSqlark.getActiveConnections(); // ['my-app']

🔄 数据迁移

按版本号管理表结构变更。

// 注册迁移
db.addMigration(2, async (db) => {
  await db.defineTable('products', {
    id: { type: 'string', primaryKey: true },
    name: { type: 'string', required: true },
  });
});

db.addMigration(3, async (db) => {
  // 添加新列、数据迁移等
  await db.query("UPDATE users SET role = 'user' WHERE role IS NULL");
});

// 执行迁移到目标版本
await db.migrateTo(3);  // 依次执行 v2, v3 的迁移函数

v0.4.2: 迁移版本持久化到库内 — 重启后从持久化版本继续执行,已执行迁移不重跑(此前 version 每次从 config 重置,可能重跑不幂等的迁移)。

🛡 崩溃恢复自愈(v0.4.2)

异常退出(强杀/断电)后无需删库重建:打开数据库时自动跳过残缺 SSTable,应用层可调用自愈 API 恢复一致性。

// 自愈 — 校验并清理损坏 SSTable / 重建二级索引 / 截断 WAL
await db.repair();

// 清空全部数据与表结构(保留库本身,实例可继续使用)
await db.clearAll();

// 引擎级元数据(迁移版本等)
// 引擎接口 IStorageEngine 可选扩展:repair() / clearAll() / getMeta() / setMeta()

📤 导入导出

// 导出单表 — 返回 JSON 数组
const userData = await db.exportTable('users');
// [{ id: '1', name: 'Alice', ... }, ...]

// 导出全库 — 返回 { tableName: rows[] }
const allData = await db.exportAll();
// { users: [...], orders: [...], products: [...] }

// 导入数据 — 返回主键列表
const pks = await db.importTable('users', userData);

🧩 插件 & 钩子

14 种生命周期钩子,支持插件机制。

钩子名称触发时机参数
beforeCreateTable创建表前schema
afterCreateTable创建表后schema
beforeDropTable删除表前tableName
afterDropTable删除表后tableName
beforeInsert插入前rows[]
afterInsert插入后rows[], pks
beforeUpdate更新前query, updates
afterUpdate更新后query, updates, count
beforeDelete删除前query
afterDelete删除后query, count
beforeQuerySQL 查询前sql
afterQuerySQL 查询后sql, result
beforeTransaction事务开始前-
afterTransaction事务完成后-
// 注册钩子
db.on('beforeInsert', async (row) => {
  console.log('即将插入:', row);
  // 可在此校验、转换数据
});

db.on('afterQuery', async (sql, result) => {
  console.log(`查询完成 [${sql}] → ${(result as any[]).length} 行`);
});

// 注册自定义插件
const loggerPlugin = {
  name: 'logger',
  version: '1.0.0',
  description: '记录所有数据库操作',
  priority: 100,
  install(db) {
    db.on('beforeQuery', (sql) => console.log('SQL:', sql));
  },
  destroy() { /* 清理 */ },
};

// 在 create 配置中注册
const db = await MetonaSqlark.create({
  name: 'my-app',
  plugins: [loggerPlugin],
});

📡 发布订阅

// 订阅表变更
const unsubscribe = db.subscribe('users', (event) => {
  // event.type: 'insert' | 'update' | 'delete'
  // event.row:  被操作的行数据
  console.log(`users 表 ${event.type}`, event.row);
});

// 手动触发变更
db.emit('users', { type: 'insert', row: { id: '1', name: 'Alice' } });

// 取消订阅
unsubscribe();

多标签页同步 (v0.3.2)

// 启用 multiTabSync 后,其他标签页的写操作会广播到此标签页
const db = await MetonaSqlark.create({
  name: 'my-app',
  mode: 'hybrid',
  multiTabSync: true,
});

// 订阅其他标签页的变更(event.type === 'external')
db.subscribe('users', (event) => {
  if (event.type === 'external') {
    // Hybrid 模式已自动从磁盘重载,此处可刷新 UI
    refreshList();
  }
});

// 手动广播(Table API 已自动广播;自定义写入可调用)
db.broadcastChange('users');

⚛️ React 集成

import { useQuery, useTable, useDatabase } from '@metona-team/metona-sqlark/react';
import { db } from './db';

function UserList() {
  // 执行 SQL 查询,自动响应 db 变化
  const { data, loading, error, refresh } = useQuery(
    db,
    'SELECT * FROM users WHERE age > 18',
    [/* deps */]
  );

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      {data.map(u => <div key={u.id}>{u.name} ({u.age})</div>)}
      <button onClick={refresh}>刷新</button>
    </div>
  );
}

// 便捷 hook — 查询整张表
const { data, loading, refresh } = useTable(db, 'users');

// 管理数据库生命周期
function App() {
  const { db, ready, error } = useDatabase({
    name: 'my-app',
    mode: 'hybrid',
  });
  if (!ready) return <div>Initializing...</div>;
  return <UserList />;
}

🟢 Vue 集成

import { useSqlarkQuery, useSqlarkTable, useSqlarkDatabase }
  from '@metona-team/metona-sqlark/vue';
import { db } from './db';

// useSqlarkQuery — 执行 SQL 查询
const { data, loading, error, refresh } = useSqlarkQuery(
  db,
  'SELECT * FROM users WHERE age > 18'
);

// useSqlarkTable — 获取全表数据
const { data, loading, refresh } = useSqlarkTable(db, 'users');

// useSqlarkDatabase — 管理数据库生命周期
const { db, ready, error } = useSqlarkDatabase({
  name: 'my-app',
  mode: 'hybrid',
});

🔷 TypeScript 泛型

interface User {
  id: string;
  name: string;
  age: number;
  email?: string;
}

// 泛型表操作 — 类型安全的 insert/select
const users = db.table<User>('users');

// ✅ 类型检查通过
await users.insert({ id: '1', name: 'Alice', age: 30 });

// ❌ TypeScript 报错:缺少 name
// await users.insert({ id: '2', age: 25 });

⚙️ 完整配置项

属性类型默认值说明
namestring'metona-sqlark'数据库名称(必填)
mode'memory'|'disk'|'hybrid'|'aria''hybrid'存储模式 🆕 aria
diskEngine'opfs'|'memory''opfs'磁盘引擎类型(v0.6.0: IndexedDB 已移除)
versionnumber1数据库版本号
pluginsMetonaPlugin[][]初始插件列表
onReady(db) => void-初始化完成回调
onError(err) => void-错误回调(v0.2.5 接入执行路径)
maxRowsPerQuerynumber0查询结果行数上限(0=不限制)✅ v0.2.5 生效
multiTabSyncbooleanfalse多标签页同步:BroadcastChannel 广播表变更,其他标签页自动刷新 🆕 v0.3.2
ariaAriaEngineConfig-AriaEngine 专属配置透传:walSyncMode / checkpointInterval / encryption / pageStorage / compression 等 🆕 v0.5.0

💾 存储引擎

引擎模式持久化索引事务适用场景
MemoryEnginememory哈希快照回滚临时数据、缓存、测试
KVStoreEngine 🆕disk✅ KVStore(OPFS)哈希原子日志 flush标准持久化(v0.6.0 替代 IndexedDB)
HybridEnginehybrid✅ Write-Through哈希双引擎代理生产推荐,读写均走内存
AriaEnginearia✅ WAL + SSTableLSM-TreeMVCC 快照隔离自研引擎:大表、高并发、需崩溃恢复

v0.6.0: IndexedDB 已完全移除 — disk 模式改用自研 KVStore 引擎(多 key 原子写 + 快照/日志崩溃恢复),旧库可经 migrateFromIndexedDB() 一键迁移。

🌲 AriaEngine 自研存储引擎

v0.2.0 新增 — AriaEngine 是专为 MetonaSqlark 设计的页面式存储引擎,对标 SQLite 设计理念。
v0.2.4 生产级 — 二级索引 · MVCC · BloomFilter · WAL CRC全同步 · AES-GCM加密 · Savepoint · EXPLAIN · ANALYZE · REINDEX · VACUUM · BufferPool · 零死代码。
v0.3.2 表达式与并发 — WAL full模式真正同步 · MVCC接入读写路径 · SSTableReader二分查找统一 · crypto实例化 · IndexedDB索引利用 · compactLevel public接口 · WAL大小阈值自动checkpoint · SQL注入防护 · ALTER TABLE · TRUNCATE TABLE · 多标签页同步 · IDB schema持久化。
v0.4.2 生产就绪与崩溃自愈 — 残缺 SSTable 打开自动跳过(不删库)· WAL 记录与计数原子写入 + 按 key 扫描恢复 · 事务进行中 checkpoint 不截断 WAL · 二级索引跨重启自动恢复 · ALTER TABLE / 事务内 DDL 全引擎持久化 · ON UPDATE 外键级联(含更新主键)· OPFS schema 持久化(空表/索引完整保留)· `repair()` / `clearAll()` 统一自愈接口 · 迁移版本持久化到库内。
v0.4.3 关闭时序与后台任务加固 — 后台 flush/compaction 不再使用 setTimeout 延迟(close 排空全部任务后才关闭存储,杜绝"backend 关闭后写存储/重开污染")· 后台失败在 `flush()`/`close()` 显式报告(`ARIA_BACKGROUND_ERROR`,不静默吞错)· 预加载等待链稳定(修复 compaction 竞态跳块丢数据)· 事务提交先落 WAL 再合并快照(崩溃一致)· OPFS 写操作串行队列 + close 等待。
v0.4.4 SSTable 编码修复 — 大段中文内容(如 300KB 笔记)写入 AriaEngine 不再崩溃:块大小估算改 UTF-8 字节精确计算(修复中文 3 字节 vs 1 码元导致的缓冲区低估越界)· 长度字段 u16 → u32(修复 >64KB value 截断)· 大 value 独立成块 · 格式 v2("SSTC")与 v1("SSTB")双格式兼容(旧库数据不丢)· 中文主键 / 大内容索引列同步支持。
v0.5.0 存储后端生产级硬化 — 真实 CRC-32 完整性校验(SSTable 整文件 + WAL 记录,旧文件兼容)· 全库 AES-256-GCM 透明加密(EncryptedBackend + PBKDF2 密钥派生 + 密码验证)· WAL 分片文件重构(真追加 + 空洞检测 + 旧格式迁移)· SSTable 4KB 页面化物理存储(BufferPool/FileManager 真实接入,meta 存 pageIds 兼容旧数据)· OPFS 后端 v2(append 真追加 / 写队列健壮性 / 残留清理)· Web Locks 多标签页独占锁(ARIA_LOCKED)· LZ4 v2 原始大小头 · Playwright 真实 Chromium e2e(7 用例)· DatabaseConfig.aria 配置透传。
v0.5.1 深度审查修复 — 14 个生命周期钩子全部真实接线(此前 6 个 CRUD 钩子从未触发)· EXPLAIN / ANALYZE / REINDEX / VACUUM / SAVEPOINT SQL 入口补齐(此前仅有引擎方法无法触发)· db.backup() 公共方法 · 删除全部死代码(utils.ts 整文件 / MVCC 读侧 / estimateQueryCost 未接线优化器 / 40+ 统计辅助方法)。
v0.6.0 完全移除 IndexedDB — 自研 KVStore 事务存储引擎(多 key 原子写 = 单日志记录原子追加 · 快照 checkpoint + 两阶段崩溃恢复 · CRC-32 自愈)· disk 模式切换 KVStoreEngine(替代 IndexedDBEngine + OPFSEngine)· 事务内 DDL / 外键级联 / 二级索引完整持久化 · migrateFromIndexedDB() 旧库一键迁移 · 10 万 key 压力验证 · e2e 崩溃注入 + KVStoreEngine 真实环境(12 用例)。
v0.6.1 生产可用性深度审查 — MemoryEngine 级联环(A→B→A)无限递归修复(visited 保护,与 Aria 对齐)· KVStore 快照损坏水位 bug 修复(metaSeq 误跳日志)· 多表事务 / 级联写入合并单条日志记录真原子(崩溃无部分提交)· 未 open 防护统一 · 27 个异常场景测试(空值/大 value/特殊字符 key/写失败/事务故障/级联环/批量删除等)· 1049 测试 65 套件 · 89.3% 行覆盖率。

存储模式对比

特性MemoryDisk(IDB)Disk(OPFS)HybridAria
持久化✅ IDB✅ OPFS✅ 双写✅ 后端决定
事务✅ 快照✅ 原子✅ 快照✅ 双引擎✅ MVCC
索引HashHashHashHashLSM二级
上限内存~2GB磁盘~2GB内存
浏览器全部全部Chrome/Edge 102+ · Firefox 111+ · Safari 15.2+全部全部

核心特性

特性说明
LSM-Tree 索引MemTable (红黑树) + 多级 SSTable,写优化,支持范围扫描
4KB 页面化物理存储SSTable 切分为 4KB 页面(FileManager 分配 pageId + BufferPool LRU 缓存 256 页 ≈ 1MB),save 即落盘,`SSTableMeta.pageIds` 持久化(旧整 value 数据兼容)✅ v0.5.0
Buffer PoolLRU 页面缓存,可控内存占用(默认 256 页 ≈ 1MB),脏页写回 ✅ v0.5.0 真实接入
WAL 日志分片文件(`__wal_%06d.bin`,4MB 阈值切换 + 真追加)· 标准 CRC32 记录校验 · full/batch/none 三种同步模式(full 真正同步)· 空洞检测截断 · 16MB 阈值自动 checkpoint ✅ v0.5.0
MVCC 事务快照隔离 (Snapshot Isolation),读写不互斥,版本链 + GC(引擎事务读取走 txnSnapshot,MVCC 版本链作 undo log)
全库 AES-GCM 加密EncryptedBackend 透明加解密(WAL/SSTable/Schema/元数据全密文)· PBKDF2 密钥派生 + salt 持久化 + 密码验证 · 明文/加密库开关一致性检测 ✅ v0.5.0
多标签页独占锁Web Locks API:第二个标签页打开同一库抛 `ARIA_LOCKED`,open 失败自动释放 ✅ v0.5.0
Bloom Filter快速判定 key 不存在,减少无效磁盘 I/O,SSTableReader 二分查找统一 ✅ v0.2.5
LZ4 压缩(v2)SSTable 级压缩,压缩流自带原始大小头(高压缩率不截断)✅ v0.5.0

使用方式

// 激活 AriaEngine(OPFS 后端默认页面化存储)
	const db = await MetonaSqlark.create({
	  name: 'my-app',
	  mode: 'aria',             // 🆕 AriaEngine 模式
	  diskEngine: 'opfs',        // 底层存储后端(opfs | memory)
	  aria: {                     // AriaEngine 专属配置透传 🆕 v0.5.0
	    walSyncMode: 'full',    // WAL 同步模式
	    encryption: { password: 'my-password' }, // 全库 AES-GCM 加密 🆕 v0.5.0
	  },
	});

	// 或直接实例化 — 支持细粒度配置
	import { AriaEngine } from '@metona-team/metona-sqlark';
	const engine = new AriaEngine({
	  bufferPoolPages: 256,       // Buffer Pool 页面数量
	  memtableSizeThreshold: 4194304, // MemTable 刷盘阈值 4MB
	  walSyncMode: 'full',        // 'full' | 'batch' | 'none'(默认 full)
	  storageBackend: 'opfs', // 存储后端(opfs | memory)
	  encryption: { password: 'my-password' }, // 全库加密
	});

AriaEngine 配置项

属性类型默认值说明
pageSizenumber4096页面大小(字节)
bufferPoolPagesnumber256Buffer Pool 页面数量
memtableSizeThresholdnumber4194304MemTable 刷盘阈值(字节)
levelSizeMultipliernumber10LSM 层级容量倍数
bloomFilterBitsPerKeynumber10Bloom Filter 每 key 位数
walEnabledbooleantrue是否启用 WAL
walSyncMode'full'|'batch'|'none''full'WAL 同步策略(full 真正同步 ✅ v0.2.4 起默认 full)
checkpointIntervalnumber1000Checkpoint 触发间隔(操作数)
walSizeThresholdnumber16777216WAL 大小阈值(字节),超阈值触发 checkpoint ✅ v0.2.5
compressionbooleanfalse是否启用页面压缩
storageBackend'opfs'|'memory''opfs'存储后端类型(v0.6.0: IndexedDB 已移除)
encryption{ password: string }-全库 AES-256-GCM 透明加密(PBKDF2 派生 + salt 持久化 + 密码验证)🆕 v0.5.0
pageStoragebooleanOPFS 自动启用SSTable 4KB 页面化物理存储(BufferPool 缓存)🆕 v0.5.0

⚠️ 错误处理

所有错误抛出 DatabaseError 实例。

try {
  await db.query('SELECT * FROM nonexistent');
} catch (err) {
  if (err instanceof DatabaseError) {
    console.log(err.message);  // 'Table "nonexistent" does not exist'
    console.log(err.code);    // 'TABLE_NOT_FOUND'
    console.log(err.details); // 附加信息
  }
}
错误码触发场景
TABLE_NOT_FOUND表不存在
TABLE_EXISTS表已存在
DUPLICATE_KEY主键重复
UNIQUE_VIOLATION唯一约束冲突
VALIDATION_ERROR数据校验失败
TYPE_ERROR字段类型错误
SCHEMA_ERROR表结构定义错误
DB_NOT_READY数据库未初始化
PARSE_ERRORSQL 语法错误
TRANSACTION_ERROR事务执行失败
COMPILE_ERROR编译 AST 到查询计划失败
CONFIG_ERROR配置错误
ARIA_LOCKED数据库已被其他标签页打开(Web Locks 独占锁)🆕 v0.5.0
ARIA_DECRYPT_ERROR解密失败:密码错误 / 密钥元数据损坏 / 数据被篡改 🆕 v0.5.0
ARIA_ENCRYPT_REQUIRED库已加密但未提供密码 🆕 v0.5.0
ARIA_ENCRYPT_CONFIG_ERROR明文库无法以加密模式打开 / 空密码 🆕 v0.5.0
ARIA_BACKGROUND_ERROR后台 flush/compaction 失败(flush/close 时显式报告)
SAVEPOINT_EXISTS / SAVEPOINT_NOT_FOUND保存点已存在 / 回滚到不存在的保存点 🆕 v0.5.1 SQL 入口
NOT_SUPPORTED引擎不支持的操作(如 Memory 引擎执行 ANALYZE / VACUUM / SAVEPOINT)🆕 v0.5.1