@bluekds/doltap-core 0.0.0-stage → 0.1.1

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.
Files changed (72) hide show
  1. package/dist/audit.d.ts +50 -0
  2. package/dist/audit.js +117 -0
  3. package/dist/audit.js.map +1 -0
  4. package/dist/config.d.ts +35 -0
  5. package/dist/config.js +49 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/db.d.ts +64 -0
  8. package/dist/db.js +268 -0
  9. package/dist/db.js.map +1 -0
  10. package/dist/git.d.ts +41 -0
  11. package/dist/git.js +233 -0
  12. package/dist/git.js.map +1 -0
  13. package/dist/index.d.ts +12 -0
  14. package/dist/index.js +13 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/lint.d.ts +18 -0
  17. package/dist/lint.js +99 -0
  18. package/dist/lint.js.map +1 -0
  19. package/dist/page.d.ts +58 -0
  20. package/dist/page.js +149 -0
  21. package/dist/page.js.map +1 -0
  22. package/dist/query.d.ts +31 -0
  23. package/dist/query.js +56 -0
  24. package/dist/query.js.map +1 -0
  25. package/dist/quiet.d.ts +1 -0
  26. package/dist/quiet.js +14 -0
  27. package/dist/quiet.js.map +1 -0
  28. package/dist/scaffold.d.ts +16 -0
  29. package/dist/scaffold.js +91 -0
  30. package/dist/scaffold.js.map +1 -0
  31. package/dist/schedule.d.ts +14 -0
  32. package/dist/schedule.js +215 -0
  33. package/dist/schedule.js.map +1 -0
  34. package/dist/tags.d.ts +8 -0
  35. package/dist/tags.js +17 -0
  36. package/dist/tags.js.map +1 -0
  37. package/dist/template.d.ts +6 -0
  38. package/dist/template.js +24 -0
  39. package/dist/template.js.map +1 -0
  40. package/dist/write.d.ts +57 -0
  41. package/dist/write.js +218 -0
  42. package/dist/write.js.map +1 -0
  43. package/package.json +29 -4
  44. package/skeleton/dot.gitattributes +9 -0
  45. package/skeleton/dot.gitignore +14 -0
  46. package/skeleton/wiki/Home.md +58 -0
  47. package/skeleton/wiki/_meta/PROTOCOL.md +129 -0
  48. package/skeleton/wiki/_meta/tags.yaml +43 -0
  49. package/skeleton/wiki/_meta/templates/decisions.md +32 -0
  50. package/skeleton/wiki/_meta/templates/howto.md +35 -0
  51. package/skeleton/wiki/_meta/templates/patterns.md +39 -0
  52. package/skeleton/wiki/_meta/templates/projects.md +31 -0
  53. package/skeleton/wiki/_meta/templates/reference.md +26 -0
  54. package/skeleton/wiki/decisions/_index.md +28 -0
  55. package/skeleton/wiki/dot.obsidian/README.md +6 -0
  56. package/skeleton/wiki/dot.obsidian/app.json +14 -0
  57. package/skeleton/wiki/dot.obsidian/appearance.json +1 -0
  58. package/skeleton/wiki/dot.obsidian/community-plugins.json +1 -0
  59. package/skeleton/wiki/dot.obsidian/core-plugins.json +32 -0
  60. package/skeleton/wiki/dot.obsidian/graph.json +22 -0
  61. package/skeleton/wiki/dot.obsidian/plugins/dataview/data.json +1 -0
  62. package/skeleton/wiki/dot.obsidian/plugins/obsidian-git/data.json +14 -0
  63. package/skeleton/wiki/dot.obsidian/templates.json +1 -0
  64. package/skeleton/wiki/howto/_index.md +31 -0
  65. package/skeleton/wiki/howto/add-wiki-page-manually.md +49 -0
  66. package/skeleton/wiki/howto/maintain-doltap-wiki.md +78 -0
  67. package/skeleton/wiki/howto/setup-wiki-on-new-machine.md +72 -0
  68. package/skeleton/wiki/patterns/_index.md +28 -0
  69. package/skeleton/wiki/projects/_index.md +28 -0
  70. package/skeleton/wiki/reference/_index.md +29 -0
  71. package/skeleton/wiki/reference/obsidian-conventions.md +47 -0
  72. package/README.md +0 -3
@@ -0,0 +1,50 @@
1
+ import type { WikiConfig } from "./config.js";
2
+ import { WikiDb } from "./db.js";
3
+ import { type SyncReport } from "./git.js";
4
+ /** Per-host access counts shared through git: wiki/_meta/stats/<host>.json */
5
+ export interface HostStats {
6
+ host: string;
7
+ generated: string;
8
+ windowDays: number;
9
+ counts: Record<string, number>;
10
+ lastAccess: Record<string, string>;
11
+ }
12
+ export declare function collectHostStats(cfg: WikiConfig, db: WikiDb, windowDays?: number): HostStats;
13
+ /**
14
+ * Write this host's stats file and commit it when the counts changed and the last publish is older
15
+ * than `minHours`. Called from `doltap sync`. Separate files per host never conflict.
16
+ */
17
+ export declare function publishHostStats(cfg: WikiConfig, db: WikiDb, report: SyncReport, minHours?: number): boolean;
18
+ export declare function loadAllStats(cfg: WikiConfig): HostStats[];
19
+ export interface AuditOptions {
20
+ /** Pages not read by any host within this many days are "unreferenced". */
21
+ unreferencedDays?: number;
22
+ /** Pages whose `updated` is older than this are "stale". */
23
+ staleDays?: number;
24
+ /** Drafts older than this (by `created`) are "lingering". */
25
+ draftDays?: number;
26
+ }
27
+ export interface AuditEntry {
28
+ id: string;
29
+ confidence: string;
30
+ updated: string;
31
+ reads: number;
32
+ lastRead: string | null;
33
+ hosts: string[];
34
+ }
35
+ export interface AuditReport {
36
+ generated: string;
37
+ hostsReporting: string[];
38
+ thresholds: Required<AuditOptions>;
39
+ unreferenced: AuditEntry[];
40
+ stale: AuditEntry[];
41
+ lingeringDrafts: AuditEntry[];
42
+ orphans: string[];
43
+ deprecatedStillLinked: {
44
+ id: string;
45
+ linkedFrom: string[];
46
+ }[];
47
+ conflictFiles: string[];
48
+ mostRead: AuditEntry[];
49
+ }
50
+ export declare function audit(cfg: WikiConfig, o?: AuditOptions): AuditReport;
package/dist/audit.js ADDED
@@ -0,0 +1,117 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { WikiDb } from "./db.js";
4
+ import { commitAndPush, listConflictFiles, readSyncState, writeSyncState } from "./git.js";
5
+ function statsDir(cfg) {
6
+ return join(cfg.wikiDir, "_meta", "stats");
7
+ }
8
+ export function collectHostStats(cfg, db, windowDays = 90) {
9
+ const since = new Date(Date.now() - windowDays * 86400000).toISOString();
10
+ const rows = db.db
11
+ .prepare("SELECT id, COUNT(*) c, MAX(ts) last FROM access_log WHERE ts >= ? GROUP BY id ORDER BY id")
12
+ .all(since);
13
+ const counts = {};
14
+ const lastAccess = {};
15
+ for (const r of rows) {
16
+ counts[r.id] = r.c;
17
+ lastAccess[r.id] = r.last.slice(0, 10);
18
+ }
19
+ return { host: cfg.hostname, generated: new Date().toISOString().slice(0, 10), windowDays, counts, lastAccess };
20
+ }
21
+ /**
22
+ * Write this host's stats file and commit it when the counts changed and the last publish is older
23
+ * than `minHours`. Called from `doltap sync`. Separate files per host never conflict.
24
+ */
25
+ export function publishHostStats(cfg, db, report, minHours = 6) {
26
+ const state = readSyncState(cfg);
27
+ const ageH = state.lastStatsPublish ? (Date.now() - Date.parse(state.lastStatsPublish)) / 3600000 : Infinity;
28
+ if (ageH < minHours)
29
+ return false;
30
+ const stats = collectHostStats(cfg, db);
31
+ if (!Object.keys(stats.counts).length)
32
+ return false;
33
+ mkdirSync(statsDir(cfg), { recursive: true });
34
+ const file = join(statsDir(cfg), `${cfg.hostname}.json`);
35
+ const next = JSON.stringify({ ...stats, generated: undefined }, null, 2);
36
+ if (existsSync(file)) {
37
+ const prev = JSON.parse(readFileSync(file, "utf8"));
38
+ if (JSON.stringify({ ...prev, generated: undefined }, null, 2) === next)
39
+ return false;
40
+ }
41
+ writeFileSync(file, JSON.stringify(stats, null, 2) + "\n");
42
+ commitAndPush(cfg, [join("wiki", "_meta", "stats", `${cfg.hostname}.json`)], `wiki: stats ${cfg.hostname}\n\n[host:${cfg.hostname}] [agent:${cfg.agent}]`, report);
43
+ writeSyncState(cfg, { lastStatsPublish: new Date().toISOString() });
44
+ return true;
45
+ }
46
+ export function loadAllStats(cfg) {
47
+ const dir = statsDir(cfg);
48
+ if (!existsSync(dir))
49
+ return [];
50
+ return readdirSync(dir)
51
+ .filter((f) => f.endsWith(".json"))
52
+ .map((f) => JSON.parse(readFileSync(join(dir, f), "utf8")));
53
+ }
54
+ function daysAgo(iso) {
55
+ return (Date.now() - Date.parse(iso)) / 86400000;
56
+ }
57
+ export function audit(cfg, o = {}) {
58
+ const thresholds = { unreferencedDays: o.unreferencedDays ?? 90, staleDays: o.staleDays ?? 90, draftDays: o.draftDays ?? 30 };
59
+ const db = new WikiDb(cfg);
60
+ try {
61
+ db.reindex();
62
+ // Merge this host's live log with every published stats file (including an older copy of our own).
63
+ const all = loadAllStats(cfg).filter((s) => s.host !== cfg.hostname);
64
+ all.push(collectHostStats(cfg, db, thresholds.unreferencedDays));
65
+ const reads = new Map();
66
+ const rows = db.list({ includeDeprecated: true }).filter((r) => !r.id.endsWith("/_index") && r.id !== "Home");
67
+ const entry = (r) => reads.get(r.id) ?? { id: r.id, confidence: r.confidence, updated: r.updated, reads: 0, lastRead: null, hosts: [] };
68
+ for (const r of rows)
69
+ reads.set(r.id, entry(r));
70
+ for (const s of all) {
71
+ for (const [id, c] of Object.entries(s.counts)) {
72
+ const e = reads.get(id);
73
+ if (!e)
74
+ continue;
75
+ e.reads += c;
76
+ e.hosts.push(s.host);
77
+ const last = s.lastAccess[id];
78
+ if (last && (!e.lastRead || last > e.lastRead))
79
+ e.lastRead = last;
80
+ }
81
+ }
82
+ const entries = [...reads.values()];
83
+ const active = entries.filter((e) => e.confidence !== "deprecated");
84
+ const inbound = new Map();
85
+ for (const r of db.db.prepare("SELECT src, dst FROM links").all()) {
86
+ inbound.set(r.dst, [...(inbound.get(r.dst) ?? []), r.src]);
87
+ }
88
+ const unreferenced = active.filter((e) => e.reads === 0 && daysAgo(e.updated) > thresholds.unreferencedDays);
89
+ const stale = active.filter((e) => daysAgo(e.updated) > thresholds.staleDays);
90
+ const lingeringDrafts = active.filter((e) => e.confidence === "draft" && daysAgo(rowsById(rows, e.id).created) > thresholds.draftDays);
91
+ const orphans = active.filter((e) => (inbound.get(e.id) ?? []).filter((s) => !s.endsWith("/_index")).length === 0 && (inbound.get(e.id) ?? []).length === 0).map((e) => e.id);
92
+ const deprecatedStillLinked = entries
93
+ .filter((e) => e.confidence === "deprecated")
94
+ .map((e) => ({ id: e.id, linkedFrom: (inbound.get(e.id) ?? []).filter((s) => !s.endsWith("/_index")) }))
95
+ .filter((x) => x.linkedFrom.length);
96
+ const mostRead = [...entries].sort((a, b) => b.reads - a.reads).filter((e) => e.reads > 0).slice(0, 10);
97
+ return {
98
+ generated: new Date().toISOString(),
99
+ hostsReporting: all.map((s) => s.host),
100
+ thresholds,
101
+ unreferenced,
102
+ stale,
103
+ lingeringDrafts,
104
+ orphans,
105
+ deprecatedStillLinked,
106
+ conflictFiles: listConflictFiles(cfg),
107
+ mostRead,
108
+ };
109
+ }
110
+ finally {
111
+ db.close();
112
+ }
113
+ }
114
+ function rowsById(rows, id) {
115
+ return rows.find((r) => r.id === id);
116
+ }
117
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ export interface WikiConfig {
2
+ /** Repository root (contains wiki/ and .git). */
3
+ root: string;
4
+ /** Directory holding pages: <root>/wiki */
5
+ wikiDir: string;
6
+ /** Per-machine state dir: <root>/.doltap (gitignored) */
7
+ stateDir: string;
8
+ dbPath: string;
9
+ hostname: string;
10
+ /** Client identity recorded in commit messages. */
11
+ agent: string;
12
+ session?: string;
13
+ /** Skip git pull/push entirely (tests, offline). */
14
+ noSync: boolean;
15
+ /** Minutes before an automatic pull is attempted on read. */
16
+ autoPullMinutes: number;
17
+ }
18
+ export declare const CATEGORIES: readonly ["howto", "patterns", "reference", "decisions", "projects"];
19
+ export type Category = (typeof CATEGORIES)[number];
20
+ export declare const CONFIDENCE: readonly ["draft", "verified", "deprecated"];
21
+ export type Confidence = (typeof CONFIDENCE)[number];
22
+ /** Walk up from `from` until a directory containing wiki/_meta/PROTOCOL.md is found. */
23
+ export declare function findRoot(from: string): string | null;
24
+ export interface ConfigOverrides {
25
+ root?: string;
26
+ agent?: string;
27
+ session?: string;
28
+ noSync?: boolean;
29
+ autoPullMinutes?: number;
30
+ }
31
+ /**
32
+ * Resolution order for the root: explicit override > $DOLTAP_ROOT > walk up from cwd > ~/.doltap.
33
+ */
34
+ export declare function resolveConfig(o?: ConfigOverrides): WikiConfig;
35
+ export declare function today(): string;
package/dist/config.js ADDED
@@ -0,0 +1,49 @@
1
+ import { existsSync, mkdirSync } from "node:fs";
2
+ import { homedir, hostname } from "node:os";
3
+ import { dirname, join, resolve } from "node:path";
4
+ export const CATEGORIES = ["howto", "patterns", "reference", "decisions", "projects"];
5
+ export const CONFIDENCE = ["draft", "verified", "deprecated"];
6
+ const MARKER = join("wiki", "_meta", "PROTOCOL.md");
7
+ function isRoot(dir) {
8
+ return existsSync(join(dir, MARKER));
9
+ }
10
+ /** Walk up from `from` until a directory containing wiki/_meta/PROTOCOL.md is found. */
11
+ export function findRoot(from) {
12
+ let dir = resolve(from);
13
+ for (;;) {
14
+ if (isRoot(dir))
15
+ return dir;
16
+ const parent = dirname(dir);
17
+ if (parent === dir)
18
+ return null;
19
+ dir = parent;
20
+ }
21
+ }
22
+ /**
23
+ * Resolution order for the root: explicit override > $DOLTAP_ROOT > walk up from cwd > ~/.doltap.
24
+ */
25
+ export function resolveConfig(o = {}) {
26
+ const env = process.env;
27
+ const candidates = [o.root, env.DOLTAP_ROOT, findRoot(process.cwd()), join(homedir(), ".doltap")].filter((c) => !!c);
28
+ const root = candidates.map((c) => resolve(c)).find(isRoot);
29
+ if (!root) {
30
+ throw new Error(`Doltap root not found. Tried: ${candidates.join(", ")}. Run \`doltap init\` or set DOLTAP_ROOT.`);
31
+ }
32
+ const stateDir = join(root, ".doltap");
33
+ mkdirSync(stateDir, { recursive: true });
34
+ return {
35
+ root,
36
+ wikiDir: join(root, "wiki"),
37
+ stateDir,
38
+ dbPath: join(stateDir, "index.sqlite"),
39
+ hostname: hostname().split(".")[0],
40
+ agent: o.agent ?? env.DOLTAP_AGENT ?? "cli",
41
+ session: o.session ?? env.DOLTAP_SESSION,
42
+ noSync: o.noSync ?? env.DOLTAP_NO_SYNC === "1",
43
+ autoPullMinutes: o.autoPullMinutes ?? Number(env.DOLTAP_AUTO_PULL_MINUTES ?? 10),
44
+ };
45
+ }
46
+ export function today() {
47
+ return new Date().toISOString().slice(0, 10);
48
+ }
49
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/db.d.ts ADDED
@@ -0,0 +1,64 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import { type WikiConfig } from "./config.js";
3
+ import { type Page } from "./page.js";
4
+ export interface PageRow {
5
+ id: string;
6
+ title: string;
7
+ category: string;
8
+ summary: string;
9
+ tags: string[];
10
+ confidence: string;
11
+ created: string;
12
+ updated: string;
13
+ rel_path: string;
14
+ }
15
+ export interface SearchHit extends PageRow {
16
+ score: number;
17
+ snippet: string;
18
+ }
19
+ export interface SearchOptions {
20
+ category?: string;
21
+ tag?: string;
22
+ confidence?: string;
23
+ limit?: number;
24
+ includeDeprecated?: boolean;
25
+ }
26
+ /** Lowercased alphanumeric tokens, length > 2, minus stopwords and category names. */
27
+ export declare function meaningfulTokens(text: string): Set<string>;
28
+ export declare class WikiDb {
29
+ readonly cfg: WikiConfig;
30
+ readonly db: DatabaseSync;
31
+ constructor(cfg: WikiConfig, path?: string);
32
+ close(): void;
33
+ /** Walk wiki/ for page files, skipping _meta, dotfiles, and conflict copies. */
34
+ listFiles(): string[];
35
+ indexPage(page: Page, mtime: number, size: number): void;
36
+ removePage(id: string): void;
37
+ /**
38
+ * Incremental reindex: re-parse files whose mtime/size changed, drop rows for deleted files.
39
+ * Returns counts. `full` forces re-parsing everything.
40
+ */
41
+ reindex(full?: boolean): {
42
+ indexed: number;
43
+ removed: number;
44
+ total: number;
45
+ pages: Page[];
46
+ };
47
+ private rowToPage;
48
+ getRow(id: string): PageRow | null;
49
+ allIds(): Set<string>;
50
+ list(o?: SearchOptions): PageRow[];
51
+ /** Turn free text into an FTS5 query: each token becomes a prefix term, joined by implicit AND. */
52
+ static toFtsQuery(q: string): string;
53
+ search(query: string, o?: SearchOptions): SearchHit[];
54
+ /**
55
+ * Duplicate detection. Candidates come from an OR query over title/summary/tags (body ignored);
56
+ * each candidate is then scored by the share of the probe's meaningful tokens it also contains.
57
+ * Returns candidates with overlap >= minOverlap and at least minShared shared tokens.
58
+ */
59
+ similar(text: string, limit?: number, minOverlap?: number, minShared?: number): SearchHit[];
60
+ outlinks(id: string): string[];
61
+ backlinks(id: string): string[];
62
+ logAccess(id: string): void;
63
+ accessCounts(sinceIso: string): Map<string, number>;
64
+ }
package/dist/db.js ADDED
@@ -0,0 +1,268 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import { readdirSync, readFileSync, statSync } from "node:fs";
3
+ import { join, relative } from "node:path";
4
+ import { CATEGORIES } from "./config.js";
5
+ import { parsePage } from "./page.js";
6
+ const SCHEMA = `
7
+ CREATE TABLE IF NOT EXISTS pages (
8
+ id TEXT PRIMARY KEY,
9
+ rel_path TEXT NOT NULL,
10
+ title TEXT NOT NULL,
11
+ category TEXT NOT NULL,
12
+ summary TEXT NOT NULL,
13
+ tags TEXT NOT NULL,
14
+ confidence TEXT NOT NULL,
15
+ created TEXT NOT NULL,
16
+ updated TEXT NOT NULL,
17
+ mtime INTEGER NOT NULL,
18
+ size INTEGER NOT NULL
19
+ );
20
+ CREATE VIRTUAL TABLE IF NOT EXISTS pages_fts USING fts5(
21
+ id UNINDEXED, title, summary, tags, body, tokenize='porter unicode61'
22
+ );
23
+ CREATE TABLE IF NOT EXISTS links (
24
+ src TEXT NOT NULL,
25
+ dst TEXT NOT NULL,
26
+ PRIMARY KEY (src, dst)
27
+ );
28
+ CREATE INDEX IF NOT EXISTS links_dst ON links(dst);
29
+ CREATE TABLE IF NOT EXISTS access_log (
30
+ id TEXT NOT NULL,
31
+ ts TEXT NOT NULL,
32
+ agent TEXT NOT NULL
33
+ );
34
+ `;
35
+ const STOPWORDS = new Set("the a an and or of to in on for with without from by at as is are was be this that it its into via when how what why use using used one two new set get".split(" "));
36
+ /** Lowercased alphanumeric tokens, length > 2, minus stopwords and category names. */
37
+ export function meaningfulTokens(text) {
38
+ const out = new Set();
39
+ for (const t of text.toLowerCase().split(/[^a-z0-9_]+/)) {
40
+ if (t.length > 2 && !STOPWORDS.has(t) && !CATEGORIES.includes(t))
41
+ out.add(t);
42
+ }
43
+ return out;
44
+ }
45
+ export class WikiDb {
46
+ cfg;
47
+ db;
48
+ constructor(cfg, path = cfg.dbPath) {
49
+ this.cfg = cfg;
50
+ this.db = new DatabaseSync(path);
51
+ this.db.exec("PRAGMA journal_mode=WAL");
52
+ this.db.exec(SCHEMA);
53
+ }
54
+ close() {
55
+ this.db.close();
56
+ }
57
+ /** Walk wiki/ for page files, skipping _meta, dotfiles, and conflict copies. */
58
+ listFiles() {
59
+ const out = [];
60
+ const walk = (dir) => {
61
+ for (const ent of readdirSync(dir, { withFileTypes: true })) {
62
+ if (ent.name.startsWith(".") || ent.name.startsWith("_meta"))
63
+ continue;
64
+ const p = join(dir, ent.name);
65
+ if (ent.isDirectory())
66
+ walk(p);
67
+ else if (ent.name.endsWith(".md") && !/\.conflict-[^.]+\.md$/.test(ent.name))
68
+ out.push(p);
69
+ }
70
+ };
71
+ walk(this.cfg.wikiDir);
72
+ return out.sort();
73
+ }
74
+ indexPage(page, mtime, size) {
75
+ const { meta } = page;
76
+ this.db
77
+ .prepare(`INSERT OR REPLACE INTO pages (id, rel_path, title, category, summary, tags, confidence, created, updated, mtime, size)
78
+ VALUES (?,?,?,?,?,?,?,?,?,?,?)`)
79
+ .run(page.id, page.relPath, meta.title, meta.category, meta.summary, JSON.stringify(meta.tags), meta.confidence, meta.created, meta.updated, mtime, size);
80
+ this.db.prepare("DELETE FROM pages_fts WHERE id = ?").run(page.id);
81
+ this.db
82
+ .prepare("INSERT INTO pages_fts (id, title, summary, tags, body) VALUES (?,?,?,?,?)")
83
+ .run(page.id, meta.title, meta.summary, meta.tags.join(" "), page.body);
84
+ this.db.prepare("DELETE FROM links WHERE src = ?").run(page.id);
85
+ const ins = this.db.prepare("INSERT OR IGNORE INTO links (src, dst) VALUES (?,?)");
86
+ for (const dst of page.links)
87
+ ins.run(page.id, dst);
88
+ }
89
+ removePage(id) {
90
+ this.db.prepare("DELETE FROM pages WHERE id = ?").run(id);
91
+ this.db.prepare("DELETE FROM pages_fts WHERE id = ?").run(id);
92
+ this.db.prepare("DELETE FROM links WHERE src = ?").run(id);
93
+ }
94
+ /**
95
+ * Incremental reindex: re-parse files whose mtime/size changed, drop rows for deleted files.
96
+ * Returns counts. `full` forces re-parsing everything.
97
+ */
98
+ reindex(full = false) {
99
+ const files = this.listFiles();
100
+ const known = new Map();
101
+ for (const r of this.db.prepare("SELECT id, mtime, size FROM pages").all()) {
102
+ known.set(r.id, r);
103
+ }
104
+ const seen = new Set();
105
+ const pages = [];
106
+ let indexed = 0;
107
+ const tx = this.db.prepare("BEGIN");
108
+ tx.run();
109
+ try {
110
+ for (const abs of files) {
111
+ const rel = relative(this.cfg.wikiDir, abs);
112
+ const st = statSync(abs);
113
+ const mtime = Math.floor(st.mtimeMs);
114
+ const page = parsePage(this.cfg, rel, readFileSync(abs, "utf8"));
115
+ seen.add(page.id);
116
+ pages.push(page);
117
+ const prev = known.get(page.id);
118
+ if (!full && prev && prev.mtime === mtime && prev.size === st.size)
119
+ continue;
120
+ this.indexPage(page, mtime, st.size);
121
+ indexed++;
122
+ }
123
+ let removed = 0;
124
+ for (const id of known.keys()) {
125
+ if (!seen.has(id)) {
126
+ this.removePage(id);
127
+ removed++;
128
+ }
129
+ }
130
+ this.db.prepare("COMMIT").run();
131
+ return { indexed, removed, total: files.length, pages };
132
+ }
133
+ catch (e) {
134
+ this.db.prepare("ROLLBACK").run();
135
+ throw e;
136
+ }
137
+ }
138
+ rowToPage(r) {
139
+ return {
140
+ id: String(r.id),
141
+ title: String(r.title),
142
+ category: String(r.category),
143
+ summary: String(r.summary),
144
+ tags: JSON.parse(String(r.tags)),
145
+ confidence: String(r.confidence),
146
+ created: String(r.created),
147
+ updated: String(r.updated),
148
+ rel_path: String(r.rel_path),
149
+ };
150
+ }
151
+ getRow(id) {
152
+ const r = this.db.prepare("SELECT * FROM pages WHERE id = ?").get(id);
153
+ return r ? this.rowToPage(r) : null;
154
+ }
155
+ allIds() {
156
+ return new Set(this.db.prepare("SELECT id FROM pages").all().map((r) => r.id));
157
+ }
158
+ list(o = {}) {
159
+ const where = [];
160
+ const args = [];
161
+ if (o.category) {
162
+ where.push("category = ?");
163
+ args.push(o.category);
164
+ }
165
+ if (o.confidence) {
166
+ where.push("confidence = ?");
167
+ args.push(o.confidence);
168
+ }
169
+ else if (!o.includeDeprecated)
170
+ where.push("confidence != 'deprecated'");
171
+ const rows = this.db
172
+ .prepare(`SELECT * FROM pages ${where.length ? "WHERE " + where.join(" AND ") : ""} ORDER BY id`)
173
+ .all(...args);
174
+ let out = rows.map((r) => this.rowToPage(r));
175
+ if (o.tag)
176
+ out = out.filter((p) => p.tags.includes(o.tag));
177
+ return out;
178
+ }
179
+ /** Turn free text into an FTS5 query: each token becomes a prefix term, joined by implicit AND. */
180
+ static toFtsQuery(q) {
181
+ const tokens = q
182
+ .toLowerCase()
183
+ .split(/[^a-z0-9_]+/)
184
+ .filter((t) => t.length > 1);
185
+ if (!tokens.length)
186
+ return "";
187
+ return tokens.map((t) => `"${t}"*`).join(" ");
188
+ }
189
+ search(query, o = {}) {
190
+ const fts = WikiDb.toFtsQuery(query);
191
+ if (!fts)
192
+ return [];
193
+ const limit = o.limit ?? 10;
194
+ const where = ["pages_fts MATCH ?"];
195
+ const args = [fts];
196
+ if (o.category) {
197
+ where.push("p.category = ?");
198
+ args.push(o.category);
199
+ }
200
+ if (o.confidence) {
201
+ where.push("p.confidence = ?");
202
+ args.push(o.confidence);
203
+ }
204
+ else if (!o.includeDeprecated)
205
+ where.push("p.confidence != 'deprecated'");
206
+ // Weight title and summary and tags above body.
207
+ const sql = `
208
+ SELECT p.*, bm25(pages_fts, 0, 10.0, 6.0, 4.0, 1.0) AS score,
209
+ snippet(pages_fts, 4, '**', '**', ' … ', 12) AS snippet
210
+ FROM pages_fts JOIN pages p ON p.id = pages_fts.id
211
+ WHERE ${where.join(" AND ")}
212
+ ORDER BY score LIMIT ?`;
213
+ args.push(limit * 3);
214
+ let rows = this.db.prepare(sql).all(...args).map((r) => ({
215
+ ...this.rowToPage(r),
216
+ score: Number(r.score),
217
+ snippet: String(r.snippet),
218
+ }));
219
+ if (o.tag)
220
+ rows = rows.filter((p) => p.tags.includes(o.tag));
221
+ return rows.slice(0, limit);
222
+ }
223
+ /**
224
+ * Duplicate detection. Candidates come from an OR query over title/summary/tags (body ignored);
225
+ * each candidate is then scored by the share of the probe's meaningful tokens it also contains.
226
+ * Returns candidates with overlap >= minOverlap and at least minShared shared tokens.
227
+ */
228
+ similar(text, limit = 5, minOverlap = 0.5, minShared = 3) {
229
+ const probe = meaningfulTokens(text);
230
+ if (probe.size < 2)
231
+ return [];
232
+ const fts = [...probe].map((t) => `"${t}"`).join(" OR ");
233
+ const sql = `
234
+ SELECT p.*, bm25(pages_fts, 0, 10.0, 6.0, 4.0, 0.0) AS score, '' AS snippet
235
+ FROM pages_fts JOIN pages p ON p.id = pages_fts.id
236
+ WHERE pages_fts MATCH ? AND p.confidence != 'deprecated'
237
+ ORDER BY score LIMIT 25`;
238
+ const rows = this.db.prepare(sql).all(fts).map((r) => ({
239
+ ...this.rowToPage(r),
240
+ score: Number(r.score),
241
+ snippet: String(r.snippet),
242
+ }));
243
+ const scored = rows
244
+ .filter((r) => !r.id.endsWith("/_index"))
245
+ .map((r) => {
246
+ const own = meaningfulTokens(`${r.title} ${r.summary} ${r.id.replace(/[/-]/g, " ")}`);
247
+ const shared = [...probe].filter((t) => own.has(t)).length;
248
+ return { hit: r, shared, overlap: shared / probe.size };
249
+ })
250
+ .filter((x) => x.shared >= minShared && x.overlap >= minOverlap)
251
+ .sort((a, b) => b.overlap - a.overlap);
252
+ return scored.slice(0, limit).map((x) => ({ ...x.hit, score: -x.overlap, snippet: `${x.shared}/${probe.size} tokens shared` }));
253
+ }
254
+ outlinks(id) {
255
+ return this.db.prepare("SELECT dst FROM links WHERE src = ? ORDER BY dst").all(id).map((r) => r.dst);
256
+ }
257
+ backlinks(id) {
258
+ return this.db.prepare("SELECT src FROM links WHERE dst = ? ORDER BY src").all(id).map((r) => r.src);
259
+ }
260
+ logAccess(id) {
261
+ this.db.prepare("INSERT INTO access_log (id, ts, agent) VALUES (?,?,?)").run(id, new Date().toISOString(), this.cfg.agent);
262
+ }
263
+ accessCounts(sinceIso) {
264
+ const rows = this.db.prepare("SELECT id, COUNT(*) c FROM access_log WHERE ts >= ? GROUP BY id").all(sinceIso);
265
+ return new Map(rows.map((r) => [r.id, r.c]));
266
+ }
267
+ }
268
+ //# sourceMappingURL=db.js.map
package/dist/db.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}