# MetonaSqlark

version license coverage tests

> 基于 TypeScript 的**前端关系型数据库**,内存与磁盘双模式运行,支持完整 SQL 查询与 Query Builder 链式 API。 --- ## ✨ v0.1.13 新特性 - 🔒 **事务回滚** — `beginTransaction/commitTransaction/rollbackTransaction` 真正的原子操作 - 🔍 **子查询** — `IN (SELECT ...)` 和标量子查询 `= (SELECT ...)` - 🔗 **外键级联** — `ON DELETE CASCADE / SET NULL / RESTRICT` 递归级联删除 - 🏊 **连接池** — `MetonaSqlark.connect()` 单例复用,引用计数管理 --- ## 📦 安装 ```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 ``` 或从 [`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 ``` --- ## 🚀 快速开始 ```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'` | `'hybrid'` | 存储模式 | | `diskEngine` | `'indexeddb' \| 'opfs'` | `'indexeddb'` | 磁盘引擎 | | `version` | `number` | `1` | 版本号 | ### 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'); ``` --- ## 🛠 开发 ```bash npm install # 安装依赖 npm run dev # 开发模式(localhost:3001) npm run build # 生产构建(生成 dist/) npm test # 运行测试 npm run lint # 代码检查 npm run typecheck # 类型检查 ``` --- ## 📊 项目状态 | 指标 | 数值 | |------|------| | 测试用例 | 264 | | 测试套件 | 15 | | 语句覆盖率 | 93.46% | | SQL 关键字 | 31 | | 存储引擎 | 4(Memory / IndexedDB / OPFS / Hybrid) | --- ## 📂 项目结构 ``` src/ ├── index.ts # 入口(MetonaSqlark + MeSqlark) ├── core.ts # 主类 ├── constants.ts # 类型定义 + 配置 + DatabaseError ├── connection-manager.ts # 连接池管理 🆕 ├── utils.ts # 工具函数 ├── engine/ # 存储引擎(Memory/IndexedDB/OPFS) ├── 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)