import { asUrl, getSolidDataset, getThingAll, getUrlAll } from '@inrupt/solid-client'; import { getTs } from './getTs'; import { getJsonLdParser } from './parsers/jsonld'; import { getRdfaParser } from './parsers/rdfa'; import { getRdfxmlParser } from './parsers/rdfxml'; import { getTurtleParser } from './parsers/turtle'; export type Mirrors = {[namespace: string]: string}; export async function generateNamespaceTs( namespace: string, options = { mirrors: {} as Mirrors }, ) { const entityTypes = { Property: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property', Class: 'http://www.w3.org/2000/01/rdf-schema#Class', Datatype: 'http://www.w3.org/2000/01/rdf-schema#Datatype', OwlClass: 'http://www.w3.org/2002/07/owl#Class', OwlObjectProperty: 'http://www.w3.org/2002/07/owl#ObjectProperty', OwlDatatypeProperty: 'http://www.w3.org/2002/07/owl#DatatypeProperty', OwlAnnotationProperty: 'http://www.w3.org/2002/07/owl#AnnotationProperty', OwlFunctionalProperty: 'http://www.w3.org/2002/07/owl#FunctionalProperty', OwlSymmetricProperty: 'http://www.w3.org/2002/07/owl#SymmetricProperty', OwlTransitiveProperty: 'http://www.w3.org/2002/07/owl#TransitiveProperty', HydraResource: 'http://www.w3.org/ns/hydra/core#Resource', HydraClass: 'http://www.w3.org/ns/hydra/core#Class', HydraLink: 'http://www.w3.org/ns/hydra/core#Link', HydraTemplatedLink: 'http://www.w3.org/ns/hydra/core#TemplatedLink', HydraVariableRepresentation: 'http://www.w3.org/ns/hydra/core#VariableRepresentation', }; const schemaLocation = options.mirrors[namespace] || namespace; const schemaDoc = await fetchWithRetries(schemaLocation); const things = getThingAll(schemaDoc); const entityTypeUrls = Object.values(entityTypes); const entities = things.filter(thing => { const thingTypes = getUrlAll(thing, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'); return thingTypes.some(thingType => entityTypeUrls.includes(thingType)); }); const typeAliases = Object.keys(entityTypes).map(alias => `type ${alias} = string;`).join('\n'); const entityTs = entities .filter(entity => { const entityName = asUrl(entity, namespace).substring(namespace.length); return ( // Only include names that are valid Javascript identifiers (i.e. alphanumeric characters, // underscores and dollar signs allowed, but shouldn't start with a digit)... /^[A-Za-z_\$](\w|\$)*$/.test(entityName) && // ...and are actually in this namespace: asUrl(entity, namespace).substring(0, namespace.length) === namespace ); }) .map(entity => getTs(entity, namespace, entityTypes)).join(''); if (entityTs.length === 0) { throw new Error(`No entities found for ${namespace}.`); } const typescript = typeAliases + '\n' + entityTs; return typescript; } const fetchWithRetries: typeof getSolidDataset = async (url) => { let error = undefined; const maxTries = 5; const baseUrl = typeof url === "string" ? url : undefined; const jsonLdParser = getJsonLdParser({ baseUrl: baseUrl }); const rdfxmlParser = getRdfxmlParser({ baseUrl: baseUrl }); const rdfaParser = getRdfaParser({ baseUrl: baseUrl }); const turtleParser = getTurtleParser({ baseUrl: baseUrl }); for(let tries = 0; tries < maxTries; tries++) { try { const fetchedDataset = await getSolidDataset( url, { parsers: { "text/plain": turtleParser, "text/turtle": turtleParser, "application/fhir+turtle": turtleParser, "application/ld+json": jsonLdParser, "application/rdf+xml": rdfxmlParser, "applications/rdf+xml": rdfxmlParser, "application/xhtml+xml": rdfaParser, "text/html": rdfaParser, }, }, ); if (fetchedDataset) { console.log(`${url} fetched successfully (on attempt ${tries}).`) return fetchedDataset; } } catch(e) { console.error(`Fetching ${url} failed (attempt ${tries}).`) error = e; await timeoutPromise(5000); } } console.error(`Fetching ${url} failed ${maxTries} times; no longer retrying.`); throw error; }; async function timeoutPromise(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); }