@cardmagic/notes 1.0.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.
@@ -0,0 +1,129 @@
1
+ import chalk from 'chalk';
2
+ export function formatDate(unixTimestamp) {
3
+ if (!unixTimestamp || unixTimestamp <= 0) {
4
+ return 'Unknown';
5
+ }
6
+ return new Date(unixTimestamp * 1000).toLocaleDateString('en-US', {
7
+ year: 'numeric',
8
+ month: 'short',
9
+ day: 'numeric',
10
+ hour: '2-digit',
11
+ minute: '2-digit',
12
+ });
13
+ }
14
+ export function formatNote(note, showBody = false) {
15
+ const lines = [];
16
+ // Title with pin indicator
17
+ const titlePrefix = note.isPinned ? chalk.yellow('📌 ') : '';
18
+ const lockSuffix = note.isLocked ? chalk.red(' 🔒') : '';
19
+ lines.push(titlePrefix + chalk.bold.cyan(note.title || 'Untitled') + lockSuffix);
20
+ // Folder
21
+ lines.push(chalk.dim(`📁 ${note.folder}`));
22
+ // Snippet
23
+ if (note.snippet) {
24
+ lines.push(chalk.gray(note.snippet));
25
+ }
26
+ // Body preview if requested
27
+ if (showBody && note.body) {
28
+ const bodyPreview = note.body.slice(0, 200).replace(/\n/g, ' ');
29
+ lines.push(chalk.dim(bodyPreview + (note.body.length > 200 ? '...' : '')));
30
+ }
31
+ // Modified date
32
+ lines.push(chalk.dim(`Modified: ${formatDate(note.modifiedAt)}`));
33
+ return lines.join('\n');
34
+ }
35
+ export function formatSearchResult(result, query) {
36
+ const lines = [];
37
+ // Match indicator and title
38
+ const titlePrefix = result.isPinned ? chalk.yellow('📌 ') : '';
39
+ const lockSuffix = result.isLocked ? chalk.red(' 🔒') : '';
40
+ const highlightedTitle = highlightMatches(result.title || 'Untitled', query);
41
+ lines.push(chalk.green('▶ ') + titlePrefix + chalk.bold.cyan(highlightedTitle) + lockSuffix);
42
+ // Folder
43
+ lines.push(chalk.dim(` 📁 ${result.folder}`));
44
+ // Highlighted snippet
45
+ if (result.snippet) {
46
+ const highlightedSnippet = highlightMatches(result.snippet, query);
47
+ lines.push(` ${highlightedSnippet}`);
48
+ }
49
+ // Score and matched terms
50
+ lines.push(chalk.dim(` Score: ${result.score.toFixed(2)} | Terms: ${result.matchedTerms.join(', ')}`));
51
+ // Modified date
52
+ lines.push(chalk.dim(` Modified: ${formatDate(result.modifiedAt)}`));
53
+ return lines.join('\n');
54
+ }
55
+ function highlightMatches(text, query) {
56
+ const terms = query.toLowerCase().split(/\s+/).filter(t => t.length > 0);
57
+ let highlighted = text;
58
+ for (const term of terms) {
59
+ const regex = new RegExp(`(${escapeRegex(term)})`, 'gi');
60
+ highlighted = highlighted.replace(regex, chalk.bgYellow.black('$1'));
61
+ }
62
+ return highlighted;
63
+ }
64
+ function escapeRegex(str) {
65
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
66
+ }
67
+ export function formatFolder(folder) {
68
+ const lines = [];
69
+ lines.push(chalk.bold.magenta(`📁 ${folder.name}`));
70
+ lines.push(chalk.dim(` ${folder.noteCount} notes | Last modified: ${formatDate(folder.lastModified)}`));
71
+ return lines.join('\n');
72
+ }
73
+ export function formatStats(stats) {
74
+ const lines = [];
75
+ lines.push(chalk.bold.cyan('📊 Notes Index Statistics'));
76
+ lines.push('');
77
+ lines.push(` Total Notes: ${chalk.bold(stats.totalNotes.toString())}`);
78
+ lines.push(` Total Folders: ${chalk.bold(stats.totalFolders.toString())}`);
79
+ lines.push(` Indexed At: ${chalk.dim(stats.indexedAt)}`);
80
+ if (stats.oldestNote) {
81
+ lines.push(` Oldest Note: ${chalk.dim(stats.oldestNote)}`);
82
+ }
83
+ if (stats.newestNote) {
84
+ lines.push(` Newest Note: ${chalk.dim(stats.newestNote)}`);
85
+ }
86
+ return lines.join('\n');
87
+ }
88
+ export function formatIndexProgress(progress) {
89
+ const { phase, current, total } = progress;
90
+ const percent = total > 0 ? Math.round((current / total) * 100) : 0;
91
+ const barWidth = 30;
92
+ const filledWidth = Math.round((percent / 100) * barWidth);
93
+ const bar = '█'.repeat(filledWidth) + '░'.repeat(barWidth - filledWidth);
94
+ let phaseText;
95
+ switch (phase) {
96
+ case 'extracting-pdfs':
97
+ phaseText = chalk.magenta('Extracting PDFs...');
98
+ break;
99
+ case 'reading':
100
+ phaseText = chalk.yellow('Reading notes...');
101
+ break;
102
+ case 'indexing':
103
+ phaseText = chalk.blue('Building index...');
104
+ break;
105
+ case 'done':
106
+ phaseText = chalk.green('Done!');
107
+ break;
108
+ }
109
+ return `${phaseText}\n[${chalk.cyan(bar)}] ${percent}% (${current}/${total})`;
110
+ }
111
+ export function formatNoteList(notes) {
112
+ if (notes.length === 0) {
113
+ return chalk.dim('No notes found.');
114
+ }
115
+ return notes.map(note => formatNote(note)).join('\n\n');
116
+ }
117
+ export function formatSearchResults(results, query) {
118
+ if (results.length === 0) {
119
+ return chalk.dim(`No results found for "${query}".`);
120
+ }
121
+ return results.map(result => formatSearchResult(result, query)).join('\n\n');
122
+ }
123
+ export function formatFolderList(folders) {
124
+ if (folders.length === 0) {
125
+ return chalk.dim('No folders found.');
126
+ }
127
+ return folders.map(folder => formatFolder(folder)).join('\n\n');
128
+ }
129
+ //# sourceMappingURL=formatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ const args = process.argv.slice(2);
3
+ if (args.includes('--mcp')) {
4
+ // Run as MCP server
5
+ import('./mcp.js').then(({ runMcpServer }) => {
6
+ runMcpServer().catch(console.error);
7
+ });
8
+ }
9
+ else {
10
+ // Run as CLI
11
+ import('./cli.js').then(({ runCli }) => {
12
+ runCli();
13
+ });
14
+ }
15
+ export {};
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;IAC3B,oBAAoB;IACpB,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE;QAC3C,YAAY,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC;KAAM,CAAC;IACN,aAAa;IACb,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;QACrC,MAAM,EAAE,CAAC;IACX,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { IndexStats } from './types.js';
2
+ export declare function indexNeedsRebuild(): boolean;
3
+ export interface IndexProgress {
4
+ phase: 'reading' | 'extracting-pdfs' | 'indexing' | 'done';
5
+ current: number;
6
+ total: number;
7
+ message?: string;
8
+ }
9
+ /**
10
+ * Incremental index update - only processes changed notes
11
+ */
12
+ export declare function updateIndex(onProgress?: (progress: IndexProgress) => void): Promise<IndexStats & {
13
+ updated: number;
14
+ deleted: number;
15
+ }>;
16
+ /**
17
+ * Full index rebuild - processes all notes from scratch
18
+ */
19
+ export declare function buildIndex(onProgress?: (progress: IndexProgress) => void): Promise<IndexStats>;
20
+ export declare function ensureIndex(): Promise<void>;
21
+ export declare function getStats(): IndexStats | null;
22
+ export declare function getIndexDbPath(): string;
23
+ export declare function getFuzzyIndexPath(): string;
24
+ //# sourceMappingURL=indexer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../src/indexer.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAwB,UAAU,EAAE,MAAM,YAAY,CAAC;AAkJnE,wBAAgB,iBAAiB,IAAI,OAAO,CAY3C;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,GAAG,iBAAiB,GAAG,UAAU,GAAG,MAAM,CAAC;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA2FD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,GAC7C,OAAO,CAAC,UAAU,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAmK5D;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,GAC7C,OAAO,CAAC,UAAU,CAAC,CA0HrB;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAQjD;AAED,wBAAgB,QAAQ,IAAI,UAAU,GAAG,IAAI,CAS5C;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
@@ -0,0 +1,483 @@
1
+ import Database from 'better-sqlite3';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { gunzipSync } from 'node:zlib';
6
+ import MiniSearch from 'minisearch';
7
+ import { appleToUnix } from './types.js';
8
+ import { extractAllPdfText, getNoteKey } from './attachments.js';
9
+ const NOTES_DB_PATH = join(homedir(), 'Library/Group Containers/group.com.apple.notes/NoteStore.sqlite');
10
+ const INDEX_DIR = join(homedir(), '.notes');
11
+ const INDEX_DB_PATH = join(INDEX_DIR, 'index.db');
12
+ const FUZZY_INDEX_PATH = join(INDEX_DIR, 'fuzzy.json');
13
+ const STATS_PATH = join(INDEX_DIR, 'stats.json');
14
+ // Entity type constants from Z_PRIMARYKEY
15
+ const ENTITY_NOTE = 12;
16
+ const ENTITY_FOLDER = 15;
17
+ function ensureIndexDir() {
18
+ if (!existsSync(INDEX_DIR)) {
19
+ mkdirSync(INDEX_DIR, { recursive: true });
20
+ }
21
+ }
22
+ function extractTextFromNoteData(data) {
23
+ if (!data || data.length === 0) {
24
+ return '';
25
+ }
26
+ try {
27
+ // The data is gzip compressed
28
+ const decompressed = gunzipSync(data);
29
+ // Extract readable text from the protobuf-like format
30
+ // The text is stored as UTF-8 strings within the binary data
31
+ const text = extractStringsFromBuffer(decompressed);
32
+ return text;
33
+ }
34
+ catch {
35
+ return '';
36
+ }
37
+ }
38
+ function extractStringsFromBuffer(buffer) {
39
+ const strings = [];
40
+ let currentString = '';
41
+ let inString = false;
42
+ for (let i = 0; i < buffer.length; i++) {
43
+ const byte = buffer[i];
44
+ // Check if this is a printable ASCII or common UTF-8 character
45
+ if ((byte >= 0x20 && byte <= 0x7E) || byte === 0x0A || byte === 0x0D || byte === 0x09) {
46
+ currentString += String.fromCharCode(byte);
47
+ inString = true;
48
+ }
49
+ else if (byte >= 0xC0 && byte <= 0xF7 && i + 1 < buffer.length) {
50
+ // Handle UTF-8 multi-byte sequences
51
+ let charLen = 1;
52
+ if ((byte & 0xE0) === 0xC0)
53
+ charLen = 2;
54
+ else if ((byte & 0xF0) === 0xE0)
55
+ charLen = 3;
56
+ else if ((byte & 0xF8) === 0xF0)
57
+ charLen = 4;
58
+ if (i + charLen <= buffer.length) {
59
+ try {
60
+ const utf8Char = buffer.slice(i, i + charLen).toString('utf8');
61
+ if (utf8Char.length === 1 && utf8Char.charCodeAt(0) >= 0x80) {
62
+ currentString += utf8Char;
63
+ i += charLen - 1;
64
+ inString = true;
65
+ continue;
66
+ }
67
+ }
68
+ catch {
69
+ // Not valid UTF-8
70
+ }
71
+ }
72
+ if (inString && currentString.length >= 3) {
73
+ strings.push(currentString.trim());
74
+ }
75
+ currentString = '';
76
+ inString = false;
77
+ }
78
+ else {
79
+ if (inString && currentString.length >= 3) {
80
+ strings.push(currentString.trim());
81
+ }
82
+ currentString = '';
83
+ inString = false;
84
+ }
85
+ }
86
+ if (currentString.length >= 3) {
87
+ strings.push(currentString.trim());
88
+ }
89
+ // Join strings and clean up
90
+ const text = strings
91
+ .filter(s => s.length >= 2)
92
+ .join('\n')
93
+ .replace(/\n{3,}/g, '\n\n')
94
+ // Remove any remaining non-printable characters except newlines/tabs
95
+ .replace(/[^\x20-\x7E\n\t\u00A0-\uFFFF]/g, '')
96
+ .trim();
97
+ // Filter out garbage lines and detect where garbage starts
98
+ const lines = text.split('\n');
99
+ // Helper to detect if a line is garbage
100
+ const isGarbageLine = (line) => {
101
+ const trimmed = line.trim();
102
+ if (trimmed.length === 0)
103
+ return false; // Empty lines aren't garbage
104
+ // Lines with characters outside basic Latin + common punctuation
105
+ const hasExoticChars = /[^\x20-\x7E\u00A0-\u00FF]/.test(trimmed);
106
+ // Short lines with exotic chars are definitely garbage
107
+ if (hasExoticChars && trimmed.length < 20)
108
+ return true;
109
+ // Lines that are mostly non-alphanumeric
110
+ const alphanumCount = (trimmed.match(/[a-zA-Z0-9]/g) || []).length;
111
+ if (alphanumCount < trimmed.length * 0.4 && trimmed.length < 15)
112
+ return true;
113
+ // Short alphanumeric-only codes that repeat (like "F>LL", "MbH", etc.)
114
+ if (trimmed.length <= 5 && /^[A-Za-z0-9>\\]+$/.test(trimmed))
115
+ return true;
116
+ return false;
117
+ };
118
+ // Find where a garbage block starts (3+ consecutive garbage lines)
119
+ let cutoffIndex = lines.length;
120
+ let consecutiveGarbage = 0;
121
+ for (let i = 0; i < lines.length; i++) {
122
+ if (isGarbageLine(lines[i])) {
123
+ consecutiveGarbage++;
124
+ if (consecutiveGarbage >= 3) {
125
+ // Found garbage block, cut from where it started
126
+ cutoffIndex = i - consecutiveGarbage + 1;
127
+ break;
128
+ }
129
+ }
130
+ else if (lines[i].trim().length > 0) {
131
+ consecutiveGarbage = 0;
132
+ }
133
+ }
134
+ return lines.slice(0, cutoffIndex).join('\n').trim();
135
+ }
136
+ export function indexNeedsRebuild() {
137
+ if (!existsSync(INDEX_DB_PATH) || !existsSync(FUZZY_INDEX_PATH)) {
138
+ return true;
139
+ }
140
+ try {
141
+ const sourceStats = statSync(NOTES_DB_PATH);
142
+ const indexStats = statSync(INDEX_DB_PATH);
143
+ return sourceStats.mtimeMs > indexStats.mtimeMs;
144
+ }
145
+ catch {
146
+ return true;
147
+ }
148
+ }
149
+ function getExtendedStats() {
150
+ try {
151
+ if (!existsSync(STATS_PATH)) {
152
+ return null;
153
+ }
154
+ return JSON.parse(readFileSync(STATS_PATH, 'utf-8'));
155
+ }
156
+ catch {
157
+ return null;
158
+ }
159
+ }
160
+ function createIndexTables(db) {
161
+ db.exec(`
162
+ CREATE TABLE IF NOT EXISTS notes (
163
+ id INTEGER PRIMARY KEY,
164
+ title TEXT,
165
+ snippet TEXT,
166
+ body TEXT,
167
+ folder TEXT,
168
+ created_at INTEGER,
169
+ modified_at INTEGER,
170
+ is_pinned INTEGER,
171
+ is_locked INTEGER
172
+ );
173
+
174
+ CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(
175
+ title,
176
+ snippet,
177
+ body,
178
+ folder,
179
+ content='notes',
180
+ content_rowid='id'
181
+ );
182
+
183
+ CREATE INDEX IF NOT EXISTS idx_notes_folder ON notes(folder);
184
+ CREATE INDEX IF NOT EXISTS idx_notes_modified ON notes(modified_at);
185
+ `);
186
+ }
187
+ function processNote(raw, pdfTextByNote) {
188
+ let body = extractTextFromNoteData(raw.data);
189
+ const noteKey = getNoteKey(raw.rowid);
190
+ const pdfText = pdfTextByNote.get(noteKey);
191
+ if (pdfText) {
192
+ body = body ? `${body}\n\n${pdfText}` : pdfText;
193
+ }
194
+ return {
195
+ id: raw.rowid,
196
+ title: raw.title || '',
197
+ snippet: raw.snippet || '',
198
+ body,
199
+ folder: raw.folderName || 'Notes',
200
+ createdAt: raw.creationDate ? appleToUnix(raw.creationDate) : 0,
201
+ modifiedAt: raw.modificationDate ? appleToUnix(raw.modificationDate) : 0,
202
+ isPinned: raw.isPinned === 1,
203
+ isLocked: raw.isPasswordProtected === 1,
204
+ };
205
+ }
206
+ function rebuildFuzzyIndex(indexDb) {
207
+ const allNotes = indexDb.prepare(`
208
+ SELECT id, title, snippet, body, folder, created_at as createdAt,
209
+ modified_at as modifiedAt, is_pinned as isPinned, is_locked as isLocked
210
+ FROM notes
211
+ `).all();
212
+ const miniSearch = new MiniSearch({
213
+ fields: ['title', 'snippet', 'body', 'folder'],
214
+ storeFields: ['id', 'title', 'snippet', 'folder', 'createdAt', 'modifiedAt', 'isPinned', 'isLocked'],
215
+ searchOptions: {
216
+ fuzzy: 0.2,
217
+ prefix: true,
218
+ boost: { title: 3, snippet: 2, body: 1, folder: 1 },
219
+ },
220
+ });
221
+ miniSearch.addAll(allNotes);
222
+ writeFileSync(FUZZY_INDEX_PATH, JSON.stringify(miniSearch.toJSON()));
223
+ }
224
+ /**
225
+ * Incremental index update - only processes changed notes
226
+ */
227
+ export async function updateIndex(onProgress) {
228
+ ensureIndexDir();
229
+ const existingStats = getExtendedStats();
230
+ const lastModTime = existingStats?.lastIndexedModTime || 0;
231
+ const sourceDb = new Database(NOTES_DB_PATH, { readonly: true });
232
+ const indexDb = new Database(INDEX_DB_PATH);
233
+ try {
234
+ createIndexTables(indexDb);
235
+ // Get current note IDs from source to detect deletions
236
+ const currentNoteIds = new Set(sourceDb.prepare(`
237
+ SELECT Z_PK as id FROM ZICCLOUDSYNCINGOBJECT
238
+ WHERE Z_ENT = ? AND ZMARKEDFORDELETION = 0
239
+ `).all(ENTITY_NOTE).map(r => r.id));
240
+ // Get indexed note IDs
241
+ const indexedNoteIds = new Set(indexDb.prepare('SELECT id FROM notes').all().map(r => r.id));
242
+ // Find deleted notes
243
+ const deletedIds = [];
244
+ for (const id of indexedNoteIds) {
245
+ if (!currentNoteIds.has(id)) {
246
+ deletedIds.push(id);
247
+ }
248
+ }
249
+ // Delete removed notes
250
+ if (deletedIds.length > 0) {
251
+ const deleteStmt = indexDb.prepare('DELETE FROM notes WHERE id = ?');
252
+ const deleteFtsStmt = indexDb.prepare('DELETE FROM notes_fts WHERE rowid = ?');
253
+ indexDb.exec('BEGIN TRANSACTION');
254
+ for (const id of deletedIds) {
255
+ deleteStmt.run(id);
256
+ deleteFtsStmt.run(id);
257
+ }
258
+ indexDb.exec('COMMIT');
259
+ }
260
+ // Query notes modified since last index
261
+ const modifiedNotesQuery = sourceDb.prepare(`
262
+ SELECT
263
+ n.Z_PK as rowid,
264
+ n.ZTITLE1 as title,
265
+ n.ZSNIPPET as snippet,
266
+ n.ZFOLDER as folderId,
267
+ f.ZTITLE2 as folderName,
268
+ COALESCE(n.ZCREATIONDATE, n.ZCREATIONDATE1, n.ZCREATIONDATE2, n.ZCREATIONDATE3) as creationDate,
269
+ COALESCE(n.ZMODIFICATIONDATE, n.ZMODIFICATIONDATE1, n.ZMODIFIEDDATE) as modificationDate,
270
+ n.ZISPINNED as isPinned,
271
+ n.ZISPASSWORDPROTECTED as isPasswordProtected,
272
+ nd.ZDATA as data
273
+ FROM ZICCLOUDSYNCINGOBJECT n
274
+ LEFT JOIN ZICCLOUDSYNCINGOBJECT f ON n.ZFOLDER = f.Z_PK
275
+ LEFT JOIN ZICNOTEDATA nd ON nd.ZNOTE = n.Z_PK
276
+ WHERE n.Z_ENT = ?
277
+ AND n.ZMARKEDFORDELETION = 0
278
+ AND COALESCE(n.ZMODIFICATIONDATE, n.ZMODIFICATIONDATE1, n.ZMODIFIEDDATE) > ?
279
+ ORDER BY COALESCE(n.ZMODIFICATIONDATE, n.ZMODIFICATIONDATE1, n.ZMODIFIEDDATE) ASC
280
+ `);
281
+ const modifiedNotes = modifiedNotesQuery.all(ENTITY_NOTE, lastModTime);
282
+ if (modifiedNotes.length === 0 && deletedIds.length === 0) {
283
+ onProgress?.({ phase: 'done', current: 0, total: 0, message: 'Index up to date' });
284
+ return { ...existingStats, updated: 0, deleted: 0 };
285
+ }
286
+ onProgress?.({
287
+ phase: 'extracting-pdfs',
288
+ current: 0,
289
+ total: modifiedNotes.length,
290
+ message: `Found ${modifiedNotes.length} modified, ${deletedIds.length} deleted`
291
+ });
292
+ // Extract PDF text only for modified notes
293
+ let pdfTextByNote;
294
+ try {
295
+ pdfTextByNote = extractAllPdfText();
296
+ }
297
+ catch {
298
+ pdfTextByNote = new Map();
299
+ }
300
+ onProgress?.({ phase: 'reading', current: 0, total: modifiedNotes.length });
301
+ // Upsert modified notes
302
+ const upsertStmt = indexDb.prepare(`
303
+ INSERT OR REPLACE INTO notes (id, title, snippet, body, folder, created_at, modified_at, is_pinned, is_locked)
304
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
305
+ `);
306
+ const deleteFtsForUpdate = indexDb.prepare('DELETE FROM notes_fts WHERE rowid = ?');
307
+ const insertFtsStmt = indexDb.prepare(`
308
+ INSERT INTO notes_fts (rowid, title, snippet, body, folder)
309
+ VALUES (?, ?, ?, ?, ?)
310
+ `);
311
+ let maxModTime = lastModTime;
312
+ indexDb.exec('BEGIN TRANSACTION');
313
+ for (let i = 0; i < modifiedNotes.length; i++) {
314
+ const raw = modifiedNotes[i];
315
+ const note = processNote(raw, pdfTextByNote);
316
+ if (raw.modificationDate && raw.modificationDate > maxModTime) {
317
+ maxModTime = raw.modificationDate;
318
+ }
319
+ upsertStmt.run(note.id, note.title, note.snippet, note.body, note.folder, note.createdAt, note.modifiedAt, note.isPinned ? 1 : 0, note.isLocked ? 1 : 0);
320
+ // Update FTS (delete then insert)
321
+ deleteFtsForUpdate.run(note.id);
322
+ insertFtsStmt.run(note.id, note.title, note.snippet, note.body, note.folder);
323
+ if (i % 50 === 0) {
324
+ onProgress?.({ phase: 'reading', current: i, total: modifiedNotes.length });
325
+ }
326
+ }
327
+ indexDb.exec('COMMIT');
328
+ onProgress?.({ phase: 'indexing', current: 0, total: 1, message: 'Rebuilding fuzzy index...' });
329
+ // Rebuild fuzzy index from SQLite
330
+ rebuildFuzzyIndex(indexDb);
331
+ // Update stats
332
+ const totalNotes = indexDb.prepare('SELECT COUNT(*) as count FROM notes').get().count;
333
+ const folderCount = sourceDb.prepare(`
334
+ SELECT COUNT(*) as count FROM ZICCLOUDSYNCINGOBJECT WHERE Z_ENT = ? AND ZMARKEDFORDELETION = 0
335
+ `).get(ENTITY_FOLDER).count;
336
+ const dateRange = indexDb.prepare(`
337
+ SELECT MIN(modified_at) as oldest, MAX(modified_at) as newest FROM notes WHERE modified_at > 0
338
+ `).get();
339
+ const stats = {
340
+ totalNotes,
341
+ totalFolders: folderCount,
342
+ indexedAt: new Date().toISOString(),
343
+ oldestNote: dateRange.oldest ? new Date(dateRange.oldest * 1000).toISOString() : null,
344
+ newestNote: dateRange.newest ? new Date(dateRange.newest * 1000).toISOString() : null,
345
+ lastIndexedModTime: maxModTime,
346
+ };
347
+ writeFileSync(STATS_PATH, JSON.stringify(stats, null, 2));
348
+ onProgress?.({ phase: 'done', current: modifiedNotes.length, total: modifiedNotes.length });
349
+ return { ...stats, updated: modifiedNotes.length, deleted: deletedIds.length };
350
+ }
351
+ finally {
352
+ sourceDb.close();
353
+ indexDb.close();
354
+ }
355
+ }
356
+ /**
357
+ * Full index rebuild - processes all notes from scratch
358
+ */
359
+ export async function buildIndex(onProgress) {
360
+ ensureIndexDir();
361
+ const sourceDb = new Database(NOTES_DB_PATH, { readonly: true });
362
+ // Remove old index and create fresh
363
+ if (existsSync(INDEX_DB_PATH)) {
364
+ try {
365
+ const oldDb = new Database(INDEX_DB_PATH);
366
+ oldDb.close();
367
+ }
368
+ catch { /* ignore */ }
369
+ }
370
+ const indexDb = new Database(INDEX_DB_PATH);
371
+ try {
372
+ // Drop and recreate tables for full rebuild
373
+ indexDb.exec(`
374
+ DROP TABLE IF EXISTS notes_fts;
375
+ DROP TABLE IF EXISTS notes;
376
+ `);
377
+ createIndexTables(indexDb);
378
+ const notesQuery = sourceDb.prepare(`
379
+ SELECT
380
+ n.Z_PK as rowid,
381
+ n.ZTITLE1 as title,
382
+ n.ZSNIPPET as snippet,
383
+ n.ZFOLDER as folderId,
384
+ f.ZTITLE2 as folderName,
385
+ COALESCE(n.ZCREATIONDATE, n.ZCREATIONDATE1, n.ZCREATIONDATE2, n.ZCREATIONDATE3) as creationDate,
386
+ COALESCE(n.ZMODIFICATIONDATE, n.ZMODIFICATIONDATE1, n.ZMODIFIEDDATE) as modificationDate,
387
+ n.ZISPINNED as isPinned,
388
+ n.ZISPASSWORDPROTECTED as isPasswordProtected,
389
+ nd.ZDATA as data
390
+ FROM ZICCLOUDSYNCINGOBJECT n
391
+ LEFT JOIN ZICCLOUDSYNCINGOBJECT f ON n.ZFOLDER = f.Z_PK
392
+ LEFT JOIN ZICNOTEDATA nd ON nd.ZNOTE = n.Z_PK
393
+ WHERE n.Z_ENT = ?
394
+ AND n.ZMARKEDFORDELETION = 0
395
+ ORDER BY COALESCE(n.ZMODIFICATIONDATE, n.ZMODIFICATIONDATE1, n.ZMODIFIEDDATE) ASC
396
+ `);
397
+ const rawNotes = notesQuery.all(ENTITY_NOTE);
398
+ const totalNotes = rawNotes.length;
399
+ onProgress?.({ phase: 'extracting-pdfs', current: 0, total: totalNotes });
400
+ let pdfTextByNote;
401
+ try {
402
+ pdfTextByNote = extractAllPdfText();
403
+ }
404
+ catch {
405
+ pdfTextByNote = new Map();
406
+ }
407
+ onProgress?.({ phase: 'reading', current: 0, total: totalNotes });
408
+ const insertStmt = indexDb.prepare(`
409
+ INSERT INTO notes (id, title, snippet, body, folder, created_at, modified_at, is_pinned, is_locked)
410
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
411
+ `);
412
+ const insertFtsStmt = indexDb.prepare(`
413
+ INSERT INTO notes_fts (rowid, title, snippet, body, folder)
414
+ VALUES (?, ?, ?, ?, ?)
415
+ `);
416
+ let maxModTime = 0;
417
+ indexDb.exec('BEGIN TRANSACTION');
418
+ for (let i = 0; i < rawNotes.length; i++) {
419
+ const raw = rawNotes[i];
420
+ const note = processNote(raw, pdfTextByNote);
421
+ if (raw.modificationDate && raw.modificationDate > maxModTime) {
422
+ maxModTime = raw.modificationDate;
423
+ }
424
+ insertStmt.run(note.id, note.title, note.snippet, note.body, note.folder, note.createdAt, note.modifiedAt, note.isPinned ? 1 : 0, note.isLocked ? 1 : 0);
425
+ insertFtsStmt.run(note.id, note.title, note.snippet, note.body, note.folder);
426
+ if (i % 100 === 0) {
427
+ onProgress?.({ phase: 'reading', current: i, total: totalNotes });
428
+ }
429
+ }
430
+ indexDb.exec('COMMIT');
431
+ onProgress?.({ phase: 'indexing', current: 0, total: totalNotes });
432
+ rebuildFuzzyIndex(indexDb);
433
+ const folderCount = sourceDb.prepare(`
434
+ SELECT COUNT(*) as count FROM ZICCLOUDSYNCINGOBJECT WHERE Z_ENT = ? AND ZMARKEDFORDELETION = 0
435
+ `).get(ENTITY_FOLDER).count;
436
+ const dateRange = indexDb.prepare(`
437
+ SELECT MIN(modified_at) as oldest, MAX(modified_at) as newest FROM notes WHERE modified_at > 0
438
+ `).get();
439
+ const stats = {
440
+ totalNotes,
441
+ totalFolders: folderCount,
442
+ indexedAt: new Date().toISOString(),
443
+ oldestNote: dateRange.oldest ? new Date(dateRange.oldest * 1000).toISOString() : null,
444
+ newestNote: dateRange.newest ? new Date(dateRange.newest * 1000).toISOString() : null,
445
+ lastIndexedModTime: maxModTime,
446
+ };
447
+ writeFileSync(STATS_PATH, JSON.stringify(stats, null, 2));
448
+ onProgress?.({ phase: 'done', current: totalNotes, total: totalNotes });
449
+ return stats;
450
+ }
451
+ finally {
452
+ sourceDb.close();
453
+ indexDb.close();
454
+ }
455
+ }
456
+ export async function ensureIndex() {
457
+ if (!existsSync(INDEX_DB_PATH) || !existsSync(FUZZY_INDEX_PATH)) {
458
+ // No index exists, do full build
459
+ await buildIndex();
460
+ }
461
+ else if (indexNeedsRebuild()) {
462
+ // Index exists but outdated, do incremental update
463
+ await updateIndex();
464
+ }
465
+ }
466
+ export function getStats() {
467
+ try {
468
+ if (!existsSync(STATS_PATH)) {
469
+ return null;
470
+ }
471
+ return JSON.parse(readFileSync(STATS_PATH, 'utf-8'));
472
+ }
473
+ catch {
474
+ return null;
475
+ }
476
+ }
477
+ export function getIndexDbPath() {
478
+ return INDEX_DB_PATH;
479
+ }
480
+ export function getFuzzyIndexPath() {
481
+ return FUZZY_INDEX_PATH;
482
+ }
483
+ //# sourceMappingURL=indexer.js.map