@noteplanco/noteplan-mcp 1.1.23 → 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/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 +315 -221
- 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 +257 -159
- package/dist/noteplan/unified-store.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +142 -61
- 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 +16 -13
- package/dist/tools/calendar.d.ts.map +1 -1
- package/dist/tools/calendar.js +17 -16
- 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 +180 -177
- package/dist/tools/notes.js.map +1 -1
- package/dist/tools/notes.test.js +242 -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 };
|
|
@@ -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
|
-
|
|
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;
|
|
@@ -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
|
-
|
|
446
|
-
//
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
.join('
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
-
|
|
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
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
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:
|
|
566
|
+
spaceId: findRootSpaceIdFromRows(row.id, allRows),
|
|
517
567
|
}));
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
return null;
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
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
|
-
|
|
599
|
-
|
|
600
|
-
.
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
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
|