cocoda-sdk 3.1.0 → 3.2.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.
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
  }
@@ -2586,6 +2599,228 @@ var LocApiProvider = class extends BaseProvider {
2586
2599
  LocApiProvider.providerName = "LocApi";
2587
2600
  LocApiProvider.providerType = "http://bartoc.org/api-type/loc";
2588
2601
 
2602
+ // src/providers/skohub-provider.js
2603
+ var import_jskos_tools10 = __toESM(require("jskos-tools"), 1);
2604
+ var import_flexsearch = __toESM(require("flexsearch"), 1);
2605
+ function decodeUnicode(text) {
2606
+ return text.replace(/\\u[\dA-F]{4}/gi, function(match) {
2607
+ return String.fromCharCode(parseInt(match.replace(/\\u/g, ""), 16));
2608
+ });
2609
+ }
2610
+ var SkohubProvider = class extends BaseProvider {
2611
+ _prepare() {
2612
+ this.has.schemes = true;
2613
+ this.has.top = true;
2614
+ this.has.data = true;
2615
+ this.has.concepts = true;
2616
+ this.has.narrower = true;
2617
+ this.has.ancestors = true;
2618
+ this.has.suggest = true;
2619
+ this.has.search = true;
2620
+ listOfCapabilities.filter((c) => !this.has[c]).forEach((c) => {
2621
+ this.has[c] = false;
2622
+ });
2623
+ }
2624
+ _setup() {
2625
+ this._index = {};
2626
+ this._conceptCache = {};
2627
+ this._schemeCache = {};
2628
+ }
2629
+ static _registryConfigForBartocApiConfig({ url, scheme } = {}) {
2630
+ if (!url || !scheme) {
2631
+ return null;
2632
+ }
2633
+ const newScheme = { uri: url, identifier: import_jskos_tools10.default.getAllUris(scheme).filter((uri) => uri !== url) };
2634
+ return { schemes: [newScheme] };
2635
+ }
2636
+ async _loadScheme({ scheme, ...config }) {
2637
+ let uris = import_jskos_tools10.default.getAllUris(scheme);
2638
+ for (let uri2 of uris) {
2639
+ if (this._schemeCache[uri2]) {
2640
+ return this._schemeCache[uri2];
2641
+ }
2642
+ }
2643
+ const schemeFromList = this.schemes.find((s) => import_jskos_tools10.default.compare(s, scheme));
2644
+ if (!schemeFromList || !schemeFromList.uri) {
2645
+ throw new InvalidRequestError({ message: `Tried to load unsupported scheme (${scheme && scheme.uri})` });
2646
+ }
2647
+ const uri = schemeFromList.uri;
2648
+ uris = import_uniq.default(uris.concat(import_jskos_tools10.default.getAllUris(schemeFromList)));
2649
+ let postfix = ".json";
2650
+ if (uri.endsWith("/")) {
2651
+ postfix = "index.json";
2652
+ }
2653
+ const data = await this.axios({ ...config, url: `${uri}${postfix}`, _skipAdditionalParameters: true });
2654
+ if (data.id !== uri) {
2655
+ throw new InvalidRequestError({ message: "Skohub URL did not return expected concept scheme" });
2656
+ }
2657
+ const { title, preferredNamespaceUri, hasTopConcept, description } = data;
2658
+ scheme = { uri, identifier: uris.filter((u) => u !== uri) };
2659
+ scheme.prefLabel = title;
2660
+ Object.keys(scheme.prefLabel || {}).forEach((key) => {
2661
+ scheme.prefLabel[key] = decodeUnicode(scheme.prefLabel[key]);
2662
+ });
2663
+ scheme.namespace = preferredNamespaceUri;
2664
+ scheme.topConcepts = (hasTopConcept || []).map((c) => this._toJskosConcept(c));
2665
+ scheme.concepts = [null];
2666
+ if (description) {
2667
+ scheme.definition = description;
2668
+ Object.keys(scheme.definition).forEach((key) => {
2669
+ scheme.definition[key] = [decodeUnicode(scheme.definition[key])];
2670
+ });
2671
+ }
2672
+ for (let key of Object.keys(scheme).filter((key2) => !scheme[key2])) {
2673
+ delete scheme[key];
2674
+ }
2675
+ for (let uri2 of uris) {
2676
+ this._schemeCache[uri2] = scheme;
2677
+ }
2678
+ return scheme;
2679
+ }
2680
+ async _loadConcept({ uri, ...config }) {
2681
+ if (this._conceptCache[uri]) {
2682
+ return this._conceptCache[uri];
2683
+ }
2684
+ try {
2685
+ const data = await this.axios({ ...config, url: `${uri}.json`, _skipAdditionalParameters: true });
2686
+ if (data.id !== uri) {
2687
+ throw new InvalidRequestError({ message: "Skohub URL did not return expected concept URI" });
2688
+ }
2689
+ const concept = this._toJskosConcept(data);
2690
+ this._conceptCache[uri] = concept;
2691
+ return concept;
2692
+ } catch (error) {
2693
+ return null;
2694
+ }
2695
+ }
2696
+ _toJskosConcept(data) {
2697
+ const concept = { uri: data.id };
2698
+ concept.prefLabel = data.prefLabel;
2699
+ Object.keys(concept.prefLabel || {}).forEach((key) => {
2700
+ concept.prefLabel[key] = decodeUnicode(concept.prefLabel[key]);
2701
+ });
2702
+ concept.narrower = (data.narrower || []).map((c) => this._toJskosConcept(c));
2703
+ concept.notation = data.notation || [];
2704
+ if (data.broader && data.broader.id) {
2705
+ concept.broader = [{ uri: data.broader.id }];
2706
+ }
2707
+ if (data.inScheme && data.inScheme.id) {
2708
+ concept.inScheme = [{ uri: data.inScheme.id }];
2709
+ }
2710
+ if (data.scopeNote) {
2711
+ concept.scopeNote = data.scopeNote;
2712
+ Object.keys(concept.scopeNote).forEach((key) => {
2713
+ concept.scopeNote[key] = [decodeUnicode(concept.scopeNote[key])];
2714
+ });
2715
+ }
2716
+ return concept;
2717
+ }
2718
+ async getSchemes({ ...config }) {
2719
+ return Promise.all(this.schemes.map((scheme) => this._loadScheme({ ...config, scheme })));
2720
+ }
2721
+ async getTop({ scheme, ...config }) {
2722
+ if (!scheme || !scheme.uri) {
2723
+ throw new InvalidOrMissingParameterError({ parameter: "scheme", message: "Missing scheme URI" });
2724
+ }
2725
+ scheme = await this._loadScheme({ scheme, ...config });
2726
+ return scheme.topConcepts || [];
2727
+ }
2728
+ async getConcepts({ concepts, ...config }) {
2729
+ if (!import_isArray.default(concepts)) {
2730
+ concepts = [concepts];
2731
+ }
2732
+ return (await Promise.all(concepts.map(({ uri }) => this._loadConcept({ ...config, uri })))).filter(Boolean);
2733
+ }
2734
+ async getAncestors({ concept, ...config }) {
2735
+ if (!concept || !concept.uri) {
2736
+ throw new InvalidOrMissingParameterError({ parameter: "concept" });
2737
+ }
2738
+ if (concept.ancestors && concept.ancestors[0] !== null) {
2739
+ return concept.ancestors;
2740
+ }
2741
+ concept = await this._loadConcept({ ...config, uri: concept.uri });
2742
+ if (!concept || !concept.broader || !concept.broader.length) {
2743
+ return [];
2744
+ }
2745
+ const broader = concept.broader[0];
2746
+ return [broader].concat(await this.getAncestors({ concept: broader, ...config })).map((c) => ({ uri: c.uri }));
2747
+ }
2748
+ async getNarrower({ concept, ...config }) {
2749
+ if (!concept || !concept.uri) {
2750
+ throw new InvalidOrMissingParameterError({ parameter: "concept" });
2751
+ }
2752
+ if (concept.narrower && concept.narrower[0] !== null) {
2753
+ return concept.narrower;
2754
+ }
2755
+ concept = await this._loadConcept({ ...config, uri: concept.uri });
2756
+ return concept.narrower;
2757
+ }
2758
+ async search({ search, scheme, limit = 100 }) {
2759
+ if (!scheme || !scheme.uri) {
2760
+ throw new InvalidOrMissingParameterError({ parameter: "scheme" });
2761
+ }
2762
+ if (!search) {
2763
+ throw new InvalidOrMissingParameterError({ parameter: "search" });
2764
+ }
2765
+ let index;
2766
+ if (!this._index[scheme.uri]) {
2767
+ this._index[scheme.uri] = {};
2768
+ }
2769
+ for (const lang of [""].concat(this.languages)) {
2770
+ if (this._index[scheme.uri][lang]) {
2771
+ index = this._index[scheme.uri][lang];
2772
+ break;
2773
+ }
2774
+ if (this._index[scheme.uri][lang] === null) {
2775
+ continue;
2776
+ }
2777
+ try {
2778
+ let postfix = lang ? `.${lang}.index` : ".index";
2779
+ if (scheme.uri.endsWith("/")) {
2780
+ postfix = `index${postfix}`;
2781
+ }
2782
+ const data = await this.axios({ url: `${scheme.uri}${postfix}`, _skipAdditionalParameters: true });
2783
+ if (data.length < 100) {
2784
+ this._index[scheme.uri][lang] = null;
2785
+ continue;
2786
+ }
2787
+ index = import_flexsearch.default.create();
2788
+ index.import(data);
2789
+ this._index[scheme.uri][lang] = index;
2790
+ break;
2791
+ } catch (error) {
2792
+ this._index[scheme.uri][lang] = null;
2793
+ }
2794
+ }
2795
+ if (!index) {
2796
+ throw new InvalidRequestError({ message: "Could not find search index for any of the available languages " + this.languages.join(",") });
2797
+ }
2798
+ const result = index.search(search);
2799
+ const concepts = await this.getConcepts({ concepts: result.map((uri) => ({ uri })) });
2800
+ return concepts.slice(0, limit);
2801
+ }
2802
+ async suggest(config) {
2803
+ config._raw = true;
2804
+ const concepts = await this.search(config);
2805
+ const result = [config.search, [], [], []];
2806
+ for (let concept of concepts) {
2807
+ const notation = import_jskos_tools10.default.notation(concept);
2808
+ const label = import_jskos_tools10.default.prefLabel(concept);
2809
+ result[1].push((notation ? notation + " " : "") + label);
2810
+ result[2].push("");
2811
+ result[3].push(concept.uri);
2812
+ }
2813
+ if (concepts._totalCount != void 0) {
2814
+ result._totalCount = concepts._totalCount;
2815
+ } else {
2816
+ result._totalCount = concepts.length;
2817
+ }
2818
+ return result;
2819
+ }
2820
+ };
2821
+ SkohubProvider.providerName = "Skohub";
2822
+ SkohubProvider.providerType = "http://bartoc.org/api-type/skohub";
2823
+
2589
2824
  // src/lib/CocodaSDK.js
2590
2825
  var providers = {
2591
2826
  [BaseProvider.providerName]: BaseProvider,
@@ -2771,7 +3006,7 @@ var CocodaSDK = class {
2771
3006
  scheme._registry = registry;
2772
3007
  scheme.__DETAILSLOADED__ = 1;
2773
3008
  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;
3009
+ let otherScheme = schemes.find((s) => import_jskos_tools11.default.compare(s, scheme)), prio, otherPrio, override = false;
2775
3010
  if (otherScheme) {
2776
3011
  prio = this.config.registries.indexOf(registry);
2777
3012
  if (prio != -1) {
@@ -2793,19 +3028,19 @@ var CocodaSDK = class {
2793
3028
  }
2794
3029
  if (!otherScheme || override) {
2795
3030
  if (override) {
2796
- let otherSchemeIndex = schemes.findIndex((s) => import_jskos_tools10.default.compare(s, otherScheme));
3031
+ let otherSchemeIndex = schemes.findIndex((s) => import_jskos_tools11.default.compare(s, otherScheme));
2797
3032
  if (otherSchemeIndex != -1) {
2798
3033
  schemes.splice(otherSchemeIndex, 1);
2799
3034
  }
2800
- scheme = import_jskos_tools10.default.merge(scheme, import_omit.default(otherScheme, ["concepts", "topConcepts"]), { mergeUris: true, skipPaths: ["_registry"] });
3035
+ scheme = import_jskos_tools11.default.merge(scheme, import_omit.default(otherScheme, ["concepts", "topConcepts"]), { mergeUris: true, skipPaths: ["_registry"] });
2801
3036
  }
2802
3037
  scheme._registry = registry;
2803
3038
  schemes.push(scheme);
2804
3039
  } else {
2805
- const index = schemes.findIndex((s) => import_jskos_tools10.default.compare(s, scheme));
3040
+ const index = schemes.findIndex((s) => import_jskos_tools11.default.compare(s, scheme));
2806
3041
  if (index != -1) {
2807
3042
  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"] });
3043
+ schemes[index] = import_jskos_tools11.default.merge(schemes[index], import_omit.default(scheme, ["concepts", "topConcepts"]), { mergeUris: true, skipPaths: ["_registry"] });
2809
3044
  schemes[index]._registry = otherSchemeRegistry;
2810
3045
  }
2811
3046
  }
@@ -2827,7 +3062,7 @@ var CocodaSDK = class {
2827
3062
  scheme._registry = newRegistry;
2828
3063
  }
2829
3064
  });
2830
- return import_jskos_tools10.default.sortSchemes(schemes.filter(Boolean));
3065
+ return import_jskos_tools11.default.sortSchemes(schemes.filter(Boolean));
2831
3066
  }
2832
3067
  registryForScheme(scheme) {
2833
3068
  let registry = scheme._registry;
@@ -2838,7 +3073,7 @@ var CocodaSDK = class {
2838
3073
  const url = config.url;
2839
3074
  if (registryCache[url]) {
2840
3075
  const registry2 = registryCache[url];
2841
- if (Array.isArray(registry2._jskos.schemes) && !import_jskos_tools10.default.isContainedIn(scheme, registry2._jskos.schemes)) {
3076
+ if (Array.isArray(registry2._jskos.schemes) && !import_jskos_tools11.default.isContainedIn(scheme, registry2._jskos.schemes)) {
2842
3077
  registry2._jskos.schemes.push(scheme);
2843
3078
  }
2844
3079
  return registry2;
@@ -2885,6 +3120,7 @@ function addAllProviders(_cdk) {
2885
3120
  MappingsApiProvider,
2886
3121
  OccurrencesApiProvider,
2887
3122
  ReconciliationApiProvider,
3123
+ SkohubProvider,
2888
3124
  SkosmosApiProvider,
2889
3125
  addAllProviders,
2890
3126
  cdk,