docs: README + site 更新 v0.1.13 新特性(事务回滚/子查询/外键级联/连接池)
CI / test (18.x) (push) Successful in 9m53s
CI / test (20.x) (push) Successful in 9m54s
CI / test (22.x) (push) Successful in 9m52s
CI / test (24.x) (push) Successful in 9m48s

This commit is contained in:
thzxx
2026-07-26 16:34:34 +08:00
parent 0f128da34a
commit b6d814aefe
4 changed files with 197 additions and 49 deletions
+60 -20
View File
@@ -1,7 +1,7 @@
# MetonaSqlark # MetonaSqlark
<p align="center"> <p align="center">
<img src="https://img.shields.io/badge/version-0.1.12-blue?style=flat-square" alt="version"> <img src="https://img.shields.io/badge/version-0.1.13-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/license-MIT-green?style=flat-square" alt="license">
<img src="https://img.shields.io/badge/coverage-93.46%25-brightgreen?style=flat-square" alt="coverage"> <img src="https://img.shields.io/badge/coverage-93.46%25-brightgreen?style=flat-square" alt="coverage">
<img src="https://img.shields.io/badge/tests-264%20passed-success?style=flat-square" alt="tests"> <img src="https://img.shields.io/badge/tests-264%20passed-success?style=flat-square" alt="tests">
@@ -11,6 +11,15 @@
--- ---
## ✨ v0.1.13 新特性
- 🔒 **事务回滚**`beginTransaction/commitTransaction/rollbackTransaction` 真正的原子操作
- 🔍 **子查询**`IN (SELECT ...)` 和标量子查询 `= (SELECT ...)`
- 🔗 **外键级联**`ON DELETE CASCADE / SET NULL / RESTRICT` 递归级联删除
- 🏊 **连接池**`MetonaSqlark.connect()` 单例复用,引用计数管理
---
## 📦 安装 ## 📦 安装
```bash ```bash
@@ -72,15 +81,6 @@ const { MetonaSqlark } = require('@metona-team/metona-sqlark');
</script> </script>
``` ```
### ES Module in Browser
```html
<script type="module">
import { MetonaSqlark } from './metona-sqlark.esm.js';
const db = await MetonaSqlark.create({ name: 'my-app', mode: 'hybrid' });
</script>
```
--- ---
## 🚀 快速开始 ## 🚀 快速开始
@@ -94,7 +94,7 @@ const db = await MetonaSqlark.create({
diskEngine: 'indexeddb', // 'indexeddb' | 'opfs' diskEngine: 'indexeddb', // 'indexeddb' | 'opfs'
}); });
// 定义表 // 定义表 — 支持外键级联
await db.defineTable('users', { await db.defineTable('users', {
id: { type: 'string', primaryKey: true }, id: { type: 'string', primaryKey: true },
name: { type: 'string', required: true }, name: { type: 'string', required: true },
@@ -102,6 +102,12 @@ await db.defineTable('users', {
email: { type: 'string', unique: true, index: true }, 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 查询 // SQL 查询
await db.query("INSERT INTO users VALUES ('1', 'Alice', 30, 'alice@demo.com')"); 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'); const rows = await db.query('SELECT * FROM users WHERE age > 18 ORDER BY name');
@@ -117,14 +123,23 @@ const result = await db.table('users')
// JOIN // JOIN
await db.query(`SELECT u.name, o.product FROM users u INNER JOIN orders o ON u.id = o.user_id`); 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 // GROUP BY
await db.query(`SELECT dept, COUNT(*) FROM employees GROUP BY dept HAVING COUNT(*) > 1`); await db.query(`SELECT dept, COUNT(*) FROM employees GROUP BY dept HAVING COUNT(*) > 1`);
// 事务 // 事务 — 自动回滚 v0.1.13
await db.transaction(async (trx) => { await db.transaction(async (trx) => {
await trx.table('users').insert({ id: '3', name: 'Charlie' }); await trx.table('users').insert({ id: '3', name: 'Charlie' });
await trx.table('orders').insert({ id: 'o1', userId: '3', amount: 99 }); 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
``` ```
--- ---
@@ -140,6 +155,20 @@ await db.transaction(async (trx) => {
| `diskEngine` | `'indexeddb' \| 'opfs'` | `'indexeddb'` | 磁盘引擎 | | `diskEngine` | `'indexeddb' \| 'opfs'` | `'indexeddb'` | 磁盘引擎 |
| `version` | `number` | `1` | 版本号 | | `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 操作符 ### WHERE 操作符
| 操作符 | 含义 | 操作符 | 含义 | | 操作符 | 含义 | 操作符 | 含义 |
@@ -156,12 +185,21 @@ await db.transaction(async (trx) => {
| `db.query(sql)` | 执行 SQL 字符串 | | `db.query(sql)` | 执行 SQL 字符串 |
| `db.table(name)` | 获取表操作对象 | | `db.table(name)` | 获取表操作对象 |
| `db.defineTable(name, cols)` | 定义表结构 | | `db.defineTable(name, cols)` | 定义表结构 |
| `db.transaction(fn)` | 执行事务 | | `db.transaction(fn)` | 执行事务(自动回滚)🆕 |
| `db.exportTable(name)` / `db.exportAll()` | 导出数据 JSON | | `db.exportTable(name)` / `db.exportAll()` | 导出数据 JSON |
| `db.importTable(name, data)` | 导入数据 | | `db.importTable(name, data)` | 导入数据 |
| `db.addMigration(v, fn)` / `db.migrateTo(v)` | 数据迁移 | | `db.addMigration(v, fn)` / `db.migrateTo(v)` | 数据迁移 |
| `db.subscribe(table, fn)` | 订阅表变更 | | `db.subscribe(table, fn)` | 订阅表变更 |
| `db.on(hook, fn)` | 注册钩子 | | `db.on(hook, fn)` | 注册钩子 14 种) |
### 连接池(v0.1.13
| 静态方法 | 说明 |
|------|------|
| `MetonaSqlark.connect(config)` | 获取或创建数据库实例(单例复用)🆕 |
| `MetonaSqlark.disconnect(name)` | 释放连接(引用计数 -1)🆕 |
| `MetonaSqlark.disconnectAll()` | 强制关闭所有连接 🆕 |
| `MetonaSqlark.getActiveConnections()` | 获取活跃连接列表 🆕 |
### React / Vue 集成 ### React / Vue 集成
@@ -197,7 +235,7 @@ npm run typecheck # 类型检查
| 测试用例 | 264 | | 测试用例 | 264 |
| 测试套件 | 15 | | 测试套件 | 15 |
| 语句覆盖率 | 93.46% | | 语句覆盖率 | 93.46% |
| SQL 关键字 | 29 | | SQL 关键字 | 31 |
| 存储引擎 | 4Memory / IndexedDB / OPFS / Hybrid | | 存储引擎 | 4Memory / IndexedDB / OPFS / Hybrid |
--- ---
@@ -208,14 +246,16 @@ npm run typecheck # 类型检查
src/ src/
├── index.ts # 入口(MetonaSqlark + MeSqlark ├── index.ts # 入口(MetonaSqlark + MeSqlark
├── core.ts # 主类 ├── core.ts # 主类
├── constants.ts # 类型定义 + 配置 ├── constants.ts # 类型定义 + 配置 + DatabaseError
├── connection-manager.ts # 连接池管理 🆕
├── utils.ts # 工具函数
├── engine/ # 存储引擎(Memory/IndexedDB/OPFS ├── engine/ # 存储引擎(Memory/IndexedDB/OPFS
├── hybrid/ # 混合引擎 ├── hybrid/ # 混合引擎write-through
├── table/ # 表管理 + Schema 校验 ├── table/ # 表管理 + Schema 校验
├── query/ # AST + Builder + Compiler + Executor ├── query/ # AST + Builder + Compiler + Executor
├── sql/ # Lexer + Parser ├── sql/ # Lexer + Parser(递归下降)
├── transaction/ # 事务管理 ├── transaction/ # 事务管理(支持回滚)
├── plugin/ # 插件系统 ├── plugin/ # 插件系统14 hooks
└── integrations/ # React / Vue hooks └── integrations/ # React / Vue hooks
``` ```
+17 -2
View File
@@ -82,7 +82,7 @@
<a href="docs.html">文档</a> <a href="docs.html">文档</a>
<a href="demo.html" class="nav-active">演示</a> <a href="demo.html" class="nav-active">演示</a>
</nav> </nav>
<div class="status"><span class="dot"></span> Memory 模式 — v0.1.12</div> <div class="status"><span class="dot"></span> Memory 模式 — v0.1.13</div>
</header> </header>
<div class="main"> <div class="main">
@@ -112,6 +112,7 @@ SELECT COUNT(*) as total_users, AVG(age) as avg_age FROM users;</textarea>
<button class="btn btn-preset" onclick="loadPreset('agg')">📊 聚合</button> <button class="btn btn-preset" onclick="loadPreset('agg')">📊 聚合</button>
<button class="btn btn-preset" onclick="loadPreset('ddl')">🏗 DDL</button> <button class="btn btn-preset" onclick="loadPreset('ddl')">🏗 DDL</button>
<button class="btn btn-preset" onclick="loadPreset('crud')">✏️ CRUD</button> <button class="btn btn-preset" onclick="loadPreset('crud')">✏️ CRUD</button>
<button class="btn btn-preset" onclick="loadPreset('subquery')">🔍 子查询</button>
</div> </div>
</div> </div>
</div> </div>
@@ -370,7 +371,21 @@ SELECT * FROM users WHERE id = '9';
DELETE FROM users WHERE id = '9'; DELETE FROM users WHERE id = '9';
-- 确认 -- 确认
SELECT COUNT(*) as user_count FROM users;` SELECT COUNT(*) as user_count FROM users;`,
subquery: `-- IN 子查询:有订单的用户
SELECT name, email FROM users
WHERE id IN (SELECT user_id FROM orders);
-- NOT IN 子查询:没有订单的用户
SELECT name, email FROM users
WHERE id NOT IN (SELECT user_id FROM orders);
-- 标量子查询:消费最高的用户
SELECT name, age FROM users
WHERE id IN (
SELECT user_id FROM orders
WHERE amount = (SELECT MAX(amount) FROM orders)
);`
}; };
function loadPreset(name) { function loadPreset(name) {
+76 -9
View File
@@ -85,7 +85,10 @@
<a href="#groupby">GROUP BY & 聚合</a> <a href="#groupby">GROUP BY & 聚合</a>
<a href="#where-ops">WHERE 操作符</a> <a href="#where-ops">WHERE 操作符</a>
<h4>高级特性</h4> <h4>高级特性</h4>
<a href="#transaction">事务</a> <a href="#transaction">事务 & 回滚</a>
<a href="#subquery">子查询</a>
<a href="#foreign-key">外键级联</a>
<a href="#connection-pool">连接池</a>
<a href="#migration">数据迁移</a> <a href="#migration">数据迁移</a>
<a href="#export">导入导出</a> <a href="#export">导入导出</a>
<a href="#plugin">插件 & 钩子</a> <a href="#plugin">插件 & 钩子</a>
@@ -189,6 +192,8 @@ db.<span class="f">isReady</span>(); <span class="c">// true</span>
<tr><td><code>maxLength</code></td><td><code>number</code></td><td>字符串最大长度</td></tr> <tr><td><code>maxLength</code></td><td><code>number</code></td><td>字符串最大长度</td></tr>
<tr><td><code>min</code>/<code>max</code></td><td><code>number</code></td><td>数值范围</td></tr> <tr><td><code>min</code>/<code>max</code></td><td><code>number</code></td><td>数值范围</td></tr>
<tr><td><code>references</code></td><td><code>string</code></td><td>外键引用 <code>'table.column'</code></td></tr> <tr><td><code>references</code></td><td><code>string</code></td><td>外键引用 <code>'table.column'</code></td></tr>
<tr><td><code>onDelete</code></td><td><code>'CASCADE'\|'SET NULL'\|'RESTRICT'</code></td><td>删除级联 🆕</td></tr>
<tr><td><code>onUpdate</code></td><td><code>'CASCADE'\|'SET NULL'\|'RESTRICT'</code></td><td>更新级联 🆕</td></tr>
</table> </table>
<h2 id="sql-query">🔍 SQL 查询</h2> <h2 id="sql-query">🔍 SQL 查询</h2>
@@ -352,21 +357,83 @@ db.<span class="f">isReady</span>(); <span class="c">// true</span>
<span class="s">name</span>: { <span class="s">$like</span>: <span class="s">'A%'</span>, <span class="s">$ne</span>: <span class="s">'Admin'</span> }, <span class="s">name</span>: { <span class="s">$like</span>: <span class="s">'A%'</span>, <span class="s">$ne</span>: <span class="s">'Admin'</span> },
})</pre> })</pre>
<h2 id="transaction">🔒 事务</h2> <h2 id="transaction">🔒 事务 & 回滚</h2>
<p>保证原子性,事务内步骤失败自动报错</p> <p>v0.1.13 起支持真正的自动回滚:事务内任何一步失败,所有变更自动撤销</p>
<pre><span class="k">await</span> db.<span class="f">transaction</span>(<span class="k">async</span> (trx) => { <pre><span class="k">await</span> db.<span class="f">transaction</span>(<span class="k">async</span> (trx) => {
<span class="c">// trx.table() 获取事务内表操作对象</span> <span class="c">// trx.table() 获取事务内表操作对象</span>
<span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'users'</span>).<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'3'</span>, <span class="s">name</span>: <span class="s">'Charlie'</span> }); <span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'users'</span>).<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'3'</span>, <span class="s">name</span>: <span class="s">'Charlie'</span> });
<span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'orders'</span>).<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'o1'</span>, <span class="s">userId</span>: <span class="s">'3'</span>, <span class="s">amount</span>: <span class="n">99</span> }); <span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'orders'</span>).<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'o1'</span>, <span class="s">userId</span>: <span class="s">'3'</span>, <span class="s">amount</span>: <span class="n">99</span> });
<span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'accounts'</span>).<span class="f">update</span>({ <span class="s">balance</span>: <span class="n">1</span> }) <span class="c">// ✅ 全部成功 → 自动 commit</span>
.<span class="f">where</span>({ <span class="s">userId</span>: <span class="s">'3'</span> }) <span class="c">// ❌ 任何一步失败 → 自动 rollback,数据恢复原状</span>
.<span class="f">execute</span>(); <span class="c">// Memory 引擎:快照回滚 | IndexedDB 引擎:延迟写入</span>
<span class="c">// 也可返回结果</span>
<span class="k">return</span> <span class="s">'success'</span>;
});</pre> });</pre>
<h2 id="subquery">🔍 子查询</h2>
<p>v0.1.13 新增子查询支持,可在 WHERE 条件中嵌套 SELECT。</p>
<pre><span class="c">// IN 子查询 — 查询有高额订单的用户</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 100)`</span>);
<span class="c">// 标量子查询 — 查询年龄等于平均年龄的用户</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
WHERE age = (SELECT AVG(age) FROM users)`</span>);
<span class="c">// NOT IN 子查询</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
WHERE id NOT IN (SELECT user_id FROM orders)`</span>);</pre>
<h2 id="foreign-key">🔗 外键级联</h2>
<p>v0.1.13 支持外键级联操作,定义表时可指定 ON DELETE / ON UPDATE 行为。</p>
<pre><span class="c">// 定义时指定外键 + 级联策略</span>
<span class="k">await</span> db.<span class="f">defineTable</span>(<span class="s">'orders'</span>, {
<span class="s">id</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">primaryKey</span>: <span class="k">true</span> },
<span class="s">user_id</span>: {
<span class="s">type</span>: <span class="s">'string'</span>,
<span class="s">references</span>: <span class="s">'users.id'</span>,
<span class="s">onDelete</span>: <span class="s">'CASCADE'</span>, <span class="c">// 删除用户时级联删除订单</span>
<span class="s">onUpdate</span>: <span class="s">'RESTRICT'</span>, <span class="c">// 禁止更新被引用的用户 ID</span>
},
<span class="s">amount</span>: { <span class="s">type</span>: <span class="s">'number'</span> },
});
<span class="c">// SQL DDL 同样支持</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`CREATE TABLE orders (
id STRING PRIMARY KEY,
user_id STRING REFERENCES users(id) ON DELETE CASCADE ON UPDATE RESTRICT,
amount NUMBER
)`</span>);
<span class="c">// 删除用户 → 其所有订单自动删除</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"DELETE FROM users WHERE id = '1'"</span>);</pre>
<table>
<tr><th>级联选项</th><th>行为</th></tr>
<tr><td><code>CASCADE</code></td><td>级联删除/更新子表中的匹配行</td></tr>
<tr><td><code>SET NULL</code></td><td>将子表中的外键列设为 NULL</td></tr>
<tr><td><code>RESTRICT</code></td><td>禁止操作(默认行为)</td></tr>
</table>
<h2 id="connection-pool">🏊 连接池</h2>
<p>v0.1.13 新增连接池管理器,避免重复创建同名数据库实例。</p>
<pre><span class="c">// connect() — 获取或创建实例(单例复用)</span>
<span class="k">const</span> db1 = <span class="k">await</span> <span class="f">MetonaSqlark.connect</span>({ <span class="s">name</span>: <span class="s">'my-app'</span>, <span class="s">mode</span>: <span class="s">'hybrid'</span> });
<span class="k">const</span> db2 = <span class="k">await</span> <span class="f">MetonaSqlark.connect</span>({ <span class="s">name</span>: <span class="s">'my-app'</span> });
<span class="c">// db1 === db2 — 复用已有实例,避免重复 open IndexedDB</span>
<span class="c">// disconnect() — 释放连接(引用计数 -1)</span>
<span class="k">await</span> db2.<span class="f">disconnect</span>(); <span class="c">// 引用计数: 2 → 1</span>
<span class="k">await</span> db1.<span class="f">disconnect</span>(); <span class="c">// 引用计数: 1 → 0,自动 close()</span>
<span class="c">// disconnectAll() — 强制关闭所有连接</span>
<span class="k">await</span> <span class="f">MetonaSqlark.disconnectAll</span>();
<span class="c">// getActiveConnections() — 查看活跃连接</span>
<span class="f">MetonaSqlark.getActiveConnections</span>(); <span class="c">// ['my-app']</span></pre>
<h2 id="migration">🔄 数据迁移</h2> <h2 id="migration">🔄 数据迁移</h2>
<p>按版本号管理表结构变更。</p> <p>按版本号管理表结构变更。</p>
+44 -18
View File
@@ -151,7 +151,7 @@
<!-- Hero --> <!-- Hero -->
<section class="hero"> <section class="hero">
<div class="container"> <div class="container">
<div class="badge" style="margin-bottom:24px;"><span class="dot"></span> v0.1.12 已发布</div> <div class="badge" style="margin-bottom:24px;"><span class="dot"></span> v0.1.13 已发布 — 事务回滚 · 子查询 · 外键级联 · 连接池</div>
<h1>前端的 <span class="gradient-text">SQL 数据库</span></h1> <h1>前端的 <span class="gradient-text">SQL 数据库</span></h1>
<p>TypeScript 原生构建,内存与磁盘双模式,支持完整 SQL 查询。<br>零运行时依赖,开箱即用。</p> <p>TypeScript 原生构建,内存与磁盘双模式,支持完整 SQL 查询。<br>零运行时依赖,开箱即用。</p>
<div class="actions"> <div class="actions">
@@ -237,32 +237,42 @@ npm install @metona-team/metona-sqlark
<div class="feature-card"> <div class="feature-card">
<div class="icon"></div> <div class="icon"></div>
<h3>完整 SQL 解析器</h3> <h3>完整 SQL 解析器</h3>
<p>手写递归下降 SQL 解析器,无第三方依赖。支持 SELECT / INSERT / UPDATE / DELETE / CREATE TABLE / DROP TABLE / JOIN / GROUP BY / HAVING / DISTINCT。</p> <p>手写递归下降 SQL 解析器SELECT / INSERT / UPDATE / DELETE / JOIN / GROUP BY / HAVING / DISTINCT / 子查询</p>
</div>
<div class="feature-card">
<div class="icon">🔗</div>
<h3>Query Builder</h3>
<p>链式 API + TypeScript 泛型支持。.select().where().innerJoin().orderBy().limit() — IDE 自动补全,类型安全。</p>
</div> </div>
<div class="feature-card"> <div class="feature-card">
<div class="icon">🔒</div> <div class="icon">🔒</div>
<h3>事务支持</h3> <h3>事务回滚 <span style="font-size:0.65rem;color:var(--accent);vertical-align:super;">NEW</span></h3>
<p>原子性操作,事务内任意步骤失败自动回滚。IndexedDB 引擎原生事务 + Memory 引擎模拟事务</p> <p>beginTransaction / commitTransaction / rollbackTransaction 三件套。失败自动回滚,Memory 快照 + IndexedDB 延迟写入</p>
</div>
<div class="feature-card">
<div class="icon">🔍</div>
<h3>子查询 <span style="font-size:0.65rem;color:var(--accent);vertical-align:super;">NEW</span></h3>
<p>IN (SELECT ...) + 标量子查询。递归执行,自动将子查询结果替换为具体值。</p>
</div>
<div class="feature-card">
<div class="icon">🔗</div>
<h3>外键级联 <span style="font-size:0.65rem;color:var(--accent);vertical-align:super;">NEW</span></h3>
<p>references + ON DELETE CASCADE / SET NULL / RESTRICT。递归级联删除,自动维护引用完整性。</p>
</div>
<div class="feature-card">
<div class="icon">🏊</div>
<h3>连接池 <span style="font-size:0.65rem;color:var(--accent);vertical-align:super;">NEW</span></h3>
<p>MetonaSqlark.connect() 单例复用,引用计数管理。避免重复打开 IndexedDB,自动释放资源。</p>
</div> </div>
<div class="feature-card"> <div class="feature-card">
<div class="icon">🧩</div> <div class="icon">🧩</div>
<h3>插件系统</h3> <h3>插件系统</h3>
<p>14 种生命周期钩子:beforeCreateTable / afterInsert / beforeQuery / afterTransaction……按优先级注册,完整生命周期管理</p> <p>14 种生命周期钩子:beforeCreateTable / afterInsert / beforeQuery / afterTransaction……按优先级注册。</p>
</div> </div>
<div class="feature-card"> <div class="feature-card">
<div class="icon">📦</div> <div class="icon">📦</div>
<h3>零运行时依赖</h3> <h3>零运行时依赖</h3>
<p>纯 TypeScript 实现,不依赖任何第三方库。Tree-shakableUMD/ESM/CJS 多格式输出,最小体积 ~10KB gzip。</p> <p>纯 TypeScript 实现,不依赖任何第三方库。Tree-shakableUMD/ESM/CJS 多格式输出,~10KB gzip。</p>
</div> </div>
<div class="feature-card"> <div class="feature-card">
<div class="icon">📊</div> <div class="icon">📊</div>
<h3>聚合 & 分组</h3> <h3>聚合 & 分组</h3>
<p>COUNT / SUM / AVG / MIN / MAX 五大聚合函数,GROUP BY + HAVING 子句,DISTINCT 去重,完整的数据分析能力</p> <p>COUNT / SUM / AVG / MIN / MAX 五大聚合函数,GROUP BY + HAVING 子句,DISTINCT 去重。</p>
</div> </div>
<div class="feature-card"> <div class="feature-card">
<div class="icon">🔄</div> <div class="icon">🔄</div>
@@ -271,8 +281,13 @@ npm install @metona-team/metona-sqlark
</div> </div>
<div class="feature-card"> <div class="feature-card">
<div class="icon">📡</div> <div class="icon">📡</div>
<h3>发布订阅</h3> <h3>发布订阅 + 框架集成</h3>
<p>subscribe() 订阅表变更事件,emit() 触发通知。React useQuery / Vue useSqlarkQuery 开箱即用。</p> <p>subscribe() 订阅表变更。React useQuery / Vue useSqlarkQuery 开箱即用。</p>
</div>
<div class="feature-card">
<div class="icon">🔗</div>
<h3>Query Builder</h3>
<p>链式 API + TypeScript 泛型。.select().where().innerJoin().orderBy().limit() — 类型安全,IDE 友好。</p>
</div> </div>
</div> </div>
</div> </div>
@@ -312,13 +327,13 @@ npm install @metona-team/metona-sqlark
<div class="container"> <div class="container">
<div class="section-title"> <div class="section-title">
<h2>5 行<span>代码</span>开始</h2> <h2>5 行<span>代码</span>开始</h2>
<p>像写 SQL 一样操作前端数据 — 或使用 Query Builder 链式 API</p> <p>像写 SQL 一样操作前端数据 — v0.1.13 新增子查询、外键级联、事务回滚</p>
</div> </div>
<div class="code-block"> <div class="code-block">
<pre><span class="comment">// 创建数据库 — MeSqlark 是别名,完全等价</span> <pre><span class="comment">// 创建数据库 — MeSqlark 是别名,完全等价</span>
<span class="keyword">const</span> db = <span class="keyword">await</span> <span class="func">MetonaSqlark.create</span>({ <span class="string">name</span>: <span class="string">'my-app'</span>, <span class="string">mode</span>: <span class="string">'hybrid'</span> }); <span class="keyword">const</span> db = <span class="keyword">await</span> <span class="func">MetonaSqlark.create</span>({ <span class="string">name</span>: <span class="string">'my-app'</span>, <span class="string">mode</span>: <span class="string">'hybrid'</span> });
<span class="comment">// 定义表结构</span> <span class="comment">// 定义表结构 — 支持外键级联</span>
<span class="keyword">await</span> db.<span class="func">defineTable</span>(<span class="string">'users'</span>, { <span class="keyword">await</span> db.<span class="func">defineTable</span>(<span class="string">'users'</span>, {
<span class="string">id</span>: { <span class="string">type</span>: <span class="string">'string'</span>, <span class="string">primaryKey</span>: <span class="keyword">true</span> }, <span class="string">id</span>: { <span class="string">type</span>: <span class="string">'string'</span>, <span class="string">primaryKey</span>: <span class="keyword">true</span> },
<span class="string">name</span>: { <span class="string">type</span>: <span class="string">'string'</span>, <span class="string">required</span>: <span class="keyword">true</span> }, <span class="string">name</span>: { <span class="string">type</span>: <span class="string">'string'</span>, <span class="string">required</span>: <span class="keyword">true</span> },
@@ -344,11 +359,22 @@ npm install @metona-team/metona-sqlark
<span class="keyword">await</span> db.<span class="func">query</span>(<span class="string">`SELECT dept, COUNT(*) as cnt, AVG(salary) as avg <span class="keyword">await</span> db.<span class="func">query</span>(<span class="string">`SELECT dept, COUNT(*) as cnt, AVG(salary) as avg
FROM employees GROUP BY dept HAVING COUNT(*) > 1`</span>); FROM employees GROUP BY dept HAVING COUNT(*) > 1`</span>);
<span class="comment">// 事务 — 原子操作</span> <span class="comment">// 事务 — 失败自动回滚</span>
<span class="keyword">await</span> db.<span class="func">transaction</span>(<span class="keyword">async</span> (trx) => { <span class="keyword">await</span> db.<span class="func">transaction</span>(<span class="keyword">async</span> (trx) => {
<span class="keyword">await</span> trx.<span class="func">table</span>(<span class="string">'users'</span>).<span class="func">insert</span>({ id: <span class="string">'3'</span>, name: <span class="string">'Charlie'</span> }); <span class="keyword">await</span> trx.<span class="func">table</span>(<span class="string">'users'</span>).<span class="func">insert</span>({ id: <span class="string">'3'</span>, name: <span class="string">'Charlie'</span> });
<span class="keyword">await</span> trx.<span class="func">table</span>(<span class="string">'orders'</span>).<span class="func">insert</span>({ id: <span class="string">'o1'</span>, userId: <span class="string">'3'</span>, amount: <span class="number">99</span> }); <span class="keyword">await</span> trx.<span class="func">table</span>(<span class="string">'orders'</span>).<span class="func">insert</span>({ id: <span class="string">'o1'</span>, userId: <span class="string">'3'</span>, amount: <span class="number">99</span> });
});</pre> <span class="comment">// 任何一步失败 → 自动回滚</span>
});
<span class="comment">// 子查询 — IN (SELECT ...)</span>
<span class="keyword">await</span> db.<span class="func">query</span>(<span class="string">`SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 100)`</span>);
<span class="comment">// 连接池 — 单例复用,避免重复打开 IndexedDB</span>
<span class="keyword">const</span> db1 = <span class="keyword">await</span> <span class="func">MetonaSqlark.connect</span>({ name: <span class="string">'my-app'</span> });
<span class="keyword">const</span> db2 = <span class="keyword">await</span> <span class="func">MetonaSqlark.connect</span>({ name: <span class="string">'my-app'</span> });
<span class="comment">// db1 === db2(复用已有实例)</span>
<span class="keyword">await</span> db2.<span class="func">disconnect</span>(); <span class="comment">// 引用计数 -1,归零时自动 close</span></pre>
</div> </div>
</div> </div>
</section> </section>