mirror of
https://github.com/lllin000/PaperForge.git
synced 2026-07-22 06:50:53 +00:00
Delete sql.js: remove db.ts, wasm, dependency, and all sql.js code paths
- Remove paperforge/plugin/src/services/db.ts (sql.js wrapper, initDatabase/searchMetadata/SearchResultItem) - Remove paperforge/plugin/sql-wasm.wasm (644KB binary) - Remove sql.js and @types/sql.js from package.json; npm install to update lockfile - dashboard.ts: remove import of db.ts, remove _sqlJsInitialized/_sqlJsFailed state, remove the sql.js search path block entirely — all searches go straight to CLI spawn. Clean up stale comments. - Bundle drops from 346KB to 167KB; 6 test files / 58 tests still pass
This commit is contained in:
parent
f52124bcf7
commit
32e69a6c7b
6 changed files with 43 additions and 9832 deletions
File diff suppressed because one or more lines are too long
28
paperforge/plugin/package-lock.json
generated
28
paperforge/plugin/package-lock.json
generated
|
|
@ -7,12 +7,8 @@
|
|||
"": {
|
||||
"name": "paperforge-plugin",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"sql.js": "^1.14.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/sql.js": "^1.4.11",
|
||||
"builtin-modules": "^3.3.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"husky": "^9.1.7",
|
||||
|
|
@ -1004,13 +1000,6 @@
|
|||
"@types/tern": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/emscripten": {
|
||||
"version": "1.41.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.41.5.tgz",
|
||||
"integrity": "sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
|
||||
|
|
@ -1029,17 +1018,6 @@
|
|||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/sql.js": {
|
||||
"version": "1.4.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/sql.js/-/sql.js-1.4.11.tgz",
|
||||
"integrity": "sha512-QXIx38p2ZThJaK9vP5ZdqdlRe1FG9I8SmCZOS7FHfB/2qPAjZwkL7/vlfPg6N/oWHuuOaGg/P/IRwfP2W0kWVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/emscripten": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/tern": {
|
||||
"version": "0.23.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",
|
||||
|
|
@ -2439,12 +2417,6 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sql.js": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.14.1.tgz",
|
||||
"integrity": "sha512-gcj8zBWU5cFsi9WUP+4bFNXAyF1iRpA3LLyS/DP5xlrNzGmPIizUeBggKa8DbDwdqaKwUcTEnChtd2grWo/x/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stackback": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/sql.js": "^1.4.11",
|
||||
"builtin-modules": "^3.3.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"husky": "^9.1.7",
|
||||
|
|
@ -24,8 +23,5 @@
|
|||
"prettier": "^3.8.4",
|
||||
"typescript": "^5.4.0",
|
||||
"vitest": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"sql.js": "^1.14.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,119 +0,0 @@
|
|||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import initSqlJs from "sql.js";
|
||||
import type {
|
||||
Database as SqlJsDatabase,
|
||||
Statement as SqlJsStatement,
|
||||
SqlValue,
|
||||
} from "sql.js";
|
||||
|
||||
// ── Types ──
|
||||
|
||||
export interface SearchResultItem {
|
||||
zotero_key: string;
|
||||
title: string;
|
||||
first_author: string;
|
||||
year: string;
|
||||
journal: string;
|
||||
domain: string;
|
||||
abstract: string;
|
||||
rank: number;
|
||||
}
|
||||
|
||||
// ── Module-level state (lazy init) ──
|
||||
|
||||
let _db: SqlJsDatabase | null = null;
|
||||
let _queryStmt: SqlJsStatement | null = null;
|
||||
|
||||
// ── FTS5 query transform ──
|
||||
// FTS5 requires explicit operators between terms: "rotator cuff" → "rotator AND cuff"
|
||||
// Quoted phrases like '"rotator cuff"' are passed through verbatim.
|
||||
|
||||
function transformFtsQuery(input: string): string {
|
||||
input = input.trim();
|
||||
if (!input) return "";
|
||||
|
||||
// Already a quoted phrase — pass through as-is
|
||||
if (input.startsWith('"') && input.endsWith('"')) {
|
||||
return input;
|
||||
}
|
||||
|
||||
const terms = input.split(/\s+/).filter(Boolean);
|
||||
if (terms.length === 0) return "";
|
||||
return terms.join(" AND ");
|
||||
}
|
||||
|
||||
// ── Database init (lazy) ──
|
||||
|
||||
function sqlValueToString(v: SqlValue): string {
|
||||
if (v === null || v === undefined) return "";
|
||||
if (typeof v === "string") return v;
|
||||
if (v instanceof Uint8Array) return new TextDecoder().decode(v);
|
||||
return String(v);
|
||||
}
|
||||
|
||||
function sqlValueToNumber(v: SqlValue): number {
|
||||
if (v === null || v === undefined) return 0;
|
||||
if (typeof v === "number") return v;
|
||||
return Number(v);
|
||||
}
|
||||
|
||||
export async function initDatabase(vaultPath: string): Promise<void> {
|
||||
const dbPath = path.join(
|
||||
vaultPath,
|
||||
"System",
|
||||
"PaperForge",
|
||||
"indexes",
|
||||
"paperforge.db"
|
||||
);
|
||||
|
||||
if (!fs.existsSync(dbPath)) {
|
||||
throw new Error(`PaperForge database not found at ${dbPath}`);
|
||||
}
|
||||
|
||||
const SQL = await initSqlJs({
|
||||
locateFile: (file: string) => path.join(__dirname, file),
|
||||
});
|
||||
|
||||
const buffer = fs.readFileSync(dbPath);
|
||||
_db = new SQL.Database(new Uint8Array(buffer));
|
||||
|
||||
_queryStmt = _db.prepare(
|
||||
`SELECT zotero_key, title, first_author, year, journal, domain, abstract, rank
|
||||
FROM paper_fts
|
||||
WHERE paper_fts MATCH ?
|
||||
ORDER BY rank
|
||||
LIMIT ?`
|
||||
);
|
||||
}
|
||||
|
||||
// ── Search ──
|
||||
|
||||
export function searchMetadata(
|
||||
query: string,
|
||||
limit: number = 20
|
||||
): SearchResultItem[] | null {
|
||||
if (!_db || !_queryStmt) return null;
|
||||
|
||||
const transformed = transformFtsQuery(query);
|
||||
if (!transformed) return [];
|
||||
|
||||
_queryStmt.bind([transformed, limit]);
|
||||
|
||||
const results: SearchResultItem[] = [];
|
||||
while (_queryStmt.step()) {
|
||||
const row = _queryStmt.getAsObject();
|
||||
results.push({
|
||||
zotero_key: sqlValueToString(row.zotero_key),
|
||||
title: sqlValueToString(row.title),
|
||||
first_author: sqlValueToString(row.first_author),
|
||||
year: sqlValueToString(row.year),
|
||||
journal: sqlValueToString(row.journal),
|
||||
domain: sqlValueToString(row.domain),
|
||||
abstract: sqlValueToString(row.abstract),
|
||||
rank: sqlValueToNumber(row.rank),
|
||||
});
|
||||
}
|
||||
_queryStmt.reset();
|
||||
return results;
|
||||
}
|
||||
|
|
@ -51,11 +51,6 @@ import {
|
|||
restoreVersion,
|
||||
compareVersions,
|
||||
} from "../services/version-history";
|
||||
import {
|
||||
initDatabase,
|
||||
searchMetadata,
|
||||
type SearchResultItem,
|
||||
} from "../services/db";
|
||||
|
||||
// ── Interface for plugin ref used by static open ──
|
||||
|
||||
|
|
@ -101,8 +96,6 @@ export class PaperForgeStatusView extends ItemView {
|
|||
_searchInput: HTMLInputElement | null = null;
|
||||
_searchResultsEl: HTMLElement | null = null;
|
||||
_searchTimer: ReturnType<typeof setTimeout> | undefined = undefined;
|
||||
_sqlJsInitialized: boolean = false;
|
||||
_sqlJsFailed: boolean = false;
|
||||
|
||||
constructor(leaf: WorkspaceLeaf) {
|
||||
super(leaf);
|
||||
|
|
@ -2396,7 +2389,7 @@ export class PaperForgeStatusView extends ItemView {
|
|||
cls: "paperforge-search-results",
|
||||
});
|
||||
|
||||
// Detect @ mode prefix + debounced metadata search via sql.js
|
||||
// Detect @ mode prefix + debounced metadata search
|
||||
this._searchInput.addEventListener("input", () => {
|
||||
const val = this._searchInput?.value || "";
|
||||
if (val.startsWith("@") && !val.startsWith("@ ")) {
|
||||
|
|
@ -2411,7 +2404,7 @@ export class PaperForgeStatusView extends ItemView {
|
|||
// Debounced metadata search for non-@ queries
|
||||
if (!val.startsWith("@") && val.trim()) {
|
||||
this._searchTimer = setTimeout(() => {
|
||||
this.executeSearch({ source: "sqljs" });
|
||||
this.executeSearch();
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
|
|
@ -2424,12 +2417,12 @@ export class PaperForgeStatusView extends ItemView {
|
|||
clearTimeout(this._searchTimer);
|
||||
this._searchTimer = undefined;
|
||||
}
|
||||
this.executeSearch({ source: "cli" });
|
||||
this.executeSearch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async executeSearch(options: { source?: "auto" | "cli" | "sqljs" } = {}) {
|
||||
async executeSearch() {
|
||||
if (!this._searchInput || !this._searchResultsEl) return;
|
||||
const raw = this._searchInput.value.trim();
|
||||
if (!raw) return;
|
||||
|
|
@ -2440,7 +2433,7 @@ export class PaperForgeStatusView extends ItemView {
|
|||
|
||||
const mode = isDeep ? "retrieve" : "search";
|
||||
|
||||
// Resolve vault path (used by both sql.js and CLI paths)
|
||||
// Resolve vault path for CLI search
|
||||
const adapter = this.app.vault.adapter;
|
||||
let vaultPath = "";
|
||||
if (adapter && typeof adapter === "object" && "basePath" in adapter) {
|
||||
|
|
@ -2449,34 +2442,7 @@ export class PaperForgeStatusView extends ItemView {
|
|||
}
|
||||
|
||||
this._searchResultsEl.empty();
|
||||
|
||||
// ── sql.js metadata search path (non-@ queries only) ──
|
||||
if (
|
||||
mode === "search" &&
|
||||
(options.source === "auto" || options.source === "sqljs")
|
||||
) {
|
||||
if (vaultPath) {
|
||||
try {
|
||||
if (!this._sqlJsInitialized && !this._sqlJsFailed) {
|
||||
await initDatabase(vaultPath);
|
||||
this._sqlJsInitialized = true;
|
||||
}
|
||||
if (this._sqlJsInitialized) {
|
||||
const results = searchMetadata(query, 20);
|
||||
if (results !== null) {
|
||||
this.renderSearchResults(results, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("PaperForge sql.js search failed:", e);
|
||||
this._sqlJsFailed = true;
|
||||
}
|
||||
}
|
||||
// sql.js unavailable — fall through to CLI
|
||||
}
|
||||
|
||||
// ── CLI search path (deep search or sql.js fallback) ──
|
||||
// ── CLI search path (deep search only) ──
|
||||
this._searchResultsEl.createEl("div", {
|
||||
cls: "paperforge-search-loading",
|
||||
text: "Searching...",
|
||||
|
|
@ -2515,10 +2481,10 @@ export class PaperForgeStatusView extends ItemView {
|
|||
undefined,
|
||||
undefined
|
||||
);
|
||||
|
||||
const deepFlag = mode === "retrieve" ? ["--deep"] : [];
|
||||
const child = spawn(
|
||||
pythonExe,
|
||||
[...pyExtra, "-m", "paperforge", mode, query, "--json"],
|
||||
[...pyExtra, "-m", "paperforge", mode, query, ...deepFlag, "--json"],
|
||||
{ cwd: vaultPath, timeout: 30000 }
|
||||
);
|
||||
|
||||
|
|
@ -2561,11 +2527,9 @@ export class PaperForgeStatusView extends ItemView {
|
|||
const d = (parsed as Record<string, unknown>).data;
|
||||
if (d && typeof d === "object") {
|
||||
const dd = d as Record<string, unknown>;
|
||||
// search output: data.matches
|
||||
// Unified PFResult v1: data.matches
|
||||
if ("matches" in dd && Array.isArray(dd.matches)) {
|
||||
results = dd.matches as unknown[];
|
||||
} else if ("results" in dd && Array.isArray(dd.results)) {
|
||||
results = dd.results as unknown[];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2674,21 +2638,10 @@ export class PaperForgeStatusView extends ItemView {
|
|||
cls: "paperforge-search-result-meta",
|
||||
});
|
||||
|
||||
if (typeof rec["authors"] === "string") {
|
||||
if (typeof rec["first_author"] === "string" && rec["first_author"]) {
|
||||
meta.createEl("span", {
|
||||
cls: "paperforge-search-result-author",
|
||||
text: rec["authors"],
|
||||
});
|
||||
} else if (Array.isArray(rec["authors"])) {
|
||||
meta.createEl("span", {
|
||||
cls: "paperforge-search-result-author",
|
||||
text: (rec["authors"] as string[]).slice(0, 3).join("; "),
|
||||
});
|
||||
}
|
||||
if (typeof rec["year"] === "number" || typeof rec["year"] === "string") {
|
||||
meta.createEl("span", {
|
||||
cls: "paperforge-search-result-year",
|
||||
text: String(rec["year"]),
|
||||
text: rec["first_author"],
|
||||
});
|
||||
}
|
||||
if (typeof rec["journal"] === "string" && rec["journal"]) {
|
||||
|
|
@ -2727,10 +2680,10 @@ export class PaperForgeStatusView extends ItemView {
|
|||
// @ mode: matched text
|
||||
if (
|
||||
isDeep &&
|
||||
typeof rec["matched_text"] === "string" &&
|
||||
rec["matched_text"]
|
||||
typeof rec["text"] === "string" &&
|
||||
rec["text"]
|
||||
) {
|
||||
const mt = rec["matched_text"] as string;
|
||||
const mt = rec["text"] as string;
|
||||
card.createEl("div", {
|
||||
cls: "paperforge-search-result-source",
|
||||
text: mt.length > 300 ? mt.slice(0, 300) + "..." : mt,
|
||||
|
|
|
|||
Loading…
Reference in a new issue