@tensorix/metahub 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/dist/cli.js +769 -79
- package/dist/cli.js.map +17 -13
- package/dist/core/databases.d.ts.map +1 -1
- package/dist/core/documents.d.ts.map +1 -1
- package/dist/core/integrity.d.ts +32 -0
- package/dist/core/integrity.d.ts.map +1 -0
- package/dist/core/properties.d.ts.map +1 -1
- package/dist/core/search.d.ts +2 -0
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/snapshot.d.ts +2 -0
- package/dist/core/snapshot.d.ts.map +1 -1
- package/dist/core/sync/server.d.ts +14 -0
- package/dist/core/sync/server.d.ts.map +1 -1
- package/dist/core/sync/webui-routes.d.ts.map +1 -1
- package/dist/core/sync/webui.d.ts +11 -0
- package/dist/core/sync/webui.d.ts.map +1 -1
- package/dist/index.js +290 -60
- package/dist/index.js.map +9 -9
- package/dist/webui.js +33 -33
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -531,9 +531,14 @@ var init_sites = __esm(() => {
|
|
|
531
531
|
// src/core/sync/webui.ts
|
|
532
532
|
var exports_webui = {};
|
|
533
533
|
__export(exports_webui, {
|
|
534
|
+
warmWebui: () => warmWebui,
|
|
535
|
+
setWebuiBundle: () => setWebuiBundle,
|
|
534
536
|
serveWebui: () => serveWebui
|
|
535
537
|
});
|
|
536
538
|
import { fileURLToPath } from "url";
|
|
539
|
+
function setWebuiBundle(js) {
|
|
540
|
+
cachedJs = js;
|
|
541
|
+
}
|
|
537
542
|
async function getJs() {
|
|
538
543
|
if (cachedJs != null)
|
|
539
544
|
return cachedJs;
|
|
@@ -549,6 +554,11 @@ async function getJs() {
|
|
|
549
554
|
cachedJs = await res.outputs[0].text();
|
|
550
555
|
return cachedJs;
|
|
551
556
|
}
|
|
557
|
+
async function warmWebui() {
|
|
558
|
+
try {
|
|
559
|
+
await getJs();
|
|
560
|
+
} catch {}
|
|
561
|
+
}
|
|
552
562
|
async function serveWebui(req) {
|
|
553
563
|
if (req.method !== "GET")
|
|
554
564
|
return null;
|
|
@@ -575,7 +585,13 @@ var HTML = `<!doctype html>
|
|
|
575
585
|
<title>Metahub</title>
|
|
576
586
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
577
587
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
578
|
-
|
|
588
|
+
<!-- Load web fonts without blocking first paint: media="print" keeps the
|
|
589
|
+
stylesheet non-render-blocking, then onload flips it to "all". The page
|
|
590
|
+
paints instantly with the system-font fallbacks baked into --ui/--mono and
|
|
591
|
+
swaps to the web fonts when they arrive (or stays on system fonts offline).
|
|
592
|
+
This is what kept the desktop cold-start white for seconds on slow networks. -->
|
|
593
|
+
<link rel="stylesheet" media="print" onload="this.media='all'" href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap">
|
|
594
|
+
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"></noscript>
|
|
579
595
|
<script>try{document.documentElement.dataset.theme=localStorage.getItem('mh-theme')||'system'}catch(e){}</script>
|
|
580
596
|
<style>
|
|
581
597
|
:root {
|
|
@@ -685,6 +701,9 @@ var HTML = `<!doctype html>
|
|
|
685
701
|
.set-section { margin-top:34px; }
|
|
686
702
|
.set-section-head { font-size:12px; font-weight:600; text-transform:uppercase; letter-spacing:.07em; color:var(--muted); margin-bottom:4px; }
|
|
687
703
|
.set-section-desc { color:var(--fg-soft); margin-bottom:16px; }
|
|
704
|
+
.set-footer { margin-top:40px; padding-top:20px; border-top:1px solid var(--line); display:flex;
|
|
705
|
+
justify-content:center; gap:8px; color:var(--muted); font-size:12px; }
|
|
706
|
+
.set-footer-sep { opacity:.5; }
|
|
688
707
|
.theme-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:12px; }
|
|
689
708
|
.theme-card { position:relative; display:flex; flex-direction:column; align-items:flex-start; gap:10px;
|
|
690
709
|
text-align:left; padding:16px; border:1px solid var(--line-strong); border-radius:var(--radius);
|
|
@@ -722,6 +741,7 @@ var HTML = `<!doctype html>
|
|
|
722
741
|
/* main */
|
|
723
742
|
.main { flex:1; display:flex; flex-direction:column; overflow:hidden; min-width:0; }
|
|
724
743
|
.topbar { display:flex; align-items:center; gap:6px; padding:10px 18px; border-bottom:1px solid var(--line); min-height:49px; }
|
|
744
|
+
.topbar.bare { border-bottom:0; }
|
|
725
745
|
.topbar .hamburger { display:none; }
|
|
726
746
|
.topbar .hamburger.show-collapsed { display:grid; }
|
|
727
747
|
.sidebar.collapsed { overflow:hidden; }
|
|
@@ -1046,7 +1066,71 @@ var HTML = `<!doctype html>
|
|
|
1046
1066
|
@keyframes slidein { from { opacity:0; transform:translateX(-12px); } }
|
|
1047
1067
|
|
|
1048
1068
|
/* misc */
|
|
1049
|
-
.empty { display:
|
|
1069
|
+
.empty { display:flex; flex-direction:column; align-items:center; justify-content:center; height:100%;
|
|
1070
|
+
color:var(--muted); text-align:center; padding:40px; }
|
|
1071
|
+
|
|
1072
|
+
/* ---- empty state: \u77E5\u8BC6\u751F\u957F\u63D2\u753B ---- */
|
|
1073
|
+
.estate-art { width:240px; height:208px; overflow:visible; animation:idleFloat 5s ease-in-out 1.6s infinite; }
|
|
1074
|
+
.estate-art .stroke-a { fill:none; stroke:var(--accent); stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; }
|
|
1075
|
+
.estate-art .stroke { fill:none; stroke:var(--line-strong); stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; }
|
|
1076
|
+
.estate-art .fill-a { fill:var(--accent); }
|
|
1077
|
+
.estate-art .fill-soft { fill:var(--accent-soft); }
|
|
1078
|
+
.estate-art .fill-surface { fill:var(--bg); }
|
|
1079
|
+
.estate-art .ground { fill:var(--accent); }
|
|
1080
|
+
.estate-title { margin:28px 0 0; font-size:18px; font-weight:600; letter-spacing:.2px; color:var(--fg);
|
|
1081
|
+
opacity:0; animation:fadeUp .5s ease forwards 1.0s; }
|
|
1082
|
+
.estate-hint { margin:9px 0 0; font-size:13.5px; line-height:1.6; color:var(--muted); max-width:300px;
|
|
1083
|
+
opacity:0; animation:fadeUp .5s ease forwards 1.15s; }
|
|
1084
|
+
.estate-link { margin:18px 0 0; font-size:14px; font-weight:600; color:var(--accent);
|
|
1085
|
+
display:inline-flex; align-items:center; gap:6px; padding:9px 8px; border-radius:var(--radius-sm);
|
|
1086
|
+
opacity:0; animation:fadeUp .5s ease forwards 1.3s; transition:opacity .15s; }
|
|
1087
|
+
.estate-link:hover { opacity:.7; }
|
|
1088
|
+
/* intro */
|
|
1089
|
+
.draw { stroke-dasharray:100; stroke-dashoffset:100; }
|
|
1090
|
+
@keyframes draw { to { stroke-dashoffset:0; } }
|
|
1091
|
+
@keyframes leafGrow { from { opacity:0; transform:scale(.2) rotate(-10deg); } to { opacity:1; transform:scale(1) rotate(0); } }
|
|
1092
|
+
@keyframes cardUp { from { opacity:0; transform:translateY(16px) scale(.96); } to { opacity:1; transform:translateY(0) scale(1); } }
|
|
1093
|
+
@keyframes groundIn { to { opacity:.1; } }
|
|
1094
|
+
@keyframes glowIn { to { opacity:.6; } }
|
|
1095
|
+
@keyframes fadeUp { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:translateY(0); } }
|
|
1096
|
+
/* idle loops \u2014 each 0% equals the settled rest state to avoid a jump */
|
|
1097
|
+
@keyframes idleFloat { 0%,100% { transform:translateY(0); } 50% { transform:translateY(-5px); } }
|
|
1098
|
+
@keyframes plantSway { 0%,100% { transform:rotate(0deg); } 25% { transform:rotate(-1.3deg); } 75% { transform:rotate(1.3deg); } }
|
|
1099
|
+
@keyframes leafSwayA { 0%,100% { transform:rotate(0deg); } 25% { transform:rotate(-2.6deg); } 75% { transform:rotate(2.6deg); } }
|
|
1100
|
+
@keyframes leafSwayB { 0%,100% { transform:rotate(0deg); } 25% { transform:rotate(2.6deg); } 75% { transform:rotate(-2.6deg); } }
|
|
1101
|
+
@keyframes groundBreathe { 0%,100% { transform:scale(1); opacity:.10; } 50% { transform:scale(1.07); opacity:.15; } }
|
|
1102
|
+
@keyframes glowPulse { 0%,100% { transform:scale(1); opacity:.6; } 50% { transform:scale(1.1); opacity:.85; } }
|
|
1103
|
+
@keyframes particle { 0% { opacity:0; transform:translate(0,0) scale(.4); } 35% { opacity:.85; transform:translate(2px,-4px) scale(1); } 70% { opacity:0; transform:translate(4px,-9px) scale(.5); } 100% { opacity:0; transform:translate(0,0) scale(.4); } }
|
|
1104
|
+
/* wiring */
|
|
1105
|
+
.kg-ground { transform-box:fill-box; transform-origin:center; opacity:0;
|
|
1106
|
+
animation:groundIn .5s ease forwards .1s, groundBreathe 6s ease-in-out 1.6s infinite; }
|
|
1107
|
+
.kg-glow { filter:blur(9px); transform-box:fill-box; transform-origin:center; opacity:0;
|
|
1108
|
+
animation:glowIn .6s ease forwards .1s, glowPulse 4.6s ease-in-out 1.6s infinite; }
|
|
1109
|
+
.kg-cardB { transform-box:fill-box; transform-origin:center; opacity:0; animation:cardUp .55s cubic-bezier(.2,.8,.2,1) forwards .2s; }
|
|
1110
|
+
.kg-cardA { transform-box:fill-box; transform-origin:center; opacity:0; animation:cardUp .55s cubic-bezier(.2,.8,.2,1) forwards .32s; }
|
|
1111
|
+
.kg-plant { transform-box:fill-box; transform-origin:50% 100%; animation:plantSway 6s ease-in-out 1.7s infinite; }
|
|
1112
|
+
.kg-stem { animation:draw .9s ease-out forwards .4s; }
|
|
1113
|
+
.kg-leaf { transform-box:fill-box; }
|
|
1114
|
+
.kg-grow { transform-box:fill-box; opacity:0; }
|
|
1115
|
+
.kg-leaf.l1 { transform-origin:90% 100%; animation:leafSwayA 4.6s ease-in-out 1.7s infinite; }
|
|
1116
|
+
.kg-leaf.l2 { transform-origin:0% 80%; animation:leafSwayB 5.2s ease-in-out 1.8s infinite; }
|
|
1117
|
+
.kg-leaf.l3 { transform-origin:0% 100%; animation:leafSwayA 5.0s ease-in-out 1.9s infinite; }
|
|
1118
|
+
.kg-leaf.l1 .kg-grow { transform-origin:90% 100%; animation:leafGrow .52s cubic-bezier(.18,.9,.24,1.22) forwards .85s; }
|
|
1119
|
+
.kg-leaf.l2 .kg-grow { transform-origin:0% 80%; animation:leafGrow .52s cubic-bezier(.18,.9,.24,1.22) forwards 1.0s; }
|
|
1120
|
+
.kg-leaf.l3 .kg-grow { transform-origin:0% 100%; animation:leafGrow .52s cubic-bezier(.18,.9,.24,1.22) forwards 1.15s; }
|
|
1121
|
+
.kg-particle { transform-box:fill-box; transform-origin:center; opacity:0; }
|
|
1122
|
+
.kg-particle.p1 { animation:particle 4.5s ease-in-out 1.6s infinite; }
|
|
1123
|
+
.kg-particle.p2 { animation:particle 5.2s ease-in-out 2.0s infinite; }
|
|
1124
|
+
.kg-particle.p3 { animation:particle 4.8s ease-in-out 2.6s infinite; }
|
|
1125
|
+
.kg-particle.p4 { animation:particle 5.6s ease-in-out 3.1s infinite; }
|
|
1126
|
+
.kg-particle.p5 { animation:particle 4.2s ease-in-out 2.3s infinite; }
|
|
1127
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1128
|
+
.estate-art, .estate-art *, .estate-title, .estate-hint, .estate-link { animation:none !important; opacity:1 !important; transform:none !important; }
|
|
1129
|
+
.draw { stroke-dashoffset:0 !important; }
|
|
1130
|
+
.kg-ground { opacity:.1 !important; }
|
|
1131
|
+
.kg-glow { opacity:.6 !important; }
|
|
1132
|
+
.kg-particle { opacity:0 !important; }
|
|
1133
|
+
}
|
|
1050
1134
|
.error-bar { margin:12px 24px; padding:10px 14px; background:var(--danger-soft); color:var(--danger); border-radius:var(--radius-sm); font-size:13px; cursor:pointer; }
|
|
1051
1135
|
.view-placeholder { padding:40px; text-align:center; border:1px dashed var(--line-strong); border-radius:var(--radius); color:var(--muted); margin-top:8px; }
|
|
1052
1136
|
.ico.flip { transform:scaleX(-1); }
|
|
@@ -1426,9 +1510,20 @@ function deleteDatabase(db, id) {
|
|
|
1426
1510
|
if (!getDatabase(db, id))
|
|
1427
1511
|
return false;
|
|
1428
1512
|
emit(db, "databases", id, "__deleted", 1);
|
|
1513
|
+
const tombstone = (table) => {
|
|
1514
|
+
for (const r of liveChildren(db, table, "database_id", id))
|
|
1515
|
+
emit(db, table, r, "__deleted", 1);
|
|
1516
|
+
};
|
|
1517
|
+
tombstone("properties");
|
|
1518
|
+
tombstone("records");
|
|
1519
|
+
for (const d of liveChildren(db, "documents", "database_id", id))
|
|
1520
|
+
emit(db, "documents", d, "database_id", null);
|
|
1429
1521
|
db.query("DELETE FROM meta WHERE key = 'current_db' AND value = ?").run(id);
|
|
1430
1522
|
return true;
|
|
1431
1523
|
}
|
|
1524
|
+
function liveChildren(db, table, col, parentId) {
|
|
1525
|
+
return db.query(`SELECT id FROM ${table} WHERE ${col} = ? AND __deleted = 0`).all(parentId).map((r) => r.id);
|
|
1526
|
+
}
|
|
1432
1527
|
|
|
1433
1528
|
// src/core/properties.ts
|
|
1434
1529
|
init_ids();
|
|
@@ -1557,9 +1652,13 @@ function setPropertyWidth(db, id, width) {
|
|
|
1557
1652
|
return updateProperty(db, id, { config: { ...cur.config ?? {}, width: w } });
|
|
1558
1653
|
}
|
|
1559
1654
|
function removeProperty(db, id) {
|
|
1560
|
-
|
|
1655
|
+
const prop = getProperty(db, id);
|
|
1656
|
+
if (!prop)
|
|
1561
1657
|
return false;
|
|
1562
1658
|
emit(db, "properties", id, "__deleted", 1);
|
|
1659
|
+
const rows = db.query("SELECT id FROM records WHERE database_id = ? AND __deleted = 0 AND data ->> ? IS NOT NULL").all(prop.database_id, id);
|
|
1660
|
+
for (const r of rows)
|
|
1661
|
+
emit(db, "records", r.id, id, undefined);
|
|
1563
1662
|
return true;
|
|
1564
1663
|
}
|
|
1565
1664
|
|
|
@@ -2225,8 +2324,15 @@ function deleteDocument(db, id) {
|
|
|
2225
2324
|
if (!getDocument(db, id))
|
|
2226
2325
|
return false;
|
|
2227
2326
|
emit(db, "documents", id, "__deleted", 1);
|
|
2327
|
+
for (const b of liveChildIds(db, "doc_blocks", "doc_id", id))
|
|
2328
|
+
emit(db, "doc_blocks", b, "__deleted", 1);
|
|
2329
|
+
for (const c of liveChildIds(db, "documents", "parent_id", id))
|
|
2330
|
+
emit(db, "documents", c, "parent_id", null);
|
|
2228
2331
|
return true;
|
|
2229
2332
|
}
|
|
2333
|
+
function liveChildIds(db, table, col, parentId) {
|
|
2334
|
+
return db.query(`SELECT id FROM ${table} WHERE ${col} = ? AND __deleted = 0`).all(parentId).map((r) => r.id);
|
|
2335
|
+
}
|
|
2230
2336
|
function countOccurrences(haystack, needle) {
|
|
2231
2337
|
if (needle === "")
|
|
2232
2338
|
return 0;
|
|
@@ -2239,26 +2345,112 @@ function countOccurrences(haystack, needle) {
|
|
|
2239
2345
|
return n;
|
|
2240
2346
|
}
|
|
2241
2347
|
// src/core/search.ts
|
|
2348
|
+
init_crdt();
|
|
2242
2349
|
var TEXT_TYPES = "('text','url','select','multi_select','date')";
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2350
|
+
var SEARCH_INDEX_VERSION = "1";
|
|
2351
|
+
function readMeta(db, key) {
|
|
2352
|
+
return db.query("SELECT value FROM meta WHERE key = ?").get(key)?.value ?? null;
|
|
2353
|
+
}
|
|
2354
|
+
function writeMeta(db, key, value) {
|
|
2355
|
+
db.query("INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(key, value);
|
|
2356
|
+
}
|
|
2357
|
+
function reindexDocument(db, id) {
|
|
2358
|
+
db.query("DELETE FROM search_fts WHERE kind = 'document' AND id = ?").run(id);
|
|
2359
|
+
const d = db.query("SELECT id, title, body, database_id FROM documents WHERE id = ? AND __deleted = 0").get(id);
|
|
2360
|
+
if (d)
|
|
2252
2361
|
db.query("INSERT INTO search_fts (kind, id, database_id, title, body) VALUES ('document', ?, ?, ?, ?)").run(d.id, d.database_id, d.title ?? "", d.body ?? "");
|
|
2253
|
-
|
|
2254
|
-
|
|
2362
|
+
}
|
|
2363
|
+
function recordBodyRows(db, where, ...args) {
|
|
2364
|
+
return db.query(`SELECT r.id AS id, r.database_id AS database_id, group_concat(je.value, ' ') AS body
|
|
2255
2365
|
FROM records r, json_each(r.data) je
|
|
2256
2366
|
JOIN properties p ON p.id = je.key AND p.__deleted = 0
|
|
2257
|
-
WHERE r.__deleted = 0 AND p.type IN ${TEXT_TYPES}
|
|
2258
|
-
GROUP BY r.id`).all()
|
|
2259
|
-
|
|
2367
|
+
WHERE ${where} AND r.__deleted = 0 AND p.type IN ${TEXT_TYPES}
|
|
2368
|
+
GROUP BY r.id`).all(...args);
|
|
2369
|
+
}
|
|
2370
|
+
function insertRecordRow(db, r) {
|
|
2371
|
+
db.query("INSERT INTO search_fts (kind, id, database_id, title, body) VALUES ('record', ?, ?, ?, ?)").run(r.id, r.database_id, r.id, r.body ?? "");
|
|
2372
|
+
}
|
|
2373
|
+
function reindexRecord(db, id) {
|
|
2374
|
+
db.query("DELETE FROM search_fts WHERE kind = 'record' AND id = ?").run(id);
|
|
2375
|
+
const r = recordBodyRows(db, "r.id = ?", id)[0];
|
|
2376
|
+
if (r)
|
|
2377
|
+
insertRecordRow(db, r);
|
|
2378
|
+
}
|
|
2379
|
+
function reindexDatabaseRecords(db, databaseId) {
|
|
2380
|
+
db.query("DELETE FROM search_fts WHERE kind = 'record' AND database_id = ?").run(databaseId);
|
|
2381
|
+
for (const r of recordBodyRows(db, "r.database_id = ?", databaseId))
|
|
2382
|
+
insertRecordRow(db, r);
|
|
2383
|
+
}
|
|
2384
|
+
function fullRebuild(db) {
|
|
2385
|
+
db.query("DELETE FROM search_fts").run();
|
|
2386
|
+
for (const d of db.query("SELECT id, title, body, database_id FROM documents WHERE __deleted = 0").all())
|
|
2387
|
+
db.query("INSERT INTO search_fts (kind, id, database_id, title, body) VALUES ('document', ?, ?, ?, ?)").run(d.id, d.database_id, d.title ?? "", d.body ?? "");
|
|
2388
|
+
for (const r of recordBodyRows(db, "1 = 1"))
|
|
2389
|
+
insertRecordRow(db, r);
|
|
2390
|
+
const max = db.query("SELECT MAX(rowid) AS m FROM crdt_changes").get().m ?? 0;
|
|
2391
|
+
writeMeta(db, "search_seq", String(max));
|
|
2392
|
+
writeMeta(db, "search_index_version", SEARCH_INDEX_VERSION);
|
|
2393
|
+
}
|
|
2394
|
+
function incrementalUpdate(db) {
|
|
2395
|
+
const cursor = Number(readMeta(db, "search_seq") ?? "0");
|
|
2396
|
+
const { changes, cursor: next } = changesAfterSeq(db, cursor);
|
|
2397
|
+
if (changes.length === 0)
|
|
2398
|
+
return;
|
|
2399
|
+
const docIds = new Set;
|
|
2400
|
+
const recordIds = new Set;
|
|
2401
|
+
const dbIds = new Set;
|
|
2402
|
+
for (const c of changes) {
|
|
2403
|
+
switch (c.dataset) {
|
|
2404
|
+
case "documents":
|
|
2405
|
+
docIds.add(c.row_id);
|
|
2406
|
+
break;
|
|
2407
|
+
case "doc_blocks": {
|
|
2408
|
+
const blk = db.query("SELECT doc_id FROM doc_blocks WHERE id = ?").get(c.row_id);
|
|
2409
|
+
if (blk?.doc_id)
|
|
2410
|
+
docIds.add(blk.doc_id);
|
|
2411
|
+
break;
|
|
2412
|
+
}
|
|
2413
|
+
case "records":
|
|
2414
|
+
recordIds.add(c.row_id);
|
|
2415
|
+
break;
|
|
2416
|
+
case "properties":
|
|
2417
|
+
if (c.col === "type" || c.col === "__deleted" || c.col === "database_id") {
|
|
2418
|
+
const p = db.query("SELECT database_id FROM properties WHERE id = ?").get(c.row_id);
|
|
2419
|
+
if (p?.database_id)
|
|
2420
|
+
dbIds.add(p.database_id);
|
|
2421
|
+
}
|
|
2422
|
+
break;
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
for (const id of docIds)
|
|
2426
|
+
reindexDocument(db, id);
|
|
2427
|
+
for (const dbId of dbIds)
|
|
2428
|
+
reindexDatabaseRecords(db, dbId);
|
|
2429
|
+
for (const id of recordIds) {
|
|
2430
|
+
const r = db.query("SELECT database_id FROM records WHERE id = ?").get(id);
|
|
2431
|
+
if (r?.database_id && dbIds.has(r.database_id))
|
|
2432
|
+
continue;
|
|
2433
|
+
reindexRecord(db, id);
|
|
2260
2434
|
}
|
|
2261
|
-
db
|
|
2435
|
+
writeMeta(db, "search_seq", String(next));
|
|
2436
|
+
}
|
|
2437
|
+
function ensureIndex(db) {
|
|
2438
|
+
if (!ftsAvailable(db))
|
|
2439
|
+
return false;
|
|
2440
|
+
const version = readMeta(db, "search_index_version");
|
|
2441
|
+
const seq = readMeta(db, "search_seq");
|
|
2442
|
+
db.transaction(() => {
|
|
2443
|
+
if (version !== SEARCH_INDEX_VERSION || seq === null)
|
|
2444
|
+
fullRebuild(db);
|
|
2445
|
+
else
|
|
2446
|
+
incrementalUpdate(db);
|
|
2447
|
+
})();
|
|
2448
|
+
return true;
|
|
2449
|
+
}
|
|
2450
|
+
function rebuildSearchIndex(db) {
|
|
2451
|
+
if (!ftsAvailable(db))
|
|
2452
|
+
return false;
|
|
2453
|
+
db.transaction(() => fullRebuild(db))();
|
|
2262
2454
|
return true;
|
|
2263
2455
|
}
|
|
2264
2456
|
function ftsSearch(db, query, limit) {
|
|
@@ -16772,7 +16964,7 @@ function getServerConfig(db) {
|
|
|
16772
16964
|
// package.json
|
|
16773
16965
|
var package_default = {
|
|
16774
16966
|
name: "@tensorix/metahub",
|
|
16775
|
-
version: "0.1.
|
|
16967
|
+
version: "0.1.4",
|
|
16776
16968
|
description: "Metahub core library and CLI",
|
|
16777
16969
|
type: "module",
|
|
16778
16970
|
main: "./dist/index.js",
|
|
@@ -16798,7 +16990,8 @@ var package_default = {
|
|
|
16798
16990
|
build: "bun run scripts/build.ts",
|
|
16799
16991
|
"build:binaries": "bun run scripts/compile-binaries.ts",
|
|
16800
16992
|
prepublishOnly: "bun run build",
|
|
16801
|
-
test: "bun test"
|
|
16993
|
+
test: "bun test",
|
|
16994
|
+
release: "bun run scripts/tag-release.ts"
|
|
16802
16995
|
},
|
|
16803
16996
|
dependencies: {
|
|
16804
16997
|
"@clack/prompts": "^1.5.0",
|
|
@@ -16907,7 +17100,15 @@ function handle(fn) {
|
|
|
16907
17100
|
}
|
|
16908
17101
|
};
|
|
16909
17102
|
}
|
|
17103
|
+
var VersionSchema = exports_external.object({ version: exports_external.string() });
|
|
16910
17104
|
var webuiRoutes = [
|
|
17105
|
+
{
|
|
17106
|
+
method: "GET",
|
|
17107
|
+
path: "/api/version",
|
|
17108
|
+
summary: "Version of the running core (sidecar). Used by the desktop app's settings footer.",
|
|
17109
|
+
response: VersionSchema,
|
|
17110
|
+
handler: () => Response.json({ version: package_default.version })
|
|
17111
|
+
},
|
|
16911
17112
|
{
|
|
16912
17113
|
method: "GET",
|
|
16913
17114
|
path: "/api/databases",
|
|
@@ -17635,6 +17836,16 @@ function unauthorized() {
|
|
|
17635
17836
|
}
|
|
17636
17837
|
|
|
17637
17838
|
// src/core/sync/server.ts
|
|
17839
|
+
class PortInUseError extends Error {
|
|
17840
|
+
port;
|
|
17841
|
+
host;
|
|
17842
|
+
constructor(port, host) {
|
|
17843
|
+
super(`port ${port} is already in use \u2014 another mh server may be running. ` + `Stop it, or start on a free port with --port <n>.`);
|
|
17844
|
+
this.port = port;
|
|
17845
|
+
this.host = host;
|
|
17846
|
+
this.name = "PortInUseError";
|
|
17847
|
+
}
|
|
17848
|
+
}
|
|
17638
17849
|
function scalarHtml(specUrl) {
|
|
17639
17850
|
return `<!doctype html><html><head>
|
|
17640
17851
|
<meta charset="utf-8"><title>Metahub API</title>
|
|
@@ -17659,48 +17870,60 @@ function startServer(opts = {}) {
|
|
|
17659
17870
|
ttlMs: DEFAULT_TTL_MS,
|
|
17660
17871
|
graceMs: DEFAULT_GRACE_MS
|
|
17661
17872
|
};
|
|
17662
|
-
|
|
17663
|
-
|
|
17664
|
-
|
|
17665
|
-
|
|
17666
|
-
|
|
17667
|
-
|
|
17668
|
-
|
|
17669
|
-
|
|
17670
|
-
|
|
17671
|
-
|
|
17672
|
-
|
|
17673
|
-
|
|
17873
|
+
let server;
|
|
17874
|
+
try {
|
|
17875
|
+
server = Bun.serve({
|
|
17876
|
+
port,
|
|
17877
|
+
hostname: host,
|
|
17878
|
+
async fetch(req) {
|
|
17879
|
+
const url2 = new URL(req.url);
|
|
17880
|
+
if (url2.pathname === SYNC_PATH) {
|
|
17881
|
+
if (!acceptsSyncToken(req, url2, auth, db))
|
|
17882
|
+
return unauthorized();
|
|
17883
|
+
} else {
|
|
17884
|
+
const exempt = url2.pathname === HEALTH_PATH || url2.pathname === RENEW_PATH || url2.pathname === PAIR_PATH;
|
|
17885
|
+
if (!exempt && !hasValidToken(req, url2, auth)) {
|
|
17886
|
+
return wantsHtml(req) ? new Response(unlockPage(), { headers: HTML_HEADERS }) : unauthorized();
|
|
17887
|
+
}
|
|
17674
17888
|
}
|
|
17889
|
+
if (url2.pathname === RENEW_PATH) {
|
|
17890
|
+
const r = renewToken(req, url2, auth);
|
|
17891
|
+
return r ? Response.json(r) : unauthorized();
|
|
17892
|
+
}
|
|
17893
|
+
if (req.method === "GET" && url2.pathname === "/docs.json") {
|
|
17894
|
+
return Response.json(buildOpenApi(package_default.version));
|
|
17895
|
+
}
|
|
17896
|
+
if (req.method === "GET" && url2.pathname === "/docs") {
|
|
17897
|
+
return withShim(new Response(scalarHtml("/docs.json"), { headers: HTML_HEADERS }), auth);
|
|
17898
|
+
}
|
|
17899
|
+
if (req.method === "GET" && (url2.pathname === "/" || url2.pathname === "/webui.js")) {
|
|
17900
|
+
const { serveWebui: serveWebui2 } = await Promise.resolve().then(() => (init_webui(), exports_webui));
|
|
17901
|
+
const res = await serveWebui2(req);
|
|
17902
|
+
if (res)
|
|
17903
|
+
return withShim(res, auth);
|
|
17904
|
+
}
|
|
17905
|
+
if (req.method === "GET" && url2.pathname.startsWith("/sites/")) {
|
|
17906
|
+
const { serveSite: serveSite2 } = await Promise.resolve().then(() => (init_sites_serve(), exports_sites_serve));
|
|
17907
|
+
const res = await serveSite2(req, ctx);
|
|
17908
|
+
if (res)
|
|
17909
|
+
return withShim(res, auth);
|
|
17910
|
+
}
|
|
17911
|
+
const route = routes.find((r) => r.method === req.method && r.path === url2.pathname);
|
|
17912
|
+
if (route)
|
|
17913
|
+
return route.handler(req, ctx);
|
|
17914
|
+
return new Response("not found", { status: 404 });
|
|
17675
17915
|
}
|
|
17676
|
-
|
|
17677
|
-
|
|
17678
|
-
|
|
17679
|
-
|
|
17680
|
-
|
|
17681
|
-
return Response.json(buildOpenApi(package_default.version));
|
|
17682
|
-
}
|
|
17683
|
-
if (req.method === "GET" && url2.pathname === "/docs") {
|
|
17684
|
-
return withShim(new Response(scalarHtml("/docs.json"), { headers: HTML_HEADERS }), auth);
|
|
17685
|
-
}
|
|
17686
|
-
if (req.method === "GET" && (url2.pathname === "/" || url2.pathname === "/webui.js")) {
|
|
17687
|
-
const { serveWebui: serveWebui2 } = await Promise.resolve().then(() => (init_webui(), exports_webui));
|
|
17688
|
-
const res = await serveWebui2(req);
|
|
17689
|
-
if (res)
|
|
17690
|
-
return withShim(res, auth);
|
|
17691
|
-
}
|
|
17692
|
-
if (req.method === "GET" && url2.pathname.startsWith("/sites/")) {
|
|
17693
|
-
const { serveSite: serveSite2 } = await Promise.resolve().then(() => (init_sites_serve(), exports_sites_serve));
|
|
17694
|
-
const res = await serveSite2(req, ctx);
|
|
17695
|
-
if (res)
|
|
17696
|
-
return withShim(res, auth);
|
|
17697
|
-
}
|
|
17698
|
-
const route = routes.find((r) => r.method === req.method && r.path === url2.pathname);
|
|
17699
|
-
if (route)
|
|
17700
|
-
return route.handler(req, ctx);
|
|
17701
|
-
return new Response("not found", { status: 404 });
|
|
17916
|
+
});
|
|
17917
|
+
} catch (e) {
|
|
17918
|
+
const code = e?.code;
|
|
17919
|
+
if (code === "EADDRINUSE") {
|
|
17920
|
+
throw new PortInUseError(port, host);
|
|
17702
17921
|
}
|
|
17703
|
-
|
|
17922
|
+
if (code === "EACCES") {
|
|
17923
|
+
throw new Error(`permission denied binding ${host}:${port} \u2014 ports below 1024 need elevated privileges; pick a higher port with --port`);
|
|
17924
|
+
}
|
|
17925
|
+
throw e;
|
|
17926
|
+
}
|
|
17704
17927
|
let timer = null;
|
|
17705
17928
|
if (autoSync && syncIntervalMs > 0) {
|
|
17706
17929
|
timer = setInterval(() => {
|
|
@@ -17709,12 +17932,17 @@ function startServer(opts = {}) {
|
|
|
17709
17932
|
timer.unref?.();
|
|
17710
17933
|
}
|
|
17711
17934
|
const active = activeToken(auth);
|
|
17935
|
+
const authMode = opts.debug ? "disabled" : opts.token ? "static" : "managed";
|
|
17712
17936
|
return {
|
|
17713
17937
|
server,
|
|
17714
17938
|
node,
|
|
17715
17939
|
port: server.port ?? port,
|
|
17940
|
+
host,
|
|
17716
17941
|
token: active?.token ?? null,
|
|
17717
17942
|
exp: active?.exp ?? null,
|
|
17943
|
+
authMode,
|
|
17944
|
+
autoSync: Boolean(autoSync && syncIntervalMs > 0),
|
|
17945
|
+
syncIntervalMs,
|
|
17718
17946
|
stop() {
|
|
17719
17947
|
if (timer)
|
|
17720
17948
|
clearInterval(timer);
|
|
@@ -17741,6 +17969,7 @@ export {
|
|
|
17741
17969
|
resolveCandidates,
|
|
17742
17970
|
removeProperty,
|
|
17743
17971
|
reconcile,
|
|
17972
|
+
rebuildSearchIndex,
|
|
17744
17973
|
randomSuffix,
|
|
17745
17974
|
putFile,
|
|
17746
17975
|
putBlob,
|
|
@@ -17810,6 +18039,7 @@ export {
|
|
|
17810
18039
|
SyncRequestSchema,
|
|
17811
18040
|
SYNC_PATH,
|
|
17812
18041
|
RENEW_PATH,
|
|
18042
|
+
PortInUseError,
|
|
17813
18043
|
PairResponseSchema,
|
|
17814
18044
|
PairRequestSchema,
|
|
17815
18045
|
PROP_TYPES,
|
|
@@ -17819,4 +18049,4 @@ export {
|
|
|
17819
18049
|
ChangeSchema
|
|
17820
18050
|
};
|
|
17821
18051
|
|
|
17822
|
-
//# debugId=
|
|
18052
|
+
//# debugId=539C6C8168968A4864756E2164756E21
|