@noteplanco/noteplan-mcp 1.1.21 → 1.1.24
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 +7 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/noteplan/attachments-paths.d.ts +13 -0
- package/dist/noteplan/attachments-paths.d.ts.map +1 -0
- package/dist/noteplan/attachments-paths.js +27 -0
- package/dist/noteplan/attachments-paths.js.map +1 -0
- package/dist/noteplan/embeddings.js +1 -1
- package/dist/noteplan/embeddings.js.map +1 -1
- package/dist/noteplan/file-reader.d.ts +37 -46
- package/dist/noteplan/file-reader.d.ts.map +1 -1
- package/dist/noteplan/file-reader.js +200 -202
- package/dist/noteplan/file-reader.js.map +1 -1
- package/dist/noteplan/file-reader.test.d.ts +2 -0
- package/dist/noteplan/file-reader.test.d.ts.map +1 -0
- package/dist/noteplan/file-reader.test.js +67 -0
- package/dist/noteplan/file-reader.test.js.map +1 -0
- package/dist/noteplan/file-writer.d.ts +35 -31
- package/dist/noteplan/file-writer.d.ts.map +1 -1
- package/dist/noteplan/file-writer.js +280 -164
- package/dist/noteplan/file-writer.js.map +1 -1
- package/dist/noteplan/file-writer.test.js +704 -191
- package/dist/noteplan/file-writer.test.js.map +1 -1
- package/dist/noteplan/filter-store.d.ts +5 -5
- package/dist/noteplan/filter-store.d.ts.map +1 -1
- package/dist/noteplan/filter-store.js +94 -79
- package/dist/noteplan/filter-store.js.map +1 -1
- package/dist/noteplan/frontmatter-parser.d.ts.map +1 -1
- package/dist/noteplan/frontmatter-parser.js +5 -4
- package/dist/noteplan/frontmatter-parser.js.map +1 -1
- package/dist/noteplan/frontmatter-parser.test.js +44 -0
- package/dist/noteplan/frontmatter-parser.test.js.map +1 -1
- package/dist/noteplan/markdown-parser.js +1 -1
- package/dist/noteplan/markdown-parser.js.map +1 -1
- package/dist/noteplan/markdown-parser.test.js +194 -0
- package/dist/noteplan/markdown-parser.test.js.map +1 -1
- package/dist/noteplan/preferences.d.ts +1 -0
- package/dist/noteplan/preferences.d.ts.map +1 -1
- package/dist/noteplan/preferences.js +1 -0
- package/dist/noteplan/preferences.js.map +1 -1
- package/dist/noteplan/ripgrep-search.d.ts +25 -2
- package/dist/noteplan/ripgrep-search.d.ts.map +1 -1
- package/dist/noteplan/ripgrep-search.js +75 -2
- package/dist/noteplan/ripgrep-search.js.map +1 -1
- package/dist/noteplan/space-row-utils.d.ts +20 -0
- package/dist/noteplan/space-row-utils.d.ts.map +1 -0
- package/dist/noteplan/space-row-utils.js +78 -0
- package/dist/noteplan/space-row-utils.js.map +1 -0
- package/dist/noteplan/space-row-utils.test.d.ts +2 -0
- package/dist/noteplan/space-row-utils.test.d.ts.map +1 -0
- package/dist/noteplan/space-row-utils.test.js +123 -0
- package/dist/noteplan/space-row-utils.test.js.map +1 -0
- package/dist/noteplan/sqlite-reader.d.ts +12 -27
- package/dist/noteplan/sqlite-reader.d.ts.map +1 -1
- package/dist/noteplan/sqlite-reader.js +325 -223
- package/dist/noteplan/sqlite-reader.js.map +1 -1
- package/dist/noteplan/sqlite-writer.d.ts +1 -1
- package/dist/noteplan/sqlite-writer.d.ts.map +1 -1
- package/dist/noteplan/sqlite-writer.js +2 -2
- package/dist/noteplan/sqlite-writer.js.map +1 -1
- package/dist/noteplan/unified-store.d.ts +41 -30
- package/dist/noteplan/unified-store.d.ts.map +1 -1
- package/dist/noteplan/unified-store.js +307 -161
- package/dist/noteplan/unified-store.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +143 -62
- package/dist/server.js.map +1 -1
- package/dist/tools/attachments.d.ts +9 -9
- package/dist/tools/attachments.d.ts.map +1 -1
- package/dist/tools/attachments.js +74 -83
- package/dist/tools/attachments.js.map +1 -1
- package/dist/tools/attachments.test.js +170 -129
- package/dist/tools/attachments.test.js.map +1 -1
- package/dist/tools/calendar.d.ts +36 -13
- package/dist/tools/calendar.d.ts.map +1 -1
- package/dist/tools/calendar.js +44 -17
- package/dist/tools/calendar.js.map +1 -1
- package/dist/tools/embeddings.d.ts +6 -6
- package/dist/tools/embeddings.d.ts.map +1 -1
- package/dist/tools/embeddings.js +6 -6
- package/dist/tools/embeddings.js.map +1 -1
- package/dist/tools/events.d.ts +7 -3
- package/dist/tools/events.d.ts.map +1 -1
- package/dist/tools/events.js +51 -16
- package/dist/tools/events.js.map +1 -1
- package/dist/tools/filters.d.ts +28 -33
- package/dist/tools/filters.d.ts.map +1 -1
- package/dist/tools/filters.js +42 -105
- package/dist/tools/filters.js.map +1 -1
- package/dist/tools/notes.d.ts +80 -218
- package/dist/tools/notes.d.ts.map +1 -1
- package/dist/tools/notes.js +194 -180
- package/dist/tools/notes.js.map +1 -1
- package/dist/tools/notes.test.js +501 -21
- package/dist/tools/notes.test.js.map +1 -1
- package/dist/tools/search.d.ts +4 -3
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +9 -5
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/search.test.d.ts +2 -0
- package/dist/tools/search.test.d.ts.map +1 -0
- package/dist/tools/search.test.js +37 -0
- package/dist/tools/search.test.js.map +1 -0
- package/dist/tools/spaces.d.ts +20 -20
- package/dist/tools/spaces.d.ts.map +1 -1
- package/dist/tools/spaces.js +28 -28
- package/dist/tools/spaces.js.map +1 -1
- package/dist/tools/tasks.d.ts +22 -22
- package/dist/tools/tasks.d.ts.map +1 -1
- package/dist/tools/tasks.js +22 -22
- package/dist/tools/tasks.js.map +1 -1
- package/dist/tools/templates.d.ts +7 -7
- package/dist/tools/templates.d.ts.map +1 -1
- package/dist/tools/templates.js +4 -4
- package/dist/tools/templates.js.map +1 -1
- package/dist/tools/themes.js +1 -1
- package/dist/tools/themes.js.map +1 -1
- package/dist/transport/bridge-availability.d.ts +5 -0
- package/dist/transport/bridge-availability.d.ts.map +1 -0
- package/dist/transport/bridge-availability.js +92 -0
- package/dist/transport/bridge-availability.js.map +1 -0
- package/dist/transport/bridge-cascade.d.ts +18 -0
- package/dist/transport/bridge-cascade.d.ts.map +1 -0
- package/dist/transport/bridge-cascade.js +78 -0
- package/dist/transport/bridge-cascade.js.map +1 -0
- package/dist/transport/bridge-cascade.test.d.ts +2 -0
- package/dist/transport/bridge-cascade.test.d.ts.map +1 -0
- package/dist/transport/bridge-cascade.test.js +160 -0
- package/dist/transport/bridge-cascade.test.js.map +1 -0
- package/dist/transport/bridge-client.d.ts +197 -0
- package/dist/transport/bridge-client.d.ts.map +1 -0
- package/dist/transport/bridge-client.js +288 -0
- package/dist/transport/bridge-client.js.map +1 -0
- package/dist/transport/bridge-client.test.d.ts +2 -0
- package/dist/transport/bridge-client.test.d.ts.map +1 -0
- package/dist/transport/bridge-client.test.js +384 -0
- package/dist/transport/bridge-client.test.js.map +1 -0
- package/dist/transport/bridge-context.d.ts +10 -0
- package/dist/transport/bridge-context.d.ts.map +1 -0
- package/dist/transport/bridge-context.js +18 -0
- package/dist/transport/bridge-context.js.map +1 -0
- package/dist/transport/bridge-fs.d.ts +25 -0
- package/dist/transport/bridge-fs.d.ts.map +1 -0
- package/dist/transport/bridge-fs.js +129 -0
- package/dist/transport/bridge-fs.js.map +1 -0
- package/dist/utils/date-utils.d.ts +24 -0
- package/dist/utils/date-utils.d.ts.map +1 -1
- package/dist/utils/date-utils.js +55 -0
- package/dist/utils/date-utils.js.map +1 -1
- package/dist/utils/date-utils.test.d.ts +2 -0
- package/dist/utils/date-utils.test.d.ts.map +1 -0
- package/dist/utils/date-utils.test.js +109 -0
- package/dist/utils/date-utils.test.js.map +1 -0
- package/dist/utils/folder-access.d.ts +23 -0
- package/dist/utils/folder-access.d.ts.map +1 -0
- package/dist/utils/folder-access.js +131 -0
- package/dist/utils/folder-access.js.map +1 -0
- package/dist/utils/folder-access.test.d.ts +2 -0
- package/dist/utils/folder-access.test.d.ts.map +1 -0
- package/dist/utils/folder-access.test.js +182 -0
- package/dist/utils/folder-access.test.js.map +1 -0
- package/dist/utils/folder-matcher.d.ts.map +1 -1
- package/dist/utils/folder-matcher.js +16 -0
- package/dist/utils/folder-matcher.js.map +1 -1
- package/dist/utils/folder-matcher.test.js +42 -0
- package/dist/utils/folder-matcher.test.js.map +1 -1
- package/dist/utils/server-config.d.ts +10 -2
- package/dist/utils/server-config.d.ts.map +1 -1
- package/dist/utils/server-config.js +16 -2
- package/dist/utils/server-config.js.map +1 -1
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -1
- package/dist/utils/version.js +5 -1
- package/dist/utils/version.js.map +1 -1
- package/package.json +4 -3
- package/scripts/calendar-helper +0 -0
- 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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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.
|
|
111
|
+
noteCount: noteCountByParent.get(row.id) ?? 0,
|
|
117
112
|
}));
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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 };
|
|
@@ -263,7 +311,7 @@ function rowToNote(row, database) {
|
|
|
263
311
|
const isCalendar = row.note_type === SQLITE_NOTE_TYPES.TEAMSPACE_CALENDAR;
|
|
264
312
|
return {
|
|
265
313
|
id: row.id,
|
|
266
|
-
title: row.
|
|
314
|
+
title: row.content ? extractTitle(row.content) : row.title || 'Untitled',
|
|
267
315
|
filename: row.filename,
|
|
268
316
|
content: row.content || '',
|
|
269
317
|
type: isCalendar ? 'calendar' : 'note',
|
|
@@ -274,73 +322,84 @@ 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
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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;
|
|
343
401
|
try {
|
|
402
|
+
// First try matching the SQLite title column directly
|
|
344
403
|
let query = `
|
|
345
404
|
SELECT id, content, note_type, title, filename, parent, is_dir, created_at, modified_at
|
|
346
405
|
FROM notes
|
|
@@ -363,8 +422,15 @@ export function getSpaceNoteByTitle(title, spaceId, includeTrash = false) {
|
|
|
363
422
|
}
|
|
364
423
|
const rows = database.prepare(query).all(...params);
|
|
365
424
|
const filteredRows = filterRowsByTrash(database, rows, spaceId, includeTrash);
|
|
366
|
-
|
|
367
|
-
|
|
425
|
+
if (filteredRows.length > 0) {
|
|
426
|
+
return rowToNote(filteredRows[0], database);
|
|
427
|
+
}
|
|
428
|
+
// Fallback: the SQLite title column may not reflect the frontmatter title.
|
|
429
|
+
// Search notes whose content contains the title in frontmatter (title: or name: keys)
|
|
430
|
+
// and resolve via extractTitle to confirm.
|
|
431
|
+
const lowerTitle = title.toLowerCase();
|
|
432
|
+
const candidates = await listSpaceNotes({ spaceId, includeTrash });
|
|
433
|
+
return candidates.find((note) => note.title.toLowerCase() === lowerTitle) || null;
|
|
368
434
|
}
|
|
369
435
|
catch (error) {
|
|
370
436
|
console.error('Error getting teamspace note by title:', error);
|
|
@@ -425,100 +491,123 @@ function parseSearchPatterns(input) {
|
|
|
425
491
|
* Search space notes using LIKE with OR pattern support
|
|
426
492
|
* This is the primary search method - we don't modify NotePlan's database
|
|
427
493
|
*/
|
|
428
|
-
export function searchSpaceNotesFTS(query, options = {}) {
|
|
429
|
-
const database = getDatabase();
|
|
430
|
-
if (!database)
|
|
431
|
-
return [];
|
|
494
|
+
export async function searchSpaceNotesFTS(query, options = {}) {
|
|
432
495
|
const { spaceId, limit = 50, includeTrash = false } = options;
|
|
433
|
-
// Parse OR patterns
|
|
434
496
|
const patterns = parseSearchPatterns(query);
|
|
435
497
|
if (patterns.length === 0)
|
|
436
498
|
return [];
|
|
437
|
-
|
|
438
|
-
//
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
.join('
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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));
|
|
454
545
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
if (descendantIds.length === 0)
|
|
459
|
-
return [];
|
|
460
|
-
const placeholders = descendantIds.map(() => '?').join(',');
|
|
461
|
-
sql += ` AND id IN (${placeholders})`;
|
|
462
|
-
params.push(...descendantIds);
|
|
546
|
+
catch (error) {
|
|
547
|
+
console.error('Error searching space notes:', error);
|
|
548
|
+
return [];
|
|
463
549
|
}
|
|
464
|
-
|
|
465
|
-
sql += ` ORDER BY modified_at DESC LIMIT ?`;
|
|
466
|
-
params.push(limit);
|
|
467
|
-
const rows = database.prepare(sql).all(...params);
|
|
468
|
-
const filteredRows = filterRowsByTrash(database, rows, spaceId, includeTrash);
|
|
469
|
-
return filteredRows.map((row) => rowToNote(row, database));
|
|
470
|
-
}
|
|
471
|
-
catch (error) {
|
|
472
|
-
console.error('Error searching space notes:', error);
|
|
473
|
-
return [];
|
|
474
|
-
}
|
|
550
|
+
});
|
|
475
551
|
}
|
|
476
552
|
/**
|
|
477
553
|
* List folders in teamspace
|
|
478
554
|
*/
|
|
479
|
-
export function listSpaceFolders(spaceIdOrOptions) {
|
|
480
|
-
const database = getDatabase();
|
|
481
|
-
if (!database)
|
|
482
|
-
return [];
|
|
555
|
+
export async function listSpaceFolders(spaceIdOrOptions) {
|
|
483
556
|
const { spaceId, includeTrash } = normalizeListSpaceOptions(spaceIdOrOptions);
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
`;
|
|
490
|
-
const params = [];
|
|
491
|
-
if (spaceId) {
|
|
492
|
-
const descendantIds = getSpaceDescendantIds(database, spaceId);
|
|
493
|
-
if (descendantIds.length === 0)
|
|
494
|
-
return [];
|
|
495
|
-
const placeholders = descendantIds.map(() => '?').join(',');
|
|
496
|
-
query += ` AND id IN (${placeholders})`;
|
|
497
|
-
params.push(...descendantIds);
|
|
498
|
-
}
|
|
499
|
-
let rows = database.prepare(query).all(...params);
|
|
500
|
-
if (!includeTrash) {
|
|
501
|
-
rows = rows.filter((row) => row.title?.toLowerCase() !== SPACE_TRASH_FOLDER_TITLE.toLowerCase());
|
|
502
|
-
}
|
|
503
|
-
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) => ({
|
|
504
562
|
id: row.id,
|
|
505
563
|
path: row.filename,
|
|
506
564
|
name: row.title || row.id,
|
|
507
565
|
source: 'space',
|
|
508
|
-
spaceId:
|
|
566
|
+
spaceId: findRootSpaceIdFromRows(row.id, allRows),
|
|
509
567
|
}));
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
+
});
|
|
515
604
|
}
|
|
516
|
-
export function resolveSpaceFolder(spaceId, identifier, options = {}) {
|
|
605
|
+
export async function resolveSpaceFolder(spaceId, identifier, options = {}) {
|
|
517
606
|
const query = identifier.trim();
|
|
518
607
|
if (!query)
|
|
519
608
|
return null;
|
|
520
609
|
const lowerQuery = query.toLowerCase();
|
|
521
|
-
const folders = listSpaceFolders({ spaceId, includeTrash: options.includeTrash === true });
|
|
610
|
+
const folders = await listSpaceFolders({ spaceId, includeTrash: options.includeTrash === true });
|
|
522
611
|
const exactById = folders.find((folder) => folder.id === query);
|
|
523
612
|
if (exactById)
|
|
524
613
|
return exactById;
|
|
@@ -531,42 +620,51 @@ export function resolveSpaceFolder(spaceId, identifier, options = {}) {
|
|
|
531
620
|
}
|
|
532
621
|
return null;
|
|
533
622
|
}
|
|
534
|
-
export function isSpaceNoteInTrash(identifier) {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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
|
+
});
|
|
564
662
|
}
|
|
565
663
|
/**
|
|
566
664
|
* Extract all unique tags from teamspace notes
|
|
567
665
|
*/
|
|
568
|
-
export function extractSpaceTags(spaceId) {
|
|
569
|
-
const notes = listSpaceNotes(spaceId);
|
|
666
|
+
export async function extractSpaceTags(spaceId) {
|
|
667
|
+
const notes = await listSpaceNotes(spaceId);
|
|
570
668
|
const tags = new Set();
|
|
571
669
|
for (const note of notes) {
|
|
572
670
|
for (const tag of extractTagsFromContent(note.content)) {
|
|
@@ -575,34 +673,38 @@ export function extractSpaceTags(spaceId) {
|
|
|
575
673
|
}
|
|
576
674
|
return Array.from(tags).sort();
|
|
577
675
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
return null;
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
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)
|
|
589
686
|
return null;
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
.
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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
|
+
});
|
|
607
709
|
}
|
|
608
710
|
//# sourceMappingURL=sqlite-reader.js.map
|