Files
MetonaSqlark/site/docs.html
T
thzxx e2a590c5b1 feat: metona-sqlark v0.1.12 — 前端TypeScript关系型数据库
- 4种存储引擎:Memory / IndexedDB / OPFS / Hybrid
- 完整SQL支持:SELECT/INSERT/UPDATE/DELETE/JOIN/GROUP BY/HAVING/DISTINCT
- Query Builder链式API + TypeScript泛型支持
- 聚合函数:COUNT/SUM/AVG/MIN/MAX
- 事务、插件系统(14 hooks)、发布订阅、数据迁移、导入导出
- React/Vue框架集成
- 264个测试用例,93.46%覆盖率
- 零运行时依赖
2026-07-26 15:00:01 +08:00

292 lines
16 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API 文档 — metona-sqlark</title>
<style>
:root {
--bg:#0a0a0f; --surface:#14141f; --surface2:#1a1a2e; --border:#2a2a45;
--primary:#6366f1; --primary-glow:#818cf8; --accent:#06b6d4; --accent2:#ec4899;
--text:#e2e8f0; --text2:#94a3b8; --gradient:linear-gradient(135deg,#6366f1 0%,#06b6d4 50%,#ec4899 100%);
--radius:14px;
}
* { margin:0; padding:0; box-sizing:border-box; }
body { font-family:'Inter',-apple-system,BlinkMacSystemFont,system-ui,sans-serif; background:var(--bg); color:var(--text); line-height:1.7; }
/* Header */
header {
position:fixed; top:0; left:0; right:0; z-index:100; backdrop-filter:blur(20px);
background:rgba(10,10,15,0.85); border-bottom:1px solid var(--border); padding:16px 32px;
}
header .inner { max-width:1400px; margin:0 auto; display:flex; align-items:center; justify-content:space-between; }
.logo { font-size:1.3rem; font-weight:800; display:flex; align-items:center; gap:10px; }
.logo .icon { width:32px; height:32px; border-radius:8px; background:var(--gradient); display:flex; align-items:center; justify-content:center; font-size:1rem; font-weight:900; color:#fff; }
.logo span { background:var(--gradient); -webkit-background-clip:text; -webkit-text-fill-color:transparent; }
nav { display:flex; gap:28px; }
nav a { color:var(--text2); text-decoration:none; font-weight:500; font-size:0.92rem; transition:.2s; }
nav a:hover,.nav-active { color:var(--text); }
/* Layout */
.layout { display:flex; max-width:1400px; margin:0 auto; padding-top:80px; min-height:100vh; }
.sidebar {
width:260px; min-width:260px; padding:32px 24px; position:sticky; top:80px; height:calc(100vh - 80px);
overflow-y:auto; border-right:1px solid var(--border); background:var(--bg);
}
.sidebar h4 { font-size:0.75rem; text-transform:uppercase; letter-spacing:1px; color:var(--text2); margin:20px 0 8px; }
.sidebar h4:first-child { margin-top:0; }
.sidebar a { display:block; padding:7px 12px; border-radius:6px; color:var(--text2); text-decoration:none; font-size:0.9rem; transition:.15s; }
.sidebar a:hover,.sidebar a.active { color:var(--text); background:var(--surface2); }
.sidebar a.active { border-left:3px solid var(--primary); }
.content { flex:1; padding:32px 48px 80px; max-width:900px; }
.content h2 { font-size:1.8rem; font-weight:800; margin:48px 0 16px; padding-top:24px; border-top:1px solid var(--border); }
.content h2:first-of-type { margin-top:0; padding-top:0; border-top:none; }
.content h3 { font-size:1.25rem; font-weight:700; margin:32px 0 12px; color:var(--primary-glow); }
.content p { color:var(--text2); margin-bottom:16px; }
.content code {
background:var(--surface2); padding:2px 8px; border-radius:5px; font-family:'JetBrains Mono',monospace;
font-size:0.88rem; color:var(--accent);
}
.content pre {
background:var(--surface2); border:1px solid var(--border); border-radius:var(--radius);
padding:20px 24px; overflow-x:auto; font-family:'JetBrains Mono',monospace; font-size:0.85rem;
line-height:1.65; margin:16px 0; color:#e2e8f0;
}
.content pre .k { color:#c084fc; } .content pre .s { color:#34d399; }
.content pre .f { color:#60a5fa; } .content pre .c { color:#64748b; }
.content pre .n { color:#fbbf24; } .content pre .t { color:#f472b6; }
/* Table */
.content table { width:100%; border-collapse:collapse; margin:16px 0; }
.content th,.content td { text-align:left; padding:12px 16px; border-bottom:1px solid var(--border); font-size:0.9rem; }
.content th { color:var(--text); font-weight:600; background:var(--surface); }
.content td { color:var(--text2); }
.content td code { font-size:0.82rem; }
/* Scrollbar */
::-webkit-scrollbar { width:6px; } ::-webkit-scrollbar-track { background:transparent; } ::-webkit-scrollbar-thumb { background:var(--border); border-radius:3px; }
</style>
</head>
<body>
<header>
<div class="inner">
<div class="logo"><div class="icon"></div><span>metona-sqlark</span></div>
<nav>
<a href="index.html">首页</a>
<a href="docs.html" class="nav-active">文档</a>
<a href="demo.html">演示</a>
</nav>
</div>
</header>
<div class="layout">
<aside class="sidebar">
<h4>快速开始</h4>
<a href="#install" class="active">安装</a>
<a href="#create">创建数据库</a>
<a href="#table">定义表</a>
<h4>查询 API</h4>
<a href="#sql-query">SQL 查询</a>
<a href="#query-builder">Query Builder</a>
<a href="#join">JOIN 查询</a>
<a href="#groupby">GROUP BY</a>
<h4>高级特性</h4>
<a href="#transaction">事务</a>
<a href="#migration">迁移</a>
<a href="#export">导入导出</a>
<a href="#plugin">插件钩子</a>
<a href="#subscribe">发布订阅</a>
<h4>类型</h4>
<a href="#types">TypeScript 类型</a>
<a href="#config">配置项</a>
</aside>
<main class="content">
<h2 id="install">📦 安装</h2>
<pre><span class="c">// npm</span>
npm install @metona-team/metona-sqlark
<span class="c">// ESM</span>
<span class="k">import</span> { MetonaSqlark, MeSqlark } <span class="k">from</span> <span class="s">'metona-sqlark'</span>;
<span class="c">// Browser</span>
&lt;script src=<span class="s">"metona-sqlark.min.js"</span>&gt;&lt;/script&gt;
<span class="c">// → window.MetonaSqlark / window.MeSqlark</span></pre>
<h2 id="create">🏗 创建数据库</h2>
<p><code>MetonaSqlark.create(config)</code> 工厂函数,返回初始化好的实例。</p>
<pre><span class="k">const</span> db = <span class="k">await</span> <span class="f">MetonaSqlark.create</span>({
<span class="s">name</span>: <span class="s">'my-app'</span>,
<span class="s">mode</span>: <span class="s">'hybrid'</span>, <span class="c">// 'memory' | 'disk' | 'hybrid'</span>
<span class="s">diskEngine</span>: <span class="s">'indexeddb'</span>, <span class="c">// 'indexeddb' | 'opfs'</span>
<span class="s">version</span>: <span class="n">1</span>,
});</pre>
<table>
<tr><th>属性</th><th>类型</th><th>默认</th><th>说明</th></tr>
<tr><td><code>name</code></td><td><code>string</code></td><td>-</td><td>数据库名称</td></tr>
<tr><td><code>mode</code></td><td><code>'memory'|'disk'|'hybrid'</code></td><td><code>'hybrid'</code></td><td>存储模式</td></tr>
<tr><td><code>diskEngine</code></td><td><code>'indexeddb'|'opfs'</code></td><td><code>'indexeddb'</code></td><td>磁盘引擎</td></tr>
<tr><td><code>version</code></td><td><code>number</code></td><td><code>1</code></td><td>版本号</td></tr>
</table>
<h2 id="table">📋 定义表</h2>
<pre><span class="k">await</span> db.<span class="f">defineTable</span>(<span class="s">'users'</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">name</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">required</span>: <span class="k">true</span> },
<span class="s">email</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">unique</span>: <span class="k">true</span>, <span class="s">index</span>: <span class="k">true</span> },
<span class="s">age</span>: { <span class="s">type</span>: <span class="s">'number'</span>, <span class="s">default</span>: <span class="n">0</span> },
<span class="s">dept_id</span>: { <span class="s">type</span>: <span class="s">'number'</span>, <span class="s">references</span>: <span class="s">'departments.id'</span> },
});</pre>
<table>
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
<tr><td><code>type</code></td><td><code>string|number|boolean|date|json</code></td><td>数据类型</td></tr>
<tr><td><code>primaryKey</code></td><td><code>boolean</code></td><td>主键</td></tr>
<tr><td><code>required</code></td><td><code>boolean</code></td><td>必填</td></tr>
<tr><td><code>unique</code></td><td><code>boolean</code></td><td>唯一约束</td></tr>
<tr><td><code>index</code></td><td><code>boolean</code></td><td>创建索引</td></tr>
<tr><td><code>default</code></td><td><code>unknown</code></td><td>默认值</td></tr>
<tr><td><code>references</code></td><td><code>string</code></td><td>外键引用</td></tr>
</table>
<h2 id="sql-query">🔍 SQL 查询</h2>
<pre><span class="c">// INSERT</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"INSERT INTO users (id, name, age) VALUES ('1', 'Alice', 30)"</span>);
<span class="c">// SELECT</span>
<span class="k">const</span> rows = <span class="k">await</span> db.<span class="f">query</span>(<span class="s">'SELECT * FROM users WHERE age > 18 ORDER BY name LIMIT 10'</span>);
<span class="c">// UPDATE</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"UPDATE users SET age = 31 WHERE id = '1'"</span>);
<span class="c">// DELETE</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"DELETE FROM users WHERE id = '1'"</span>);
<span class="c">// DDL</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">'DROP TABLE users'</span>);</pre>
<h2 id="query-builder">⛓ Query Builder</h2>
<pre><span class="k">const</span> users = db.<span class="f">table</span>(<span class="s">'users'</span>);
<span class="c">// 插入</span>
<span class="k">await</span> users.<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'1'</span>, <span class="s">name</span>: <span class="s">'Alice'</span> });
<span class="k">await</span> users.<span class="f">insertMany</span>([...]);
<span class="c">// 查询</span>
<span class="k">const</span> result = <span class="k">await</span> users
.<span class="f">select</span>([<span class="s">'name'</span>, <span class="s">'age'</span>])
.<span class="f">where</span>({ <span class="s">age</span>: { <span class="s">$gt</span>: <span class="n">18</span> }, <span class="s">name</span>: { <span class="s">$like</span>: <span class="s">'A%'</span> } })
.<span class="f">orderBy</span>(<span class="s">'age'</span>, <span class="s">'desc'</span>)
.<span class="f">limit</span>(<span class="n">10</span>).<span class="f">offset</span>(<span class="n">0</span>)
.<span class="f">execute</span>();
<span class="c">// 更新/删除</span>
<span class="k">await</span> users.<span class="f">update</span>({ <span class="s">age</span>: <span class="n">31</span> }).<span class="f">where</span>({ <span class="s">id</span>: <span class="s">'1'</span> }).<span class="f">execute</span>();
<span class="k">await</span> users.<span class="f">delete</span>().<span class="f">where</span>({ <span class="s">id</span>: <span class="s">'1'</span> }).<span class="f">execute</span>();</pre>
<h2 id="join">🔗 JOIN 查询</h2>
<pre><span class="c">// SQL</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT u.name, d.name
FROM users u
INNER JOIN departments d ON u.dept_id = d.id
WHERE d.name = 'Engineering'`</span>);
<span class="c">// QueryBuilder</span>
<span class="k">await</span> db.<span class="f">table</span>(<span class="s">'users'</span>).<span class="f">select</span>()
.<span class="f">innerJoin</span>(<span class="s">'departments'</span>, { <span class="s">'users.dept_id'</span>: { <span class="s">$col</span>: <span class="s">'departments.id'</span> } })
.<span class="f">execute</span>();
<span class="c">// LEFT JOIN / RIGHT JOIN / CROSS JOIN</span>
.<span class="f">leftJoin</span>(<span class="s">'table'</span>, on)
.<span class="f">rightJoin</span>(<span class="s">'table'</span>, on)
.<span class="f">crossJoin</span>(<span class="s">'table'</span>)</pre>
<h2 id="groupby">📊 GROUP BY & 聚合</h2>
<pre><span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT dept, COUNT(*) as cnt, SUM(salary) as total
FROM employees
GROUP BY dept
HAVING COUNT(*) > 1
ORDER BY total DESC`</span>);
<span class="c">// 支持的聚合函数:COUNT, SUM, AVG, MIN, MAX</span>
<span class="c">// 支持别名:COUNT(*) AS cnt</span></pre>
<h2 id="transaction">🔒 事务</h2>
<pre><span class="k">await</span> db.<span class="f">transaction</span>(<span class="k">async</span> (trx) => {
<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="c">// 任何一步失败 → 全部回滚</span>
});</pre>
<h2 id="migration">🔄 数据迁移</h2>
<pre>db.<span class="f">addMigration</span>(<span class="n">2</span>, <span class="k">async</span> (db) => {
<span class="k">await</span> db.<span class="f">defineTable</span>(<span class="s">'products'</span>, { ... });
});
<span class="k">await</span> db.<span class="f">migrateTo</span>(<span class="n">2</span>); <span class="c">// 执行所有未执行的迁移</span></pre>
<h2 id="export">📤 导入导出</h2>
<pre><span class="c">// 导出单表</span>
<span class="k">const</span> data = <span class="k">await</span> db.<span class="f">exportTable</span>(<span class="s">'users'</span>);
<span class="c">// 导出全库</span>
<span class="k">const</span> all = <span class="k">await</span> db.<span class="f">exportAll</span>();
<span class="c">// 导入</span>
<span class="k">await</span> db.<span class="f">importTable</span>(<span class="s">'users'</span>, data);</pre>
<h2 id="plugin">🧩 插件钩子</h2>
<pre><span class="c">// 14 个生命周期钩子</span>
db.<span class="f">on</span>(<span class="s">'beforeInsert'</span>, <span class="k">async</span> (row) => {
<span class="f">console.log</span>(<span class="s">'即将插入:'</span>, row);
});
db.<span class="f">on</span>(<span class="s">'afterQuery'</span>, <span class="k">async</span> (sql, result) => {
<span class="f">console.log</span>(<span class="s">'查询完成:'</span>, sql);
});</pre>
<table>
<tr><th>钩子</th><th>触发时机</th></tr>
<tr><td><code>beforeCreateTable / afterCreateTable</code></td><td>创建表前后</td></tr>
<tr><td><code>beforeDropTable / afterDropTable</code></td><td>删除表前后</td></tr>
<tr><td><code>beforeInsert / afterInsert</code></td><td>插入前后</td></tr>
<tr><td><code>beforeUpdate / afterUpdate</code></td><td>更新前后</td></tr>
<tr><td><code>beforeDelete / afterDelete</code></td><td>删除前后</td></tr>
<tr><td><code>beforeQuery / afterQuery</code></td><td>查询前后</td></tr>
<tr><td><code>beforeTransaction / afterTransaction</code></td><td>事务前后</td></tr>
</table>
<h2 id="subscribe">📡 发布订阅</h2>
<pre><span class="c">// 订阅表变更</span>
<span class="k">const</span> unsubscribe = db.<span class="f">subscribe</span>(<span class="s">'users'</span>, (event) => {
<span class="c">// event: { type: 'insert'|'update'|'delete', row: {...} }</span>
});
<span class="c">// 取消订阅</span>
<span class="f">unsubscribe</span>();</pre>
<h2 id="types">🔷 TypeScript 泛型</h2>
<pre><span class="k">interface</span> <span class="t">User</span> {
<span class="s">id</span>: <span class="t">string</span>;
<span class="s">name</span>: <span class="t">string</span>;
<span class="s">age</span>: <span class="t">number</span>;
}
<span class="k">const</span> users = db.<span class="f">table</span>&lt;<span class="t">User</span>&gt;(<span class="s">'users'</span>);
<span class="k">await</span> users.<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'1'</span>, <span class="s">name</span>: <span class="s">'Alice'</span>, <span class="s">age</span>: <span class="n">30</span> }); <span class="c">// ✅ 类型安全</span></pre>
<h2 id="config">⚙️ 配置项</h2>
<pre><span class="k">import</span> { MetonaSqlark, MeSqlark } <span class="k">from</span> <span class="s">'metona-sqlark'</span>;
<span class="c">// MeSqlark 是 MetonaSqlark 的别名,完全等价</span></pre>
</main>
</div>
<script>
document.querySelectorAll('.sidebar a').forEach(a => {
a.addEventListener('click', function() {
document.querySelectorAll('.sidebar a').forEach(x => x.classList.remove('active'));
this.classList.add('active');
});
});
</script>
</body>
</html>