cocoda-sdk 3.1.0 → 3.2.2

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.
package/README.md CHANGED
@@ -165,6 +165,8 @@ The following providers are also exported, but have to be added via `cdk.addProv
165
165
  - `SkosmosApi` - access to concept schemes and concepts via a [Skosmos](https://github.com/NatLibFi/Skosmos) API
166
166
  - `LocApi` - access to concept schemes and concepts via the [Library of Congress Linked Data Service](https://id.loc.gov/)
167
167
  - **This integration is currently experimental and only supports LCSH and LCNAF.**
168
+ - `Skohub` - access to concept schemes and concepts via a [SkoHub Vocabs](https://blog.lobid.org/2019/09/27/presenting-skohub-vocabs.html)
169
+ - **This integration is currently experimental. Only vocabularies that use a [slash namespace pattern]([hash](https://www.w3.org/2001/sw/BestPractices/VM/http-examples/2006-01-18/#slash)) with dereferenceable URIs are supported.**
168
170
  - `ReconciliationApi` - access to mapping suggestions via a [OpenRefine Reconciliation API](https://github.com/OpenRefine/OpenRefine/wiki/Reconciliation-Service-API)
169
171
  - `OccurrencesApi` - access to concept occurrences via [occurrences-api](https://github.com/gbv/occurrences-api) (will be changed to [occurrences-server](https://github.com/gbv/occurrences-server) in the future)
170
172
  - `LabelSearchSuggestion` - access to mapping suggestions using other registries' search endpoints (using [jskos-server])
@@ -31,6 +31,7 @@ __export(src_exports, {
31
31
  MappingsApiProvider: () => MappingsApiProvider,
32
32
  OccurrencesApiProvider: () => OccurrencesApiProvider,
33
33
  ReconciliationApiProvider: () => ReconciliationApiProvider,
34
+ SkohubProvider: () => SkohubProvider,
34
35
  SkosmosApiProvider: () => SkosmosApiProvider,
35
36
  addAllProviders: () => addAllProviders,
36
37
  cdk: () => cdk,
@@ -108,7 +109,7 @@ var import_omit = __toESM(require("lodash/omit.js"), 1);
108
109
  var import_concat = __toESM(require("lodash/concat.js"), 1);
109
110
 
110
111
  // src/lib/CocodaSDK.js
111
- var import_jskos_tools10 = __toESM(require("jskos-tools"), 1);
112
+ var import_jskos_tools11 = __toESM(require("jskos-tools"), 1);
112
113
 
113
114
  // src/providers/index.js
114
115
  var providers_exports = {};
@@ -121,6 +122,7 @@ __export(providers_exports, {
121
122
  MappingsApiProvider: () => MappingsApiProvider,
122
123
  OccurrencesApiProvider: () => OccurrencesApiProvider,
123
124
  ReconciliationApiProvider: () => ReconciliationApiProvider,
125
+ SkohubProvider: () => SkohubProvider,
124
126
  SkosmosApiProvider: () => SkosmosApiProvider
125
127
  });
126
128
 
@@ -330,10 +332,12 @@ var BaseProvider = class {
330
332
  this._config = {};
331
333
  this.setRetryConfig();
332
334
  this.axios.interceptors.request.use((config) => {
333
- const language = import_uniq.default([].concat(import_get.default(config, "params.language", "").split(","), this.languages, this._defaultLanguages).filter((lang) => lang != "")).join(",");
334
- import_set.default(config, "params.language", language);
335
- if (this.has.auth && this._auth.bearerToken && !import_get.default(config, "headers.Authorization")) {
336
- import_set.default(config, "headers.Authorization", `Bearer ${this._auth.bearerToken}`);
335
+ if (!config._skipAdditionalParameters) {
336
+ const language = import_uniq.default([].concat(import_get.default(config, "params.language", "").split(","), this.languages, this._defaultLanguages).filter((lang) => lang != "")).join(",");
337
+ import_set.default(config, "params.language", language);
338
+ if (this.has.auth && this._auth.bearerToken && !import_get.default(config, "headers.Authorization")) {
339
+ import_set.default(config, "headers.Authorization", `Bearer ${this._auth.bearerToken}`);
340
+ }
337
341
  }
338
342
  if (config.url.startsWith("http:") && typeof window !== "undefined" && window.location.protocol == "https:") {
339
343
  throw new import_axios.default.Cancel("Can't call http API from https.");
@@ -1108,7 +1112,7 @@ var MappingsApiProvider = class extends BaseProvider {
1108
1112
  throw error;
1109
1113
  }
1110
1114
  }
1111
- async getMappings({ from, fromScheme, to, toScheme, creator, type, partOf, offset, limit, direction, mode, identifier, cardinality, sort, order, ...config }) {
1115
+ async getMappings({ from, fromScheme, to, toScheme, creator, type, partOf, offset, limit, direction, mode, identifier, cardinality, annotatedBy, annotatedFor, annotatedWith, sort, order, ...config }) {
1112
1116
  let params = {}, url = this._api.mappings;
1113
1117
  if (from) {
1114
1118
  params.from = import_isString.default(from) ? from : from.uri;
@@ -1143,6 +1147,15 @@ var MappingsApiProvider = class extends BaseProvider {
1143
1147
  if (cardinality) {
1144
1148
  params.cardinality = cardinality;
1145
1149
  }
1150
+ if (annotatedBy) {
1151
+ params.annotatedBy = annotatedBy;
1152
+ }
1153
+ if (annotatedFor) {
1154
+ params.annotatedFor = annotatedFor;
1155
+ }
1156
+ if (annotatedWith) {
1157
+ params.annotatedWith = annotatedWith;
1158
+ }
1146
1159
  if (mode) {
1147
1160
  params.mode = mode;
1148
1161
  }
@@ -1406,8 +1419,10 @@ var OccurrencesApiProvider = class extends BaseProvider {
1406
1419
  }
1407
1420
  async getMappings(config) {
1408
1421
  const occurrences = await this.getOccurrences(config);
1409
- const fromScheme = import_get.default(config, "from.inScheme[0]") || config.fromScheme;
1410
- const toScheme = import_get.default(config, "to.inScheme[0]") || config.toScheme;
1422
+ const from = config.from;
1423
+ const fromScheme = import_get.default(from, "inScheme[0]") || config.fromScheme;
1424
+ const to = config.to;
1425
+ const toScheme = import_get.default(to, "inScheme[0]") || config.toScheme;
1411
1426
  const mappings = [];
1412
1427
  for (let occurrence of occurrences) {
1413
1428
  if (!occurrence) {
@@ -1428,7 +1443,7 @@ var OccurrencesApiProvider = class extends BaseProvider {
1428
1443
  mapping.to = { memberSet: [] };
1429
1444
  }
1430
1445
  mapping.toScheme = import_get.default(occurrence, "memberSet[1].inScheme[0]");
1431
- if (fromScheme && mapping.fromScheme && !import_jskos_tools4.default.compare(mapping.fromScheme, fromScheme) || toScheme && mapping.toScheme && !import_jskos_tools4.default.compare(mapping.toScheme, toScheme)) {
1446
+ if (from && import_jskos_tools4.default.compare(from, import_get.default(mapping, "to.memberSet[0]")) || to && import_jskos_tools4.default.compare(to, import_get.default(mapping, "from.memberSet[0]"))) {
1432
1447
  [mapping.from, mapping.fromScheme, mapping.to, mapping.toScheme] = [mapping.to, mapping.toScheme, mapping.from, mapping.fromScheme];
1433
1448
  }
1434
1449
  if (!mapping.fromScheme && fromScheme) {
@@ -1440,14 +1455,12 @@ var OccurrencesApiProvider = class extends BaseProvider {
1440
1455
  mapping.type = [import_jskos_tools4.default.defaultMappingType.uri];
1441
1456
  mapping._occurrence = occurrence;
1442
1457
  mapping = import_jskos_tools4.default.addMappingIdentifiers(mapping);
1443
- if (occurrence.database) {
1444
- mapping.creator = [occurrence.database];
1445
- }
1446
1458
  mappings.push(mapping);
1447
1459
  }
1460
+ mappings._url = occurrences._url;
1448
1461
  return mappings;
1449
1462
  }
1450
- async getOccurrences({ from, to, concepts, ...config }) {
1463
+ async getOccurrences({ from, to, concepts, threshold = 0, ...config }) {
1451
1464
  let promises = [];
1452
1465
  concepts = (concepts || []).concat([from, to]).filter((c) => !!c);
1453
1466
  for (let concept of concepts) {
@@ -1471,17 +1484,7 @@ var OccurrencesApiProvider = class extends BaseProvider {
1471
1484
  params: {
1472
1485
  member: uri,
1473
1486
  scheme: "*",
1474
- threshold: 5
1475
- }
1476
- }));
1477
- }
1478
- if (uris.length > 1) {
1479
- let urisString = uris.join(" ");
1480
- promises.push(this._getOccurrences({
1481
- ...config,
1482
- params: {
1483
- member: urisString,
1484
- threshold: 5
1487
+ threshold
1485
1488
  }
1486
1489
  }));
1487
1490
  }
@@ -1505,7 +1508,9 @@ var OccurrencesApiProvider = class extends BaseProvider {
1505
1508
  delete occurrences[value];
1506
1509
  });
1507
1510
  occurrences = occurrences.filter((o) => o != null);
1508
- return occurrences.sort((a, b) => parseInt(b.count || 0) - parseInt(a.count || 0));
1511
+ occurrences = occurrences.sort((a, b) => parseInt(b.count || 0) - parseInt(a.count || 0));
1512
+ occurrences._url = results.map((result) => result._url);
1513
+ return occurrences;
1509
1514
  }
1510
1515
  async _getOccurrences(config) {
1511
1516
  let resultsFromCache = this._cache.find((item) => {
@@ -2586,6 +2591,229 @@ var LocApiProvider = class extends BaseProvider {
2586
2591
  LocApiProvider.providerName = "LocApi";
2587
2592
  LocApiProvider.providerType = "http://bartoc.org/api-type/loc";
2588
2593
 
2594
+ // src/providers/skohub-provider.js
2595
+ var import_jskos_tools10 = __toESM(require("jskos-tools"), 1);
2596
+ var import_flexsearch = __toESM(require("flexsearch"), 1);
2597
+ function decodeUnicode(text) {
2598
+ return text.replace(/\\u[\dA-F]{4}/gi, function(match) {
2599
+ return String.fromCharCode(parseInt(match.replace(/\\u/g, ""), 16));
2600
+ });
2601
+ }
2602
+ var SkohubProvider = class extends BaseProvider {
2603
+ _prepare() {
2604
+ this.has.schemes = true;
2605
+ this.has.top = true;
2606
+ this.has.data = true;
2607
+ this.has.concepts = true;
2608
+ this.has.narrower = true;
2609
+ this.has.ancestors = true;
2610
+ this.has.suggest = true;
2611
+ this.has.search = true;
2612
+ listOfCapabilities.filter((c) => !this.has[c]).forEach((c) => {
2613
+ this.has[c] = false;
2614
+ });
2615
+ }
2616
+ _setup() {
2617
+ this._index = {};
2618
+ this._conceptCache = {};
2619
+ this._schemeCache = {};
2620
+ }
2621
+ static _registryConfigForBartocApiConfig({ url, scheme } = {}) {
2622
+ if (!url || !scheme) {
2623
+ return null;
2624
+ }
2625
+ const newScheme = { uri: url, identifier: import_jskos_tools10.default.getAllUris(scheme).filter((uri) => uri !== url) };
2626
+ return { schemes: [newScheme] };
2627
+ }
2628
+ async _loadScheme({ scheme, ...config }) {
2629
+ let uris = import_jskos_tools10.default.getAllUris(scheme);
2630
+ for (let uri2 of uris) {
2631
+ if (this._schemeCache[uri2]) {
2632
+ return this._schemeCache[uri2];
2633
+ }
2634
+ }
2635
+ const schemeFromList = this.schemes.find((s) => import_jskos_tools10.default.compare(s, scheme));
2636
+ if (!schemeFromList || !schemeFromList.uri) {
2637
+ throw new InvalidRequestError({ message: `Tried to load unsupported scheme (${scheme && scheme.uri})` });
2638
+ }
2639
+ const uri = schemeFromList.uri;
2640
+ uris = import_uniq.default(uris.concat(import_jskos_tools10.default.getAllUris(schemeFromList)));
2641
+ let postfix = ".json";
2642
+ if (uri.endsWith("/")) {
2643
+ postfix = "index.json";
2644
+ }
2645
+ const data = await this.axios({ ...config, url: `${uri}${postfix}`, _skipAdditionalParameters: true });
2646
+ if (data.id !== uri) {
2647
+ throw new InvalidRequestError({ message: "Skohub URL did not return expected concept scheme" });
2648
+ }
2649
+ const { title, preferredNamespaceUri, hasTopConcept, description } = data;
2650
+ scheme = { uri, identifier: uris.filter((u) => u !== uri) };
2651
+ scheme.prefLabel = title;
2652
+ Object.keys(scheme.prefLabel || {}).forEach((key) => {
2653
+ scheme.prefLabel[key] = decodeUnicode(scheme.prefLabel[key]);
2654
+ });
2655
+ scheme.namespace = preferredNamespaceUri;
2656
+ scheme.topConcepts = (hasTopConcept || []).map((c) => this._toJskosConcept(c));
2657
+ scheme.concepts = [null];
2658
+ if (description) {
2659
+ scheme.definition = description;
2660
+ Object.keys(scheme.definition).forEach((key) => {
2661
+ scheme.definition[key] = [decodeUnicode(scheme.definition[key])];
2662
+ });
2663
+ }
2664
+ for (let key of Object.keys(scheme).filter((key2) => !scheme[key2])) {
2665
+ delete scheme[key];
2666
+ }
2667
+ for (let uri2 of uris) {
2668
+ this._schemeCache[uri2] = scheme;
2669
+ }
2670
+ return scheme;
2671
+ }
2672
+ async _loadConcept({ uri, ...config }) {
2673
+ if (this._conceptCache[uri]) {
2674
+ return this._conceptCache[uri];
2675
+ }
2676
+ try {
2677
+ const data = await this.axios({ ...config, url: `${uri}.json`, _skipAdditionalParameters: true });
2678
+ if (data.id !== uri) {
2679
+ throw new InvalidRequestError({ message: "Skohub URL did not return expected concept URI" });
2680
+ }
2681
+ const concept = this._toJskosConcept(data);
2682
+ this._conceptCache[uri] = concept;
2683
+ return concept;
2684
+ } catch (error) {
2685
+ return null;
2686
+ }
2687
+ }
2688
+ _toJskosConcept(data) {
2689
+ const concept = { uri: data.id };
2690
+ concept.prefLabel = data.prefLabel;
2691
+ Object.keys(concept.prefLabel || {}).forEach((key) => {
2692
+ concept.prefLabel[key] = decodeUnicode(concept.prefLabel[key]);
2693
+ });
2694
+ concept.narrower = (data.narrower || []).map((c) => this._toJskosConcept(c));
2695
+ concept.notation = data.notation || [];
2696
+ if (data.broader && data.broader.id) {
2697
+ concept.broader = [{ uri: data.broader.id }];
2698
+ }
2699
+ if (data.inScheme && data.inScheme.id) {
2700
+ concept.inScheme = [{ uri: data.inScheme.id }];
2701
+ }
2702
+ if (data.scopeNote) {
2703
+ concept.scopeNote = data.scopeNote;
2704
+ Object.keys(concept.scopeNote).forEach((key) => {
2705
+ concept.scopeNote[key] = [decodeUnicode(concept.scopeNote[key])];
2706
+ });
2707
+ }
2708
+ return concept;
2709
+ }
2710
+ async getSchemes({ ...config }) {
2711
+ return Promise.all(this.schemes.map((scheme) => this._loadScheme({ ...config, scheme })));
2712
+ }
2713
+ async getTop({ scheme, ...config }) {
2714
+ if (!scheme || !scheme.uri) {
2715
+ throw new InvalidOrMissingParameterError({ parameter: "scheme", message: "Missing scheme URI" });
2716
+ }
2717
+ scheme = await this._loadScheme({ scheme, ...config });
2718
+ return scheme.topConcepts || [];
2719
+ }
2720
+ async getConcepts({ concepts, ...config }) {
2721
+ if (!import_isArray.default(concepts)) {
2722
+ concepts = [concepts];
2723
+ }
2724
+ return (await Promise.all(concepts.map(({ uri }) => this._loadConcept({ ...config, uri })))).filter(Boolean);
2725
+ }
2726
+ async getAncestors({ concept, ...config }) {
2727
+ if (!concept || !concept.uri) {
2728
+ throw new InvalidOrMissingParameterError({ parameter: "concept" });
2729
+ }
2730
+ if (concept.ancestors && concept.ancestors[0] !== null) {
2731
+ return concept.ancestors;
2732
+ }
2733
+ concept = await this._loadConcept({ ...config, uri: concept.uri });
2734
+ if (!concept || !concept.broader || !concept.broader.length) {
2735
+ return [];
2736
+ }
2737
+ const broader = concept.broader[0];
2738
+ return [broader].concat(await this.getAncestors({ concept: broader, ...config })).map((c) => ({ uri: c.uri }));
2739
+ }
2740
+ async getNarrower({ concept, ...config }) {
2741
+ if (!concept || !concept.uri) {
2742
+ throw new InvalidOrMissingParameterError({ parameter: "concept" });
2743
+ }
2744
+ if (concept.narrower && concept.narrower[0] !== null) {
2745
+ return concept.narrower;
2746
+ }
2747
+ concept = await this._loadConcept({ ...config, uri: concept.uri });
2748
+ return concept.narrower;
2749
+ }
2750
+ async search({ search, scheme, limit = 100 }) {
2751
+ scheme = await this._loadScheme({ scheme });
2752
+ if (!scheme || !scheme.uri) {
2753
+ throw new InvalidOrMissingParameterError({ parameter: "scheme" });
2754
+ }
2755
+ if (!search) {
2756
+ throw new InvalidOrMissingParameterError({ parameter: "search" });
2757
+ }
2758
+ let index;
2759
+ if (!this._index[scheme.uri]) {
2760
+ this._index[scheme.uri] = {};
2761
+ }
2762
+ for (const lang of [""].concat(this.languages)) {
2763
+ if (this._index[scheme.uri][lang]) {
2764
+ index = this._index[scheme.uri][lang];
2765
+ break;
2766
+ }
2767
+ if (this._index[scheme.uri][lang] === null) {
2768
+ continue;
2769
+ }
2770
+ try {
2771
+ let postfix = lang ? `.${lang}.index` : ".index";
2772
+ if (scheme.uri.endsWith("/")) {
2773
+ postfix = `index${postfix}`;
2774
+ }
2775
+ const data = await this.axios({ url: `${scheme.uri}${postfix}`, _skipAdditionalParameters: true });
2776
+ if (data.length < 100) {
2777
+ this._index[scheme.uri][lang] = null;
2778
+ continue;
2779
+ }
2780
+ index = import_flexsearch.default.create();
2781
+ index.import(data);
2782
+ this._index[scheme.uri][lang] = index;
2783
+ break;
2784
+ } catch (error) {
2785
+ this._index[scheme.uri][lang] = null;
2786
+ }
2787
+ }
2788
+ if (!index) {
2789
+ throw new InvalidRequestError({ message: "Could not find search index for any of the available languages " + this.languages.join(",") });
2790
+ }
2791
+ const result = index.search(search);
2792
+ const concepts = await this.getConcepts({ concepts: result.map((uri) => ({ uri })) });
2793
+ return concepts.slice(0, limit);
2794
+ }
2795
+ async suggest(config) {
2796
+ config._raw = true;
2797
+ const concepts = await this.search(config);
2798
+ const result = [config.search, [], [], []];
2799
+ for (let concept of concepts) {
2800
+ const notation = import_jskos_tools10.default.notation(concept);
2801
+ const label = import_jskos_tools10.default.prefLabel(concept);
2802
+ result[1].push((notation ? notation + " " : "") + label);
2803
+ result[2].push("");
2804
+ result[3].push(concept.uri);
2805
+ }
2806
+ if (concepts._totalCount != void 0) {
2807
+ result._totalCount = concepts._totalCount;
2808
+ } else {
2809
+ result._totalCount = concepts.length;
2810
+ }
2811
+ return result;
2812
+ }
2813
+ };
2814
+ SkohubProvider.providerName = "Skohub";
2815
+ SkohubProvider.providerType = "http://bartoc.org/api-type/skohub";
2816
+
2589
2817
  // src/lib/CocodaSDK.js
2590
2818
  var providers = {
2591
2819
  [BaseProvider.providerName]: BaseProvider,
@@ -2771,7 +2999,7 @@ var CocodaSDK = class {
2771
2999
  scheme._registry = registry;
2772
3000
  scheme.__DETAILSLOADED__ = 1;
2773
3001
  scheme.type = scheme.type || ["http://www.w3.org/2004/02/skos/core#ConceptScheme"];
2774
- let otherScheme = schemes.find((s) => import_jskos_tools10.default.compare(s, scheme)), prio, otherPrio, override = false;
3002
+ let otherScheme = schemes.find((s) => import_jskos_tools11.default.compare(s, scheme)), prio, otherPrio, override = false;
2775
3003
  if (otherScheme) {
2776
3004
  prio = this.config.registries.indexOf(registry);
2777
3005
  if (prio != -1) {
@@ -2793,19 +3021,19 @@ var CocodaSDK = class {
2793
3021
  }
2794
3022
  if (!otherScheme || override) {
2795
3023
  if (override) {
2796
- let otherSchemeIndex = schemes.findIndex((s) => import_jskos_tools10.default.compare(s, otherScheme));
3024
+ let otherSchemeIndex = schemes.findIndex((s) => import_jskos_tools11.default.compare(s, otherScheme));
2797
3025
  if (otherSchemeIndex != -1) {
2798
3026
  schemes.splice(otherSchemeIndex, 1);
2799
3027
  }
2800
- scheme = import_jskos_tools10.default.merge(scheme, import_omit.default(otherScheme, ["concepts", "topConcepts"]), { mergeUris: true, skipPaths: ["_registry"] });
3028
+ scheme = import_jskos_tools11.default.merge(scheme, import_omit.default(otherScheme, ["concepts", "topConcepts"]), { mergeUris: true, skipPaths: ["_registry"] });
2801
3029
  }
2802
3030
  scheme._registry = registry;
2803
3031
  schemes.push(scheme);
2804
3032
  } else {
2805
- const index = schemes.findIndex((s) => import_jskos_tools10.default.compare(s, scheme));
3033
+ const index = schemes.findIndex((s) => import_jskos_tools11.default.compare(s, scheme));
2806
3034
  if (index != -1) {
2807
3035
  const otherSchemeRegistry = schemes[index]._registry;
2808
- schemes[index] = import_jskos_tools10.default.merge(schemes[index], import_omit.default(scheme, ["concepts", "topConcepts"]), { mergeUris: true, skipPaths: ["_registry"] });
3036
+ schemes[index] = import_jskos_tools11.default.merge(schemes[index], import_omit.default(scheme, ["concepts", "topConcepts"]), { mergeUris: true, skipPaths: ["_registry"] });
2809
3037
  schemes[index]._registry = otherSchemeRegistry;
2810
3038
  }
2811
3039
  }
@@ -2827,7 +3055,7 @@ var CocodaSDK = class {
2827
3055
  scheme._registry = newRegistry;
2828
3056
  }
2829
3057
  });
2830
- return import_jskos_tools10.default.sortSchemes(schemes.filter(Boolean));
3058
+ return import_jskos_tools11.default.sortSchemes(schemes.filter(Boolean));
2831
3059
  }
2832
3060
  registryForScheme(scheme) {
2833
3061
  let registry = scheme._registry;
@@ -2838,7 +3066,7 @@ var CocodaSDK = class {
2838
3066
  const url = config.url;
2839
3067
  if (registryCache[url]) {
2840
3068
  const registry2 = registryCache[url];
2841
- if (Array.isArray(registry2._jskos.schemes) && !import_jskos_tools10.default.isContainedIn(scheme, registry2._jskos.schemes)) {
3069
+ if (Array.isArray(registry2._jskos.schemes) && !import_jskos_tools11.default.isContainedIn(scheme, registry2._jskos.schemes)) {
2842
3070
  registry2._jskos.schemes.push(scheme);
2843
3071
  }
2844
3072
  return registry2;
@@ -2885,6 +3113,7 @@ function addAllProviders(_cdk) {
2885
3113
  MappingsApiProvider,
2886
3114
  OccurrencesApiProvider,
2887
3115
  ReconciliationApiProvider,
3116
+ SkohubProvider,
2888
3117
  SkosmosApiProvider,
2889
3118
  addAllProviders,
2890
3119
  cdk,