@abhishekmcp/notes 0.1.1 → 0.3.0
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 +71 -40
- package/dist/config.d.ts +41 -0
- package/dist/config.js +67 -0
- package/dist/config.js.map +1 -0
- package/dist/embed.d.ts +9 -0
- package/dist/embed.js +134 -0
- package/dist/embed.js.map +1 -0
- package/dist/fsutil.d.ts +23 -0
- package/dist/fsutil.js +113 -0
- package/dist/fsutil.js.map +1 -0
- package/dist/graph.d.ts +38 -0
- package/dist/graph.js +160 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.js +240 -100
- package/dist/index.js.map +1 -1
- package/dist/parse.d.ts +55 -0
- package/dist/parse.js +202 -0
- package/dist/parse.js.map +1 -0
- package/dist/semantic.d.ts +14 -0
- package/dist/semantic.js +142 -0
- package/dist/semantic.js.map +1 -0
- package/dist/store.d.ts +102 -0
- package/dist/store.js +410 -0
- package/dist/store.js.map +1 -0
- package/dist/tokenizer.d.ts +30 -0
- package/dist/tokenizer.js +140 -0
- package/dist/tokenizer.js.map +1 -0
- package/package.json +4 -2
- package/dist/notes.d.ts +0 -36
- package/dist/notes.js +0 -138
- package/dist/notes.js.map +0 -1
package/dist/parse.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, dependency-free markdown parsing: a tiny YAML-frontmatter reader plus
|
|
3
|
+
* extractors for headings, sections, wiki-links, tags and todos. We deliberately
|
|
4
|
+
* avoid gray-matter / js-yaml (npm-audit noise + native-free goal); the subset of
|
|
5
|
+
* YAML notes actually use in frontmatter is small enough to hand-roll safely.
|
|
6
|
+
*/
|
|
7
|
+
export interface Frontmatter {
|
|
8
|
+
title?: string;
|
|
9
|
+
tags?: string[];
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface Heading {
|
|
13
|
+
level: number;
|
|
14
|
+
text: string;
|
|
15
|
+
line: number;
|
|
16
|
+
}
|
|
17
|
+
export interface Todo {
|
|
18
|
+
text: string;
|
|
19
|
+
done: boolean;
|
|
20
|
+
line: number;
|
|
21
|
+
}
|
|
22
|
+
export interface ParsedNote {
|
|
23
|
+
frontmatter: Frontmatter;
|
|
24
|
+
body: string;
|
|
25
|
+
title?: string;
|
|
26
|
+
tags: string[];
|
|
27
|
+
links: string[];
|
|
28
|
+
headings: Heading[];
|
|
29
|
+
}
|
|
30
|
+
/** Strip a trailing ".md" and normalize separators so links/names compare equal. */
|
|
31
|
+
export declare function normalizeLinkTarget(raw: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Split raw note text into a frontmatter object and the remaining body.
|
|
34
|
+
* Recognizes a leading `---` ... `---` fence. Unparseable frontmatter is
|
|
35
|
+
* treated as body (never throws).
|
|
36
|
+
*/
|
|
37
|
+
export declare function splitFrontmatter(raw: string): {
|
|
38
|
+
frontmatter: Frontmatter;
|
|
39
|
+
body: string;
|
|
40
|
+
};
|
|
41
|
+
/** Extract ATX headings (`#`..`######`) from body text, ignoring fenced code blocks. */
|
|
42
|
+
export declare function extractHeadings(body: string): Heading[];
|
|
43
|
+
/**
|
|
44
|
+
* Return the text under a heading (case-insensitive match on heading text),
|
|
45
|
+
* up to the next heading of the same or higher level. Returns null if not found.
|
|
46
|
+
*/
|
|
47
|
+
export declare function extractSection(body: string, heading: string): string | null;
|
|
48
|
+
/** Extract wiki-link targets (`[[name]]` / `[[name|alias]]`), normalized + deduped. */
|
|
49
|
+
export declare function extractWikiLinks(body: string): string[];
|
|
50
|
+
/** Collect tags from frontmatter plus inline #hashtags in the body, deduped. */
|
|
51
|
+
export declare function extractTags(frontmatter: Frontmatter, body: string): string[];
|
|
52
|
+
/** Extract checkbox todos (`- [ ]` / `- [x]`) from body text. */
|
|
53
|
+
export declare function extractTodos(body: string): Todo[];
|
|
54
|
+
/** Full parse of a raw note into structured pieces used by the index and graph. */
|
|
55
|
+
export declare function parseNote(raw: string): ParsedNote;
|
package/dist/parse.js
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, dependency-free markdown parsing: a tiny YAML-frontmatter reader plus
|
|
3
|
+
* extractors for headings, sections, wiki-links, tags and todos. We deliberately
|
|
4
|
+
* avoid gray-matter / js-yaml (npm-audit noise + native-free goal); the subset of
|
|
5
|
+
* YAML notes actually use in frontmatter is small enough to hand-roll safely.
|
|
6
|
+
*/
|
|
7
|
+
const WIKI_LINK = /\[\[([^\]]+)\]\]/g;
|
|
8
|
+
/** Inline #hashtag: a '#' not followed by whitespace (so markdown headings "# x" don't match). */
|
|
9
|
+
const HASHTAG = /(?:^|\s)#([A-Za-z0-9][\w/-]*)/g;
|
|
10
|
+
/** Strip a trailing ".md" and normalize separators so links/names compare equal. */
|
|
11
|
+
export function normalizeLinkTarget(raw) {
|
|
12
|
+
return raw.trim().replace(/\.md$/i, "").replace(/\\/g, "/").replace(/^\.?\/+/, "");
|
|
13
|
+
}
|
|
14
|
+
/** Strip surrounding single/double quotes from a scalar value. */
|
|
15
|
+
function unquote(v) {
|
|
16
|
+
const t = v.trim();
|
|
17
|
+
if (t.length >= 2 && ((t[0] === '"' && t.endsWith('"')) || (t[0] === "'" && t.endsWith("'")))) {
|
|
18
|
+
return t.slice(1, -1);
|
|
19
|
+
}
|
|
20
|
+
return t;
|
|
21
|
+
}
|
|
22
|
+
/** Parse `[a, b, "c"]` or `a, b, c` into a string list. */
|
|
23
|
+
function parseInlineList(v) {
|
|
24
|
+
let s = v.trim();
|
|
25
|
+
if (s.startsWith("[") && s.endsWith("]"))
|
|
26
|
+
s = s.slice(1, -1);
|
|
27
|
+
return s
|
|
28
|
+
.split(",")
|
|
29
|
+
.map((x) => unquote(x))
|
|
30
|
+
.filter((x) => x.length > 0);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Split raw note text into a frontmatter object and the remaining body.
|
|
34
|
+
* Recognizes a leading `---` ... `---` fence. Unparseable frontmatter is
|
|
35
|
+
* treated as body (never throws).
|
|
36
|
+
*/
|
|
37
|
+
export function splitFrontmatter(raw) {
|
|
38
|
+
// Must start with --- on the very first line.
|
|
39
|
+
const m = /^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/.exec(raw);
|
|
40
|
+
if (!m || raw.slice(0, 3) !== "---") {
|
|
41
|
+
return { frontmatter: {}, body: raw };
|
|
42
|
+
}
|
|
43
|
+
const block = m[1];
|
|
44
|
+
const body = raw.slice(m[0].length);
|
|
45
|
+
const fm = {};
|
|
46
|
+
const lines = block.split(/\r?\n/);
|
|
47
|
+
for (let i = 0; i < lines.length; i++) {
|
|
48
|
+
const line = lines[i];
|
|
49
|
+
if (line.trim() === "" || line.trimStart().startsWith("#"))
|
|
50
|
+
continue;
|
|
51
|
+
const kv = /^([A-Za-z0-9_-]+):[ \t]*(.*)$/.exec(line);
|
|
52
|
+
if (!kv)
|
|
53
|
+
continue;
|
|
54
|
+
const key = kv[1];
|
|
55
|
+
const rest = kv[2];
|
|
56
|
+
if (rest.trim() === "") {
|
|
57
|
+
// Possible block list: subsequent "- item" lines.
|
|
58
|
+
const items = [];
|
|
59
|
+
let j = i + 1;
|
|
60
|
+
for (; j < lines.length; j++) {
|
|
61
|
+
const item = /^[ \t]+-[ \t]+(.*)$/.exec(lines[j]);
|
|
62
|
+
if (!item)
|
|
63
|
+
break;
|
|
64
|
+
items.push(unquote(item[1]));
|
|
65
|
+
}
|
|
66
|
+
if (items.length > 0) {
|
|
67
|
+
fm[key] = key === "tags" ? items : items;
|
|
68
|
+
i = j - 1;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
fm[key] = "";
|
|
72
|
+
}
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (key === "tags") {
|
|
76
|
+
fm.tags = parseInlineList(rest);
|
|
77
|
+
}
|
|
78
|
+
else if (rest.trim().startsWith("[")) {
|
|
79
|
+
fm[key] = parseInlineList(rest);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
fm[key] = unquote(rest);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { frontmatter: fm, body };
|
|
86
|
+
}
|
|
87
|
+
/** Extract ATX headings (`#`..`######`) from body text, ignoring fenced code blocks. */
|
|
88
|
+
export function extractHeadings(body) {
|
|
89
|
+
const out = [];
|
|
90
|
+
const lines = body.split(/\r?\n/);
|
|
91
|
+
let inFence = false;
|
|
92
|
+
let fence = "";
|
|
93
|
+
for (let i = 0; i < lines.length; i++) {
|
|
94
|
+
const line = lines[i];
|
|
95
|
+
const fenceMatch = /^[ \t]*(```+|~~~+)/.exec(line);
|
|
96
|
+
if (fenceMatch) {
|
|
97
|
+
if (!inFence) {
|
|
98
|
+
inFence = true;
|
|
99
|
+
fence = fenceMatch[1][0];
|
|
100
|
+
}
|
|
101
|
+
else if (line.trimStart().startsWith(fence)) {
|
|
102
|
+
inFence = false;
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (inFence)
|
|
107
|
+
continue;
|
|
108
|
+
const h = /^(#{1,6})[ \t]+(.+?)[ \t]*#*$/.exec(line);
|
|
109
|
+
if (h)
|
|
110
|
+
out.push({ level: h[1].length, text: h[2].trim(), line: i + 1 });
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Return the text under a heading (case-insensitive match on heading text),
|
|
116
|
+
* up to the next heading of the same or higher level. Returns null if not found.
|
|
117
|
+
*/
|
|
118
|
+
export function extractSection(body, heading) {
|
|
119
|
+
const lines = body.split(/\r?\n/);
|
|
120
|
+
const want = heading.trim().toLowerCase();
|
|
121
|
+
const headings = extractHeadings(body);
|
|
122
|
+
const start = headings.find((h) => h.text.toLowerCase() === want);
|
|
123
|
+
if (!start)
|
|
124
|
+
return null;
|
|
125
|
+
const startIdx = start.line - 1;
|
|
126
|
+
let endIdx = lines.length;
|
|
127
|
+
for (const h of headings) {
|
|
128
|
+
if (h.line - 1 > startIdx && h.level <= start.level) {
|
|
129
|
+
endIdx = h.line - 1;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Include the heading line itself for context.
|
|
134
|
+
return lines.slice(startIdx, endIdx).join("\n").trimEnd();
|
|
135
|
+
}
|
|
136
|
+
/** Extract wiki-link targets (`[[name]]` / `[[name|alias]]`), normalized + deduped. */
|
|
137
|
+
export function extractWikiLinks(body) {
|
|
138
|
+
const seen = new Set();
|
|
139
|
+
const out = [];
|
|
140
|
+
let m;
|
|
141
|
+
WIKI_LINK.lastIndex = 0;
|
|
142
|
+
while ((m = WIKI_LINK.exec(body)) !== null) {
|
|
143
|
+
const target = normalizeLinkTarget(m[1].split("|")[0]);
|
|
144
|
+
if (target && !seen.has(target)) {
|
|
145
|
+
seen.add(target);
|
|
146
|
+
out.push(target);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
/** Collect tags from frontmatter plus inline #hashtags in the body, deduped. */
|
|
152
|
+
export function extractTags(frontmatter, body) {
|
|
153
|
+
const seen = new Set();
|
|
154
|
+
const out = [];
|
|
155
|
+
const add = (t) => {
|
|
156
|
+
const tag = t.trim().replace(/^#/, "");
|
|
157
|
+
const key = tag.toLowerCase();
|
|
158
|
+
if (tag && !seen.has(key)) {
|
|
159
|
+
seen.add(key);
|
|
160
|
+
out.push(tag);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
if (Array.isArray(frontmatter.tags))
|
|
164
|
+
for (const t of frontmatter.tags)
|
|
165
|
+
add(String(t));
|
|
166
|
+
// Strip fenced code so code samples don't pollute tags.
|
|
167
|
+
const withoutFences = body.replace(/(```+|~~~+)[\s\S]*?\1/g, "");
|
|
168
|
+
let m;
|
|
169
|
+
HASHTAG.lastIndex = 0;
|
|
170
|
+
while ((m = HASHTAG.exec(withoutFences)) !== null)
|
|
171
|
+
add(m[1]);
|
|
172
|
+
return out;
|
|
173
|
+
}
|
|
174
|
+
/** Extract checkbox todos (`- [ ]` / `- [x]`) from body text. */
|
|
175
|
+
export function extractTodos(body) {
|
|
176
|
+
const out = [];
|
|
177
|
+
const lines = body.split(/\r?\n/);
|
|
178
|
+
for (let i = 0; i < lines.length; i++) {
|
|
179
|
+
const m = /^[ \t]*[-*+][ \t]+\[([ xX])\][ \t]+(.*)$/.exec(lines[i]);
|
|
180
|
+
if (m)
|
|
181
|
+
out.push({ done: m[1].toLowerCase() === "x", text: m[2].trim(), line: i + 1 });
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
/** Full parse of a raw note into structured pieces used by the index and graph. */
|
|
186
|
+
export function parseNote(raw) {
|
|
187
|
+
const { frontmatter, body } = splitFrontmatter(raw);
|
|
188
|
+
const headings = extractHeadings(body);
|
|
189
|
+
const tags = extractTags(frontmatter, body);
|
|
190
|
+
const links = extractWikiLinks(body);
|
|
191
|
+
let title;
|
|
192
|
+
if (typeof frontmatter.title === "string" && frontmatter.title.trim()) {
|
|
193
|
+
title = frontmatter.title.trim();
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const h1 = headings.find((h) => h.level === 1);
|
|
197
|
+
if (h1)
|
|
198
|
+
title = h1.text;
|
|
199
|
+
}
|
|
200
|
+
return { frontmatter, body, title, tags, links, headings };
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type SearchHit } from "./store.js";
|
|
2
|
+
/**
|
|
3
|
+
* Rank notes by semantic similarity to `query`. With `hybrid`, fuse the vector
|
|
4
|
+
* ranking with MiniSearch's lexical ranking via Reciprocal Rank Fusion (k=60).
|
|
5
|
+
* First call downloads/loads the model and embeds the whole vault.
|
|
6
|
+
*/
|
|
7
|
+
export declare function semanticSearch(query: string, opts?: {
|
|
8
|
+
limit?: number;
|
|
9
|
+
hybrid?: boolean;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
hits: SearchHit[];
|
|
12
|
+
total: number;
|
|
13
|
+
hasMore: boolean;
|
|
14
|
+
}>;
|
package/dist/semantic.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic vector store: keeps an L2-normalized embedding per note, persisted to
|
|
3
|
+
* a sidecar cache and incrementally synced (mtime/size) against the text index.
|
|
4
|
+
* Mirrors store.ts's cache+sync pattern. The embedding model is only touched
|
|
5
|
+
* when a semantic search actually runs (first call embeds the whole vault).
|
|
6
|
+
*/
|
|
7
|
+
import { promises as fs } from "node:fs";
|
|
8
|
+
import { EMBED_CACHE_VERSION, EMBED_DIM, EMBED_MODEL_ID, getEmbeddingsPath, cacheDisabled, } from "./config.js";
|
|
9
|
+
import { atomicWrite, readRaw, resolveSafe } from "./fsutil.js";
|
|
10
|
+
import { parseNote } from "./parse.js";
|
|
11
|
+
import { embed } from "./embed.js";
|
|
12
|
+
import { getAllMeta, searchNotes } from "./store.js";
|
|
13
|
+
const vectors = new Map();
|
|
14
|
+
let cacheLoaded = false;
|
|
15
|
+
function encodeVec(v) {
|
|
16
|
+
return Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64");
|
|
17
|
+
}
|
|
18
|
+
function decodeVec(b64) {
|
|
19
|
+
const buf = Buffer.from(b64, "base64");
|
|
20
|
+
return new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);
|
|
21
|
+
}
|
|
22
|
+
/** Load the persisted vectors once (ignored if model/dim/version changed). */
|
|
23
|
+
async function loadCache() {
|
|
24
|
+
if (cacheLoaded || cacheDisabled()) {
|
|
25
|
+
cacheLoaded = true;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
cacheLoaded = true;
|
|
29
|
+
try {
|
|
30
|
+
const cache = JSON.parse(await fs.readFile(getEmbeddingsPath(), "utf8"));
|
|
31
|
+
if (cache.version !== EMBED_CACHE_VERSION || cache.model !== EMBED_MODEL_ID || cache.dim !== EMBED_DIM) {
|
|
32
|
+
return; // stale → will be rebuilt by sync
|
|
33
|
+
}
|
|
34
|
+
for (const [name, e] of Object.entries(cache.perNote ?? {})) {
|
|
35
|
+
vectors.set(name, { mtimeMs: e.mtimeMs, size: e.size, vec: decodeVec(e.vector) });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
if (err.code !== "ENOENT") {
|
|
40
|
+
console.error(`Embeddings cache unreadable, rebuilding: ${err.message}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function writeCache() {
|
|
45
|
+
if (cacheDisabled())
|
|
46
|
+
return;
|
|
47
|
+
const perNote = {};
|
|
48
|
+
for (const [name, e] of vectors)
|
|
49
|
+
perNote[name] = { mtimeMs: e.mtimeMs, size: e.size, vector: encodeVec(e.vec) };
|
|
50
|
+
const cache = { version: EMBED_CACHE_VERSION, model: EMBED_MODEL_ID, dim: EMBED_DIM, perNote };
|
|
51
|
+
try {
|
|
52
|
+
await atomicWrite(getEmbeddingsPath(), JSON.stringify(cache));
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
console.error(`Could not write embeddings cache: ${err.message}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Embed new/changed notes, drop deleted ones, persist if anything changed. */
|
|
59
|
+
async function sync() {
|
|
60
|
+
await loadCache();
|
|
61
|
+
const meta = getAllMeta();
|
|
62
|
+
let changed = false;
|
|
63
|
+
for (const [name, m] of meta) {
|
|
64
|
+
const prev = vectors.get(name);
|
|
65
|
+
if (prev && prev.mtimeMs === m.mtimeMs && prev.size === m.size)
|
|
66
|
+
continue;
|
|
67
|
+
try {
|
|
68
|
+
const { title, body } = parseNote(await readRaw(await resolveSafe(name)));
|
|
69
|
+
const vec = await embed(`${title ?? name}\n${body}`);
|
|
70
|
+
vectors.set(name, { mtimeMs: m.mtimeMs, size: m.size, vec });
|
|
71
|
+
changed = true;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
console.error(`Could not embed "${name}": ${err.message}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
for (const name of [...vectors.keys()]) {
|
|
78
|
+
if (!meta.has(name)) {
|
|
79
|
+
vectors.delete(name);
|
|
80
|
+
changed = true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (changed)
|
|
84
|
+
await writeCache();
|
|
85
|
+
}
|
|
86
|
+
function dot(a, b) {
|
|
87
|
+
let s = 0;
|
|
88
|
+
for (let i = 0; i < EMBED_DIM; i++)
|
|
89
|
+
s += a[i] * b[i];
|
|
90
|
+
return s;
|
|
91
|
+
}
|
|
92
|
+
function snippet(body, width = 160) {
|
|
93
|
+
const flat = body.replace(/\s+/g, " ").trim();
|
|
94
|
+
return flat.length > width ? `${flat.slice(0, width)}…` : flat;
|
|
95
|
+
}
|
|
96
|
+
async function snippetFor(name) {
|
|
97
|
+
try {
|
|
98
|
+
return snippet(parseNote(await readRaw(await resolveSafe(name))).body);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return "";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Rank notes by semantic similarity to `query`. With `hybrid`, fuse the vector
|
|
106
|
+
* ranking with MiniSearch's lexical ranking via Reciprocal Rank Fusion (k=60).
|
|
107
|
+
* First call downloads/loads the model and embeds the whole vault.
|
|
108
|
+
*/
|
|
109
|
+
export async function semanticSearch(query, opts = {}) {
|
|
110
|
+
const limit = opts.limit ?? 10;
|
|
111
|
+
await sync();
|
|
112
|
+
const qv = await embed(query);
|
|
113
|
+
const ranked = [...vectors.entries()]
|
|
114
|
+
.map(([name, e]) => ({ name, score: dot(qv, e.vec) }))
|
|
115
|
+
.sort((a, b) => b.score - a.score);
|
|
116
|
+
let order;
|
|
117
|
+
if (opts.hybrid) {
|
|
118
|
+
const K = 60;
|
|
119
|
+
const fused = new Map();
|
|
120
|
+
ranked.slice(0, 50).forEach((r, i) => fused.set(r.name, (fused.get(r.name) ?? 0) + 1 / (K + i + 1)));
|
|
121
|
+
const lexical = await searchNotes(query, { limit: 50 });
|
|
122
|
+
lexical.hits.forEach((h, i) => fused.set(h.name, (fused.get(h.name) ?? 0) + 1 / (K + i + 1)));
|
|
123
|
+
order = [...fused.entries()].map(([name, score]) => ({ name, score })).sort((a, b) => b.score - a.score);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
order = ranked;
|
|
127
|
+
}
|
|
128
|
+
const total = order.length;
|
|
129
|
+
const page = order.slice(0, limit);
|
|
130
|
+
const meta = getAllMeta();
|
|
131
|
+
const hits = [];
|
|
132
|
+
for (const r of page) {
|
|
133
|
+
hits.push({
|
|
134
|
+
name: r.name,
|
|
135
|
+
title: meta.get(r.name)?.title ?? r.name,
|
|
136
|
+
score: r.score,
|
|
137
|
+
snippet: await snippetFor(r.name),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return { hits, total, hasMore: limit < total };
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=semantic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic.js","sourceRoot":"","sources":["../src/semantic.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { validateName } from "./fsutil.js";
|
|
2
|
+
/** Per-note metadata kept in memory and persisted to the cache. */
|
|
3
|
+
export interface NoteMeta {
|
|
4
|
+
mtimeMs: number;
|
|
5
|
+
size: number;
|
|
6
|
+
title: string;
|
|
7
|
+
tags: string[];
|
|
8
|
+
outLinks: string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Load the index. Tries the cache and incrementally syncs against disk
|
|
12
|
+
* (re-parsing only new/changed files, dropping deleted ones). Falls back to a
|
|
13
|
+
* full rebuild if the cache is missing, unreadable, or the version changed.
|
|
14
|
+
* Call once on startup before serving requests.
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildIndex(): Promise<void>;
|
|
17
|
+
export declare function getAllMeta(): Map<string, NoteMeta>;
|
|
18
|
+
export declare function getMeta(name: string): NoteMeta | undefined;
|
|
19
|
+
export declare function noteExists(name: string): boolean;
|
|
20
|
+
export interface NoteSummary {
|
|
21
|
+
name: string;
|
|
22
|
+
title: string;
|
|
23
|
+
size: number;
|
|
24
|
+
mtimeMs: number;
|
|
25
|
+
tags: string[];
|
|
26
|
+
}
|
|
27
|
+
export interface ListResult {
|
|
28
|
+
items: NoteSummary[];
|
|
29
|
+
total: number;
|
|
30
|
+
hasMore: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** List notes (newest first), optionally filtered by tag, with pagination. */
|
|
33
|
+
export declare function listNotes(offset?: number, limit?: number, tag?: string): ListResult;
|
|
34
|
+
export interface ReadResult {
|
|
35
|
+
text: string;
|
|
36
|
+
truncated: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Read a note's content. With `section`, returns just that heading's block.
|
|
40
|
+
* With `offset`/`limit` (character window), returns a slice + a `truncated` flag.
|
|
41
|
+
*/
|
|
42
|
+
export declare function readNote(name: string, opts?: {
|
|
43
|
+
section?: string;
|
|
44
|
+
offset?: number;
|
|
45
|
+
limit?: number;
|
|
46
|
+
}): Promise<ReadResult>;
|
|
47
|
+
/** Heading-tree outline of a note (cheap way to grasp a big note). */
|
|
48
|
+
export declare function getOutline(name: string): Promise<string>;
|
|
49
|
+
export declare function createNote(name: string, content: string, overwrite?: boolean): Promise<string>;
|
|
50
|
+
export declare function appendNote(name: string, content: string): Promise<string>;
|
|
51
|
+
export declare function deleteNote(name: string): Promise<void>;
|
|
52
|
+
export interface MoveResult {
|
|
53
|
+
from: string;
|
|
54
|
+
to: string;
|
|
55
|
+
rewritten: string[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Move/rename a note and rewrite every `[[from]]` wiki-link across the vault to
|
|
59
|
+
* `[[to]]`. Keeps the index, graph and cache consistent.
|
|
60
|
+
*/
|
|
61
|
+
export declare function moveNote(from: string, to: string): Promise<MoveResult>;
|
|
62
|
+
export interface SearchHit {
|
|
63
|
+
name: string;
|
|
64
|
+
title: string;
|
|
65
|
+
score: number;
|
|
66
|
+
snippet: string;
|
|
67
|
+
}
|
|
68
|
+
export interface SearchResultPage {
|
|
69
|
+
hits: SearchHit[];
|
|
70
|
+
total: number;
|
|
71
|
+
hasMore: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Ranked full-text search. Supports `fuzzy`, `prefix`, a `field` filter
|
|
75
|
+
* (title/tag/body, or `path` to match the note name), and pagination. Returns
|
|
76
|
+
* ranked snippets with surrounding context.
|
|
77
|
+
*/
|
|
78
|
+
export declare function searchNotes(query: string, opts?: {
|
|
79
|
+
fuzzy?: boolean;
|
|
80
|
+
prefix?: boolean;
|
|
81
|
+
field?: string;
|
|
82
|
+
offset?: number;
|
|
83
|
+
limit?: number;
|
|
84
|
+
}): Promise<SearchResultPage>;
|
|
85
|
+
export interface TagCount {
|
|
86
|
+
tag: string;
|
|
87
|
+
count: number;
|
|
88
|
+
}
|
|
89
|
+
/** All tags across the vault with note counts (case-insensitive grouping). */
|
|
90
|
+
export declare function listTags(): TagCount[];
|
|
91
|
+
export interface TodoItem {
|
|
92
|
+
note: string;
|
|
93
|
+
text: string;
|
|
94
|
+
done: boolean;
|
|
95
|
+
line: number;
|
|
96
|
+
}
|
|
97
|
+
/** Aggregate `- [ ]` / `- [x]` checkboxes across all notes. */
|
|
98
|
+
export declare function listTodos(includeDone?: boolean): Promise<TodoItem[]>;
|
|
99
|
+
/** Notes-dir banner for startup logging. */
|
|
100
|
+
export declare function notesDir(): string;
|
|
101
|
+
/** Exposed for tests: validate a name without touching disk. */
|
|
102
|
+
export { validateName };
|