cocoda-sdk 3.3.4 → 3.4.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
@@ -1,5 +1,5 @@
1
1
  # Cocoda SDK
2
- ![Node.js CI](https://github.com/gbv/cocoda-sdk/workflows/Node.js%20CI/badge.svg)
2
+ [![Test and build](https://github.com/gbv/cocoda-sdk/actions/workflows/test-and-build.yml/badge.svg)](https://github.com/gbv/cocoda-sdk/actions/workflows/test-and-build.yml)
3
3
  [![GitHub package version](https://img.shields.io/github/package-json/v/gbv/cocoda-sdk.svg?label=version)](https://github.com/gbv/cocoda-sdk)
4
4
  [![NPM package name](https://img.shields.io/badge/npm-cocoda--sdk-blue.svg)](https://www.npmjs.com/package/cocoda-sdk)
5
5
  [![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg)](https://github.com/RichardLitt/standard-readme)
@@ -167,6 +167,8 @@ The following providers are also exported, but have to be added via `cdk.addProv
167
167
  - **This integration is currently experimental and only supports LCSH and LCNAF.**
168
168
  - `Skohub` - access to concept schemes and concepts via a [SkoHub Vocabs](https://blog.lobid.org/2019/09/27/presenting-skohub-vocabs.html)
169
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.**
170
+ - `LobidApi` - access to GND via [lobid](https://lobid.org)
171
+ - **This integration is currently experimental.**
170
172
  - `MyCoRe` - access to vocabularies via [MyCoRe](https://www.mycore.de/)
171
173
  - **This integration is currently experimental. Only one vocabulary per registry is supported. Not recommended for large vocabularies as all of the vocabulary data is loaded and kept in memory.**
172
174
  - `ReconciliationApi` - access to mapping suggestions via a [OpenRefine Reconciliation API](https://github.com/OpenRefine/OpenRefine/wiki/Reconciliation-Service-API)
@@ -33,6 +33,7 @@ __export(src_exports, {
33
33
  CocodaSDK: () => CocodaSDK,
34
34
  ConceptApiProvider: () => ConceptApiProvider,
35
35
  LabelSearchSuggestionProvider: () => LabelSearchSuggestionProvider,
36
+ LobidApiProvider: () => LobidApiProvider,
36
37
  LocApiProvider: () => LocApiProvider,
37
38
  LocalMappingsProvider: () => LocalMappingsProvider,
38
39
  MappingsApiProvider: () => MappingsApiProvider,
@@ -120,7 +121,7 @@ var InvalidProviderError = class extends CDKError {
120
121
  };
121
122
 
122
123
  // src/lib/CocodaSDK.js
123
- var import_axios3 = __toESM(require("axios"), 1);
124
+ var import_axios4 = __toESM(require("axios"), 1);
124
125
 
125
126
  // src/utils/lodash.js
126
127
  var import_get = __toESM(require("lodash/get.js"), 1);
@@ -140,7 +141,7 @@ var import_omit = __toESM(require("lodash/omit.js"), 1);
140
141
  var import_concat = __toESM(require("lodash/concat.js"), 1);
141
142
 
142
143
  // src/lib/CocodaSDK.js
143
- var import_jskos_tools12 = __toESM(require("jskos-tools"), 1);
144
+ var import_jskos_tools13 = __toESM(require("jskos-tools"), 1);
144
145
 
145
146
  // src/providers/index.js
146
147
  var providers_exports = {};
@@ -148,6 +149,7 @@ __export(providers_exports, {
148
149
  BaseProvider: () => BaseProvider,
149
150
  ConceptApiProvider: () => ConceptApiProvider,
150
151
  LabelSearchSuggestionProvider: () => LabelSearchSuggestionProvider,
152
+ LobidApiProvider: () => LobidApiProvider,
151
153
  LocApiProvider: () => LocApiProvider,
152
154
  LocalMappingsProvider: () => LocalMappingsProvider,
153
155
  MappingsApiProvider: () => MappingsApiProvider,
@@ -1122,7 +1124,7 @@ var LocalMappingsProvider = class extends BaseProvider {
1122
1124
  throw new InvalidOrMissingParameterError({ parameter: "mapping", message: "Duplicate URI" });
1123
1125
  }
1124
1126
  if (!mapping.created) {
1125
- mapping.created = new Date().toISOString();
1127
+ mapping.created = (/* @__PURE__ */ new Date()).toISOString();
1126
1128
  }
1127
1129
  if (!mapping.modified) {
1128
1130
  mapping.modified = mapping.created;
@@ -1158,7 +1160,7 @@ var LocalMappingsProvider = class extends BaseProvider {
1158
1160
  if (!mapping.created) {
1159
1161
  mapping.created = localMappings[index].created;
1160
1162
  }
1161
- mapping.modified = new Date().toISOString();
1163
+ mapping.modified = (/* @__PURE__ */ new Date()).toISOString();
1162
1164
  localMappings[index] = mapping;
1163
1165
  localMappings = localMappings.map((mapping2) => import_jskos_tools2.default.minifyMapping(mapping2));
1164
1166
  try {
@@ -1190,7 +1192,7 @@ var LocalMappingsProvider = class extends BaseProvider {
1190
1192
  if (!mapping.created) {
1191
1193
  mapping.created = localMappings[index].created;
1192
1194
  }
1193
- mapping.modified = new Date().toISOString();
1195
+ mapping.modified = (/* @__PURE__ */ new Date()).toISOString();
1194
1196
  localMappings[index] = Object.assign(localMappings[index], mapping);
1195
1197
  localMappings = localMappings.map((mapping2) => import_jskos_tools2.default.minifyMapping(mapping2));
1196
1198
  try {
@@ -3536,8 +3538,243 @@ var SkohubProvider = class extends BaseProvider {
3536
3538
  SkohubProvider.providerName = "Skohub";
3537
3539
  SkohubProvider.providerType = "http://bartoc.org/api-type/skohub";
3538
3540
 
3539
- // src/providers/mycore-provider.js
3541
+ // src/providers/lobid-api-provider.js
3540
3542
  var import_jskos_tools11 = __toESM(require("jskos-tools"), 1);
3543
+ var import_axios3 = __toESM(require("axios"), 1);
3544
+ var gndJson = {
3545
+ uri: "http://bartoc.org/en/node/430",
3546
+ concepts: [
3547
+ null
3548
+ ],
3549
+ topConcepts: [],
3550
+ type: [
3551
+ "http://www.w3.org/2004/02/skos/core#ConceptScheme"
3552
+ ],
3553
+ DISPLAY: {
3554
+ hideNotation: true
3555
+ },
3556
+ identifier: [
3557
+ "http://www.wikidata.org/entity/Q36578"
3558
+ ],
3559
+ license: [
3560
+ {
3561
+ uri: "http://creativecommons.org/publicdomain/zero/1.0/"
3562
+ }
3563
+ ],
3564
+ namespace: "https://d-nb.info/gnd/",
3565
+ notation: [
3566
+ "GND"
3567
+ ],
3568
+ notationPattern: "[0-9X-]+",
3569
+ prefLabel: {
3570
+ de: "Gemeinsame Normdatei",
3571
+ en: "Integrated Authority File"
3572
+ },
3573
+ types: [
3574
+ {
3575
+ uri: "https://d-nb.info/standards/elementset/gnd#DifferentiatedPerson",
3576
+ prefLabel: {
3577
+ de: "Person",
3578
+ en: "Person"
3579
+ }
3580
+ },
3581
+ {
3582
+ uri: "https://d-nb.info/standards/elementset/gnd#PlaceOrGeographicName",
3583
+ prefLabel: {
3584
+ de: "Geografikum",
3585
+ en: "Place"
3586
+ }
3587
+ },
3588
+ {
3589
+ uri: "https://d-nb.info/standards/elementset/gnd#CorporateBody",
3590
+ prefLabel: {
3591
+ de: "Organisation",
3592
+ en: "Organization"
3593
+ }
3594
+ },
3595
+ {
3596
+ uri: "https://d-nb.info/standards/elementset/gnd#SubjectHeading",
3597
+ prefLabel: {
3598
+ de: "Sachbegriff",
3599
+ en: "Subject"
3600
+ }
3601
+ },
3602
+ {
3603
+ uri: "https://d-nb.info/standards/elementset/gnd#Work",
3604
+ prefLabel: {
3605
+ de: "Werk",
3606
+ en: "Work"
3607
+ }
3608
+ },
3609
+ {
3610
+ uri: "https://d-nb.info/standards/elementset/gnd#ConferenceOrEvent",
3611
+ prefLabel: {
3612
+ de: "Konferenz oder Veranstaltung",
3613
+ en: "ConferenceOrEvent"
3614
+ }
3615
+ }
3616
+ ]
3617
+ };
3618
+ var gndTypeScheme = new import_jskos_tools11.default.ConceptScheme({
3619
+ uri: "https://d-nb.info/standards/elementset/gnd",
3620
+ namespace: "https://d-nb.info/standards/elementset/gnd#"
3621
+ });
3622
+ gndJson.types.forEach((type) => {
3623
+ type.notation = [gndTypeScheme.notationFromUri(type.uri)];
3624
+ });
3625
+ var gnd = new import_jskos_tools11.default.ConceptScheme(gndJson);
3626
+ var broaderProps = [
3627
+ "broaderTerm",
3628
+ "broaderTermGeneral",
3629
+ "broaderTermGeneric",
3630
+ "broaderTermInstantial",
3631
+ "broaderTermPartitive"
3632
+ ];
3633
+ function toJSKOS(data3) {
3634
+ const concept = {
3635
+ uri: data3.id,
3636
+ notation: [data3.gndIdentifier],
3637
+ prefLabel: { de: data3.preferredName },
3638
+ inScheme: [{ uri: gndJson.uri }]
3639
+ };
3640
+ if (data3.variantName) {
3641
+ concept.altLabel = { de: data3.variantName };
3642
+ }
3643
+ concept.type = data3.type.map((type) => gndTypeScheme.uriFromNotation(type)).filter(Boolean);
3644
+ concept.broader = [];
3645
+ broaderProps.forEach((prop) => {
3646
+ concept.broader = concept.broader.concat(data3[prop] || []);
3647
+ });
3648
+ concept.broader = concept.broader.map((broader) => ({ uri: broader.id }));
3649
+ concept.identifier = [concept.uri.replace("https://", "http://")];
3650
+ return concept;
3651
+ }
3652
+ function fixURI(uri) {
3653
+ if (uri && uri.startsWith("http://")) {
3654
+ return uri.replace("http://", "https://");
3655
+ }
3656
+ return uri;
3657
+ }
3658
+ var LobidApiProvider = class extends BaseProvider {
3659
+ _prepare() {
3660
+ this.has.schemes = true;
3661
+ this.has.data = true;
3662
+ this.has.concepts = true;
3663
+ this.has.narrower = true;
3664
+ this.has.suggest = true;
3665
+ this.has.search = true;
3666
+ this.has.types = true;
3667
+ listOfCapabilities.filter((c) => !this.has[c]).forEach((c) => {
3668
+ this.has[c] = false;
3669
+ });
3670
+ }
3671
+ /**
3672
+ * Used by `registryForScheme` (see src/lib/CocodaSDK.js) to determine a provider config for a concept schceme.
3673
+ *
3674
+ * @param {Object} options
3675
+ * @param {Object} options.url API URL for server
3676
+ * @returns {Object} provider configuration
3677
+ */
3678
+ static _registryConfigForBartocApiConfig({ url, scheme } = {}) {
3679
+ if (!url || !scheme || !import_jskos_tools11.default.compare(scheme, gndJson) || url !== "https://lobid.org/gnd/api") {
3680
+ return null;
3681
+ }
3682
+ return {
3683
+ api: "https://lobid.org/gnd/",
3684
+ schemes: [gndJson]
3685
+ };
3686
+ }
3687
+ async getSchemes() {
3688
+ return [gndJson];
3689
+ }
3690
+ async getTop() {
3691
+ return [];
3692
+ }
3693
+ async getConcepts({ concepts }) {
3694
+ if (!concepts) {
3695
+ throw new errors.InvalidOrMissingParameterError({ parameter: "concepts" });
3696
+ }
3697
+ if (!Array.isArray(concepts)) {
3698
+ concepts = [concepts];
3699
+ }
3700
+ const notations = concepts.map((concept) => {
3701
+ if (concept?.notation?.[0]) {
3702
+ return concept?.notation?.[0];
3703
+ }
3704
+ return gnd.notationFromUri(fixURI(concept?.uri));
3705
+ }).filter(Boolean);
3706
+ const errors = [];
3707
+ const results = await Promise.all(notations.map(async (notation) => {
3708
+ try {
3709
+ const result = await import_axios3.default.get(`${this._api.api}${notation}.json`);
3710
+ return toJSKOS(result.data);
3711
+ } catch (error) {
3712
+ errors.push(error);
3713
+ }
3714
+ }));
3715
+ if (errors.length === concepts.length) {
3716
+ throw errors[0];
3717
+ }
3718
+ return results.filter(Boolean);
3719
+ }
3720
+ async getNarrower({ concept, limit = 200, offset = 0 }) {
3721
+ if (!concept || !concept.uri) {
3722
+ throw new InvalidOrMissingParameterError({ parameter: "concept" });
3723
+ }
3724
+ const uri = fixURI(concept.uri);
3725
+ const q = broaderProps.map((prop) => `${prop}.id:"${uri}"`).join(" OR ");
3726
+ const result = await import_axios3.default.get(`${this._api.api}search`, {
3727
+ params: {
3728
+ q,
3729
+ format: "json",
3730
+ size: limit,
3731
+ from: offset
3732
+ }
3733
+ });
3734
+ return result.data.member.map((member) => toJSKOS(member));
3735
+ }
3736
+ async suggest(config) {
3737
+ const results = await this._search({ ...config, format: "json:suggest" });
3738
+ return [
3739
+ config.search,
3740
+ results.map((r) => r.label),
3741
+ [],
3742
+ results.map((r) => r.id)
3743
+ ];
3744
+ }
3745
+ async search(config) {
3746
+ const results = await this._search(config);
3747
+ return results.member.map((member) => toJSKOS(member));
3748
+ }
3749
+ async _search({ search, types, limit = 100, offset = 0, format = "json" }) {
3750
+ if (!search) {
3751
+ throw new InvalidOrMissingParameterError({ parameter: "search" });
3752
+ }
3753
+ let filter = "";
3754
+ types = types?.map((type) => gndTypeScheme.notationFromUri(fixURI(type))).filter(Boolean) || [];
3755
+ if (types.length) {
3756
+ filter = types.map((type) => `type:${type}`).join(" OR ");
3757
+ }
3758
+ const results = await import_axios3.default.get(`${this._api.api}search`, { params: {
3759
+ q: search,
3760
+ filter,
3761
+ format,
3762
+ size: limit,
3763
+ from: offset
3764
+ } });
3765
+ return results.data;
3766
+ }
3767
+ async getTypes({ scheme }) {
3768
+ if (!scheme || !import_jskos_tools11.default.compare(scheme, gndJson)) {
3769
+ throw new InvalidOrMissingParameterError({ parameter: "search", message: scheme?.uri ? `scheme ${scheme?.uri} not supported` : "" });
3770
+ }
3771
+ return gndJson.types;
3772
+ }
3773
+ };
3774
+ LobidApiProvider.providerName = "LobidApi";
3775
+
3776
+ // src/providers/mycore-provider.js
3777
+ var import_jskos_tools12 = __toESM(require("jskos-tools"), 1);
3541
3778
  var import_flexsearch2 = __toESM(require("flexsearch"), 1);
3542
3779
  var data2 = {};
3543
3780
  var MyCoReProvider = class extends BaseProvider {
@@ -3686,7 +3923,7 @@ var MyCoReProvider = class extends BaseProvider {
3686
3923
  if (!this._scheme) {
3687
3924
  await this.getSchemes(config);
3688
3925
  }
3689
- if (!import_jskos_tools11.default.compare(scheme, this._scheme)) {
3926
+ if (!import_jskos_tools12.default.compare(scheme, this._scheme)) {
3690
3927
  throw new InvalidOrMissingParameterError({ parameter: "scheme", message: "Requested vocabulary seems to be unsupported by this API." });
3691
3928
  }
3692
3929
  return data2[this._scheme.uri].topConcepts.map(this._removeNarrower);
@@ -3752,7 +3989,7 @@ var MyCoReProvider = class extends BaseProvider {
3752
3989
  if (!this._scheme) {
3753
3990
  await this.getSchemes();
3754
3991
  }
3755
- if (!import_jskos_tools11.default.compare(scheme, this._scheme)) {
3992
+ if (!import_jskos_tools12.default.compare(scheme, this._scheme)) {
3756
3993
  throw new InvalidOrMissingParameterError({ parameter: "scheme", message: "Requested vocabulary seems to be unsupported by this API." });
3757
3994
  }
3758
3995
  const result = await data2[this._scheme.uri].searchIndex.search(search);
@@ -3772,8 +4009,8 @@ var MyCoReProvider = class extends BaseProvider {
3772
4009
  const concepts = await this.search(config);
3773
4010
  const result = [config.search, [], [], []];
3774
4011
  for (let concept of concepts) {
3775
- const notation = import_jskos_tools11.default.notation(concept);
3776
- const label = import_jskos_tools11.default.prefLabel(concept);
4012
+ const notation = import_jskos_tools12.default.notation(concept);
4013
+ const label = import_jskos_tools12.default.prefLabel(concept);
3777
4014
  result[1].push((notation ? notation + " " : "") + label);
3778
4015
  result[2].push("");
3779
4016
  result[3].push(concept.uri);
@@ -3817,7 +4054,7 @@ var CocodaSDK = class {
3817
4054
  */
3818
4055
  constructor(config) {
3819
4056
  this.config = config;
3820
- this.axios = import_axios3.default.create();
4057
+ this.axios = import_axios4.default.create();
3821
4058
  }
3822
4059
  /**
3823
4060
  * Method to set the configuration.
@@ -4073,7 +4310,7 @@ var CocodaSDK = class {
4073
4310
  scheme._registry = registry;
4074
4311
  scheme.__DETAILSLOADED__ = 1;
4075
4312
  scheme.type = scheme.type || ["http://www.w3.org/2004/02/skos/core#ConceptScheme"];
4076
- let otherScheme = schemes.find((s) => import_jskos_tools12.default.compare(s, scheme)), prio, otherPrio, override = false;
4313
+ let otherScheme = schemes.find((s) => import_jskos_tools13.default.compare(s, scheme)), prio, otherPrio, override = false;
4077
4314
  if (otherScheme) {
4078
4315
  prio = this.config.registries.indexOf(registry);
4079
4316
  if (prio != -1) {
@@ -4095,19 +4332,19 @@ var CocodaSDK = class {
4095
4332
  }
4096
4333
  if (!otherScheme || override) {
4097
4334
  if (override) {
4098
- let otherSchemeIndex = schemes.findIndex((s) => import_jskos_tools12.default.compare(s, otherScheme));
4335
+ let otherSchemeIndex = schemes.findIndex((s) => import_jskos_tools13.default.compare(s, otherScheme));
4099
4336
  if (otherSchemeIndex != -1) {
4100
4337
  schemes.splice(otherSchemeIndex, 1);
4101
4338
  }
4102
- scheme = import_jskos_tools12.default.merge(scheme, import_omit.default(otherScheme, ["concepts", "topConcepts"]), { mergeUris: true, skipPaths: ["_registry"] });
4339
+ scheme = import_jskos_tools13.default.merge(scheme, import_omit.default(otherScheme, ["concepts", "topConcepts"]), { mergeUris: true, skipPaths: ["_registry"] });
4103
4340
  }
4104
4341
  scheme._registry = registry;
4105
4342
  schemes.push(scheme);
4106
4343
  } else {
4107
- const index = schemes.findIndex((s) => import_jskos_tools12.default.compare(s, scheme));
4344
+ const index = schemes.findIndex((s) => import_jskos_tools13.default.compare(s, scheme));
4108
4345
  if (index != -1) {
4109
4346
  const otherSchemeRegistry = schemes[index]._registry;
4110
- schemes[index] = import_jskos_tools12.default.merge(schemes[index], import_omit.default(scheme, ["concepts", "topConcepts"]), { mergeUris: true, skipPaths: ["_registry"] });
4347
+ schemes[index] = import_jskos_tools13.default.merge(schemes[index], import_omit.default(scheme, ["concepts", "topConcepts"]), { mergeUris: true, skipPaths: ["_registry"] });
4111
4348
  schemes[index]._registry = otherSchemeRegistry;
4112
4349
  }
4113
4350
  }
@@ -4129,7 +4366,7 @@ var CocodaSDK = class {
4129
4366
  scheme._registry = newRegistry;
4130
4367
  }
4131
4368
  });
4132
- return import_jskos_tools12.default.sortSchemes(schemes.filter(Boolean));
4369
+ return import_jskos_tools13.default.sortSchemes(schemes.filter(Boolean));
4133
4370
  }
4134
4371
  registryForScheme(scheme) {
4135
4372
  let registry = scheme._registry;
@@ -4140,30 +4377,34 @@ var CocodaSDK = class {
4140
4377
  const url = config.url;
4141
4378
  if (registryCache[url]) {
4142
4379
  const registry2 = registryCache[url];
4143
- if (Array.isArray(registry2._jskos.schemes) && !import_jskos_tools12.default.isContainedIn(scheme, registry2._jskos.schemes)) {
4380
+ if (Array.isArray(registry2._jskos.schemes) && !import_jskos_tools13.default.isContainedIn(scheme, registry2._jskos.schemes)) {
4144
4381
  registry2._jskos.schemes.push(scheme);
4145
4382
  }
4146
4383
  return registry2;
4147
4384
  } else {
4148
- const provider = Object.values(providers).find((p) => p.providerType === type);
4149
- if (!provider || !provider._registryConfigForBartocApiConfig) {
4150
- continue;
4151
- }
4152
- const providerName = provider.providerName;
4153
4385
  config.scheme = scheme;
4154
- config = providers[providerName]._registryConfigForBartocApiConfig(config);
4155
- if (!config) {
4156
- continue;
4157
- }
4158
- config.provider = providerName;
4159
- try {
4160
- registry = this.initializeRegistry(config);
4161
- if (registry) {
4162
- registryCache[url] = registry;
4163
- return registry;
4386
+ for (const provider of Object.values(providers)) {
4387
+ if (provider.providerType && provider.providerType !== type) {
4388
+ continue;
4389
+ }
4390
+ if (!provider._registryConfigForBartocApiConfig) {
4391
+ continue;
4392
+ }
4393
+ const providerName = provider.providerName;
4394
+ const registryConfig = providers[providerName]._registryConfigForBartocApiConfig(config);
4395
+ if (!registryConfig) {
4396
+ continue;
4397
+ }
4398
+ registryConfig.provider = providerName;
4399
+ try {
4400
+ registry = this.initializeRegistry(registryConfig);
4401
+ if (registry) {
4402
+ registryCache[url] = registry;
4403
+ return registry;
4404
+ }
4405
+ } catch (error) {
4406
+ continue;
4164
4407
  }
4165
- } catch (error) {
4166
- continue;
4167
4408
  }
4168
4409
  }
4169
4410
  }
@@ -4182,6 +4423,7 @@ function addAllProviders(_cdk) {
4182
4423
  CocodaSDK,
4183
4424
  ConceptApiProvider,
4184
4425
  LabelSearchSuggestionProvider,
4426
+ LobidApiProvider,
4185
4427
  LocApiProvider,
4186
4428
  LocalMappingsProvider,
4187
4429
  MappingsApiProvider,