@noteplanco/noteplan-mcp 1.1.23 → 1.1.25

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 (165) hide show
  1. package/README.md +7 -0
  2. package/dist/index.js +6 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/noteplan/attachments-paths.d.ts +13 -0
  5. package/dist/noteplan/attachments-paths.d.ts.map +1 -0
  6. package/dist/noteplan/attachments-paths.js +27 -0
  7. package/dist/noteplan/attachments-paths.js.map +1 -0
  8. package/dist/noteplan/embeddings.js +1 -1
  9. package/dist/noteplan/embeddings.js.map +1 -1
  10. package/dist/noteplan/file-reader.d.ts +37 -46
  11. package/dist/noteplan/file-reader.d.ts.map +1 -1
  12. package/dist/noteplan/file-reader.js +200 -202
  13. package/dist/noteplan/file-reader.js.map +1 -1
  14. package/dist/noteplan/file-reader.test.d.ts +2 -0
  15. package/dist/noteplan/file-reader.test.d.ts.map +1 -0
  16. package/dist/noteplan/file-reader.test.js +67 -0
  17. package/dist/noteplan/file-reader.test.js.map +1 -0
  18. package/dist/noteplan/file-writer.d.ts +35 -31
  19. package/dist/noteplan/file-writer.d.ts.map +1 -1
  20. package/dist/noteplan/file-writer.js +280 -164
  21. package/dist/noteplan/file-writer.js.map +1 -1
  22. package/dist/noteplan/file-writer.test.js +704 -191
  23. package/dist/noteplan/file-writer.test.js.map +1 -1
  24. package/dist/noteplan/filter-store.d.ts +5 -5
  25. package/dist/noteplan/filter-store.d.ts.map +1 -1
  26. package/dist/noteplan/filter-store.js +94 -79
  27. package/dist/noteplan/filter-store.js.map +1 -1
  28. package/dist/noteplan/ripgrep-search.d.ts +25 -2
  29. package/dist/noteplan/ripgrep-search.d.ts.map +1 -1
  30. package/dist/noteplan/ripgrep-search.js +75 -2
  31. package/dist/noteplan/ripgrep-search.js.map +1 -1
  32. package/dist/noteplan/space-row-utils.d.ts +20 -0
  33. package/dist/noteplan/space-row-utils.d.ts.map +1 -0
  34. package/dist/noteplan/space-row-utils.js +78 -0
  35. package/dist/noteplan/space-row-utils.js.map +1 -0
  36. package/dist/noteplan/space-row-utils.test.d.ts +2 -0
  37. package/dist/noteplan/space-row-utils.test.d.ts.map +1 -0
  38. package/dist/noteplan/space-row-utils.test.js +123 -0
  39. package/dist/noteplan/space-row-utils.test.js.map +1 -0
  40. package/dist/noteplan/sqlite-reader.d.ts +12 -27
  41. package/dist/noteplan/sqlite-reader.d.ts.map +1 -1
  42. package/dist/noteplan/sqlite-reader.js +315 -221
  43. package/dist/noteplan/sqlite-reader.js.map +1 -1
  44. package/dist/noteplan/sqlite-writer.d.ts +1 -1
  45. package/dist/noteplan/sqlite-writer.d.ts.map +1 -1
  46. package/dist/noteplan/sqlite-writer.js +2 -2
  47. package/dist/noteplan/sqlite-writer.js.map +1 -1
  48. package/dist/noteplan/unified-store.d.ts +41 -30
  49. package/dist/noteplan/unified-store.d.ts.map +1 -1
  50. package/dist/noteplan/unified-store.js +257 -159
  51. package/dist/noteplan/unified-store.js.map +1 -1
  52. package/dist/server.d.ts.map +1 -1
  53. package/dist/server.js +142 -61
  54. package/dist/server.js.map +1 -1
  55. package/dist/tools/attachments.d.ts +9 -9
  56. package/dist/tools/attachments.d.ts.map +1 -1
  57. package/dist/tools/attachments.js +74 -83
  58. package/dist/tools/attachments.js.map +1 -1
  59. package/dist/tools/attachments.test.js +170 -129
  60. package/dist/tools/attachments.test.js.map +1 -1
  61. package/dist/tools/calendar.d.ts +16 -13
  62. package/dist/tools/calendar.d.ts.map +1 -1
  63. package/dist/tools/calendar.js +17 -16
  64. package/dist/tools/calendar.js.map +1 -1
  65. package/dist/tools/embeddings.d.ts +6 -6
  66. package/dist/tools/embeddings.d.ts.map +1 -1
  67. package/dist/tools/embeddings.js +6 -6
  68. package/dist/tools/embeddings.js.map +1 -1
  69. package/dist/tools/events.d.ts +7 -3
  70. package/dist/tools/events.d.ts.map +1 -1
  71. package/dist/tools/events.js +51 -16
  72. package/dist/tools/events.js.map +1 -1
  73. package/dist/tools/filters.d.ts +28 -33
  74. package/dist/tools/filters.d.ts.map +1 -1
  75. package/dist/tools/filters.js +42 -105
  76. package/dist/tools/filters.js.map +1 -1
  77. package/dist/tools/notes.d.ts +80 -218
  78. package/dist/tools/notes.d.ts.map +1 -1
  79. package/dist/tools/notes.js +180 -177
  80. package/dist/tools/notes.js.map +1 -1
  81. package/dist/tools/notes.test.js +242 -21
  82. package/dist/tools/notes.test.js.map +1 -1
  83. package/dist/tools/search.d.ts +4 -3
  84. package/dist/tools/search.d.ts.map +1 -1
  85. package/dist/tools/search.js +9 -5
  86. package/dist/tools/search.js.map +1 -1
  87. package/dist/tools/search.test.d.ts +2 -0
  88. package/dist/tools/search.test.d.ts.map +1 -0
  89. package/dist/tools/search.test.js +37 -0
  90. package/dist/tools/search.test.js.map +1 -0
  91. package/dist/tools/spaces.d.ts +20 -20
  92. package/dist/tools/spaces.d.ts.map +1 -1
  93. package/dist/tools/spaces.js +28 -28
  94. package/dist/tools/spaces.js.map +1 -1
  95. package/dist/tools/tasks.d.ts +22 -22
  96. package/dist/tools/tasks.d.ts.map +1 -1
  97. package/dist/tools/tasks.js +22 -22
  98. package/dist/tools/tasks.js.map +1 -1
  99. package/dist/tools/templates.d.ts +7 -7
  100. package/dist/tools/templates.d.ts.map +1 -1
  101. package/dist/tools/templates.js +4 -4
  102. package/dist/tools/templates.js.map +1 -1
  103. package/dist/tools/themes.d.ts.map +1 -1
  104. package/dist/tools/themes.js +26 -35
  105. package/dist/tools/themes.js.map +1 -1
  106. package/dist/transport/bridge-availability.d.ts +5 -0
  107. package/dist/transport/bridge-availability.d.ts.map +1 -0
  108. package/dist/transport/bridge-availability.js +92 -0
  109. package/dist/transport/bridge-availability.js.map +1 -0
  110. package/dist/transport/bridge-cascade.d.ts +18 -0
  111. package/dist/transport/bridge-cascade.d.ts.map +1 -0
  112. package/dist/transport/bridge-cascade.js +78 -0
  113. package/dist/transport/bridge-cascade.js.map +1 -0
  114. package/dist/transport/bridge-cascade.test.d.ts +2 -0
  115. package/dist/transport/bridge-cascade.test.d.ts.map +1 -0
  116. package/dist/transport/bridge-cascade.test.js +160 -0
  117. package/dist/transport/bridge-cascade.test.js.map +1 -0
  118. package/dist/transport/bridge-client.d.ts +197 -0
  119. package/dist/transport/bridge-client.d.ts.map +1 -0
  120. package/dist/transport/bridge-client.js +288 -0
  121. package/dist/transport/bridge-client.js.map +1 -0
  122. package/dist/transport/bridge-client.test.d.ts +2 -0
  123. package/dist/transport/bridge-client.test.d.ts.map +1 -0
  124. package/dist/transport/bridge-client.test.js +384 -0
  125. package/dist/transport/bridge-client.test.js.map +1 -0
  126. package/dist/transport/bridge-context.d.ts +10 -0
  127. package/dist/transport/bridge-context.d.ts.map +1 -0
  128. package/dist/transport/bridge-context.js +18 -0
  129. package/dist/transport/bridge-context.js.map +1 -0
  130. package/dist/transport/bridge-fs.d.ts +25 -0
  131. package/dist/transport/bridge-fs.d.ts.map +1 -0
  132. package/dist/transport/bridge-fs.js +129 -0
  133. package/dist/transport/bridge-fs.js.map +1 -0
  134. package/dist/utils/date-utils.d.ts +24 -0
  135. package/dist/utils/date-utils.d.ts.map +1 -1
  136. package/dist/utils/date-utils.js +55 -0
  137. package/dist/utils/date-utils.js.map +1 -1
  138. package/dist/utils/date-utils.test.d.ts +2 -0
  139. package/dist/utils/date-utils.test.d.ts.map +1 -0
  140. package/dist/utils/date-utils.test.js +109 -0
  141. package/dist/utils/date-utils.test.js.map +1 -0
  142. package/dist/utils/folder-access.d.ts +23 -0
  143. package/dist/utils/folder-access.d.ts.map +1 -0
  144. package/dist/utils/folder-access.js +131 -0
  145. package/dist/utils/folder-access.js.map +1 -0
  146. package/dist/utils/folder-access.test.d.ts +2 -0
  147. package/dist/utils/folder-access.test.d.ts.map +1 -0
  148. package/dist/utils/folder-access.test.js +182 -0
  149. package/dist/utils/folder-access.test.js.map +1 -0
  150. package/dist/utils/folder-matcher.d.ts.map +1 -1
  151. package/dist/utils/folder-matcher.js +16 -0
  152. package/dist/utils/folder-matcher.js.map +1 -1
  153. package/dist/utils/folder-matcher.test.js +42 -0
  154. package/dist/utils/folder-matcher.test.js.map +1 -1
  155. package/dist/utils/server-config.d.ts +10 -2
  156. package/dist/utils/server-config.d.ts.map +1 -1
  157. package/dist/utils/server-config.js +16 -2
  158. package/dist/utils/server-config.js.map +1 -1
  159. package/dist/utils/version.d.ts +2 -0
  160. package/dist/utils/version.d.ts.map +1 -1
  161. package/dist/utils/version.js +5 -1
  162. package/dist/utils/version.js.map +1 -1
  163. package/package.json +4 -3
  164. package/scripts/calendar-helper +0 -0
  165. package/scripts/reminders-helper +0 -0
@@ -5,6 +5,8 @@ import * as fs from 'fs';
5
5
  import { SQLITE_NOTE_TYPES } from './types.js';
6
6
  import { extractTitle, extractTagsFromContent } from './markdown-parser.js';
7
7
  import { isSqliteAvailable, SqliteDatabase } from './sqlite-loader.js';
8
+ import { bridgeOrFallback } from '../transport/bridge-cascade.js';
9
+ import { bridgeRowToNote, filterBridgeRowsByTrash, findRootSpaceIdFromRows, isTrashFolderRow, } from './space-row-utils.js';
8
10
  // Possible NotePlan storage paths (same as file-reader.ts)
9
11
  const POSSIBLE_PATHS = [
10
12
  // Direct local paths (AppStore version) - preferred for local dev
@@ -90,36 +92,51 @@ export function closeDatabase() {
90
92
  db = null;
91
93
  }
92
94
  }
93
- /**
94
- * List all spaces
95
- */
96
- export function listSpaces() {
97
- const database = getDatabase();
98
- if (!database)
99
- return [];
100
- try {
101
- // Spaces are stored as note_type = 10 with is_dir = 1
102
- const rows = database
103
- .prepare(`
104
- SELECT
105
- id,
106
- title,
107
- (SELECT COUNT(*) FROM notes n2 WHERE n2.parent = notes.id AND n2.note_type IN (?, ?)) as note_count
108
- FROM notes
109
- WHERE note_type = ?
110
- AND is_dir = 1
111
- `)
112
- .all(SQLITE_NOTE_TYPES.TEAMSPACE_NOTE, SQLITE_NOTE_TYPES.TEAMSPACE_CALENDAR, SQLITE_NOTE_TYPES.TEAMSPACE);
113
- return rows.map((row) => ({
95
+ export async function listSpaces() {
96
+ return bridgeOrFallback(async (bridge) => {
97
+ // Independent fetches — run in parallel.
98
+ const [teamspaceRows, allRows] = await Promise.all([
99
+ bridge.listTeamspaces(),
100
+ bridge.listSpaceNoteRows(),
101
+ ]);
102
+ const noteCountByParent = new Map();
103
+ for (const row of allRows) {
104
+ if (row.is_dir === 0 && row.parent) {
105
+ noteCountByParent.set(row.parent, (noteCountByParent.get(row.parent) ?? 0) + 1);
106
+ }
107
+ }
108
+ return teamspaceRows.map((row) => ({
114
109
  id: row.id,
115
110
  name: row.title || row.id,
116
- noteCount: row.note_count,
111
+ noteCount: noteCountByParent.get(row.id) ?? 0,
117
112
  }));
118
- }
119
- catch (error) {
120
- console.error('Error listing spaces:', error);
121
- return [];
122
- }
113
+ }, () => {
114
+ const database = getDatabase();
115
+ if (!database)
116
+ return [];
117
+ try {
118
+ const rows = database
119
+ .prepare(`
120
+ SELECT
121
+ id,
122
+ title,
123
+ (SELECT COUNT(*) FROM notes n2 WHERE n2.parent = notes.id AND n2.note_type IN (?, ?)) as note_count
124
+ FROM notes
125
+ WHERE note_type = ?
126
+ AND is_dir = 1
127
+ `)
128
+ .all(SQLITE_NOTE_TYPES.TEAMSPACE_NOTE, SQLITE_NOTE_TYPES.TEAMSPACE_CALENDAR, SQLITE_NOTE_TYPES.TEAMSPACE);
129
+ return rows.map((row) => ({
130
+ id: row.id,
131
+ name: row.title || row.id,
132
+ noteCount: row.note_count,
133
+ }));
134
+ }
135
+ catch (error) {
136
+ console.error('Error listing spaces:', error);
137
+ return [];
138
+ }
139
+ });
123
140
  }
124
141
  /**
125
142
  * Get all descendant IDs of a space (folders and notes)
@@ -145,10 +162,41 @@ function getSpaceDescendantIds(database, spaceId) {
145
162
  return [];
146
163
  }
147
164
  }
148
- /**
149
- * Count notes and subfolders under a space folder (recursive)
150
- */
151
- export function countSpaceFolderContents(folderId) {
165
+ export async function countSpaceFolderContents(folderId) {
166
+ return bridgeOrFallback(async (bridge) => {
167
+ const allRows = await bridge.listSpaceNoteRows();
168
+ const childrenByParent = new Map();
169
+ for (const r of allRows) {
170
+ if (!r.parent)
171
+ continue;
172
+ const arr = childrenByParent.get(r.parent) ?? [];
173
+ arr.push(r);
174
+ childrenByParent.set(r.parent, arr);
175
+ }
176
+ let noteCount = 0;
177
+ let folderCount = 0;
178
+ const queue = [folderId];
179
+ const seen = new Set();
180
+ while (queue.length > 0) {
181
+ const id = queue.shift();
182
+ const children = childrenByParent.get(id) ?? [];
183
+ for (const child of children) {
184
+ if (seen.has(child.id))
185
+ continue;
186
+ seen.add(child.id);
187
+ if (child.is_dir === 1) {
188
+ folderCount += 1;
189
+ queue.push(child.id);
190
+ }
191
+ else {
192
+ noteCount += 1;
193
+ }
194
+ }
195
+ }
196
+ return { noteCount, folderCount };
197
+ }, () => sqlCountSpaceFolderContents(folderId));
198
+ }
199
+ function sqlCountSpaceFolderContents(folderId) {
152
200
  const database = getDatabase();
153
201
  if (!database)
154
202
  return { noteCount: 0, folderCount: 0 };
@@ -274,69 +322,79 @@ function rowToNote(row, database) {
274
322
  createdAt: row.created_at ? new Date(row.created_at) : undefined,
275
323
  };
276
324
  }
277
- /**
278
- * List notes in a teamspace
279
- */
280
- export function listSpaceNotes(spaceIdOrOptions) {
281
- const database = getDatabase();
282
- if (!database)
283
- return [];
325
+ export async function listSpaceNotes(spaceIdOrOptions) {
284
326
  const { spaceId, includeTrash } = normalizeListSpaceOptions(spaceIdOrOptions);
285
- try {
286
- let query = `
287
- SELECT id, content, note_type, title, filename, parent, is_dir, created_at, modified_at
288
- FROM notes
289
- WHERE note_type IN (?, ?)
290
- AND is_dir = 0
291
- `;
292
- const params = [
293
- SQLITE_NOTE_TYPES.TEAMSPACE_NOTE,
294
- SQLITE_NOTE_TYPES.TEAMSPACE_CALENDAR,
295
- ];
296
- if (spaceId) {
297
- const descendantIds = getSpaceDescendantIds(database, spaceId);
298
- if (descendantIds.length === 0)
299
- return [];
300
- const placeholders = descendantIds.map(() => '?').join(',');
301
- query += ` AND id IN (${placeholders})`;
302
- params.push(...descendantIds);
327
+ return bridgeOrFallback(async (bridge) => {
328
+ const allRows = await bridge.listSpaceNoteRows({ spaceId });
329
+ const visible = filterBridgeRowsByTrash(allRows, includeTrash);
330
+ const notesOnly = visible.filter((r) => r.is_dir === 0);
331
+ return notesOnly.map((row) => bridgeRowToNote(row, allRows));
332
+ }, () => {
333
+ const database = getDatabase();
334
+ if (!database)
335
+ return [];
336
+ try {
337
+ let query = `
338
+ SELECT id, content, note_type, title, filename, parent, is_dir, created_at, modified_at
339
+ FROM notes
340
+ WHERE note_type IN (?, ?)
341
+ AND is_dir = 0
342
+ `;
343
+ const params = [
344
+ SQLITE_NOTE_TYPES.TEAMSPACE_NOTE,
345
+ SQLITE_NOTE_TYPES.TEAMSPACE_CALENDAR,
346
+ ];
347
+ if (spaceId) {
348
+ const descendantIds = getSpaceDescendantIds(database, spaceId);
349
+ if (descendantIds.length === 0)
350
+ return [];
351
+ const placeholders = descendantIds.map(() => '?').join(',');
352
+ query += ` AND id IN (${placeholders})`;
353
+ params.push(...descendantIds);
354
+ }
355
+ const rows = database.prepare(query).all(...params);
356
+ const filteredRows = filterRowsByTrash(database, rows, spaceId, includeTrash);
357
+ return filteredRows.map(row => rowToNote(row, database));
303
358
  }
304
- const rows = database.prepare(query).all(...params);
305
- const filteredRows = filterRowsByTrash(database, rows, spaceId, includeTrash);
306
- return filteredRows.map(row => rowToNote(row, database));
307
- }
308
- catch (error) {
309
- console.error('Error listing teamspace notes:', error);
310
- return [];
311
- }
359
+ catch (error) {
360
+ console.error('Error listing teamspace notes:', error);
361
+ return [];
362
+ }
363
+ });
312
364
  }
313
- /**
314
- * Get a specific teamspace note by ID or filename
315
- */
316
- export function getSpaceNote(identifier) {
317
- const database = getDatabase();
318
- if (!database)
319
- return null;
320
- try {
321
- const row = database
322
- .prepare(`
323
- SELECT id, content, note_type, title, filename, parent, is_dir, created_at, modified_at
324
- FROM notes
325
- WHERE (id = ? OR filename = ?)
326
- AND is_dir = 0
327
- `)
328
- .get(identifier, identifier);
329
- return row ? rowToNote(row, database) : null;
330
- }
331
- catch (error) {
332
- console.error('Error getting teamspace note:', error);
333
- return null;
334
- }
365
+ export async function getSpaceNote(identifier) {
366
+ return bridgeOrFallback(async (bridge) => {
367
+ const row = (await bridge.getSpaceNoteRow({ id: identifier })) ??
368
+ (await bridge.getSpaceNoteRow({ filename: identifier }));
369
+ if (!row)
370
+ return null;
371
+ const parents = await bridge.getSpaceParentRows(row.id);
372
+ return bridgeRowToNote(row, [row, ...parents]);
373
+ }, () => {
374
+ const database = getDatabase();
375
+ if (!database)
376
+ return null;
377
+ try {
378
+ const row = database
379
+ .prepare(`
380
+ SELECT id, content, note_type, title, filename, parent, is_dir, created_at, modified_at
381
+ FROM notes
382
+ WHERE (id = ? OR filename = ?)
383
+ AND is_dir = 0
384
+ `)
385
+ .get(identifier, identifier);
386
+ return row ? rowToNote(row, database) : null;
387
+ }
388
+ catch (error) {
389
+ console.error('Error getting teamspace note:', error);
390
+ return null;
391
+ }
392
+ });
335
393
  }
336
394
  /**
337
395
  * Get a teamspace note by title
338
396
  */
339
- export function getSpaceNoteByTitle(title, spaceId, includeTrash = false) {
397
+ export async function getSpaceNoteByTitle(title, spaceId, includeTrash = false) {
340
398
  const database = getDatabase();
341
399
  if (!database)
342
400
  return null;
@@ -371,7 +429,7 @@ export function getSpaceNoteByTitle(title, spaceId, includeTrash = false) {
371
429
  // Search notes whose content contains the title in frontmatter (title: or name: keys)
372
430
  // and resolve via extractTitle to confirm.
373
431
  const lowerTitle = title.toLowerCase();
374
- const candidates = listSpaceNotes({ spaceId, includeTrash });
432
+ const candidates = await listSpaceNotes({ spaceId, includeTrash });
375
433
  return candidates.find((note) => note.title.toLowerCase() === lowerTitle) || null;
376
434
  }
377
435
  catch (error) {
@@ -433,100 +491,123 @@ function parseSearchPatterns(input) {
433
491
  * Search space notes using LIKE with OR pattern support
434
492
  * This is the primary search method - we don't modify NotePlan's database
435
493
  */
436
- export function searchSpaceNotesFTS(query, options = {}) {
437
- const database = getDatabase();
438
- if (!database)
439
- return [];
494
+ export async function searchSpaceNotesFTS(query, options = {}) {
440
495
  const { spaceId, limit = 50, includeTrash = false } = options;
441
- // Parse OR patterns
442
496
  const patterns = parseSearchPatterns(query);
443
497
  if (patterns.length === 0)
444
498
  return [];
445
- try {
446
- // Build OR conditions for multiple patterns
447
- const orConditions = patterns
448
- .map(() => '(content LIKE ? OR title LIKE ?)')
449
- .join(' OR ');
450
- let sql = `
451
- SELECT id, content, note_type, title, filename, parent, is_dir, created_at, modified_at
452
- FROM notes
453
- WHERE (${orConditions})
454
- AND note_type IN (?, ?)
455
- AND is_dir = 0
456
- `;
457
- // Build params: each pattern needs two placeholders (content, title)
458
- const params = [];
459
- for (const pattern of patterns) {
460
- const searchPattern = `%${pattern}%`;
461
- params.push(searchPattern, searchPattern);
499
+ return bridgeOrFallback(async (bridge) => {
500
+ // Two independent fetches: regex matches + parent context for trash
501
+ // filtering and root-space resolution. Parallel cuts wall-clock by ~half.
502
+ const [matchRows, contextRows] = await Promise.all([
503
+ bridge.searchSpaceNoteRows(patterns.join('|'), { spaceId, limit: limit * 2 }),
504
+ bridge.listSpaceNoteRows({ spaceId }),
505
+ ]);
506
+ const trashFreeIds = new Set(filterBridgeRowsByTrash(contextRows, includeTrash).map((r) => r.id));
507
+ return matchRows
508
+ .filter((r) => trashFreeIds.has(r.id))
509
+ .slice(0, limit)
510
+ .map((row) => bridgeRowToNote(row, contextRows));
511
+ }, () => {
512
+ const database = getDatabase();
513
+ if (!database)
514
+ return [];
515
+ try {
516
+ const orConditions = patterns
517
+ .map(() => '(content LIKE ? OR title LIKE ?)')
518
+ .join(' OR ');
519
+ let sql = `
520
+ SELECT id, content, note_type, title, filename, parent, is_dir, created_at, modified_at
521
+ FROM notes
522
+ WHERE (${orConditions})
523
+ AND note_type IN (?, ?)
524
+ AND is_dir = 0
525
+ `;
526
+ const params = [];
527
+ for (const pattern of patterns) {
528
+ const searchPattern = `%${pattern}%`;
529
+ params.push(searchPattern, searchPattern);
530
+ }
531
+ params.push(SQLITE_NOTE_TYPES.TEAMSPACE_NOTE, SQLITE_NOTE_TYPES.TEAMSPACE_CALENDAR);
532
+ if (spaceId) {
533
+ const descendantIds = getSpaceDescendantIds(database, spaceId);
534
+ if (descendantIds.length === 0)
535
+ return [];
536
+ const placeholders = descendantIds.map(() => '?').join(',');
537
+ sql += ` AND id IN (${placeholders})`;
538
+ params.push(...descendantIds);
539
+ }
540
+ sql += ` ORDER BY modified_at DESC LIMIT ?`;
541
+ params.push(limit);
542
+ const rows = database.prepare(sql).all(...params);
543
+ const filteredRows = filterRowsByTrash(database, rows, spaceId, includeTrash);
544
+ return filteredRows.map((row) => rowToNote(row, database));
462
545
  }
463
- params.push(SQLITE_NOTE_TYPES.TEAMSPACE_NOTE, SQLITE_NOTE_TYPES.TEAMSPACE_CALENDAR);
464
- if (spaceId) {
465
- const descendantIds = getSpaceDescendantIds(database, spaceId);
466
- if (descendantIds.length === 0)
467
- return [];
468
- const placeholders = descendantIds.map(() => '?').join(',');
469
- sql += ` AND id IN (${placeholders})`;
470
- params.push(...descendantIds);
546
+ catch (error) {
547
+ console.error('Error searching space notes:', error);
548
+ return [];
471
549
  }
472
- // Order by modified_at (most recent first) for relevance
473
- sql += ` ORDER BY modified_at DESC LIMIT ?`;
474
- params.push(limit);
475
- const rows = database.prepare(sql).all(...params);
476
- const filteredRows = filterRowsByTrash(database, rows, spaceId, includeTrash);
477
- return filteredRows.map((row) => rowToNote(row, database));
478
- }
479
- catch (error) {
480
- console.error('Error searching space notes:', error);
481
- return [];
482
- }
550
+ });
483
551
  }
484
552
  /**
485
553
  * List folders in teamspace
486
554
  */
487
- export function listSpaceFolders(spaceIdOrOptions) {
488
- const database = getDatabase();
489
- if (!database)
490
- return [];
555
+ export async function listSpaceFolders(spaceIdOrOptions) {
491
556
  const { spaceId, includeTrash } = normalizeListSpaceOptions(spaceIdOrOptions);
492
- try {
493
- let query = `
494
- SELECT id, title, filename, parent
495
- FROM notes
496
- WHERE is_dir = 1
497
- `;
498
- const params = [];
499
- if (spaceId) {
500
- const descendantIds = getSpaceDescendantIds(database, spaceId);
501
- if (descendantIds.length === 0)
502
- return [];
503
- const placeholders = descendantIds.map(() => '?').join(',');
504
- query += ` AND id IN (${placeholders})`;
505
- params.push(...descendantIds);
506
- }
507
- let rows = database.prepare(query).all(...params);
508
- if (!includeTrash) {
509
- rows = rows.filter((row) => row.title?.toLowerCase() !== SPACE_TRASH_FOLDER_TITLE.toLowerCase());
510
- }
511
- return rows.map((row) => ({
557
+ return bridgeOrFallback(async (bridge) => {
558
+ const allRows = await bridge.listSpaceNoteRows({ spaceId });
559
+ const visible = filterBridgeRowsByTrash(allRows, includeTrash);
560
+ const folderRows = visible.filter((r) => r.is_dir === 1 && (includeTrash || !isTrashFolderRow(r)));
561
+ return folderRows.map((row) => ({
512
562
  id: row.id,
513
563
  path: row.filename,
514
564
  name: row.title || row.id,
515
565
  source: 'space',
516
- spaceId: findRootSpaceId(database, row.id),
566
+ spaceId: findRootSpaceIdFromRows(row.id, allRows),
517
567
  }));
518
- }
519
- catch (error) {
520
- console.error('Error listing teamspace folders:', error);
521
- return [];
522
- }
568
+ }, () => {
569
+ const database = getDatabase();
570
+ if (!database)
571
+ return [];
572
+ try {
573
+ let query = `
574
+ SELECT id, title, filename, parent
575
+ FROM notes
576
+ WHERE is_dir = 1
577
+ `;
578
+ const params = [];
579
+ if (spaceId) {
580
+ const descendantIds = getSpaceDescendantIds(database, spaceId);
581
+ if (descendantIds.length === 0)
582
+ return [];
583
+ const placeholders = descendantIds.map(() => '?').join(',');
584
+ query += ` AND id IN (${placeholders})`;
585
+ params.push(...descendantIds);
586
+ }
587
+ let rows = database.prepare(query).all(...params);
588
+ if (!includeTrash) {
589
+ rows = rows.filter((row) => !isTrashFolderRow({ ...row, is_dir: 1 }));
590
+ }
591
+ return rows.map((row) => ({
592
+ id: row.id,
593
+ path: row.filename,
594
+ name: row.title || row.id,
595
+ source: 'space',
596
+ spaceId: findRootSpaceId(database, row.id),
597
+ }));
598
+ }
599
+ catch (error) {
600
+ console.error('Error listing teamspace folders:', error);
601
+ return [];
602
+ }
603
+ });
523
604
  }
524
- export function resolveSpaceFolder(spaceId, identifier, options = {}) {
605
+ export async function resolveSpaceFolder(spaceId, identifier, options = {}) {
525
606
  const query = identifier.trim();
526
607
  if (!query)
527
608
  return null;
528
609
  const lowerQuery = query.toLowerCase();
529
- const folders = listSpaceFolders({ spaceId, includeTrash: options.includeTrash === true });
610
+ const folders = await listSpaceFolders({ spaceId, includeTrash: options.includeTrash === true });
530
611
  const exactById = folders.find((folder) => folder.id === query);
531
612
  if (exactById)
532
613
  return exactById;
@@ -539,42 +620,51 @@ export function resolveSpaceFolder(spaceId, identifier, options = {}) {
539
620
  }
540
621
  return null;
541
622
  }
542
- export function isSpaceNoteInTrash(identifier) {
543
- const database = getDatabase();
544
- if (!database)
545
- return false;
546
- try {
547
- const row = database
548
- .prepare(`
549
- WITH RECURSIVE parent_chain AS (
550
- SELECT id, parent, is_dir, title
551
- FROM notes
552
- WHERE (id = ? OR filename = ?)
553
- AND is_dir = 0
554
- UNION ALL
555
- SELECT n.id, n.parent, n.is_dir, n.title
556
- FROM notes n
557
- INNER JOIN parent_chain pc ON n.id = pc.parent
558
- )
559
- SELECT id
560
- FROM parent_chain
561
- WHERE is_dir = 1
562
- AND lower(title) = lower(?)
563
- LIMIT 1
564
- `)
565
- .get(identifier, identifier, SPACE_TRASH_FOLDER_TITLE);
566
- return Boolean(row?.id);
567
- }
568
- catch (error) {
569
- console.error('Error checking TeamSpace trash status:', error);
570
- return false;
571
- }
623
+ export async function isSpaceNoteInTrash(identifier) {
624
+ return bridgeOrFallback(async (bridge) => {
625
+ const target = (await bridge.getSpaceNoteRow({ id: identifier })) ??
626
+ (await bridge.getSpaceNoteRow({ filename: identifier }));
627
+ if (!target)
628
+ return false;
629
+ const parents = await bridge.getSpaceParentRows(target.id);
630
+ return parents.some(isTrashFolderRow);
631
+ }, () => {
632
+ const database = getDatabase();
633
+ if (!database)
634
+ return false;
635
+ try {
636
+ const row = database
637
+ .prepare(`
638
+ WITH RECURSIVE parent_chain AS (
639
+ SELECT id, parent, is_dir, title
640
+ FROM notes
641
+ WHERE (id = ? OR filename = ?)
642
+ AND is_dir = 0
643
+ UNION ALL
644
+ SELECT n.id, n.parent, n.is_dir, n.title
645
+ FROM notes n
646
+ INNER JOIN parent_chain pc ON n.id = pc.parent
647
+ )
648
+ SELECT id
649
+ FROM parent_chain
650
+ WHERE is_dir = 1
651
+ AND lower(title) = lower(?)
652
+ LIMIT 1
653
+ `)
654
+ .get(identifier, identifier, SPACE_TRASH_FOLDER_TITLE);
655
+ return Boolean(row?.id);
656
+ }
657
+ catch (error) {
658
+ console.error('Error checking TeamSpace trash status:', error);
659
+ return false;
660
+ }
661
+ });
572
662
  }
573
663
  /**
574
664
  * Extract all unique tags from teamspace notes
575
665
  */
576
- export function extractSpaceTags(spaceId) {
577
- const notes = listSpaceNotes(spaceId);
666
+ export async function extractSpaceTags(spaceId) {
667
+ const notes = await listSpaceNotes(spaceId);
578
668
  const tags = new Set();
579
669
  for (const note of notes) {
580
670
  for (const tag of extractTagsFromContent(note.content)) {
@@ -583,34 +673,38 @@ export function extractSpaceTags(spaceId) {
583
673
  }
584
674
  return Array.from(tags).sort();
585
675
  }
586
- /**
587
- * Get calendar note from teamspace by date
588
- */
589
- export function getSpaceCalendarNote(dateStr, spaceId) {
590
- const database = getDatabase();
591
- if (!database)
592
- return null;
593
- try {
594
- // Get descendants first
595
- const descendantIds = getSpaceDescendantIds(database, spaceId);
596
- if (descendantIds.length === 0)
676
+ export async function getSpaceCalendarNote(dateStr, spaceId) {
677
+ return bridgeOrFallback(async (bridge) => {
678
+ const allRows = await bridge.listSpaceNoteRows({ spaceId });
679
+ const match = allRows.find((r) => r.note_type === SQLITE_NOTE_TYPES.TEAMSPACE_CALENDAR &&
680
+ r.is_dir === 0 &&
681
+ r.filename.includes(dateStr));
682
+ return match ? bridgeRowToNote(match, allRows) : null;
683
+ }, () => {
684
+ const database = getDatabase();
685
+ if (!database)
597
686
  return null;
598
- const placeholders = descendantIds.map(() => '?').join(',');
599
- const row = database
600
- .prepare(`
601
- SELECT id, content, note_type, title, filename, parent, is_dir, created_at, modified_at
602
- FROM notes
603
- WHERE note_type = ?
604
- AND id IN (${placeholders})
605
- AND filename LIKE ?
606
- AND is_dir = 0
607
- `)
608
- .get(SQLITE_NOTE_TYPES.TEAMSPACE_CALENDAR, ...descendantIds, `%${dateStr}%`);
609
- return row ? rowToNote(row, database) : null;
610
- }
611
- catch (error) {
612
- console.error('Error getting teamspace calendar note:', error);
613
- return null;
614
- }
687
+ try {
688
+ const descendantIds = getSpaceDescendantIds(database, spaceId);
689
+ if (descendantIds.length === 0)
690
+ return null;
691
+ const placeholders = descendantIds.map(() => '?').join(',');
692
+ const row = database
693
+ .prepare(`
694
+ SELECT id, content, note_type, title, filename, parent, is_dir, created_at, modified_at
695
+ FROM notes
696
+ WHERE note_type = ?
697
+ AND id IN (${placeholders})
698
+ AND filename LIKE ?
699
+ AND is_dir = 0
700
+ `)
701
+ .get(SQLITE_NOTE_TYPES.TEAMSPACE_CALENDAR, ...descendantIds, `%${dateStr}%`);
702
+ return row ? rowToNote(row, database) : null;
703
+ }
704
+ catch (error) {
705
+ console.error('Error getting teamspace calendar note:', error);
706
+ return null;
707
+ }
708
+ });
615
709
  }
616
710
  //# sourceMappingURL=sqlite-reader.js.map