From ee17097d50af2bebe456708ba903e00f56103518 Mon Sep 17 00:00:00 2001 From: ajuvercr Date: Mon, 26 Aug 2024 19:25:21 +0200 Subject: [PATCH] Format (#23) * add generate types * 1.2.8 * fix linting issues --- package-lock.json | 4 +- package.json | 2 +- src/lens.ts | 408 ++++++++++---------- src/ontology.ts | 72 ++-- src/shacl.ts | 949 ++++++++++++++++++++++++---------------------- tsconfig.json | 2 + 6 files changed, 735 insertions(+), 702 deletions(-) diff --git a/package-lock.json b/package-lock.json index d7d5e39..ac3e1eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rdf-lens", - "version": "1.2.7", + "version": "1.2.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rdf-lens", - "version": "1.2.7", + "version": "1.2.8", "license": "MIT", "dependencies": { "@rdfjs/types": "^1.1.0", diff --git a/package.json b/package.json index 9a3c25f..6e5288b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rdf-lens", - "version": "1.2.7", + "version": "1.2.8", "main": "./dist/index.js", "type": "module", "exports": { diff --git a/src/lens.ts b/src/lens.ts index c881db7..f5d0bb5 100644 --- a/src/lens.ts +++ b/src/lens.ts @@ -6,243 +6,251 @@ export type Res = [Term, T]; let lensIndex = 0; export class BasicLens { - _exec: (container: C, state: any, states: any[]) => T; - index: number; - constructor(execute: (container: C, state: any, states: any[]) => T) { - this._exec = execute; - this.index = lensIndex; - lensIndex += 1; - } - - asMulti(): T extends any[] ? BasicLensM : never { - return : never>( - new BasicLensM((c, _, states) => { - const out = this.execute(c, states); - return out; - }) - ); - } - - and( - ...and: { [K in keyof F]: BasicLens } - ): BasicLens { - return >( - new BasicLens((c, _, states) => { - const a = this.execute(c, states); - const rest: F = and.map((x) => x.execute(c, states)); - return [a, ...rest]; - }) - ); - } - - orM(...others: BasicLens[]): BasicLensM { - return new BasicLensM((c, _, states) => { - const all = [this, ...others]; - return all.flatMap((x) => { - try { - return [x.execute(c, states)]; - } catch (ex: any) { - return []; - } - }); - }); - } - - or(...others: BasicLens[]): BasicLens { - return new BasicLens((c, _, states) => { - try { - return this.execute(c, states); - } catch (ex: any) { - for (let i = 0; i < others.length; i++) { - try { - return others[i].execute(c, states); - } catch (ex) {} - } - } - throw "nope"; - }); - } + _exec: (container: C, state: unknown, states: unknown[]) => T; + index: number; + constructor( + execute: (container: C, state: unknown, states: unknown[]) => T, + ) { + this._exec = execute; + this.index = lensIndex; + lensIndex += 1; + } - map(fn: (t: T) => F): BasicLens { - return new BasicLens((c, _, states) => { - const a = this.execute(c, states); - return fn(a); - }); - } + asMulti(): T extends unknown[] ? BasicLensM : never { + return : never>( + new BasicLensM((c, _, states) => { + const out = this.execute(c, states); + return out; + }) + ); + } - then(next: BasicLens): BasicLens { - return new BasicLens((c, _, states) => { - const a = this.execute(c, states); - return next.execute(a, states); - }); - } + and( + ...and: { [K in keyof F]: BasicLens } + ): BasicLens { + return >( + new BasicLens((c, _, states) => { + const a = this.execute(c, states); + const rest: unknown[] = and.map((x) => x.execute(c, states)); + return [a, ...rest]; + }) + ); + } - execute(container: C, states: any[] = []): T { - if (!states[this.index]) { - states[this.index] = {}; + orM(...others: BasicLens[]): BasicLensM { + return new BasicLensM((c, _, states) => { + const all = [this, ...others]; + return all.flatMap((x) => { + try { + return [x.execute(c, states)]; + } catch (ex: unknown) { + return []; + } + }); + }); + } + + or(...others: BasicLens[]): BasicLens { + return new BasicLens((c, _, states) => { + try { + return this.execute(c, states); + } catch (ex: unknown) { + for (let i = 0; i < others.length; i++) { + try { + return others[i].execute(c, states); + } catch (ex) { + // this can be ignored + } + } + } + throw "nope"; + }); + } + + map(fn: (t: T) => F): BasicLens { + return new BasicLens((c, _, states) => { + const a = this.execute(c, states); + return fn(a); + }); + } + + then(next: BasicLens): BasicLens { + return new BasicLens((c, _, states) => { + const a = this.execute(c, states); + return next.execute(a, states); + }); + } + + execute(container: C, states: unknown[] = []): T { + if (!states[this.index]) { + states[this.index] = {}; + } + return this._exec(container, states[this.index], states); } - return this._exec(container, states[this.index], states); - } } export class BasicLensM extends BasicLens { - one(def?: D): BasicLens { - return new BasicLens((c, _, states) => { - const qs = this.execute(c, states); - return qs[0] || def!; - }); - } - expectOne(): BasicLens { - return new BasicLens((c, _, states) => { - const qs = this.execute(c, states); - if (qs.length < 1) throw "Nope"; - return qs[0]; - }); - } - - thenAll(next: BasicLens): BasicLensM { - return new BasicLensM((c, _, states) => { - const qs = this.execute(c, states); - return qs.flatMap((x) => { - try { - const o = next.execute(x, states); - return [o]; - } catch (ex: any) { - return []; - } - }); - }); - } - thenSome(next: BasicLens): BasicLensM { - return this.thenAll(next); - } - - thenFlat(next: BasicLensM): BasicLensM { - return new BasicLensM((c, _, states) => { - const qs = this.execute(c, states); - return qs.flatMap((x) => next.execute(x, states)); - }); - } - mapAll(fn: (t: T) => F): BasicLensM { - return new BasicLensM((c, _, states) => { - const qs = this.execute(c, states); - return qs.map(fn); - }); - } - - orAll(...others: BasicLensM[]): BasicLensM { - return new BasicLensM((c, _, states) => { - let out = []; - try { - out.push(...this.execute(c, states)); - } catch (ex: any) {} - for (let i = 0; i < others.length; i++) { - try { - out.push(...others[i].execute(c, states)); - } catch (ex: any) {} - } - - return out; - }); - } + one(def?: D): BasicLens { + return new BasicLens((c, _, states) => { + const qs = this.execute(c, states); + return qs[0] || def!; + }); + } + expectOne(): BasicLens { + return new BasicLens((c, _, states) => { + const qs = this.execute(c, states); + if (qs.length < 1) throw "Nope"; + return qs[0]; + }); + } - filter(fn: (object: T) => boolean): BasicLensM { - return new BasicLensM((c, _, states) => { - return this.execute(c, states).filter(fn); - }); - } + thenAll(next: BasicLens): BasicLensM { + return new BasicLensM((c, _, states) => { + const qs = this.execute(c, states); + return qs.flatMap((x) => { + try { + const o = next.execute(x, states); + return [o]; + } catch (ex: unknown) { + return []; + } + }); + }); + } + thenSome(next: BasicLens): BasicLensM { + return this.thenAll(next); + } + + thenFlat(next: BasicLensM): BasicLensM { + return new BasicLensM((c, _, states) => { + const qs = this.execute(c, states); + return qs.flatMap((x) => next.execute(x, states)); + }); + } + mapAll(fn: (t: T) => F): BasicLensM { + return new BasicLensM((c, _, states) => { + const qs = this.execute(c, states); + return qs.map(fn); + }); + } + + orAll(...others: BasicLensM[]): BasicLensM { + return new BasicLensM((c, _, states) => { + const out = []; + try { + out.push(...this.execute(c, states)); + } catch (ex: unknown) { + // this can be ignored + } + for (let i = 0; i < others.length; i++) { + try { + out.push(...others[i].execute(c, states)); + } catch (ex: unknown) { + // this can be ignored + } + } + + return out; + }); + } + + filter(fn: (object: T) => boolean): BasicLensM { + return new BasicLensM((c, _, states) => { + return this.execute(c, states).filter(fn); + }); + } } export function pred(pred?: Term): BasicLensM { - return new BasicLensM(({ quads, id }) => { - const out = quads.filter( - (q) => q.subject.equals(id) && (!pred || q.predicate.equals(pred)), - ); - return out.map((q) => ({ quads, id: q.object })); - }); + return new BasicLensM(({ quads, id }) => { + const out = quads.filter( + (q) => q.subject.equals(id) && (!pred || q.predicate.equals(pred)), + ); + return out.map((q) => ({ quads, id: q.object })); + }); } export function invPred(pred?: Term): BasicLensM { - return new BasicLensM(({ quads, id }) => { - const out = quads.filter( - (q) => q.object.equals(id) && (!pred || q.predicate.equals(pred)), - ); - return out.map((q) => ({ quads, id: q.subject })); - }); + return new BasicLensM(({ quads, id }) => { + const out = quads.filter( + (q) => q.object.equals(id) && (!pred || q.predicate.equals(pred)), + ); + return out.map((q) => ({ quads, id: q.subject })); + }); } export function predTriple(pred?: Term): BasicLensM> { - return new BasicLensM(({ quads, id }) => { - const out = quads.filter( - (q) => q.subject.equals(id) && (!pred || q.predicate.equals(pred)), - ); - return out.map((q) => ({ quads, id: q })); - }); + return new BasicLensM(({ quads, id }) => { + const out = quads.filter( + (q) => q.subject.equals(id) && (!pred || q.predicate.equals(pred)), + ); + return out.map((q) => ({ quads, id: q })); + }); } export function unique(): BasicLensM { - return new BasicLensM((qs) => { - const literals: { [id: string]: Cont } = {}; - const named: { [id: string]: Cont } = {}; - const blank: { [id: string]: Cont } = {}; - for (let q of qs) { - const ty = q.id.termType; - if (ty === "Literal") literals[q.id.value] = q; - if (ty === "NamedNode") named[q.id.value] = q; - if (ty === "BlankNode") blank[q.id.value] = q; - } - const out = []; - out.push(...Object.values(literals)); - out.push(...Object.values(named)); - out.push(...Object.values(blank)); - return out; - }); + return new BasicLensM((qs) => { + const literals: { [id: string]: Cont } = {}; + const named: { [id: string]: Cont } = {}; + const blank: { [id: string]: Cont } = {}; + for (const q of qs) { + const ty = q.id.termType; + if (ty === "Literal") literals[q.id.value] = q; + if (ty === "NamedNode") named[q.id.value] = q; + if (ty === "BlankNode") blank[q.id.value] = q; + } + const out = []; + out.push(...Object.values(literals)); + out.push(...Object.values(named)); + out.push(...Object.values(blank)); + return out; + }); } export function subjects(): BasicLensM { - return new BasicLensM((quads) => { - return quads.map((x) => ({ id: x.subject, quads })); - }); + return new BasicLensM((quads) => { + return quads.map((x) => ({ id: x.subject, quads })); + }); } export function match( - subject: Term | undefined, - predicate: Term | undefined, - object: Term | undefined, + subject: Term | undefined, + predicate: Term | undefined, + object: Term | undefined, ): BasicLensM> { - return new BasicLensM((quads) => { - return quads - .filter( - (x) => - (!subject || x.subject.equals(subject)) && - (!predicate || x.predicate.equals(predicate)) && - (!object || x.object.equals(object)), - ) - .map((id) => ({ id, quads })); - }); + return new BasicLensM((quads) => { + return quads + .filter( + (x) => + (!subject || x.subject.equals(subject)) && + (!predicate || x.predicate.equals(predicate)) && + (!object || x.object.equals(object)), + ) + .map((id) => ({ id, quads })); + }); } export const subject: BasicLens, Cont> = new BasicLens( - ({ id, quads }) => ({ - id: id.subject, - quads, - }), + ({ id, quads }) => ({ + id: id.subject, + quads, + }), ); export const predicate: BasicLens, Cont> = new BasicLens( - ({ id, quads }) => ({ - id: id.predicate, - quads, - }), + ({ id, quads }) => ({ + id: id.predicate, + quads, + }), ); export const object: BasicLens, Cont> = new BasicLens( - ({ id, quads }) => ({ - id: id.object, - quads, - }), + ({ id, quads }) => ({ + id: id.object, + quads, + }), ); export function empty(): BasicLens { - return new BasicLens((x) => x); + return new BasicLens((x) => x); } diff --git a/src/ontology.ts b/src/ontology.ts index cd07516..d65827d 100644 --- a/src/ontology.ts +++ b/src/ontology.ts @@ -1,45 +1,45 @@ import { createTermNamespace, createUriAndTermNamespace } from "@treecg/types"; export const RDFS = createTermNamespace( - "http://www.w3.org/2000/01/rdf-schema#", - "subClassOf", + "http://www.w3.org/2000/01/rdf-schema#", + "subClassOf", ); export const SHACL = createTermNamespace( - "http://www.w3.org/ns/shacl#", - // Basics - "Shape", - "NodeShape", - "PropertyShape", - // SHACL target constraints - "targetNode", - "targetClass", - "targetSubjectsOf", - "targetObjectsOf", - // Property things - "property", - "path", - "class", - "name", - "description", - "defaultValue", - // Path things - "alternativePath", - "zeroOrMorePath", - "oneOrMorePath", - "zeroOrOnePath", - "inversePath", - "minCount", - "maxCount", - "datatype", + "http://www.w3.org/ns/shacl#", + // Basics + "Shape", + "NodeShape", + "PropertyShape", + // SHACL target constraints + "targetNode", + "targetClass", + "targetSubjectsOf", + "targetObjectsOf", + // Property things + "property", + "path", + "class", + "name", + "description", + "defaultValue", + // Path things + "alternativePath", + "zeroOrMorePath", + "oneOrMorePath", + "zeroOrOnePath", + "inversePath", + "minCount", + "maxCount", + "datatype", ); export const RDFL = createUriAndTermNamespace( - "https://w3id.org/rdf-lens/ontology#", - "CBD", - "PathLens", - "Context", - "TypedExtract", - "EnvVariable", - "envKey", - "envDefault" + "https://w3id.org/rdf-lens/ontology#", + "CBD", + "PathLens", + "Context", + "TypedExtract", + "EnvVariable", + "envKey", + "envDefault", ); diff --git a/src/shacl.ts b/src/shacl.ts index fca4261..fabf678 100644 --- a/src/shacl.ts +++ b/src/shacl.ts @@ -1,14 +1,14 @@ import { Quad, Term } from "@rdfjs/types"; import { RDF, XSD } from "@treecg/types"; import { - BasicLens, - BasicLensM, - Cont, - empty, - invPred, - pred, - subjects, - unique, + BasicLens, + BasicLensM, + Cont, + empty, + invPred, + pred, + subjects, + unique, } from "./lens"; import { DataFactory } from "rdf-data-factory"; @@ -17,527 +17,550 @@ import { RDFL, RDFS, SHACL } from "./ontology"; const { literal } = new DataFactory(); export interface ShapeField { - name: string; - path: BasicLensM; - minCount?: number; - maxCount?: number; - extract: BasicLens; + name: string; + path: BasicLensM; + minCount?: number; + maxCount?: number; + extract: BasicLens; } export interface Shape { - id: string; - ty: Term; - description?: string; - fields: ShapeField[]; + id: string; + ty: Term; + description?: string; + fields: ShapeField[]; } export function toLens( - shape: Shape, -): BasicLens { - if (shape.fields.length === 0) return empty().map(() => ({})); - - const fields = shape.fields.map((field) => { - const minCount = field.minCount || 0; - const maxCount = field.maxCount || Number.MAX_SAFE_INTEGER; - const base = (() => { - if (maxCount < 2) { - return field.path.one().then( - new BasicLens((x, _, states) => { - if (x) { - return field.extract.execute(x, states); - } else { - if (minCount > 0) { - throw "Thing is undefined " + field.name; - } else { - return x; - } + shape: Shape, +): BasicLens { + if (shape.fields.length === 0) return empty().map(() => ({})); + + const fields = shape.fields.map((field) => { + const minCount = field.minCount || 0; + const maxCount = field.maxCount || Number.MAX_SAFE_INTEGER; + const base = (() => { + if (maxCount < 2) { + return field.path.one().then( + new BasicLens((x, _, states) => { + if (x) { + return field.extract.execute(x, states); + } else { + if (minCount > 0) { + throw "Thing is undefined " + field.name; + } else { + return x; + } + } + }), + ); } - }), - ); - } - - const thenListExtract = RdfList.and(empty()).map( - ([terms, { quads }]) => terms.map((id) => ({ id, quads })), - ); - const noListExtract = empty().map((x) => [x]); - - return field.path - .thenFlat(thenListExtract.or(noListExtract).asMulti()) - .thenAll(field.extract) - .map((x) => x.filter((x) => !!x)) - .map((xs) => { - if (xs.length < minCount) { - throw `${shape.ty}:${field.name} required at least ${minCount} elements, found ${xs.length}`; - } - if (xs.length > maxCount) { - throw `${shape.ty}:${field.name} required at most ${maxCount} elements, found ${xs.length}`; - } - return xs; - }) - .map((x) => { - const out = x.filter((x) => !!x); - if (maxCount < 2) { - // console.log(out, x); - return out[0]; - } else { + + const thenListExtract = RdfList.and(empty()).map( + ([terms, { quads }]) => terms.map((id) => ({ id, quads })), + ); + const noListExtract = empty().map((x) => [x]); + + return field.path + .thenFlat(thenListExtract.or(noListExtract).asMulti()) + .thenAll(field.extract) + .map((x) => x.filter((x) => !!x)) + .map((xs) => { + if (xs.length < minCount) { + throw `${shape.ty}:${field.name} required at least ${minCount} elements, found ${xs.length}`; + } + if (xs.length > maxCount) { + throw `${shape.ty}:${field.name} required at most ${maxCount} elements, found ${xs.length}`; + } + return xs; + }) + .map((x) => { + const out = x.filter((x) => !!x); + if (maxCount < 2) { + // console.log(out, x); + return out[0]; + } else { + return out; + } + }); + })(); + + const asField = base.map((x) => { + const out = <{ [label: string]: unknown }>{}; + out[field.name] = x; return out; - } }); - })(); - const asField = base.map((x) => { - const out = <{ [label: string]: any }>{}; - out[field.name] = x; - return out; + return minCount > 0 ? asField : asField.or(empty().map(() => ({}))); }); - return minCount > 0 ? asField : asField.or(empty().map(() => ({}))); - }); - - return fields[0] - .and(...fields.slice(1)) - .map((xs) => Object.assign({}, ...xs)); + return fields[0] + .and(...fields.slice(1)) + .map((xs) => Object.assign({}, ...xs)); } const RDFListElement = pred(RDF.terms.first) - .expectOne() - .and(pred(RDF.terms.rest).expectOne()); + .expectOne() + .and(pred(RDF.terms.rest).expectOne()); export const RdfList: BasicLens = new BasicLens( - (c, _, states) => { - if (c.id.equals(RDF.terms.nil)) { - return []; - } + (c, _, states) => { + if (c.id.equals(RDF.terms.nil)) { + return []; + } - const [first, rest] = RDFListElement.execute(c, states); - const els = RdfList.execute(rest, states); - els.unshift(first.id); - return els; - }, + const [first, rest] = RDFListElement.execute(c, states); + const els = RdfList.execute(rest, states); + els.unshift(first.id); + return els; + }, ); export const ShaclSequencePath: BasicLens< - Cont, - BasicLensM + Cont, + BasicLensM > = new BasicLens((c, _, states) => { - const pathList = RdfList.execute(c, states); - const paths = pathList.map((x) => - ShaclPath.execute({ id: x, quads: c.quads }), - ); + const pathList = RdfList.execute(c, states); + const paths = pathList.map((x) => + ShaclPath.execute({ id: x, quads: c.quads }), + ); - if (paths.length === 0) { - return new BasicLensM((c) => [c]); - } + if (paths.length === 0) { + return new BasicLensM((c) => [c]); + } - let start = paths[0]; + let start = paths[0]; - for (let i = 1; i < pathList.length; i++) { - start = start.thenFlat(paths[i]); - } + for (let i = 1; i < pathList.length; i++) { + start = start.thenFlat(paths[i]); + } - return start; + return start; }); export const ShaclAlternativepath: BasicLens< - Cont, - BasicLensM + Cont, + BasicLensM > = new BasicLens((c, _, states) => { - const options = pred(SHACL.alternativePath) - .one() - .then(RdfList) - .execute(c, states); - const optionLenses = options.map((id) => - ShaclPath.execute({ id, quads: c.quads }, states), - ); - return optionLenses[0].orAll(...optionLenses.slice(1)); + const options = pred(SHACL.alternativePath) + .one() + .then(RdfList) + .execute(c, states); + const optionLenses = options.map((id) => + ShaclPath.execute({ id, quads: c.quads }, states), + ); + return optionLenses[0].orAll(...optionLenses.slice(1)); }); export const ShaclPredicatePath: BasicLens< - Cont, - BasicLensM + Cont, + BasicLensM > = new BasicLens((c) => { - return pred(c.id); + return pred(c.id); }); export const ShaclInversePath: BasicLens> = pred( - SHACL.inversePath, + SHACL.inversePath, ) - .one() - .then( - new BasicLens>((c, _, states) => { - const pathList = RdfList.execute(c, states); + .one() + .then( + new BasicLens>((c, _, states) => { + const pathList = RdfList.execute(c, states); - if (pathList.length === 0) { - return new BasicLensM((c) => [c]); - } + if (pathList.length === 0) { + return new BasicLensM((c) => [c]); + } - pathList.reverse(); + pathList.reverse(); - let start = invPred(pathList[0]); + let start = invPred(pathList[0]); - for (let i = 1; i < pathList.length; i++) { - start = start.thenFlat(invPred(pathList[i])); - } + for (let i = 1; i < pathList.length; i++) { + start = start.thenFlat(invPred(pathList[i])); + } - return start; - }).or( - new BasicLens>((c) => { - return invPred(c.id); - }), - ), - ); + return start; + }).or( + new BasicLens>((c) => { + return invPred(c.id); + }), + ), + ); export function MultiPath( - predicate: Term, - min: number, - max?: number, + predicate: Term, + min: number, + max?: number, ): BasicLens> { - return pred(predicate) - .one() - .then( - new BasicLens>((c, _, states) => { - return ShaclPath.execute(c, states); - }), - ) - .map( - (x) => - new BasicLensM((c, _, states) => { - let out: Cont[] = []; - let current = [c]; - let done = 0; - - if (min == 0) { - out.push(c); - } - - while (current.length > 0) { - done += 1; - const todo = current.slice(); - current = []; - for (let c of todo) { - try { - const news = x.execute(c, states); - console.log("adding ", news.length, "news"); - current.push(...news); - - if (done >= min && (!max || done <= max)) { - out.push(c); - } - } catch (ex) { - console.log(ex); - if (done >= min && (!max || done <= max)) { - out.push(c); - } - break; - } - } - } - - return out; - }), - ); + return pred(predicate) + .one() + .then( + new BasicLens>((c, _, states) => { + return ShaclPath.execute(c, states); + }), + ) + .map( + (x) => + new BasicLensM((c, _, states) => { + const out: Cont[] = []; + let current = [c]; + let done = 0; + + if (min == 0) { + out.push(c); + } + + while (current.length > 0) { + done += 1; + const todo = current.slice(); + current = []; + for (const c of todo) { + try { + const news = x.execute(c, states); + console.log("adding ", news.length, "news"); + current.push(...news); + + if (done >= min && (!max || done <= max)) { + out.push(c); + } + } catch (ex) { + console.log(ex); + if (done >= min && (!max || done <= max)) { + out.push(c); + } + break; + } + } + } + + return out; + }), + ); } export const ShaclPath = ShaclSequencePath.or( - ShaclAlternativepath, - ShaclInversePath, - MultiPath(SHACL.zeroOrMorePath, 0), - MultiPath(SHACL.zeroOrMorePath, 1), - MultiPath(SHACL.zeroOrOnePath, 0, 1), - ShaclPredicatePath, + ShaclAlternativepath, + ShaclInversePath, + MultiPath(SHACL.zeroOrMorePath, 0), + MultiPath(SHACL.zeroOrMorePath, 1), + MultiPath(SHACL.zeroOrOnePath, 0, 1), + ShaclPredicatePath, ); function field( - predicate: Term, - name: T, - convert?: (inp: string) => O, + predicate: Term, + name: T, + convert?: (inp: string) => O, ): BasicLens { - const conv = convert || ((x: string) => x); + const conv = convert || ((x: string) => x); - return pred(predicate) - .one() - .map(({ id }) => { - const out = <{ [F in T]: O }>{}; - out[name] = conv(id.value); - return out; - }); + return pred(predicate) + .one() + .map(({ id }) => { + const out = <{ [F in T]: O }>{}; + out[name] = conv(id.value); + return out; + }); } function optionalField( - predicate: Term, - name: T, - convert?: (inp: string) => O | undefined, + predicate: Term, + name: T, + convert?: (inp: string) => O | undefined, ): BasicLens { - const conv = convert || ((x: string) => x); - - return pred(predicate) - .one(undefined) - .map((inp) => { - const out = <{ [F in T]: O | undefined }>{}; - if (inp) { - out[name] = conv(inp.id.value); - } - return out; - }); + const conv = convert || ((x: string) => x); + + return pred(predicate) + .one(undefined) + .map((inp) => { + const out = <{ [F in T]: O | undefined }>{}; + if (inp) { + out[name] = conv(inp.id.value); + } + return out; + }); } -function dataTypeToExtract(dataType: Term, t: Term): any { - if (dataType.equals(XSD.terms.integer)) return +t.value; - if (dataType.equals(XSD.terms.custom("float"))) return +t.value; - if (dataType.equals(XSD.terms.custom("double"))) return +t.value; - if (dataType.equals(XSD.terms.custom("decimal"))) return +t.value; - if (dataType.equals(XSD.terms.string)) return t.value; - if (dataType.equals(XSD.terms.dateTime)) return new Date(t.value); - if (dataType.equals(XSD.terms.custom("boolean"))) return t.value === "true"; - - return t; +function dataTypeToExtract(dataType: Term, t: Term): unknown { + if (dataType.equals(XSD.terms.integer)) return +t.value; + if (dataType.equals(XSD.terms.custom("float"))) return +t.value; + if (dataType.equals(XSD.terms.custom("double"))) return +t.value; + if (dataType.equals(XSD.terms.custom("decimal"))) return +t.value; + if (dataType.equals(XSD.terms.string)) return t.value; + if (dataType.equals(XSD.terms.dateTime)) return new Date(t.value); + if (dataType.equals(XSD.terms.custom("boolean"))) return t.value === "true"; + + return t; } type Cache = { - [clazz: string]: BasicLens; + [clazz: string]: BasicLens; }; type SubClasses = { - [clazz: string]: string; + [clazz: string]: string; }; -function envLens(dataType: Term): BasicLens { - const checkType = pred(RDF.terms.type) - .thenSome( - new BasicLens(({ id }) => { - if (!id.equals(RDFL.terms.EnvVariable)) { - throw "expected type " + RDFL.EnvVariable; - } - return { checked: true }; - }), - ) - .expectOne(); - - const envName = pred(RDFL.terms.envKey) - .one() - .map(({ id }) => ({ - key: id.value, - })); - - const defaultValue = pred(RDFL.terms.envDefault) - .one(undefined) - .map((found) => ({ - defaultValue: found?.id.value, - })); - - return checkType - .and(envName, defaultValue) - .map(([_, { key }, { defaultValue }]) => { - const value = process.env[key] || defaultValue; - if (value) { - return dataTypeToExtract(dataType, literal(value)); - } else { - throw "Nothing set for ENV " + key + ". No default was set either!"; - } - }); +function envLens(dataType: Term): BasicLens { + const checkType = pred(RDF.terms.type) + .thenSome( + new BasicLens(({ id }) => { + if (!id.equals(RDFL.terms.EnvVariable)) { + throw "expected type " + RDFL.EnvVariable; + } + return { checked: true }; + }), + ) + .expectOne(); + + const envName = pred(RDFL.terms.envKey) + .one() + .map(({ id }) => ({ + key: id.value, + })); + + const defaultValue = pred(RDFL.terms.envDefault) + .one(undefined) + .map((found) => ({ + defaultValue: found?.id.value, + })); + + return checkType + .and(envName, defaultValue) + .map(([_thing, { key }, { defaultValue }]) => { + const value = process.env[key] || defaultValue; + if (value) { + return dataTypeToExtract(dataType, literal(value)); + } else { + throw ( + "Nothing set for ENV " + + key + + ". No default was set either!" + ); + } + }); } function extractProperty( - cache: Cache, - subClasses: SubClasses, - apply: { [clazz: string]: (item: any) => any }, + cache: Cache, + _subClasses: SubClasses, + apply: { [clazz: string]: (item: unknown) => unknown }, ): BasicLens { - const pathLens = pred(SHACL.path) - .one() - .then(ShaclPath) - .map((path) => ({ - path, - })); - const nameLens = field(SHACL.name, "name"); - const minCount = optionalField(SHACL.minCount, "minCount", (x) => +x); - const maxCount = optionalField(SHACL.maxCount, "maxCount", (x) => +x); - - const dataTypeLens: BasicLens = - pred(SHACL.datatype) - .one() - .map(({ id }) => ({ - extract: envLens(id).or( - empty().map((item) => dataTypeToExtract(id, item.id)), - ), - })); - - const clazzLens: BasicLens = field( - SHACL.class, - "clazz", - ).map(({ clazz: expected_class }) => { - return { - extract: new BasicLens(({ id, quads }, _, states) => { - // We did not find a type, so use the expected class lens - const lens = cache[expected_class]; - if (!lens) { - throw `Tried extracting class ${expected_class} but no shape was defined`; - } - if (apply[expected_class]) { - return lens.map(apply[expected_class]).execute({ id, quads }, states); - } else { - return lens.execute({ id, quads }, states); - } - }), - }; - }); + const pathLens = pred(SHACL.path) + .one() + .then(ShaclPath) + .map((path) => ({ + path, + })); + const nameLens = field(SHACL.name, "name"); + const minCount = optionalField(SHACL.minCount, "minCount", (x) => +x); + const maxCount = optionalField(SHACL.maxCount, "maxCount", (x) => +x); + + const dataTypeLens: BasicLens = + pred(SHACL.datatype) + .one() + .map(({ id }) => ({ + extract: envLens(id).or( + empty().map((item) => dataTypeToExtract(id, item.id)), + ), + })); + + const clazzLens: BasicLens = + field(SHACL.class, "clazz").map(({ clazz: expected_class }) => { + return { + extract: new BasicLens( + ({ id, quads }, _, states) => { + // We did not find a type, so use the expected class lens + const lens = cache[expected_class]; + if (!lens) { + throw `Tried extracting class ${expected_class} but no shape was defined`; + } + if (apply[expected_class]) { + return lens + .map(apply[expected_class]) + .execute({ id, quads }, states); + } else { + return lens.execute({ id, quads }, states); + } + }, + ), + }; + }); - return pathLens - .and(nameLens, minCount, maxCount, clazzLens.or(dataTypeLens)) - .map((xs) => Object.assign({}, ...xs)); + return pathLens + .and(nameLens, minCount, maxCount, clazzLens.or(dataTypeLens)) + .map((xs) => Object.assign({}, ...xs)); } export const CBDLens = new BasicLensM(({ id, quads }) => { - const done = new Set(); - const todo = [id]; - const out = []; - let item = todo.pop(); - while (item) { - const found = quads.filter((x) => x.subject.equals(item)); - out.push(...found); - for (let option of found) { - const object = option.object; - if (object.termType !== "BlankNode") { - continue; - } - - if (done.has(object.value)) continue; - done.add(object.value); - todo.push(object); + const done = new Set(); + const todo = [id]; + const out = []; + let item = todo.pop(); + while (item) { + const found = quads.filter((x) => x.subject.equals(item)); + out.push(...found); + for (const option of found) { + const object = option.object; + if (object.termType !== "BlankNode") { + continue; + } + + if (done.has(object.value)) continue; + done.add(object.value); + todo.push(object); + } + item = todo.pop(); } - item = todo.pop(); - } - return out; + return out; }); +type StateDict = { + [id: string]: { lens: BasicLens; result: unknown }[]; +}; +type CachedLens = { + lenses: { + lens: BasicLens; + from: BasicLens; + }[]; +}; export const Cached = function ( - lens: BasicLens, - cachedLenses: { - lenses: { lens: BasicLens; from: BasicLens }[]; - }, -): BasicLens { - const lenses = cachedLenses["lenses"] ?? (cachedLenses.lenses = []); - - const found = lenses.find((x) => x.from === lens); - if (found) { - return found.lens; - } - - const newLens = new BasicLens(({ id, quads }, _, states) => { - const state = states[lens.index] ?? (states[lens.index] = {}); - let stateDict: { - [id: string]: { lens: BasicLens; result: any }[]; - } = {}; - if (id.termType == "NamedNode") { - stateDict = state.namedNodes = state.namedNodes ?? {}; - } - if (id.termType == "BlankNode") { - stateDict = state.blankNodes = state.blankNodes ?? {}; + lens: BasicLens, + cachedLenses: CachedLens, +): BasicLens { + const lenses = cachedLenses["lenses"] ?? (cachedLenses.lenses = []); + + const found = lenses.find((x) => x.from === lens); + if (found) { + return found.lens; } - if (!(id.value in stateDict!)) { - stateDict[id.value] = []; - } + const newLens = new BasicLens(({ id, quads }, _, states) => { + const state: { namedNodes: StateDict; blankNodes: StateDict } = + <{ namedNodes: StateDict; blankNodes: StateDict }>( + states[lens.index] + ) ?? + (states[lens.index] = { + namedNodes: {}, + blankNodes: {}, + }); + let stateDict: StateDict = {}; + if (id.termType == "NamedNode") { + stateDict = state.namedNodes = state.namedNodes ?? {}; + } + if (id.termType == "BlankNode") { + stateDict = state.blankNodes = state.blankNodes ?? {}; + } - const res = stateDict![id.value].find((x) => x.lens == lens); - if (res) { - return res.result; - } + if (!(id.value in stateDict!)) { + stateDict[id.value] = []; + } - const thisThing = { lens: lens, result: {} }; - stateDict[id.value].push(thisThing); + const res = stateDict![id.value].find((x) => x.lens == lens); + if (res) { + return res.result; + } - const executedLens = lens.execute({ quads, id }, states); - Object.assign(thisThing.result, executedLens); + const thisThing = { lens: lens, result: {} }; + stateDict[id.value].push(thisThing); - return thisThing.result; - }); + const executedLens = lens.execute({ quads, id }, states); + Object.assign(thisThing.result, executedLens); - lenses.push({ lens: newLens, from: lens }); - return newLens; + return thisThing.result; + }); + + lenses.push({ lens: newLens, from: lens }); + return newLens; }; export const TypedExtract = function ( - cache: Cache, - apply: ApplyDict, - subClasses: SubClasses, -): BasicLens { - return new BasicLens(({ id, quads }, state, states) => { - const ty = quads.find( - (q) => q.subject.equals(id) && q.predicate.equals(RDF.terms.type), - )?.object.value; - - if (!ty) { - return; - } + cache: Cache, + apply: ApplyDict, + subClasses: SubClasses, +): BasicLens { + return new BasicLens(({ id, quads }, state, states) => { + const ty = quads.find( + (q) => q.subject.equals(id) && q.predicate.equals(RDF.terms.type), + )?.object.value; + + if (!ty) { + return; + } - // We found a type, let's see if the expected class is inside the class hierachry - const lenses: (typeof cache)[string][] = []; + // We found a type, let's see if the expected class is inside the class hierachry + const lenses: (typeof cache)[string][] = []; - let current = ty; - while (!!current) { - const thisLens = cache[current]; - if (thisLens) { - lenses.push(Cached(thisLens, state)); - } - current = subClasses[current]; - } + let current = ty; + while (current) { + const thisLens = cache[current]; + if (thisLens) { + lenses.push(Cached(thisLens, state)); + } + current = subClasses[current]; + } - if (lenses.length === 0) { - // Maybe we just return here - // Or we log - // Or we make it conditional - throw `Tried the classhierarchy for ${ty}, but found no shape definition`; - } + if (lenses.length === 0) { + // Maybe we just return here + // Or we log + // Or we make it conditional + throw `Tried the classhierarchy for ${ty}, but found no shape definition`; + } - const finalLens = - lenses.length == 1 - ? lenses[0] - : lenses[0] - .and(...lenses.slice(1)) - .map((xs) => Object.assign({}, ...xs)); - - if (apply[ty]) { - return finalLens.map(apply[ty]).execute({ id, quads }, states); - } else { - return finalLens.execute({ id, quads }, states); - } - }); + const finalLens = + lenses.length == 1 + ? lenses[0] + : lenses[0] + .and(...lenses.slice(1)) + .map((xs) => Object.assign({}, ...xs)); + + if (apply[ty]) { + return finalLens.map(apply[ty]).execute({ id, quads }, states); + } else { + return finalLens.execute({ id, quads }, states); + } + }); }; -export type ApplyDict = { [label: string]: (item: any) => any }; +export type ApplyDict = { [label: string]: (item: unknown) => unknown }; export function extractShape( - cache: Cache, - subclasses: { [label: string]: string }, - apply: ApplyDict, + cache: Cache, + subclasses: { [label: string]: string }, + apply: ApplyDict, ): BasicLens { - const checkTy = pred(RDF.terms.type) - .one() - .map(({ id }) => { - if (id.equals(SHACL.NodeShape)) return {}; - throw "Shape is not sh:NodeShape"; - }); - - const idLens = empty().map(({ id }) => ({ id: id.value })); - const clazzs = pred(SHACL.targetClass); + const checkTy = pred(RDF.terms.type) + .one() + .map(({ id }) => { + if (id.equals(SHACL.NodeShape)) return {}; + throw "Shape is not sh:NodeShape"; + }); - const multiple = clazzs.thenAll(empty().map(({ id }) => ({ ty: id }))); + const idLens = empty().map(({ id }) => ({ id: id.value })); + const clazzs = pred(SHACL.targetClass); - // TODO: Add implictTargetClass - const descriptionClassLens = optionalField(SHACL.description, "description"); - const fields = pred(SHACL.property) - .thenSome(extractProperty(cache, subclasses, apply)) - .map((fields) => ({ fields })); + const multiple = clazzs.thenAll( + empty().map(({ id }) => ({ ty: id })), + ); - return multiple - .and(checkTy, idLens, descriptionClassLens, fields) - .map(([multiple, ...others]) => - multiple.map((xs) => Object.assign({}, xs, ...others)), + // TODO: Add implictTargetClass + const descriptionClassLens = optionalField( + SHACL.description, + "description", ); + const fields = pred(SHACL.property) + .thenSome(extractProperty(cache, subclasses, apply)) + .map((fields) => ({ fields })); + + return multiple + .and(checkTy, idLens, descriptionClassLens, fields) + .map(([multiple, ...others]) => + multiple.map((xs) => Object.assign({}, xs, ...others)), + ); } export type Shapes = { - shapes: Shape[]; - lenses: Cache; - subClasses: SubClasses; + shapes: Shape[]; + lenses: Cache; + subClasses: SubClasses; }; /** @@ -546,45 +569,45 @@ export type Shapes = { * @param [customClasses={}] lenses that are used to extract special objects types */ export function extractShapes( - quads: Quad[], - apply: ApplyDict = {}, - customClasses: Cache = {}, + quads: Quad[], + apply: ApplyDict = {}, + customClasses: Cache = {}, ): Shapes { - const cache: Cache = Object.assign({}, customClasses); - - cache[RDFL.PathLens] = ShaclPath; - cache[RDFL.CBD] = CBDLens; - cache[RDFL.Context] = new BasicLens(({ quads }) => { - return quads; - }); - const subClasses: SubClasses = {}; - quads - .filter((x) => x.predicate.equals(RDFS.subClassOf)) - .forEach((x) => (subClasses[x.subject.value] = x.object.value)); - - const shapes = subjects() - .then(unique()) - .asMulti() - .thenSome(extractShape(cache, subClasses, apply)) - .execute(quads, []) - .flat(); - const lenses = []; - - cache[RDFL.TypedExtract] = TypedExtract(cache, apply, subClasses); - - // Populate cache - for (let shape of shapes) { - const lens = toLens(shape); - const target = cache[shape.ty.value]; - - if (target) { - cache[shape.ty.value] = target.or(lens); - // subClasses: shape.subTypes, - } else { - cache[shape.ty.value] = lens; + const cache: Cache = Object.assign({}, customClasses); + + cache[RDFL.PathLens] = ShaclPath; + cache[RDFL.CBD] = >CBDLens; + cache[RDFL.Context] = new BasicLens(({ quads }) => { + return quads; + }); + const subClasses: SubClasses = {}; + quads + .filter((x) => x.predicate.equals(RDFS.subClassOf)) + .forEach((x) => (subClasses[x.subject.value] = x.object.value)); + + const shapes = subjects() + .then(unique()) + .asMulti() + .thenSome(extractShape(cache, subClasses, apply)) + .execute(quads, []) + .flat(); + const lenses = []; + + cache[RDFL.TypedExtract] = TypedExtract(cache, apply, subClasses); + + // Populate cache + for (const shape of shapes) { + const lens = toLens(shape); + const target = cache[shape.ty.value]; + + if (target) { + cache[shape.ty.value] = target.or(lens); + // subClasses: shape.subTypes, + } else { + cache[shape.ty.value] = lens; + } + lenses.push(lens); } - lenses.push(lens); - } - return { lenses: cache, shapes, subClasses }; + return { lenses: cache, shapes, subClasses }; } diff --git a/tsconfig.json b/tsconfig.json index 3ab46be..c178ebe 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,8 @@ "lib": [ "ES2022" ], + "declaration": true, + "declarationMap": true, "target": "ES2022", "module": "ES2022", "moduleResolution": "node",