diff --git a/JustificationSet.ts b/JustificationSet.ts new file mode 100644 index 0000000..a3260c8 --- /dev/null +++ b/JustificationSet.ts @@ -0,0 +1,114 @@ +// @ts-ignore: Import unneccesary for typings, will collate .d.ts files +import type { JustifiedSynonym, Treatment } from "./SynonymGroup.ts"; + +interface Justification { + toString: () => string; + precedingSynonym?: JustifiedSynonym; // eslint-disable-line no-use-before-define +} + +interface TreatmentJustification extends Justification { + treatment: Treatment; +} +type LexicalJustification = Justification; +export type anyJustification = TreatmentJustification | LexicalJustification; + +export class JustificationSet implements AsyncIterable { + private monitor = new EventTarget(); + contents: anyJustification[] = []; + isFinished = false; + isAborted = false; + entries = ((Array.from(this.contents.values()).map((v) => [v, v])) as [ + anyJustification, + anyJustification, + ][]).values; + + constructor(iterable?: Iterable) { + if (iterable) { + for (const el of iterable) { + this.add(el); + } + } + return this; + } + + get size() { + return new Promise((resolve, reject) => { + if (this.isAborted) { + reject(new Error("JustificationSet has been aborted")); + } else if (this.isFinished) { + resolve(this.contents.length); + } else { + const listener = () => { + if (this.isFinished) { + this.monitor.removeEventListener("updated", listener); + resolve(this.contents.length); + } + }; + this.monitor.addEventListener("updated", listener); + } + }); + } + + add(value: anyJustification) { + if ( + this.contents.findIndex((c) => c.toString() === value.toString()) === -1 + ) { + this.contents.push(value); + this.monitor.dispatchEvent(new CustomEvent("updated")); + } + return this; + } + + finish() { + //console.info("%cJustificationSet finished", "color: #69F0AE;"); + this.isFinished = true; + this.monitor.dispatchEvent(new CustomEvent("updated")); + } + + forEachCurrent(cb: (val: anyJustification) => void) { + this.contents.forEach(cb); + } + + first() { + return new Promise((resolve) => { + if (this.contents[0]) { + resolve(this.contents[0]); + } else { + this.monitor.addEventListener("update", () => { + resolve(this.contents[0]); + }); + } + }); + } + + [Symbol.toStringTag] = ""; + [Symbol.asyncIterator]() { + // this.monitor.addEventListener("updated", () => console.log("ARA")); + let returnedSoFar = 0; + return { + next: () => { + return new Promise>( + (resolve, reject) => { + const _ = () => { + if (this.isAborted) { + reject(new Error("JustificationSet has been aborted")); + } else if (returnedSoFar < this.contents.length) { + resolve({ value: this.contents[returnedSoFar++] }); + } else if (this.isFinished) { + resolve({ done: true, value: true }); + } else { + const listener = () => { + console.log("ahgfd"); + this.monitor.removeEventListener("updated", listener); + _(); + }; + this.monitor.addEventListener("updated", listener); + } + }; + _(); + }, + ); + }, + }; + } +} diff --git a/SynonymGroup.ts b/SynonymGroup.ts index f3fef2f..77e1415 100644 --- a/SynonymGroup.ts +++ b/SynonymGroup.ts @@ -1,7 +1,5 @@ -interface Justification { - toString: () => string; - precedingSynonym?: JustifiedSynonym; // eslint-disable-line no-use-before-define -} +// @ts-ignore: Import unneccesary for typings, will collate .d.ts files +import { JustificationSet } from "./JustificationSet.ts"; export type MaterialCitation = { "catalogNumber": string; @@ -24,220 +22,36 @@ export type MaterialCitation = { "httpUri"?: string[]; }; -export type Treatment = { - url: string; - materialCitations: Promise; +export type TreatmentDetails = { + materialCitations: MaterialCitation[]; date?: number; creators?: string; title?: string; }; -interface TreatmentJustification extends Justification { - treatment: Treatment; -} - -type LexicalJustification = Justification; -export type anyJustification = TreatmentJustification | LexicalJustification; - -export type anySyncJustification = { - toString: () => string; - precedingSynonym?: JustifiedSynonym; // eslint-disable-line no-use-before-define - treatment?: Treatment; +export type Treatment = { + url: string; + details: Promise; }; -export class JustificationSet implements AsyncIterable { - private monitor = new EventTarget(); - contents: anyJustification[] = []; - isFinished = false; - isAborted = false; - entries = ((Array.from(this.contents.values()).map((v) => [v, v])) as [ - anyJustification, - anyJustification, - ][]).values; - - constructor(iterable?: Iterable) { - if (iterable) { - for (const el of iterable) { - this.add(el); - } - } - return this; - } - - get size() { - return new Promise((resolve, reject) => { - if (this.isAborted) { - reject(new Error("JustificationSet has been aborted")); - } else if (this.isFinished) { - resolve(this.contents.length); - } else { - const listener = () => { - if (this.isFinished) { - this.monitor.removeEventListener("updated", listener); - resolve(this.contents.length); - } - }; - this.monitor.addEventListener("updated", listener); - } - }); - } - - add(value: anyJustification) { - if ( - this.contents.findIndex((c) => c.toString() === value.toString()) === -1 - ) { - this.contents.push(value); - this.monitor.dispatchEvent(new CustomEvent("updated")); - } - return this; - } - - finish() { - //console.info("%cJustificationSet finished", "color: #69F0AE;"); - this.isFinished = true; - this.monitor.dispatchEvent(new CustomEvent("updated")); - } - - forEachCurrent(cb: (val: anyJustification) => void) { - this.contents.forEach(cb); - } - - first() { - return new Promise((resolve) => { - if (this.contents[0]) { - resolve(this.contents[0]); - } else { - this.monitor.addEventListener("update", () => { - resolve(this.contents[0]); - }); - } - }); - } - - [Symbol.toStringTag] = ""; - [Symbol.asyncIterator]() { - this.monitor.addEventListener("updated", () => console.log("ARA")); - let returnedSoFar = 0; - return { - next: () => { - return new Promise>( - (resolve, reject) => { - const _ = () => { - if (this.isAborted) { - reject(new Error("JustificationSet has been aborted")); - } else if (returnedSoFar < this.contents.length) { - resolve({ value: this.contents[returnedSoFar++] }); - } else if (this.isFinished) { - resolve({ done: true, value: true }); - } else { - const listener = () => { - console.log("ahgfd"); - this.monitor.removeEventListener("updated", listener); - _(); - }; - this.monitor.addEventListener("updated", listener); - } - }; - _(); - }, - ); - }, - }; - } -} - -class TreatmentSet implements AsyncIterable { - private monitor = new EventTarget(); - contents: Treatment[] = []; - isFinished = false; - isAborted = false; - - constructor(iterable?: Iterable) { - if (iterable) { - for (const el of iterable) { - this.add(el); - } - } - return this; - } - - get size() { - return new Promise((resolve, reject) => { - if (this.isAborted) { - reject(new Error("JustificationSet has been aborted")); - } else if (this.isFinished) { - resolve(this.contents.length); - } else { - const listener = () => { - if (this.isFinished) { - this.monitor.removeEventListener("updated", listener); - resolve(this.contents.length); - } - }; - this.monitor.addEventListener("updated", listener); - } - }); - } - - add(value: Treatment) { - if (this.contents.findIndex((c) => c.url === value.url) === -1) { - this.contents.push(value); - this.monitor.dispatchEvent(new CustomEvent("updated")); - } - return this; - } - - finish() { - //console.info("%cTreatmentSet finished", "color: #B9F6CA;"); - this.isFinished = true; - this.monitor.dispatchEvent(new CustomEvent("updated")); - } - - [Symbol.asyncIterator]() { - let returnedSoFar = 0; - return { - next: () => { - return new Promise>((resolve, reject) => { - const _ = () => { - /*console.log( - this.isFinished, - this.isAborted, - returnedSoFar, - "<", - this.contents.length, - );*/ - if (this.isAborted) { - reject(new Error("TreatmentSet has been aborted")); - } else if (returnedSoFar < this.contents.length) { - resolve({ value: this.contents[returnedSoFar++] }); - } else if (this.isFinished) { - //console.info("%cTreatmentSet finished 22", "color: #B9F6CA;"); - resolve({ done: true, value: true }); - } else { - const listener = () => { - this.monitor.removeEventListener("updated", listener); - _(); - }; - this.monitor.addEventListener("updated", listener); - } - }; - _(); - }); - }, - }; - } -} +export type TaxonName = { + uri: string; + treatments: { + aug: Set; + cite: Set; + }; + loading: boolean; +}; type Treatments = { - def: TreatmentSet; - aug: TreatmentSet; - dpr: TreatmentSet; - cite: TreatmentSet; + def: Set; + aug: Set; + dpr: Set; + cite: Set; }; - export type JustifiedSynonym = { taxonConceptUri: string; - taxonNameUri: string; + taxonName: TaxonName; taxonConceptAuthority?: string; justifications: JustificationSet; treatments: Treatments; @@ -251,20 +65,32 @@ async function sleep(ms: number): Promise { return await p; } +type SparqlJson = { + head: { + vars: string[]; + }; + results: { + bindings: { + [key: string]: { type: string; value: string; "xml:lang"?: string }; + }[]; + }; +}; + export class SparqlEndpoint { constructor(private sparqlEnpointUri: string) { } async getSparqlResultSet( query: string, fetchOptions: RequestInit = {}, + _reason = "", ) { fetchOptions.headers = fetchOptions.headers || {}; (fetchOptions.headers as Record)["Accept"] = "application/sparql-results+json"; let retryCount = 0; - // deno-lint-ignore no-explicit-any - const sendRequest = async (): Promise => { + const sendRequest = async (): Promise => { try { + // console.info(`SPARQL ${_reason} (${retryCount + 1})`); const response = await fetch( this.sparqlEnpointUri + "?query=" + encodeURIComponent(query), fetchOptions, @@ -289,28 +115,23 @@ export class SparqlEndpoint { return await sendRequest(); } console.warn("!! Fetch Error:", query, "\n---\n", error); - return {}; // as not to crash code expecting parsed json + throw error; } }; return await sendRequest(); } } -type SparqlJson = { - head: { - vars: string[]; - }; - results: { - bindings: { [key: string]: { type: string; value: string } }[]; - }; -}; - export default class SynonymGroup implements AsyncIterable { justifiedArray: JustifiedSynonym[] = []; monitor = new EventTarget(); isFinished = false; isAborted = false; + // Maps from url to object + treatments: Map = new Map(); + taxonNames: Map = new Map(); + private controller = new AbortController(); constructor( @@ -332,18 +153,121 @@ export default class SynonymGroup implements AsyncIterable { const fetchInit = { signal: this.controller.signal }; + function getTreatmentDetails( + treatmentUri: string, + ): Promise { + const query = ` +PREFIX dc: +PREFIX dwc: +PREFIX trt: +SELECT DISTINCT ?date ?mc ?catalogNumber ?collectionCode ?typeStatus ?countryCode ?stateProvince ?municipality ?county ?locality ?verbatimLocality ?recordedBy ?eventDate ?samplingProtocol ?decimalLatitude ?decimalLongitude ?verbatimElevation ?gbifOccurrenceId ?gbifSpecimenId ?title (group_concat(DISTINCT ?creator;separator="; ") as ?creators) (group_concat(DISTINCT ?httpUri;separator="|") as ?httpUris) +WHERE { +<${treatmentUri}> dc:creator ?creator . +OPTIONAL { <${treatmentUri}> trt:publishedIn/dc:date ?date . } +OPTIONAL { <${treatmentUri}> dc:title ?title } +OPTIONAL { + <${treatmentUri}> dwc:basisOfRecord ?mc . + ?mc dwc:catalogNumber ?catalogNumber . + OPTIONAL { ?mc dwc:collectionCode ?collectionCode . } + OPTIONAL { ?mc dwc:typeStatus ?typeStatus . } + OPTIONAL { ?mc dwc:countryCode ?countryCode . } + OPTIONAL { ?mc dwc:stateProvince ?stateProvince . } + OPTIONAL { ?mc dwc:municipality ?municipality . } + OPTIONAL { ?mc dwc:county ?county . } + OPTIONAL { ?mc dwc:locality ?locality . } + OPTIONAL { ?mc dwc:verbatimLocality ?verbatimLocality . } + OPTIONAL { ?mc dwc:recordedBy ?recordedBy . } + OPTIONAL { ?mc dwc:eventDate ?eventDate . } + OPTIONAL { ?mc dwc:samplingProtocol ?samplingProtocol . } + OPTIONAL { ?mc dwc:decimalLatitude ?decimalLatitude . } + OPTIONAL { ?mc dwc:decimalLongitude ?decimalLongitude . } + OPTIONAL { ?mc dwc:verbatimElevation ?verbatimElevation . } + OPTIONAL { ?mc trt:gbifOccurrenceId ?gbifOccurrenceId . } + OPTIONAL { ?mc trt:gbifSpecimenId ?gbifSpecimenId . } + OPTIONAL { ?mc trt:httpUri ?httpUri . } +} +} +GROUP BY ?date ?mc ?catalogNumber ?collectionCode ?typeStatus ?countryCode ?stateProvince ?municipality ?county ?locality ?verbatimLocality ?recordedBy ?eventDate ?samplingProtocol ?decimalLatitude ?decimalLongitude ?verbatimElevation ?gbifOccurrenceId ?gbifSpecimenId ?title`; + if (fetchInit.signal.aborted) { + return Promise.resolve({ materialCitations: [] }); + } + return sparqlEndpoint.getSparqlResultSet( + query, + fetchInit, + `Treatment Details for ${treatmentUri}`, + ).then( + (json) => { + const result: TreatmentDetails = { + creators: json.results.bindings[0]?.creators?.value, + date: json.results.bindings[0]?.date?.value + ? parseInt(json.results.bindings[0].date.value, 10) + : undefined, + title: json.results.bindings[0]?.title?.value, + materialCitations: [], + }; + json.results.bindings.forEach((t) => { + if (!t.mc || !t.catalogNumber) return; + const mc = { + "catalogNumber": t.catalogNumber.value, + "collectionCode": t.collectionCode?.value || undefined, + "typeStatus": t.typeStatus?.value || undefined, + "countryCode": t.countryCode?.value || undefined, + "stateProvince": t.stateProvince?.value || undefined, + "municipality": t.municipality?.value || undefined, + "county": t.county?.value || undefined, + "locality": t.locality?.value || undefined, + "verbatimLocality": t.verbatimLocality?.value || undefined, + "recordedBy": t.recordedBy?.value || undefined, + "eventDate": t.eventDate?.value || undefined, + "samplingProtocol": t.samplingProtocol?.value || undefined, + "decimalLatitude": t.decimalLatitude?.value || undefined, + "decimalLongitude": t.decimalLongitude?.value || undefined, + "verbatimElevation": t.verbatimElevation?.value || undefined, + "gbifOccurrenceId": t.gbifOccurrenceId?.value || undefined, + "gbifSpecimenId": t.gbifSpecimenId?.value || undefined, + "httpUri": t.httpUris?.value.split("|") || undefined, + }; + result.materialCitations.push(mc); + }); + return result; + }, + (error) => { + console.warn("SPARQL Error: " + error); + return { materialCitations: [] }; + }, + ); + } + + const makeTreatmentSet = (urls?: string[]): Set => { + if (!urls) return new Set(); + return new Set( + urls.filter((url) => !!url).map((url) => { + if (!this.treatments.has(url)) { + this.treatments.set(url, { + url, + details: getTreatmentDetails(url), + }); + } + return this.treatments.get(url) as Treatment; + }), + ); + }; + const build = async () => { - function getStartingPoints( + const getStartingPoints = ( taxonName: string, - ): Promise { - if (fetchInit.signal.aborted) return new Promise((r) => r([])); + ): Promise => { + if (fetchInit.signal.aborted) return Promise.resolve([]); const [genus, species, subspecies] = taxonName.split(" "); // subspecies could also be variety // ignoreRank has no effect when there is a 'subspecies', as this is assumed to be the lowest rank & should thus not be able to return results in another rank - const query = ` + const query = `PREFIX cito: +PREFIX dc: PREFIX dwc: PREFIX treat: -SELECT DISTINCT ?tn ?tc (group_concat(DISTINCT ?auth;separator=" / ") as ?authority) WHERE { +SELECT DISTINCT + ?tn ?tc (group_concat(DISTINCT ?auth; separator=" / ") as ?authority) (group_concat(DISTINCT ?aug;separator="|") as ?augs) (group_concat(DISTINCT ?def;separator="|") as ?defs) (group_concat(DISTINCT ?dpr;separator="|") as ?dprs) (group_concat(DISTINCT ?cite;separator="|") as ?cites) (group_concat(DISTINCT ?trtn;separator="|") as ?trtns) (group_concat(DISTINCT ?citetn;separator="|") as ?citetns) +WHERE { ?tc dwc:genus "${genus}"; treat:hasTaxonName ?tn; ${species ? `dwc:species "${species}";` : ""} @@ -354,31 +278,48 @@ SELECT DISTINCT ?tn ?tc (group_concat(DISTINCT ?auth;separator=" / ") as ?author : `dwc:rank "${species ? "species" : "genus"}";` } a . - ?s ?p ?tc . - OPTIONAL { ?tc dwc:scientificNameAuthorship ?auth } + OPTIONAL { ?tc dwc:scientificNameAuthorship ?auth . } + OPTIONAL { ?aug treat:augmentsTaxonConcept ?tc . } + OPTIONAL { ?def treat:definesTaxonConcept ?tc . } + OPTIONAL { ?dpr treat:deprecates ?tc . } + OPTIONAL { ?cite cito:cites ?tc . } + OPTIONAL { ?trtn treat:treatsTaxonName ?tn . } + OPTIONAL { ?citetn treat:citesTaxonName ?tn . } } GROUP BY ?tn ?tc`; // console.info('%cREQ', 'background: red; font-weight: bold; color: white;', `getStartingPoints('${taxonName}')`) - if (fetchInit.signal.aborted) return new Promise((r) => r([])); - return sparqlEndpoint.getSparqlResultSet(query, fetchInit) + if (fetchInit.signal.aborted) return Promise.resolve([]); + return sparqlEndpoint.getSparqlResultSet( + query, + fetchInit, + "Starting Points", + ) .then( (json: SparqlJson) => json.results.bindings.filter((t) => (t.tc && t.tn)) .map((t) => { + if (!this.taxonNames.has(t.tn.value)) { + this.taxonNames.set(t.tn.value, { + uri: t.tn.value, + loading: true, + treatments: { + aug: makeTreatmentSet(t.trtns?.value.split("|")), + cite: makeTreatmentSet(t.citetns?.value.split("|")), + }, + }); + } return { taxonConceptUri: t.tc.value, - taxonNameUri: t.tn.value, + taxonName: this.taxonNames.get(t.tn.value) as TaxonName, taxonConceptAuthority: t.authority?.value, justifications: new JustificationSet([ - `matches "${genus}${species ? " " + species : ""}${ - subspecies ? " " + subspecies : "" - }"`, + `${t.tc.value} matches "${taxonName}"`, ]), treatments: { - def: new TreatmentSet(), - aug: new TreatmentSet(), - dpr: new TreatmentSet(), - cite: new TreatmentSet(), + def: makeTreatmentSet(t.defs?.value.split("|")), + aug: makeTreatmentSet(t.augs?.value.split("|")), + dpr: makeTreatmentSet(t.dprs?.value.split("|")), + cite: makeTreatmentSet(t.cites?.value.split("|")), }, loading: true, }; @@ -388,102 +329,133 @@ GROUP BY ?tn ?tc`; return []; }, ); - } + }; const synonymFinders = [ /** Get the Synonyms having the same {taxon-name} */ (taxon: JustifiedSynonym): Promise => { - const query = ` + const query = `PREFIX cito: PREFIX dc: PREFIX dwc: PREFIX treat: -SELECT DISTINCT ?tc (group_concat(DISTINCT ?auth;separator=" / ") as ?authority) +SELECT DISTINCT + ?tc (group_concat(DISTINCT ?auth; separator=" / ") as ?authority) (group_concat(DISTINCT ?aug;separator="|") as ?augs) (group_concat(DISTINCT ?def;separator="|") as ?defs) (group_concat(DISTINCT ?dpr;separator="|") as ?dprs) (group_concat(DISTINCT ?cite;separator="|") as ?cites) WHERE { - ?tc treat:hasTaxonName <${taxon.taxonNameUri}> . - OPTIONAL { ?tc dwc:scientificNameAuthorship ?auth } - FILTER (?tc != <${taxon.taxonConceptUri}>) + ?tc treat:hasTaxonName <${taxon.taxonName.uri}> . + OPTIONAL { ?tc dwc:scientificNameAuthorship ?auth . } + OPTIONAL { ?aug treat:augmentsTaxonConcept ?tc . } + OPTIONAL { ?def treat:definesTaxonConcept ?tc . } + OPTIONAL { ?dpr treat:deprecates ?tc . } + OPTIONAL { ?cite cito:cites ?tc . } } GROUP BY ?tc`; // console.info('%cREQ', 'background: red; font-weight: bold; color: white;', `synonymFinder[0]( ${taxon.taxonConceptUri} )`) // Check wether we already expanded this taxon name horizontally - otherwise add - if (expandedTaxonNames.has(taxon.taxonNameUri)) { + if (expandedTaxonNames.has(taxon.taxonName.uri)) { return Promise.resolve([]); } - expandedTaxonNames.add(taxon.taxonNameUri); - if (fetchInit.signal.aborted) return new Promise((r) => r([])); - return sparqlEndpoint.getSparqlResultSet(query, fetchInit).then(( + expandedTaxonNames.add(taxon.taxonName.uri); + if (fetchInit.signal.aborted) return Promise.resolve([]); + return sparqlEndpoint.getSparqlResultSet( + query, + fetchInit, + `Same taxon name ${taxon.taxonConceptUri}`, + ).then(( json: SparqlJson, - ) => - json.results.bindings.filter((t) => t.tc).map((t) => { - return { - taxonConceptUri: t.tc.value, - taxonNameUri: taxon.taxonNameUri, - taxonConceptAuthority: t.authority?.value, - justifications: new JustificationSet([{ - toString: () => - `${t.tc.value} has taxon name ${taxon.taxonNameUri}`, - precedingSynonym: taxon, - }]), - treatments: { - def: new TreatmentSet(), - aug: new TreatmentSet(), - dpr: new TreatmentSet(), - cite: new TreatmentSet(), - }, - loading: true, - }; - }), (error) => { + ) => { + taxon.taxonName.loading = false; + return json.results.bindings.filter((t) => t.tc).map( + (t): JustifiedSynonym => { + return { + taxonConceptUri: t.tc.value, + taxonName: taxon.taxonName, + taxonConceptAuthority: t.authority?.value, + justifications: new JustificationSet([{ + toString: () => + `${t.tc.value} has taxon name ${taxon.taxonName.uri}`, + precedingSynonym: taxon, + }]), + treatments: { + def: makeTreatmentSet(t.defs?.value.split("|")), + aug: makeTreatmentSet(t.augs?.value.split("|")), + dpr: makeTreatmentSet(t.dprs?.value.split("|")), + cite: makeTreatmentSet(t.cites?.value.split("|")), + }, + loading: true, + }; + }, + ); + }, (error) => { console.warn("SPARQL Error: " + error); return []; }); }, /** Get the Synonyms deprecating {taxon} */ (taxon: JustifiedSynonym): Promise => { - const query = ` + const query = `PREFIX cito: PREFIX dc: PREFIX dwc: PREFIX treat: SELECT DISTINCT -?tc ?tn ?treat ?date (group_concat(DISTINCT ?creator;separator="; ") as ?creators) (group_concat(DISTINCT ?auth;separator=" / ") as ?authority) + ?tn ?tc (group_concat(DISTINCT ?auth; separator=" / ") as ?authority) (group_concat(DISTINCT ?justification; separator="|") as ?justs) (group_concat(DISTINCT ?aug;separator="|") as ?augs) (group_concat(DISTINCT ?def;separator="|") as ?defs) (group_concat(DISTINCT ?dpr;separator="|") as ?dprs) (group_concat(DISTINCT ?cite;separator="|") as ?cites) (group_concat(DISTINCT ?trtn;separator="|") as ?trtns) (group_concat(DISTINCT ?citetn;separator="|") as ?citetns) WHERE { - ?treat treat:deprecates <${taxon.taxonConceptUri}> ; - (treat:augmentsTaxonConcept|treat:definesTaxonConcept) ?tc ; - dc:creator ?creator . + ?justification treat:deprecates <${taxon.taxonConceptUri}> ; + (treat:augmentsTaxonConcept|treat:definesTaxonConcept) ?tc . ?tc ?tn . - OPTIONAL { - ?treat treat:publishedIn ?publ . - ?publ dc:date ?date . - } - OPTIONAL { ?tc dwc:scientificNameAuthorship ?auth } + OPTIONAL { ?tc dwc:scientificNameAuthorship ?auth . } + OPTIONAL { ?aug treat:augmentsTaxonConcept ?tc . } + OPTIONAL { ?def treat:definesTaxonConcept ?tc . } + OPTIONAL { ?dpr treat:deprecates ?tc . } + OPTIONAL { ?cite cito:cites ?tc . } + OPTIONAL { ?trtn treat:treatsTaxonName ?tn . } + OPTIONAL { ?citetn treat:citesTaxonName ?tn . } } -GROUP BY ?tc ?tn ?treat ?date`; +GROUP BY ?tn ?tc`; // console.info('%cREQ', 'background: red; font-weight: bold; color: white;', `synonymFinder[1]( ${taxon.taxonConceptUri} )`) - - if (fetchInit.signal.aborted) return new Promise((r) => r([])); - return sparqlEndpoint.getSparqlResultSet(query, fetchInit).then(( + if (fetchInit.signal.aborted) return Promise.resolve([]); + return sparqlEndpoint.getSparqlResultSet( + query, + fetchInit, + `Deprecating ${taxon.taxonConceptUri}`, + ).then(( json: SparqlJson, ) => json.results.bindings.filter((t) => t.tc).map((t) => { + if (!this.taxonNames.has(t.tn.value)) { + this.taxonNames.set(t.tn.value, { + uri: t.tn.value, + loading: true, + treatments: { + aug: makeTreatmentSet(t.trtns?.value.split("|")), + cite: makeTreatmentSet(t.citetns?.value.split("|")), + }, + }); + } return { taxonConceptUri: t.tc.value, - taxonNameUri: t.tn.value, + taxonName: this.taxonNames.get(t.tn.value) as TaxonName, taxonConceptAuthority: t.authority?.value, - justifications: new JustificationSet([{ - toString: () => - `${t.tc.value} deprecates ${taxon.taxonConceptUri} according to ${t.treat.value}`, - precedingSynonym: taxon, - treatment: { - url: t.treat.value, - creators: t.creators.value, - date: t.date ? parseInt(t.date.value, 10) : undefined, - materialCitations: getMaterialCitations(t.treat.value), - }, - }]), + justifications: new JustificationSet( + t.justs?.value.split("|").map((url) => { + if (!this.treatments.has(url)) { + this.treatments.set(url, { + url, + details: getTreatmentDetails(url), + }); + } + return { + toString: () => + `${t.tc.value} deprecates ${taxon.taxonConceptUri} according to ${url}`, + precedingSynonym: taxon, + treatment: this.treatments.get(url), + }; + }), + ), treatments: { - def: new TreatmentSet(), - aug: new TreatmentSet(), - dpr: new TreatmentSet(), - cite: new TreatmentSet(), + def: makeTreatmentSet(t.defs?.value.split("|")), + aug: makeTreatmentSet(t.augs?.value.split("|")), + dpr: makeTreatmentSet(t.dprs?.value.split("|")), + cite: makeTreatmentSet(t.cites?.value.split("|")), } as Treatments, loading: true, }; @@ -494,50 +466,70 @@ GROUP BY ?tc ?tn ?treat ?date`; }, /** Get the Synonyms deprecated by {taxon} */ (taxon: JustifiedSynonym): Promise => { - const query = ` + const query = `PREFIX cito: PREFIX dc: PREFIX dwc: PREFIX treat: SELECT DISTINCT -?tc ?tn ?treat ?date (group_concat(DISTINCT ?creator;separator="; ") as ?creators) (group_concat(DISTINCT ?auth;separator=" / ") as ?authority) + ?tn ?tc (group_concat(DISTINCT ?auth; separator=" / ") as ?authority) (group_concat(DISTINCT ?justification; separator="|") as ?justs) (group_concat(DISTINCT ?aug;separator="|") as ?augs) (group_concat(DISTINCT ?def;separator="|") as ?defs) (group_concat(DISTINCT ?dpr;separator="|") as ?dprs) (group_concat(DISTINCT ?cite;separator="|") as ?cites) (group_concat(DISTINCT ?trtn;separator="|") as ?trtns) (group_concat(DISTINCT ?citetn;separator="|") as ?citetns) WHERE { - ?treat (treat:augmentsTaxonConcept|treat:definesTaxonConcept) <${taxon.taxonConceptUri}> ; - treat:deprecates ?tc ; - dc:creator ?creator . + ?justification (treat:augmentsTaxonConcept|treat:definesTaxonConcept) <${taxon.taxonConceptUri}> ; + treat:deprecates ?tc . ?tc ?tn . - OPTIONAL { - ?treat treat:publishedIn ?publ . - ?publ dc:date ?date . - } - OPTIONAL { ?tc dwc:scientificNameAuthorship ?auth } + OPTIONAL { ?tc dwc:scientificNameAuthorship ?auth . } + OPTIONAL { ?aug treat:augmentsTaxonConcept ?tc . } + OPTIONAL { ?def treat:definesTaxonConcept ?tc . } + OPTIONAL { ?dpr treat:deprecates ?tc . } + OPTIONAL { ?cite cito:cites ?tc . } + OPTIONAL { ?trtn treat:treatsTaxonName ?tn . } + OPTIONAL { ?citetn treat:citesTaxonName ?tn . } } -GROUP BY ?tc ?tn ?treat ?date`; +GROUP BY ?tn ?tc`; // console.info('%cREQ', 'background: red; font-weight: bold; color: white;', `synonymFinder[2]( ${taxon.taxonConceptUri} )`) - if (fetchInit.signal.aborted) return new Promise((r) => r([])); - return sparqlEndpoint.getSparqlResultSet(query, fetchInit).then(( + if (fetchInit.signal.aborted) return Promise.resolve([]); + return sparqlEndpoint.getSparqlResultSet( + query, + fetchInit, + `Deprecated by ${taxon.taxonConceptUri}`, + ).then(( json: SparqlJson, ) => json.results.bindings.filter((t) => t.tc).map((t) => { + if (!this.taxonNames.has(t.tn.value)) { + this.taxonNames.set(t.tn.value, { + uri: t.tn.value, + loading: true, + treatments: { + aug: makeTreatmentSet(t.trtns?.value.split("|")), + cite: makeTreatmentSet(t.citetns?.value.split("|")), + }, + }); + } return { taxonConceptUri: t.tc.value, - taxonNameUri: t.tn.value, + taxonName: this.taxonNames.get(t.tn.value) as TaxonName, taxonConceptAuthority: t.authority?.value, - justifications: new JustificationSet([{ - toString: () => - `${t.tc.value} deprecated by ${taxon.taxonConceptUri} according to ${t.treat.value}`, - precedingSynonym: taxon, - treatment: { - url: t.treat.value, - creators: t.creators.value, - date: t.date ? parseInt(t.date.value, 10) : undefined, - materialCitations: getMaterialCitations(t.treat.value), - }, - }]), + justifications: new JustificationSet( + t.justs?.value.split("|").map((url) => { + if (!this.treatments.has(url)) { + this.treatments.set(url, { + url, + details: getTreatmentDetails(url), + }); + } + return { + toString: () => + `${t.tc.value} deprecates ${taxon.taxonConceptUri} according to ${url}`, + precedingSynonym: taxon, + treatment: this.treatments.get(url), + }; + }), + ), treatments: { - def: new TreatmentSet(), - aug: new TreatmentSet(), - dpr: new TreatmentSet(), - cite: new TreatmentSet(), + def: makeTreatmentSet(t.defs?.value.split("|")), + aug: makeTreatmentSet(t.augs?.value.split("|")), + dpr: makeTreatmentSet(t.dprs?.value.split("|")), + cite: makeTreatmentSet(t.cites?.value.split("|")), } as Treatments, loading: true, }; @@ -558,140 +550,15 @@ GROUP BY ?tc ?tn ?treat ?date`; return foundGroups.reduce((a, b) => a.concat(b), []); } - function getTreatments( - uri: string, - treatments: Treatments, - ): Promise { - const treat = "http://plazi.org/vocab/treatment#"; - const query = `PREFIX treat: <${treat}> - PREFIX dc: - PREFIX dwc: - PREFIX cito: - SELECT DISTINCT ?treat ?how ?date ?title (group_concat(DISTINCT ?c;separator="; ") as ?creators) - WHERE { - ?treat (treat:definesTaxonConcept|treat:augmentsTaxonConcept|treat:deprecates|cito:cites) <${uri}> ; - ?how <${uri}> ; - dc:creator ?c . - OPTIONAL { ?treat dc:title ?title } - OPTIONAL { - ?treat treat:publishedIn ?pub . - ?pub dc:date ?date . - } - } - GROUP BY ?treat ?how ?date ?title`; - // console.info('%cREQ', 'background: red; font-weight: bold; color: white;', `getTreatments('${uri}')`) - - if (fetchInit.signal.aborted) return new Promise((r) => r()); - return sparqlEndpoint.getSparqlResultSet(query, fetchInit).then( - (json: SparqlJson) => { - json.results.bindings.forEach((t) => { - if (!t.treat) return; - const treatment: Treatment = { - url: t.treat.value, - date: t.date ? parseInt(t.date.value, 10) : undefined, - creators: t.creators.value, - materialCitations: getMaterialCitations(t.treat.value), - title: t.title?.value, - }; - switch (t.how.value) { - case treat + "definesTaxonConcept": - treatments.def.add(treatment); - break; - case treat + "augmentsTaxonConcept": - treatments.aug.add(treatment); - break; - case treat + "deprecates": - treatments.dpr.add(treatment); - break; - case "http://purl.org/spar/cito/cites": - treatments.cite.add(treatment); - break; - } - }); - }, - (error) => console.warn("SPARQL Error: " + error), - ); - } - - function getMaterialCitations(uri: string): Promise { - const query = ` -PREFIX dwc: -PREFIX trt: -SELECT DISTINCT ?mc ?catalogNumber ?collectionCode ?typeStatus ?countryCode ?stateProvince ?municipality ?county ?locality ?verbatimLocality ?recordedBy ?eventDate ?samplingProtocol ?decimalLatitude ?decimalLongitude ?verbatimElevation ?gbifOccurrenceId ?gbifSpecimenId (group_concat(DISTINCT ?httpUri;separator="|") as ?httpUris) -WHERE { - <${uri}> dwc:basisOfRecord ?mc . - ?mc dwc:catalogNumber ?catalogNumber . - OPTIONAL { ?mc dwc:collectionCode ?collectionCode . } - OPTIONAL { ?mc dwc:typeStatus ?typeStatus . } - OPTIONAL { ?mc dwc:countryCode ?countryCode . } - OPTIONAL { ?mc dwc:stateProvince ?stateProvince . } - OPTIONAL { ?mc dwc:municipality ?municipality . } - OPTIONAL { ?mc dwc:county ?county . } - OPTIONAL { ?mc dwc:locality ?locality . } - OPTIONAL { ?mc dwc:verbatimLocality ?verbatimLocality . } - OPTIONAL { ?mc dwc:recordedBy ?recordedBy . } - OPTIONAL { ?mc dwc:eventDate ?eventDate . } - OPTIONAL { ?mc dwc:samplingProtocol ?samplingProtocol . } - OPTIONAL { ?mc dwc:decimalLatitude ?decimalLatitude . } - OPTIONAL { ?mc dwc:decimalLongitude ?decimalLongitude . } - OPTIONAL { ?mc dwc:verbatimElevation ?verbatimElevation . } - OPTIONAL { ?mc trt:gbifOccurrenceId ?gbifOccurrenceId . } - OPTIONAL { ?mc trt:gbifSpecimenId ?gbifSpecimenId . } - OPTIONAL { ?mc trt:httpUri ?httpUri . } -} -GROUP BY ?mc ?catalogNumber ?collectionCode ?typeStatus ?countryCode ?stateProvince ?municipality ?county ?locality ?verbatimLocality ?recordedBy ?eventDate ?samplingProtocol ?decimalLatitude ?decimalLongitude ?verbatimElevation ?gbifOccurrenceId ?gbifSpecimenId`; - if (fetchInit.signal.aborted) return new Promise((r) => r([])); - return sparqlEndpoint.getSparqlResultSet(query, fetchInit).then( - (json: SparqlJson) => { - const resultArray: MaterialCitation[] = []; - json.results.bindings.forEach((t) => { - if (!t.mc || !t.catalogNumber) return; - const result = { - "catalogNumber": t.catalogNumber.value, - "collectionCode": t.collectionCode?.value || undefined, - "typeStatus": t.typeStatus?.value || undefined, - "countryCode": t.countryCode?.value || undefined, - "stateProvince": t.stateProvince?.value || undefined, - "municipality": t.municipality?.value || undefined, - "county": t.county?.value || undefined, - "locality": t.locality?.value || undefined, - "verbatimLocality": t.verbatimLocality?.value || undefined, - "recordedBy": t.recordedBy?.value || undefined, - "eventDate": t.eventDate?.value || undefined, - "samplingProtocol": t.samplingProtocol?.value || undefined, - "decimalLatitude": t.decimalLatitude?.value || undefined, - "decimalLongitude": t.decimalLongitude?.value || undefined, - "verbatimElevation": t.verbatimElevation?.value || undefined, - "gbifOccurrenceId": t.gbifOccurrenceId?.value || undefined, - "gbifSpecimenId": t.gbifSpecimenId?.value || undefined, - "httpUri": t.httpUris?.value.split("|") || undefined, - }; - resultArray.push(result); - }); - return resultArray; - }, - (error) => { - console.warn("SPARQL Error: " + error); - return []; - }, - ); - } - const finish = (justsyn: JustifiedSynonym) => { justsyn.justifications.finish(); - getTreatments(justsyn.taxonConceptUri, justsyn.treatments).then(() => { - justsyn.treatments.def.finish(); - justsyn.treatments.aug.finish(); - justsyn.treatments.dpr.finish(); - justsyn.treatments.cite.finish(); - justsyn.loading = false; - }); + justsyn.loading = false; }; let justifiedSynsToExpand: JustifiedSynonym[] = await getStartingPoints( taxonName, ); - await justifiedSynsToExpand.forEach((justsyn) => { + justifiedSynsToExpand.forEach((justsyn) => { finish(justsyn); justifiedSynonyms.set( justsyn.taxonConceptUri, @@ -699,11 +566,14 @@ GROUP BY ?mc ?catalogNumber ?collectionCode ?typeStatus ?countryCode ?stateProvi ); resolver(justsyn); }); - const expandedTaxonConcepts: string[] = []; + const expandedTaxonConcepts: Set = new Set(); while (justifiedSynsToExpand.length > 0) { const foundThisRound: string[] = []; - const promises = justifiedSynsToExpand.map((j, index) => - lookUpRound(j).then((newSynonyms) => { + const promises = justifiedSynsToExpand.map( + async (j): Promise => { + if (expandedTaxonConcepts.has(j.taxonConceptUri)) return false; + expandedTaxonConcepts.add(j.taxonConceptUri); + const newSynonyms = await lookUpRound(j); newSynonyms.forEach((justsyn) => { // Check whether we know about this synonym already if (justifiedSynonyms.has(justsyn.taxonConceptUri)) { @@ -724,14 +594,13 @@ GROUP BY ?mc ?catalogNumber ?collectionCode ?typeStatus ?countryCode ?stateProvi ); resolver(justsyn); } - if (!~expandedTaxonConcepts.indexOf(justsyn.taxonConceptUri)) { + if (!expandedTaxonConcepts.has(justsyn.taxonConceptUri)) { justifiedSynsToExpand.push(justsyn); foundThisRound.push(justsyn.taxonConceptUri); } }); - expandedTaxonConcepts.push(j.taxonConceptUri); return true; - }) + }, ); justifiedSynsToExpand = []; await Promise.allSettled(promises); diff --git a/index.html b/index.html index 6476ea7..c220139 100644 --- a/index.html +++ b/index.html @@ -42,32 +42,32 @@ } })(); (async () => { - for await (const treatment of synonym.treatments.aug) { + for (const treatment of synonym.treatments.aug) { const treatElem = document.createElement("li"); treatElem.innerHTML = `Aug. by: ${treatment.url}` treatListElem.appendChild(treatElem) - if ((await treatment.materialCitations).length > 0) { - treatElem.append(" · Cites: ", (await treatment.materialCitations).map(mc => mc.catalogNumber).join(", ")) + if ((await treatment.details).materialCitations?.length > 0) { + treatElem.append(" · Cites: ", (await treatment.details).materialCitations.map(mc => mc.catalogNumber).join(", ")) } } })(); (async () => { - for await (const treatment of synonym.treatments.def) { + for (const treatment of synonym.treatments.def) { const treatElem = document.createElement("li"); treatElem.innerHTML = `Def. by: ${treatment.url}` treatListElem.appendChild(treatElem) - if ((await treatment.materialCitations).length > 0) { - treatElem.append(" · Cites: ", (await treatment.materialCitations).map(mc => mc.catalogNumber).join(", ")) + if ((await treatment.details).materialCitations.length > 0) { + treatElem.append(" · Cites: ", (await treatment.details).materialCitations.map(mc => mc.catalogNumber).join(", ")) } } })(); (async () => { - for await (const treatment of synonym.treatments.dpr) { + for (const treatment of synonym.treatments.dpr) { const treatElem = document.createElement("li"); treatElem.innerHTML = `Dpr. by: ${treatment.url}` treatListElem.appendChild(treatElem) - if ((await treatment.materialCitations).length > 0) { - treatElem.append(" · Cites: ", (await treatment.materialCitations).map(mc => mc.catalogNumber).join(", ")) + if ((await treatment.details).materialCitations.length > 0) { + treatElem.append(" · Cites: ", (await treatment.details).materialCitations.map(mc => mc.catalogNumber).join(", ")) } } })(); diff --git a/main.ts b/main.ts index e895fd2..c81ed73 100644 --- a/main.ts +++ b/main.ts @@ -1,7 +1,11 @@ /** Command line tool that returns all information as it becomes available */ -import SynoGroup, { JustifiedSynonym, SparqlEndpoint } from "./SynonymGroup.ts"; -import * as Colors from "https://deno.land/std/fmt/colors.ts"; +import SynoGroup, { + JustifiedSynonym, + SparqlEndpoint, + TaxonName, +} from "./SynonymGroup.ts"; +import * as Colors from "https://deno.land/std@0.214.0/fmt/colors.ts"; const sparqlEndpoint = new SparqlEndpoint( "https://treatment.ld.plazi.org/sparql", @@ -19,96 +23,147 @@ try { ` * Found synonym: ${tcName(synonym)} <${synonym.taxonConceptUri}>`, ), ); + console.log( + Colors.blue(` ... with taxon name: ${tnName(synonym.taxonName)} <${synonym.taxonName.uri}>`), + ); + for (const treatment of synonym.taxonName.treatments.aug) { + console.log( + Colors.gray( + ` - Found treatment for ${ + tnName(synonym.taxonName) + }: ${treatment.url}`, + ), + ); + treatment.details.then((details) => { + if (details.materialCitations.length) { + console.log( + Colors.gray( + ` - Found MCS for ${treatment.url}: ${ + details.materialCitations.map((mc) => mc.catalogNumber).join( + ", ", + ) + }`, + ), + ); + } + }); + } + for (const treatment of synonym.taxonName.treatments.cite) { + console.log( + Colors.gray( + ` - Found treatment citing ${ + tnName(synonym.taxonName) + }: ${treatment.url}`, + ), + ); + treatment.details.then((details) => { + if (details.materialCitations.length) { + console.log( + Colors.gray( + ` - Found MCS for ${treatment.url}: ${ + details.materialCitations.map((mc) => mc.catalogNumber).join( + ", ", + ) + }`, + ), + ); + } + }); + } - (async () => { - for await (const justification of synonym.justifications) { - console.log( - Colors.magenta( - ` - Found justification for ${tcName(synonym)}: ${justification}`, - ), - ); - } - })(); - (async () => { - for await (const treatment of synonym.treatments!.aug) { - console.log( - Colors.gray( - ` - Found augmenting treatment for ${ - tcName(synonym) - }: ${treatment.url}`, - ), - ); - treatment.materialCitations.then((mcs) => { + for await (const justification of synonym.justifications) { + console.log( + Colors.magenta( + ` - Found justification for ${tcName(synonym)}: ${justification}`, + ), + ); + } + for (const treatment of synonym.treatments!.aug) { + console.log( + Colors.gray( + ` - Found augmenting treatment for ${ + tcName(synonym) + }: ${treatment.url}`, + ), + ); + treatment.details.then((details) => { + if (details.materialCitations.length) { console.log( Colors.gray( ` - Found MCS for ${treatment.url}: ${ - mcs.map((mc) => mc.catalogNumber).join(", ") + details.materialCitations.map((mc) => mc.catalogNumber).join( + ", ", + ) }`, ), ); - }); - } - })(); - (async () => { - for await (const treatment of synonym.treatments.def) { - console.log( - Colors.gray( - ` - Found defining treatment for ${ - tcName(synonym) - }: ${treatment.url}`, - ), - ); - treatment.materialCitations.then((mcs) => { + } + }); + } + for (const treatment of synonym.treatments.def) { + console.log( + Colors.gray( + ` - Found defining treatment for ${ + tcName(synonym) + }: ${treatment.url}`, + ), + ); + treatment.details.then((details) => { + if (details.materialCitations.length) { console.log( Colors.gray( ` - Found MCS for ${treatment.url}: ${ - mcs.map((mc) => mc.catalogNumber).join(", ") + details.materialCitations.map((mc) => mc.catalogNumber).join( + ", ", + ) }`, ), ); - }); - } - })(); - (async () => { - for await (const treatment of synonym.treatments.dpr) { - console.log( - Colors.gray( - ` - Found deprecating treatment for ${ - tcName(synonym) - }: ${treatment.url}`, - ), - ); - treatment.materialCitations.then((mcs) => { + } + }); + } + for (const treatment of synonym.treatments.dpr) { + console.log( + Colors.gray( + ` - Found deprecating treatment for ${ + tcName(synonym) + }: ${treatment.url}`, + ), + ); + treatment.details.then((details) => { + if (details.materialCitations.length) { console.log( Colors.gray( ` - Found MCS for ${treatment.url}: ${ - mcs.map((mc) => mc.catalogNumber).join(", ") + details.materialCitations.map((mc) => mc.catalogNumber).join( + ", ", + ) }`, ), ); - }); - } - })(); - (async () => { - for await (const treatment of synonym.treatments.cite) { - console.log( - Colors.gray( - ` - Found treatment citing ${ - tcName(synonym) - }: ${treatment.url}`, - ), - ); - treatment.materialCitations.then((mcs) => { + } + }); + } + for (const treatment of synonym.treatments.cite) { + console.log( + Colors.gray( + ` - Found treatment citing ${tcName(synonym)}: ${treatment.url}`, + ), + ); + treatment.details.then((details) => { + if (details.materialCitations.length) { console.log( Colors.gray( ` - Found MCS for ${treatment.url}: ${ - mcs.map((mc) => mc.catalogNumber).join(", ") + details.materialCitations.map((mc) => mc.catalogNumber).join( + ", ", + ) }`, ), ); - }); - } - })(); + } + }); + } } } catch (error) { console.error(Colors.red(error + "")); @@ -116,7 +171,7 @@ try { function tcName(synonym: JustifiedSynonym) { if (synonym.taxonConceptAuthority) { - const name = synonym.taxonNameUri.replace( + const name = synonym.taxonName.uri.replace( "http://taxon-name.plazi.org/id/", "", ); @@ -128,3 +183,11 @@ function tcName(synonym: JustifiedSynonym) { ); return suffix.replaceAll("_", " "); } + +function tnName(taxonName: TaxonName) { + const name = taxonName.uri.replace( + "http://taxon-name.plazi.org/id/", + "", + ); + return name.replaceAll("_", " "); +} diff --git a/npm-package/package.json b/npm-package/package.json index 3d41301..16633ef 100644 --- a/npm-package/package.json +++ b/npm-package/package.json @@ -1,10 +1,10 @@ { "name": "@factsmission/synogroup", - "version": "1.1.0", + "version": "2.0.0", "description": "", "main": "index.js", "scripts": { - "make-package": "deno bundle ../SynonymGroup.ts index.js && ./node_modules/typescript/bin/tsc && mv SynonymGroup.d.ts index.d.ts", + "make-package": "deno bundle ../SynonymGroup.ts index.js && ./node_modules/typescript/bin/tsc && mv JustificationSet.d.ts index.d.ts && cat SynonymGroup.d.ts >> index.d.ts && rm SynonymGroup.d.ts", "publish-package": "npm run make-package && npm publish --access public" }, "repository": { @@ -18,6 +18,6 @@ }, "homepage": "https://github.com/factsmission/synogroup#readme", "devDependencies": { - "typescript": "^4.4.2" + "typescript": "^4.9.5" } } diff --git a/npm-package/readme.md b/npm-package/readme.md index 2e20530..f141c95 100644 --- a/npm-package/readme.md +++ b/npm-package/readme.md @@ -13,6 +13,8 @@ npm version patch # or ensure that the version number differs from the last publ npm run publish-package # generates and publishes npm package ``` +**Note that the generated types are currently slightly broken, manually remove `import`s from `index.d.ts` before publishing** + ## Testing (-ish) ```bash diff --git a/npm-package/tsconfig.json b/npm-package/tsconfig.json index c195f58..e7da24a 100644 --- a/npm-package/tsconfig.json +++ b/npm-package/tsconfig.json @@ -1,8 +1,12 @@ { - "include": ["../SynonymGroup.ts"], + "include": [ + "../SynonymGroup.ts", + "../JustificationSet.ts" + ], "compilerOptions": { "declaration": true, "emitDeclarationOnly": true, + "removeComments": false, "outDir": ".", "lib": [ "esnext",