mcp-databnf 2.0.0 → 2.1.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.
@@ -254,6 +254,16 @@ interface EntityId {
254
254
  /** The page a person can open. */
255
255
  pageUrl: string;
256
256
  }
257
+ /**
258
+ * Read an identifier a caller wrote, in any of the forms they see.
259
+ *
260
+ * Rows carry `http://data.bnf.fr/ark:/12148/cb119219976#about`, prose carries
261
+ * `cb119219976`, and a browser carries `https://data.bnf.fr/ark:/12148/cb119219976`.
262
+ * All three name one record. Anything else is refused rather than guessed at,
263
+ * because an identifier built from a guess sends the next call to a record that
264
+ * does not exist and the answer reads as an absence.
265
+ */
266
+ declare function parseEntityId(input: string): EntityId;
257
267
 
258
268
  /**
259
269
  * The one place that talks to data.bnf.fr.
@@ -346,4 +356,4 @@ declare class BnfClient {
346
356
  types(id: EntityId): Promise<Read<string[]>>;
347
357
  }
348
358
 
349
- export { BnfClient, type ClientOptions, type Read };
359
+ export { BnfClient, type ClientOptions, type EntityId, type EntityKind, type Read, parseEntityId };
@@ -2,7 +2,7 @@
2
2
  import process from "process";
3
3
 
4
4
  // src/version.ts
5
- var PKG_VERSION = "2.0.0";
5
+ var PKG_VERSION = "2.1.0";
6
6
  var REPO_URL = "https://github.com/smeet666/mcp-databnf";
7
7
 
8
8
  // src/config.ts
@@ -438,6 +438,11 @@ function parseResults(body) {
438
438
  // src/bnf/sparql.ts
439
439
  var LEADING_JOINERS = /^['-]+/u;
440
440
  var TRAILING_JOINERS = /['-]+$/u;
441
+ var ARK_IN_INPUT = /(?:^|\/)ark:\/12148\/(c[bc][0-9a-z]+)/i;
442
+ var LEADING_HASH = /^#/;
443
+ var FRAGMENT_OR_QUERY = /[#?]/;
444
+ var TEMP_WORK_IN_INPUT = /temp-work\/([0-9a-f]{32})/i;
445
+ var GALLICA_ARK = /ark:\/12148\/([0-9a-z]+)/i;
441
446
  function escapeLiteral(value) {
442
447
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t").replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/gu, "");
443
448
  }
@@ -471,6 +476,51 @@ function containsAllWords(words) {
471
476
  const clause = words.map((word) => `'${word.replace(/'/g, "''")}'`).join(" AND ");
472
477
  return literal(clause);
473
478
  }
479
+ var ARK_ID = /^c[bc][0-9a-z]{6,20}$/;
480
+ var TEMP_WORK_ID = /^[0-9a-f]{32}$/;
481
+ function parseEntityId(input) {
482
+ const trimmed = input.trim();
483
+ if (trimmed === "") {
484
+ throw invalidInput("An identifier is required.");
485
+ }
486
+ const arkMatch = ARK_IN_INPUT.exec(trimmed);
487
+ const bare = arkMatch ? arkMatch[1] : trimmed.replace(LEADING_HASH, "").split(FRAGMENT_OR_QUERY)[0];
488
+ const candidate = (bare ?? "").toLowerCase();
489
+ if (ARK_ID.test(candidate)) {
490
+ return {
491
+ kind: "ark",
492
+ id: candidate,
493
+ iri: `http://data.bnf.fr/ark:/12148/${candidate}#about`,
494
+ pageUrl: `https://data.bnf.fr/ark:/12148/${candidate}`
495
+ };
496
+ }
497
+ const tempMatch = TEMP_WORK_IN_INPUT.exec(trimmed);
498
+ const digest = (tempMatch?.[1] ?? candidate).toLowerCase();
499
+ if (TEMP_WORK_ID.test(digest)) {
500
+ return {
501
+ kind: "temp-work",
502
+ id: `temp-work/${digest}`,
503
+ iri: `http://data.bnf.fr/temp-work/${digest}/#about`,
504
+ pageUrl: `https://data.bnf.fr/temp-work/${digest}/`
505
+ };
506
+ }
507
+ throw invalidInput(`"${input}" is not a data.bnf.fr identifier.`, hintFor(trimmed));
508
+ }
509
+ var GALLICA_DOCUMENT = /^(?:bpt6k|btv1b|bd6t|cb32|btv1b)[0-9a-z]+$/i;
510
+ var PAGE_ADDRESS = /data\.bnf\.fr\/(?:[a-z]{2}\/)?(\d{6,12})\/([a-z0-9_]+)/i;
511
+ var GENERAL_HINT = "An identifier looks like 'cb119219976' for an established record, or 'temp-work/22d7f68c1a4bdd081ad7ca791fd3b730' for a provisional one. Take it from a search result rather than building one.";
512
+ function hintFor(input) {
513
+ const page2 = PAGE_ADDRESS.exec(input);
514
+ if (page2) {
515
+ const name = (page2[2] ?? "").replace(/_/g, " ");
516
+ return `That is the readable address of a data.bnf.fr page, and ${page2[1]} in it is the record's FRBNF number rather than the identifier these tools take. Search for "${name}" with search_authors or search_works, and use the identifier the row carries.`;
517
+ }
518
+ const gallicaArk = GALLICA_ARK.exec(input)?.[1] ?? input;
519
+ if (GALLICA_DOCUMENT.test(gallicaArk)) {
520
+ return "That is a Gallica identifier: it names a digitised document rather than a record in the catalogue, and these tools read the catalogue. Search for the work with search_works, then list_editions will give you the Gallica link alongside the edition it belongs to.";
521
+ }
522
+ return GENERAL_HINT;
523
+ }
474
524
  var iriOf = (id) => `<${id.iri}>`;
475
525
  function boundedInteger(value, min, max, name) {
476
526
  if (!(Number.isFinite(value) && Number.isInteger(value))) {
@@ -1471,6 +1521,7 @@ var BnfClient = class {
1471
1521
  }
1472
1522
  };
1473
1523
  export {
1474
- BnfClient
1524
+ BnfClient,
1525
+ parseEntityId
1475
1526
  };
1476
1527
  //# sourceMappingURL=client.js.map