Files
MetonaSqlark/README.md
T
thzxx d33f4ab3b0
CI / test (18.x) (push) Successful in 9m58s
CI / test (20.x) (push) Successful in 9m58s
CI / test (22.x) (push) Successful in 10m18s
CI / test (24.x) (push) Successful in 10m2s
test: OPFSEngine 12项mock测试 + Schema约束5项测试 → 543 passed
2026-07-27 21:34:12 +08:00

341 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MetonaSqlark
<p align="center">
<img src="https://img.shields.io/badge/version-0.2.2-blue?style=flat-square" alt="version">
<img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="license">
<img src="https://img.shields.io/badge/coverage-91.0%25-brightgreen?style=flat-square" alt="coverage">
<img src="https://img.shields.io/badge/tests-543%20passed-success?style=flat-square" alt="tests">
</p>
> 基于 TypeScript 的**前端关系型数据库**,支持完整 SQL 查询、Query Builder 链式 API、与 **AriaEngine 自研页面式存储引擎**。
---
## ✨ 特性
- 🚀 **AriaEngine 自研存储引擎** — LSM-Tree 页面式存储,4KB Slotted Page、WAL 崩溃恢复、LZ4 压缩
- 💾 **OPFS 自研存储后端** — 纯浏览器文件系统,零 IndexedDB 依赖,二进制页面文件
- 🔒 **生产级数据安全** — WAL CRC 完整性校验、`RESTRICT` 外键约束、Hybrid 提交原子性
- 🛡 **输入校验全覆盖**`maxLength`/`min`/`max` 约束、类型检查、必填验证
- 💾 **多引擎架构** — Memory / IndexedDB / OPFS / Hybrid(write-through) / Aria 五种模式
- 📝 **完整 SQL 支持** — SELECT/JOIN/子查询/GROUP BY/HAVING/ORDER BY/LIMIT/BETWEEN/IF NOT EXISTS
- 🔗 **Query Builder API** — 链式 `.select().where().orderBy().limit().execute()`
- 🔄 **事务回滚** — Memory/IndexedDB/Hybrid/Aria 四引擎事务原子性,自动回滚
- 🌲 **RB-Tree 完整实现** — 标准红黑树插入+删除修复,O(log n) 保证
- 🌐 **浏览器兼容** — Chrome 80+ / Firefox 80+ / Safari 14+ / Edge 80+ / Node.js 16+
- 🧪 **526 测试 · 91.0% 覆盖率** — 27 套件,生产级质量保证
---
## 📦 安装
```bash
npm install @metona-team/metona-sqlark
```
> 如果提示找不到包,先配置 scope registry(一次性):
> ```bash
> npm config set @metona-team:registry https://git.metona.cn/api/packages/MetonaTeam/npm/
> ```
### CDN / 直接下载
```html
<!-- UMD 格式,暴露 window.MetonaSqlark 和 window.MeSqlark -->
<script src="https://git.metona.cn/MetonaTeam/MetonaSqlark/raw/branch/master/dist/metona-sqlark.min.js"></script>
```
或从 [`dist/`](./dist/) 目录下载:
- `metona-sqlark.js` — UMD 开发版(含 sourcemap
- `metona-sqlark.min.js` — UMD 压缩版(~42KB
- `metona-sqlark.esm.js` — ES Module
- `metona-sqlark.cjs.js` — CommonJS
- `metona-sqlark.d.ts` — TypeScript 类型声明
---
## 🚀 引入方式
### ESM / TypeScript
```typescript
import { MetonaSqlark } from '@metona-team/metona-sqlark';
// 别名
import { MeSqlark } from '@metona-team/metona-sqlark';
const db = await MetonaSqlark.create({ name: 'my-app', mode: 'hybrid' });
```
### CommonJS
```javascript
const { MetonaSqlark } = require('@metona-team/metona-sqlark');
(async () => {
const db = await MetonaSqlark.create({ name: 'my-app', mode: 'hybrid' });
})();
```
### Browser UMD
```html
<script src="metona-sqlark.min.js"></script>
<script>
(async () => {
const db = await window.MetonaSqlark.create({ name: 'my-app', mode: 'hybrid' });
// 或 window.MeSqlark(完全等价)
})();
</script>
```
---
## 🚀 快速开始
```typescript
import { MetonaSqlark } from '@metona-team/metona-sqlark';
const db = await MetonaSqlark.create({
name: 'my-app',
mode: 'hybrid', // 'memory' | 'disk' | 'hybrid'
diskEngine: 'indexeddb', // 'indexeddb' | 'opfs'
});
// 定义表 — 支持外键级联
await db.defineTable('users', {
id: { type: 'string', primaryKey: true },
name: { type: 'string', required: true },
age: { type: 'number', default: 0 },
email: { type: 'string', unique: true, index: true },
});
await db.defineTable('orders', {
id: { type: 'string', primaryKey: true },
user_id: { type: 'string', references: 'users.id', onDelete: 'CASCADE' },
amount: { type: 'number' },
});
// SQL 查询
await db.query("INSERT INTO users VALUES ('1', 'Alice', 30, 'alice@demo.com')");
const rows = await db.query('SELECT * FROM users WHERE age > 18 ORDER BY name');
// Query Builder
const result = await db.table('users')
.select(['name', 'age'])
.where({ age: { $gt: 18 } })
.orderBy('age', 'desc')
.limit(10)
.execute();
// JOIN
await db.query(`SELECT u.name, o.product FROM users u INNER JOIN orders o ON u.id = o.user_id`);
// 子查询 v0.1.13
await db.query(`SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE amount > 100)`);
// GROUP BY
await db.query(`SELECT dept, COUNT(*) FROM employees GROUP BY dept HAVING COUNT(*) > 1`);
// 事务 — 自动回滚 v0.1.13
await db.transaction(async (trx) => {
await trx.table('users').insert({ id: '3', name: 'Charlie' });
await trx.table('orders').insert({ id: 'o1', userId: '3', amount: 99 });
// 任何一步失败 → 全部回滚
});
// 连接池 v0.1.13
const db2 = await MetonaSqlark.connect({ name: 'my-app', mode: 'hybrid' });
// db2 === db(复用已有实例)
await db2.disconnect(); // 引用计数 -1
```
---
## 📖 API 速览
### 数据库配置
| 属性 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `name` | `string` | `'metona-sqlark'` | 数据库名称 |
| `mode` | `'memory' \| 'disk' \| 'hybrid' \| 'aria'` | `'hybrid'` | 存储模式 🆕 aria |
| `diskEngine` | `'indexeddb' \| 'opfs'` | `'indexeddb'` | 磁盘引擎(aria 模式下为存储后端) |
| `version` | `number` | `1` | 版本号 |
| `maxRowsPerQuery` | `number` | `0` | 查询结果行数上限(0=不限制)🆕 |
| `debug` | `boolean` | `false` | 调试模式,输出详细日志 🆕 |
| `onError` | `(error) => void` | — | 全局错误回调 🆕 |
### ColumnDef 列定义
| 属性 | 类型 | 说明 |
|------|------|------|
| `type` | `'string'\|'number'\|'boolean'\|'date'\|'json'` | 数据类型 |
| `primaryKey` | `boolean` | 主键 |
| `required` | `boolean` | 必填 |
| `unique` | `boolean` | 唯一约束 |
| `index` | `boolean` | 创建哈希索引 |
| `default` | `unknown` | 默认值 |
| `references` | `string` | 外键引用 `'table.column'` |
| `onDelete` | `'CASCADE'\|'SET NULL'\|'RESTRICT'` | 删除级联 🆕 |
| `onUpdate` | `'CASCADE'\|'SET NULL'\|'RESTRICT'` | 更新级联 🆕 |
### WHERE 操作符
| 操作符 | 含义 | 操作符 | 含义 |
|--------|------|--------|------|
| `$eq` / 直接值 | 等于 | `$gt` / `$gte` | 大于 / 大于等于 |
| `$ne` | 不等于 | `$lt` / `$lte` | 小于 / 小于等于 |
| `$in` / `$nin` | 在列表中 | `$like` | 模糊匹配 |
| `$and` / `$or` / `$not` | 逻辑组合 | | |
### 核心方法
| 方法 | 说明 |
|------|------|
| `db.query(sql)` | 执行 SQL 字符串 |
| `db.table(name)` | 获取表操作对象 |
| `db.defineTable(name, cols)` | 定义表结构 |
| `db.transaction(fn)` | 执行事务(自动回滚)🆕 |
| `db.exportTable(name)` / `db.exportAll()` | 导出数据 JSON |
| `db.importTable(name, data)` | 导入数据 |
| `db.addMigration(v, fn)` / `db.migrateTo(v)` | 数据迁移 |
| `db.subscribe(table, fn)` | 订阅表变更 |
| `db.on(hook, fn)` | 注册钩子 14 种) |
### 连接池(v0.1.13
| 静态方法 | 说明 |
|------|------|
| `MetonaSqlark.connect(config)` | 获取或创建数据库实例(单例复用)🆕 |
| `MetonaSqlark.disconnect(name)` | 释放连接(引用计数 -1)🆕 |
| `MetonaSqlark.disconnectAll()` | 强制关闭所有连接 🆕 |
| `MetonaSqlark.getActiveConnections()` | 获取活跃连接列表 🆕 |
### React / Vue 集成
```tsx
// React
import { useQuery } from '@metona-team/metona-sqlark/react';
const { data, loading, refresh } = useQuery(db, 'SELECT * FROM users');
// Vue
import { useSqlarkQuery } from '@metona-team/metona-sqlark/vue';
const { data, loading, refresh } = useSqlarkQuery(db, 'SELECT * FROM users');
```
---
## 🌲 AriaEngine — 自研存储引擎 (v0.2.0)
AriaEngine 是内置的页面式存储引擎,对标 SQLite 的设计理念:
```typescript
// 激活 AriaEngine
const db = await MetonaSqlark.create({
name: 'my-app',
mode: 'aria', // 🆕 自研引擎模式
diskEngine: 'indexeddb', // 底层存储后端
});
// 与现有 API 完全兼容
await db.defineTable('users', {
id: { type: 'string', primaryKey: true },
name: { type: 'string', required: true },
});
await db.query("INSERT INTO users VALUES ('1', 'Alice')");
const rows = await db.query('SELECT * FROM users');
```
### AriaEngine 架构
```
┌──────────────────────────────────────────┐
│ AriaEngine │
│ (implements IStorageEngine) │
├──────────────────────────────────────────┤
│ LSM-Tree │ Buffer Pool │ WAL │
│ MemTable │ LRU (256pp) │ Recovery │
│ +SSTable │ │ │
├──────────────────────────────────────────┤
│ MVCC │ Bloom Filter │ LZ4 │
│ Snapshot │ FNV-1a+Murmur│ Compress │
├──────────────────────────────────────────┤
│ Storage Backend (IDB / OPFS / Memory) │
└──────────────────────────────────────────┘
```
| 特性 | 说明 |
|------|------|
| **LSM-Tree** | MemTable (红黑树) → SSTable 多级索引,写优化,支持点查 + 范围扫描 |
| **WAL** | Write-Ahead Log 二进制格式,full/batch/none 三种同步模式 |
| **MVCC** | 版本链 + 快照隔离,事务读写不互斥 |
| **Buffer Pool** | LRU 页面缓存,默认 256 页 ≈ 1MB 可控内存 |
| **Bloom Filter** | FNV-1a + Murmur 双哈希,快速否定 key |
| **Slotted Page** | 4KB 固定页面,Slot Directory + Tuple 二进制序列化 |
| **Compaction** | Leveled Compaction,自动合并回收空间 |
| **OPFS Backend** 🆕 | 自研文件存储后端,零外部依赖,纯二进制页面文件 |
---
## 🛠 开发
```bash
npm install # 安装依赖
npm run dev # 开发模式(localhost:3001
npm run build # 生产构建(生成 dist/
npm test # 运行测试
npm run lint # 代码检查
npm run typecheck # 类型检查
```
---
## 📊 项目状态
| 指标 | 数值 |
|------|------|
| 测试用例 | 543 |
| 测试套件 | 27 |
| 行覆盖率 | 91.0% |
| SQL 关键字 | 33 |
| 存储引擎 | 5Memory / IndexedDB / OPFS / Hybrid / **Aria** 🆕) |
### 🌐 浏览器兼容性
| 浏览器 | 最低版本 | Memory | IndexedDB | OPFS | Aria |
|--------|----------|--------|-----------|------|------|
| Chrome | 80+ | ✅ | ✅ | ✅ (102+) | ✅ |
| Firefox | 80+ | ✅ | ✅ | ❌ | ✅ |
| Safari | 14+ | ✅ | ✅ | ❌ | ✅ |
| Edge | 80+ | ✅ | ✅ | ✅ (102+) | ✅ |
| Node.js | 16+ | ✅ | ✅ (fake-idb) | ❌ | ✅ |
> **注意**: OPFS 模式仅限 Chromium 内核浏览器 (Chrome/Edge 102+)Firefox/Safari 请使用 `diskEngine: 'indexeddb'`。
---
## 📂 项目结构
```
src/
├── index.ts # 入口(MetonaSqlark + MeSqlark
├── core.ts # 主类
├── constants.ts # 类型定义 + 配置 + DatabaseError
├── connection-manager.ts # 连接池管理
├── utils.ts # 工具函数
├── engine/ # 存储引擎(Memory/IndexedDB/OPFS/Aria 🆕)
├── hybrid/ # 混合引擎(write-through
├── table/ # 表管理 + Schema 校验
├── query/ # AST + Builder + Compiler + Executor
├── sql/ # Lexer + Parser(递归下降)
├── transaction/ # 事务管理(支持回滚)
├── plugin/ # 插件系统(14 hooks
└── integrations/ # React / Vue hooks
```
---
## 📄 License
MIT © [MetonaTeam](https://git.metona.cn/MetonaTeam/MetonaSqlark)