@kit-data-manager/pid-component
Version:
The PID-Component is a web component that can be used to evaluate and display FAIR Digital Objects, PIDs, ORCiDs, and possibly other identifiers in a user-friendly way. It is easily extensible to support other identifier types.
200 lines (199 loc) • 9.17 kB
JavaScript
/*!
*
* Copyright 2024-2026 Karlsruhe Institute of Technology.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { h } from "@stencil/core";
import { GenericIdentifierType } from "../../utils/GenericIdentifierType";
import { FoldableItem } from "../../utils/FoldableItem";
import { FoldableAction } from "../../utils/FoldableAction";
export class SPDXType extends GenericIdentifierType {
constructor() {
super(...arguments);
this.licenseData = null;
this.licenseId = '';
this.corsFallback = true;
this.corsProxy = 'https://corsproxy.io/?';
this.spdxBaseUrl = 'https://spdx.org/licenses';
this.fileFormat = 'json';
this.requestTimeout = 10000;
}
get data() {
return this.licenseData;
}
getSettingsKey() {
return 'SPDXType';
}
quickCheck() {
if (SPDXType.URL_REGEX.test(this.value))
return true;
if (SPDXType.ID_REGEX.test(this.value))
return undefined;
return false;
}
async hasMeaningfulInformation() {
const licenseId = this.extractLicenseId();
if (!licenseId)
return false;
const url = this.buildLicenseApiUrl(licenseId);
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.requestTimeout);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
if (!response.ok)
return false;
const data = await response.json();
if (data && data.licenseId) {
this.licenseData = data;
this.licenseId = licenseId;
return true;
}
return false;
}
catch (_a) {
return false;
}
}
logLicenseData() {
console.log('Current license data:', {
licenseId: this.licenseId,
licenseData: this.licenseData,
hasData: Boolean(this.licenseData && this.licenseData.licenseId && this.licenseData.name),
});
}
async init() {
if (!this.licenseData) {
const success = await this.hasMeaningfulInformation();
if (!success) {
this.handleInitError(new Error('Failed to fetch SPDX license data'));
return;
}
}
this.populateLicenseData();
this.addActionButtons();
}
renderPreview() {
if (!this.licenseData) {
return h("span", { class: `font-mono text-sm` }, "SPDX: ", this.licenseId || this.value);
}
return (h("span", { class: `inline-flex flex-nowrap items-baseline font-mono min-w-0 max-w-full` }, h("span", { class: `font-medium min-w-0 overflow-hidden text-ellipsis whitespace-nowrap` }, this.licenseData.name || this.licenseId), this.licenseData.licenseId &&
h("span", { class: `flex-none pl-1 text-gray-500` }, "(", this.licenseData.licenseId, ")")));
}
extractLicenseId() {
if (!this.value.includes('/') && !this.value.includes('://')) {
return this.value.trim();
}
return this.value
.replace(/^https?:\/\/spdx\.org\/licenses\//i, '')
.replace(/\/$/, '')
.replace(/\.(json|html)$/i, '');
}
buildLicenseApiUrl(licenseId) {
const baseUrl = this.corsFallback
? `${this.corsProxy}${encodeURIComponent(`${this.spdxBaseUrl}/${licenseId}.${this.fileFormat}`)}`
: `${this.spdxBaseUrl}/${licenseId}.${this.fileFormat}`;
return baseUrl;
}
populateLicenseData() {
if (!this.licenseData)
return;
this.items.push(new FoldableItem(0, 'Full Name', this.licenseData.name, 'The full legal name of the license', undefined, null, false));
this.items.push(new FoldableItem(10, 'SPDX ID', this.licenseData.licenseId, 'The unique SPDX identifier for this license', undefined, null, false));
this.addDeprecationInfo();
this.items.push(new FoldableItem(20, 'OSI Approved', this.licenseData.isOsiApproved ? 'Yes' : 'No', 'Whether the license is approved by the Open Source Initiative', 'https://opensource.org/licenses/', undefined, false));
this.addFsfInfo();
this.addRelatedUrls();
}
addDeprecationInfo() {
if (!this.licenseData || !this.licenseData.isDeprecatedLicenseId)
return;
this.items.push(new FoldableItem(15, 'Deprecated', 'Yes', 'This license ID has been deprecated by SPDX', undefined, null, false));
if (this.licenseData.deprecatedVersion) {
this.items.push(new FoldableItem(16, 'Deprecated Since', this.licenseData.deprecatedVersion, 'The SPDX version when this license was deprecated', undefined, null, false));
}
}
addFsfInfo() {
if (!this.licenseData || this.licenseData.isFsfLibre === undefined)
return;
this.items.push(new FoldableItem(25, 'FSF Free/Libre', this.licenseData.isFsfLibre ? 'Yes' : 'No', 'Whether the license is considered "Free" by the Free Software Foundation', 'https://www.fsf.org/licensing/', undefined, false));
}
addRelatedUrls() {
if (!this.licenseData || !this.licenseData.seeAlso || this.licenseData.seeAlso.length === 0)
return;
for (let i = 0; i < this.licenseData.seeAlso.length; i++) {
const url = this.licenseData.seeAlso[i];
this.items.push(new FoldableItem(30 + i, `Related URL`, url, 'A related URL with more information about this license'));
}
}
addActionButtons() {
if (!this.licenseData)
return;
this.actions.push(new FoldableAction(10, 'View on SPDX', `https://spdx.org/licenses/${this.licenseData.licenseId}`, 'primary'));
if (this.licenseData.isOsiApproved) {
this.actions.push(new FoldableAction(20, 'View on OSI', 'https://opensource.org/licenses/', 'secondary'));
}
this.addOfficialLicenseLink();
}
addOfficialLicenseLink() {
if (!this.licenseData || !this.licenseData.seeAlso || this.licenseData.seeAlso.length === 0)
return;
const officialUrl = this.findOfficialUrl(this.licenseData.seeAlso) || this.licenseData.seeAlso[0];
this.actions.push(new FoldableAction(30, 'View Official License', officialUrl, 'secondary'));
}
findOfficialUrl(urls) {
const allowedHosts = ['opensource.org', 'fsf.org', 'gnu.org', 'apache.org', 'creativecommons.org'];
return urls.find((url) => {
try {
const parsedUrl = new URL(url);
return allowedHosts.includes(parsedUrl.host);
}
catch (_a) {
return false;
}
});
}
handleInitError(error) {
if (error.message && error.message.includes('CORS')) {
this.items.push(new FoldableItem(0, 'Error', `CORS error: Cannot access SPDX API due to cross-origin restrictions. The proxy service may be unavailable.`, 'This is a browser security restriction. Try again later or use a different browser.'));
}
else {
this.items.push(new FoldableItem(0, 'Error', `Failed to fetch data from SPDX API: ${error.message}`));
}
this.addBasicErrorInfo();
this.addNetworkIssueInfo();
this.licenseData = {
licenseId: this.licenseId,
name: this.licenseId,
};
}
addBasicErrorInfo() {
if (this.licenseId) {
this.items.push(new FoldableItem(10, 'License ID', this.licenseId, 'The license identifier that was detected'));
this.actions.push(new FoldableAction(10, 'View on SPDX', `https://spdx.org/licenses/${this.licenseId}`, 'primary'));
}
else {
this.licenseId = this.value.replace(/^https?:\/\/spdx\.org\/licenses\//i, '').replace(/\/$/, '');
this.items.push(new FoldableItem(10, 'Possible License ID', this.licenseId, 'Extracted from the input value'));
this.actions.push(new FoldableAction(10, 'Search on SPDX', 'https://spdx.org/licenses/', 'primary'));
}
}
addNetworkIssueInfo() {
this.items.push(new FoldableItem(20, 'Network Issue', 'The SPDX API could not be reached. This may be due to network connectivity issues or the SPDX service being unavailable.', 'Try again when you have internet connectivity'));
}
}
SPDXType.ID_REGEX = /^[\w.\-+]+$/;
SPDXType.URL_REGEX = /^https?:\/\/spdx\.org\/licenses\/[\w.\-+]+\/?$/i;
//# sourceMappingURL=SPDXType.js.map