zotero-bridge 1.1.2 → 1.1.4

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.
@@ -113,6 +113,24 @@ export declare class ZoteroDatabase {
113
113
  * Execute a statement (INSERT, UPDATE, DELETE)
114
114
  */
115
115
  private execute;
116
+ /**
117
+ * Begin a transaction for batch operations
118
+ * IMPORTANT: Always use try-finally to ensure commit/rollback
119
+ */
120
+ beginTransaction(): void;
121
+ /**
122
+ * Commit the current transaction
123
+ */
124
+ commitTransaction(): void;
125
+ /**
126
+ * Rollback the current transaction
127
+ */
128
+ rollbackTransaction(): void;
129
+ /**
130
+ * Execute a function within a transaction
131
+ * Automatically commits on success, rolls back on error
132
+ */
133
+ withTransaction<T>(fn: () => T): T;
116
134
  /**
117
135
  * Get all collections
118
136
  */
@@ -161,6 +179,7 @@ export declare class ZoteroDatabase {
161
179
  * Add tag to item
162
180
  *
163
181
  * Following Zotero's pattern: modifying item tags should update item metadata
182
+ * Note: itemTags.type is required (NOT NULL) - 0=user tag, 1=automatic
164
183
  */
165
184
  addTagToItem(itemID: number, tagName: string, type?: number): boolean;
166
185
  /**
@@ -194,6 +213,7 @@ export declare class ZoteroDatabase {
194
213
  getItemByKey(key: string): ZoteroItem | null;
195
214
  /**
196
215
  * Search items by title
216
+ * Excludes deleted items and non-searchable types (attachments, notes)
197
217
  */
198
218
  searchItems(query: string, limit?: number, libraryID?: number): any[];
199
219
  /**
@@ -240,14 +260,17 @@ export declare class ZoteroDatabase {
240
260
  getStoragePath(): string;
241
261
  /**
242
262
  * Find item by DOI
263
+ * Returns the most recently modified item if duplicates exist
243
264
  */
244
265
  findItemByDOI(doi: string): any | null;
245
266
  /**
246
267
  * Find item by ISBN
268
+ * Returns the most recently modified item if duplicates exist
247
269
  */
248
270
  findItemByISBN(isbn: string): any | null;
249
271
  /**
250
272
  * Find item by any identifier (DOI, ISBN, PMID, arXiv, etc.)
273
+ * Returns the most recently modified item if duplicates exist
251
274
  */
252
275
  findItemByIdentifier(identifier: string, type?: string): any | null;
253
276
  /**
@@ -331,6 +354,11 @@ export declare class ZoteroDatabase {
331
354
  };
332
355
  /**
333
356
  * Find duplicate items based on title, DOI, or ISBN
357
+ *
358
+ * Note: itemTypeID values vary by Zotero version:
359
+ * - attachment = 3
360
+ * - note = 28 (in newer versions) or 1 (in older versions)
361
+ * We exclude both attachments and notes from duplicate detection
334
362
  */
335
363
  findDuplicates(field?: 'title' | 'doi' | 'isbn', libraryID?: number): any[];
336
364
  /**
@@ -368,12 +396,14 @@ export declare class ZoteroDatabase {
368
396
  };
369
397
  /**
370
398
  * Merge items by transferring notes and tags from source items to target
399
+ * Uses transaction to ensure data consistency
371
400
  */
372
401
  mergeItems(targetItemID: number, sourceItemIDs: number[]): {
373
402
  success: boolean;
374
403
  transferred: {
375
404
  notes: number;
376
405
  tags: number;
406
+ attachments: number;
377
407
  };
378
408
  errors: string[];
379
409
  };
@@ -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
@@ -279,6 +279,55 @@ export class ZoteroDatabase {
279
279
  lastInsertRowid: lastId?.id || 0
280
280
  };
281
281
  }
282
+ /**
283
+ * Begin a transaction for batch operations
284
+ * IMPORTANT: Always use try-finally to ensure commit/rollback
285
+ */
286
+ beginTransaction() {
287
+ if (!this.db) {
288
+ throw new Error('Database not connected');
289
+ }
290
+ this.db.run('BEGIN TRANSACTION');
291
+ }
292
+ /**
293
+ * Commit the current transaction
294
+ */
295
+ commitTransaction() {
296
+ if (!this.db) {
297
+ throw new Error('Database not connected');
298
+ }
299
+ this.db.run('COMMIT');
300
+ }
301
+ /**
302
+ * Rollback the current transaction
303
+ */
304
+ rollbackTransaction() {
305
+ if (!this.db) {
306
+ throw new Error('Database not connected');
307
+ }
308
+ try {
309
+ this.db.run('ROLLBACK');
310
+ }
311
+ catch (e) {
312
+ // Ignore if no transaction is active
313
+ }
314
+ }
315
+ /**
316
+ * Execute a function within a transaction
317
+ * Automatically commits on success, rolls back on error
318
+ */
319
+ withTransaction(fn) {
320
+ this.beginTransaction();
321
+ try {
322
+ const result = fn();
323
+ this.commitTransaction();
324
+ return result;
325
+ }
326
+ catch (error) {
327
+ this.rollbackTransaction();
328
+ throw error;
329
+ }
330
+ }
282
331
  // ============================================
283
332
  // Collection (Directory) Operations
284
333
  // ============================================
@@ -330,8 +379,8 @@ export class ZoteroDatabase {
330
379
  createCollection(name, parentCollectionID = null, libraryID = 1) {
331
380
  const key = this.generateKey();
332
381
  const result = this.execute(`
333
- INSERT INTO collections (collectionName, parentCollectionID, libraryID, key, version)
334
- VALUES (?, ?, ?, ?, 0)
382
+ INSERT INTO collections (collectionName, parentCollectionID, libraryID, key, version, clientDateModified)
383
+ VALUES (?, ?, ?, ?, 0, CURRENT_TIMESTAMP)
335
384
  `, [name, parentCollectionID, libraryID, key]);
336
385
  if (!this.readonly) {
337
386
  this.save();
@@ -344,7 +393,7 @@ export class ZoteroDatabase {
344
393
  renameCollection(collectionID, newName) {
345
394
  const result = this.execute(`
346
395
  UPDATE collections
347
- SET collectionName = ?, version = version + 1
396
+ SET collectionName = ?, version = version + 1, clientDateModified = CURRENT_TIMESTAMP
348
397
  WHERE collectionID = ?
349
398
  `, [newName, collectionID]);
350
399
  if (!this.readonly && result.changes > 0) {
@@ -358,7 +407,7 @@ export class ZoteroDatabase {
358
407
  moveCollection(collectionID, newParentID) {
359
408
  const result = this.execute(`
360
409
  UPDATE collections
361
- SET parentCollectionID = ?, version = version + 1
410
+ SET parentCollectionID = ?, version = version + 1, clientDateModified = CURRENT_TIMESTAMP
362
411
  WHERE collectionID = ?
363
412
  `, [newParentID, collectionID]);
364
413
  if (!this.readonly && result.changes > 0) {
@@ -419,6 +468,7 @@ export class ZoteroDatabase {
419
468
  * Add tag to item
420
469
  *
421
470
  * Following Zotero's pattern: modifying item tags should update item metadata
471
+ * Note: itemTags.type is required (NOT NULL) - 0=user tag, 1=automatic
422
472
  */
423
473
  addTagToItem(itemID, tagName, type = 0) {
424
474
  // Get or create tag
@@ -430,7 +480,8 @@ export class ZoteroDatabase {
430
480
  if (existing) {
431
481
  return false;
432
482
  }
433
- this.execute('INSERT INTO itemTags (itemID, tagID) VALUES (?, ?)', [itemID, tagID]);
483
+ // itemTags.type is NOT NULL, must provide value
484
+ this.execute('INSERT INTO itemTags (itemID, tagID, type) VALUES (?, ?, ?)', [itemID, tagID, type]);
434
485
  // CRITICAL: Update item metadata for Zotero compatibility
435
486
  this.updateItemMetadata(itemID);
436
487
  if (!this.readonly) {
@@ -532,8 +583,13 @@ export class ZoteroDatabase {
532
583
  }
533
584
  /**
534
585
  * Search items by title
586
+ * Excludes deleted items and non-searchable types (attachments, notes)
535
587
  */
536
588
  searchItems(query, limit = 50, libraryID = 1) {
589
+ // Get note and attachment type IDs dynamically
590
+ const noteType = this.queryOne("SELECT itemTypeID FROM itemTypes WHERE typeName = 'note'");
591
+ const attachType = this.queryOne("SELECT itemTypeID FROM itemTypes WHERE typeName = 'attachment'");
592
+ const excludeTypes = [noteType?.itemTypeID || 28, attachType?.itemTypeID || 3];
537
593
  return this.queryAll(`
538
594
  SELECT DISTINCT i.itemID, i.key, i.itemTypeID, i.dateAdded, i.dateModified,
539
595
  iv.value as title
@@ -544,9 +600,11 @@ export class ZoteroDatabase {
544
600
  WHERE f.fieldName = 'title'
545
601
  AND iv.value LIKE ?
546
602
  AND i.libraryID = ?
603
+ AND i.itemTypeID NOT IN (?, ?)
604
+ AND i.itemID NOT IN (SELECT itemID FROM deletedItems)
547
605
  ORDER BY i.dateModified DESC
548
606
  LIMIT ?
549
- `, [`%${query}%`, libraryID, limit]);
607
+ `, [`%${query}%`, libraryID, excludeTypes[0], excludeTypes[1], limit]);
550
608
  }
551
609
  /**
552
610
  * Get item details with all fields
@@ -749,11 +807,13 @@ export class ZoteroDatabase {
749
807
  // ============================================
750
808
  /**
751
809
  * Find item by DOI
810
+ * Returns the most recently modified item if duplicates exist
752
811
  */
753
812
  findItemByDOI(doi) {
754
813
  // Normalize DOI (remove common prefixes)
755
814
  const normalizedDOI = doi.replace(/^https?:\/\/doi\.org\//i, '').replace(/^doi:/i, '').trim();
756
- const result = this.queryOne(`
815
+ // First check for duplicates
816
+ const allMatches = this.queryAll(`
757
817
  SELECT DISTINCT i.itemID, i.key, i.itemTypeID, i.dateAdded, i.dateModified,
758
818
  iv.value as doi
759
819
  FROM items i
@@ -761,19 +821,31 @@ export class ZoteroDatabase {
761
821
  JOIN itemDataValues iv ON id.valueID = iv.valueID
762
822
  JOIN fields f ON id.fieldID = f.fieldID
763
823
  WHERE f.fieldName = 'DOI' AND LOWER(iv.value) = LOWER(?)
824
+ ORDER BY i.dateModified DESC
764
825
  `, [normalizedDOI]);
765
- if (result) {
766
- return this.getItemDetails(result.itemID);
826
+ if (allMatches.length === 0) {
827
+ return null;
767
828
  }
768
- return null;
829
+ const result = this.getItemDetails(allMatches[0].itemID);
830
+ // Warn about duplicates
831
+ if (allMatches.length > 1) {
832
+ result._duplicateWarning = {
833
+ message: `Found ${allMatches.length} items with same DOI`,
834
+ duplicateItemIDs: allMatches.map((m) => m.itemID),
835
+ usingItemID: allMatches[0].itemID
836
+ };
837
+ }
838
+ return result;
769
839
  }
770
840
  /**
771
841
  * Find item by ISBN
842
+ * Returns the most recently modified item if duplicates exist
772
843
  */
773
844
  findItemByISBN(isbn) {
774
845
  // Normalize ISBN (remove hyphens and spaces)
775
846
  const normalizedISBN = isbn.replace(/[-\s]/g, '').trim();
776
- const result = this.queryOne(`
847
+ // First check for duplicates
848
+ const allMatches = this.queryAll(`
777
849
  SELECT DISTINCT i.itemID, i.key, i.itemTypeID, i.dateAdded, i.dateModified,
778
850
  iv.value as isbn
779
851
  FROM items i
@@ -781,14 +853,25 @@ export class ZoteroDatabase {
781
853
  JOIN itemDataValues iv ON id.valueID = iv.valueID
782
854
  JOIN fields f ON id.fieldID = f.fieldID
783
855
  WHERE f.fieldName = 'ISBN' AND REPLACE(REPLACE(iv.value, '-', ''), ' ', '') = ?
856
+ ORDER BY i.dateModified DESC
784
857
  `, [normalizedISBN]);
785
- if (result) {
786
- return this.getItemDetails(result.itemID);
858
+ if (allMatches.length === 0) {
859
+ return null;
787
860
  }
788
- return null;
861
+ const result = this.getItemDetails(allMatches[0].itemID);
862
+ // Warn about duplicates
863
+ if (allMatches.length > 1) {
864
+ result._duplicateWarning = {
865
+ message: `Found ${allMatches.length} items with same ISBN`,
866
+ duplicateItemIDs: allMatches.map((m) => m.itemID),
867
+ usingItemID: allMatches[0].itemID
868
+ };
869
+ }
870
+ return result;
789
871
  }
790
872
  /**
791
873
  * Find item by any identifier (DOI, ISBN, PMID, arXiv, etc.)
874
+ * Returns the most recently modified item if duplicates exist
792
875
  */
793
876
  findItemByIdentifier(identifier, type) {
794
877
  const fieldMap = {
@@ -806,16 +889,26 @@ export class ZoteroDatabase {
806
889
  else if (type.toLowerCase() === 'isbn') {
807
890
  return this.findItemByISBN(identifier);
808
891
  }
809
- const result = this.queryOne(`
892
+ // For other identifier types, also check for duplicates
893
+ const allMatches = this.queryAll(`
810
894
  SELECT DISTINCT i.itemID, i.key
811
895
  FROM items i
812
896
  JOIN itemData id ON i.itemID = id.itemID
813
897
  JOIN itemDataValues iv ON id.valueID = iv.valueID
814
898
  JOIN fields f ON id.fieldID = f.fieldID
815
899
  WHERE f.fieldName = ? AND iv.value LIKE ?
900
+ ORDER BY i.dateModified DESC
816
901
  `, [fieldName, `%${identifier}%`]);
817
- if (result) {
818
- return this.getItemDetails(result.itemID);
902
+ if (allMatches.length > 0) {
903
+ const result = this.getItemDetails(allMatches[0].itemID);
904
+ if (allMatches.length > 1) {
905
+ result._duplicateWarning = {
906
+ message: `Found ${allMatches.length} items with same ${fieldName}`,
907
+ duplicateItemIDs: allMatches.map((m) => m.itemID),
908
+ usingItemID: allMatches[0].itemID
909
+ };
910
+ }
911
+ return result;
819
912
  }
820
913
  }
821
914
  // Try all identifier types
@@ -1205,8 +1298,17 @@ export class ZoteroDatabase {
1205
1298
  // ============================================
1206
1299
  /**
1207
1300
  * Find duplicate items based on title, DOI, or ISBN
1301
+ *
1302
+ * Note: itemTypeID values vary by Zotero version:
1303
+ * - attachment = 3
1304
+ * - note = 28 (in newer versions) or 1 (in older versions)
1305
+ * We exclude both attachments and notes from duplicate detection
1208
1306
  */
1209
1307
  findDuplicates(field = 'title', libraryID = 1) {
1308
+ // Get note and attachment type IDs dynamically
1309
+ const noteType = this.queryOne("SELECT itemTypeID FROM itemTypes WHERE typeName = 'note'");
1310
+ const attachType = this.queryOne("SELECT itemTypeID FROM itemTypes WHERE typeName = 'attachment'");
1311
+ const excludeTypes = [noteType?.itemTypeID || 28, attachType?.itemTypeID || 3].join(',');
1210
1312
  if (field === 'title') {
1211
1313
  // Find items with the same title
1212
1314
  return this.queryAll(`
@@ -1220,7 +1322,8 @@ export class ZoteroDatabase {
1220
1322
  JOIN fields f ON id.fieldID = f.fieldID
1221
1323
  WHERE f.fieldName = 'title'
1222
1324
  AND i.libraryID = ?
1223
- AND i.itemTypeID NOT IN (1, 14) -- Exclude notes and attachments
1325
+ AND i.itemTypeID NOT IN (${excludeTypes})
1326
+ AND i.itemID NOT IN (SELECT itemID FROM deletedItems)
1224
1327
  GROUP BY iv.value
1225
1328
  HAVING COUNT(*) > 1
1226
1329
  ORDER BY count DESC
@@ -1239,6 +1342,7 @@ export class ZoteroDatabase {
1239
1342
  WHERE f.fieldName = 'DOI'
1240
1343
  AND i.libraryID = ?
1241
1344
  AND iv.value != ''
1345
+ AND i.itemID NOT IN (SELECT itemID FROM deletedItems)
1242
1346
  GROUP BY LOWER(iv.value)
1243
1347
  HAVING COUNT(*) > 1
1244
1348
  ORDER BY count DESC
@@ -1257,6 +1361,7 @@ export class ZoteroDatabase {
1257
1361
  WHERE f.fieldName = 'ISBN'
1258
1362
  AND i.libraryID = ?
1259
1363
  AND iv.value != ''
1364
+ AND i.itemID NOT IN (SELECT itemID FROM deletedItems)
1260
1365
  GROUP BY REPLACE(REPLACE(iv.value, '-', ''), ' ', '')
1261
1366
  HAVING COUNT(*) > 1
1262
1367
  ORDER BY count DESC
@@ -1450,51 +1555,101 @@ export class ZoteroDatabase {
1450
1555
  }
1451
1556
  /**
1452
1557
  * Merge items by transferring notes and tags from source items to target
1558
+ * Uses transaction to ensure data consistency
1453
1559
  */
1454
1560
  mergeItems(targetItemID, sourceItemIDs) {
1455
1561
  const errors = [];
1456
1562
  let notesTransferred = 0;
1457
1563
  let tagsTransferred = 0;
1458
- // Verify target exists
1564
+ let attachmentsTransferred = 0;
1565
+ // Verify target exists and is not deleted
1459
1566
  const target = this.getItemDetails(targetItemID);
1460
1567
  if (!target) {
1461
1568
  return {
1462
1569
  success: false,
1463
- transferred: { notes: 0, tags: 0 },
1570
+ transferred: { notes: 0, tags: 0, attachments: 0 },
1464
1571
  errors: ['Target item not found']
1465
1572
  };
1466
1573
  }
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++;
1574
+ // Check if target is in deletedItems
1575
+ const targetDeleted = this.queryOne('SELECT 1 FROM deletedItems WHERE itemID = ?', [targetItemID]);
1576
+ if (targetDeleted) {
1577
+ return {
1578
+ success: false,
1579
+ transferred: { notes: 0, tags: 0, attachments: 0 },
1580
+ errors: ['Target item is in trash']
1581
+ };
1582
+ }
1583
+ // Use transaction for atomic operation
1584
+ this.beginTransaction();
1585
+ try {
1586
+ for (const sourceID of sourceItemIDs) {
1587
+ if (sourceID === targetItemID)
1588
+ continue;
1589
+ // Verify source exists
1590
+ const source = this.queryOne('SELECT itemID FROM items WHERE itemID = ?', [sourceID]);
1591
+ if (!source) {
1592
+ errors.push(`Source item ${sourceID} not found`);
1593
+ continue;
1476
1594
  }
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++;
1595
+ // Transfer notes
1596
+ try {
1597
+ const notes = this.getItemNotes(sourceID);
1598
+ for (const note of notes) {
1599
+ this.addItemNote(targetItemID, note.note, `[Merged] ${note.title || ''}`);
1600
+ notesTransferred++;
1601
+ }
1602
+ }
1603
+ catch (error) {
1604
+ errors.push(`Failed to transfer notes from ${sourceID}: ${error}`);
1605
+ }
1606
+ // Transfer tags
1607
+ try {
1608
+ const tags = this.getItemTags(sourceID);
1609
+ for (const tag of tags) {
1610
+ this.addTagToItem(targetItemID, tag.name, tag.type);
1611
+ tagsTransferred++;
1612
+ }
1613
+ }
1614
+ catch (error) {
1615
+ errors.push(`Failed to transfer tags from ${sourceID}: ${error}`);
1616
+ }
1617
+ // Transfer attachments (update parentItemID)
1618
+ try {
1619
+ const attachments = this.queryAll(`
1620
+ SELECT itemID FROM itemAttachments WHERE parentItemID = ?
1621
+ `, [sourceID]);
1622
+ for (const att of attachments) {
1623
+ this.execute(`
1624
+ UPDATE itemAttachments SET parentItemID = ? WHERE itemID = ?
1625
+ `, [targetItemID, att.itemID]);
1626
+ attachmentsTransferred++;
1627
+ }
1628
+ }
1629
+ catch (error) {
1630
+ errors.push(`Failed to transfer attachments from ${sourceID}: ${error}`);
1487
1631
  }
1488
1632
  }
1489
- catch (error) {
1490
- errors.push(`Failed to transfer tags from ${sourceID}: ${error}`);
1633
+ this.commitTransaction();
1634
+ if (!this.readonly) {
1635
+ this.save();
1491
1636
  }
1492
1637
  }
1638
+ catch (error) {
1639
+ this.rollbackTransaction();
1640
+ errors.push(`Transaction failed: ${error}`);
1641
+ return {
1642
+ success: false,
1643
+ transferred: { notes: 0, tags: 0, attachments: 0 },
1644
+ errors
1645
+ };
1646
+ }
1493
1647
  return {
1494
1648
  success: errors.length === 0,
1495
1649
  transferred: {
1496
1650
  notes: notesTransferred,
1497
- tags: tagsTransferred
1651
+ tags: tagsTransferred,
1652
+ attachments: attachmentsTransferred
1498
1653
  },
1499
1654
  errors
1500
1655
  };