@ncukondo/reference-manager 0.7.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +71 -4
  2. package/dist/chunks/{action-menu-CTtINmWd.js → action-menu-BN2HHFPR.js} +2 -2
  3. package/dist/chunks/{action-menu-CTtINmWd.js.map → action-menu-BN2HHFPR.js.map} +1 -1
  4. package/dist/chunks/{file-watcher-D7oyc-9z.js → file-watcher-DdhXSm1l.js} +3 -3
  5. package/dist/chunks/file-watcher-DdhXSm1l.js.map +1 -0
  6. package/dist/chunks/{index-_7NEUoS7.js → index-DmMZCOno.js} +316 -41
  7. package/dist/chunks/index-DmMZCOno.js.map +1 -0
  8. package/dist/chunks/{loader-BItrdVWG.js → loader-C-bdImuO.js} +44 -7
  9. package/dist/chunks/loader-C-bdImuO.js.map +1 -0
  10. package/dist/cli/commands/add.d.ts +4 -0
  11. package/dist/cli/commands/add.d.ts.map +1 -1
  12. package/dist/cli/commands/fulltext.d.ts +20 -3
  13. package/dist/cli/commands/fulltext.d.ts.map +1 -1
  14. package/dist/cli/commands/remove.d.ts +7 -16
  15. package/dist/cli/commands/remove.d.ts.map +1 -1
  16. package/dist/cli/commands/update.d.ts +40 -0
  17. package/dist/cli/commands/update.d.ts.map +1 -1
  18. package/dist/cli/index.d.ts.map +1 -1
  19. package/dist/cli.js +582 -152
  20. package/dist/cli.js.map +1 -1
  21. package/dist/core/library.d.ts.map +1 -1
  22. package/dist/features/duplicate/types.d.ts +1 -1
  23. package/dist/features/duplicate/types.d.ts.map +1 -1
  24. package/dist/features/import/detector.d.ts +1 -1
  25. package/dist/features/import/detector.d.ts.map +1 -1
  26. package/dist/features/import/fetcher.d.ts +6 -0
  27. package/dist/features/import/fetcher.d.ts.map +1 -1
  28. package/dist/features/import/importer.d.ts +3 -1
  29. package/dist/features/import/importer.d.ts.map +1 -1
  30. package/dist/features/import/parser.d.ts +16 -0
  31. package/dist/features/import/parser.d.ts.map +1 -1
  32. package/dist/features/operations/add.d.ts +4 -0
  33. package/dist/features/operations/add.d.ts.map +1 -1
  34. package/dist/features/operations/fulltext/index.d.ts +1 -0
  35. package/dist/features/operations/fulltext/index.d.ts.map +1 -1
  36. package/dist/features/operations/fulltext/open.d.ts +36 -0
  37. package/dist/features/operations/fulltext/open.d.ts.map +1 -0
  38. package/dist/features/operations/index.d.ts +2 -0
  39. package/dist/features/operations/index.d.ts.map +1 -1
  40. package/dist/features/operations/json-output.d.ts +93 -0
  41. package/dist/features/operations/json-output.d.ts.map +1 -0
  42. package/dist/features/operations/remove.d.ts +11 -0
  43. package/dist/features/operations/remove.d.ts.map +1 -1
  44. package/dist/index.js +5 -3
  45. package/dist/index.js.map +1 -1
  46. package/dist/server.js +2 -2
  47. package/dist/utils/index.d.ts +1 -0
  48. package/dist/utils/index.d.ts.map +1 -1
  49. package/dist/utils/opener.d.ts +13 -0
  50. package/dist/utils/opener.d.ts.map +1 -0
  51. package/package.json +1 -1
  52. package/dist/chunks/file-watcher-D7oyc-9z.js.map +0 -1
  53. package/dist/chunks/index-_7NEUoS7.js.map +0 -1
  54. package/dist/chunks/loader-BItrdVWG.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { Hono } from "hono";
2
- import { e as CslItemSchema, d as detectDuplicate, h as generateId, q as sortOrderSchema, u as sortFieldSchema, p as pickDefined, t as tokenize, s as search$1, b as sortResults, v as searchSortFieldSchema, L as Library, F as FileWatcher } from "./file-watcher-D7oyc-9z.js";
2
+ import { e as CslItemSchema, d as detectDuplicate, h as generateId, q as sortOrderSchema, u as sortFieldSchema, p as pickDefined, t as tokenize, s as search$1, b as sortResults, v as searchSortFieldSchema, L as Library, F as FileWatcher } from "./file-watcher-DdhXSm1l.js";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { Cite } from "@citation-js/core";
5
5
  import "@citation-js/plugin-doi";
@@ -7,8 +7,9 @@ import "@citation-js/plugin-isbn";
7
7
  import "@citation-js/plugin-bibtex";
8
8
  import "@citation-js/plugin-ris";
9
9
  import { z } from "zod";
10
- import "node:path";
10
+ import { join } from "node:path";
11
11
  import "@citation-js/plugin-csl";
12
+ import { unlink } from "node:fs/promises";
12
13
  async function updateReference(library, options) {
13
14
  const { identifier, idType = "id", updates, onIdCollision = "fail" } = options;
14
15
  const updateResult = await library.update(identifier, updates, { idType, onIdCollision });
@@ -120,6 +121,61 @@ const BUILTIN_STYLES = ["apa", "vancouver", "harvard"];
120
121
  function isBuiltinStyle(styleName) {
121
122
  return BUILTIN_STYLES.includes(styleName);
122
123
  }
124
+ function getFulltextAttachmentTypes(item) {
125
+ const types = [];
126
+ const fulltext = item.custom?.fulltext;
127
+ if (fulltext?.pdf) {
128
+ types.push("pdf");
129
+ }
130
+ if (fulltext?.markdown) {
131
+ types.push("markdown");
132
+ }
133
+ return types;
134
+ }
135
+ async function deleteFulltextFiles(item, fulltextDirectory) {
136
+ const fulltext = item.custom?.fulltext;
137
+ if (!fulltext) {
138
+ return;
139
+ }
140
+ const filesToDelete = [];
141
+ if (fulltext.pdf) {
142
+ filesToDelete.push(join(fulltextDirectory, fulltext.pdf));
143
+ }
144
+ if (fulltext.markdown) {
145
+ filesToDelete.push(join(fulltextDirectory, fulltext.markdown));
146
+ }
147
+ for (const filePath of filesToDelete) {
148
+ try {
149
+ await unlink(filePath);
150
+ } catch {
151
+ }
152
+ }
153
+ }
154
+ async function removeReference(library, options) {
155
+ const { identifier, idType = "id", fulltextDirectory, deleteFulltext = false } = options;
156
+ const result = await library.remove(identifier, { idType });
157
+ if (!result.removed || !result.removedItem) {
158
+ return { removed: false };
159
+ }
160
+ let deletedFulltextTypes;
161
+ if (deleteFulltext && fulltextDirectory) {
162
+ deletedFulltextTypes = getFulltextAttachmentTypes(result.removedItem);
163
+ if (deletedFulltextTypes.length > 0) {
164
+ await deleteFulltextFiles(result.removedItem, fulltextDirectory);
165
+ }
166
+ }
167
+ await library.save();
168
+ return {
169
+ removed: true,
170
+ removedItem: result.removedItem,
171
+ ...deletedFulltextTypes && deletedFulltextTypes.length > 0 && { deletedFulltextTypes }
172
+ };
173
+ }
174
+ const remove = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
175
+ __proto__: null,
176
+ getFulltextAttachmentTypes,
177
+ removeReference
178
+ }, Symbol.toStringTag, { value: "Module" }));
123
179
  const DEFAULT_TTL_MS = 60 * 60 * 1e3;
124
180
  const pmidCache = /* @__PURE__ */ new Map();
125
181
  const doiCache = /* @__PURE__ */ new Map();
@@ -208,7 +264,8 @@ function normalizeIsbn(isbn) {
208
264
  const EXTENSION_MAP = {
209
265
  ".json": "json",
210
266
  ".bib": "bibtex",
211
- ".ris": "ris"
267
+ ".ris": "ris",
268
+ ".nbib": "nbib"
212
269
  };
213
270
  const DOI_URL_PREFIXES = [
214
271
  "https://doi.org/",
@@ -237,6 +294,9 @@ function detectByContent(content) {
237
294
  if (trimmed.startsWith("TY -")) {
238
295
  return "ris";
239
296
  }
297
+ if (trimmed.startsWith("PMID-")) {
298
+ return "nbib";
299
+ }
240
300
  return detectIdentifier(trimmed);
241
301
  }
242
302
  function detectIdentifier(input) {
@@ -440,13 +500,15 @@ function buildPmidResult(pmid, foundItems, validationErrors) {
440
500
  return {
441
501
  pmid,
442
502
  success: false,
443
- error: `Invalid CSL-JSON data: ${validationError}`
503
+ error: `Invalid CSL-JSON data: ${validationError}`,
504
+ reason: "validation_error"
444
505
  };
445
506
  }
446
507
  return {
447
508
  pmid,
448
509
  success: false,
449
- error: `PMID ${pmid} not found`
510
+ error: `PMID ${pmid} not found`,
511
+ reason: "not_found"
450
512
  };
451
513
  }
452
514
  async function fetchPmids(pmids, config) {
@@ -467,7 +529,8 @@ async function fetchPmids(pmids, config) {
467
529
  return pmids.map((pmid) => ({
468
530
  pmid,
469
531
  success: false,
470
- error: errorMsg
532
+ error: errorMsg,
533
+ reason: "fetch_error"
471
534
  }));
472
535
  }
473
536
  const data = await response.json();
@@ -479,7 +542,8 @@ async function fetchPmids(pmids, config) {
479
542
  return pmids.map((pmid) => ({
480
543
  pmid,
481
544
  success: false,
482
- error: errorMsg
545
+ error: errorMsg,
546
+ reason: "fetch_error"
483
547
  }));
484
548
  }
485
549
  }
@@ -487,7 +551,8 @@ async function fetchDoi(doi) {
487
551
  if (!DOI_PATTERN.test(doi)) {
488
552
  return {
489
553
  success: false,
490
- error: `Invalid DOI format: ${doi}`
554
+ error: `Invalid DOI format: ${doi}`,
555
+ reason: "validation_error"
491
556
  };
492
557
  }
493
558
  const rateLimiter = getRateLimiter("crossref", {});
@@ -498,14 +563,16 @@ async function fetchDoi(doi) {
498
563
  if (!rawItems || !Array.isArray(rawItems) || rawItems.length === 0) {
499
564
  return {
500
565
  success: false,
501
- error: `No data returned for DOI ${doi}`
566
+ error: `No data returned for DOI ${doi}`,
567
+ reason: "not_found"
502
568
  };
503
569
  }
504
570
  const parseResult = CslItemSchema.safeParse(rawItems[0]);
505
571
  if (!parseResult.success) {
506
572
  return {
507
573
  success: false,
508
- error: `Invalid CSL-JSON data for DOI ${doi}: ${parseResult.error.message}`
574
+ error: `Invalid CSL-JSON data for DOI ${doi}: ${parseResult.error.message}`,
575
+ reason: "validation_error"
509
576
  };
510
577
  }
511
578
  return { success: true, item: parseResult.data };
@@ -513,7 +580,8 @@ async function fetchDoi(doi) {
513
580
  const errorMsg = error instanceof Error ? error.message : String(error);
514
581
  return {
515
582
  success: false,
516
- error: errorMsg
583
+ error: errorMsg,
584
+ reason: "fetch_error"
517
585
  };
518
586
  }
519
587
  }
@@ -523,7 +591,8 @@ async function fetchIsbn(isbn) {
523
591
  if (!ISBN10_PATTERN.test(isbn) && !ISBN13_PATTERN.test(isbn)) {
524
592
  return {
525
593
  success: false,
526
- error: `Invalid ISBN format: ${isbn}`
594
+ error: `Invalid ISBN format: ${isbn}`,
595
+ reason: "validation_error"
527
596
  };
528
597
  }
529
598
  const rateLimiter = getRateLimiter("isbn", {});
@@ -534,14 +603,16 @@ async function fetchIsbn(isbn) {
534
603
  if (!rawItems || !Array.isArray(rawItems) || rawItems.length === 0) {
535
604
  return {
536
605
  success: false,
537
- error: `No data returned for ISBN ${isbn}`
606
+ error: `No data returned for ISBN ${isbn}`,
607
+ reason: "not_found"
538
608
  };
539
609
  }
540
610
  const parseResult = CslItemSchema.safeParse(rawItems[0]);
541
611
  if (!parseResult.success) {
542
612
  return {
543
613
  success: false,
544
- error: `Invalid CSL-JSON data for ISBN ${isbn}: ${parseResult.error.message}`
614
+ error: `Invalid CSL-JSON data for ISBN ${isbn}: ${parseResult.error.message}`,
615
+ reason: "validation_error"
545
616
  };
546
617
  }
547
618
  return { success: true, item: parseResult.data };
@@ -549,7 +620,8 @@ async function fetchIsbn(isbn) {
549
620
  const errorMsg = error instanceof Error ? error.message : String(error);
550
621
  return {
551
622
  success: false,
552
- error: errorMsg
623
+ error: errorMsg,
624
+ reason: "fetch_error"
553
625
  };
554
626
  }
555
627
  }
@@ -559,6 +631,145 @@ function parseBibtex(content) {
559
631
  function parseRis(content) {
560
632
  return parseWithCitationJs(content, "ris");
561
633
  }
634
+ const NBIB_TO_RIS_TAG_MAP = {
635
+ PMID: "AN",
636
+ // PubMed ID -> Accession Number
637
+ TI: "TI",
638
+ // Title -> Title
639
+ FAU: "AU",
640
+ // Full Author -> Author
641
+ AU: "AU",
642
+ // Author -> Author (short form, use FAU when available)
643
+ JT: "JO",
644
+ // Journal Title -> Journal Name
645
+ TA: "JA",
646
+ // Title Abbreviation -> Journal Abbreviation
647
+ AB: "AB",
648
+ // Abstract -> Abstract
649
+ VI: "VL",
650
+ // Volume -> Volume
651
+ IP: "IS",
652
+ // Issue/Part -> Issue Number
653
+ PG: "SP",
654
+ // Pagination -> Start Page (includes range)
655
+ DP: "PY",
656
+ // Date of Publication -> Publication Year
657
+ LA: "LA",
658
+ // Language -> Language
659
+ MH: "KW",
660
+ // MeSH Headings -> Keywords
661
+ OT: "KW",
662
+ // Other Terms -> Keywords
663
+ AD: "AD",
664
+ // Affiliation/Address -> Author Address
665
+ IS: "SN",
666
+ // ISSN -> Serial Number
667
+ PT: "TY"
668
+ // Publication Type -> Type of Reference
669
+ };
670
+ const NBIB_PUBLICATION_TYPE_MAP = {
671
+ "Journal Article": "JOUR",
672
+ Review: "JOUR",
673
+ Book: "BOOK",
674
+ "Book Chapter": "CHAP",
675
+ "Conference Paper": "CPAPER",
676
+ Thesis: "THES",
677
+ Report: "RPRT"
678
+ };
679
+ function parseNbibEntry(entry) {
680
+ const result = [];
681
+ const lines = entry.split("\n");
682
+ let currentTag = "";
683
+ let currentValue = "";
684
+ for (const line of lines) {
685
+ const tagMatch = line.match(/^([A-Z]{2,4})\s*-\s*(.*)$/);
686
+ if (tagMatch) {
687
+ if (currentTag) {
688
+ result.push({ tag: currentTag, value: currentValue.trim() });
689
+ }
690
+ currentTag = tagMatch[1] ?? "";
691
+ currentValue = tagMatch[2] ?? "";
692
+ } else if (currentTag && line.match(/^\s+/)) {
693
+ currentValue += ` ${line.trim()}`;
694
+ }
695
+ }
696
+ if (currentTag) {
697
+ result.push({ tag: currentTag, value: currentValue.trim() });
698
+ }
699
+ return result;
700
+ }
701
+ function convertNbibTagToRisLine(tag, value) {
702
+ if (tag === "AID" && value.includes("[doi]")) {
703
+ const doi = value.replace(/\s*\[doi\].*$/, "").trim();
704
+ return { line: `DO - ${doi}` };
705
+ }
706
+ if (tag === "AID" && value.includes("[pii]")) {
707
+ const pii = value.replace(/\s*\[pii\].*$/, "").trim();
708
+ return { line: `C1 - ${pii}` };
709
+ }
710
+ const risTag = NBIB_TO_RIS_TAG_MAP[tag];
711
+ if (!risTag) {
712
+ return null;
713
+ }
714
+ if (risTag === "PY") {
715
+ const yearMatch = value.match(/^(\d{4})/);
716
+ return yearMatch ? { line: `PY - ${yearMatch[1]}` } : null;
717
+ }
718
+ if (risTag === "TY") {
719
+ const risType = NBIB_PUBLICATION_TYPE_MAP[value] || "JOUR";
720
+ return { line: `TY - ${risType}`, isType: true };
721
+ }
722
+ return { line: `${risTag} - ${value}` };
723
+ }
724
+ function convertSingleNbibEntryToRis(entry) {
725
+ const parsed = parseNbibEntry(entry);
726
+ if (parsed.length === 0) {
727
+ return "";
728
+ }
729
+ const risLines = [];
730
+ let hasType = false;
731
+ for (const { tag, value } of parsed) {
732
+ const converted = convertNbibTagToRisLine(tag, value);
733
+ if (!converted) {
734
+ continue;
735
+ }
736
+ if (converted.isType) {
737
+ risLines.unshift(converted.line);
738
+ hasType = true;
739
+ } else {
740
+ risLines.push(converted.line);
741
+ }
742
+ }
743
+ if (!hasType) {
744
+ risLines.unshift("TY - JOUR");
745
+ }
746
+ risLines.push("ER -");
747
+ return risLines.join("\n");
748
+ }
749
+ function convertNbibToRis(content) {
750
+ const trimmed = content.trim();
751
+ if (!trimmed) {
752
+ return "";
753
+ }
754
+ const entries = trimmed.split(/\n\s*\n/).filter((e) => e.trim());
755
+ const risEntries = entries.map((entry) => convertSingleNbibEntryToRis(entry)).filter(Boolean);
756
+ return risEntries.join("\n\n");
757
+ }
758
+ function parseNbib(content) {
759
+ const trimmed = content.trim();
760
+ if (!trimmed) {
761
+ return { success: true, items: [] };
762
+ }
763
+ const risContent = convertNbibToRis(trimmed);
764
+ if (!risContent) {
765
+ return {
766
+ success: false,
767
+ items: [],
768
+ error: "Failed to convert NBIB to RIS: No valid entries found"
769
+ };
770
+ }
771
+ return parseRis(risContent);
772
+ }
562
773
  function parseWithCitationJs(content, format) {
563
774
  const trimmed = content.trim();
564
775
  if (!trimmed) {
@@ -622,7 +833,8 @@ function buildUnknownResults(unknowns) {
622
833
  return unknowns.map((unknown) => ({
623
834
  success: false,
624
835
  error: `Cannot interpret '${unknown}' as identifier (not a valid PMID or DOI)`,
625
- source: unknown
836
+ source: unknown,
837
+ reason: "validation_error"
626
838
  }));
627
839
  }
628
840
  async function fetchPmidsWithCache(pmids, pubmedConfig) {
@@ -650,7 +862,8 @@ async function fetchPmidsWithCache(pmids, pubmedConfig) {
650
862
  results.push({
651
863
  success: false,
652
864
  error: fetchResult.error,
653
- source: fetchResult.pmid
865
+ source: fetchResult.pmid,
866
+ reason: fetchResult.reason
654
867
  });
655
868
  }
656
869
  }
@@ -670,7 +883,12 @@ async function fetchDoisWithCache(dois) {
670
883
  cacheDoiResult(doi, fetchResult.item);
671
884
  results.push({ success: true, item: fetchResult.item, source: doi });
672
885
  } else {
673
- results.push({ success: false, error: fetchResult.error, source: doi });
886
+ results.push({
887
+ success: false,
888
+ error: fetchResult.error,
889
+ source: doi,
890
+ reason: fetchResult.reason
891
+ });
674
892
  }
675
893
  }
676
894
  return results;
@@ -688,7 +906,12 @@ async function fetchIsbnsWithCache(isbns) {
688
906
  cacheIsbnResult(isbn, fetchResult.item);
689
907
  results.push({ success: true, item: fetchResult.item, source: isbn });
690
908
  } else {
691
- results.push({ success: false, error: fetchResult.error, source: isbn });
909
+ results.push({
910
+ success: false,
911
+ error: fetchResult.error,
912
+ source: isbn,
913
+ reason: fetchResult.reason
914
+ });
692
915
  }
693
916
  }
694
917
  return results;
@@ -709,7 +932,8 @@ function parseJsonContent(content) {
709
932
  results.push({
710
933
  success: false,
711
934
  error: `Invalid CSL-JSON: ${parseResult.error.message}`,
712
- source: "json"
935
+ source: "json",
936
+ reason: "validation_error"
713
937
  });
714
938
  }
715
939
  }
@@ -717,7 +941,14 @@ function parseJsonContent(content) {
717
941
  } catch (error) {
718
942
  const message = error instanceof Error ? error.message : String(error);
719
943
  return {
720
- results: [{ success: false, error: `Failed to parse JSON: ${message}`, source: "json" }]
944
+ results: [
945
+ {
946
+ success: false,
947
+ error: `Failed to parse JSON: ${message}`,
948
+ source: "json",
949
+ reason: "parse_error"
950
+ }
951
+ ]
721
952
  };
722
953
  }
723
954
  }
@@ -726,7 +957,12 @@ function parseBibtexContent(content) {
726
957
  if (!parseResult.success) {
727
958
  return {
728
959
  results: [
729
- { success: false, error: parseResult.error ?? "Failed to parse BibTeX", source: "bibtex" }
960
+ {
961
+ success: false,
962
+ error: parseResult.error ?? "Failed to parse BibTeX",
963
+ source: "bibtex",
964
+ reason: "parse_error"
965
+ }
730
966
  ]
731
967
  };
732
968
  }
@@ -746,7 +982,12 @@ function parseRisContent(content) {
746
982
  if (!parseResult.success) {
747
983
  return {
748
984
  results: [
749
- { success: false, error: parseResult.error ?? "Failed to parse RIS", source: "ris" }
985
+ {
986
+ success: false,
987
+ error: parseResult.error ?? "Failed to parse RIS",
988
+ source: "ris",
989
+ reason: "parse_error"
990
+ }
750
991
  ]
751
992
  };
752
993
  }
@@ -761,6 +1002,31 @@ function parseRisContent(content) {
761
1002
  }))
762
1003
  };
763
1004
  }
1005
+ function parseNbibContent(content) {
1006
+ const parseResult = parseNbib(content);
1007
+ if (!parseResult.success) {
1008
+ return {
1009
+ results: [
1010
+ {
1011
+ success: false,
1012
+ error: parseResult.error ?? "Failed to parse NBIB",
1013
+ source: "nbib",
1014
+ reason: "parse_error"
1015
+ }
1016
+ ]
1017
+ };
1018
+ }
1019
+ if (parseResult.items.length === 0) {
1020
+ return { results: [] };
1021
+ }
1022
+ return {
1023
+ results: parseResult.items.map((item) => ({
1024
+ success: true,
1025
+ item,
1026
+ source: "nbib"
1027
+ }))
1028
+ };
1029
+ }
764
1030
  async function importFromContent(content, format, _options) {
765
1031
  let actualFormat;
766
1032
  if (format === "auto") {
@@ -771,7 +1037,8 @@ async function importFromContent(content, format, _options) {
771
1037
  {
772
1038
  success: false,
773
1039
  error: "Cannot detect input format. Use --format to specify explicitly.",
774
- source: "content"
1040
+ source: "content",
1041
+ reason: "validation_error"
775
1042
  }
776
1043
  ]
777
1044
  };
@@ -786,13 +1053,16 @@ async function importFromContent(content, format, _options) {
786
1053
  return parseBibtexContent(content);
787
1054
  case "ris":
788
1055
  return parseRisContent(content);
1056
+ case "nbib":
1057
+ return parseNbibContent(content);
789
1058
  default:
790
1059
  return {
791
1060
  results: [
792
1061
  {
793
1062
  success: false,
794
1063
  error: `Unsupported format for content parsing: ${actualFormat}`,
795
- source: "content"
1064
+ source: "content",
1065
+ reason: "validation_error"
796
1066
  }
797
1067
  ]
798
1068
  };
@@ -839,7 +1109,8 @@ async function processFile(filePath, options) {
839
1109
  {
840
1110
  success: false,
841
1111
  error: `Failed to read file: ${message}`,
842
- source: filePath
1112
+ source: filePath,
1113
+ reason: "fetch_error"
843
1114
  }
844
1115
  ];
845
1116
  }
@@ -858,7 +1129,8 @@ async function processIdentifiers(inputs, options) {
858
1129
  results.push({
859
1130
  success: false,
860
1131
  error: `Cannot interpret '${input}' as identifier (not a valid PMID, DOI, or ISBN).${hint}`,
861
- source: input
1132
+ source: input,
1133
+ reason: "validation_error"
862
1134
  });
863
1135
  }
864
1136
  }
@@ -893,7 +1165,7 @@ async function importFromInputs(inputs, options) {
893
1165
  }
894
1166
  async function processStdinContent(content, options) {
895
1167
  const format = options.format || "auto";
896
- if (format === "json" || format === "bibtex" || format === "ris") {
1168
+ if (format === "json" || format === "bibtex" || format === "ris" || format === "nbib") {
897
1169
  const result = await importFromContent(content, format);
898
1170
  return result.results.map((r) => ({
899
1171
  ...r,
@@ -905,7 +1177,7 @@ async function processStdinContent(content, options) {
905
1177
  return processIdentifiers(identifiers2, options);
906
1178
  }
907
1179
  const detectedFormat = detectByContent(content);
908
- if (detectedFormat === "json" || detectedFormat === "bibtex" || detectedFormat === "ris") {
1180
+ if (detectedFormat === "json" || detectedFormat === "bibtex" || detectedFormat === "ris" || detectedFormat === "nbib") {
909
1181
  const result = await importFromContent(content, detectedFormat);
910
1182
  return result.results.map((r) => ({
911
1183
  ...r,
@@ -962,7 +1234,10 @@ function buildImportOptions(options) {
962
1234
  }
963
1235
  async function processImportResult(result, existingItems, addedIds, force, library) {
964
1236
  if (!result.success) {
965
- return { type: "failed", item: { source: result.source, error: result.error } };
1237
+ return {
1238
+ type: "failed",
1239
+ item: { source: result.source, error: result.error, reason: result.reason }
1240
+ };
966
1241
  }
967
1242
  const item = result.item;
968
1243
  if (!force) {
@@ -971,7 +1246,11 @@ async function processImportResult(result, existingItems, addedIds, force, libra
971
1246
  if (existingMatch) {
972
1247
  return {
973
1248
  type: "skipped",
974
- item: { source: result.source, existingId: existingMatch.existing.id ?? "" }
1249
+ item: {
1250
+ source: result.source,
1251
+ existingId: existingMatch.existing.id ?? "",
1252
+ duplicateType: existingMatch.type
1253
+ }
975
1254
  };
976
1255
  }
977
1256
  }
@@ -979,10 +1258,12 @@ async function processImportResult(result, existingItems, addedIds, force, libra
979
1258
  const generatedId = generateId(item);
980
1259
  const { id, changed } = resolveIdCollision(generatedId, allExistingIds);
981
1260
  const finalItem = { ...item, id };
982
- await library.add(finalItem);
1261
+ const addedToLibrary = await library.add(finalItem);
983
1262
  addedIds.add(id);
1263
+ const uuid = addedToLibrary.custom?.uuid ?? "";
984
1264
  const addedItem = {
985
1265
  id,
1266
+ uuid,
986
1267
  title: typeof finalItem.title === "string" ? finalItem.title : ""
987
1268
  };
988
1269
  if (changed) {
@@ -1526,14 +1807,6 @@ function createListRoute(library) {
1526
1807
  });
1527
1808
  return route;
1528
1809
  }
1529
- async function removeReference(library, options) {
1530
- const { identifier, idType = "id" } = options;
1531
- const result = await library.remove(identifier, { idType });
1532
- if (result.removed) {
1533
- await library.save();
1534
- }
1535
- return result;
1536
- }
1537
1810
  function createReferencesRoute(library) {
1538
1811
  const route = new Hono();
1539
1812
  route.get("/", async (c) => {
@@ -1785,8 +2058,10 @@ export {
1785
2058
  cite as d,
1786
2059
  search as e,
1787
2060
  formatBibliographyCSL as f,
2061
+ getFulltextAttachmentTypes as g,
1788
2062
  list as l,
2063
+ remove as r,
1789
2064
  startServerWithFileWatcher as s,
1790
2065
  updateReference as u
1791
2066
  };
1792
- //# sourceMappingURL=index-_7NEUoS7.js.map
2067
+ //# sourceMappingURL=index-DmMZCOno.js.map