@ncukondo/reference-manager 0.5.3 → 0.7.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 (87) hide show
  1. package/README.md +93 -0
  2. package/dist/chunks/action-menu-CTtINmWd.js +118 -0
  3. package/dist/chunks/action-menu-CTtINmWd.js.map +1 -0
  4. package/dist/chunks/{file-watcher-CBAbblss.js → file-watcher-D7oyc-9z.js} +55 -11
  5. package/dist/chunks/file-watcher-D7oyc-9z.js.map +1 -0
  6. package/dist/chunks/{index-Bl_mOQRe.js → index-_7NEUoS7.js} +271 -136
  7. package/dist/chunks/index-_7NEUoS7.js.map +1 -0
  8. package/dist/chunks/{loader-DuzyKV70.js → loader-BItrdVWG.js} +149 -26
  9. package/dist/chunks/loader-BItrdVWG.js.map +1 -0
  10. package/dist/chunks/search-prompt-D67WyKrY.js +179 -0
  11. package/dist/chunks/search-prompt-D67WyKrY.js.map +1 -0
  12. package/dist/chunks/tty-CDBIQraQ.js +17 -0
  13. package/dist/chunks/tty-CDBIQraQ.js.map +1 -0
  14. package/dist/cli/commands/list.d.ts +7 -1
  15. package/dist/cli/commands/list.d.ts.map +1 -1
  16. package/dist/cli/commands/search.d.ts +26 -1
  17. package/dist/cli/commands/search.d.ts.map +1 -1
  18. package/dist/cli/completion.d.ts +65 -0
  19. package/dist/cli/completion.d.ts.map +1 -0
  20. package/dist/cli/index.d.ts.map +1 -1
  21. package/dist/cli.js +535 -119
  22. package/dist/cli.js.map +1 -1
  23. package/dist/config/defaults.d.ts.map +1 -1
  24. package/dist/config/loader.d.ts.map +1 -1
  25. package/dist/config/schema.d.ts +97 -0
  26. package/dist/config/schema.d.ts.map +1 -1
  27. package/dist/features/import/importer.d.ts.map +1 -1
  28. package/dist/features/interactive/action-menu.d.ts +56 -0
  29. package/dist/features/interactive/action-menu.d.ts.map +1 -0
  30. package/dist/features/interactive/debounce.d.ts +22 -0
  31. package/dist/features/interactive/debounce.d.ts.map +1 -0
  32. package/dist/features/interactive/format.d.ts +52 -0
  33. package/dist/features/interactive/format.d.ts.map +1 -0
  34. package/dist/features/interactive/search-prompt.d.ts +47 -0
  35. package/dist/features/interactive/search-prompt.d.ts.map +1 -0
  36. package/dist/features/interactive/tty.d.ts +20 -0
  37. package/dist/features/interactive/tty.d.ts.map +1 -0
  38. package/dist/features/operations/add.d.ts.map +1 -1
  39. package/dist/features/operations/list.d.ts +12 -3
  40. package/dist/features/operations/list.d.ts.map +1 -1
  41. package/dist/features/operations/search.d.ts +19 -3
  42. package/dist/features/operations/search.d.ts.map +1 -1
  43. package/dist/features/pagination/aliases.d.ts +14 -0
  44. package/dist/features/pagination/aliases.d.ts.map +1 -0
  45. package/dist/features/pagination/index.d.ts +8 -0
  46. package/dist/features/pagination/index.d.ts.map +1 -0
  47. package/dist/features/pagination/paginate.d.ts +20 -0
  48. package/dist/features/pagination/paginate.d.ts.map +1 -0
  49. package/dist/features/pagination/sorter.d.ts +16 -0
  50. package/dist/features/pagination/sorter.d.ts.map +1 -0
  51. package/dist/features/pagination/types.d.ts +74 -0
  52. package/dist/features/pagination/types.d.ts.map +1 -0
  53. package/dist/index.js +13 -12
  54. package/dist/index.js.map +1 -1
  55. package/dist/mcp/context.d.ts +4 -4
  56. package/dist/mcp/context.d.ts.map +1 -1
  57. package/dist/mcp/resources/index.d.ts +3 -3
  58. package/dist/mcp/resources/index.d.ts.map +1 -1
  59. package/dist/mcp/resources/library.d.ts +5 -5
  60. package/dist/mcp/resources/library.d.ts.map +1 -1
  61. package/dist/mcp/tools/add.d.ts +3 -3
  62. package/dist/mcp/tools/add.d.ts.map +1 -1
  63. package/dist/mcp/tools/cite.d.ts +3 -3
  64. package/dist/mcp/tools/cite.d.ts.map +1 -1
  65. package/dist/mcp/tools/fulltext.d.ts +7 -7
  66. package/dist/mcp/tools/fulltext.d.ts.map +1 -1
  67. package/dist/mcp/tools/index.d.ts +3 -3
  68. package/dist/mcp/tools/index.d.ts.map +1 -1
  69. package/dist/mcp/tools/list.d.ts +10 -3
  70. package/dist/mcp/tools/list.d.ts.map +1 -1
  71. package/dist/mcp/tools/remove.d.ts +3 -3
  72. package/dist/mcp/tools/remove.d.ts.map +1 -1
  73. package/dist/mcp/tools/search.d.ts +10 -3
  74. package/dist/mcp/tools/search.d.ts.map +1 -1
  75. package/dist/server/routes/list.d.ts +13 -0
  76. package/dist/server/routes/list.d.ts.map +1 -1
  77. package/dist/server/routes/search.d.ts +14 -0
  78. package/dist/server/routes/search.d.ts.map +1 -1
  79. package/dist/server.js +4 -4
  80. package/dist/utils/index.d.ts +1 -0
  81. package/dist/utils/index.d.ts.map +1 -1
  82. package/dist/utils/object.d.ts +16 -0
  83. package/dist/utils/object.d.ts.map +1 -0
  84. package/package.json +4 -1
  85. package/dist/chunks/file-watcher-CBAbblss.js.map +0 -1
  86. package/dist/chunks/index-Bl_mOQRe.js.map +0 -1
  87. package/dist/chunks/loader-DuzyKV70.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { Hono } from "hono";
2
- import { e as CslItemSchema, d as detectDuplicate, t as tokenize, s as search$1, b as sortResults, L as Library, F as FileWatcher } from "./file-watcher-CBAbblss.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-D7oyc-9z.js";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { Cite } from "@citation-js/core";
5
5
  import "@citation-js/plugin-doi";
@@ -30,6 +30,92 @@ async function updateReference(library, options) {
30
30
  }
31
31
  return result;
32
32
  }
33
+ function getCreatedAt(item) {
34
+ const createdAt = item.custom?.created_at;
35
+ if (!createdAt) return 0;
36
+ return new Date(createdAt).getTime();
37
+ }
38
+ function getUpdatedAt(item) {
39
+ const timestamp = item.custom?.timestamp;
40
+ if (timestamp) return new Date(timestamp).getTime();
41
+ return getCreatedAt(item);
42
+ }
43
+ function getPublishedDate(item) {
44
+ const dateParts = item.issued?.["date-parts"]?.[0];
45
+ if (!dateParts || dateParts.length === 0) return 0;
46
+ const year = dateParts[0] ?? 0;
47
+ const month = dateParts[1] ?? 1;
48
+ const day = dateParts[2] ?? 1;
49
+ return new Date(year, month - 1, day).getTime();
50
+ }
51
+ function getAuthorName(item) {
52
+ const firstAuthor = item.author?.[0];
53
+ if (!firstAuthor) return "Anonymous";
54
+ return firstAuthor.family ?? firstAuthor.literal ?? "Anonymous";
55
+ }
56
+ function getTitle(item) {
57
+ return item.title ?? "";
58
+ }
59
+ function getSortValue(item, field) {
60
+ switch (field) {
61
+ case "created":
62
+ return getCreatedAt(item);
63
+ case "updated":
64
+ return getUpdatedAt(item);
65
+ case "published":
66
+ return getPublishedDate(item);
67
+ case "author":
68
+ return getAuthorName(item).toLowerCase();
69
+ case "title":
70
+ return getTitle(item).toLowerCase();
71
+ }
72
+ }
73
+ function compareValues(a, b, order) {
74
+ const multiplier = order === "desc" ? -1 : 1;
75
+ if (typeof a === "number" && typeof b === "number") {
76
+ return (a - b) * multiplier;
77
+ }
78
+ const strA = String(a);
79
+ const strB = String(b);
80
+ return strA.localeCompare(strB) * multiplier;
81
+ }
82
+ function sortReferences(items, sort, order) {
83
+ return [...items].sort((a, b) => {
84
+ const aValue = getSortValue(a, sort);
85
+ const bValue = getSortValue(b, sort);
86
+ const primaryCompare = compareValues(aValue, bValue, order);
87
+ if (primaryCompare !== 0) return primaryCompare;
88
+ const aCreated = getCreatedAt(a);
89
+ const bCreated = getCreatedAt(b);
90
+ const createdCompare = bCreated - aCreated;
91
+ if (createdCompare !== 0) return createdCompare;
92
+ return a.id.localeCompare(b.id);
93
+ });
94
+ }
95
+ function paginate(items, options) {
96
+ const offset = options.offset ?? 0;
97
+ const limit = options.limit ?? 0;
98
+ if (limit < 0) {
99
+ throw new Error("limit must be non-negative");
100
+ }
101
+ if (offset < 0) {
102
+ throw new Error("offset must be non-negative");
103
+ }
104
+ const isUnlimited = limit === 0;
105
+ const afterOffset = items.slice(offset);
106
+ const paginatedItems = isUnlimited ? afterOffset : afterOffset.slice(0, limit);
107
+ let nextOffset = null;
108
+ if (!isUnlimited && paginatedItems.length > 0) {
109
+ const nextPosition = offset + paginatedItems.length;
110
+ if (nextPosition < items.length) {
111
+ nextOffset = nextPosition;
112
+ }
113
+ }
114
+ return {
115
+ items: paginatedItems,
116
+ nextOffset
117
+ };
118
+ }
33
119
  const BUILTIN_STYLES = ["apa", "vancouver", "harvard"];
34
120
  function isBuiltinStyle(styleName) {
35
121
  return BUILTIN_STYLES.includes(styleName);
@@ -764,13 +850,14 @@ async function processIdentifiers(inputs, options) {
764
850
  for (const input of inputs) {
765
851
  const isValidPmid = isPmid(input);
766
852
  const isValidDoi = isDoi(input);
767
- if (isValidPmid || isValidDoi) {
853
+ const isValidIsbn = isIsbn(input);
854
+ if (isValidPmid || isValidDoi || isValidIsbn) {
768
855
  validIdentifiers.push(input);
769
856
  } else {
770
857
  const hint = looksLikeFilePath(input) ? " Hint: If this is a file path, check that the file exists." : "";
771
858
  results.push({
772
859
  success: false,
773
- error: `Cannot interpret '${input}' as identifier (not a valid PMID or DOI).${hint}`,
860
+ error: `Cannot interpret '${input}' as identifier (not a valid PMID, DOI, or ISBN).${hint}`,
774
861
  source: input
775
862
  });
776
863
  }
@@ -889,7 +976,8 @@ async function processImportResult(result, existingItems, addedIds, force, libra
889
976
  }
890
977
  }
891
978
  const allExistingIds = /* @__PURE__ */ new Set([...existingItems.map((i) => i.id), ...addedIds]);
892
- const { id, changed } = resolveIdCollision(item.id, allExistingIds);
979
+ const generatedId = generateId(item);
980
+ const { id, changed } = resolveIdCollision(generatedId, allExistingIds);
893
981
  const finalItem = { ...item, id };
894
982
  await library.add(finalItem);
895
983
  addedIds.add(id);
@@ -899,7 +987,7 @@ async function processImportResult(result, existingItems, addedIds, force, libra
899
987
  };
900
988
  if (changed) {
901
989
  addedItem.idChanged = true;
902
- addedItem.originalId = item.id;
990
+ addedItem.originalId = generatedId;
903
991
  }
904
992
  return { type: "added", item: addedItem };
905
993
  }
@@ -930,6 +1018,47 @@ const add = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty(
930
1018
  __proto__: null,
931
1019
  addReferences
932
1020
  }, Symbol.toStringTag, { value: "Module" }));
1021
+ function buildPubmedConfig(config) {
1022
+ const pubmedConfig = {};
1023
+ if (config.pubmed.email !== void 0) {
1024
+ pubmedConfig.email = config.pubmed.email;
1025
+ }
1026
+ if (config.pubmed.apiKey !== void 0) {
1027
+ pubmedConfig.apiKey = config.pubmed.apiKey;
1028
+ }
1029
+ return pubmedConfig;
1030
+ }
1031
+ function createAddRoute(library, config) {
1032
+ const route = new Hono();
1033
+ route.post("/", async (c) => {
1034
+ let body;
1035
+ try {
1036
+ body = await c.req.json();
1037
+ } catch {
1038
+ return c.json({ error: "Invalid JSON" }, 400);
1039
+ }
1040
+ if (!body || typeof body !== "object") {
1041
+ return c.json({ error: "Request body must be an object" }, 400);
1042
+ }
1043
+ const { inputs, options } = body;
1044
+ if (!inputs || !Array.isArray(inputs) || inputs.length === 0) {
1045
+ return c.json({ error: "inputs must be a non-empty array of strings" }, 400);
1046
+ }
1047
+ if (!inputs.every((input) => typeof input === "string")) {
1048
+ return c.json({ error: "All inputs must be strings" }, 400);
1049
+ }
1050
+ const addOptions = {
1051
+ force: options?.force ?? false,
1052
+ pubmedConfig: buildPubmedConfig(config)
1053
+ };
1054
+ if (options?.format) {
1055
+ addOptions.format = options.format;
1056
+ }
1057
+ const result = await addReferences(inputs, library, addOptions);
1058
+ return c.json(result);
1059
+ });
1060
+ return route;
1061
+ }
933
1062
  function formatAuthor(author) {
934
1063
  const family = author.family || "";
935
1064
  const givenInitial = author.given ? `${author.given.charAt(0)}.` : "";
@@ -1281,113 +1410,6 @@ const cite = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty
1281
1410
  __proto__: null,
1282
1411
  citeReferences
1283
1412
  }, Symbol.toStringTag, { value: "Module" }));
1284
- async function listReferences(library, options) {
1285
- const format = options.format ?? "pretty";
1286
- const items = await library.getAll();
1287
- switch (format) {
1288
- case "json":
1289
- return { items: items.map((item) => JSON.stringify(item)) };
1290
- case "bibtex":
1291
- return { items: items.map((item) => formatBibtex([item])) };
1292
- case "ids-only":
1293
- return { items: items.map((item) => item.id) };
1294
- case "uuid":
1295
- return {
1296
- items: items.filter(
1297
- (item) => Boolean(item.custom?.uuid)
1298
- ).map((item) => item.custom.uuid)
1299
- };
1300
- default:
1301
- return { items: items.map((item) => formatPretty([item])) };
1302
- }
1303
- }
1304
- const list = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1305
- __proto__: null,
1306
- listReferences
1307
- }, Symbol.toStringTag, { value: "Module" }));
1308
- async function removeReference(library, options) {
1309
- const { identifier, idType = "id" } = options;
1310
- const result = await library.remove(identifier, { idType });
1311
- if (result.removed) {
1312
- await library.save();
1313
- }
1314
- return result;
1315
- }
1316
- async function searchReferences(library, options) {
1317
- const format = options.format ?? "pretty";
1318
- const query = options.query;
1319
- const allItems = await library.getAll();
1320
- let matchedItems;
1321
- if (!query.trim()) {
1322
- matchedItems = allItems;
1323
- } else {
1324
- const tokens = tokenize(query).tokens;
1325
- const results = search$1(allItems, tokens);
1326
- const sorted = sortResults(results);
1327
- matchedItems = sorted.map((result) => result.reference);
1328
- }
1329
- switch (format) {
1330
- case "json":
1331
- return { items: matchedItems.map((item) => JSON.stringify(item)) };
1332
- case "bibtex":
1333
- return { items: matchedItems.map((item) => formatBibtex([item])) };
1334
- case "ids-only":
1335
- return { items: matchedItems.map((item) => item.id) };
1336
- case "uuid":
1337
- return {
1338
- items: matchedItems.filter(
1339
- (item) => Boolean(item.custom?.uuid)
1340
- ).map((item) => item.custom.uuid)
1341
- };
1342
- default:
1343
- return { items: matchedItems.map((item) => formatPretty([item])) };
1344
- }
1345
- }
1346
- const search = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1347
- __proto__: null,
1348
- searchReferences
1349
- }, Symbol.toStringTag, { value: "Module" }));
1350
- function buildPubmedConfig(config) {
1351
- const pubmedConfig = {};
1352
- if (config.pubmed.email !== void 0) {
1353
- pubmedConfig.email = config.pubmed.email;
1354
- }
1355
- if (config.pubmed.apiKey !== void 0) {
1356
- pubmedConfig.apiKey = config.pubmed.apiKey;
1357
- }
1358
- return pubmedConfig;
1359
- }
1360
- function createAddRoute(library, config) {
1361
- const route = new Hono();
1362
- route.post("/", async (c) => {
1363
- let body;
1364
- try {
1365
- body = await c.req.json();
1366
- } catch {
1367
- return c.json({ error: "Invalid JSON" }, 400);
1368
- }
1369
- if (!body || typeof body !== "object") {
1370
- return c.json({ error: "Request body must be an object" }, 400);
1371
- }
1372
- const { inputs, options } = body;
1373
- if (!inputs || !Array.isArray(inputs) || inputs.length === 0) {
1374
- return c.json({ error: "inputs must be a non-empty array of strings" }, 400);
1375
- }
1376
- if (!inputs.every((input) => typeof input === "string")) {
1377
- return c.json({ error: "All inputs must be strings" }, 400);
1378
- }
1379
- const addOptions = {
1380
- force: options?.force ?? false,
1381
- pubmedConfig: buildPubmedConfig(config)
1382
- };
1383
- if (options?.format) {
1384
- addOptions.format = options.format;
1385
- }
1386
- const result = await addReferences(inputs, library, addOptions);
1387
- return c.json(result);
1388
- });
1389
- return route;
1390
- }
1391
1413
  const CiteRequestSchema = z.object({
1392
1414
  identifiers: z.array(z.string()).min(1, "identifiers must be a non-empty array"),
1393
1415
  idType: z.enum(["id", "uuid", "doi", "pmid", "isbn"]).optional(),
@@ -1431,8 +1453,51 @@ const healthRoute = new Hono();
1431
1453
  healthRoute.get("/", (c) => {
1432
1454
  return c.json({ status: "ok" });
1433
1455
  });
1456
+ function formatItems$1(items, format) {
1457
+ switch (format) {
1458
+ case "json":
1459
+ return items.map((item) => JSON.stringify(item));
1460
+ case "bibtex":
1461
+ return items.map((item) => formatBibtex([item]));
1462
+ case "ids-only":
1463
+ return items.map((item) => item.id);
1464
+ case "uuid":
1465
+ return items.filter(
1466
+ (item) => Boolean(item.custom?.uuid)
1467
+ ).map((item) => item.custom.uuid);
1468
+ default:
1469
+ return items.map((item) => formatPretty([item]));
1470
+ }
1471
+ }
1472
+ async function listReferences(library, options) {
1473
+ const format = options.format ?? "pretty";
1474
+ const sort = options.sort ?? "updated";
1475
+ const order = options.order ?? "desc";
1476
+ const limit = options.limit ?? 0;
1477
+ const offset = options.offset ?? 0;
1478
+ const allItems = await library.getAll();
1479
+ const total = allItems.length;
1480
+ const sorted = sortReferences(allItems, sort, order);
1481
+ const { items: paginatedItems, nextOffset } = paginate(sorted, { limit, offset });
1482
+ const formattedItems = formatItems$1(paginatedItems, format);
1483
+ return {
1484
+ items: formattedItems,
1485
+ total,
1486
+ limit,
1487
+ offset,
1488
+ nextOffset
1489
+ };
1490
+ }
1491
+ const list = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1492
+ __proto__: null,
1493
+ listReferences
1494
+ }, Symbol.toStringTag, { value: "Module" }));
1434
1495
  const listRequestBodySchema = z.object({
1435
- format: z.enum(["pretty", "json", "bibtex", "ids-only", "uuid"]).optional()
1496
+ format: z.enum(["pretty", "json", "bibtex", "ids-only", "uuid"]).optional(),
1497
+ sort: sortFieldSchema.optional(),
1498
+ order: sortOrderSchema.optional(),
1499
+ limit: z.number().int().min(0).optional(),
1500
+ offset: z.number().int().min(0).optional()
1436
1501
  });
1437
1502
  function createListRoute(library) {
1438
1503
  const route = new Hono();
@@ -1445,18 +1510,30 @@ function createListRoute(library) {
1445
1510
  }
1446
1511
  const parseResult = listRequestBodySchema.safeParse(body);
1447
1512
  if (!parseResult.success) {
1448
- return c.json({ error: "Request body must be an object" }, 400);
1513
+ const errorMessage = parseResult.error.issues[0]?.message ?? "Invalid request body";
1514
+ return c.json({ error: errorMessage }, 400);
1449
1515
  }
1450
1516
  const requestBody = parseResult.data;
1451
- const options = {};
1452
- if (requestBody.format !== void 0) {
1453
- options.format = requestBody.format;
1454
- }
1517
+ const options = pickDefined(requestBody, [
1518
+ "format",
1519
+ "sort",
1520
+ "order",
1521
+ "limit",
1522
+ "offset"
1523
+ ]);
1455
1524
  const result = await listReferences(library, options);
1456
1525
  return c.json(result);
1457
1526
  });
1458
1527
  return route;
1459
1528
  }
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
+ }
1460
1537
  function createReferencesRoute(library) {
1461
1538
  const route = new Hono();
1462
1539
  route.get("/", async (c) => {
@@ -1570,9 +1647,71 @@ function createReferencesRoute(library) {
1570
1647
  });
1571
1648
  return route;
1572
1649
  }
1650
+ function formatItems(items, format) {
1651
+ switch (format) {
1652
+ case "json":
1653
+ return items.map((item) => JSON.stringify(item));
1654
+ case "bibtex":
1655
+ return items.map((item) => formatBibtex([item]));
1656
+ case "ids-only":
1657
+ return items.map((item) => item.id);
1658
+ case "uuid":
1659
+ return items.filter(
1660
+ (item) => Boolean(item.custom?.uuid)
1661
+ ).map((item) => item.custom.uuid);
1662
+ default:
1663
+ return items.map((item) => formatPretty([item]));
1664
+ }
1665
+ }
1666
+ async function searchReferences(library, options) {
1667
+ const format = options.format ?? "pretty";
1668
+ const query = options.query;
1669
+ const sort = options.sort ?? "updated";
1670
+ const order = options.order ?? "desc";
1671
+ const limit = options.limit ?? 0;
1672
+ const offset = options.offset ?? 0;
1673
+ const allItems = await library.getAll();
1674
+ let matchedItems;
1675
+ if (!query.trim()) {
1676
+ matchedItems = allItems;
1677
+ } else {
1678
+ const tokens = tokenize(query).tokens;
1679
+ const results = search$1(allItems, tokens);
1680
+ if (sort === "relevance") {
1681
+ const sorted2 = sortResults(results);
1682
+ matchedItems = order === "desc" ? sorted2.map((r) => r.reference) : sorted2.map((r) => r.reference).reverse();
1683
+ } else {
1684
+ matchedItems = results.map((r) => r.reference);
1685
+ }
1686
+ }
1687
+ const total = matchedItems.length;
1688
+ let sorted;
1689
+ if (sort === "relevance") {
1690
+ sorted = matchedItems;
1691
+ } else {
1692
+ sorted = sortReferences(matchedItems, sort, order);
1693
+ }
1694
+ const { items: paginatedItems, nextOffset } = paginate(sorted, { limit, offset });
1695
+ const formattedItems = formatItems(paginatedItems, format);
1696
+ return {
1697
+ items: formattedItems,
1698
+ total,
1699
+ limit,
1700
+ offset,
1701
+ nextOffset
1702
+ };
1703
+ }
1704
+ const search = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1705
+ __proto__: null,
1706
+ searchReferences
1707
+ }, Symbol.toStringTag, { value: "Module" }));
1573
1708
  const searchRequestBodySchema = z.object({
1574
1709
  query: z.string(),
1575
- format: z.enum(["pretty", "json", "bibtex", "ids-only", "uuid"]).optional()
1710
+ format: z.enum(["pretty", "json", "bibtex", "ids-only", "uuid"]).optional(),
1711
+ sort: searchSortFieldSchema.optional(),
1712
+ order: sortOrderSchema.optional(),
1713
+ limit: z.number().int().min(0).optional(),
1714
+ offset: z.number().int().min(0).optional()
1576
1715
  });
1577
1716
  function createSearchRoute(library) {
1578
1717
  const route = new Hono();
@@ -1585,15 +1724,14 @@ function createSearchRoute(library) {
1585
1724
  }
1586
1725
  const parseResult = searchRequestBodySchema.safeParse(body);
1587
1726
  if (!parseResult.success) {
1588
- return c.json({ error: "Invalid request body" }, 400);
1727
+ const errorMessage = parseResult.error.issues[0]?.message ?? "Invalid request body";
1728
+ return c.json({ error: errorMessage }, 400);
1589
1729
  }
1590
1730
  const requestBody = parseResult.data;
1591
1731
  const options = {
1592
- query: requestBody.query
1732
+ query: requestBody.query,
1733
+ ...pickDefined(requestBody, ["format", "sort", "order", "limit", "offset"])
1593
1734
  };
1594
- if (requestBody.format !== void 0) {
1595
- options.format = requestBody.format;
1596
- }
1597
1735
  const result = await searchReferences(library, options);
1598
1736
  return c.json(result);
1599
1737
  });
@@ -1641,17 +1779,14 @@ async function startServerWithFileWatcher(libraryPath, config) {
1641
1779
  }
1642
1780
  export {
1643
1781
  BUILTIN_STYLES as B,
1644
- addReferences as a,
1645
- startServerWithFileWatcher as b,
1646
- citeReferences as c,
1647
- createServer as d,
1648
- add as e,
1649
- cite as f,
1650
- list as g,
1651
- search as h,
1652
- listReferences as l,
1653
- removeReference as r,
1654
- searchReferences as s,
1782
+ formatBibtex as a,
1783
+ add as b,
1784
+ createServer as c,
1785
+ cite as d,
1786
+ search as e,
1787
+ formatBibliographyCSL as f,
1788
+ list as l,
1789
+ startServerWithFileWatcher as s,
1655
1790
  updateReference as u
1656
1791
  };
1657
- //# sourceMappingURL=index-Bl_mOQRe.js.map
1792
+ //# sourceMappingURL=index-_7NEUoS7.js.map