parent
02de7a1757
commit
bfce541e8f
@ -0,0 +1,3 @@ |
||||
{ |
||||
"extends": ["../../.eslintrc"] |
||||
} |
@ -0,0 +1,6 @@ |
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const sharedConfig = require("../../jest.config.js"); |
||||
module.exports = { |
||||
...sharedConfig, |
||||
rootDir: "./", |
||||
}; |
@ -0,0 +1,46 @@ |
||||
{ |
||||
"name": "@ldo/jsonld-dataset-proxy", |
||||
"version": "0.0.0", |
||||
"description": "", |
||||
"main": "dist/index.js", |
||||
"scripts": { |
||||
"build": "tsc --project tsconfig.build.json", |
||||
"build:watch": "tsc-watch", |
||||
"test": "jest --coverage", |
||||
"prepublishOnly": "npm run test && npm run build", |
||||
"start": "ts-node ./example/example.ts", |
||||
"start:lang": "ts-node ./example/languageExample.ts", |
||||
"lint": "eslint src/** --fix --no-error-on-unmatched-pattern" |
||||
}, |
||||
"repository": { |
||||
"type": "git", |
||||
"url": "git+https://github.com/o-development/jsonld-dataset-proxy.git" |
||||
}, |
||||
"author": "Jackson Morgan", |
||||
"license": "MIT", |
||||
"bugs": { |
||||
"url": "https://github.com/o-development/jsonld-dataset-proxy/issues" |
||||
}, |
||||
"homepage": "https://github.com/o-development/jsonld-dataset-proxy#readme", |
||||
"devDependencies": { |
||||
"@rdfjs/types": "^1.0.1", |
||||
"@types/jest": "^27.0.3", |
||||
"@types/jsonld": "^1.5.6", |
||||
"@types/n3": "^1.10.4", |
||||
"@types/rdfjs__dataset": "^1.0.5", |
||||
"@types/shexj": "2.1.4", |
||||
"jest": "^27.4.5", |
||||
"shex-test": "^0.5.5", |
||||
"ts-jest": "^27.1.2", |
||||
"ts-node": "^10.4.0", |
||||
"tsc-watch": "^6.0.0" |
||||
}, |
||||
"files": [ |
||||
"dist" |
||||
], |
||||
"dependencies": { |
||||
"@rdfjs/data-model": "^1.2.0", |
||||
"jsonld2graphobject": "^0.0.4", |
||||
"o-dataset-pack": "^0.2.14" |
||||
} |
||||
} |
After Width: | Height: | Size: 269 KiB |
@ -0,0 +1,90 @@ |
||||
import type { ContextDefinition, ExpandedTermDefinition } from "jsonld"; |
||||
|
||||
// Create JSONLD Shorthands
|
||||
const shorthandToIriMap: Record<string, string> = { |
||||
"@type": "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", |
||||
}; |
||||
|
||||
/** |
||||
* Context Util |
||||
* Handles the JSON-LD context and allows conversion between IRIs and terms |
||||
*/ |
||||
export class ContextUtil { |
||||
public readonly context: ContextDefinition; |
||||
private iriToKeyMap: Record<string, string>; |
||||
|
||||
constructor(context: ContextDefinition) { |
||||
this.context = context; |
||||
this.iriToKeyMap = {}; |
||||
Object.entries(context).forEach(([contextKey, contextValue]) => { |
||||
if (typeof contextValue === "string") { |
||||
this.iriToKeyMap[this.keyIdToIri(contextValue)] = contextKey; |
||||
} else if ( |
||||
typeof contextValue === "object" && |
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(contextValue as any)["@id"] |
||||
) { |
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
this.iriToKeyMap[this.keyIdToIri((contextValue as any)["@id"])] = |
||||
contextKey; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
public keyToIri(key: string): string { |
||||
if (!this.context[key]) { |
||||
return key; |
||||
} else if (typeof this.context[key] === "string") { |
||||
return this.keyIdToIri(this.context[key] as string); |
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} else if (this.context[key] && (this.context[key] as any)["@id"]) { |
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return this.keyIdToIri((this.context[key] as any)["@id"]); |
||||
} |
||||
return key; |
||||
} |
||||
|
||||
private keyIdToIri(keyId: string) { |
||||
if (shorthandToIriMap[keyId]) { |
||||
return shorthandToIriMap[keyId]; |
||||
} else { |
||||
return keyId; |
||||
} |
||||
} |
||||
|
||||
public iriToKey(iri: string): string { |
||||
if (this.iriToKeyMap[iri]) { |
||||
return this.iriToKeyMap[iri]; |
||||
} |
||||
return iri; |
||||
} |
||||
|
||||
public getType(key: string): string { |
||||
if ( |
||||
typeof this.context[key] === "object" && |
||||
(this.context[key] as ExpandedTermDefinition)["@type"] |
||||
) { |
||||
return (this.context[key] as ExpandedTermDefinition)["@type"] as string; |
||||
} |
||||
return "http://www.w3.org/2001/XMLSchema#string"; |
||||
} |
||||
|
||||
public isArray(key: string): boolean { |
||||
return !!( |
||||
this.context[key] && |
||||
typeof this.context[key] === "object" && |
||||
(this.context[key] as ExpandedTermDefinition)["@container"] && |
||||
(this.context[key] as ExpandedTermDefinition)["@container"] === "@set" |
||||
); |
||||
} |
||||
|
||||
public isLangString(key: string): boolean { |
||||
return !!( |
||||
this.context[key] && |
||||
typeof this.context[key] === "object" && |
||||
(this.context[key] as ExpandedTermDefinition)["@type"] && |
||||
(this.context[key] as ExpandedTermDefinition)["@type"] === |
||||
"http://www.w3.org/1999/02/22-rdf-syntax-ns#langString" |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,105 @@ |
||||
import { blankNode, namedNode } from "@rdfjs/data-model"; |
||||
import type { BlankNode, NamedNode } from "@rdfjs/types"; |
||||
import type { LanguageOrdering } from "./language/languageTypes"; |
||||
import type { ProxyContext } from "./ProxyContext"; |
||||
import type { GraphType, ObjectLike, QuadMatch } from "./types"; |
||||
|
||||
/** |
||||
* Helps build JSON LD Dataset Proxies for a specific dataset and context |
||||
*/ |
||||
export class JsonldDatasetProxyBuilder { |
||||
private proxyContext: ProxyContext; |
||||
|
||||
constructor(proxyContext: ProxyContext) { |
||||
this.proxyContext = proxyContext; |
||||
} |
||||
|
||||
/** |
||||
* Designates that all Jsonld Dataset Proxies created should write to the |
||||
* specified graphs |
||||
*/ |
||||
write(...graphs: GraphType[]): JsonldDatasetProxyBuilder { |
||||
return new JsonldDatasetProxyBuilder( |
||||
this.proxyContext.duplicate({ writeGraphs: graphs }), |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* List the language tags in the order they should be used. When a langString |
||||
* is accessed, LDO will search for values in the order of language given. |
||||
* @param languageOrdering The ordering of languages. For example |
||||
* ("en", "fr", "none", "other"). Defaults to |
||||
* ("none", "en", "other") |
||||
*/ |
||||
setLanguagePreferences( |
||||
...languageOrdering: LanguageOrdering |
||||
): JsonldDatasetProxyBuilder { |
||||
return new JsonldDatasetProxyBuilder( |
||||
this.proxyContext.duplicate({ languageOrdering }), |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Creates a JSON LD Dataset Proxy that matches the given subject |
||||
* @param subject The node to match |
||||
*/ |
||||
fromSubject<T extends ObjectLike>(subject: NamedNode | BlankNode): T { |
||||
return this.proxyContext.createSubjectProxy(subject) as unknown as T; |
||||
} |
||||
|
||||
/** |
||||
* Matches Subjects to provided predicates, objects, and graphs. Returns a |
||||
* JSON LD Dataset that can be read an modified. |
||||
* @param predicate The predicate to match |
||||
* @param object The object to match |
||||
* @param graph The graph to match |
||||
*/ |
||||
matchSubject<T extends ObjectLike>( |
||||
predicate?: QuadMatch[1], |
||||
object?: QuadMatch[2], |
||||
graph?: QuadMatch[3], |
||||
): T[] { |
||||
return this.proxyContext.createArrayProxy( |
||||
[null, predicate, object, graph], |
||||
true, |
||||
) as unknown as T[]; |
||||
} |
||||
|
||||
/** |
||||
* Matches Objects to provided subjects, predicates, and graphs. Returns a |
||||
* JSON LD Dataset that can be read an modified. |
||||
* @param subject The subject to match |
||||
* @param predicate The predicate to match |
||||
* @param graph The graph to match |
||||
*/ |
||||
matchObject<T extends ObjectLike>( |
||||
subject?: QuadMatch[0], |
||||
predicate?: QuadMatch[1], |
||||
graph?: QuadMatch[3], |
||||
): T[] { |
||||
return this.proxyContext.createArrayProxy([ |
||||
subject, |
||||
predicate, |
||||
null, |
||||
graph, |
||||
]) as unknown as T[]; |
||||
} |
||||
|
||||
/** |
||||
* Takes a given object and places it in the dataset while returning a JSON LD |
||||
* Dataset Proxy representing the object. |
||||
* |
||||
* @param inputData Initial Data |
||||
* @param graph Optional graph to save this data to |
||||
*/ |
||||
fromJson<T extends ObjectLike>(inputData: T): T { |
||||
const entryNode = inputData["@id"] |
||||
? namedNode(inputData["@id"]) |
||||
: blankNode(); |
||||
const proxy = this.fromSubject<T>(entryNode); |
||||
Object.entries(inputData).forEach(([key, value]) => { |
||||
proxy[<keyof T>key] = value; |
||||
}); |
||||
return proxy; |
||||
} |
||||
} |
@ -0,0 +1,110 @@ |
||||
import type { BlankNode, Dataset, NamedNode } from "@rdfjs/types"; |
||||
import type { ArrayProxyTarget } from "./arrayProxy/createArrayHandler"; |
||||
import { createArrayHandler } from "./arrayProxy/createArrayHandler"; |
||||
import { createSubjectHandler } from "./subjectProxy/createSubjectHandler"; |
||||
import type { SubjectProxy } from "./subjectProxy/SubjectProxy"; |
||||
import type { ArrayProxy } from "./arrayProxy/ArrayProxy"; |
||||
import type { GraphType, QuadMatch } from "./types"; |
||||
import { _getUnderlyingArrayTarget } from "./types"; |
||||
import type { ContextUtil } from "./ContextUtil"; |
||||
import type { LanguageOrdering } from "./language/languageTypes"; |
||||
|
||||
export interface ProxyContextOptions { |
||||
dataset: Dataset; |
||||
contextUtil: ContextUtil; |
||||
writeGraphs: GraphType[]; |
||||
languageOrdering: LanguageOrdering; |
||||
prefilledArrayTargets?: ArrayProxyTarget[]; |
||||
state?: Record<string, unknown>; |
||||
} |
||||
|
||||
/** |
||||
* This file keeps track of the target objects used in the proxies. |
||||
* The reason is so that JSON.stringify does not recurse inifinitely |
||||
* when it encounters a circular object. |
||||
*/ |
||||
export class ProxyContext { |
||||
private subjectMap: Map<string, SubjectProxy> = new Map(); |
||||
private arrayMap: Map<string, ArrayProxy> = new Map(); |
||||
|
||||
readonly dataset: Dataset; |
||||
readonly contextUtil: ContextUtil; |
||||
readonly writeGraphs: GraphType[]; |
||||
readonly languageOrdering: LanguageOrdering; |
||||
public state: Record<string, unknown>; |
||||
|
||||
constructor(options: ProxyContextOptions) { |
||||
this.dataset = options.dataset; |
||||
this.contextUtil = options.contextUtil; |
||||
this.writeGraphs = options.writeGraphs; |
||||
this.languageOrdering = options.languageOrdering; |
||||
this.state = options.state || {}; |
||||
if (options.prefilledArrayTargets) { |
||||
options.prefilledArrayTargets.forEach((target) => { |
||||
this.createArrayProxy(target[0], target[2], target); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
public createSubjectProxy(node: NamedNode | BlankNode): SubjectProxy { |
||||
if (!this.subjectMap.has(node.value)) { |
||||
const proxy = new Proxy( |
||||
{ "@id": node }, |
||||
this.createSubjectHandler(), |
||||
) as unknown as SubjectProxy; |
||||
this.subjectMap.set(node.value, proxy); |
||||
} |
||||
return this.subjectMap.get(node.value) as SubjectProxy; |
||||
} |
||||
|
||||
protected createSubjectHandler() { |
||||
return createSubjectHandler(this); |
||||
} |
||||
|
||||
private getArrayKey(...quadMatch: QuadMatch) { |
||||
return `${quadMatch[0]?.value || "undefined"}|${ |
||||
quadMatch[1]?.value || "undefined" |
||||
}|${quadMatch[2]?.value || "undefined"}|${ |
||||
quadMatch[3]?.value || "undefined" |
||||
}`;
|
||||
} |
||||
|
||||
public createArrayProxy( |
||||
quadMatch: QuadMatch, |
||||
isSubjectOriented = false, |
||||
initialTarget?: ArrayProxyTarget, |
||||
isLangStringArray?: boolean, |
||||
): ArrayProxy { |
||||
const key = this.getArrayKey(...quadMatch); |
||||
if (!this.arrayMap.has(key)) { |
||||
const proxy = new Proxy( |
||||
initialTarget || [quadMatch, [], isSubjectOriented, isLangStringArray], |
||||
this.createArrayHandler(), |
||||
) as unknown as ArrayProxy; |
||||
this.arrayMap.set(key, proxy); |
||||
} |
||||
return this.arrayMap.get(key) as ArrayProxy; |
||||
} |
||||
|
||||
protected createArrayHandler() { |
||||
return createArrayHandler(this); |
||||
} |
||||
|
||||
public duplicate(alternativeOptions: Partial<ProxyContextOptions>) { |
||||
const prefilledArrayTargets: ArrayProxyTarget[] = []; |
||||
this.arrayMap.forEach((value) => { |
||||
prefilledArrayTargets.push(value[_getUnderlyingArrayTarget]); |
||||
}); |
||||
const fullOptions: ProxyContextOptions = { |
||||
...{ |
||||
dataset: this.dataset, |
||||
contextUtil: this.contextUtil, |
||||
writeGraphs: this.writeGraphs, |
||||
languageOrdering: this.languageOrdering, |
||||
prefilledArrayTargets, |
||||
}, |
||||
...alternativeOptions, |
||||
}; |
||||
return new ProxyContext(fullOptions); |
||||
} |
||||
} |
@ -0,0 +1,20 @@ |
||||
import type { Dataset } from "@rdfjs/types"; |
||||
import type { ArrayProxyTarget } from "./createArrayHandler"; |
||||
import type { |
||||
ObjectType, |
||||
_getNodeAtIndex, |
||||
_getUnderlyingArrayTarget, |
||||
_getUnderlyingDataset, |
||||
_getUnderlyingMatch, |
||||
_proxyContext, |
||||
} from "../types"; |
||||
import { _getUnderlyingNode } from "../types"; |
||||
import type { ProxyContext } from "../ProxyContext"; |
||||
|
||||
export type ArrayProxy = Array<unknown> & { |
||||
readonly [_getUnderlyingDataset]: Dataset; |
||||
readonly [_getUnderlyingMatch]: ArrayProxyTarget[0]; |
||||
readonly [_getNodeAtIndex]: (index: number) => ObjectType | undefined; |
||||
readonly [_getUnderlyingArrayTarget]: ArrayProxyTarget; |
||||
[_proxyContext]: ProxyContext; |
||||
}; |
@ -0,0 +1,216 @@ |
||||
import type { ArrayProxyTarget } from "./createArrayHandler"; |
||||
import type { ObjectJsonRepresentation } from "../util/nodeToJsonldRepresentation"; |
||||
import { nodeToJsonldRepresentation } from "../util/nodeToJsonldRepresentation"; |
||||
import { modifyArray } from "./modifyArray"; |
||||
import type { ProxyContext } from "../ProxyContext"; |
||||
|
||||
export type methodBuilder<Return> = ( |
||||
target: ArrayProxyTarget, |
||||
key: string, |
||||
proxyContext: ProxyContext, |
||||
) => Return; |
||||
|
||||
export interface ArrayMethodBuildersType { |
||||
copyWithin: methodBuilder<Array<ObjectJsonRepresentation>["copyWithin"]>; |
||||
fill: methodBuilder<Array<ObjectJsonRepresentation>["fill"]>; |
||||
pop: methodBuilder<Array<ObjectJsonRepresentation>["pop"]>; |
||||
push: methodBuilder<Array<ObjectJsonRepresentation>["push"]>; |
||||
reverse: methodBuilder<Array<ObjectJsonRepresentation>["reverse"]>; |
||||
shift: methodBuilder<Array<ObjectJsonRepresentation>["shift"]>; |
||||
sort: methodBuilder<Array<ObjectJsonRepresentation>["sort"]>; |
||||
splice: methodBuilder<Array<ObjectJsonRepresentation>["splice"]>; |
||||
unshift: methodBuilder<Array<ObjectJsonRepresentation>["unshift"]>; |
||||
} |
||||
|
||||
export const methodNames: Set<keyof ArrayMethodBuildersType> = new Set([ |
||||
"copyWithin", |
||||
"fill", |
||||
"pop", |
||||
"push", |
||||
"reverse", |
||||
"shift", |
||||
"sort", |
||||
"splice", |
||||
"unshift", |
||||
]); |
||||
|
||||
export const arrayMethodsBuilders: ArrayMethodBuildersType = { |
||||
copyWithin: (target, key, proxyContext) => { |
||||
return (targetIndex, start, end) => { |
||||
return modifyArray( |
||||
{ |
||||
target, |
||||
key, |
||||
quadsToDelete: (quads) => { |
||||
const realEnd = end || quads.length; |
||||
return quads.slice(targetIndex, targetIndex + (realEnd - start)); |
||||
}, |
||||
modifyCoreArray: (coreArray) => { |
||||
coreArray.copyWithin(targetIndex, start, end); |
||||
return proxyContext.createArrayProxy( |
||||
target[0], |
||||
target[2], |
||||
) as ObjectJsonRepresentation[]; |
||||
}, |
||||
}, |
||||
proxyContext, |
||||
); |
||||
}; |
||||
}, |
||||
fill: (target, key, proxyContext) => { |
||||
return (value, start, end) => { |
||||
return modifyArray( |
||||
{ |
||||
target, |
||||
key, |
||||
toAdd: [value], |
||||
quadsToDelete: (quads) => { |
||||
return quads.slice(start, end); |
||||
}, |
||||
modifyCoreArray: (coreArray, addedValues) => { |
||||
coreArray.fill(addedValues[0], start, end); |
||||
return proxyContext.createArrayProxy( |
||||
target[0], |
||||
target[2], |
||||
) as ObjectJsonRepresentation[]; |
||||
}, |
||||
}, |
||||
proxyContext, |
||||
); |
||||
}; |
||||
}, |
||||
pop: (target, key, proxyContext) => { |
||||
return () => { |
||||
return modifyArray( |
||||
{ |
||||
target, |
||||
key, |
||||
quadsToDelete: (quads) => { |
||||
return quads[quads.length - 1] ? [quads[quads.length - 1]] : []; |
||||
}, |
||||
modifyCoreArray: (coreArray) => { |
||||
const popped = coreArray.pop(); |
||||
return popped |
||||
? nodeToJsonldRepresentation(popped, proxyContext) |
||||
: undefined; |
||||
}, |
||||
}, |
||||
proxyContext, |
||||
); |
||||
}; |
||||
}, |
||||
push: (target, key, proxyContext) => { |
||||
return (...args) => { |
||||
return modifyArray( |
||||
{ |
||||
target, |
||||
key, |
||||
toAdd: args, |
||||
modifyCoreArray: (coreArray, addedValues) => { |
||||
coreArray.push(...addedValues); |
||||
return proxyContext.createArrayProxy(target[0], target[2]).length; |
||||
}, |
||||
}, |
||||
proxyContext, |
||||
); |
||||
}; |
||||
}, |
||||
reverse: (target, _key, proxyContext) => { |
||||
return () => { |
||||
target[1].reverse(); |
||||
return proxyContext.createArrayProxy( |
||||
target[0], |
||||
target[2], |
||||
) as ObjectJsonRepresentation[]; |
||||
}; |
||||
}, |
||||
shift: (target, key, proxyContext) => { |
||||
return () => { |
||||
return modifyArray( |
||||
{ |
||||
target, |
||||
key, |
||||
quadsToDelete: (quads) => { |
||||
return quads[0] ? [quads[0]] : []; |
||||
}, |
||||
modifyCoreArray: (coreArray) => { |
||||
const shifted = coreArray.shift(); |
||||
return shifted |
||||
? nodeToJsonldRepresentation(shifted, proxyContext) |
||||
: undefined; |
||||
}, |
||||
}, |
||||
proxyContext, |
||||
); |
||||
}; |
||||
}, |
||||
sort: (target, _key, proxyContext) => { |
||||
return (compareFunction) => { |
||||
if (compareFunction) { |
||||
target[1].sort((a, b) => { |
||||
return compareFunction( |
||||
nodeToJsonldRepresentation(a, proxyContext), |
||||
nodeToJsonldRepresentation(b, proxyContext), |
||||
); |
||||
}); |
||||
} else if (target) { |
||||
target[1].sort((a, b) => { |
||||
const aReal = nodeToJsonldRepresentation(a, proxyContext); |
||||
const bReal = nodeToJsonldRepresentation(b, proxyContext); |
||||
if (aReal > bReal) { |
||||
return 1; |
||||
} else if (bReal > aReal) { |
||||
return -1; |
||||
} else { |
||||
return 0; |
||||
} |
||||
}); |
||||
} |
||||
return proxyContext.createArrayProxy( |
||||
target[0], |
||||
target[2], |
||||
) as ObjectJsonRepresentation[]; |
||||
}; |
||||
}, |
||||
splice: (target, key, proxyContext) => { |
||||
return (start, deleteCount, ...items: ObjectJsonRepresentation[]) => { |
||||
return modifyArray( |
||||
{ |
||||
target, |
||||
key, |
||||
toAdd: items, |
||||
quadsToDelete: (quads) => { |
||||
return quads.splice(start, deleteCount); |
||||
}, |
||||
modifyCoreArray: (coreArray, addedValues) => { |
||||
const spliced = coreArray.splice( |
||||
start, |
||||
deleteCount || 0, |
||||
...addedValues, |
||||
); |
||||
return spliced.map((node) => { |
||||
return nodeToJsonldRepresentation(node, proxyContext); |
||||
}); |
||||
}, |
||||
}, |
||||
proxyContext, |
||||
); |
||||
}; |
||||
}, |
||||
unshift: (target, key, proxyContext) => { |
||||
return (...args) => { |
||||
return modifyArray( |
||||
{ |
||||
target, |
||||
key, |
||||
toAdd: args, |
||||
modifyCoreArray: (coreArray, addedValues) => { |
||||
coreArray.unshift(...addedValues); |
||||
return proxyContext.createArrayProxy(target[0], target[2]).length; |
||||
}, |
||||
}, |
||||
proxyContext, |
||||
); |
||||
}; |
||||
}, |
||||
}; |
@ -0,0 +1,177 @@ |
||||
import type { NamedNode } from "@rdfjs/types"; |
||||
import type { ObjectJsonRepresentation } from "../util/nodeToJsonldRepresentation"; |
||||
import { nodeToJsonldRepresentation } from "../util/nodeToJsonldRepresentation"; |
||||
import { quad } from "@rdfjs/data-model"; |
||||
import type { ArrayMethodBuildersType } from "./arrayMethods"; |
||||
import { arrayMethodsBuilders, methodNames } from "./arrayMethods"; |
||||
import type { ObjectType, QuadMatch, SubjectType } from "../types"; |
||||
import { |
||||
_getNodeAtIndex, |
||||
_getUnderlyingArrayTarget, |
||||
_getUnderlyingDataset, |
||||
_getUnderlyingMatch, |
||||
_isSubjectOriented, |
||||
_proxyContext, |
||||
} from "../types"; |
||||
import { modifyArray } from "./modifyArray"; |
||||
import type { ProxyContext } from "../ProxyContext"; |
||||
import { NodeSet } from "../util/NodeSet"; |
||||
import { filterQuadsByLanguageOrdering } from "../language/languageUtils"; |
||||
|
||||
export type ArrayProxyTarget = [ |
||||
quadMatch: QuadMatch, |
||||
curArray: ObjectType[], |
||||
isSubjectOriented?: boolean, |
||||
isLangStringArray?: boolean, |
||||
]; |
||||
|
||||
function updateArrayOrder( |
||||
target: ArrayProxyTarget, |
||||
proxyContext: ProxyContext, |
||||
): void { |
||||
let quads = proxyContext.dataset.match(...target[0]); |
||||
if (target[3]) { |
||||
// Is lang string array
|
||||
quads = filterQuadsByLanguageOrdering(quads, proxyContext.languageOrdering); |
||||
} |
||||
const datasetObjects = new NodeSet(); |
||||
quads.toArray().forEach((quad) => { |
||||
// If this this a subject-oriented document
|
||||
if (target[2]) { |
||||
datasetObjects.add(quad.subject as SubjectType); |
||||
} else { |
||||
datasetObjects.add(quad.object as ObjectType); |
||||
} |
||||
}); |
||||
const processedObjects: ObjectType[] = []; |
||||
target[1].forEach((arrItem) => { |
||||
if (datasetObjects.has(arrItem)) { |
||||
processedObjects.push(arrItem); |
||||
datasetObjects.delete(arrItem); |
||||
} |
||||
}); |
||||
datasetObjects.toArray().forEach((datasetObject) => { |
||||
processedObjects.push(datasetObject); |
||||
}); |
||||
target[1] = processedObjects; |
||||
} |
||||
|
||||
function getProcessedArray( |
||||
target: ArrayProxyTarget, |
||||
proxyContext: ProxyContext, |
||||
): ObjectJsonRepresentation[] { |
||||
return target[1].map((node) => { |
||||
return nodeToJsonldRepresentation(node, proxyContext); |
||||
}); |
||||
} |
||||
|
||||
export function createArrayHandler( |
||||
proxyContext: ProxyContext, |
||||
): ProxyHandler<ArrayProxyTarget> { |
||||
return { |
||||
get(target, key, ...rest) { |
||||
switch (key) { |
||||
case _getUnderlyingDataset: |
||||
return proxyContext.dataset; |
||||
case _getUnderlyingMatch: |
||||
return target[0]; |
||||
case _isSubjectOriented: |
||||
return target[2]; |
||||
case _getUnderlyingArrayTarget: |
||||
return target; |
||||
case _proxyContext: |
||||
return proxyContext; |
||||
case _getNodeAtIndex: |
||||
return (index: number): ObjectType | undefined => { |
||||
updateArrayOrder(target, proxyContext); |
||||
return target[1][index]; |
||||
}; |
||||
} |
||||
|
||||
// TODO: Because of this, every get operation is O(n). Consider changing
|
||||
// this
|
||||
updateArrayOrder(target, proxyContext); |
||||
const processedArray = getProcessedArray(target, proxyContext); |
||||
if (methodNames.has(key as keyof ArrayMethodBuildersType)) { |
||||
return arrayMethodsBuilders[key as keyof ArrayMethodBuildersType]( |
||||
target, |
||||
key as string, |
||||
proxyContext, |
||||
); |
||||
} |
||||
return Reflect.get(processedArray, key, ...rest); |
||||
}, |
||||
getOwnPropertyDescriptor(target, key, ...rest) { |
||||
updateArrayOrder(target, proxyContext); |
||||
const processedArray = getProcessedArray(target, proxyContext); |
||||
return Reflect.getOwnPropertyDescriptor(processedArray, key, ...rest); |
||||
}, |
||||
ownKeys(target, ...rest) { |
||||
updateArrayOrder(target, proxyContext); |
||||
const processedArray = getProcessedArray(target, proxyContext); |
||||
return Reflect.ownKeys(processedArray, ...rest); |
||||
}, |
||||
getPrototypeOf(target, ...rest) { |
||||
updateArrayOrder(target, proxyContext); |
||||
const processedObjects = getProcessedArray(target, proxyContext); |
||||
return Reflect.getPrototypeOf(processedObjects, ...rest); |
||||
}, |
||||
has(target, ...rest) { |
||||
updateArrayOrder(target, proxyContext); |
||||
const processedObjects = getProcessedArray(target, proxyContext); |
||||
return Reflect.has(processedObjects, ...rest); |
||||
}, |
||||
set(target, key, value, ...rest) { |
||||
if (key === _proxyContext) { |
||||
proxyContext = value; |
||||
return true; |
||||
} |
||||
updateArrayOrder(target, proxyContext); |
||||
if (typeof key !== "symbol" && !isNaN(parseInt(key as string))) { |
||||
const index = parseInt(key); |
||||
return modifyArray( |
||||
{ |
||||
target, |
||||
key, |
||||
toAdd: [value], |
||||
quadsToDelete(allQuads) { |
||||
return allQuads[index] ? [allQuads[index]] : []; |
||||
}, |
||||
modifyCoreArray(coreArray, addedValues) { |
||||
coreArray[index] = addedValues[0]; |
||||
return true; |
||||
}, |
||||
}, |
||||
proxyContext, |
||||
); |
||||
} |
||||
return Reflect.set(target[1], key, ...rest); |
||||
}, |
||||
deleteProperty(target, key) { |
||||
const { dataset } = proxyContext; |
||||
if (typeof key !== "symbol" && !isNaN(parseInt(key as string))) { |
||||
const objectQuad = dataset.match(...target[0]).toArray()[parseInt(key)]; |
||||
if (!objectQuad) { |
||||
return true; |
||||
} |
||||
const term = target[2] ? objectQuad.subject : objectQuad.object; |
||||
if (term.termType === "Literal") { |
||||
const subject = target[0][0] as NamedNode; |
||||
const predicate = target[0][1] as NamedNode; |
||||
if (subject && predicate) { |
||||
dataset.delete(quad(subject, predicate, term)); |
||||
} |
||||
return true; |
||||
} else if ( |
||||
term.termType === "NamedNode" || |
||||
term.termType === "BlankNode" |
||||
) { |
||||
dataset.deleteMatches(term, undefined, undefined); |
||||
dataset.deleteMatches(undefined, undefined, term); |
||||
return true; |
||||
} |
||||
} |
||||
return true; |
||||
}, |
||||
}; |
||||
} |
@ -0,0 +1,23 @@ |
||||
import { |
||||
_getNodeAtIndex, |
||||
_getUnderlyingArrayTarget, |
||||
_getUnderlyingDataset, |
||||
_getUnderlyingMatch, |
||||
_getUnderlyingNode, |
||||
_proxyContext, |
||||
_writeGraphs, |
||||
} from "../types"; |
||||
import type { ArrayProxy } from "./ArrayProxy"; |
||||
|
||||
export function isArrayProxy(someObject?: unknown): someObject is ArrayProxy { |
||||
if (!someObject) return false; |
||||
if (typeof someObject !== "object") return false; |
||||
const potentialArrayProxy = someObject as ArrayProxy; |
||||
|
||||
return !( |
||||
typeof potentialArrayProxy[_getUnderlyingDataset] !== "object" || |
||||
typeof potentialArrayProxy[_getUnderlyingMatch] !== "object" || |
||||
typeof potentialArrayProxy[_getNodeAtIndex] !== "function" || |
||||
typeof potentialArrayProxy[_getUnderlyingArrayTarget] !== "object" |
||||
); |
||||
} |
@ -0,0 +1,129 @@ |
||||
import { defaultGraph } from "@rdfjs/data-model"; |
||||
import type { Quad } from "@rdfjs/types"; |
||||
import { ProxyTransactionalDataset } from "o-dataset-pack"; |
||||
import { createExtendedDatasetFactory } from "o-dataset-pack/dist/createExtendedDataset"; |
||||
import type { ProxyContext } from "../ProxyContext"; |
||||
import type { ObjectType } from "../types"; |
||||
import { addObjectToDataset } from "../util/addObjectToDataset"; |
||||
import { |
||||
getNodeFromRawObject, |
||||
getNodeFromRawValue, |
||||
} from "../util/getNodeFromRaw"; |
||||
import { nodeToString } from "../util/NodeSet"; |
||||
import type { ObjectJsonRepresentation } from "../util/nodeToJsonldRepresentation"; |
||||
import type { RawObject, RawValue } from "../util/RawObject"; |
||||
import type { ArrayProxyTarget } from "./createArrayHandler"; |
||||
|
||||
export function checkArrayModification( |
||||
target: ArrayProxyTarget, |
||||
objectsToAdd: RawValue[], |
||||
proxyContext: ProxyContext, |
||||
) { |
||||
if (target[2]) { |
||||
for (const objectToAdd of objectsToAdd) { |
||||
// Undefined is fine no matter what
|
||||
if (objectToAdd === undefined) { |
||||
return; |
||||
} |
||||
if (typeof objectToAdd !== "object") { |
||||
throw new Error( |
||||
`Cannot add a literal "${objectToAdd}"(${typeof objectToAdd}) to a subject-oriented collection.`, |
||||
); |
||||
} |
||||
// Create a test dataset to see if the inputted data is valid
|
||||
const testDataset = new ProxyTransactionalDataset( |
||||
proxyContext.dataset, |
||||
createExtendedDatasetFactory(), |
||||
); |
||||
addObjectToDataset( |
||||
objectToAdd as RawObject, |
||||
false, |
||||
proxyContext.duplicate({ |
||||
writeGraphs: [defaultGraph()], |
||||
}), |
||||
); |
||||
const isValidAddition = |
||||
testDataset.match( |
||||
getNodeFromRawObject(objectToAdd, proxyContext.contextUtil), |
||||
target[0][1], |
||||
target[0][2], |
||||
).size !== 0; |
||||
if (!isValidAddition) { |
||||
throw new Error( |
||||
`Cannot add value to collection. This must contain a quad that matches (${nodeToString( |
||||
target[0][0], |
||||
)}, ${nodeToString(target[0][1])}, ${nodeToString( |
||||
target[0][2], |
||||
)}, ${nodeToString(target[0][3])})`,
|
||||
); |
||||
} |
||||
} |
||||
} else if (!target[0][0] || !target[0][1]) { |
||||
throw new Error( |
||||
"A collection that does not specify a match for both a subject or predicate cannot be modified directly.", |
||||
); |
||||
} |
||||
} |
||||
|
||||
export function modifyArray<ReturnType>( |
||||
config: { |
||||
target: ArrayProxyTarget; |
||||
key: string; |
||||
toAdd?: RawValue[]; |
||||
quadsToDelete?: (quads: Quad[]) => Quad[]; |
||||
modifyCoreArray: ( |
||||
coreArray: ArrayProxyTarget[1], |
||||
addedValues: ArrayProxyTarget[1], |
||||
) => ReturnType; |
||||
}, |
||||
proxyContext: ProxyContext, |
||||
): ReturnType { |
||||
const { target, toAdd, quadsToDelete, modifyCoreArray, key } = config; |
||||
const { dataset, contextUtil } = proxyContext; |
||||
checkArrayModification(target, toAdd || [], proxyContext); |
||||
|
||||
// Remove appropriate Quads
|
||||
if (quadsToDelete) { |
||||
const quadArr = dataset.match(...target[0]).toArray(); |
||||
const deleteQuadArr = quadsToDelete(quadArr); |
||||
// Filter out overlapping items
|
||||
deleteQuadArr.forEach((delQuad) => { |
||||
if (target[2]) { |
||||
dataset.deleteMatches(delQuad.subject, undefined, undefined); |
||||
} else { |
||||
dataset.delete(delQuad); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
// Add new items to the dataset
|
||||
const added = toAdd |
||||
?.map((item) => { |
||||
return typeof item === "object" |
||||
? addObjectToDataset(item, false, proxyContext) |
||||
: item; |
||||
}) |
||||
.filter( |
||||
(val) => val != undefined, |
||||
) as NonNullable<ObjectJsonRepresentation>[]; |
||||
if (!target[2] && target[0][0] && target[0][1] && added) { |
||||
addObjectToDataset( |
||||
{ |
||||
"@id": target[0][0], |
||||
[contextUtil.iriToKey(target[0][1].value)]: added, |
||||
} as RawObject, |
||||
false, |
||||
proxyContext, |
||||
); |
||||
} |
||||
const addedNodes = added |
||||
? (added |
||||
.map((addedValue) => { |
||||
return getNodeFromRawValue(key, addedValue, proxyContext); |
||||
}) |
||||
.filter((val) => val != undefined) as ObjectType[]) |
||||
: []; |
||||
|
||||
// Allow the base array to be modified
|
||||
return modifyCoreArray(target[1], addedNodes); |
||||
} |
@ -0,0 +1,62 @@ |
||||
import { namedNode } from "@rdfjs/data-model"; |
||||
import { |
||||
getSubjectProxyFromObject, |
||||
isSubjectProxy, |
||||
} from "./subjectProxy/isSubjectProxy"; |
||||
import type { GraphType, ObjectLike, ObjectType } from "./types"; |
||||
import { |
||||
_getNodeAtIndex, |
||||
_getUnderlyingDataset, |
||||
_getUnderlyingMatch, |
||||
_getUnderlyingNode, |
||||
_proxyContext, |
||||
} from "./types"; |
||||
|
||||
/** |
||||
* Returns the graph for which a defined triple is a member |
||||
* @param subject A JsonldDatasetProxy that represents the subject |
||||
* @param predicate The key on the JsonldDatasetProxy |
||||
* @param object The direct object. This can be a JsonldDatasetProxy or the index |
||||
* @returns a list of graphs for which the triples are members |
||||
*/ |
||||
export function graphOf<Subject extends ObjectLike, Key extends keyof Subject>( |
||||
subject: Subject, |
||||
predicate: Key, |
||||
object?: NonNullable<Subject[Key]> extends Array<unknown> |
||||
? number | ObjectLike |
||||
: ObjectLike, |
||||
): GraphType[] { |
||||
const subjectProxy = getSubjectProxyFromObject(subject); |
||||
const proxyContext = subjectProxy[_proxyContext]; |
||||
const subjectNode = subjectProxy[_getUnderlyingNode]; |
||||
const predicateNode = namedNode( |
||||
proxyContext.contextUtil.keyToIri(predicate as string), |
||||
); |
||||
let objectNode: ObjectType | null; |
||||
if (object == null) { |
||||
objectNode = null; |
||||
} else if (typeof object === "number") { |
||||
const proxyArray = subject[predicate]; |
||||
if (!proxyArray[_getUnderlyingMatch]) { |
||||
throw new Error( |
||||
`Key "${String(predicate)}" of ${subject} is not an array.`, |
||||
); |
||||
} |
||||
if (!proxyArray[object]) { |
||||
throw new Error(`Index ${object} does not exist.`); |
||||
} |
||||
if (isSubjectProxy(proxyArray[object])) { |
||||
objectNode = proxyArray[object][1]; |
||||
} |
||||
objectNode = proxyArray[_getNodeAtIndex](object); |
||||
} else { |
||||
const objectProxy = getSubjectProxyFromObject(object); |
||||
objectNode = objectProxy[_getUnderlyingNode]; |
||||
} |
||||
const quads = subjectProxy[_getUnderlyingDataset].match( |
||||
subjectNode, |
||||
predicateNode, |
||||
objectNode, |
||||
); |
||||
return quads.toArray().map((quad): GraphType => quad.graph as GraphType); |
||||
} |
@ -0,0 +1,37 @@ |
||||
import { jsonldDatasetProxy } from "./jsonldDatasetProxy"; |
||||
|
||||
export default jsonldDatasetProxy; |
||||
export * from "./types"; |
||||
export * from "./ContextUtil"; |
||||
export * from "./ProxyContext"; |
||||
export * from "./JsonldDatasetProxyBuilder"; |
||||
export * from "./jsonldDatasetProxy"; |
||||
export * from "./write"; |
||||
export * from "./graphOf"; |
||||
export * from "./setLanguagePreferences"; |
||||
|
||||
export * from "./language/languagesOf"; |
||||
export * from "./language/languageMapProxy"; |
||||
export * from "./language/languageSet"; |
||||
export * from "./language/languageTypes"; |
||||
export * from "./language/languageUtils"; |
||||
|
||||
export * from "./arrayProxy/createArrayHandler"; |
||||
export * from "./arrayProxy/arrayMethods"; |
||||
export * from "./arrayProxy/ArrayProxy"; |
||||
export * from "./arrayProxy/modifyArray"; |
||||
export * from "./arrayProxy/isArrayProxy"; |
||||
|
||||
export * from "./subjectProxy/createSubjectHandler"; |
||||
export * from "./subjectProxy/SubjectProxy"; |
||||
export * from "./subjectProxy/getValueForKey"; |
||||
export * from "./subjectProxy/deleteFromDataset"; |
||||
export * from "./subjectProxy/isSubjectProxy"; |
||||
|
||||
export * from "./util/addObjectToDataset"; |
||||
export * from "./util/nodeToJsonldRepresentation"; |
||||
export * from "./util/RawObject"; |
||||
export * from "./util/getNodeFromRaw"; |
||||
export * from "./util/NodeSet"; |
||||
export * from "./util/isProxy"; |
||||
export * from "./util/createInteractOptions"; |
@ -0,0 +1,27 @@ |
||||
import { defaultGraph } from "@rdfjs/data-model"; |
||||
import type { Dataset } from "@rdfjs/types"; |
||||
import type { ContextDefinition } from "jsonld"; |
||||
import { ContextUtil } from "./ContextUtil"; |
||||
import { JsonldDatasetProxyBuilder } from "./JsonldDatasetProxyBuilder"; |
||||
import { ProxyContext } from "./ProxyContext"; |
||||
|
||||
/** |
||||
* Creates a JSON-LD Dataset Proxy |
||||
* |
||||
* @param inputDataset the source dataset |
||||
* @param context JSON-LD Context |
||||
* @returns a JSON-LD Dataset proxy |
||||
*/ |
||||
export function jsonldDatasetProxy( |
||||
inputDataset: Dataset, |
||||
context: ContextDefinition, |
||||
): JsonldDatasetProxyBuilder { |
||||
const contextUtil = new ContextUtil(context); |
||||
const proxyContext = new ProxyContext({ |
||||
dataset: inputDataset, |
||||
contextUtil, |
||||
writeGraphs: [defaultGraph()], |
||||
languageOrdering: ["none", "en", "other"], |
||||
}); |
||||
return new JsonldDatasetProxyBuilder(proxyContext); |
||||
} |
@ -0,0 +1,75 @@ |
||||
import { literal, quad } from "@rdfjs/data-model"; |
||||
import type { ProxyContext } from "../ProxyContext"; |
||||
import type { PredicateType, SubjectType } from "../types"; |
||||
import { |
||||
languageKeyToLiteralLanguage, |
||||
quadsToLanguageQuadMap, |
||||
languageDeleteMatch, |
||||
} from "./languageUtils"; |
||||
import type { LanguageMap, LanguageSetMap } from "./languagesOf"; |
||||
import LanguageSet from "./languageSet"; |
||||
|
||||
export function createLanguageMapProxy< |
||||
Target extends LanguageMap | LanguageSetMap, |
||||
>( |
||||
subject: SubjectType, |
||||
predicate: PredicateType, |
||||
proxyContext: ProxyContext, |
||||
isArray: boolean, |
||||
): Target { |
||||
const target: Target = {} as Target; |
||||
// Function to call to update the target to represent what's in the dataset
|
||||
const targetSetter = (target: Target) => { |
||||
// Clear the target
|
||||
Object.keys(target).forEach((key) => delete target[key]); |
||||
// Add current language map to target
|
||||
const allQuads = proxyContext.dataset.match(subject, predicate); |
||||
const languageQuadMap = quadsToLanguageQuadMap(allQuads); |
||||
Object.entries(languageQuadMap).forEach(([language, quads]) => { |
||||
const stringArray = quads.toArray().map((quad) => quad.object.value); |
||||
if (isArray) { |
||||
target[language] = new Set(stringArray); |
||||
} else { |
||||
target[language] = stringArray[0]; |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
targetSetter(target); |
||||
|
||||
return new Proxy<Target>(target, { |
||||
get: (target, key) => { |
||||
targetSetter(target); |
||||
if (typeof key !== "string") { |
||||
return Reflect.get(target, key); |
||||
} |
||||
if (isArray) { |
||||
return new LanguageSet(subject, predicate, key, proxyContext); |
||||
} |
||||
return Reflect.get(target, key); |
||||
}, |
||||
set: (target, key, value) => { |
||||
const language = languageKeyToLiteralLanguage(key); |
||||
// Delete all quads with the language currently
|
||||
if (!isArray) { |
||||
languageDeleteMatch(proxyContext.dataset, subject, predicate, language); |
||||
} |
||||
// Add the new quad for the language
|
||||
proxyContext.writeGraphs.forEach((writeGraph) => { |
||||
proxyContext.dataset.add( |
||||
quad(subject, predicate, literal(value, language), writeGraph), |
||||
); |
||||
}); |
||||
return Reflect.set(target, key, value); |
||||
}, |
||||
deleteProperty: (target, key) => { |
||||
languageDeleteMatch( |
||||
proxyContext.dataset, |
||||
subject, |
||||
predicate, |
||||
languageKeyToLiteralLanguage(key), |
||||
); |
||||
return Reflect.deleteProperty(target, key); |
||||
}, |
||||
}) as Target; |
||||
} |
@ -0,0 +1,129 @@ |
||||
import type { Dataset, Literal } from "@rdfjs/types"; |
||||
import type { PredicateType, SubjectType } from "../types"; |
||||
import type { LanguageKey } from "./languageTypes"; |
||||
import type { LiteralObjectQuad } from "./languageUtils"; |
||||
import { languageDeleteMatch, languageMatch } from "./languageUtils"; |
||||
import { literal, quad } from "@rdfjs/data-model"; |
||||
import type { ProxyContext } from "../ProxyContext"; |
||||
|
||||
export default class LanguageSet implements Set<string> { |
||||
private subject: SubjectType; |
||||
private predicate: PredicateType; |
||||
private languageKey: LanguageKey; |
||||
private proxyContext: ProxyContext; |
||||
|
||||
constructor( |
||||
subject: SubjectType, |
||||
predicate: PredicateType, |
||||
languageKey: LanguageKey, |
||||
proxyContext: ProxyContext, |
||||
) { |
||||
this.subject = subject; |
||||
this.predicate = predicate; |
||||
this.languageKey = languageKey; |
||||
this.proxyContext = proxyContext; |
||||
} |
||||
|
||||
private matchThis(): Dataset<LiteralObjectQuad> { |
||||
return languageMatch( |
||||
this.proxyContext.dataset, |
||||
this.subject, |
||||
this.predicate, |
||||
this.languageKey, |
||||
); |
||||
} |
||||
|
||||
private getLiteral(value: string): Literal { |
||||
return this.languageKey === "@none" |
||||
? literal(value) |
||||
: literal(value, this.languageKey); |
||||
} |
||||
|
||||
public get size(): number { |
||||
return this.matchThis().size; |
||||
} |
||||
|
||||
add(value: string): this { |
||||
this.proxyContext.writeGraphs.forEach((graph) => { |
||||
this.proxyContext.dataset.add( |
||||
quad( |
||||
this.subject, |
||||
this.predicate, |
||||
literal(value, this.languageKey), |
||||
graph, |
||||
), |
||||
); |
||||
}); |
||||
return this; |
||||
} |
||||
|
||||
clear(): void { |
||||
languageDeleteMatch( |
||||
this.proxyContext.dataset, |
||||
this.subject, |
||||
this.predicate, |
||||
this.languageKey, |
||||
); |
||||
} |
||||
|
||||
delete(value: string): boolean { |
||||
const hadValue = this.has(value); |
||||
this.proxyContext.dataset.deleteMatches( |
||||
this.subject, |
||||
this.predicate, |
||||
this.getLiteral(value), |
||||
); |
||||
return hadValue; |
||||
} |
||||
|
||||
forEach( |
||||
callbackfn: (value: string, value2: string, set: Set<string>) => void, |
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
thisArg?: any, |
||||
): void { |
||||
const quads = this.matchThis(); |
||||
quads.forEach((curQuad) => { |
||||
callbackfn(curQuad.object.value, curQuad.object.value, thisArg || this); |
||||
}); |
||||
} |
||||
|
||||
has(item: string): boolean { |
||||
return ( |
||||
this.proxyContext.dataset.match( |
||||
this.subject, |
||||
this.predicate, |
||||
this.getLiteral(item), |
||||
).size > 0 |
||||
); |
||||
} |
||||
|
||||
*entries(): IterableIterator<[string, string]> { |
||||
const quads = this.matchThis(); |
||||
for (const curQuad of quads) { |
||||
yield [curQuad.object.value, curQuad.object.value]; |
||||
} |
||||
} |
||||
|
||||
*keys(): IterableIterator<string> { |
||||
const quads = this.matchThis(); |
||||
for (const curQuad of quads) { |
||||
yield curQuad.object.value; |
||||
} |
||||
} |
||||
|
||||
*values(): IterableIterator<string> { |
||||
const quads = this.matchThis(); |
||||
for (const curQuad of quads) { |
||||
yield curQuad.object.value; |
||||
} |
||||
} |
||||
|
||||
*[Symbol.iterator](): IterableIterator<string> { |
||||
const quads = this.matchThis(); |
||||
for (const curQuad of quads) { |
||||
yield curQuad.object.value; |
||||
} |
||||
} |
||||
|
||||
[Symbol.toStringTag] = "LanguageSet"; |
||||
} |
@ -0,0 +1,3 @@ |
||||
export type LanguageOrdering = ("@none" | "@other" | string)[]; |
||||
|
||||
export type LanguageKey = "@none" | string; |
@ -0,0 +1,159 @@ |
||||
import type { Dataset, Literal, Quad, Quad_Object } from "@rdfjs/types"; |
||||
import { createDataset } from "o-dataset-pack"; |
||||
import type { PredicateType, SubjectType } from "../types"; |
||||
import type { LanguageKey, LanguageOrdering } from "./languageTypes"; |
||||
|
||||
/** |
||||
* |
||||
* @param dataset |
||||
* @param subject |
||||
* @param predicate |
||||
* @param languageKey |
||||
* @returns |
||||
*/ |
||||
export function languageMatch( |
||||
dataset: Dataset, |
||||
subject: SubjectType, |
||||
predicate: PredicateType, |
||||
languageKey: LanguageKey, |
||||
): Dataset<LiteralObjectQuad> { |
||||
const literalLanguage = languageKeyToLiteralLanguage(languageKey); |
||||
return dataset.match(subject, predicate).filter((quad) => { |
||||
return ( |
||||
isLanguageLiteral(quad.object) && quad.object.language === literalLanguage |
||||
); |
||||
}) as Dataset<LiteralObjectQuad>; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @param dataset |
||||
* @param subject |
||||
* @param predicate |
||||
* @param languageKey |
||||
*/ |
||||
export function languageDeleteMatch( |
||||
dataset: Dataset, |
||||
subject: SubjectType, |
||||
predicate: PredicateType, |
||||
languageKey: LanguageKey, |
||||
): void { |
||||
const quadsToDelete = languageMatch(dataset, subject, predicate, languageKey); |
||||
quadsToDelete.forEach((quad) => { |
||||
dataset.delete(quad); |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* Given a node, will return true if that node is a literal that could have a |
||||
* language. This does not guarantee that it is a language literal. |
||||
* @param node the node to test |
||||
* @returns boolean |
||||
*/ |
||||
export function isLanguageLiteral(node: Quad_Object): node is Literal { |
||||
return ( |
||||
node.termType === "Literal" && |
||||
(node.datatype.value === |
||||
"http://www.w3.org/1999/02/22-rdf-syntax-ns#langString" || |
||||
node.datatype.value === "http://www.w3.org/2001/XMLSchema#string") |
||||
); |
||||
} |
||||
|
||||
export interface LiteralObjectQuad extends Quad { |
||||
object: Literal; |
||||
} |
||||
|
||||
export function quadsToLanguageQuadMap( |
||||
quads: Dataset, |
||||
): Record<LanguageKey, Dataset<LiteralObjectQuad>> { |
||||
const languageQuadMap: Record<LanguageKey, Dataset<LiteralObjectQuad>> = {}; |
||||
quads.forEach((quad) => { |
||||
const literal = quad.object; |
||||
if (isLanguageLiteral(literal)) { |
||||
const languageKey = literalLanguageToLanguageKey(literal.language); |
||||
if (!languageQuadMap[languageKey]) { |
||||
languageQuadMap[languageKey] = |
||||
createDataset() as Dataset<LiteralObjectQuad>; |
||||
} |
||||
languageQuadMap[languageKey].add(quad as LiteralObjectQuad); |
||||
} |
||||
}); |
||||
return languageQuadMap; |
||||
} |
||||
|
||||
export function filterQuadsByLanguageOrdering( |
||||
quads: Dataset, |
||||
languageOrdering: LanguageOrdering, |
||||
): Dataset { |
||||
const languageQuadMap = quadsToLanguageQuadMap(quads); |
||||
const validLanguages = new Set(languageOrdering); |
||||
const presentLanguages = new Set(Object.keys(languageQuadMap)); |
||||
for (const currentLanguageKey of languageOrdering) { |
||||
if (presentLanguages.has(currentLanguageKey)) { |
||||
return languageQuadMap[currentLanguageKey]; |
||||
} |
||||
if (currentLanguageKey === "@other") { |
||||
for (const presentLang of presentLanguages) { |
||||
if (!validLanguages.has(presentLang)) { |
||||
return languageQuadMap[presentLang]; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return createDataset(); |
||||
} |
||||
|
||||
export function getLanguageKeyForWriteOperation( |
||||
languageOrdering: LanguageOrdering, |
||||
): LanguageKey | undefined { |
||||
return languageOrdering.find((lang) => lang !== "@other"); |
||||
} |
||||
|
||||
// function addToDatasetMap(
|
||||
// key: string,
|
||||
// value: Quad,
|
||||
// map: Record<string, Dataset>
|
||||
// ) {
|
||||
// if (!map[key]) {
|
||||
// map[key] = createDataset();
|
||||
// }
|
||||
// map[key].add(value);
|
||||
// }
|
||||
|
||||
// export function filterDatasetByLanguageOrdering(
|
||||
// dataset: Dataset,
|
||||
// proxyContext: ProxyContext
|
||||
// ): Dataset {
|
||||
// // TODO: This is an O(n) task that could be reduced to O(1) if we cached some
|
||||
// // of the processing
|
||||
// const validLangs = new Set(proxyContext.languageOrdering);
|
||||
// const sortedLangs: Record<string, Dataset> = {};
|
||||
// dataset.forEach((quad) => {
|
||||
// const literal = quad.object;
|
||||
// if (isLangStringNode(literal)) {
|
||||
// if (literal.language === "") {
|
||||
// addToDatasetMap("@none", quad, sortedLangs);
|
||||
// } else if (validLangs.has(literal.language)) {
|
||||
// addToDatasetMap(literal.language, quad, sortedLangs);
|
||||
// } else {
|
||||
// addToDatasetMap("@other", quad, sortedLangs);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// for (const language of proxyContext.languageOrdering) {
|
||||
// if (sortedLangs[language]) {
|
||||
// return sortedLangs[language];
|
||||
// }
|
||||
// }
|
||||
// return createDataset();
|
||||
// }
|
||||
|
||||
export function languageKeyToLiteralLanguage( |
||||
languageKey: string | symbol, |
||||
): string { |
||||
return (languageKey === "@none" ? "" : languageKey) as string; |
||||
} |
||||
|
||||
export function literalLanguageToLanguageKey(literalLanguage: string): string { |
||||
return literalLanguage === "" ? "@none" : literalLanguage; |
||||
} |
@ -0,0 +1,61 @@ |
||||
import { namedNode } from "@rdfjs/data-model"; |
||||
import { getSubjectProxyFromObject } from "../subjectProxy/isSubjectProxy"; |
||||
import type { ObjectLike } from "../types"; |
||||
import { _getUnderlyingNode, _proxyContext } from "../types"; |
||||
import { createLanguageMapProxy } from "./languageMapProxy"; |
||||
|
||||
/** |
||||
* ----------------------------------------------------------------------------- |
||||
* Types |
||||
* ----------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
export type LanguageMap = { |
||||
"@none"?: string; |
||||
[language: string]: string | undefined; |
||||
}; |
||||
|
||||
export type LanguageSetMap = { |
||||
"@none"?: LanguageSet; |
||||
[language: string]: LanguageSet | undefined; |
||||
}; |
||||
|
||||
export type LanguageSet = Set<string>; |
||||
|
||||
export type LanguageOfConditionalReturn< |
||||
SubjectObject extends ObjectLike, |
||||
Key extends keyof SubjectObject, |
||||
> = NonNullable<SubjectObject[Key]> extends Array<unknown> |
||||
? LanguageSetMap |
||||
: LanguageMap; |
||||
|
||||
/** |
||||
* ----------------------------------------------------------------------------- |
||||
* Functions |
||||
* ----------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
/** |
||||
* |
||||
* @param subject |
||||
* @param predicate |
||||
* @returns |
||||
*/ |
||||
export function languagesOf< |
||||
SubjectObject extends ObjectLike, |
||||
Key extends keyof SubjectObject, |
||||
>( |
||||
subjectObject: SubjectObject, |
||||
key: Key, |
||||
): LanguageOfConditionalReturn<SubjectObject, Key> { |
||||
const proxy = getSubjectProxyFromObject(subjectObject); |
||||
const proxyContext = proxy[_proxyContext]; |
||||
const subject = proxy[_getUnderlyingNode]; |
||||
const predicate = namedNode(proxyContext.contextUtil.keyToIri(key as string)); |
||||
return createLanguageMapProxy<LanguageMap>( |
||||
subject, |
||||
predicate, |
||||
proxyContext, |
||||
proxyContext.contextUtil.isArray(key as string), |
||||
) as LanguageOfConditionalReturn<SubjectObject, Key>; |
||||
} |
@ -0,0 +1,14 @@ |
||||
import type { LanguageOrdering } from "./language/languageTypes"; |
||||
import type { InteractOptions } from "./util/createInteractOptions"; |
||||
import { createInteractOptions } from "./util/createInteractOptions"; |
||||
|
||||
/** |
||||
* Set the default language pr |
||||
* @param graphs The graphs that should be written to |
||||
* @returns a write builder |
||||
*/ |
||||
export function setLanguagePreferences( |
||||
...languageOrdering: LanguageOrdering |
||||
): InteractOptions { |
||||
return createInteractOptions("languageOrdering", languageOrdering); |
||||
} |
@ -0,0 +1,20 @@ |
||||
import type { BlankNode, Dataset, NamedNode } from "@rdfjs/types"; |
||||
import type { ContextDefinition } from "jsonld"; |
||||
import type { ProxyContext } from "../ProxyContext"; |
||||
import type { |
||||
GraphType, |
||||
_getUnderlyingDataset, |
||||
_getUnderlyingNode, |
||||
_proxyContext, |
||||
_writeGraphs, |
||||
} from "../types"; |
||||
|
||||
export type SubjectProxy = { |
||||
"@id"?: string; |
||||
"@context": ContextDefinition; |
||||
readonly [key: string | number | symbol]: unknown; |
||||
readonly [_getUnderlyingDataset]: Dataset; |
||||
readonly [_getUnderlyingNode]: NamedNode | BlankNode; |
||||
[_proxyContext]: ProxyContext; |
||||
readonly [_writeGraphs]: GraphType[]; |
||||
}; |
@ -0,0 +1,106 @@ |
||||
import type { BlankNode, NamedNode } from "@rdfjs/types"; |
||||
import { namedNode, quad } from "@rdfjs/data-model"; |
||||
import { addObjectToDataset } from "../util/addObjectToDataset"; |
||||
import { deleteValueFromDataset } from "./deleteFromDataset"; |
||||
import { |
||||
_getUnderlyingDataset, |
||||
_getUnderlyingNode, |
||||
_proxyContext, |
||||
_writeGraphs, |
||||
} from "../types"; |
||||
import { getValueForKey } from "./getValueForKey"; |
||||
import type { ProxyContext } from "../ProxyContext"; |
||||
|
||||
export interface SubjectProxyTarget { |
||||
"@id": NamedNode | BlankNode; |
||||
} |
||||
|
||||
export function createSubjectHandler( |
||||
initialProxyContext: ProxyContext, |
||||
): ProxyHandler<SubjectProxyTarget> { |
||||
let proxyContext = initialProxyContext; |
||||
return { |
||||
get(target: SubjectProxyTarget, key: string | symbol) { |
||||
switch (key) { |
||||
case _getUnderlyingDataset: |
||||
return proxyContext.dataset; |
||||
case _getUnderlyingNode: |
||||
return target["@id"]; |
||||
case _proxyContext: |
||||
return proxyContext; |
||||
case _writeGraphs: |
||||
return proxyContext.writeGraphs; |
||||
case "@context": |
||||
return proxyContext.contextUtil.context; |
||||
} |
||||
return getValueForKey(target, key, proxyContext); |
||||
}, |
||||
getOwnPropertyDescriptor(target: SubjectProxyTarget, key: string) { |
||||
return { |
||||
value: getValueForKey(target, key, proxyContext), |
||||
writable: true, |
||||
enumerable: true, |
||||
configurable: true, |
||||
}; |
||||
}, |
||||
ownKeys(target) { |
||||
const subject = target["@id"]; |
||||
const tripleDataset = proxyContext.dataset.match(subject); |
||||
const keys: Set<string> = new Set(["@id"]); |
||||
tripleDataset.toArray().forEach((quad) => { |
||||
keys.add(proxyContext.contextUtil.iriToKey(quad.predicate.value)); |
||||
}); |
||||
return Array.from(keys); |
||||
}, |
||||
set: (target: SubjectProxyTarget, key, value) => { |
||||
if (key === _proxyContext) { |
||||
proxyContext = value; |
||||
return true; |
||||
} |
||||
if (key === "@id" && typeof value === "string") { |
||||
// Replace Subject Quads
|
||||
const currentSubjectQuads = proxyContext.dataset |
||||
.match(target["@id"]) |
||||
.toArray(); |
||||
const newSubjectQuads = currentSubjectQuads.map((curQuad) => |
||||
quad( |
||||
namedNode(value), |
||||
curQuad.predicate, |
||||
curQuad.object, |
||||
curQuad.graph, |
||||
), |
||||
); |
||||
currentSubjectQuads.forEach((curQuad) => |
||||
proxyContext.dataset.delete(curQuad), |
||||
); |
||||
proxyContext.dataset.addAll(newSubjectQuads); |
||||
// Replace Object Quads
|
||||
const currentObjectQuads = proxyContext.dataset |
||||
.match(undefined, undefined, target["@id"]) |
||||
.toArray(); |
||||
const newObjectQuads = currentObjectQuads.map((curQuad) => |
||||
quad( |
||||
curQuad.subject, |
||||
curQuad.predicate, |
||||
namedNode(value), |
||||
curQuad.graph, |
||||
), |
||||
); |
||||
currentObjectQuads.forEach((curQuad) => |
||||
proxyContext.dataset.delete(curQuad), |
||||
); |
||||
proxyContext.dataset.addAll(newObjectQuads); |
||||
target["@id"] = namedNode(value); |
||||
} |
||||
addObjectToDataset( |
||||
{ "@id": target["@id"], [key]: value }, |
||||
true, |
||||
proxyContext, |
||||
); |
||||
return true; |
||||
}, |
||||
deleteProperty(target, key) { |
||||
return deleteValueFromDataset(target, key, proxyContext); |
||||
}, |
||||
}; |
||||
} |
@ -0,0 +1,44 @@ |
||||
import type { Term } from "@rdfjs/types"; |
||||
import { namedNode, quad } from "@rdfjs/data-model"; |
||||
import type { SubjectProxyTarget } from "./createSubjectHandler"; |
||||
import type { ProxyContext } from "../ProxyContext"; |
||||
|
||||
export function deleteValueFromDataset( |
||||
target: SubjectProxyTarget, |
||||
key: string | symbol, |
||||
proxyContext: ProxyContext, |
||||
) { |
||||
const nodesToRemove: Term[] = []; |
||||
if (key === "@context") { |
||||
return true; |
||||
} |
||||
if (key === "toString" || key === Symbol.toStringTag) { |
||||
return true; |
||||
} |
||||
if (typeof key === "symbol") { |
||||
return true; |
||||
} |
||||
const subject = target["@id"]; |
||||
const predicate = namedNode(proxyContext.contextUtil.keyToIri(key)); |
||||
if (key === "@id") { |
||||
nodesToRemove.push(target["@id"]); |
||||
} else { |
||||
const objectDataset = proxyContext.dataset.match(subject, predicate); |
||||
if (objectDataset.size === 0) { |
||||
return true; |
||||
} else { |
||||
nodesToRemove.push(...objectDataset.toArray().map((quad) => quad.object)); |
||||
} |
||||
} |
||||
nodesToRemove.forEach((term) => { |
||||
if (term.termType === "Literal") { |
||||
proxyContext.dataset.delete(quad(subject, predicate, term)); |
||||
return true; |
||||
} else if (term.termType === "NamedNode") { |
||||
proxyContext.dataset.deleteMatches(term, undefined, undefined); |
||||
proxyContext.dataset.deleteMatches(undefined, undefined, term); |
||||
return true; |
||||
} |
||||
}); |
||||
return true; |
||||
} |
@ -0,0 +1,62 @@ |
||||
import type { SubjectProxyTarget } from "./createSubjectHandler"; |
||||
import { namedNode } from "@rdfjs/data-model"; |
||||
import { nodeToJsonldRepresentation } from "../util/nodeToJsonldRepresentation"; |
||||
import type { SubjectProxy } from "./SubjectProxy"; |
||||
import type { ArrayProxy } from "../arrayProxy/ArrayProxy"; |
||||
import type { ProxyContext } from "../ProxyContext"; |
||||
import { filterQuadsByLanguageOrdering } from "../language/languageUtils"; |
||||
|
||||
/** |
||||
* Given a subject target and a key return the correct value |
||||
*/ |
||||
export function getValueForKey( |
||||
target: SubjectProxyTarget, |
||||
key: string | symbol, |
||||
proxyContext: ProxyContext, |
||||
): SubjectProxy | ArrayProxy | string | number | boolean | undefined { |
||||
const { contextUtil, dataset } = proxyContext; |
||||
if (key === "@id") { |
||||
if (target["@id"].termType === "BlankNode") { |
||||
return undefined; |
||||
} |
||||
return contextUtil.iriToKey(target["@id"].value); |
||||
} |
||||
if (key === "toString" || key === Symbol.toStringTag) { |
||||
// TODO: this toString method right now returns [object Object],
|
||||
// which is correct, but it could be more descriptive, especially
|
||||
// because console.log doesn't return anyting helpful due to the proxy.
|
||||
return Reflect.get(target, "toString"); |
||||
} |
||||
if (typeof key === "symbol") { |
||||
return; |
||||
} |
||||
const subject = target["@id"]; |
||||
const predicate = namedNode(contextUtil.keyToIri(key)); |
||||
if (contextUtil.isArray(key)) { |
||||
const arrayProxy = proxyContext.createArrayProxy( |
||||
[subject, predicate, null, null], |
||||
false, |
||||
undefined, |
||||
contextUtil.isLangString(key), |
||||
); |
||||
return arrayProxy; |
||||
} |
||||
let objectDataset = dataset.match(subject, predicate); |
||||
if (contextUtil.isLangString(key)) { |
||||
objectDataset = filterQuadsByLanguageOrdering( |
||||
objectDataset, |
||||
proxyContext.languageOrdering, |
||||
); |
||||
} |
||||
if (objectDataset.size === 0) { |
||||
return undefined; |
||||
} else if (objectDataset.size === 1) { |
||||
const thing = nodeToJsonldRepresentation( |
||||
objectDataset.toArray()[0].object, |
||||
proxyContext, |
||||
); |
||||
return thing; |
||||
} else { |
||||
return proxyContext.createArrayProxy([subject, predicate, null, null]); |
||||
} |
||||
} |
@ -0,0 +1,30 @@ |
||||
import type { ObjectLike } from "../types"; |
||||
import { |
||||
_getUnderlyingDataset, |
||||
_getUnderlyingNode, |
||||
_proxyContext, |
||||
_writeGraphs, |
||||
} from "../types"; |
||||
import type { SubjectProxy } from "./SubjectProxy"; |
||||
|
||||
export function isSubjectProxy( |
||||
someObject?: unknown, |
||||
): someObject is SubjectProxy { |
||||
if (!someObject) return false; |
||||
if (typeof someObject !== "object") return false; |
||||
const potentialSubjectProxy = someObject as SubjectProxy; |
||||
return !( |
||||
typeof potentialSubjectProxy[_writeGraphs] !== "object" || |
||||
typeof potentialSubjectProxy[_getUnderlyingDataset] !== "object" || |
||||
typeof potentialSubjectProxy[_getUnderlyingNode] !== "object" || |
||||
typeof potentialSubjectProxy[_proxyContext] !== "object" |
||||
); |
||||
} |
||||
|
||||
export function getSubjectProxyFromObject(object: ObjectLike): SubjectProxy { |
||||
const potentialSubjectProxy = object as SubjectProxy; |
||||
if (!isSubjectProxy(potentialSubjectProxy)) { |
||||
throw new Error(`${object} is not a Jsonld Dataset Proxy Subject`); |
||||
} |
||||
return potentialSubjectProxy; |
||||
} |
@ -0,0 +1,25 @@ |
||||
import type { BlankNode, DefaultGraph, Literal, NamedNode } from "@rdfjs/types"; |
||||
|
||||
export const _getUnderlyingNode = Symbol("_getUnderlyingNode"); |
||||
export const _getUnderlyingMatch = Symbol("_getUnderlyingMatch"); |
||||
export const _isSubjectOriented = Symbol("_isSubjectOriented"); |
||||
export const _getNodeAtIndex = Symbol("_getNodeAtIndex"); |
||||
export const _getUnderlyingDataset = Symbol("_getUnderlyingDataset"); |
||||
export const _getUnderlyingArrayTarget = Symbol("_getUnderlyingArrayTarget"); |
||||
export const _proxyContext = Symbol("_proxyContext"); |
||||
export const _writeGraphs = Symbol("_writeGraphs"); |
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type ObjectLike = Record<string | number | symbol, any>; |
||||
|
||||
export type SubjectType = NamedNode | BlankNode; |
||||
export type PredicateType = NamedNode; |
||||
export type ObjectType = NamedNode | BlankNode | Literal; |
||||
export type GraphType = NamedNode | BlankNode | DefaultGraph; |
||||
|
||||
export type QuadMatch = [ |
||||
SubjectType | undefined | null, |
||||
PredicateType | undefined | null, |
||||
ObjectType | undefined | null, |
||||
GraphType | undefined | null, |
||||
]; |
@ -0,0 +1,47 @@ |
||||
import type { BlankNode, DefaultGraph, Literal, NamedNode } from "@rdfjs/types"; |
||||
import type { ObjectType } from "../types"; |
||||
|
||||
export function nodeToString( |
||||
node: NamedNode | BlankNode | DefaultGraph | Literal | null | undefined, |
||||
): string { |
||||
if (node == null) { |
||||
return "null"; |
||||
} |
||||
switch (node.termType) { |
||||
case "NamedNode": |
||||
return `namedNode(${node.value})`; |
||||
case "BlankNode": |
||||
return `blankNode(${node.value})`; |
||||
case "Literal": |
||||
return `literal(${node.value},${node.datatype.value})`; |
||||
case "DefaultGraph": |
||||
return "defaultGraph()"; |
||||
} |
||||
} |
||||
|
||||
export class NodeSet { |
||||
private set: Set<string> = new Set(); |
||||
private map: Record<string, ObjectType> = {}; |
||||
|
||||
add(node: ObjectType) { |
||||
const key = nodeToString(node); |
||||
this.set.add(key); |
||||
this.map[key] = node; |
||||
} |
||||
|
||||
has(node: ObjectType): boolean { |
||||
return this.set.has(nodeToString(node)); |
||||
} |
||||
|
||||
delete(node: ObjectType) { |
||||
const key = nodeToString(node); |
||||
delete this.map[key]; |
||||
return this.set.delete(nodeToString(node)); |
||||
} |
||||
|
||||
toArray() { |
||||
return Array.from(this.set).map((stringVal) => { |
||||
return this.map[stringVal]; |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
import type { BlankNode, NamedNode } from "@rdfjs/types"; |
||||
import { _getUnderlyingNode } from "../types"; |
||||
import type { SubjectProxy } from "../subjectProxy/SubjectProxy"; |
||||
|
||||
export type RawObject = |
||||
| ({ |
||||
"@id"?: string | NamedNode | BlankNode; |
||||
} & { |
||||
[key: string | symbol | number]: RawValue | RawValue[]; |
||||
}) |
||||
| SubjectProxy; |
||||
|
||||
export type RawValue = string | boolean | number | RawObject | undefined; |
@ -0,0 +1,140 @@ |
||||
import type { BlankNode, NamedNode } from "@rdfjs/types"; |
||||
import { literal, namedNode, quad } from "@rdfjs/data-model"; |
||||
import { _getUnderlyingNode } from "../types"; |
||||
import type { SubjectProxy } from "../subjectProxy/SubjectProxy"; |
||||
import { getNodeFromRawObject, getNodeFromRawValue } from "./getNodeFromRaw"; |
||||
import type { RawObject, RawValue } from "./RawObject"; |
||||
import type { ProxyContext } from "../ProxyContext"; |
||||
import { isSubjectProxy } from "../subjectProxy/isSubjectProxy"; |
||||
import { NodeSet } from "./NodeSet"; |
||||
import { |
||||
getLanguageKeyForWriteOperation, |
||||
languageDeleteMatch, |
||||
languageKeyToLiteralLanguage, |
||||
} from "../language/languageUtils"; |
||||
|
||||
export function addRawValueToDatasetRecursive( |
||||
subject: NamedNode | BlankNode, |
||||
key: string, |
||||
value: RawValue, |
||||
visitedObjects: NodeSet, |
||||
shouldDeleteOldTriples: boolean, |
||||
proxyContext: ProxyContext, |
||||
): void { |
||||
const { dataset, contextUtil } = proxyContext; |
||||
const predicate = namedNode(contextUtil.keyToIri(key)); |
||||
// Get the Object Node
|
||||
const object = getNodeFromRawValue(key, value, proxyContext); |
||||
if (object == undefined) { |
||||
dataset.deleteMatches(subject, predicate); |
||||
} else if (object.termType === "Literal") { |
||||
let languageAppliedObject = object; |
||||
// Handle language use case
|
||||
if (contextUtil.isLangString(key)) { |
||||
const languageKey = getLanguageKeyForWriteOperation( |
||||
proxyContext.languageOrdering, |
||||
); |
||||
if (!languageKey) return; |
||||
languageAppliedObject = literal( |
||||
object.value, |
||||
languageKeyToLiteralLanguage(languageKey), |
||||
); |
||||
} |
||||
proxyContext.writeGraphs.forEach((graph) => { |
||||
proxyContext.dataset.add( |
||||
quad(subject, predicate, languageAppliedObject, graph), |
||||
); |
||||
}); |
||||
} else { |
||||
// Delete any triples if the id is the same
|
||||
if (!visitedObjects.has(object) && !isSubjectProxy(value)) { |
||||
dataset.deleteMatches(object, undefined, undefined); |
||||
} |
||||
proxyContext.writeGraphs.forEach((graph) => { |
||||
dataset.add(quad(subject, predicate, object, graph)); |
||||
}); |
||||
if (!isSubjectProxy(value)) { |
||||
const updateData: RawObject = ( |
||||
typeof value === "object" |
||||
? { ...value, "@id": object } |
||||
: { "@id": object } |
||||
) as RawObject; |
||||
addRawObjectToDatasetRecursive( |
||||
updateData, |
||||
visitedObjects, |
||||
shouldDeleteOldTriples, |
||||
proxyContext, |
||||
); |
||||
} |
||||
} |
||||
} |
||||
|
||||
export function addRawObjectToDatasetRecursive( |
||||
item: RawObject, |
||||
visitedObjects: NodeSet, |
||||
shouldDeleteOldTriples: boolean, |
||||
proxyContext: ProxyContext, |
||||
): SubjectProxy { |
||||
if (isSubjectProxy(item)) { |
||||
return item as SubjectProxy; |
||||
} |
||||
const { dataset } = proxyContext; |
||||
const subject = getNodeFromRawObject(item, proxyContext.contextUtil); |
||||
if (visitedObjects.has(subject)) { |
||||
return proxyContext.createSubjectProxy(subject); |
||||
} |
||||
visitedObjects.add(subject); |
||||
Object.entries(item).forEach(([key, value]) => { |
||||
if (key === "@id") { |
||||
return; |
||||
} |
||||
const predicate = namedNode(proxyContext.contextUtil.keyToIri(key)); |
||||
if (shouldDeleteOldTriples) { |
||||
if (proxyContext.contextUtil.isLangString(key)) { |
||||
const languageKey = getLanguageKeyForWriteOperation( |
||||
proxyContext.languageOrdering, |
||||
); |
||||
if (languageKey) { |
||||
languageDeleteMatch(dataset, subject, predicate, languageKey); |
||||
} |
||||
} else { |
||||
dataset.deleteMatches(subject, predicate); |
||||
} |
||||
} |
||||
if (Array.isArray(value)) { |
||||
value.forEach((valueItem) => { |
||||
addRawValueToDatasetRecursive( |
||||
subject, |
||||
key, |
||||
valueItem, |
||||
visitedObjects, |
||||
true, |
||||
proxyContext, |
||||
); |
||||
}); |
||||
} else { |
||||
addRawValueToDatasetRecursive( |
||||
subject, |
||||
key, |
||||
value as RawValue, |
||||
visitedObjects, |
||||
true, |
||||
proxyContext, |
||||
); |
||||
} |
||||
}); |
||||
return proxyContext.createSubjectProxy(subject); |
||||
} |
||||
|
||||
export function addObjectToDataset( |
||||
item: RawObject, |
||||
shouldDeleteOldTriples: boolean, |
||||
proxyContext: ProxyContext, |
||||
): SubjectProxy { |
||||
return addRawObjectToDatasetRecursive( |
||||
item, |
||||
new NodeSet(), |
||||
shouldDeleteOldTriples, |
||||
proxyContext, |
||||
); |
||||
} |
@ -0,0 +1,55 @@ |
||||
import { getSubjectProxyFromObject } from "../subjectProxy/isSubjectProxy"; |
||||
import type { ObjectLike } from "../types"; |
||||
import { _getUnderlyingNode, _proxyContext } from "../types"; |
||||
import { getProxyFromObject } from "./isProxy"; |
||||
|
||||
export interface InteractOptions { |
||||
/** |
||||
* Given a dataset proxy, this will set the action on that dataset proxy |
||||
* @param objects Any number of dataset proxies |
||||
* @returns An end function. Call this if to reset the interaction |
||||
*/ |
||||
using(...objects: ObjectLike[]): () => void; |
||||
/** |
||||
* Given a dataset proxy this will copy the dataset proxy and set the action |
||||
* on the copy |
||||
* @param objects Any number of dataset proxies |
||||
* @returns cloned dataset proxies |
||||
*/ |
||||
usingCopy<T extends ObjectLike>(...objects: T[]): T[]; |
||||
} |
||||
|
||||
export function createInteractOptions( |
||||
paramKey: string, |
||||
parameter: unknown, |
||||
): InteractOptions { |
||||
return { |
||||
using(...objects: ObjectLike[]): () => void { |
||||
const onEndFunctions: (() => void)[] = []; |
||||
objects.forEach((object) => { |
||||
const proxy = getProxyFromObject(object); |
||||
const oldProxyContext = proxy[_proxyContext]; |
||||
proxy[_proxyContext] = proxy[_proxyContext].duplicate({ |
||||
[paramKey]: parameter, |
||||
}); |
||||
onEndFunctions.push(() => { |
||||
proxy[_proxyContext] = oldProxyContext; |
||||
}); |
||||
}); |
||||
return function endWrite() { |
||||
onEndFunctions.forEach((func) => func()); |
||||
}; |
||||
}, |
||||
usingCopy<T extends ObjectLike>(...objects: T[]): T[] { |
||||
return objects.map((object) => { |
||||
const proxy = getSubjectProxyFromObject(object); |
||||
const newProxyContext = proxy[_proxyContext].duplicate({ |
||||
[paramKey]: parameter, |
||||
}); |
||||
return newProxyContext.createSubjectProxy( |
||||
proxy[_getUnderlyingNode], |
||||
) as unknown as T; |
||||
}); |
||||
}, |
||||
}; |
||||
} |
@ -0,0 +1,45 @@ |
||||
import type { BlankNode, Literal, NamedNode } from "@rdfjs/types"; |
||||
import { namedNode, literal, blankNode } from "@rdfjs/data-model"; |
||||
import type { ContextUtil } from "../ContextUtil"; |
||||
import { _getUnderlyingNode } from "../types"; |
||||
import type { RawObject, RawValue } from "./RawObject"; |
||||
import type { ProxyContext } from "../ProxyContext"; |
||||
|
||||
export function getNodeFromRawObject( |
||||
item: RawObject, |
||||
contextUtil: ContextUtil, |
||||
): NamedNode | BlankNode { |
||||
if (item[_getUnderlyingNode]) { |
||||
return item[_getUnderlyingNode] as NamedNode | BlankNode; |
||||
} else if (!item["@id"]) { |
||||
return blankNode(); |
||||
} else if (typeof item["@id"] === "string") { |
||||
return namedNode(contextUtil.keyToIri(item["@id"])); |
||||
} else { |
||||
return item["@id"]; |
||||
} |
||||
} |
||||
|
||||
export function getNodeFromRawValue( |
||||
key: string, |
||||
value: RawValue, |
||||
proxyContext: ProxyContext, |
||||
): BlankNode | NamedNode | Literal | undefined { |
||||
// Get the Object Node
|
||||
if (value == undefined) { |
||||
return undefined; |
||||
} else if ( |
||||
typeof value === "string" || |
||||
typeof value === "boolean" || |
||||
typeof value === "number" |
||||
) { |
||||
const datatype = proxyContext.contextUtil.getType(key); |
||||
if (datatype === "@id") { |
||||
return namedNode(value.toString()); |
||||
} else { |
||||
return literal(value.toString(), datatype); |
||||
} |
||||
} else { |
||||
return getNodeFromRawObject(value, proxyContext.contextUtil); |
||||
} |
||||
} |
@ -0,0 +1,20 @@ |
||||
import type { ArrayProxy } from "../arrayProxy/ArrayProxy"; |
||||
import { isArrayProxy } from "../arrayProxy/isArrayProxy"; |
||||
import { isSubjectProxy } from "../subjectProxy/isSubjectProxy"; |
||||
import type { SubjectProxy } from "../subjectProxy/SubjectProxy"; |
||||
import type { ObjectLike } from "../types"; |
||||
|
||||
export function isProxy( |
||||
someObject?: unknown, |
||||
): someObject is ArrayProxy | SubjectProxy { |
||||
return isSubjectProxy(someObject) || isArrayProxy(someObject); |
||||
} |
||||
|
||||
export function getProxyFromObject( |
||||
object: ObjectLike | ObjectLike[], |
||||
): SubjectProxy | ArrayProxy { |
||||
if (!isProxy(object)) { |
||||
throw new Error(`${object} is not a Jsonld Dataset Proxy`); |
||||
} |
||||
return object; |
||||
} |
@ -0,0 +1,76 @@ |
||||
import type { Literal, Quad_Object } from "@rdfjs/types"; |
||||
import type { ProxyContext } from "../ProxyContext"; |
||||
import type { SubjectProxy } from "../subjectProxy/SubjectProxy"; |
||||
|
||||
export type ObjectJsonRepresentation = string | number | boolean | SubjectProxy; |
||||
|
||||
export function literalToJsonldRepresentation(literal: Literal) { |
||||
switch (literal.datatype.value) { |
||||
case "http://www.w3.org/2001/XMLSchema#string": |
||||
case "http://www.w3.org/2001/XMLSchema#ENTITIES": |
||||
case "http://www.w3.org/2001/XMLSchema#ENTITY": |
||||
case "http://www.w3.org/2001/XMLSchema#ID": |
||||
case "http://www.w3.org/2001/XMLSchema#IDREF": |
||||
case "http://www.w3.org/2001/XMLSchema#IDREFS": |
||||
case "http://www.w3.org/2001/XMLSchema#language": |
||||
case "http://www.w3.org/2001/XMLSchema#Name": |
||||
case "http://www.w3.org/2001/XMLSchema#NCName": |
||||
case "http://www.w3.org/2001/XMLSchema#NMTOKEN": |
||||
case "http://www.w3.org/2001/XMLSchema#NMTOKENS": |
||||
case "http://www.w3.org/2001/XMLSchema#normalizedString": |
||||
case "http://www.w3.org/2001/XMLSchema#QName": |
||||
case "http://www.w3.org/2001/XMLSchema#token": |
||||
return literal.value; |
||||
case "http://www.w3.org/2001/XMLSchema#date": |
||||
case "http://www.w3.org/2001/XMLSchema#dateTime": |
||||
case "http://www.w3.org/2001/XMLSchema#duration": |
||||
case "http://www.w3.org/2001/XMLSchema#gDay": |
||||
case "http://www.w3.org/2001/XMLSchema#gMonth": |
||||
case "http://www.w3.org/2001/XMLSchema#gMonthDay": |
||||
case "http://www.w3.org/2001/XMLSchema#gYear": |
||||
case "http://www.w3.org/2001/XMLSchema#gYearMonth": |
||||
case "http://www.w3.org/2001/XMLSchema#time": |
||||
return literal.value; |
||||
case "http://www.w3.org/2001/XMLSchema#integer": |
||||
case "http://www.w3.org/2001/XMLSchema#byte": |
||||
case "http://www.w3.org/2001/XMLSchema#decimal": |
||||
case "http://www.w3.org/2001/XMLSchema#int": |
||||
case "http://www.w3.org/2001/XMLSchema#long": |
||||
case "http://www.w3.org/2001/XMLSchema#negativeInteger": |
||||
case "http://www.w3.org/2001/XMLSchema#nonNegativeInteger": |
||||
case "http://www.w3.org/2001/XMLSchema#nonPositiveInteger": |
||||
case "http://www.w3.org/2001/XMLSchema#positiveInteger": |
||||
case "http://www.w3.org/2001/XMLSchema#short": |
||||
case "http://www.w3.org/2001/XMLSchema#unsignedLong": |
||||
case "http://www.w3.org/2001/XMLSchema#unsignedInt": |
||||
case "http://www.w3.org/2001/XMLSchema#unsignedShort": |
||||
case "http://www.w3.org/2001/XMLSchema#unsignedByte": |
||||
return parseFloat(literal.value); |
||||
case "http://www.w3.org/2001/XMLSchema#boolean": |
||||
return literal.value === "true"; |
||||
case "http://www.w3.org/2001/XMLSchema#hexBinary": |
||||
return literal.value; |
||||
case "http://www.w3.org/2001/XMLSchema#anyURI": |
||||
return literal.value; |
||||
case "http://www.w3.org/1999/02/22-rdf-syntax-ns#HTML": |
||||
case "http://www.w3.org/1999/02/22-rdf-syntax-ns#PlainLiteral": |
||||
case "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral": |
||||
case "http://www.w3.org/1999/02/22-rdf-syntax-ns#JSON": |
||||
return literal.value; |
||||
default: |
||||
return literal.value; |
||||
} |
||||
} |
||||
|
||||
export function nodeToJsonldRepresentation( |
||||
node: Quad_Object, |
||||
proxyContext: ProxyContext, |
||||
): string | number | boolean | SubjectProxy { |
||||
if (node.termType === "Literal") { |
||||
return literalToJsonldRepresentation(node); |
||||
} else if (node.termType === "NamedNode" || node.termType === "BlankNode") { |
||||
return proxyContext.createSubjectProxy(node); |
||||
} else { |
||||
throw new Error("Can only convert NamedNodes or Literals or BlankNodes"); |
||||
} |
||||
} |
@ -0,0 +1,12 @@ |
||||
import type { GraphType } from "./types"; |
||||
import type { InteractOptions } from "./util/createInteractOptions"; |
||||
import { createInteractOptions } from "./util/createInteractOptions"; |
||||
|
||||
/** |
||||
* Set the graphs that should be written to |
||||
* @param graphs The graphs that should be written to |
||||
* @returns a write builder |
||||
*/ |
||||
export function write(...graphs: GraphType[]): InteractOptions { |
||||
return createInteractOptions("writeGraphs", graphs); |
||||
} |
@ -0,0 +1,39 @@ |
||||
import { ContextUtil } from "../src/ContextUtil"; |
||||
|
||||
describe("ContextUtil", () => { |
||||
describe("keyToIri and iriToKey", () => { |
||||
it("handles a context that is simply a string map", () => { |
||||
const fakeContext = { |
||||
name: "http://hl7.org/fhir/name", |
||||
}; |
||||
const contextUtil = new ContextUtil(fakeContext); |
||||
expect(contextUtil.keyToIri("name")).toBe("http://hl7.org/fhir/name"); |
||||
}); |
||||
|
||||
it("returns the given key if it is not in the context", () => { |
||||
const contextUtil = new ContextUtil({}); |
||||
expect(contextUtil.keyToIri("name")).toBe("name"); |
||||
expect(contextUtil.iriToKey("http://hl7.org/fhir/name")).toBe( |
||||
"http://hl7.org/fhir/name", |
||||
); |
||||
}); |
||||
|
||||
it("handles a context that existsm, but does not have an id", () => { |
||||
const contextUtil = new ContextUtil({ |
||||
name: { "@type": "http://www.w3.org/2001/XMLSchema#string" }, |
||||
}); |
||||
expect(contextUtil.keyToIri("name")).toBe("name"); |
||||
}); |
||||
}); |
||||
|
||||
describe("getType", () => { |
||||
it("returns xsd:string if no type is provided", () => { |
||||
const contextUtil = new ContextUtil({ |
||||
name: { "@id": "http://hl7.org/fhir/name" }, |
||||
}); |
||||
expect(contextUtil.getType("name")).toBe( |
||||
"http://www.w3.org/2001/XMLSchema#string", |
||||
); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,36 @@ |
||||
import { |
||||
getProxyFromObject, |
||||
getSubjectProxyFromObject, |
||||
isArrayProxy, |
||||
isSubjectProxy, |
||||
} from "../src"; |
||||
|
||||
describe("isSubjectProxy", () => { |
||||
it("returns false if undefined is passed as a parameter", () => { |
||||
expect(isSubjectProxy(undefined)).toBe(false); |
||||
}); |
||||
|
||||
it("throws an error if the given object isn't a subject proxy", () => { |
||||
expect(() => getSubjectProxyFromObject({ cool: "bean" })).toThrowError( |
||||
`[object Object] is not a Jsonld Dataset Proxy`, |
||||
); |
||||
}); |
||||
}); |
||||
|
||||
describe("isProxy", () => { |
||||
it("throws an error if the given object isn't a proxy", () => { |
||||
expect(() => getProxyFromObject({ cool: "bean" })).toThrowError( |
||||
`[object Object] is not a Jsonld Dataset Proxy`, |
||||
); |
||||
}); |
||||
}); |
||||
|
||||
describe("isArrayProxy", () => { |
||||
it("returns false if undefined is passed as a parameter", () => { |
||||
expect(isArrayProxy(undefined)).toBe(false); |
||||
}); |
||||
|
||||
it("returns false if string is passed as a parameter", () => { |
||||
expect(isArrayProxy("hello")).toBe(false); |
||||
}); |
||||
}); |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,63 @@ |
||||
import { createDataset } from "o-dataset-pack"; |
||||
import { ContextUtil } from "../src/ContextUtil"; |
||||
import { nodeToJsonldRepresentation } from "../src/util/nodeToJsonldRepresentation"; |
||||
import { literal, defaultGraph } from "@rdfjs/data-model"; |
||||
import { ProxyContext } from "../src"; |
||||
|
||||
describe("objectToJsonRepresentation", () => { |
||||
const extraParams: ProxyContext = new ProxyContext({ |
||||
dataset: createDataset(), |
||||
contextUtil: new ContextUtil({}), |
||||
writeGraphs: [defaultGraph()], |
||||
languageOrdering: ["@none", "@other"], |
||||
}); |
||||
|
||||
it("returns a string for hexBinary", () => { |
||||
expect( |
||||
nodeToJsonldRepresentation( |
||||
literal("F03493", "http://www.w3.org/2001/XMLSchema#hexBinary"), |
||||
extraParams, |
||||
), |
||||
).toBe("F03493"); |
||||
}); |
||||
|
||||
it("returns a string for HTML", () => { |
||||
expect( |
||||
nodeToJsonldRepresentation( |
||||
literal( |
||||
"<body></body>", |
||||
"http://www.w3.org/1999/02/22-rdf-syntax-ns#HTML", |
||||
), |
||||
extraParams, |
||||
), |
||||
).toBe("<body></body>"); |
||||
}); |
||||
|
||||
it("returns a string for anyUri", () => { |
||||
expect( |
||||
nodeToJsonldRepresentation( |
||||
literal( |
||||
"http://example.com", |
||||
"http://www.w3.org/2001/XMLSchema#anyURI", |
||||
), |
||||
extraParams, |
||||
), |
||||
).toBe("http://example.com"); |
||||
}); |
||||
|
||||
it("returns a string for an unrecognized datatype", () => { |
||||
expect( |
||||
nodeToJsonldRepresentation( |
||||
literal("meh", "http://weirddatatype.com"), |
||||
extraParams, |
||||
), |
||||
).toBe("meh"); |
||||
}); |
||||
|
||||
it("throws an error when it encoutners a quad that is not a Liter, NamedNode, or BlankNode", () => { |
||||
expect(() => |
||||
// @ts-expect-error defaultGraph is not allowed
|
||||
nodeToJsonldRepresentation(defaultGraph(), extraParams), |
||||
).toThrow("Can only convert NamedNodes or Literals or BlankNodes"); |
||||
}); |
||||
}); |
@ -0,0 +1,15 @@ |
||||
import { blankNode, defaultGraph, literal, namedNode } from "@rdfjs/data-model"; |
||||
import { nodeToString } from "../src"; |
||||
|
||||
describe("nodeToString", () => { |
||||
it("returns all the correct values for nodeToString", () => { |
||||
expect(nodeToString(namedNode("http://example.com"))).toBe( |
||||
"namedNode(http://example.com)", |
||||
); |
||||
expect(nodeToString(blankNode("_b1"))).toBe("blankNode(_b1)"); |
||||
expect(nodeToString(literal("Hello"))).toBe( |
||||
"literal(Hello,http://www.w3.org/2001/XMLSchema#string)", |
||||
); |
||||
expect(nodeToString(defaultGraph())).toBe("defaultGraph()"); |
||||
}); |
||||
}); |
@ -0,0 +1,196 @@ |
||||
import type { ContextDefinition } from "jsonld"; |
||||
import type { Schema } from "shexj"; |
||||
|
||||
export interface ObservationShape { |
||||
"@id"?: string; |
||||
"@context"?: ContextDefinition; |
||||
subject?: PatientShape; |
||||
notes?: string; |
||||
langNotes?: string; |
||||
} |
||||
|
||||
export type PatientShape = { |
||||
"@id"?: string; |
||||
"@context"?: ContextDefinition; |
||||
type?: { "@id": "Patient" }; |
||||
name?: string[]; |
||||
langName?: string[]; |
||||
birthdate?: string; |
||||
age?: number; |
||||
isHappy?: boolean; |
||||
roommate?: PatientShape[]; |
||||
}; |
||||
|
||||
// No need to fully define the schema because this library doesn't use it
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
export const patientSchema: Schema = {}; |
||||
|
||||
export const patientContext: ContextDefinition = { |
||||
type: { |
||||
"@id": "@type", |
||||
}, |
||||
Patient: "http://hl7.org/fhir/Patient", |
||||
subject: { "@id": "http://hl7.org/fhir/subject", "@type": "@id" }, |
||||
name: { |
||||
"@id": "http://hl7.org/fhir/name", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
"@container": "@set", |
||||
}, |
||||
langName: { |
||||
"@id": "http://hl7.org/fhir/langName", |
||||
"@type": "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString", |
||||
"@container": "@set", |
||||
}, |
||||
birthdate: { |
||||
"@id": "http://hl7.org/fhir/birthdate", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#date", |
||||
}, |
||||
age: { |
||||
"@id": "http://hl7.org/fhir/age", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#integer", |
||||
}, |
||||
isHappy: { |
||||
"@id": "http://hl7.org/fhir/isHappy", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#boolean", |
||||
}, |
||||
roommate: { |
||||
"@id": "http://hl7.org/fhir/roommate", |
||||
"@type": "@id", |
||||
"@container": "@set", |
||||
}, |
||||
notes: { |
||||
"@id": "http://hl7.org/fhir/notes", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
langNotes: { |
||||
"@id": "http://hl7.org/fhir/langNotes", |
||||
"@type": "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString", |
||||
}, |
||||
}; |
||||
|
||||
export const patientData = ` |
||||
@prefix example: <http://example.com/> . |
||||
@prefix fhir: <http://hl7.org/fhir/> . |
||||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . |
||||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . |
||||
|
||||
example:Observation1 |
||||
fhir:notes "Cool Notes"^^xsd:string ; |
||||
fhir:subject example:Patient1 . |
||||
|
||||
example:Patient1 |
||||
rdf:type fhir:Patient ;
|
||||
fhir:name "Garrett"^^xsd:string, "Bobby"^^xsd:string, "Ferguson"^^xsd:string ; |
||||
fhir:birthdate "1986-01-01"^^xsd:date ; |
||||
fhir:age "35"^^xsd:integer ; |
||||
fhir:isHappy "true"^^xsd:boolean ; |
||||
fhir:roommate example:Patient2, example:Patient3 . |
||||
|
||||
example:Patient2 |
||||
rdf:type fhir:Patient ;
|
||||
fhir:name "Rob"^^xsd:string ; |
||||
fhir:birthdate "1987-01-01"^^xsd:date ; |
||||
fhir:age "34"^^xsd:integer ; |
||||
fhir:isHappy "false"^^xsd:boolean ; |
||||
fhir:roommate example:Patient1, example:Patient3 . |
||||
|
||||
example:Patient3 |
||||
rdf:type fhir:Patient ;
|
||||
fhir:name "Amy"^^xsd:string ; |
||||
fhir:birthdate "1988-01-01"^^xsd:date ; |
||||
fhir:age "33"^^xsd:integer ; |
||||
fhir:isHappy "true"^^xsd:boolean . |
||||
`;
|
||||
|
||||
export const patientDataWithBlankNodes = ` |
||||
@prefix example: <http://example.com/> . |
||||
@prefix fhir: <http://hl7.org/fhir/> . |
||||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . |
||||
|
||||
example:Observation1 |
||||
fhir:notes "Cool Notes"^^xsd:string ; |
||||
fhir:subject _:Patient1 . |
||||
|
||||
_:Patient1 |
||||
fhir:name "Garrett"^^xsd:string, "Bobby"^^xsd:string, "Ferguson"^^xsd:string ; |
||||
fhir:birthdate "1986-01-01"^^xsd:date ; |
||||
fhir:age "35"^^xsd:integer ; |
||||
fhir:isHappy "true"^^xsd:boolean ; |
||||
fhir:roommate _:Patient2, _:Patient3 . |
||||
|
||||
_:Patient2 |
||||
fhir:name "Rob"^^xsd:string ; |
||||
fhir:birthdate "1987-01-01"^^xsd:date ; |
||||
fhir:age "34"^^xsd:integer ; |
||||
fhir:isHappy "false"^^xsd:boolean ; |
||||
fhir:roommate _:Patient1, _:Patient3 . |
||||
|
||||
_:Patient3 |
||||
fhir:name "Amy"^^xsd:string ; |
||||
fhir:birthdate "1988-01-01"^^xsd:date ; |
||||
fhir:age "33"^^xsd:integer ; |
||||
fhir:isHappy "true"^^xsd:boolean . |
||||
`;
|
||||
|
||||
export const tinyPatientData = ` |
||||
@prefix example: <http://example.com/> . |
||||
@prefix fhir: <http://hl7.org/fhir/> . |
||||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . |
||||
|
||||
example:Observation1 |
||||
fhir:subject example:Patient1 . |
||||
|
||||
example:Patient1 |
||||
fhir:name "Garrett"^^xsd:string ; |
||||
fhir:roommate example:Patient2 . |
||||
|
||||
example:Patient2 |
||||
fhir:name "Rob"^^xsd:string ; |
||||
fhir:roommate example:Patient1 . |
||||
`;
|
||||
|
||||
export const tinyArrayPatientData = ` |
||||
@prefix example: <http://example.com/> . |
||||
@prefix fhir: <http://hl7.org/fhir/> . |
||||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . |
||||
|
||||
example:Patient1 |
||||
fhir:name "Garrett"^^xsd:string, "Bobby"^^xsd:string, "Ferguson"^^xsd:string . |
||||
`;
|
||||
|
||||
export const tinyPatientDataWithBlankNodes = ` |
||||
@prefix example: <http://example.com/> . |
||||
@prefix fhir: <http://hl7.org/fhir/> . |
||||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . |
||||
|
||||
example:Observation1 |
||||
fhir:subject _:Patient1 . |
||||
|
||||
_:Patient1 |
||||
fhir:name "Garrett"^^xsd:string ; |
||||
fhir:roommate _:Patient2 . |
||||
|
||||
_:Patient2 |
||||
fhir:name "Rob"^^xsd:string ; |
||||
fhir:roommate _:Patient1 . |
||||
`;
|
||||
|
||||
export const tinyPatientDataWithLanguageTags = ` |
||||
@prefix example: <http://example.com/> . |
||||
@prefix fhir: <http://hl7.org/fhir/> . |
||||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . |
||||
|
||||
example:Observation1 |
||||
fhir:subject example:Patient1 ; |
||||
fhir:langNotes "Cool Notes" ; |
||||
fhir:langNotes "Cooler Notes"@en ; |
||||
fhir:langNotes "Notas Geniales"@es ; |
||||
fhir:langNotes "Notes Sympas"@fr . |
||||
|
||||
example:Patient1 |
||||
fhir:langName "Jon" ; |
||||
fhir:langName "John"@en ; |
||||
fhir:langName "Juan"@es ; |
||||
fhir:langName "Jean"@fr . |
||||
`;
|
@ -0,0 +1,7 @@ |
||||
{ |
||||
"extends": "../../tsconfig.base.json", |
||||
"compilerOptions": { |
||||
"outDir": "./dist" |
||||
}, |
||||
"include": ["./src"] |
||||
} |
@ -1,5 +1,6 @@ |
||||
const sharedConfig = require('../../jest.config.js'); |
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const sharedConfig = require("../../jest.config.js"); |
||||
module.exports = { |
||||
...sharedConfig, |
||||
'rootDir': './', |
||||
} |
||||
rootDir: "./", |
||||
}; |
||||
|
@ -1,5 +1,6 @@ |
||||
const sharedConfig = require('../../jest.config.js'); |
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const sharedConfig = require("../../jest.config.js"); |
||||
module.exports = { |
||||
...sharedConfig, |
||||
'rootDir': './', |
||||
} |
||||
rootDir: "./", |
||||
}; |
||||
|
Loading…
Reference in new issue