fix(site/v0.8.0): 站点版本同步 + 现场失败修复(file:// 打不开 OPFS 的可操作错误)
用户报告"站点演示失败了",实测复现并定位根因: - 现象:直接双击 site/demo.html(file://)→ 点「🌲 Aria」→ "❌ 数据库初始化失败: Failed to open AriaEngine database "demo""(Memory 正常)。 - 根因:file:// 属不透明来源,Chromium 拒绝 navigator.storage.getDirectory() 并抛 SecurityError;此时 isSecureContext 仍为 true、API 也存在,无法提前探测。 引擎把它包成 ARIA_OPEN_ERROR 时丢掉了底层错误 → 消息对用户不可操作。 - 修复:OPFSBackend.open() 显式检查并抛 ARIA_OPFS_UNAVAILABLE,消息给出两条出路 (用 http(s) 打开 / 改用 mode:'memory'),原始 SecurityError 挂 cause; site/demo.html 额外用中文说明"为什么失败 + 怎么修"。 顺带修掉一个更普遍的问题:DatabaseError 的第三个参数只进 details,err.cause 恒为 undefined,而文档/注释多处写"底层错误作为 cause 保留"。现在两者都成立 (details 语义不变;cause 声明为公开字段并接入标准错误链)。 站点版本同步:demo.html(title / 状态栏 / SQL 预置脚本 / console 日志)与 benchmark.html(title)此前仍是 v0.7.4(日志甚至是 v0.4.2)→ 统一 v0.8.0; docs.html 的 AriaEngine 版本演进列表补上 v0.8.0 条目、错误码表补 ARIA_OPFS_UNAVAILABLE;README 补"OPFS 需要 http(s) 页面"的浏览器兼容说明。 回归与门禁:tests/engine/aria-opfs-unavailable.test.ts(5 项,含正常环境正控); 变异 R19 / R20 均被拦住(总计 42/42);93 套件 / 1985 用例;覆盖率 90.59 / 82.61 / 94.14 / 93.50(阈值 90/82/94/93);e2e 14/14;lint + 两份 tsc 干净; dist 重建(251,731 B / gzip 63,431 B)并已同步全部体积宣称。
This commit is contained in:
+1
-1
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>⚡ 性能基准 — MetonaSqlark v0.7.4</title>
|
||||
<title>⚡ 性能基准 — MetonaSqlark v0.8.0</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='8' fill='%236366f1'/><text x='16' y='22' text-anchor='middle' font-size='20' fill='white'>◈</text></svg>">
|
||||
<style>
|
||||
:root {
|
||||
|
||||
+20
-7
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>🧪 在线演示 — MetonaSqlark v0.7.4</title>
|
||||
<title>🧪 在线演示 — MetonaSqlark v0.8.0</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='8' fill='%236366f1'/><text x='16' y='22' text-anchor='middle' font-size='20' fill='white'>◈</text></svg>">
|
||||
<style>
|
||||
:root {
|
||||
@@ -84,7 +84,7 @@
|
||||
<a href="demo.html" class="nav-active">演示</a>
|
||||
<a href="benchmark.html">基准</a>
|
||||
</nav>
|
||||
<div class="status"><span class="dot" id="engine-dot"></span> <span id="engine-status">Memory</span> 模式 — v0.7.4</div>
|
||||
<div class="status"><span class="dot" id="engine-dot"></span> <span id="engine-status">Memory</span> 模式 — v0.8.0</div>
|
||||
<button class="btn btn-preset" onclick="switchEngine('memory')" id="btn-memory" style="margin:6px 4px 6px 0;padding:6px 12px;">⚡ Memory</button>
|
||||
<button class="btn btn-preset" onclick="switchEngine('aria')" id="btn-aria" style="margin:6px 0;padding:6px 12px;color:#ec4899;border-color:#ec4899;">🌲 Aria</button>
|
||||
</header>
|
||||
@@ -92,9 +92,10 @@
|
||||
<div class="main">
|
||||
<div class="editor-panel">
|
||||
<div class="editor-area">
|
||||
<textarea id="sql-input" placeholder="输入 SQL 语句... SELECT * FROM users; INSERT INTO users VALUES ('4', 'Diana', 'diana@test.com', 28); SELECT u.name, o.amount FROM users u INNER JOIN orders o ON u.id = o.user_id;">-- 🚀 MetonaSqlark v0.7.4 在线演示
|
||||
<textarea id="sql-input" placeholder="输入 SQL 语句... SELECT * FROM users; INSERT INTO users VALUES ('4', 'Diana', 'diana@test.com', 28); SELECT u.name, o.amount FROM users u INNER JOIN orders o ON u.id = o.user_id;">-- 🚀 MetonaSqlark v0.8.0 在线演示
|
||||
-- 已预置 users / orders / products 表数据
|
||||
-- 新特性: ALTER TABLE · TRUNCATE TABLE · WAL同步 · MVCC · SQL注入防护
|
||||
-- 新特性 (v0.8.0): manifest 单一提交点 · WAL 记录级损坏上报 · 崩溃恢复报告
|
||||
-- 另有: ALTER TABLE · TRUNCATE TABLE · 参数化查询 · 三值逻辑 · 时间/大小写函数 · 外键级联 · 发布订阅
|
||||
|
||||
-- 查看所有数据
|
||||
SELECT * FROM users;
|
||||
@@ -185,6 +186,18 @@ async function initDB() {
|
||||
await seedDemoData(db);
|
||||
renderInfo(`✅ ${engine === 'aria' ? '🌲 AriaEngine(LSM-Tree + WAL + MVCC)' : '⚡ Memory'} 引擎已就绪`);
|
||||
} catch (e) {
|
||||
// v0.8.0:OPFS 在当前环境不可用(最常见:直接双击打开的 file:// 页面,Chromium
|
||||
// 禁止其访问 OPFS;此时 isSecureContext 仍为 true、API 也存在,只能调用才发现)
|
||||
// → 用中文说清"为什么失败 + 怎么修",而不是丢一句底层英文报错。
|
||||
if (e && e.code === 'ARIA_OPFS_UNAVAILABLE') {
|
||||
renderError(
|
||||
'Aria 引擎需要 OPFS,而当前页面环境不允许访问(' + location.protocol + ')。\n' +
|
||||
'· 原因:浏览器只允许 http(s) 页面使用 OPFS,file:// 直接打开会被拒绝。\n' +
|
||||
'· 解决:用本地服务器打开(仓库内执行 node tests/e2e/server.cjs 3344,' +
|
||||
'再访问 http://127.0.0.1:3344/site/demo.html),或点上面的「⚡ Memory」用内存模式体验。'
|
||||
);
|
||||
return;
|
||||
}
|
||||
renderError('数据库初始化失败: ' + e.message);
|
||||
}
|
||||
}
|
||||
@@ -555,9 +568,9 @@ SELECT COUNT(*) as total FROM temp_logs;
|
||||
|
||||
-- 清理
|
||||
DROP TABLE temp_logs;`,
|
||||
aria: `-- 🌲 AriaEngine 演示 (v0.7.4)
|
||||
aria: `-- 🌲 AriaEngine 演示 (v0.8.0)
|
||||
-- 点击右上角「🌲 Aria」按钮切换数据库引擎到 AriaEngine
|
||||
-- 当前数据库即运行在 Aria 引擎上(LSM-Tree · WAL 崩溃恢复 · MVCC · BloomFilter)
|
||||
-- 当前数据库即运行在 Aria 引擎上(LSM-Tree · WAL 崩溃恢复 · BloomFilter · 4KB 页面化)
|
||||
-- 基础 CRUD 与 Memory 引擎完全兼容
|
||||
|
||||
-- 当前引擎确认
|
||||
@@ -826,7 +839,7 @@ document.addEventListener('keydown', e => {
|
||||
document.getElementById('btn-aria').style.opacity = '0.6';
|
||||
document.getElementById('engine-status').textContent = '⚡ Memory';
|
||||
initDB().then(() => {
|
||||
console.log('✅ MetonaSqlark v0.4.2 demo ready');
|
||||
console.log('✅ MetonaSqlark v0.8.0 demo ready');
|
||||
setTimeout(runQuery, 300);
|
||||
}).catch(err => {
|
||||
renderError('初始化失败: ' + err.message);
|
||||
|
||||
+4
-2
@@ -140,7 +140,7 @@ npm install @metona-team/metona-sqlark</pre>
|
||||
<table>
|
||||
<tr><th>文件</th><th>格式</th><th>用途</th></tr>
|
||||
<tr><td><code>metona-sqlark.js</code></td><td>UMD</td><td>浏览器开发版(含 sourcemap)</td></tr>
|
||||
<tr><td><code>metona-sqlark.min.js</code></td><td>UMD (minified)</td><td>生产环境(实测 251,109 字节 / gzip 63,145 字节,含全部引擎与 SQL 层)</td></tr>
|
||||
<tr><td><code>metona-sqlark.min.js</code></td><td>UMD (minified)</td><td>生产环境(实测 251,731 字节 / gzip 63,431 字节,含全部引擎与 SQL 层)</td></tr>
|
||||
<tr><td><code>metona-sqlark.esm.js</code></td><td>ES Module</td><td>现代打包工具 / 浏览器 ESM</td></tr>
|
||||
<tr><td><code>metona-sqlark.cjs</code></td><td>CommonJS</td><td>Node.js require()</td></tr>
|
||||
<tr><td><code>metona-sqlark.d.ts</code></td><td>TypeScript 声明</td><td>类型提示</td></tr>
|
||||
@@ -815,6 +815,7 @@ db.<span class="f">broadcastChange</span>(<span class="s">'users'</span>);</pre>
|
||||
<strong>v0.7.2 语句级原子性 / 事务 DDL / 约束硬化</strong> — UPDATE 语句级两阶段原子(多行匹配第 N 行失败整句不执行 + 批内唯一互查)· 事务内 DDL 显式拒绝(四引擎对齐)· SET NULL 级联绕过 required 约束整体拒绝 · 参数绑定注释感知(注释中 `?`/引号不参与绑定)· 未闭合字符串显式 PARSE_ERROR · 未知 where 操作符抛 QUERY_ERROR(此前静默全匹配)· update undefined 语义化(保留旧值)· Hybrid write-through 失败补偿(磁盘失败自动重载内存对齐)。<br>
|
||||
<strong>v0.7.3 深度审计第六阶段:INSERT 原子 / 索引一致性 / 边界窗口</strong> — INSERT 语句级两阶段原子(三引擎 + Aria PK 批内重复,此前部分提交)· 索引列 IS NULL 恒空修复(Memory/KVStore/Hybrid,对齐 Aria)· delete RESTRICT 预检不再破坏索引 · queryStream 子查询静默空结果修复($subquery/$exists/$col 回退物化)· ALTER DROP 索引列残留清理 · CREATE UNIQUE INDEX 存量重复数据校验(失败原子回滚)· <code>SELECT *, col AS alias</code> 解析与投影 · WAL BEGIN/ROLLBACK 写失败窗口修复(事务不泄漏/数据不复活)· aria $in 批级预加载(消除逐值 drainChain 性能悬崖)· 多条件 AND 等值下推(索引真正生效,EXPLAIN 同步)· ANALYZE 统计含二级索引 · React/Vue hooks 生命周期修复(config 变更重建 / 卸载关闭)· 迁移无主键旧库兜底 · 1256 测试 75 套件 · 90.0% 行覆盖率。<br>
|
||||
<strong>v0.7.4 深度审计第七阶段:写语句子查询 / 约束硬化 / 真惰性流式</strong> — UPDATE/DELETE WHERE 子查询正确执行(四引擎,此前静默 0 行;关联引用显式 NOT_SUPPORTED;EXPLAIN 估算同步修复)· 主键 NULL/undefined 强制拒绝(SQL 语义 PK 隐含 NOT NULL,此前静默生成 "null"/"undefined" 主键)· DROP INDEX 保留建表 UNIQUE 约束(对齐 SQLite:需重建表解除;仅 CREATE UNIQUE INDEX 添加的可随索引删除)· GROUP BY / DISTINCT / UNION 键类型安全编码(null 与 'null' 字符串不再合并)· UPDATE 未知列显式 COLUMN_NOT_FOUND(此前脏列写入存储行)· queryStream 多语句显式 PARSE_ERROR(此前静默忽略后续语句)· KVStore 后台错误跨 reopen 清理 + Hybrid beginTransaction 失败补偿回滚 · <strong>Aria findStream 真惰性</strong>(MergeIterator 迭代器化 + SSTable/MemTable 生成器扫描,limit 提前终止,大表流式内存 O(1)——此前内部 drain 全量物化)· REINDEX 单次全表扫描重建全部索引列(此前每列一次全扫描)· RB-Tree 删除双黑修复边界 + LSM 死代码清理 · 1304 测试 76 套件 · 90.1% 行覆盖率。</p>
|
||||
<strong>v0.8.0 根治性迭代:统一语义 / 消灭复发结构 / 验证基础设施</strong> — 三份行校验实现收敛为唯一 choke point(未知列/NaN 显式拒绝)· 唯一值比较与编码原语 · SQL 与 TABLE API 单管线(QueryBuilder 只产 AST)· CASE 表达式改 token 流递归下降 · 输出列序号与分隔标识符 · <strong>`__aria_manifest_<generation>` 单一提交点</strong>(页面水位 + SSTable 元数据 + 表结构 + WAL 水位 + 冻结表意图一次原子提交;头部/载荷双 CRC、先写后验、保留两代;顺序固定为<strong>数据落盘 → manifest 提交 → 才允许截断 WAL / 删除旧文件</strong>)· 元数据损坏不再静默空库(`ARIA_MANIFEST_CORRUPT` / `ARIA_LEGACY_META_CORRUPT`)· WAL LSN 全库单调 + 按水位删除分片 + 空洞与记录级损坏<strong>如实上报</strong>(`ARIA_WAL_GAP` / `droppedWALRecords`)· LSM 冻结表一等状态、compaction 不再摘整层、底部层原地合并回收墓碑、按层 `compacting`、退休表 + 读者 epoch · 读路径"快照 + 结构版本乐观重试"(删除全部 prefetch 依赖)· checkpoint 不等 compaction(根治 "8~11s 悬崖")· 介质读故障与"文件不存在"分离(`ARIA_SSTABLE_READ_FAILED`,不误删元数据)· 恢复报告 `getRecoveryReport()` · 覆盖率门禁 + 变异验证(40 项)成为标准做法 ✅ v0.8.0
|
||||
|
||||
<h3>存储模式对比</h3>
|
||||
<table>
|
||||
@@ -932,7 +933,8 @@ db.<span class="f">broadcastChange</span>(<span class="s">'users'</span>);</pre>
|
||||
<tr><td><code>ARIA_MANIFEST_NOT_LOADED</code></td><td>未 <code>load()</code> 就提交 manifest:拒绝写坏介质 🆕 v0.8.0</td></tr>
|
||||
<tr><td><code>ARIA_SSTABLE_SAVE_CONTRACT</code></td><td>存储实现违反 <code>save()</code> 契约(既不抛错也不返回结果):不把"写调用返回了"当成落盘成功 🆕 v0.8.0</td></tr>
|
||||
<tr><td><code>ARIA_DB_NOT_OPEN</code></td><td>在 <code>open()</code> 之前使用 Aria 存储层(如加密后端) 🆕 v0.8.0</td></tr>
|
||||
<tr><td><code>ARIA_OPEN_ERROR</code></td><td>AriaEngine 打开失败(底层错误作为 cause 保留) 🆕 v0.8.0</td></tr>
|
||||
<tr><td><code>ARIA_OPEN_ERROR</code></td><td>AriaEngine 打开失败(底层错误保留在 <code>err.details</code> 与标准 <code>err.cause</code> 上,便于定位根因) 🆕 v0.8.0</td></tr>
|
||||
<tr><td><code>ARIA_OPFS_UNAVAILABLE</code></td><td>OPFS 在当前环境不可用(如 <code>file://</code> 页面被 Chromium 禁止访问 OPFS):错误信息给出可操作建议(改用 http(s) 或 <code>mode:'memory'</code>) 🆕 v0.8.0</td></tr>
|
||||
<tr><td><code>DB_NOT_OPEN</code></td><td>数据库未打开(KVStoreEngine 操作前未 <code>open()</code>)</td></tr>
|
||||
<tr><td><code>KV_SCHEMA_ERROR</code></td><td>KVStore 中的表结构记录损坏</td></tr>
|
||||
<tr><td><code>KV_LOG_ERROR</code> / <code>KV_BACKGROUND_ERROR</code></td><td>KVStore 日志写入失败 / 后台刷盘失败</td></tr>
|
||||
|
||||
+4
-4
@@ -153,7 +153,7 @@
|
||||
<!-- Hero -->
|
||||
<section class="hero">
|
||||
<div class="container">
|
||||
<div class="badge" style="margin-bottom:24px;"><span class="dot"></span> v0.8.0 根治性迭代 — 1980 测试 92 套件 · 语句/分支/函数/行覆盖率 90.59% / 82.59% / 94.14% / 93.50% · UPDATE/DELETE 子查询正确执行 · 主键非空强制 · DROP INDEX 保留 UNIQUE 约束 · findStream 真惰性 · 参数化查询 · 自研 KVStore 事务引擎 · AriaEngine LSM+WAL+MVCC · 崩溃恢复</div>
|
||||
<div class="badge" style="margin-bottom:24px;"><span class="dot"></span> v0.8.0 根治性迭代 — 1985 测试 93 套件 · 语句/分支/函数/行覆盖率 90.59% / 82.61% / 94.14% / 93.50% · UPDATE/DELETE 子查询正确执行 · 主键非空强制 · DROP INDEX 保留 UNIQUE 约束 · findStream 真惰性 · 参数化查询 · 自研 KVStore 事务引擎 · AriaEngine LSM+WAL+MVCC · 崩溃恢复</div>
|
||||
<h1>前端的 <span class="gradient-text">SQL 数据库</span></h1>
|
||||
<p>TypeScript 原生构建,4 种存储模式 + 3 种存储后端,支持完整 SQL 查询。<br>零运行时依赖,开箱即用。AriaEngine 自研引擎:LSM-Tree + WAL 同步 + MVCC。</p>
|
||||
<div class="actions">
|
||||
@@ -279,7 +279,7 @@ npm install @metona-team/metona-sqlark
|
||||
<div class="feature-card">
|
||||
<div class="icon">📦</div>
|
||||
<h3>零运行时依赖</h3>
|
||||
<p>纯 TypeScript 实现,不依赖任何第三方库。UMD/ESM/CJS 多格式输出,min 产物实测 251,109 字节 / gzip 63,145 字节。</p>
|
||||
<p>纯 TypeScript 实现,不依赖任何第三方库。UMD/ESM/CJS 多格式输出,min 产物实测 251,731 字节 / gzip 63,431 字节。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">📊</div>
|
||||
@@ -414,12 +414,12 @@ npm install @metona-team/metona-sqlark
|
||||
<p>MetonaSqlark 的核心指标</p>
|
||||
</div>
|
||||
<div class="stats">
|
||||
<div class="stat-card"><div class="num">1980</div><div class="label">测试用例(+14 e2e)</div></div>
|
||||
<div class="stat-card"><div class="num">1985</div><div class="label">测试用例(+14 e2e)</div></div>
|
||||
<div class="stat-card"><div class="num">93.50%</div><div class="label">行覆盖率</div></div>
|
||||
<div class="stat-card"><div class="num">63KB</div><div class="label">gzip 体积</div></div>
|
||||
<div class="stat-card"><div class="num">4+3</div><div class="label">存储模式+后端</div></div>
|
||||
<div class="stat-card"><div class="num">72</div><div class="label">SQL 关键字</div></div>
|
||||
<div class="stat-card"><div class="num">91</div><div class="label">测试套件</div></div>
|
||||
<div class="stat-card"><div class="num">93</div><div class="label">测试套件</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user