@abhishekmcp/notes 0.1.0 → 0.2.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/dist/store.js ADDED
@@ -0,0 +1,410 @@
1
+ /**
2
+ * The notes store: an in-memory MiniSearch full-text index + a wiki-link/tag
3
+ * graph (derived from per-note metadata), persisted to a JSON cache for fast
4
+ * warm starts with incremental mtime/size sync. Files on disk are the source of
5
+ * truth; this module keeps the index, the graph metadata, and the cache in sync
6
+ * across every mutation. All mutating ops are refused when NOTES_READONLY=1.
7
+ */
8
+ import { promises as fs } from "node:fs";
9
+ import MiniSearch from "minisearch";
10
+ import { INDEX_VERSION, cacheDisabled, getIndexPath, getNotesDir, isReadOnly, } from "./config.js";
11
+ import { atomicWrite, listNoteFiles, readRaw, resolveSafe, validateName, } from "./fsutil.js";
12
+ import { extractSection, extractTodos, normalizeLinkTarget, parseNote, } from "./parse.js";
13
+ /** One fixed options object — reused for `new MiniSearch` and `loadJSON`. */
14
+ const MINI_OPTS = {
15
+ idField: "name",
16
+ fields: ["title", "body", "tags"],
17
+ storeFields: ["name", "title", "tags"],
18
+ };
19
+ let mini = new MiniSearch(MINI_OPTS);
20
+ const perNote = new Map();
21
+ // --- Index construction ---------------------------------------------------
22
+ /** Read + parse a note file into a search doc and its metadata. */
23
+ async function buildEntry(name, fullPath, mtimeMs, size) {
24
+ const raw = await readRaw(fullPath);
25
+ const parsed = parseNote(raw);
26
+ const title = parsed.title ?? name;
27
+ const meta = { mtimeMs, size, title, tags: parsed.tags, outLinks: parsed.links };
28
+ const doc = { name, title, body: parsed.body, tags: parsed.tags.join(" ") };
29
+ return { doc, meta };
30
+ }
31
+ /** Add or replace a single note in the in-memory index + metadata map. */
32
+ async function indexNote(name, fullPath, mtimeMs, size) {
33
+ const { doc, meta } = await buildEntry(name, fullPath, mtimeMs, size);
34
+ if (perNote.has(name))
35
+ mini.discard(name);
36
+ mini.add(doc);
37
+ perNote.set(name, meta);
38
+ }
39
+ /** Remove a note from the in-memory index + metadata map. */
40
+ function unindexNote(name) {
41
+ if (perNote.has(name)) {
42
+ mini.discard(name);
43
+ perNote.delete(name);
44
+ }
45
+ }
46
+ /** Build the index from scratch by walking the notes dir. */
47
+ async function fullRebuild() {
48
+ mini = new MiniSearch(MINI_OPTS);
49
+ perNote.clear();
50
+ const files = await listNoteFiles();
51
+ for (const f of files) {
52
+ try {
53
+ await indexNote(f.name, f.fullPath, f.mtimeMs, f.size);
54
+ }
55
+ catch (err) {
56
+ console.error(`Skipping unindexable note "${f.name}": ${err.message}`);
57
+ }
58
+ }
59
+ }
60
+ /** Persist the index + metadata to the on-disk cache (best-effort). */
61
+ async function writeCache() {
62
+ if (cacheDisabled())
63
+ return;
64
+ const cache = {
65
+ version: INDEX_VERSION,
66
+ perNote: Object.fromEntries(perNote),
67
+ minisearch: JSON.parse(JSON.stringify(mini)),
68
+ };
69
+ try {
70
+ await atomicWrite(getIndexPath(), JSON.stringify(cache));
71
+ }
72
+ catch (err) {
73
+ console.error(`Could not write index cache: ${err.message}`);
74
+ }
75
+ }
76
+ /**
77
+ * Load the index. Tries the cache and incrementally syncs against disk
78
+ * (re-parsing only new/changed files, dropping deleted ones). Falls back to a
79
+ * full rebuild if the cache is missing, unreadable, or the version changed.
80
+ * Call once on startup before serving requests.
81
+ */
82
+ export async function buildIndex() {
83
+ if (cacheDisabled()) {
84
+ await fullRebuild();
85
+ return;
86
+ }
87
+ let loaded = false;
88
+ try {
89
+ const raw = await fs.readFile(getIndexPath(), "utf8");
90
+ const cache = JSON.parse(raw);
91
+ if (cache.version === INDEX_VERSION && cache.minisearch) {
92
+ mini = MiniSearch.loadJSON(JSON.stringify(cache.minisearch), MINI_OPTS);
93
+ perNote.clear();
94
+ for (const [name, meta] of Object.entries(cache.perNote ?? {}))
95
+ perNote.set(name, meta);
96
+ loaded = true;
97
+ }
98
+ }
99
+ catch (err) {
100
+ if (err.code !== "ENOENT") {
101
+ console.error(`Index cache unreadable, rebuilding: ${err.message}`);
102
+ }
103
+ }
104
+ if (!loaded) {
105
+ await fullRebuild();
106
+ await writeCache();
107
+ return;
108
+ }
109
+ // Incremental sync against the current files on disk.
110
+ let changed = false;
111
+ const files = await listNoteFiles();
112
+ const onDisk = new Set();
113
+ for (const f of files) {
114
+ onDisk.add(f.name);
115
+ const prev = perNote.get(f.name);
116
+ if (!prev || prev.mtimeMs !== f.mtimeMs || prev.size !== f.size) {
117
+ try {
118
+ await indexNote(f.name, f.fullPath, f.mtimeMs, f.size);
119
+ changed = true;
120
+ }
121
+ catch (err) {
122
+ console.error(`Skipping unindexable note "${f.name}": ${err.message}`);
123
+ }
124
+ }
125
+ }
126
+ for (const name of [...perNote.keys()]) {
127
+ if (!onDisk.has(name)) {
128
+ unindexNote(name);
129
+ changed = true;
130
+ }
131
+ }
132
+ if (changed)
133
+ await writeCache();
134
+ }
135
+ // --- Graph accessors (used by graph.ts) -----------------------------------
136
+ export function getAllMeta() {
137
+ return perNote;
138
+ }
139
+ export function getMeta(name) {
140
+ return perNote.get(normalizeLinkTarget(name));
141
+ }
142
+ export function noteExists(name) {
143
+ return perNote.has(normalizeLinkTarget(name));
144
+ }
145
+ /** List notes (newest first), optionally filtered by tag, with pagination. */
146
+ export function listNotes(offset = 0, limit = 50, tag) {
147
+ let entries = [...perNote.entries()];
148
+ if (tag) {
149
+ const want = tag.toLowerCase().replace(/^#/, "");
150
+ entries = entries.filter(([, m]) => m.tags.some((t) => t.toLowerCase() === want));
151
+ }
152
+ entries.sort((a, b) => b[1].mtimeMs - a[1].mtimeMs);
153
+ const total = entries.length;
154
+ const page = entries.slice(offset, offset + limit);
155
+ return {
156
+ items: page.map(([name, m]) => ({
157
+ name,
158
+ title: m.title,
159
+ size: m.size,
160
+ mtimeMs: m.mtimeMs,
161
+ tags: m.tags,
162
+ })),
163
+ total,
164
+ hasMore: offset + limit < total,
165
+ };
166
+ }
167
+ /**
168
+ * Read a note's content. With `section`, returns just that heading's block.
169
+ * With `offset`/`limit` (character window), returns a slice + a `truncated` flag.
170
+ */
171
+ export async function readNote(name, opts = {}) {
172
+ const abs = await resolveSafe(name);
173
+ const raw = await readRaw(abs);
174
+ let text = raw;
175
+ if (opts.section) {
176
+ const { body } = parseNote(raw);
177
+ const section = extractSection(body, opts.section);
178
+ if (section === null)
179
+ return { text: `Section "${opts.section}" not found in "${name}".`, truncated: false };
180
+ text = section;
181
+ }
182
+ if (opts.offset !== undefined || opts.limit !== undefined) {
183
+ const start = Math.max(0, opts.offset ?? 0);
184
+ const end = opts.limit !== undefined ? start + opts.limit : text.length;
185
+ const sliced = text.slice(start, end);
186
+ return { text: sliced, truncated: end < text.length };
187
+ }
188
+ return { text, truncated: false };
189
+ }
190
+ /** Heading-tree outline of a note (cheap way to grasp a big note). */
191
+ export async function getOutline(name) {
192
+ const abs = await resolveSafe(name);
193
+ const raw = await readRaw(abs);
194
+ const { headings } = parseNote(raw);
195
+ if (headings.length === 0)
196
+ return `"${name}" has no headings.`;
197
+ return headings.map((h) => `${" ".repeat(h.level - 1)}- ${h.text}`).join("\n");
198
+ }
199
+ // --- Mutations ------------------------------------------------------------
200
+ function assertWritable() {
201
+ if (isReadOnly())
202
+ throw new Error("Server is read-only (NOTES_READONLY=1); mutations are disabled.");
203
+ }
204
+ /** Re-stat a file and (re)index it, then persist the cache. */
205
+ async function syncFromDisk(name) {
206
+ const abs = await resolveSafe(name);
207
+ const stat = await fs.stat(abs);
208
+ await indexNote(normalizeLinkTarget(name), abs, stat.mtimeMs, stat.size);
209
+ }
210
+ export async function createNote(name, content, overwrite = false) {
211
+ assertWritable();
212
+ const abs = await resolveSafe(name);
213
+ if (!overwrite) {
214
+ try {
215
+ await fs.access(abs);
216
+ throw new Error(`Note "${name}" already exists. Use overwrite, or append_note.`);
217
+ }
218
+ catch (err) {
219
+ if (err.code !== "ENOENT")
220
+ throw err;
221
+ }
222
+ }
223
+ await atomicWrite(abs, content);
224
+ await syncFromDisk(name);
225
+ await writeCache();
226
+ return normalizeLinkTarget(name);
227
+ }
228
+ export async function appendNote(name, content) {
229
+ assertWritable();
230
+ const abs = await resolveSafe(name);
231
+ let existing = "";
232
+ try {
233
+ existing = await readRaw(abs);
234
+ }
235
+ catch (err) {
236
+ if (err.code !== "ENOENT")
237
+ throw err;
238
+ }
239
+ const sep = existing && !existing.endsWith("\n") ? "\n\n" : existing ? "\n" : "";
240
+ await atomicWrite(abs, existing + sep + content);
241
+ await syncFromDisk(name);
242
+ await writeCache();
243
+ return normalizeLinkTarget(name);
244
+ }
245
+ export async function deleteNote(name) {
246
+ assertWritable();
247
+ const abs = await resolveSafe(name);
248
+ await fs.unlink(abs);
249
+ unindexNote(normalizeLinkTarget(name));
250
+ await writeCache();
251
+ }
252
+ /** Replace wiki-links in `text` that point at `from` with `to` (alias preserved). */
253
+ function rewriteLinks(text, from, to) {
254
+ const fromNorm = normalizeLinkTarget(from);
255
+ return text.replace(/\[\[([^\]]+)\]\]/g, (whole, inner) => {
256
+ const [target, ...aliasParts] = inner.split("|");
257
+ if (normalizeLinkTarget(target) !== fromNorm)
258
+ return whole;
259
+ const alias = aliasParts.length ? `|${aliasParts.join("|")}` : "";
260
+ return `[[${to}${alias}]]`;
261
+ });
262
+ }
263
+ /**
264
+ * Move/rename a note and rewrite every `[[from]]` wiki-link across the vault to
265
+ * `[[to]]`. Keeps the index, graph and cache consistent.
266
+ */
267
+ export async function moveNote(from, to) {
268
+ assertWritable();
269
+ const fromNorm = normalizeLinkTarget(from);
270
+ const toNorm = normalizeLinkTarget(to);
271
+ const fromAbs = await resolveSafe(from);
272
+ const toAbs = await resolveSafe(to);
273
+ // Move the file (refuse to clobber an existing target).
274
+ try {
275
+ await fs.access(toAbs);
276
+ throw new Error(`Target note "${to}" already exists.`);
277
+ }
278
+ catch (err) {
279
+ if (err.code !== "ENOENT")
280
+ throw err;
281
+ }
282
+ const content = await readRaw(fromAbs);
283
+ await atomicWrite(toAbs, content);
284
+ await fs.unlink(fromAbs);
285
+ unindexNote(fromNorm);
286
+ await syncFromDisk(toNorm);
287
+ // Rewrite backlinks in every note that linked to `from`.
288
+ const rewritten = [];
289
+ for (const [name, meta] of [...perNote.entries()]) {
290
+ if (name === toNorm)
291
+ continue;
292
+ if (!meta.outLinks.some((l) => normalizeLinkTarget(l) === fromNorm))
293
+ continue;
294
+ const abs = await resolveSafe(name);
295
+ const text = await readRaw(abs);
296
+ const updated = rewriteLinks(text, fromNorm, toNorm);
297
+ if (updated !== text) {
298
+ await atomicWrite(abs, updated);
299
+ await syncFromDisk(name);
300
+ rewritten.push(name);
301
+ }
302
+ }
303
+ await writeCache();
304
+ return { from: fromNorm, to: toNorm, rewritten };
305
+ }
306
+ const FIELD_MAP = { title: "title", tag: "tags", tags: "tags", body: "body" };
307
+ /** Build a ±context snippet around the first query-term hit in the body. */
308
+ function makeSnippet(body, terms, width = 160) {
309
+ const flat = body.replace(/\s+/g, " ").trim();
310
+ if (!flat)
311
+ return "";
312
+ const lower = flat.toLowerCase();
313
+ let pos = -1;
314
+ for (const t of terms) {
315
+ const i = lower.indexOf(t);
316
+ if (i !== -1 && (pos === -1 || i < pos))
317
+ pos = i;
318
+ }
319
+ if (pos === -1)
320
+ return flat.slice(0, width) + (flat.length > width ? "…" : "");
321
+ const half = Math.floor(width / 2);
322
+ const start = Math.max(0, pos - half);
323
+ const end = Math.min(flat.length, pos + half);
324
+ return (start > 0 ? "…" : "") + flat.slice(start, end).trim() + (end < flat.length ? "…" : "");
325
+ }
326
+ /**
327
+ * Ranked full-text search. Supports `fuzzy`, `prefix`, a `field` filter
328
+ * (title/tag/body, or `path` to match the note name), and pagination. Returns
329
+ * ranked snippets with surrounding context.
330
+ */
331
+ export async function searchNotes(query, opts = {}) {
332
+ const offset = opts.offset ?? 0;
333
+ const limit = opts.limit ?? 10;
334
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
335
+ // `path:` is not an indexed field — filter note names directly.
336
+ if (opts.field === "path" || opts.field === "name") {
337
+ const matches = [...perNote.entries()]
338
+ .filter(([name]) => terms.every((t) => name.toLowerCase().includes(t)))
339
+ .map(([name, m]) => ({ name, title: m.title, score: 1, snippet: "" }));
340
+ return { hits: matches.slice(offset, offset + limit), total: matches.length, hasMore: offset + limit < matches.length };
341
+ }
342
+ const searchOpts = {
343
+ prefix: opts.prefix ?? true,
344
+ fuzzy: opts.fuzzy ? 0.2 : false,
345
+ boost: { title: 2, tags: 1.5 },
346
+ };
347
+ if (opts.field && FIELD_MAP[opts.field])
348
+ searchOpts.fields = [FIELD_MAP[opts.field]];
349
+ const results = mini.search(query, searchOpts);
350
+ const total = results.length;
351
+ const page = results.slice(offset, offset + limit);
352
+ const hits = [];
353
+ for (const r of page) {
354
+ const name = r.id;
355
+ let snippet = "";
356
+ try {
357
+ const abs = await resolveSafe(name);
358
+ const { body } = parseNote(await readRaw(abs));
359
+ snippet = makeSnippet(body, terms);
360
+ }
361
+ catch {
362
+ // Note vanished since indexing — skip snippet.
363
+ }
364
+ hits.push({ name, title: r.title ?? name, score: r.score, snippet });
365
+ }
366
+ return { hits, total, hasMore: offset + limit < total };
367
+ }
368
+ /** All tags across the vault with note counts (case-insensitive grouping). */
369
+ export function listTags() {
370
+ const counts = new Map();
371
+ for (const m of perNote.values()) {
372
+ for (const t of m.tags) {
373
+ const key = t.toLowerCase();
374
+ const cur = counts.get(key);
375
+ if (cur)
376
+ cur.count++;
377
+ else
378
+ counts.set(key, { display: t, count: 1 });
379
+ }
380
+ }
381
+ return [...counts.values()]
382
+ .map((c) => ({ tag: c.display, count: c.count }))
383
+ .sort((a, b) => b.count - a.count || a.tag.localeCompare(b.tag));
384
+ }
385
+ /** Aggregate `- [ ]` / `- [x]` checkboxes across all notes. */
386
+ export async function listTodos(includeDone = false) {
387
+ const out = [];
388
+ for (const name of perNote.keys()) {
389
+ try {
390
+ const abs = await resolveSafe(name);
391
+ const { body } = parseNote(await readRaw(abs));
392
+ for (const t of extractTodos(body)) {
393
+ if (!includeDone && t.done)
394
+ continue;
395
+ out.push({ note: name, text: t.text, done: t.done, line: t.line });
396
+ }
397
+ }
398
+ catch {
399
+ // skip unreadable note
400
+ }
401
+ }
402
+ return out;
403
+ }
404
+ /** Notes-dir banner for startup logging. */
405
+ export function notesDir() {
406
+ return getNotesDir();
407
+ }
408
+ /** Exposed for tests: validate a name without touching disk. */
409
+ export { validateName };
410
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@abhishekmcp/notes",
3
- "version": "0.1.0",
4
- "description": "MCP server for local markdown notes — search, create, link, and read notes from any MCP client.",
3
+ "version": "0.2.0",
4
+ "description": "MCP server for local markdown notes — ranked search, tags, todos, and a wiki-link knowledge graph, from any MCP client.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "mcp-notes": "dist/index.js"
@@ -43,6 +43,7 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@modelcontextprotocol/sdk": "^1.29.0",
46
+ "minisearch": "^7.1.0",
46
47
  "zod": "^3.23.8"
47
48
  },
48
49
  "devDependencies": {
package/dist/notes.d.ts DELETED
@@ -1,36 +0,0 @@
1
- /**
2
- * Resolve the notes directory from the NOTES_DIR env var, defaulting to ~/notes.
3
- * A leading "~" is expanded to the user's home directory.
4
- */
5
- export declare function getNotesDir(): string;
6
- /** Ensure the notes directory exists. */
7
- export declare function ensureNotesDir(): Promise<string>;
8
- /**
9
- * Turn a user-supplied note name into a safe absolute path inside the notes
10
- * directory. Throws if the resolved path would escape the notes directory.
11
- * A ".md" extension is added when missing.
12
- */
13
- export declare function resolveNotePath(name: string): string;
14
- export interface NoteInfo {
15
- name: string;
16
- size: number;
17
- modified: string;
18
- }
19
- /** Recursively list all markdown notes in the notes directory. */
20
- export declare function listNotes(): Promise<NoteInfo[]>;
21
- export declare function readNote(name: string): Promise<string>;
22
- export declare function createNote(name: string, content: string, overwrite?: boolean): Promise<string>;
23
- export declare function appendNote(name: string, content: string): Promise<string>;
24
- export declare function deleteNote(name: string): Promise<void>;
25
- export interface SearchHit {
26
- name: string;
27
- line: number;
28
- text: string;
29
- }
30
- /** Case-insensitive substring search across all notes. */
31
- export declare function searchNotes(query: string, limit?: number): Promise<SearchHit[]>;
32
- /**
33
- * Find notes that link to the given note via [[wiki-link]] syntax.
34
- * Matches the bare note name (with or without the .md extension).
35
- */
36
- export declare function getBacklinks(name: string): Promise<SearchHit[]>;
package/dist/notes.js DELETED
@@ -1,138 +0,0 @@
1
- import { promises as fs } from "node:fs";
2
- import { homedir } from "node:os";
3
- import path from "node:path";
4
- /**
5
- * Resolve the notes directory from the NOTES_DIR env var, defaulting to ~/notes.
6
- * A leading "~" is expanded to the user's home directory.
7
- */
8
- export function getNotesDir() {
9
- const raw = process.env.NOTES_DIR ?? path.join(homedir(), "notes");
10
- const expanded = raw.startsWith("~")
11
- ? path.join(homedir(), raw.slice(1))
12
- : raw;
13
- return path.resolve(expanded);
14
- }
15
- /** Ensure the notes directory exists. */
16
- export async function ensureNotesDir() {
17
- const dir = getNotesDir();
18
- await fs.mkdir(dir, { recursive: true });
19
- return dir;
20
- }
21
- /**
22
- * Turn a user-supplied note name into a safe absolute path inside the notes
23
- * directory. Throws if the resolved path would escape the notes directory.
24
- * A ".md" extension is added when missing.
25
- */
26
- export function resolveNotePath(name) {
27
- const dir = getNotesDir();
28
- const withExt = name.endsWith(".md") ? name : `${name}.md`;
29
- const resolved = path.resolve(dir, withExt);
30
- const rel = path.relative(dir, resolved);
31
- if (rel.startsWith("..") || path.isAbsolute(rel)) {
32
- throw new Error(`Refusing to access "${name}": path escapes the notes directory.`);
33
- }
34
- return resolved;
35
- }
36
- /** Recursively list all markdown notes in the notes directory. */
37
- export async function listNotes() {
38
- const dir = await ensureNotesDir();
39
- const results = [];
40
- async function walk(current) {
41
- const entries = await fs.readdir(current, { withFileTypes: true });
42
- for (const entry of entries) {
43
- const full = path.join(current, entry.name);
44
- if (entry.isDirectory()) {
45
- await walk(full);
46
- }
47
- else if (entry.isFile() && entry.name.endsWith(".md")) {
48
- const stat = await fs.stat(full);
49
- const rel = path.relative(dir, full).replace(/\.md$/, "");
50
- results.push({
51
- name: rel,
52
- size: stat.size,
53
- modified: stat.mtime.toISOString(),
54
- });
55
- }
56
- }
57
- }
58
- await walk(dir);
59
- results.sort((a, b) => b.modified.localeCompare(a.modified));
60
- return results;
61
- }
62
- export async function readNote(name) {
63
- const file = resolveNotePath(name);
64
- return fs.readFile(file, "utf8");
65
- }
66
- export async function createNote(name, content, overwrite = false) {
67
- const file = resolveNotePath(name);
68
- await fs.mkdir(path.dirname(file), { recursive: true });
69
- if (!overwrite) {
70
- try {
71
- await fs.access(file);
72
- throw new Error(`Note "${name}" already exists. Use overwrite or append instead.`);
73
- }
74
- catch (err) {
75
- // ENOENT means it does not exist yet — that's what we want.
76
- if (err.code !== "ENOENT")
77
- throw err;
78
- }
79
- }
80
- await fs.writeFile(file, content, "utf8");
81
- return file;
82
- }
83
- export async function appendNote(name, content) {
84
- const file = resolveNotePath(name);
85
- await fs.mkdir(path.dirname(file), { recursive: true });
86
- // Separate appended content with a blank line for readability.
87
- const prefix = content.startsWith("\n") ? "" : "\n\n";
88
- await fs.appendFile(file, prefix + content, "utf8");
89
- return file;
90
- }
91
- export async function deleteNote(name) {
92
- const file = resolveNotePath(name);
93
- await fs.unlink(file);
94
- }
95
- /** Case-insensitive substring search across all notes. */
96
- export async function searchNotes(query, limit = 50) {
97
- const notes = await listNotes();
98
- const needle = query.toLowerCase();
99
- const hits = [];
100
- for (const note of notes) {
101
- const content = await readNote(note.name);
102
- const lines = content.split("\n");
103
- for (let i = 0; i < lines.length; i++) {
104
- if (lines[i].toLowerCase().includes(needle)) {
105
- hits.push({ name: note.name, line: i + 1, text: lines[i].trim() });
106
- if (hits.length >= limit)
107
- return hits;
108
- }
109
- }
110
- }
111
- return hits;
112
- }
113
- /**
114
- * Find notes that link to the given note via [[wiki-link]] syntax.
115
- * Matches the bare note name (with or without the .md extension).
116
- */
117
- export async function getBacklinks(name) {
118
- const target = name.replace(/\.md$/, "");
119
- const notes = await listNotes();
120
- const hits = [];
121
- for (const note of notes) {
122
- if (note.name === target)
123
- continue;
124
- const content = await readNote(note.name);
125
- const lines = content.split("\n");
126
- for (let i = 0; i < lines.length; i++) {
127
- const wikiLinks = lines[i].match(/\[\[([^\]]+)\]\]/g) ?? [];
128
- for (const link of wikiLinks) {
129
- const inner = link.slice(2, -2).split("|")[0].trim().replace(/\.md$/, "");
130
- if (inner === target) {
131
- hits.push({ name: note.name, line: i + 1, text: lines[i].trim() });
132
- }
133
- }
134
- }
135
- }
136
- return hits;
137
- }
138
- //# sourceMappingURL=notes.js.map
package/dist/notes.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"notes.js","sourceRoot":"","sources":["../src/notes.ts"],"names":[],"mappings":"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"}