zotero-bridge 1.0.3 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"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"}
package/dist/database.js CHANGED
@@ -9,14 +9,17 @@
9
9
  * @author Combjellyshen
10
10
  */
11
11
  import initSqlJs from 'sql.js';
12
- import { existsSync, readFileSync, writeFileSync } from 'fs';
12
+ import { existsSync, readFileSync, writeFileSync, copyFileSync } from 'fs';
13
13
  import { homedir } from 'os';
14
14
  import { join } from 'path';
15
+ import { execSync } from 'child_process';
15
16
  export class ZoteroDatabase {
16
17
  db = null;
17
18
  dbPath;
18
19
  readonly;
19
20
  SQL = null;
21
+ backupPath = null;
22
+ hasUnsavedChanges = false;
20
23
  constructor(dbPath, readonly = false) {
21
24
  this.dbPath = dbPath || this.findDefaultZoteroDB();
22
25
  this.readonly = readonly;
@@ -48,6 +51,59 @@ export class ZoteroDatabase {
48
51
  // Default fallback
49
52
  return join(home, 'Zotero', 'zotero.sqlite');
50
53
  }
54
+ /**
55
+ * Check if Zotero is currently running
56
+ */
57
+ isZoteroRunning() {
58
+ try {
59
+ if (process.platform === 'win32') {
60
+ const result = execSync('tasklist /FI "IMAGENAME eq zotero.exe" /NH', { encoding: 'utf8' });
61
+ return result.toLowerCase().includes('zotero.exe');
62
+ }
63
+ else if (process.platform === 'darwin') {
64
+ const result = execSync('pgrep -x Zotero', { encoding: 'utf8' });
65
+ return result.trim().length > 0;
66
+ }
67
+ else {
68
+ const result = execSync('pgrep -x zotero', { encoding: 'utf8' });
69
+ return result.trim().length > 0;
70
+ }
71
+ }
72
+ catch {
73
+ return false;
74
+ }
75
+ }
76
+ /**
77
+ * Check if WAL files exist (indicates active Zotero session)
78
+ */
79
+ hasWALFiles() {
80
+ const walPath = this.dbPath + '-wal';
81
+ const shmPath = this.dbPath + '-shm';
82
+ return existsSync(walPath) || existsSync(shmPath);
83
+ }
84
+ /**
85
+ * Create a backup of the database before modification
86
+ */
87
+ createBackup() {
88
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
89
+ const backupPath = this.dbPath.replace('.sqlite', `.backup-${timestamp}.sqlite`);
90
+ copyFileSync(this.dbPath, backupPath);
91
+ return backupPath;
92
+ }
93
+ /**
94
+ * Verify database integrity
95
+ */
96
+ verifyIntegrity() {
97
+ if (!this.db)
98
+ return false;
99
+ try {
100
+ const result = this.queryOne('PRAGMA integrity_check');
101
+ return result && result.integrity_check === 'ok';
102
+ }
103
+ catch {
104
+ return false;
105
+ }
106
+ }
51
107
  /**
52
108
  * Connect to the Zotero database
53
109
  */
@@ -58,6 +114,16 @@ export class ZoteroDatabase {
58
114
  if (!existsSync(this.dbPath)) {
59
115
  throw new Error(`Zotero database not found at: ${this.dbPath}`);
60
116
  }
117
+ // WARNING: Check if Zotero is running (write operations are dangerous)
118
+ if (!this.readonly && this.isZoteroRunning()) {
119
+ console.warn('⚠️ WARNING: Zotero is currently running. Write operations may corrupt the database!');
120
+ console.warn(' Please close Zotero before making changes, or use readonly mode.');
121
+ }
122
+ // WARNING: Check for WAL files
123
+ if (!this.readonly && this.hasWALFiles()) {
124
+ console.warn('⚠️ WARNING: WAL files detected. Zotero may be running or was not closed properly.');
125
+ console.warn(' Writing to the database may cause corruption!');
126
+ }
61
127
  // Initialize sql.js
62
128
  this.SQL = await initSqlJs();
63
129
  // Read the database file
@@ -66,25 +132,63 @@ export class ZoteroDatabase {
66
132
  this.db = db;
67
133
  // Enable foreign keys
68
134
  db.run('PRAGMA foreign_keys = ON');
135
+ // Verify database integrity on connect
136
+ if (!this.verifyIntegrity()) {
137
+ console.error('❌ ERROR: Database integrity check failed! The database may already be corrupted.');
138
+ }
69
139
  }
70
140
  /**
71
141
  * Save changes to the database file
142
+ * WARNING: This can corrupt the database if Zotero is running!
72
143
  */
73
144
  save() {
74
- if (!this.db || this.readonly) {
145
+ if (!this.db || this.readonly || !this.hasUnsavedChanges) {
75
146
  return;
76
147
  }
148
+ // Safety check before save
149
+ if (this.isZoteroRunning()) {
150
+ throw new Error('Cannot save: Zotero is currently running. Please close Zotero first to avoid database corruption.');
151
+ }
152
+ if (this.hasWALFiles()) {
153
+ throw new Error('Cannot save: WAL files detected. Please close Zotero and wait for WAL files to be cleaned up.');
154
+ }
155
+ // Create backup before saving
156
+ if (!this.backupPath) {
157
+ this.backupPath = this.createBackup();
158
+ console.log(`📁 Backup created at: ${this.backupPath}`);
159
+ }
160
+ // Verify integrity before save (only check a few important tables to speed up)
161
+ try {
162
+ this.queryOne("SELECT 1 FROM items LIMIT 1");
163
+ this.queryOne("SELECT 1 FROM itemData LIMIT 1");
164
+ }
165
+ catch (e) {
166
+ throw new Error('Cannot save: Basic database check failed. Database may be corrupted.');
167
+ }
77
168
  const data = this.db.export();
78
169
  const buffer = Buffer.from(data);
79
170
  writeFileSync(this.dbPath, buffer);
171
+ this.hasUnsavedChanges = false;
172
+ console.log('✅ Database saved successfully.');
173
+ }
174
+ /**
175
+ * Mark that changes have been made (for tracking unsaved changes)
176
+ */
177
+ markDirty() {
178
+ this.hasUnsavedChanges = true;
80
179
  }
81
180
  /**
82
181
  * Disconnect from the database
83
182
  */
84
183
  disconnect() {
85
184
  if (this.db) {
86
- if (!this.readonly) {
87
- this.save();
185
+ if (!this.readonly && this.hasUnsavedChanges) {
186
+ try {
187
+ this.save();
188
+ }
189
+ catch (e) {
190
+ console.error('Failed to save on disconnect:', e);
191
+ }
88
192
  }
89
193
  this.db.close();
90
194
  this.db = null;
@@ -105,6 +209,32 @@ export class ZoteroDatabase {
105
209
  getPath() {
106
210
  return this.dbPath;
107
211
  }
212
+ /**
213
+ * Get current timestamp in Zotero SQL format
214
+ */
215
+ getCurrentTimestamp() {
216
+ return new Date().toISOString().replace('T', ' ').replace('Z', '').slice(0, -4);
217
+ }
218
+ /**
219
+ * Update item metadata after modification (dateModified, version, synced)
220
+ * This is CRITICAL for Zotero compatibility!
221
+ *
222
+ * According to Zotero's official code:
223
+ * - dateModified must be updated on every change
224
+ * - version must be incremented
225
+ * - synced must be set to 0 (false) to indicate local change
226
+ */
227
+ updateItemMetadata(itemID) {
228
+ const timestamp = this.getCurrentTimestamp();
229
+ this.db.run(`
230
+ UPDATE items
231
+ SET dateModified = ?,
232
+ clientDateModified = ?,
233
+ version = version + 1,
234
+ synced = 0
235
+ WHERE itemID = ?
236
+ `, [timestamp, timestamp, itemID]);
237
+ }
108
238
  /**
109
239
  * Execute a query and return all results
110
240
  */
@@ -136,7 +266,12 @@ export class ZoteroDatabase {
136
266
  if (!this.db) {
137
267
  throw new Error('Database not connected');
138
268
  }
269
+ // Safety check before modifying database
270
+ if (this.isZoteroRunning()) {
271
+ throw new Error('Cannot modify database: Zotero is currently running. Please close Zotero first.');
272
+ }
139
273
  this.db.run(sql, params);
274
+ this.markDirty(); // Mark that we have unsaved changes
140
275
  const changes = this.db.getRowsModified();
141
276
  const lastId = this.queryOne('SELECT last_insert_rowid() as id');
142
277
  return {
@@ -282,6 +417,8 @@ export class ZoteroDatabase {
282
417
  }
283
418
  /**
284
419
  * Add tag to item
420
+ *
421
+ * Following Zotero's pattern: modifying item tags should update item metadata
285
422
  */
286
423
  addTagToItem(itemID, tagName, type = 0) {
287
424
  // Get or create tag
@@ -294,6 +431,8 @@ export class ZoteroDatabase {
294
431
  return false;
295
432
  }
296
433
  this.execute('INSERT INTO itemTags (itemID, tagID) VALUES (?, ?)', [itemID, tagID]);
434
+ // CRITICAL: Update item metadata for Zotero compatibility
435
+ this.updateItemMetadata(itemID);
297
436
  if (!this.readonly) {
298
437
  this.save();
299
438
  }
@@ -308,6 +447,10 @@ export class ZoteroDatabase {
308
447
  return false;
309
448
  }
310
449
  const result = this.execute('DELETE FROM itemTags WHERE itemID = ? AND tagID = ?', [itemID, tag.tagID]);
450
+ if (result.changes > 0) {
451
+ // CRITICAL: Update item metadata for Zotero compatibility
452
+ this.updateItemMetadata(itemID);
453
+ }
311
454
  if (!this.readonly && result.changes > 0) {
312
455
  this.save();
313
456
  }
@@ -342,6 +485,8 @@ export class ZoteroDatabase {
342
485
  }
343
486
  /**
344
487
  * Add item to collection
488
+ *
489
+ * Following Zotero's pattern: collection membership changes update item metadata
345
490
  */
346
491
  addItemToCollection(itemID, collectionID) {
347
492
  // Check if already in collection
@@ -352,6 +497,8 @@ export class ZoteroDatabase {
352
497
  return false;
353
498
  }
354
499
  this.execute('INSERT INTO collectionItems (itemID, collectionID) VALUES (?, ?)', [itemID, collectionID]);
500
+ // CRITICAL: Update item metadata for Zotero compatibility
501
+ this.updateItemMetadata(itemID);
355
502
  if (!this.readonly) {
356
503
  this.save();
357
504
  }
@@ -359,9 +506,15 @@ export class ZoteroDatabase {
359
506
  }
360
507
  /**
361
508
  * Remove item from collection
509
+ *
510
+ * Following Zotero's pattern: collection membership changes update item metadata
362
511
  */
363
512
  removeItemFromCollection(itemID, collectionID) {
364
513
  const result = this.execute('DELETE FROM collectionItems WHERE itemID = ? AND collectionID = ?', [itemID, collectionID]);
514
+ if (result.changes > 0) {
515
+ // CRITICAL: Update item metadata for Zotero compatibility
516
+ this.updateItemMetadata(itemID);
517
+ }
365
518
  if (!this.readonly && result.changes > 0) {
366
519
  this.save();
367
520
  }
@@ -462,6 +615,11 @@ export class ZoteroDatabase {
462
615
  }
463
616
  /**
464
617
  * Set item abstract
618
+ *
619
+ * Following Zotero's official pattern for modifying item data:
620
+ * 1. Get or create value in itemDataValues
621
+ * 2. Insert or update itemData
622
+ * 3. Update item metadata (dateModified, version, synced)
465
623
  */
466
624
  setItemAbstract(itemID, abstract) {
467
625
  // Get abstractNote field ID
@@ -469,7 +627,7 @@ export class ZoteroDatabase {
469
627
  if (!field) {
470
628
  return false;
471
629
  }
472
- // Get or create value
630
+ // Get or create value (Zotero stores unique values in itemDataValues)
473
631
  let valueRow = this.queryOne('SELECT valueID FROM itemDataValues WHERE value = ?', [abstract]);
474
632
  if (!valueRow) {
475
633
  const result = this.execute('INSERT INTO itemDataValues (value) VALUES (?)', [abstract]);
@@ -487,6 +645,8 @@ export class ZoteroDatabase {
487
645
  // Insert
488
646
  this.execute('INSERT INTO itemData (itemID, fieldID, valueID) VALUES (?, ?, ?)', [itemID, field.fieldID, valueRow.valueID]);
489
647
  }
648
+ // CRITICAL: Update item metadata for Zotero compatibility
649
+ this.updateItemMetadata(itemID);
490
650
  if (!this.readonly) {
491
651
  this.save();
492
652
  }
@@ -504,6 +664,11 @@ export class ZoteroDatabase {
504
664
  }
505
665
  /**
506
666
  * Add note to item
667
+ *
668
+ * Following Zotero's official pattern for creating notes:
669
+ * 1. Create item with note type
670
+ * 2. Create itemNotes entry
671
+ * 3. Update parent item's metadata (as adding a child note is a modification)
507
672
  */
508
673
  addItemNote(parentItemID, noteContent, title = '') {
509
674
  // Get parent item's library ID
@@ -513,19 +678,27 @@ export class ZoteroDatabase {
513
678
  }
514
679
  // Get note item type ID
515
680
  const noteType = this.queryOne("SELECT itemTypeID FROM itemTypes WHERE typeName = 'note'");
516
- // Create item entry
681
+ // Create item entry with proper timestamp format
517
682
  const key = this.generateKey();
518
- const now = new Date().toISOString().replace('T', ' ').replace('Z', '');
683
+ const now = this.getCurrentTimestamp();
684
+ // Note: synced=0 for new local items, clientDateModified is also set
519
685
  const itemResult = this.execute(`
520
- INSERT INTO items (itemTypeID, dateAdded, dateModified, key, libraryID, version)
521
- VALUES (?, ?, ?, ?, ?, 0)
522
- `, [noteType.itemTypeID, now, now, key, parent.libraryID]);
686
+ INSERT INTO items (itemTypeID, dateAdded, dateModified, clientDateModified, key, libraryID, version, synced)
687
+ VALUES (?, ?, ?, ?, ?, ?, 0, 0)
688
+ `, [noteType.itemTypeID, now, now, now, key, parent.libraryID]);
523
689
  const itemID = itemResult.lastInsertRowid;
690
+ // Wrap note content in Zotero's expected format if not already
691
+ let formattedNote = noteContent;
692
+ if (!noteContent.startsWith('<div class="zotero-note')) {
693
+ formattedNote = `<div class="zotero-note znv1">${noteContent}</div>`;
694
+ }
524
695
  // Create note entry
525
696
  this.execute(`
526
697
  INSERT INTO itemNotes (itemID, parentItemID, note, title)
527
698
  VALUES (?, ?, ?, ?)
528
- `, [itemID, parentItemID, noteContent, title]);
699
+ `, [itemID, parentItemID, formattedNote, title]);
700
+ // CRITICAL: Update parent item metadata (adding a note is considered a modification)
701
+ this.updateItemMetadata(parentItemID);
529
702
  if (!this.readonly) {
530
703
  this.save();
531
704
  }
@@ -1027,5 +1200,304 @@ export class ZoteroDatabase {
1027
1200
  run(sql, params = []) {
1028
1201
  return this.execute(sql, params);
1029
1202
  }
1203
+ // ============================================
1204
+ // Duplicate Detection & Attachment Validation
1205
+ // ============================================
1206
+ /**
1207
+ * Find duplicate items based on title, DOI, or ISBN
1208
+ */
1209
+ findDuplicates(field = 'title', libraryID = 1) {
1210
+ if (field === 'title') {
1211
+ // Find items with the same title
1212
+ return this.queryAll(`
1213
+ SELECT
1214
+ iv.value as title,
1215
+ GROUP_CONCAT(i.itemID) as itemIDs,
1216
+ COUNT(*) as count
1217
+ FROM items i
1218
+ JOIN itemData id ON i.itemID = id.itemID
1219
+ JOIN itemDataValues iv ON id.valueID = iv.valueID
1220
+ JOIN fields f ON id.fieldID = f.fieldID
1221
+ WHERE f.fieldName = 'title'
1222
+ AND i.libraryID = ?
1223
+ AND i.itemTypeID NOT IN (1, 14) -- Exclude notes and attachments
1224
+ GROUP BY iv.value
1225
+ HAVING COUNT(*) > 1
1226
+ ORDER BY count DESC
1227
+ `, [libraryID]);
1228
+ }
1229
+ else if (field === 'doi') {
1230
+ return this.queryAll(`
1231
+ SELECT
1232
+ iv.value as doi,
1233
+ GROUP_CONCAT(i.itemID) as itemIDs,
1234
+ COUNT(*) as count
1235
+ FROM items i
1236
+ JOIN itemData id ON i.itemID = id.itemID
1237
+ JOIN itemDataValues iv ON id.valueID = iv.valueID
1238
+ JOIN fields f ON id.fieldID = f.fieldID
1239
+ WHERE f.fieldName = 'DOI'
1240
+ AND i.libraryID = ?
1241
+ AND iv.value != ''
1242
+ GROUP BY LOWER(iv.value)
1243
+ HAVING COUNT(*) > 1
1244
+ ORDER BY count DESC
1245
+ `, [libraryID]);
1246
+ }
1247
+ else {
1248
+ return this.queryAll(`
1249
+ SELECT
1250
+ iv.value as isbn,
1251
+ GROUP_CONCAT(i.itemID) as itemIDs,
1252
+ COUNT(*) as count
1253
+ FROM items i
1254
+ JOIN itemData id ON i.itemID = id.itemID
1255
+ JOIN itemDataValues iv ON id.valueID = iv.valueID
1256
+ JOIN fields f ON id.fieldID = f.fieldID
1257
+ WHERE f.fieldName = 'ISBN'
1258
+ AND i.libraryID = ?
1259
+ AND iv.value != ''
1260
+ GROUP BY REPLACE(REPLACE(iv.value, '-', ''), ' ', '')
1261
+ HAVING COUNT(*) > 1
1262
+ ORDER BY count DESC
1263
+ `, [libraryID]);
1264
+ }
1265
+ }
1266
+ /**
1267
+ * Validate attachment files exist on disk
1268
+ */
1269
+ validateAttachments(itemID, checkAll = false) {
1270
+ let attachments;
1271
+ if (itemID) {
1272
+ attachments = this.queryAll(`
1273
+ SELECT ia.itemID, ia.parentItemID, ia.path, ia.contentType, i.key
1274
+ FROM itemAttachments ia
1275
+ JOIN items i ON ia.itemID = i.itemID
1276
+ WHERE ia.parentItemID = ? AND ia.path IS NOT NULL
1277
+ `, [itemID]);
1278
+ }
1279
+ else if (checkAll) {
1280
+ attachments = this.queryAll(`
1281
+ SELECT ia.itemID, ia.parentItemID, ia.path, ia.contentType, i.key
1282
+ FROM itemAttachments ia
1283
+ JOIN items i ON ia.itemID = i.itemID
1284
+ WHERE ia.path IS NOT NULL
1285
+ LIMIT 1000
1286
+ `);
1287
+ }
1288
+ else {
1289
+ return { valid: [], missing: [], total: 0 };
1290
+ }
1291
+ const valid = [];
1292
+ const missing = [];
1293
+ for (const att of attachments) {
1294
+ const fullPath = this.getAttachmentPath(att.itemID);
1295
+ if (fullPath && existsSync(fullPath)) {
1296
+ valid.push({
1297
+ ...att,
1298
+ fullPath,
1299
+ exists: true
1300
+ });
1301
+ }
1302
+ else {
1303
+ missing.push({
1304
+ ...att,
1305
+ fullPath,
1306
+ exists: false
1307
+ });
1308
+ }
1309
+ }
1310
+ return {
1311
+ valid,
1312
+ missing,
1313
+ total: attachments.length
1314
+ };
1315
+ }
1316
+ /**
1317
+ * Get a valid (existing) attachment for an item
1318
+ * Useful when multiple attachment records exist but only one file is present
1319
+ */
1320
+ getValidAttachment(parentItemID, contentType = 'application/pdf') {
1321
+ const attachments = this.queryAll(`
1322
+ SELECT ia.itemID, ia.path, ia.contentType, i.key
1323
+ FROM itemAttachments ia
1324
+ JOIN items i ON ia.itemID = i.itemID
1325
+ WHERE ia.parentItemID = ?
1326
+ AND ia.contentType = ?
1327
+ AND ia.path IS NOT NULL
1328
+ `, [parentItemID, contentType]);
1329
+ // Return the first attachment that actually exists
1330
+ for (const att of attachments) {
1331
+ const fullPath = this.getAttachmentPath(att.itemID);
1332
+ if (fullPath && existsSync(fullPath)) {
1333
+ return {
1334
+ ...att,
1335
+ fullPath,
1336
+ exists: true
1337
+ };
1338
+ }
1339
+ }
1340
+ return null;
1341
+ }
1342
+ /**
1343
+ * Find items with valid (existing) PDF files
1344
+ */
1345
+ findItemsWithValidPDF(options) {
1346
+ let items;
1347
+ if (options.doi) {
1348
+ // Search by DOI
1349
+ const normalizedDOI = options.doi.replace(/^https?:\/\/doi\.org\//i, '').replace(/^doi:/i, '').trim();
1350
+ items = this.queryAll(`
1351
+ SELECT DISTINCT i.itemID, i.key, i.dateAdded, iv.value as doi
1352
+ FROM items i
1353
+ JOIN itemData id ON i.itemID = id.itemID
1354
+ JOIN itemDataValues iv ON id.valueID = iv.valueID
1355
+ JOIN fields f ON id.fieldID = f.fieldID
1356
+ WHERE f.fieldName = 'DOI' AND LOWER(iv.value) = LOWER(?)
1357
+ `, [normalizedDOI]);
1358
+ }
1359
+ else if (options.title) {
1360
+ // Search by title
1361
+ items = this.queryAll(`
1362
+ SELECT DISTINCT i.itemID, i.key, i.dateAdded, iv.value as title
1363
+ FROM items i
1364
+ JOIN itemData id ON i.itemID = id.itemID
1365
+ JOIN itemDataValues iv ON id.valueID = iv.valueID
1366
+ JOIN fields f ON id.fieldID = f.fieldID
1367
+ WHERE f.fieldName = 'title' AND iv.value LIKE ?
1368
+ LIMIT 50
1369
+ `, [`%${options.title}%`]);
1370
+ }
1371
+ else {
1372
+ return [];
1373
+ }
1374
+ if (!options.requireValidPDF) {
1375
+ return items.map(item => ({
1376
+ ...this.getItemDetails(item.itemID),
1377
+ hasValidPDF: this.getValidAttachment(item.itemID) !== null
1378
+ }));
1379
+ }
1380
+ // Filter to only items with valid PDF
1381
+ const results = [];
1382
+ for (const item of items) {
1383
+ const validAttachment = this.getValidAttachment(item.itemID);
1384
+ if (validAttachment) {
1385
+ results.push({
1386
+ ...this.getItemDetails(item.itemID),
1387
+ validAttachment
1388
+ });
1389
+ }
1390
+ }
1391
+ return results;
1392
+ }
1393
+ /**
1394
+ * Find orphan attachments (records without files)
1395
+ */
1396
+ findOrphanAttachments(limit = 100) {
1397
+ const attachments = this.queryAll(`
1398
+ SELECT ia.itemID, ia.parentItemID, ia.path, ia.contentType, i.key,
1399
+ parent.itemID as parentExists
1400
+ FROM itemAttachments ia
1401
+ JOIN items i ON ia.itemID = i.itemID
1402
+ LEFT JOIN items parent ON ia.parentItemID = parent.itemID
1403
+ WHERE ia.path LIKE 'storage:%'
1404
+ LIMIT ?
1405
+ `, [limit]);
1406
+ const orphans = [];
1407
+ for (const att of attachments) {
1408
+ const fullPath = this.getAttachmentPath(att.itemID);
1409
+ if (!fullPath || !existsSync(fullPath)) {
1410
+ orphans.push({
1411
+ itemID: att.itemID,
1412
+ parentItemID: att.parentItemID,
1413
+ key: att.key,
1414
+ path: att.path,
1415
+ expectedPath: fullPath,
1416
+ reason: !fullPath ? 'invalid_path' : 'file_not_found'
1417
+ });
1418
+ }
1419
+ }
1420
+ return orphans;
1421
+ }
1422
+ /**
1423
+ * Delete orphan attachment records (use with caution!)
1424
+ */
1425
+ deleteOrphanAttachments(dryRun = true) {
1426
+ const orphans = this.findOrphanAttachments(500);
1427
+ if (dryRun || this.readonly) {
1428
+ return {
1429
+ orphans,
1430
+ deleted: 0,
1431
+ dryRun: true
1432
+ };
1433
+ }
1434
+ let deleted = 0;
1435
+ for (const orphan of orphans) {
1436
+ try {
1437
+ this.execute('DELETE FROM itemAttachments WHERE itemID = ?', [orphan.itemID]);
1438
+ this.execute('DELETE FROM items WHERE itemID = ?', [orphan.itemID]);
1439
+ deleted++;
1440
+ }
1441
+ catch (error) {
1442
+ console.error(`Failed to delete orphan ${orphan.itemID}:`, error);
1443
+ }
1444
+ }
1445
+ return {
1446
+ orphans,
1447
+ deleted,
1448
+ dryRun: false
1449
+ };
1450
+ }
1451
+ /**
1452
+ * Merge items by transferring notes and tags from source items to target
1453
+ */
1454
+ mergeItems(targetItemID, sourceItemIDs) {
1455
+ const errors = [];
1456
+ let notesTransferred = 0;
1457
+ let tagsTransferred = 0;
1458
+ // Verify target exists
1459
+ const target = this.getItemDetails(targetItemID);
1460
+ if (!target) {
1461
+ return {
1462
+ success: false,
1463
+ transferred: { notes: 0, tags: 0 },
1464
+ errors: ['Target item not found']
1465
+ };
1466
+ }
1467
+ for (const sourceID of sourceItemIDs) {
1468
+ if (sourceID === targetItemID)
1469
+ continue;
1470
+ // Transfer notes
1471
+ try {
1472
+ const notes = this.getItemNotes(sourceID);
1473
+ for (const note of notes) {
1474
+ this.addItemNote(targetItemID, note.note, `[Merged] ${note.title || ''}`);
1475
+ notesTransferred++;
1476
+ }
1477
+ }
1478
+ catch (error) {
1479
+ errors.push(`Failed to transfer notes from ${sourceID}: ${error}`);
1480
+ }
1481
+ // Transfer tags
1482
+ try {
1483
+ const tags = this.getItemTags(sourceID);
1484
+ for (const tag of tags) {
1485
+ this.addTagToItem(targetItemID, tag.name, tag.type);
1486
+ tagsTransferred++;
1487
+ }
1488
+ }
1489
+ catch (error) {
1490
+ errors.push(`Failed to transfer tags from ${sourceID}: ${error}`);
1491
+ }
1492
+ }
1493
+ return {
1494
+ success: errors.length === 0,
1495
+ transferred: {
1496
+ notes: notesTransferred,
1497
+ tags: tagsTransferred
1498
+ },
1499
+ errors
1500
+ };
1501
+ }
1030
1502
  }
1031
1503
  //# sourceMappingURL=database.js.map