From bfce541e8f23f2bca887431868558ea5547df7b7 Mon Sep 17 00:00:00 2001 From: jaxoncreed Date: Thu, 31 Aug 2023 14:22:21 -0400 Subject: [PATCH] added jsonld-dataset-proxy --- package-lock.json | 391 +++- packages/jsonld-dataset-proxy/.eslintrc | 3 + packages/jsonld-dataset-proxy/README.md | 809 ++++++++ packages/jsonld-dataset-proxy/jest.config.js | 6 + packages/jsonld-dataset-proxy/package.json | 46 + .../readme-images/Intellisense.png | Bin 0 -> 275221 bytes .../jsonld-dataset-proxy/src/ContextUtil.ts | 90 + .../src/JsonldDatasetProxyBuilder.ts | 105 ++ .../jsonld-dataset-proxy/src/ProxyContext.ts | 110 ++ .../src/arrayProxy/ArrayProxy.ts | 20 + .../src/arrayProxy/arrayMethods.ts | 216 +++ .../src/arrayProxy/createArrayHandler.ts | 177 ++ .../src/arrayProxy/isArrayProxy.ts | 23 + .../src/arrayProxy/modifyArray.ts | 129 ++ packages/jsonld-dataset-proxy/src/graphOf.ts | 62 + packages/jsonld-dataset-proxy/src/index.ts | 37 + .../src/jsonldDatasetProxy.ts | 27 + .../src/language/languageMapProxy.ts | 75 + .../src/language/languageSet.ts | 129 ++ .../src/language/languageTypes.ts | 3 + .../src/language/languageUtils.ts | 159 ++ .../src/language/languagesOf.ts | 61 + .../src/setLanguagePreferences.ts | 14 + .../src/subjectProxy/SubjectProxy.ts | 20 + .../src/subjectProxy/createSubjectHandler.ts | 106 ++ .../src/subjectProxy/deleteFromDataset.ts | 44 + .../src/subjectProxy/getValueForKey.ts | 62 + .../src/subjectProxy/isSubjectProxy.ts | 30 + packages/jsonld-dataset-proxy/src/types.ts | 25 + .../jsonld-dataset-proxy/src/util/NodeSet.ts | 47 + .../src/util/RawObject.ts | 13 + .../src/util/addObjectToDataset.ts | 140 ++ .../src/util/createInteractOptions.ts | 55 + .../src/util/getNodeFromRaw.ts | 45 + .../jsonld-dataset-proxy/src/util/isProxy.ts | 20 + .../src/util/nodeToJsonldRepresentation.ts | 76 + packages/jsonld-dataset-proxy/src/write.ts | 12 + .../test/ContextUtil.test.ts | 39 + .../jsonld-dataset-proxy/test/isProxy.test.ts | 36 + .../test/jsonldDatasetProxy.test.ts | 1659 +++++++++++++++++ .../test/nodeToJsonRepresentation.test.ts | 63 + .../test/nodeToString.test.ts | 15 + .../test/patientExampleData.ts | 196 ++ .../jsonld-dataset-proxy/tsconfig.build.json | 7 + packages/ldo/jest.config.js | 7 +- packages/ldo/package.json | 2 +- packages/ldo/src/LdoBuilder.ts | 2 +- packages/ldo/src/LdoDataset.ts | 2 +- packages/ldo/src/methods.ts | 6 +- packages/ldo/src/util.ts | 4 +- packages/ldo/test/LdoDataset.test.ts | 4 +- packages/ldo/test/methods.test.ts | 4 +- packages/schema-converter-shex/jest.config.js | 7 +- packages/schema-converter-shex/package.json | 3 +- .../src/context/JsonLdContextBuilder.ts | 10 +- .../src/context/ShexJContextVisitor.ts | 16 +- .../src/context/shexjToContext.ts | 8 +- .../src/typing/ShapeInterfaceDeclaration.ts | 2 +- .../src/typing/ShexJTypingTransformer.ts | 52 +- .../src/typing/shexjToTyping.ts | 10 +- packages/solid-react/package.json | 2 +- .../ldoHooks/helpers/TrackingProxyContext.ts | 4 +- .../src/ldoHooks/helpers/UpdateManager.ts | 4 +- .../solid-react/src/ldoHooks/useSubject.ts | 7 +- packages/solid-react/src/useLdo.ts | 2 +- .../src/util/splitChangesByGraph.ts | 2 +- 66 files changed, 5460 insertions(+), 102 deletions(-) create mode 100644 packages/jsonld-dataset-proxy/.eslintrc create mode 100644 packages/jsonld-dataset-proxy/README.md create mode 100644 packages/jsonld-dataset-proxy/jest.config.js create mode 100644 packages/jsonld-dataset-proxy/package.json create mode 100644 packages/jsonld-dataset-proxy/readme-images/Intellisense.png create mode 100644 packages/jsonld-dataset-proxy/src/ContextUtil.ts create mode 100644 packages/jsonld-dataset-proxy/src/JsonldDatasetProxyBuilder.ts create mode 100644 packages/jsonld-dataset-proxy/src/ProxyContext.ts create mode 100644 packages/jsonld-dataset-proxy/src/arrayProxy/ArrayProxy.ts create mode 100644 packages/jsonld-dataset-proxy/src/arrayProxy/arrayMethods.ts create mode 100644 packages/jsonld-dataset-proxy/src/arrayProxy/createArrayHandler.ts create mode 100644 packages/jsonld-dataset-proxy/src/arrayProxy/isArrayProxy.ts create mode 100644 packages/jsonld-dataset-proxy/src/arrayProxy/modifyArray.ts create mode 100644 packages/jsonld-dataset-proxy/src/graphOf.ts create mode 100644 packages/jsonld-dataset-proxy/src/index.ts create mode 100644 packages/jsonld-dataset-proxy/src/jsonldDatasetProxy.ts create mode 100644 packages/jsonld-dataset-proxy/src/language/languageMapProxy.ts create mode 100644 packages/jsonld-dataset-proxy/src/language/languageSet.ts create mode 100644 packages/jsonld-dataset-proxy/src/language/languageTypes.ts create mode 100644 packages/jsonld-dataset-proxy/src/language/languageUtils.ts create mode 100644 packages/jsonld-dataset-proxy/src/language/languagesOf.ts create mode 100644 packages/jsonld-dataset-proxy/src/setLanguagePreferences.ts create mode 100644 packages/jsonld-dataset-proxy/src/subjectProxy/SubjectProxy.ts create mode 100644 packages/jsonld-dataset-proxy/src/subjectProxy/createSubjectHandler.ts create mode 100644 packages/jsonld-dataset-proxy/src/subjectProxy/deleteFromDataset.ts create mode 100644 packages/jsonld-dataset-proxy/src/subjectProxy/getValueForKey.ts create mode 100644 packages/jsonld-dataset-proxy/src/subjectProxy/isSubjectProxy.ts create mode 100644 packages/jsonld-dataset-proxy/src/types.ts create mode 100644 packages/jsonld-dataset-proxy/src/util/NodeSet.ts create mode 100644 packages/jsonld-dataset-proxy/src/util/RawObject.ts create mode 100644 packages/jsonld-dataset-proxy/src/util/addObjectToDataset.ts create mode 100644 packages/jsonld-dataset-proxy/src/util/createInteractOptions.ts create mode 100644 packages/jsonld-dataset-proxy/src/util/getNodeFromRaw.ts create mode 100644 packages/jsonld-dataset-proxy/src/util/isProxy.ts create mode 100644 packages/jsonld-dataset-proxy/src/util/nodeToJsonldRepresentation.ts create mode 100644 packages/jsonld-dataset-proxy/src/write.ts create mode 100644 packages/jsonld-dataset-proxy/test/ContextUtil.test.ts create mode 100644 packages/jsonld-dataset-proxy/test/isProxy.test.ts create mode 100644 packages/jsonld-dataset-proxy/test/jsonldDatasetProxy.test.ts create mode 100644 packages/jsonld-dataset-proxy/test/nodeToJsonRepresentation.test.ts create mode 100644 packages/jsonld-dataset-proxy/test/nodeToString.test.ts create mode 100644 packages/jsonld-dataset-proxy/test/patientExampleData.ts create mode 100644 packages/jsonld-dataset-proxy/tsconfig.build.json diff --git a/package-lock.json b/package-lock.json index e9f36ff..5f02dca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5181,6 +5181,10 @@ "resolved": "packages/demo-react", "link": true }, + "node_modules/@ldo/jsonld-dataset-proxy": { + "resolved": "packages/jsonld-dataset-proxy", + "link": true + }, "node_modules/@ldo/ldo": { "resolved": "packages/ldo", "link": true @@ -13609,6 +13613,33 @@ "node": ">= 0.6" } }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/event-stream/node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -14833,6 +14864,12 @@ "node": ">= 0.6" } }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -18187,16 +18224,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.12.tgz", "integrity": "sha512-d6xjC9fJ/nSnfDeU0AMDsaJyb1iHsqCSOdi84w4u+SlN/UgQdY5tRhpMzaFYsI4mnpvgTivEaQd0yOUhAtOnEQ==" }, - "node_modules/jsonld-dataset-proxy": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jsonld-dataset-proxy/-/jsonld-dataset-proxy-1.2.3.tgz", - "integrity": "sha512-TcKHylUSeGe3wr39L06nszpk7MFResd5TLetqlBh9rWVMpJxg96hsgSpjV8I8skgWVOx2ch638xUPhD3pXsOFQ==", - "dependencies": { - "@rdfjs/data-model": "^1.2.0", - "jsonld2graphobject": "^0.0.4", - "o-dataset-pack": "^0.2.14" - } - }, "node_modules/jsonld-streaming-parser": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsonld-streaming-parser/-/jsonld-streaming-parser-3.2.0.tgz", @@ -19408,6 +19435,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, "node_modules/md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -21326,6 +21359,12 @@ "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", "dev": true }, + "node_modules/node-cleanup": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", + "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==", + "dev": true + }, "node_modules/node-dir": { "version": "0.1.17", "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", @@ -22881,6 +22920,15 @@ "node": ">=8" } }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -24515,6 +24563,21 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -27075,6 +27138,15 @@ "node": ">= 0.10.0" } }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -27118,6 +27190,15 @@ } ] }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -28393,6 +28474,27 @@ "node": ">=0.4.0" } }, + "node_modules/tsc-watch": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-6.0.4.tgz", + "integrity": "sha512-cHvbvhjO86w2aGlaHgSCeQRl+Aqw6X6XN4sQMPZKF88GoP30O+oTuh5lRIJr5pgFWrRpF1AgXnJJ2DoFEIPHyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "node-cleanup": "^2.1.2", + "ps-tree": "^1.2.0", + "string-argv": "^0.3.1" + }, + "bin": { + "tsc-watch": "dist/lib/tsc-watch.js" + }, + "engines": { + "node": ">=12.12.0" + }, + "peerDependencies": { + "typescript": "*" + } + }, "node_modules/tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", @@ -30697,14 +30799,116 @@ "node": ">= 6" } }, + "packages/jsonld-dataset-proxy": { + "name": "@ldo/jsonld-dataset-proxy", + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "@rdfjs/data-model": "^1.2.0", + "jsonld2graphobject": "^0.0.4", + "o-dataset-pack": "^0.2.14" + }, + "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" + } + }, + "packages/jsonld-dataset-proxy/node_modules/n3": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/n3/-/n3-0.4.5.tgz", + "integrity": "sha512-sv4bFeqVTTj9hT/OAdndpHpECxlkmpHxdnHUkhNgx3P3Tnw2WqpTUzMEeY+ELEoeW1q6Xqq9LNO0lu/zqogIZA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "packages/jsonld-dataset-proxy/node_modules/shex-test": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/shex-test/-/shex-test-0.5.8.tgz", + "integrity": "sha512-wfrhu/lb2zrr4MANpoGbqLbPaoL0yOoDTJp6n/Ewtk2iEeVQD8RLYXVCMLYdmo6ccue/I/yubcIqv4p2uarCbg==", + "dev": true, + "dependencies": { + "n3": "^0.4.5", + "xlsx": "^0.8.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "packages/jsonld-dataset-proxy/node_modules/ts-jest": { + "version": "27.1.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.5.tgz", + "integrity": "sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^27.0.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@types/jest": "^27.0.0", + "babel-jest": ">=27.0.0 <28", + "jest": "^27.0.0", + "typescript": ">=3.8 <5.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@types/jest": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "packages/jsonld-dataset-proxy/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "packages/ldo": { "name": "@ldo/ldo", "version": "0.0.0", "license": "MIT", "dependencies": { + "@ldo/jsonld-dataset-proxy": "^0.0.0", "@rdfjs/data-model": "^1.2.0", "buffer": "^6.0.3", - "jsonld-dataset-proxy": "^1.2.1", "n3": "^1.16.2", "o-dataset-pack": "^0.2.11", "readable-stream": "^4.3.0" @@ -30897,9 +31101,9 @@ "license": "MIT", "dependencies": { "@inrupt/solid-client": "^1.29.0", + "@ldo/jsonld-dataset-proxy": "^0.0.0", "@ldo/ldo": "^0.0.0", "cross-fetch": "^3.1.6", - "jsonld-dataset-proxy": "^1.2.3", "o-dataset-pack": "^0.2.14", "solid-authn-react-native": "^2.0.3", "stream": "^0.0.2" @@ -36128,9 +36332,70 @@ } } }, + "@ldo/jsonld-dataset-proxy": { + "version": "file:packages/jsonld-dataset-proxy", + "requires": { + "@rdfjs/data-model": "^1.2.0", + "@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", + "jsonld2graphobject": "^0.0.4", + "o-dataset-pack": "^0.2.14", + "shex-test": "^0.5.5", + "ts-jest": "^27.1.2", + "ts-node": "^10.4.0", + "tsc-watch": "^6.0.0" + }, + "dependencies": { + "n3": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/n3/-/n3-0.4.5.tgz", + "integrity": "sha512-sv4bFeqVTTj9hT/OAdndpHpECxlkmpHxdnHUkhNgx3P3Tnw2WqpTUzMEeY+ELEoeW1q6Xqq9LNO0lu/zqogIZA==", + "dev": true + }, + "shex-test": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/shex-test/-/shex-test-0.5.8.tgz", + "integrity": "sha512-wfrhu/lb2zrr4MANpoGbqLbPaoL0yOoDTJp6n/Ewtk2iEeVQD8RLYXVCMLYdmo6ccue/I/yubcIqv4p2uarCbg==", + "dev": true, + "requires": { + "n3": "^0.4.5", + "xlsx": "^0.8.0" + } + }, + "ts-jest": { + "version": "27.1.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.5.tgz", + "integrity": "sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^27.0.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + } + }, + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "peer": true + } + } + }, "@ldo/ldo": { "version": "file:packages/ldo", "requires": { + "@ldo/jsonld-dataset-proxy": "^0.0.0", "@rdfjs/data-model": "^1.2.0", "@rdfjs/types": "^1.1.0", "@types/jest": "^27.0.3", @@ -36141,7 +36406,6 @@ "buffer": "^6.0.3", "cross-fetch": "^3.1.5", "jest": "^27.4.5", - "jsonld-dataset-proxy": "^1.2.1", "n3": "^1.16.2", "o-dataset-pack": "^0.2.11", "readable-stream": "^4.3.0", @@ -36252,6 +36516,7 @@ "@babel/preset-typescript": "^7.22.11", "@inrupt/solid-client": "^1.29.0", "@ldo/cli": "^0.0.0", + "@ldo/jsonld-dataset-proxy": "^0.0.0", "@ldo/ldo": "^0.0.0", "@rdfjs/types": "^1.1.0", "@types/jest": "^29.0.3", @@ -36259,7 +36524,6 @@ "@types/n3": "^1.10.4", "@types/shexj": "2.1.4", "cross-fetch": "^3.1.6", - "jsonld-dataset-proxy": "^1.2.3", "o-dataset-pack": "^0.2.14", "solid-authn-react-native": "^2.0.3", "stream": "^0.0.2", @@ -43461,6 +43725,32 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + }, + "dependencies": { + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "requires": { + "through": "2" + } + } + } + }, "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -44398,6 +44688,12 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -46920,16 +47216,6 @@ } } }, - "jsonld-dataset-proxy": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jsonld-dataset-proxy/-/jsonld-dataset-proxy-1.2.3.tgz", - "integrity": "sha512-TcKHylUSeGe3wr39L06nszpk7MFResd5TLetqlBh9rWVMpJxg96hsgSpjV8I8skgWVOx2ch638xUPhD3pXsOFQ==", - "requires": { - "@rdfjs/data-model": "^1.2.0", - "jsonld2graphobject": "^0.0.4", - "o-dataset-pack": "^0.2.14" - } - }, "jsonld-streaming-parser": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsonld-streaming-parser/-/jsonld-streaming-parser-3.2.0.tgz", @@ -47799,6 +48085,12 @@ "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, "md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -49333,6 +49625,12 @@ "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", "dev": true }, + "node-cleanup": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", + "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==", + "dev": true + }, "node-dir": { "version": "0.1.17", "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", @@ -50529,6 +50827,15 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "requires": { + "through": "~2.3" + } + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -51527,6 +51834,15 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true }, + "ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "requires": { + "event-stream": "=3.3.4" + } + }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -53542,6 +53858,15 @@ "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", "peer": true }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "requires": { + "duplexer": "~0.1.1" + } + }, "streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -53567,6 +53892,12 @@ } } }, + "string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true + }, "string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -54540,6 +54871,18 @@ } } }, + "tsc-watch": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-6.0.4.tgz", + "integrity": "sha512-cHvbvhjO86w2aGlaHgSCeQRl+Aqw6X6XN4sQMPZKF88GoP30O+oTuh5lRIJr5pgFWrRpF1AgXnJJ2DoFEIPHyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "node-cleanup": "^2.1.2", + "ps-tree": "^1.2.0", + "string-argv": "^0.3.1" + } + }, "tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", diff --git a/packages/jsonld-dataset-proxy/.eslintrc b/packages/jsonld-dataset-proxy/.eslintrc new file mode 100644 index 0000000..83c51a9 --- /dev/null +++ b/packages/jsonld-dataset-proxy/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": ["../../.eslintrc"] +} \ No newline at end of file diff --git a/packages/jsonld-dataset-proxy/README.md b/packages/jsonld-dataset-proxy/README.md new file mode 100644 index 0000000..e4c19cc --- /dev/null +++ b/packages/jsonld-dataset-proxy/README.md @@ -0,0 +1,809 @@ +# JSONLD Dataset Proxy + +Edit RDFJS Dataset just like regular JavaScript Object Literals. + +Just a few lines of familiar code: +```typescript +const personNode = namedNode("http://example.com/Person1"); +const person = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")); +person.age = 23; +person.name.push("John"); +``` + +are equivalent to: +```typescript +dataset.deleteMatches( + namedNode("http://example.com/Person1"), + namedNode("http://xmlns.com/foaf/0.1/age") +); +dataset.add( + quad( + namedNode("http://example.com/Person1"), + namedNode("http://xmlns.com/foaf/0.1/age"), + literal("23", "http://www.w3.org/2001/XMLSchema#integer") + ) +); +dataset.add( + quad( + namedNode("http://example.com/Person1"), + namedNode("http://xmlns.com/foaf/0.1/name"), + literal("John", "http://www.w3.org/2001/XMLSchema#string") + ) +); +``` + +Plus, you get IntelliSense typescript suggestions to help you write your code! + +![Intellisense Example](./readme-images/Intellisense.png) + + +## Installation +```bash +npm install jsonld-dataset-proxy +``` + +## Simple Example +```typescript +import jsonldDatasetProxy, { write } from "jsonld-dataset-proxy"; +import { ContextDefinition } from "jsonld"; +import { serializedToDataset } from "o-dataset-pack"; +import { namedNode } from "@rdfjs/data-model"; + +async function start() { + // Define initial data + const initialData = ` + @prefix example: . + @prefix foaf: . + @prefix xsd: . + + example:Person1 + foaf:name "Johnathan"^^xsd:string; + foaf:age "22"^^xsd:integer. + `; + // Create a dataset loaded with initial data + const dataset = await serializedToDataset(initialData); + // Make a JSONLD Dataset Proxy + const person = jsonldDatasetProxy( + dataset, + PersonContext + ).fromSubject(namedNode("http://example.com/Person1")); + // Make Modifications + person.age = 23; + person.name.push("John"); + write(namedNode("http://example.com/otherGraph")).using(person); + person.name.push("Smith"); + + console.log(dataset.toString()); + // Logs: + // "Johnathan" . + // "John" . + // "23"^^ . + // "Smith" . +} + +// Person Typescript Typing +interface IPerson { + name: string[]; + age: number; +} + +// Person JSONLD Context +const PersonContext: ContextDefinition = { + name: { + "@id": "http://xmlns.com/foaf/0.1/name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + "@container": "@set", + }, + age: { + "@id": "http://xmlns.com/foaf/0.1/age", + "@type": "http://www.w3.org/2001/XMLSchema#integer", + }, +}; + +start(); +``` + +## Full Usage + + - [Defining a Context and Type](#defining-a-context-and-type) + - [Getting a Jsonld DatasetProxy](#getting-a-jsonld-dataset-proxy) + - [`.fromSubject(entryNode)`](#fromsubjecttentrynode) + - [`.matchSubject(predicate?, object?, graph?)`](#matchsubjecttpredicate-object-graph) + - [`.matchObject(subject?, predicate?, graph?)`](#matchobjecttsubject-predicate-object) + - [`.fromJson(inputData)`](#fromjsontinputdata) + - [Getting Field Values and Traversing](#getting-field-values-and-traversing) + - [Setting a Primitive](#setting-a-primitive) + - [Setting an Object](#setting-an-object) + - [Array Methods](#array-methods) + - [Overwriting an Object](#overwriting-an-object) + - [Changing an Object's Id](#changing-an-objects-id) + - [Removing an Object Connection](#removing-an-object-connection) + - [Deleting an Entire Object](#deleting-an-entire-object) + - [Using Blank Nodes](#using-blank-nodes) + - [Writing Information to a Specific Graph](#writing-information-to-a-specific-graph) + - [`jsonldDatasetProxy(...).write(...graphs)`](#jsonlddatasetproxywritegraphs) + - [`write(...graphs).using(...jsonldDatasetProxies)`](#writegraphsusingjsonlddatasetproxies) + - [`write(...graphs).usingCopy(...jsonldDatasetProxies)`](#writegraphsusingcopyjsonlddatasetproxies) + - [Detecting a the graph of specific information](#detecting-a-the-graph-of-specific-information) + +For the most part, a JSONLD Dataset Proxy has parity with JavaScript Object Literals. However, there are a few differences to highlight. This section details how you would do different tasks. + +### Defining a Context and Type +The first step to getting a JSONLD Dataset Proxy is defining the JSONLD Context and TypeScript Typings. This can either be done through a [generator](https://github.com/o-development/shexj2typeandcontext) or defining them manually. + +In this example typescript typing `IPerson` is an interface that represents a person. Notice the `@id` and `@context` fields. Be sure to include them in your interfaces if you wish to use those properties. + +```typescript +import { ContextDefinition } from "jsonld"; + +interface IPerson { + "@id"?: string; + "@context"?: ContextDefinition; + name?: string[]; + age?: number; + bestFriend?: IPerson; + knows?: IPerson[]; +} +``` + +We can make a [JSONLD context](https://w3c.github.io/json-ld-syntax/#the-context) to match this type: + +```typescript +import { ContextDefinition } from "jsonld"; + +const PersonContext: ContextDefinition = { + name: { + "@id": "http://xmlns.com/foaf/0.1/name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + "@container": "@set", + }, + age: { + "@id": "http://xmlns.com/foaf/0.1/age", + "@type": "http://www.w3.org/2001/XMLSchema#integer", + }, + bestFriend: { + "@id": "http://xmlns.com/foaf/0.1/bestFriend", + "@type": "@id", + }, + knows: { + "@id": "http://xmlns.com/foaf/0.1/knows", + "@type": "@id", + "@container": "@set", + }, +}; +``` + +To do this, create an object that has corresponding fields to your type. Each field is an object that contains the following properties: + - `@id`: indicates the URI of the corresponding predicate + - `@type`: If the corresponding type is a pimitive (Like a number or string), use this field to list the RDF Literal type (Most often this is an XMLSchema type). If the corresponding type is an object, list `@id` here. + - `@container`: If the corresponding type is an array of items, set `@container` to `@set`, if not, do not include the `@container` property. + +Note that only the features described here work with JSONLD Dataset Proxy. Other features of JSONLD Contexts are not yet supported. + +### Getting a Jsonld Dataset Proxy +Once the Typescript Typings and Context have been defined, we can get the JSONLD Dataset Proxy for a specific dataset. + +```typescript +import jsonldDatasetProxy from "jsonld-dataset-proxy"; +import { createDataset } from "o-dataset-pack"; + +const dataset = await createDataset(); +// Make a JSONLD Dataset Proxy +const person = jsonldDatasetProxy( + dataset, + PersonContext +) // ... +``` + +The functon `jsonldDatasetProxy` takes in three parameters: + - `dataset`: The dataset you wish to traverse and manipulate. This can be any dataset that follows the [RDFJS Dataset Interface](https://rdf.js.org/dataset-spec/#dataset-interface). Note that this is not to be confused with the RDFJS Dataset ***Core*** interface. This example uses the "o-dataset-pack", but any implementation of the RDFJS Dataset Interface is acceptable. + - `context`: The JSONLD context. + + After defining the `dataset` and `context` there are a few ways to get Jsonld Dataset Proxies: + +#### `.fromSubject(entryNode)` +`fromSubject` lets you define a an `entryNode`, the place of entry for the graph. The object returned by `jsonldDatasetProxy` will represent the given node. This parameter accepts both `namedNode`s and `blankNode`s. `fromSubject` takes a generic type representing the typescript type of the given subject. + +```typescript +const person = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")); +``` + +#### `.matchSubject(predicate?, object?, graph?)` +`matchSubject` returns a Jsonld Dataset Proxy representing all subjects in the dataset matching the given predicate, object, and graph. + +```typescript +const people = jsonldDatasetProxy( + dataset, + PersonContext +).matchSubject( + namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), + namedNode("http://xmlns.com/foaf/0.1/Person") +); +people.forEach((person) => { + console.log(person.name); +}); +``` + +#### `.matchObject(subject?, predicate?, object?)` +`matchObject` returns a Jsonld Dataset Proxy representing all objects in the dataset matching the given subject, predicate, and graph. + +```typescript +const friendsOfPerson1 = jsonldDatasetProxy( + dataset, + PersonContext +).matchSubject( + namedNode("http://example.com/Person1"), + namedNode("http://xmlns.com/foaf/0.1/knows") +); +friendsOfPerson1.forEach((person) => { + console.log(person.name); +}); +``` + +#### `.fromJson(inputData)` +`fromJson` will take any regular Json, add the information to the dataset, and return a Jsonld Dataset Proxy representing the given data. + +```typescript +const person2 = jsonldDatasetProxy( + dataset, + PersonContext +).fromJson({ + "@id": "http://example.com/Person2", + name: ["Jane", "Doe"], + birthdate: "1990/11/03", + age: 33, +}); +``` + +### Getting Field Values and Traversing +Getting a field and traversing the object is just as easy as getting data out of a standard JavaScript Object Literal. + +In all the following example, we will use a dataset loaded with the following data: + +```typescript +const dataset = await serializedToDataset(` + @prefix example: . + @prefix foaf: . + @prefix xsd: . + + example:Person1 + foaf:name "Johnathan"^^xsd:string, "John"^^xsd:string; + foaf:age "22"^^xsd:integer; + foaf:bestFriend example:Person2; + foaf:friends example:Person2, example:Person3. + + example:Person2 + foaf:name "Alice"^^xsd:string; + foaf:age "28"^^xsd:integer. + + example:Person3 + foaf:name "Dave"^^xsd:string; + foaf:age "33"^^xsd:integer. +`); +const person = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")); +// Get primitives +console.log(person.age); // 22 +// Get nested primitives +console.log(person?.bestFriend?.age); // 28 +// All array methods work +console.log(person.name?.reduce((agg, cur) => agg + cur, "")); // JonathanJohn +// You can also access array items via their index +// But this isn't recommened. The library will do its best to maintain the +// ordering in the array, but as datasets have no concept of order, this is +// not always accurate. +console.log(person.name?.[1]); // John +// Get the id of the object +// (If the node is a blankNode the @id will be undefined) +console.log(person.bestFriend?.["@id"]); // "http://example.com/Person2" +// Finally, you can retrieve the context +console.log(person["@context"]); // { "name": { "@id": ... }} +``` + + + +### Setting a Primitive +Setting a non-array primitive will remove the existing triple from the dataset and add a new triple. + +```typescript +const dataset = createDataset(); +const person = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")) +person.age = 23; +console.log(dataset.toString()); +// "23"^^ . +``` + +### Setting an Object +Setting a field to a JavaScript object literal will recursively add all parts of the object literal to the dataset. + +```typescript +const dataset = createDataset(); +const person = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")); +person.bestFriend = { + "@id": "http://example.com/Person2", + name: ["Alice"], + bestFriend: { + "@id": "http://example.com/Person3", + name: ["Bob"], + }, +}; +console.log(dataset.toString()); +// . +// . +// "Alice" . +// "Bob" . +``` + +### Array Methods +Any methods that modify arrays work as expected. + +```typescript +const dataset = await serializedToDataset(` + @prefix example: . + @prefix foaf: . + @prefix xsd: . + + example:Person1 + foaf:name "Garrett"^^xsd:string, "Bobby"^^xsd:string. +`); +const person = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")); +person.name?.push("Ferguson"); +console.log(dataset.toString()); +// "Garrett" . +// "Bobby" . +// "Ferguson" . +``` + +### Overwriting an Object +If an object literal is set and the id is equivalent to an existing id, that node will be overwritten. All triples from the previous object are removed and replaced with triples from the new object. + +```typescript +const dataset = await serializedToDataset(` + @prefix example: . + @prefix foaf: . + @prefix xsd: . + + example:Person2 + foaf:name "Alice"^^xsd:string; + foaf:age "28"^^xsd:integer. +`); +const person = jsonldDatasetProxy( + dataset, + PersonContext, +).fromSubject(namedNode("http://example.com/Person1")); +person.bestFriend = { + "@id": "http://example.com/Person2", + name: ["Jane"], +}; +console.log(dataset.toString()); +// "Jane" . +// . +``` + +### Changing an Object's Id +You can rename an object by setting its `@id` field. This will update all triples that reference the id to the new id. + +```typescript +const dataset = await serializedToDataset(` + @prefix example: . + @prefix foaf: . + @prefix xsd: . + + example:Person1 + foaf:name "Alice"^^xsd:string; + foaf:bestFriend example:Person2. + + example:Person2 + foaf:bestFriend example:Person1. +`); +const person = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")) +person["@id"] = "http://example.com/NewPersonId"; +console.log(dataset.toString()); +// . +// "Alice" . +// . +``` + +### Removing an Object Connection +Removing one triple can be done by setting a property to `undefined`; + +```typescript +const dataset = await serializedToDataset(` + @prefix example: . + @prefix foaf: . + @prefix xsd: . + + example:Person1 + foaf:name "Alice"^^xsd:string; + foaf:bestFriend example:Person2. + + example:Person2 + foaf:name "Bob"^^xsd:string; + foaf:bestFriend example:Person1. +`); +const person = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")); +person.bestFriend = undefined; +console.log(dataset.toString()); +// "Alice" . +// "Bob" . +// . +``` + +### Deleting an Entire Object +If you want to delete all triples represented by an object, there are two ways using the `delete` operator. + +First, you can call `delete` on a specific property: +```typescript +const dataset = await serializedToDataset(` + @prefix example: . + @prefix foaf: . + @prefix xsd: . + + example:Person1 + foaf:name "Alice"^^xsd:string; + foaf:bestFriend example:Person2. + + example:Person2 + foaf:name "Bob"^^xsd:string; + foaf:bestFriend example:Person1. +`); +const person = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")); +delete person.bestFriend; +console.log(dataset.toString()); +// "Alice" . +``` + +And secondly, you can call `delete` on the `@id` property. +```typescript +const dataset = await serializedToDataset(` + @prefix example: . + @prefix foaf: . + @prefix xsd: . + + example:Person1 + foaf:name "Alice"^^xsd:string; + foaf:bestFriend example:Person2. + + example:Person2 + foaf:name "Bob"^^xsd:string; + foaf:bestFriend example:Person1. +`); +const person = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")); +delete person["@id"]; +console.log(dataset.toString()); +// "Bob" . +``` + +### Using Blank Nodes +If you want to create an object with a blankNode subject, simply omit the `@id` field when you're making the object. +```typescript +const dataset = await createDataset(); +const person = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")); +person.bestFriend = { + name: ["Charlie"], +}; +console.log(dataset.toString()); +// _:b1 . +// _:b1 "Charlie" . +``` + +If your dataset has blank nodes and you want to assign that blank node as a triple's object, you can retrieve it from the JSONLD Dataset Proxy and assign it. +```typescript +const dataset = await serializedToDataset(` + @prefix example: . + @prefix foaf: . + @prefix xsd: . + + example:Person1 + foaf:knows [ + foaf:name "Alice"^^xsd:string; + ]. +`); +const person = jsonldDatasetProxy( + dataset, + PersonContext, +).fromSubject(namedNode("http://example.com/Person1")); + +const alice = person.knows?.[0]; +person.bestFriend = alice; +console.log(dataset.toString()); +// _:n3-0 "Alice" . +// _:n3-0 . +// _:n3-0 . +``` + +### Writing Information to a Specific Graph +By default, all new quads are added to the default graph, but you can change the graph to which new quads are added in a few different ways: + +NOTE: These operations only dictate the graph for new triples. Any operations that delete triples will delete triples regardless of their graph. + +#### `jsonldDatasetProxy(...).write(...graphs)` +The write graph can be set upon creating a jsonld dataset proxy by using the `write` method. This method takes in any number of graphs. + +```typescript +const person1 = jsonldDatasetProxy(dataset, PersonContext) + .write(namedNode("http://example.com/ExampleGraph")) + .fromSubject(namedNode("http://example.com/Person1")); +person1.name.push("Jack"); +console.log(dataset.toString()); +// Logs: +// "Jack" . +``` + +#### `write(...graphs).using(...jsonldDatasetProxies)` +The `write(...).using(...)` function lets you define the graphs you wish to write to using specific jsonldDatasetProxies. + +```typescript +import jsonldDatasetProxy, { write } from "jsonld-dataset-proxy"; + +const person1 = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")); +// Now all additions with person1 will be on ExampleGraph1 +write(namedNode("http://example.com/ExampleGraph1")).using(person1); +person1.name.push("Jack"); +// Now all additions with person1 will be on ExampleGraph2 +write(namedNode("http://example.com/ExampleGraph2")).using(person1); +person1.name.push("Spicer"); + +console.log(dataset.toString()); +// Logs: +// "Jack" . +// "Spicer" . +``` + +The function also returns an `end` function that will reset the graph to what it was before. This is useful for nesting graph modifications. + +```typescript +const person1 = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")); +person1.name.push("default"); +const end1 = write(namedNode("http://example.com/Graph1")).using(person1); +person1.name.push("1"); +const end2 = write(namedNode("http://example.com/Graph2")).using(person1); +person1.name.push("2"); +const end3 = write(namedNode("http://example.com/Graph3")).using(person1); +person1.name.push("3"); +end3(); +person1.name.push("2 again"); +end2(); +person1.name.push("1 again"); +end1(); +person1.name.push("default again"); +console.log(dataset.toString()); +// Logs: +// "default" . +// "default again" . +// "1" . +// "1 again" . +// "2" . +// "2 again" . +// "3" . +``` + +#### `write(...graphs).usingCopy(...jsonldDatasetProxies)` +If you would like a new variable to write to without modifying the original Jsonld Dataset Proxy, you can use `write(...).usingCopy(...)`. + +```typescript +const person1 = jsonldDatasetProxy( + dataset, + PersonContext +).fromSubject(namedNode("http://example.com/Person1")); +const [person1WritingToNewGraph] = write( + namedNode("http://example.com/NewGraph") +).usingCopy(person1); +person1WritingToNewGraph.name.push("Brandon"); +person1.name.push("Sanderson"); +console.log(dataset.toString()); +// Logs: +// "Brandon" . +// "Sanderson" . +``` + +### Detecting a the graph of specific information + +The graph of specific information can be detected using the `graphOf(subject, predicate, object)` function. The `graphOf` function takes in two to three arguments. + + - `subject`: A Jsonld Dataset Proxy that represents the subject of a quad. + - `predicate`: A string key + - `object?`: An optional parameter that represents the direct object of a statement. This could be a Jsonld Dataset Proxy or a number to indicate the location in an array. This argument can be left blank if the given field is not an array. + +```typescript +graphOf(person, "name", 0); // returns defaultGraph() +graphOf(person, "age"); // returns defaultGraph() +``` + +## Managing Language Tags + +RDF includes a special attribute for string literals called a [language tag](https://www.w3.org/TR/rdf11-concepts/#section-Graph-Literal). Lanugage tags let developers provide string representations for many different translations and JSON-LD Dataset Proxy helps you manage them. + +To use language tags, RDF requires the datatype of a literal to be `http://www.w3.org/1999/02/22-rdf-syntax-ns#langString`, and LDO's functions will only work on literals of type that type. + +For the following examples, we'll use this context and dataset, typescript typing and JSON-LD Context. Notice that there is a field called "label" with translations for French and Korean and one language string that doesn't have a language tag. There's also a field called "description" that holds multiple strings per language. + +```typescript +// Define initial data +const initialData = ` + @prefix example: . + @prefix rdfs: . + @prefix ns: . + + example:Hospital + rdfs:label "Hospital"^^ns:langString; + rdfs:label "Hôpital"@fr; + rdfs:label "병원"@ko; + rdfs:description "Heals patients"^^ns:langString; + rdfs:description "Has doctors"^^ns:langString; + rdfs:description "Guérit les malades"@fr; + rdfs:description "A des médecins"@fr; + rdfs:description "환자를 치료하다"@ko; + rdfs:description "의사 있음"@ko. +`; + +// Typescript Typing +interface IThing { + label: string; + description: string[]; +} + +// Define JSON-LD Context +const PersonContext: ContextDefinition = { + label: { + "@id": "http://www.w3.org/2000/01/rdf-schema#label", + "@type": "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString", + }, + description: { + "@id": "http://www.w3.org/2000/01/rdf-schema#description", + "@type": "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString", + "@container": "@set", + }, +}; +``` + +### Language Preferences +A language preference is an ordered list telling the JSON-LD Dataset Proxy the language you prefer as well as callbacks. + +Valid values for the language preferences includes any [IETF Language Tag](https://en.wikipedia.org/wiki/IETF_language_tag) as well as the special tags `@none` and `@other`. `@none` represents any language literal that doesn't have a language tag. `@other` represents any language literal that isn't listed among the language preferences. + +For read operations, the JSON-LD Dataset Proxy will search for values in order of the preference. Write operations will choose the first language in the language preference, unless that language is `@other`, in which case it will choose the next language. + +```typescript +// Read Spansih first, then Korean, then language strings with no language +// New writes are in Spanish +["es", "ko", "@none"] + +// Read any language other than french, then french +// New writes are in French +["@other", "fr"] +``` + +Language preferences can be set when a JSON-LD Dataset Proxy is created using the `setLanguagePreferences` method. + +```typescript +// Create a dataset loaded with initial data +const dataset = await serializedToDataset(initialData); +// Make a JSONLD Dataset Proxy +const hospitalInfo = jsonldDatasetProxy(dataset, PersonContext) + .setLanguagePreferences("es", "ko", "@none") + .fromSubject(namedNode("http://example.com/Hospital")); + +console.log(hospitalInfo.label); // Logs "병원" +console.log(hospitalInfo.description.length); // Logs "2" for the 2 korean entries +console.log(hospitalInfo.description[0]); // Logs "환자를 치료하다" +console.log(hospitalInfo.description[1]); // Logs "의사 있음" + +// Adds a string to the description in spanish, because spanish if the first +// language in the language preference +hospitalInfo.description.push("Cura a las pacientes"); + +// Now that a spanish entry exists, JSON-LD dataset proxy focuses on that +console.log(hospitalInfo.description.length); // Logs "1" for the 1 spanish entry +console.log(hospitalInfo.description[0]); // Logs "Cura a las pacientes" +``` + +### `setLanguagePreferences(...languagePreferences).using(...jsonldDatasetProxies)` +The `setLanguagePreferences(...).using(...)` function sets the language preferences for a set of JSON-LD Dataset Proxies. It follows roughly the same paridigms as the `write(...).using(...)` function. + +```typescript +import { setLanguagePreferences } from "jsonld-dataset-proxy"; + +setLanguagePreferences("fr", "ko").using(hospitalInfo); +console.log(hospitalInfo.label); // Logs "Hôpital" +setLanguagePreferences("@none").using(hospitalInfo); +console.log(hospitalInfo.label); // Logs "Hospital" +``` + +### `setLanguagePreferences(...languagePreferences).usingCopy(...jsonldDatasetProxies)` +The `setLanguagePreferences(...).usingCopy(...)` function returns a copy of the provided JSON-LD Dataset Proxies with the given language preferences. It follows roughly the same paridigms as the `write(...).usingCopy(...)` function. + +```typescript +import { setLanguagePreferences } from "jsonld-dataset-proxy"; + +// ... + +const [frenchPreference] = setLanguagePreferences("fr").usingCopy(hospitalInfo); +const [koreanPreference] = setLanguagePreferences("ko").usingCopy(hospitalInfo); +console.log(frenchPreference.label); // Logs "Hôpital" +console.log(koreanPreference.label); // Logs "병원" +``` + +### `languageOf(jsonldDatasetProxy, key)` +The `languageOf` function lets you view and modify the languages more directly. `languageOf` takes two properties: + + - `jsonldDatasetProxy`: A JSON-LD dataset proxy + - `key`: A key on the JSON-LD dataset proxy pointing to a language string. + +It returns a mapping of languages to strings or sets of strings depending on the cardinality of the JSON-LD context. + +```typescript +const labelLanguages = languagesOf(hospitalInfo, "label"); +// labelLanguages: { '@none': 'Hospital', fr: 'Hôpital', ko: '병원' } +const descriptionLanguages = languagesOf(hospitalInfo, "description"); +// descriptionLanguages: +// { +// '@none': Set(2) { 'Heals patients', 'Has doctors' }, +// fr: Set(2) { 'Guérit les malades', 'A des médecins' }, +// ko: Set(2) { '환자를 치료하다', '의사 있음' } +// } +``` + +You can also modify languauages by changing the mappings. Mappings with sets of strings follow the JavaScript [`Set` interface](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set). + +```typescript +// Adds a Chinese label +labelLanguages.zh = "医院"; +// Changes the no-language label from to "Super Hospital" +labelLanguages["@none"] = "Super Hospital"; +// Removes the French label +delete labelLanguages.fr; +// Adds a Hindi description +descriptionLanguages.hi?.add("रोगियों को ठीक करता है"); +// Checks to see if the korean label contains "의사 있음" +descriptionLanguages.ko?.has("의사 있음"); // returns true +// Removes "Has Doctors" from the no-language description +descriptionLanguages["@none"]?.delete("Has Doctors"); +``` + +## Limitations + - Currently this library only supports the following features of JSON-LD context: + - "@id", + - "@type", + - "@container": "@set" + +## Liscense +MIT \ No newline at end of file diff --git a/packages/jsonld-dataset-proxy/jest.config.js b/packages/jsonld-dataset-proxy/jest.config.js new file mode 100644 index 0000000..bad5f64 --- /dev/null +++ b/packages/jsonld-dataset-proxy/jest.config.js @@ -0,0 +1,6 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const sharedConfig = require("../../jest.config.js"); +module.exports = { + ...sharedConfig, + rootDir: "./", +}; diff --git a/packages/jsonld-dataset-proxy/package.json b/packages/jsonld-dataset-proxy/package.json new file mode 100644 index 0000000..5a72ad9 --- /dev/null +++ b/packages/jsonld-dataset-proxy/package.json @@ -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" + } +} diff --git a/packages/jsonld-dataset-proxy/readme-images/Intellisense.png b/packages/jsonld-dataset-proxy/readme-images/Intellisense.png new file mode 100644 index 0000000000000000000000000000000000000000..9c25bec24e23fa0b6b896ba5f57ba8cf8f0ba57d GIT binary patch literal 275221 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%0Akxng@45Tc|X1tR`0s1GNu>1_t4_$xuy3U^0+%k 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%0DwZZf>@PmO_WWnD^s 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%0D6n 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%0Dkz5^& z6&_l{-+P&mlTOu=!CTepS#iE822ZIm?b$K4hWVS)PKGGrwzq)1YM8dw=0Olje zr|t2NBeeu2d_?sZX!n6gSTYtCsp)MV?qnWT2yxU>2}U2INL{7*#T}GGQLKB1W=Ymb zXPgz?)LlLoQ77Q4EV{jdt2h!^+4g{LbU#B?`6VGky+`fga3sVt!@~g)f_UF5M$ihp zRdcnUiNO0h%D(mN@5I0<`^c%OUQE)hVJ2t5M4C?I;l*@z$b`bgpFa8>6F$FJ+v@tm zLy*h^z4w>X(MIs=wNM`M);ZXF7KLpSHGJ*XdFUhgci~aQv#F!gg;FLn z6!s0A)&!(H7K?TJa(OX&eT)kZg7Y2MaT-IcqGb8{RM%I`z2Gu@omP0^A1Od)l9u|7 zp3v|GVdVQ2!55`k%9-}8!t{KKd7jNDMNpi=-YJM(puZDQIeX6DN?5Yajp#z@vDl&d z%r^U;yig}FL#Wxjrh^B)@VX_i%`HSU_v=F$sANDBR6zcXoc!Te8yRcsk7_@KZl?AQUQJ^Bcdlwdzg`6*sj^h`GIfqMiTkq8>g8+uV2>c6^`?W zws{09A4>K+n?R!16h}yy@A>j^`4_amJe+Not{s7!wRBr93*32w9cfTW4>kWl%z8(HveSGgfRj39sI6MiXKEC z#t;2Mz%7evj?OIK-Ac05?QqDKvVN1XipD~i#j>URiIv$&X7KFS>CQ~jP2FoI@q+H; z`RNI*bb%r#MXAEZx~BM`e$C1=WicGum6eQ83Z z>OOP!I2{@sH!ym&CRTg^j>HRbH0V$Xi5k64zt3-Gtq@V4V#PpESg4z~V=a4O#Wjc3 zdE9>VnwOL~7{V87-23ORn#0baC>seaGvmD(`X{@;w6KrU?FwJ}^V?#gO2V4{luHGJ z(=##|afQ%R9d@Ap{+sI3ss*E=JkrhjJH=1 zBqmO|UMzw#oX7Qc2XHDTYgy3=N|SN`<35V1GXrc~9^#QsL7r+1+0w(tCRB2dij(ee+G`Im*5or1|Z$%Dh(X@JwK%K5+?wMcg!})ODeZ9 z74~`ISVa4X8XDGD*qoi8wF|vx9r*U4d8u5GSRdPYknJ=6QAl279a)7Gjk>qAI>#E*I^fvRyf?3nWzY2NWDNL@WDix;?_LCo-uYUYgZl-C zsvx*R%BR2XE&}KEHKX>Heyw5N)+7iyyv~^yxN*bbse6et(-339e?$E1~o)u)|@U4B(v0r#I$NP-oKGxV% zZEw}pz4}GTud11!1m4cZWIKt3mScRf$L{>y6XEf>XkPPjBlMS)4`Ove0~UX&V85Fp z-jJ9%hr*oFuj}hn(I2rMHin~Ig^YjxD#Vdmr1#Uxw$}ZuN8ahI9l2PeKKqf=QkpD^ z^+dNW~*2p&KOqSl4FCWWuOu!K+%b-YoPD1A#lRlH7+rI)5I*RcE8_>R4=F|Jr zfu%JjJaA^1J?R$Z?86S;(QChp>58?%G|Vrfs4i4Fm!B+}KVC?;;>BwBy2-OAIwqaV z<-Bw`=pJRq8J*CW{J__H%^v5Y`uBM0wS7$tWbqSAToeA+8W;CdINF48k^PT*tE)%q zMdV6iE5pJ5(qfANmClUIlh0>EF`&FhNtnoA6x+a;d&ON!*H!yeh}TF&T3I8IL2quct);e={kMLGDDcW=-q zE9(F;L#^+i3*_tn$G-RY&tj*y&oDmvuyPI$1~Mpc z#EUIeSKscYy9E8vTkAW$71lpt-3X`p(>xhj)t{bp3|=TY z;e*(8sqbi#Ae?spsAI_p;q_oFOE*Oy8X3lex_2n7qn<6+dvjce2adiq%c9!zcC9LR z=jF~IhYC7&6k`n1o-4YOW7`^n$vW&Ga2;3BENpeGuvb}u2 z*9JqUJYLc)rBAlF=Yy{hHxs!Mk(lGd)pu$F{BJO*7Zlv*gHGS@I$D6Ba~T0+YwWbY z@yvv@PK5>%$MUmhpNZUz#4a0Yj!qlB7Cxq-(5B@*pDp+85QGG&c35%5s7o0TA~BfW z$|BmRJ3I>zd4>A@r7h+_r0QFJzl)2LHSoh_TMq_ zADTeFwE4J&;q>p*L6})NUupli6V`Q17N@FVjDv+18Y8o^L^P^%!nKggqsA{i1b?1| z$}FD{ft(`{(IKWK7Ixgk zQ5zaWzQ%Za{#ud*9h&Jwf)Ys=`};Y*ZtExq3$inb{)y4udegkHsX_ow;yQ<4bsPcl zcL)=KVHh39GnZK7t2k%V2GcqZIQsgm@bAjq5<-(j<5zcHT&B^g;A*KF<5zy2(&z9M zD#ts8zUy|8w9GzGxY%JgjO!%?d?8hgGGj&oU1eSyckyN>yQd@o0kpN zC}*ZpnqC#pv_=Iq&zv!X!hRPko@DSs+bkGOF$|et;eS%R_|$WNu{foz&UNB{@~#7? zwlzr+_?_664s=q#i2r{8gg|@0d$x0GZ+CG3Uc+Rp_8U0TEl4k18}5^rom_h5KG$f} z!+P7vI&*D~z}LD~BW+xxFS}n={eQF&sFd-4v{PRVyn?_BfPh3zff`2kINlHc<@=|K zk5G{B<(T|1p-%qx;;rI85{~9GV!Ru6W+}a1a~RebD^2+BpE!zX{~fa& zOwLPwk8o!GAeY0%F@^f^2lFCmbRuXNZpN@Z#=)CbS%zUG3iJEsLP7Mv7Q^dR&z?!j zE*X=bes)o318wLv5Xv|EuQJN9hCu58s#HOsg1{RE0dPnYN5+kXdx3CcNA_$3N8lX} zJ_%1kkMoj8oZ^Le;*Y?Ef$zk5_0aGO_#&a2y5Gv@;u7(Sc+6MIovH{NT;kBEv%X^U zlUY1e_QSG4LvlHdwBT!>b%7hZ5WqT74rqLg%lq_<7ndsR+Xr7cQMW>)N)~d&XX{Z( zz@LQMLx78nsqdCA$B?UucVQKM%Kgvp{#uShL$&3F$HpX{54xCP!FTJUtvL*)D%qtW z_Sdn+aqy_7tjjkPpbEd93RoqNA$SgHXu+fYC>O0y%q#?5(?Aix->e@S`}N%49S8m9 z)d5Y~yRDPgR_3M^$NUwPxZQCA;YP<$(!rY-exbH@XwRVp^fF9^O@?{=z@PkX|NNZk zRsH;IgsFM+Tv*26E$o~1M;*SGbL{VVotY;!>W4DfQB$Zo$WGm;St)&GeXhECL9Y*w|3&bA8}qvzulU^< ztyV!QTac%NkHSbB`!3c--Mmn5hRPa0SGFB(*ag0qk7}PYJY(+*-;^y7nQK&d!rxqn z2m1dm-70h@oI}Sjj1g@R-9;qVS((ysDCatLOg^v;+pW!unR%9fv%KxsC4X8QN1oH1 z!C%W1ANk{Z#(}n1r8g1+l`{T~oYrbk6$HLV2uPS9Iwy(2`OklJsyKaMH^TgU@g-g% z|Hp4H7613|zD^8jW?Gs+J9R`wTDCF8GxJE8Jtr1S06J#`sNauH5O)wyIcX_G^&`CG z|Ag4|Q!r{Db1AGIn^U}&WZq2QhJM+0v=1S*-(EQ6OJzG3o_4@Y9K_>mD-7cUJox%$ zHp1KAa;e$)n+IoISs;kI{gof{@9CT2EJ@~N%t6C>kn{Uqm*1R`Y|py=jlMR}KfOwN z@#36=agZr~1l&etB2=zCL$XSRsPL#nfNmk4-cgQr8`tk9^~`u&)D(R)cal5`G*g&|E=RB%T~6Y_zBYUoLnApTn6PeN*ZF=bp)zSn-s z-+j-oD6qax@r^0o%FpRZdYFIT_{`@DM&d_)Gd8*cRV#h&Kp50JYR=yqcrL!*STzIl zlu(=&qP!=G{2q!X^RHSrFVi>TMC<2OW`De~@;Ba|nEWk|)pr5Udw3o4(Y|SjU0O-; zxf2j0Qz}Q^CGfbHr%yn;P7?m@JuXYvL+sRnJ?K1^i)(nL4~pl5zDtK+R>Gqn_-{oC z-vWJ|p+72X+nJZ$c&@6*4LHjKq=D8t;**qsfpGAZlOI=oR*pMA_q{$}es2DlOFr0E z&J*=Sp`0`QmVeJr{q7owep+agWS))XTx|9+expqM{-p9{ozYJ|`~JKsx9|E3&}`Bt zt2N(K$5WOwZ;aWFl+Wy!Wqt9{{iY#b=Y_oA=FPKWK3=&`R`FOWf9Ea1$PXm@zj33k zgPnsj3;E@zXn$dLu6XchsTgHlw{u_7B0S?X&oUi| zJRAF+%SD|O40u?9b;bnoj6(jN+sXB9Z0BWtFS`-#+dtE~esAbd?k8i2qW&?Nt@c~_ z+2^L5C&o?2Tz}**v()nV-8{Sr&v@>a4XM7Zx}U_4OBlZlwHCNsdSG717B4Ebx?Fyf zs-rsRggW%*KE?^%54lUlkXtjz0^`Uat16G{=wn?M-n@^($azo}!P9CLdFeV~MsFgo zHMZgr&+`>zvSnou_v1#Gmhf-kw|3+z@jqt1kU0Wad0qmqPmNpK zX`VMejylWydGD3k*S$r#van^Wzx-;xwZ8DMk#EwWwTJ7fO5;dXtpB0Di1p+;csJ#C z)3=xV*CwOAvYF^Y*^k_Ez%DYbgGFtg=)e0!Wqdm_rS}1>uy>qq`OBp|zqy{e@zp?Q zTRwM*<&^u9@uw4E0iRoE)K_NqnR8)z+BeSw9Q*pd;?5k`B>a-=c16C&aUaf#R|EnQ42V`2 zUJUzp_QdgDiEIzY-Or(z9~{Th%rWy>JSoTV5}DBxN$-pq=kS9CN*k=ZEp?z3|jfIbJrjjItvxrGd!r z#PiS)4<2Fd=E$bMU%5R{+~uO$RbUlH2Lu8=%Z$r>lJWHdOE0dSLpkwaaEzmTgIrXL z;1~=7OsY&ib@w6!7?V*?&qnea+xWP!p&q>-M}dW*eulVg(fGSp@m3i|IUin5^gk!9 z^$IC6Ov3Rr7rM3s^C=i;l{AOx&kFV38|W?WF zLAfaKfq}-(MwBm;d$-5$_C*h`Y1-B+Qd%co41vl4may3Zz1jzCwm~1IHF~b+ai|=! z4%;`(!MwE6M4Qn#9}TS`tyM`K9>;i<2Zf^z<8p5SK3cNWT` zxkilF7|(52FEuY99LCThEgN*`4*EqWs}v@**TNdnYueBe(!b#e))i|(dhYnPFjvc2 zsIEz;WrrLazZq^~7jvlhr7(1Ct~=+r6&U+GP6&{F8bRANGLqLXdE02C3xl&fhM^S9 ztHDX5fN%^$fxFEONP|2WCK^P|%1j7hzNXNZQS6F+RZl1NF zUO*V^=^j>xPr}A$ z`Q?6LEbWX>o+)4pTukGv^8xCaRe8wRo5-^h_-hPlXI^3tI|n3T>zZ=C3<5_lybH$% ze%``~lYJ;7y}Z)6|L)PZ4)2XRTKH@X={ffsJeB)RgOJ@D10LcOW64Ldue{{9TnC=C z6i*n_*LLLtyV*a6%hJ3SVfNsMrr^s`ZH(a{yq9I1hb3NK61>81BlLX^ zoZ<6Wk7YOUBF|}($Ff~0({*m&od^5mdKb6btnhK)%-B!nVAMHgf0)0Kd^@*w$9$^z@-&jC667K0$gh`d z1JAvDS~y7?ME^Q|7&>CgwLxE;(`M@0!kjp#+knkh=F>gy{t4Vhi(U%pUKbb3lg9}N z=f%XERhifROIOabP7i=L8% z!`-D*+2rB(E@VCHlzzx_h_BKbP4n*>8>hT@ZJM#naq3&ynUxv;V*@ar9qftStSwLS zx##97C;f7d$@eU==K_xO>xCvaAaxq*P8m>s&OQWe=tt1F5~kuN`{Y~*Q}?%*vb#qe zzjC?rc!HC6JCISOD~|OD&;I1CqcI=WD?S~7P8uGG+CHpQqh9fp9;A+M&!5k?!$9i{usEUpRzarl$99$Do z7I+J{%+pHOTqCXx*Q@I*(uj%MXfw@uSnl@2}r4{_*SE#24->uH$h>z|f?CCSnQ|1mh7jFIV8ZjI)H8 zHd_V3~1y_5TkKO_F=1;W!DfGF-jF|M#IeUoT)K@@u+m?IF_IUhbxvONiIXA74* zZ7<#?B*S04ce3~qv210!M#g7~7M0z!DTARxNaqPem0-p`zH*OK0GEqD5JR~i#r(8= zhv_TY8jGXU_s>2!Q+#;(AaQZ`gy8$X{kz{3cTup1w>nH(Fq4cy!Qfy0{O#f|-#dlU zzb%yg|IdH^y7<@s0l^UkIMhGEe4e`7n~Hbn-~asQKZ4BTp4R|m9vKOli^CWpezAHKqeN>QCzL@AVUgcw=I1d!j1|AS zbSGX{na_uO4+R@=k2SGYZS|>wKm~!<83H99b^j|YdDK{8$uMLKd`6O3+w74^I29Ez3Q`JU3d!Ogm6#txXNJGNQ!J^t0iSntw{Usn!Q#h+P7`OF4BsU_v#dtU z{U{GZ*ruHjq!9AI=0dT*{rq|{O`U<4DJOpPpMsyl*eQ&a@1u-QiaG>#`k^p-pHo}c ziGlhRv4ZEaGM)a1K*#vEmoc=@agt0WTT&FKP&qRQ?R9qgtbo(MP$omO${nshy> zSlC1V4wuSYl@Fr`*H@WejoM#xvEbauVqAdxle0&P6Rh`L80ihQqe9R1Vp>>Uiam4t z5k~dCk>c{5zT%N9es@p0cD6KzvV1RNIe%z(aT0!N8*mp^S6J_V z_rU?gQF%4we!Fs~_=hi1uA%gBLw90O_%VC* zLtdTiGm}aWl@-FyOM+Fn)$cbIC6~B+BKDj0*zZ=9V66Ez6!B+J-v1fnRYCR@r!9vt z6vOPMA^huE>J$h0v$#-2v;3t>J7N13b8&_BC5*d)oA}Aormo-H@L9HTox7I? z*ef8vgnz}s^54Fkdy(slO3nxPp_Tsq=;VRoXUv63gPpj`_1*!`sS;ELxZ}~=^lR@E z;iT#$`^7nJVa(z!?|pJT%%J!(OwcFS?%`wt{26aUM{_9WK7dc&2F%3mUgG@wt9Oej z=ST+>=2U~y9-KptvKQVW{`4W{$2}KbwyYcHNE&mWvNv#e`2wCi3_b1#lDOPgGxObz zg3&=T-yPz9qk&JcD%tf)W-qMqx@D&Gh{ceLiy-x`JUr7nZ zg43Fg3hrqd(NgP}z=cQQQ@nR0(WaKbSD!KNi=6Ix1RN&K7i*I;FL3LpIxz--3(tXv zkI@ffz3&9~ZGk2?5}L>Ph>&-K<3Us9eeN+YbIbF=a~byT9CyjR&AQkN@1rw;`&1ZG zL4=wWKGI6}kaswI%sP<PHzkH}lZ+RbOUSZo4{A6;p&}eBr zzrJ#}n8SH&c}YV~!Y`+h!H@NB55utz4AM`HDmp;iWgag9+q=O-y5CFL9QbzcXe)lf zJh&dm!3XjPIvsjiRNNuxH40>)i!tqOWJ5t$z+*Dt&dWYfP~p z^Uw|*R>G*snj3=$xCPH34hrWC>u}#x>5YQG2CDK#jjb9?1%V0z-vwbtRftbm_9bU#gt2dx=|&Lt@}ENpj;A1? zUNrYU%4d}YdYO$5%poZCgfJijy@mE=61>dy`4gNT{gg1un92C)HTe!qgehC%{oSPC zM2TP^^h8wIW1k$y9D-6Pau^$0jZog4gn{sqIz0hYqWoXK`Z6){GEvr~bjD{H(6b0F zADum9GsQCDVHB3*QW=#G%d`(uK_~IH--dv6u=p=79E0KON%>&}W%4Fr0A!Sehn{ez z;fXyV^f^u$#`7MVmn{+b%Qf*H~a#;)tRFbET~pwQ*qWrm}^ z^6GO1feHezCj{Kn;5@xl6{7U~)+=76q(Z~G{f@FrWYGc6Q~7ijAxuH31%XE8w`fOY z*gk})5WY}UDS(-0Ue8u=$$bot3kW2m2x5!N2(93sJqUna`lS$LTyX_Cm7Re(snmY$ z-oCXM1^?buNEh$YN16+7aXp$k!RaOivJlMq+!`LtD0}&=;wQq;fWP#-OmVO+VJ$%# zqW|_+oTze6rM`-#sFOUqwk?JdNkz|1lsNbtmv|P#E%ae4I9r93<2VC7P{A5XA;w}W z;@`a(I~8(OHmDGqRcR~W#MksqFY=?5y+9pD5Ms9@7+Y51O{Kw3;3#~gBlC-R2&e9o zW3vQ&d9313!M2CY#?#l>p|Lp?erg4qRr(FjsE>A}O#2o3rBOkTbYbB7-v&P=yw9^QkHRlF?n&+kxQF7veQUWc;BbOA z&S4<%<#^^%d?dpHdH7^~?bLq_#jyJ@+U3nD5k75_m{ZrH_(!jA$0^=&&!`NK)0nYGN}#79$C$p=AHhL5NR^1Lzqc5NYuFR7g3C4L z9Jlat-?@*rF&6uCgD@p}LxW&q-Zj#?epQ-HOec@zizwZqBAH40YFU+S!rt*Xe=7EC zdFYOY5bM#gMLfD2*w`20;M#PJsbpIMHo`IXspBwHBvvz@g}eAgT;x0nQ*pU%+J8?~ zNcU8%6%F9?T)eC$7WfAGXiRjCJK~NSef+L-klTAz-x*(k2Dmq~C*2R?Z0V44lWRvW z?jdNJI5_rZ-CQ^<*H0&V%u~rr8eK?&*5q%vU9p{#5_<{2x3sZ8+`BoGJ)r##%1a+r z0E;J&!H1Zf7u$BfZ)N|lvIoT{o*JHDzEl$LVeLvsP94B|o4H@ailE%%`tSm1@wt48 z>q7;9Eb>5a9qtXog!n@`B%fis8u2~F_GogBH9r~q$#olR zR7JdaoO(^-295ux_V14UHb6U%=<|K>zu}Ok0{-UpRRYa|-!(CgCi>}KbgyY_Rvs7s zh(Enl-TiOBXBJnO^T}c>{G0Qxc{6&?XASDkizg<{YY%;`^4ZgY9UV?%I&C2SlZFIh z&HazHl1?Of`rf{XYdmcx4wlymB|q>DT1TI}h<+ER`#jxkeaq0)+@D5%Zw2;V6ucdW zH)*7}{Q>~jB@$otM|gw z>48>j7`53d1+o2(LrHW~#dKoCa&@*nLo8jr;)d|1nnKwv;eU(q={a=>CgESw7lr;i5c3CE z#NoW6xI|V)E|_zksF2Ev?_7U=xBX0*R{0g-n)vDXBr{qb7;*t zxL5D|R)nq-c)q=hfNbpQKJeQ^Uc=BI4VWHJ);sAD9&Q>NLLj7!Ng>I9Dn;$5$`;G( z&Fz>}V5=0ik9xOnK^P38gpg`Fh63>dxYKY5UY05>_1qk0oGKzc)ugAg#%k&1`{Y%L zd*l94(NBLuDNEaJv~d)ru}TJ&NqT4LiRDQJ!)hEs$?Ay%6=Xp&k_Z}oc+wvQNIlA> zPa)hQSZ1U1GZcSPwVOW=LX$#496d4?saS__3QCoxbVR(Rjcod#R z3D5%khq$19VYc1?l<1XoQh{3UX+u^BTa{?i(S8&ceIt{h5K{1VURBtKf`x)oXlc8! zwht;26s-3%k19}9B3*~B&av*K;|k6mL6=r$gH2Y}xZe6{OL}Pt8I`lHm1E4wE|l?x zCvr9Q5gz3c&I)=`qERRo`E00;Cg$G@lBZaUim8U=<$oHb@LRye`PDPf znCGjkKkJB;z!44rJAmy;@*hBf>gBj9m-H-DS>26NSK2)QAET%K4fc~KDpXwj2t~sO zc($LwbFd9BQ%@3HgqI8Namr{oJHotlfD=@P1kV7>+$)BE`0^h70jpg_FE;3uNC&6ivK$( zXEmnmLAiWtA7QXiP`!=8posH%cb>PAuK}f`_jrXDKG}EhMxu|dB|y)-k7M9cHc9j`O8Do^Oy&IP!92-Yyn+1Dg3(Lb=7|ME zU8zhKzxqo1%?T-!<)J$_!9#!Z#f_k!o^H0Bp)pmm-p~UcI&OFt*UU8QtslClC$sw? z5{g)e7T(L<#ZSOrZzGrWu=nIUZh_x!t`je$onFG(Dsb*<7;80dPSB5Q(5_j=k`AD$hx#|I+}?abvzSK_**RQI*@Lt z<@vvY$HW^A%tH@*-~hbFDd@C{c#YHp6NBM2Ar9XyU4zC+Q-z7s=^l22$y4279n6E1 z9|0d7#*V=I>?MCUb&JQv9~Y4^E${d|^&q`SXN42_m?czw2>YdQwRrMJrV4wl!3@32jg2rCJxYEgYL+N<8g1BGW zrtIBK9p*L3YlP7^V88=`P^C8x0+llUjhoVHKotaj00=-R^*T~0HAdUt5yIp%!qQxU z85xH7{S%bsz1xrAA%M~vMT2o^jj7iNgR8L9*E|};SR$wOd?)$NaAff3#0mTvjFpU& z0)QS|62AZR)y?AHetHE$wHU4+5-$WXi{#010!fG zRgb>QoQ8B0Ok8fbe7F8GDaaTy;2$pDMtB?xAyn^v8EX~sdJ!pPeu_~=2AM%72`5)j zy;l&ZAn`XIuM!t>bFcqqdRg`l9K639>k zhD%V{sp3zi$sjn(@LAUo>@8mBq;_`gWl(>t>7|M+N`^qJ0QvAAmPcBGjt1QjvIr zz8OY=CL<)-afHIIw!I;gokh9l50mu0A0>43g>u^vj*ZK$N0ng|6y$E;g|AS52pFjt z+=+mklpdSSPTjMT_IS+PcuVM#-WN@mxxFx|?UOLt~~19m5a-7I0PTz9vant(zfKE97RA7>rq zet0+`_L%#O*T&={jcLAwyE$sO%*iKduymbzDptIwg79-feSFTIX`$R+Xn|{aJMfi1 z*MJhq%1PGZcnqy0uCtwo?8nQD#c>%@*4rL3wZvjI5y^Z%qOso39W|%z}L`hxe4%q z>qg_OINuO#D#+dEzqyR_73*4ByTINWlSY%Djgo3Z2Ey7{>_fivM4pB_guFytTaLob!7y>r98C|oBrPNS4} zO)8gU8oUGW$zfqU4K>34m%qGt7sm_Ay7zT%cnDrvpiiv2SclSUPo<60FW1{W z@_Fh`KO|!bXFkCwz1u5EsgKiIY4{uR*}O;yN*$&$nWbWd7ti zPlLBU;P?HkbNPdN@Ts3cU**3H0i>hGF$_2w36-loO(9N7IFJr_YNASS90V$5{2Mo= z)qpAp{7?{BVh;N z4};@U)BHL&!L}CY1#W zc*f_~Lt`0uO~6=u1;aFh$I9J)m_US*ewfqn@(G}=^+)ijjL7N1&;&959~OT{Oj=_z znx<&OlL{(|WSnG-^%TwgcrIJLR}iQm@H#^v;h6@6%_fw13I;0k6sR>Q4x`8$C1$3= zL``t4AFiZ;yyCqOmQaY@#xw0U0+0sR^M}#}d)E=5?jVd9dO@XYJBkhQiJm|v6}DdX z)r*&z%18wkW6*k9$hyNLnLK*T>DjI4n@YJsV!|qnA=wox2=J5ST>TB0g&qAZVhp~!j#4V6$%ef z3_L_RV|^-#cO!hKupi5zbSFQJPnJ*9~Xi{Z(2ZmXF906+jqL_t)HQfm(i{mC7i zBw*Z;P_{-W6lje~N&EU0-v7TvN$~(>y_b!u_&mh;^{P^l;ZW@J6B9qt^-$ai3zb$X zy21lb#YrS{yuf62z{A?5l`8OrjS309`=v1&pjGH-_|fRA0(zJ^y3YJ1I{J8@9Hnc^ zzH8|C0tM`6#8g+w=68R8KwXl(aq8Cld>Li1DPRPgr1vTu^g6#YJXTymadH!ye-C4} z9(y^j3L@its~j?E=>F}c+Ywe|n0#}r5fu*GQT`}^yEYZpRZy#RRZ!Ou@rXH{WBpmT zO8jn~JO3KIJw_kCfOeRdjthpm=_YK6^%=&algB8~k<4ku}YvVm%2 zUR2U}V#2ll6*2d}V&BMDx)1EP>qEZNxrlmzm+x^ZhkRbFsOP%-$+hkZN_)dbH%}@+ z(wZQyFk|`x{^}DA6Y!xkjI#w7={|T`y(tZA_$kMIAH5Ij(y> zJm-Otmz~}q|JNA%W`MW*Ee5>#FrV>h!hd$oH5WJ#nBPEweH;EzIB6vG#6<6Q6xiSq z4XfjiZRxg@%B9d zVv~B!Mnxq~vu^wAniAKzCyZYocR;Y7c7Uh7u+`J9&gVgJ)N$mN0m{s<=2uk{JY&#D`;`WgM^itFH}Es?Twq@e0mF7+JW!ZruN zc`Cuhf6@^R=_+fzB>dn`4A0P=Lnx9pAc^w~m1djbX&t8e;7x9^zK^o5UAs%r@&OzQ zTraL2>s0Y8-w_T;0x%v_}PY3>vPH()~FCjJ-%%L!ovmVLFS^!;7>>p-o=A>+pl7xW{5( z(VMzI2@lC5YV5j;9^kj!UE&D`?=i9Nht6ft-9^0fxeT4QGwzYjIfz-x4uE$@F;*{9 zX053w!%z@Amm2as(W%V+5d3@%ox~%IPu^wW{S)%bhTD?gJBG7?G;x}ImK87hyRB&G zzXP5Yhk9!LHoS)ATT#I4T;lq%%}DvZ{93)eSrDj{@o(0AR->sP@Pk1h6z~vOnGcVk zDWG{|_G_40<3f7m?JP{sF9=m*XqBIzJ4_69!VloRbq9hfF;d7>IR7!>Y4mth`Te_V z4~qX6@9bY9><&rL$g?7`hrn)ysL8lVM+&9{1~3HZc#{4om}WT2)Cl{}Z#*o1haqGdh5>Fmj@7B$Fhqkf zeN9hQ_N1z-zZC>32)uR>&;toCTkyy>aIwloQz*W{zbYNXK_PU-FKd6qnZ82VK|Z}x z4Iyw7g}h-F!qW?Ubr*bo6@`(PQ7UjMm|8H5g5|r)kYot(a;$VpNFhZ}%+6I}wxa}7 zVAosB5E>pq_az<<2@9cVr2+3b%0_%w=!dk*p6j7zoeG5W2yJLjz>Ad2$w8D!df?0P z>k%v-ya(Ni_@dCb2PjGnTk%t9n?n9UkEtW*PavpHp#acu;8+~n1a0-BT=256TjFK# zX&(wym22-G!>A&D3;YcmlPEttQDNQn^e(q4%~i=0YX@(+W(00SlAJ&p&Z?&Pp0^41rG8=8Uj4^p|`YQlH5ys3G?Jd?)gpy=Uz~$h#o^> zBk%GxVIcky1-2&-(EJq5^hHl>$DpEG1%;o5xrPJRu&jl9%`i-Ql24=faUb=w#s(TW ztw9PH*jRj_KI#f(CHYp^gNyDv_Zu(wj!G`?LC|{}24=&4V*-o;vMwdQ#J%cx4a29C zz?Yl?Tx2iHm$+uJn?!PMBjrB9BYAnBfl?+Ilc#j-hy5v+1HV{k?OT-hsd=bd1-T~* zzvRNizrS<~YYv79ltDY0XG0))*{E~o{xLsam}Z`QuAPJrV*NRWX8yFgW|+TAtgS0NH>`v4m%VrB45t%};VjRo_icngqOZcj z@DYCxpL&ONIRl=6>0>Y@56bV~CRr+R!zfNMVu=I$FpL=D<34;bi>(+K>55Yw;;>Hc zY&Z&UJdVQY9^(z;m+Q^0@F(7>ldq4Zb$KE~&XxL}6q~Fg+EmFeu8%t)+%G&ceA6&{ zYIq_kfUPe!cZt;UKGylii(XaO-sg0|9rn};w5pH2r!mTtrw7}60=CC7tS*D+@3Rhq z?$H%-#a8CS%d#_l^s?q@PAnLr%K6%CT|GA7H|c@&mOVb*PM9w6x%*Ze)zkhO&Xqe?@sVDIl@DbpuxV)0N5|64RuGPgHvS~P}(^0&Klh7IFNyV@@OyjP+ zhkMffWqo?0yB2+USBIyvJh`J>Bz>_DzUII!BN%lCFv?ESZqOs}p9Vnjsrgm7YG7RH zO1LBsWraR@*{e(K42$D{)39`bGC>=i z8v7o7;SY5Lo?ig}vwt+y%4Y~a^EtN~aG3OfdjoFoQT_@2rxd?aP#AK`>n3~F%fE+| zQGv1eGt$~HQvO_@{h^$6Y=$|K_YfKgUb(I5fW2gGG*kAwqDAp*Ob$G#otAdbdOiW zD1@m{aU+AMfGDGM1ffW;Og$HNtg-rDL7;-b>k9$Hbf|biIfvk)U}@6Wn!FN;pUmD* zqUJ{Al}0TJQl0xts?Zo1{9>CA^ti(`?t(*3eeJ&ND9F=SeFu( zGiH9epihIP_)z6f;Kxw7CBe=<=xJB;n4=z_so1oAmA8ggP$?hft%EV@A+6vo-fn{~ z>ZKe83cBotaR(W<@sfqd_)?0|st~I{OvVVKp{Mg+JOYpI@}jvl8df)y$38~iDDQiK z3k8?&k=BFZv9izo!20O_67$upM=^qZjFHDG=e<1CIguw2R?e#)^f}B*xYViG*(8}? zg@woY4Jon;EH#?zZ0%)nwL#_i&DGS#2)y`hfjbKfWpfX@J_aoeWrE6T>WF%B+X4xg z8IquvxiK`+7W&;ye+`MUhdHzfY&1X^ zwrCK=prQN>HRC#WjD~43REkIcS6Q>d#t?uiY4y@o;Xkj!l(A1}D8eA52i^n5Lrf81 zLWl?Z&OAuhQ%WaHZc}#;o_eQg$GEtjO86O}_l{u%nnii!$%Qf2oPE|i(mk-^zVT?k zaI{`u@yxB4_BBspzXgvcEMvd%n`NAv4f`$iDe0qJ$9f_@U@nXw-NzbV3?3}uBx+fk zTgL=^YttQlNy;(X&WfO@-?pDArOSBrhG${vs!J(OHL!2>5Z5qidn)~Ec!#;%!JHi+ zyo0fU_4e#XF>QFAVM6P;E?2;N4*`sxlC$7J>yV!^`B(ln420m#P)t(CBJ*{V=c|s9 zJ?lLYiGRjJmvx{)pc{O!mv%G|h!={bRHth%79exWWTekNiiU+-na^jNr2Mm|1muGa zB_*y39LBrKHp6=e4^R5+#;7Q7szFM}l%zyU6+5)lpY;s|ZQzIK>iVA&PLz!^i1cKZ-4tV;ZFbzel5KxDHu`sq0u=Za(LOg%_1kI6mm-Zq5A0%e~@AEQA2<37<{IFCH$gw}lb^-amK z`Ep*Z#y?S(kM7Mpc~7gSR^B?Il2nIfS#QsjVjS!<=(+Go%PiORlp3P{(qi|jWy>-Y zNlIbhTjGq>I6YIsq|E2K7x(FTEWWTh>51#lHKn3Gl=0-(=$*bNkA3%vrx@J3R;LUq zo`@58%&&@gd*gHIzeXT&PP^-hXRZ;Iz%fnKqg*)Y{$O1hH{Eq(=&^JHknmn8XAOm? zm$>(bc;}1wVBI`Byb|Ub6+E?J*gem}k-%6qi$ z2||s2d%G=5wsDZr)Y*zhrr4Vcp;YxY#B9=^T`nI#N$r}(Ub{le0hdHPaIPT zQ``9huSl8wgDB&F#0di#%>Us}&KLjmofE+)YFDJ{bkVm23c&;tY4H0@kC1xn6$S?j852;AjgG)oDUU3nxm)}B2Rj4gr z8K+Y;%KAM7Y2He(J+Kxq@+&oN$feF%AmY!RPF+0s+kusb^uhE;Mk@JwOUaR*JbAJn5@sd7mHQRR^I zC;DACj&&aay2A=@?NZrf9V$M3f1xQRm_Nd}6``BC$DHdl2;BNAc^t@5QrR z!Q0>Uj#r_v5tg}st^r>vP*lXvVC;`^vsUAS{KvZFs7Vdw81v%uF$Gl^=uvEp`cTfv zcgV{`D$BZJ5t>a0P+0%P`=^Ricq{AO?q$K|(#WF{R+_3pPs4(Oy2+_?%|^eOvpeuT zdcKdGo+ZSLC+>QR_qeopZ0Bt3iQ_2RHAJcC^qvBh@$(qV?U$D&x3LaARp7kM&Ko-& zg(US3p_EkF6QMd(8t5VISn^u;JrW@UsQV&E?x$T(jNn5&oV`TR3->~i%{X*SFwU<^ zaTP>Y2)i<`GJ(Aruom#XqW;7)2n_z0kJ)eWyLtSfe5Fo2_E-3r-}QW7zJ#%>;EFwG z*_apJGY0XQ-}0Q=GVQp>g|~J2FJ($^Px~w8(wL$LwwDOk#)i_>Cfv_CY?gg>?bw%g zl~o!@PzHKgv>v_g&0)j+sCeK)Df1c)WI7G_7xW$$Ukeud^-SrpY~=tP3rXeVm+-$UJU+__R8Oa>DEtZQ)w!By z9p7S|e8%aC>)aO+-px4j=V+vowm z0HHu$zwtlQNAIRkiDnA=h!Jj3!vJkQl|GW6Y7z_`bHJx=@XnPPo;B5q@*zI^=Nap$ zm8D3UthH7{`CZr7wO=0pIs)W;A0=3JU4G2F=4h+DiLB(INHr)0EFPfl4^E!i#mR%<); zHOA_5oeEe}BgliEw$NEhL#dYyYh3-15Ud)r#2Mbl@$0Mii{BDjbOOaJgd&vIwK*&M zIm%wwxk~!yKXH<4SKg}Dk2-kO`uc4@P*=b}UW7KhKwbl|^0_C}bcVPwuz?Mgr{YetND@dka720Pq%#B8|}K4fLlGWZ!|IHpZ!plMK=&1iViT1p~1%Ul3`D8G?E6h2Il( zN4;Yan(GVM5%V){-doJ^o_p+SIcop8h zYs}Etn~fc52A)#KGyg;%`L9sgh@wgbg{Qc(f>cF{%9Ga8;NC+Bfn@B3OhLP(r^Zb0 zgT|o+OJiOecJ#uzSyCD2+{ibS@Q(Lq?5BdZ^J-WcFQ5w#x*`e6wJL<0b<^i9C_(qZ z!<w@xGxx(V69hHKHnNu;igeN^=iMV&h zb?iLrEiXKh@^HGCC$D=GEq~-u2_h^zfvNMT!b#(VMgT8GRB_k^?8hi44bMiaFkIO_ zd5xX>2rqi)-n@oNF>H>ZQS`vx!MtfO=%7u*f@n~>F~H^S?zOaFYapusu=bOm(l`Ic z^a;nOq%?)e&@zhR`Vop#mA#P|@7mlxt)6l}k>wkj+q5T6ckDHL?3Xh6N#(B!Xj9&M zeinxFz+5=Ho?^afT)dEUn`J#|fKl3VIq8dwuWlIXoKp=k;c?AeG!u@iow+!Gp+v_I z>(*h!xl}>t>6%*@cf1#%cBhhcu_oeDM&{1?JYi#Uzr=|gly5OB-vFe5w0|6r737qM z#P|4_zZ-It;B)QQ%|!xm;8w??Le=%FqR%SblaYX(72DUV4<7M5w23}@@nPUEX%i== zynmz>I4x1kJt6&?)CeL@Fr{i@UR1)b!*_Pv45Zwy;E25%a48F$$9}UGjdyN6t`X;G z+dBG4o>G||m2UXsd>NO!n^OetF)wVio{%f3SH-O-PeSp!p=^95uP}4GUP5a7Dkps= zFR$#Pp&-0(?Qe|X@rn!&(uBvZ23*&MIK(=f1948AY@n|}Qy(wB_9Q^Ztotsg;``dq zPyJ;6R4kG1vaIl^_mtnfj_U7NeCfr?@N=1GZJ^}#2ll*P@8V=}{v-M!{_nwoLHK%a#cnQq?PBka;M_FI z{M@0>qZn5g=fK6WE!SGke3K2jZ=#8S)LIW}U3rloz-@+v9iU$qaiWv18H&$SUeW>Y z%IN9#f>$m^rz->OOFQ=nL)f(R$&PB&ij?NZIq*q-N~StEmRXSjX zcb^8V_&w#Zx>{@gTK1Ei?m?DF6pF_9Q)|?W{7%_;*~R~rB?w0l`{TS zn1!kj6$IW`2tW*=6#k7Aga8Gu1O=CoMi{0E6f0i7;!$)x6h=@ohesNMUJ#NH&J%HY z3QwC`cscJu!yt}9AU8H4VOvi)A0wpF#C63==2GDx2z33A$*WRNP*eI_mM)2G5NrgI zQ5emC|NG0~&1RgsUz|Q17l9q&NTe4F$$%clGsAfH@8LD{AHTR!{O^DL+u|$2?SulL zR(~{=+M=mADjLU3*Y`T*vQX+(?R)j1f=THzK zP%6Nys5Kmd$`FM(6?)M($~mTC6p?x+tC-dB5DA(Y3S(%A3KZkzZ{y`>3+M7F^X7jI zuaU#WM3@%@@I`5pDFZ8g`}TkB2;=F1o*(8?vonNa*$Z8YL>n67g=;5k%ax)sE_pDf zF{ms#u)8O`@J5(>m2mZgvzz1$Az48=8A_h9yLpUs(KVzObd1|c-i+3fr-Aut$2hPD zqr!gfh}q6*5fx`%@Oc>{`4I86J=HhLx?Ms+_U_T%xQzH9it+Hcv}-O1lkNM$$S^@D z7(-^LRP$m^*MNovJ?2zAsH`_EK#ZNesCK3gWyvo;yHI2cNm8kI5(hpIY?a}9@!gl z@Kwef=aTwcTu9oC=QCQZw2pB$PdnHny zxAg5L|IL%M)=YY`9cY+&NYJgg*D6fQyTJxijMtYhp$3QFy#Y6m(tk zYA8_C+DrATNY)j?3qp^wSB}He9o*3q%CN2A54}`1P`S@lN_iQeVFkU=&^W|i?yD#M zDtJTu;CgLR(Mnq-#snV$+&i~x6b2~@)m}%Zr1HP+()&A`uYZmYu{%Z_JCBr#ip}kBctLHz zSh`T3tdsG3s-Yb?iYw017Y!QDg{KC*f8!$ILRX20Ul2NDT_5V}dO@F^j`}H)ri%vy zfjH((l7@#csxZf`tdj}CfPBoF{zsg;G-h z+CEYKlLqtL{l1;DhK6ZxWxvz!yR47E+hH&!-^R63cIP|21&@JeJY&#_?sG0g4+T8< ze=mkW??y8=x-{kh>&{S7-K?XJ$$QIFW%j?G2V+R?cyWd@vdM0T;oSLMx547)P5+oP z4DZtUD7R_0m+~-HfL(Vi%(btlrNDQ>0h%g}U1o1vhoNhf(RE0O8pON&?wS-fp01Ps zSy#IS&1Zdn_D9U%jgIOz`s2&i0ld`U?sW5-$l&RyzCuuu!-+8M9SJ$O{6)b0QeS zL=gQ=g6eNFax#rt%oWUK_)ON~^I$0X9K;r;r2z≦}<**_NRayf7>&B76=E>Qnwo z-3ti8{V+ZKFx$8KM~fMb4y&j*KDdMMH3)7T`!^Q9{qrRe|0UspzPi;P@%ODZlooaM z+gFKnyev&$ z$jIwSS^b^L#IFb)p=9ShD_6yz5ib~J(&~F9&ykND)l)1eUxS@e-EQDQCZuKBCi#JPJdQ~tTx7@LP0M)m08&|D(^J{a@m#oEd=@oUZ{aLHZdds?-nS(a^ z)xk@Ji^?D`_Pt05jr%CSmf&kbFoq5ren?MsmsMU`wW@r;SQW(k4R3%Vzjs@A%%esZ z*T0u$$|rewpk9}eLg?nmJFu9+P<8JS3N^zBpd?wMF9vK-Ij2Xa@N-=VS3PC-0Pkkj z%MxurK!H2Tg~E$-?(cLVs(1DbGnVKq`5xk>-^G4cskIZupYh~-n3qP^u6SUvR1mB| zDn7|W#eO48x<`Ti2Ha}<&0J1a2Fix=e)T7_gn>wf>a$L+0Cuv-A8Ud=RbSMf15cz6 zk+u9|!IUgszX$63>H2U!b6&sZ-{E0ri48x9N4DXAc7ZdU3qw_i+lH8D*Uw%IQ}SvW zo9s)(Po^H{y#qYq+CRm`l;;oaiO@wmfW02=t_c->diSb`_ue8e^}TX$uow_G%0Dxg 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 Q0|p@QboFyt=akR{0O<_I2LJ#7 literal 0 HcmV?d00001 diff --git a/packages/jsonld-dataset-proxy/src/ContextUtil.ts b/packages/jsonld-dataset-proxy/src/ContextUtil.ts new file mode 100644 index 0000000..887f7cf --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/ContextUtil.ts @@ -0,0 +1,90 @@ +import type { ContextDefinition, ExpandedTermDefinition } from "jsonld"; + +// Create JSONLD Shorthands +const shorthandToIriMap: Record = { + "@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; + + 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" + ); + } +} diff --git a/packages/jsonld-dataset-proxy/src/JsonldDatasetProxyBuilder.ts b/packages/jsonld-dataset-proxy/src/JsonldDatasetProxyBuilder.ts new file mode 100644 index 0000000..5c29f75 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/JsonldDatasetProxyBuilder.ts @@ -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(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( + 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( + 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(inputData: T): T { + const entryNode = inputData["@id"] + ? namedNode(inputData["@id"]) + : blankNode(); + const proxy = this.fromSubject(entryNode); + Object.entries(inputData).forEach(([key, value]) => { + proxy[key] = value; + }); + return proxy; + } +} diff --git a/packages/jsonld-dataset-proxy/src/ProxyContext.ts b/packages/jsonld-dataset-proxy/src/ProxyContext.ts new file mode 100644 index 0000000..1fa43bb --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/ProxyContext.ts @@ -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; +} + +/** + * 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 = new Map(); + private arrayMap: Map = new Map(); + + readonly dataset: Dataset; + readonly contextUtil: ContextUtil; + readonly writeGraphs: GraphType[]; + readonly languageOrdering: LanguageOrdering; + public state: Record; + + 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) { + 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); + } +} diff --git a/packages/jsonld-dataset-proxy/src/arrayProxy/ArrayProxy.ts b/packages/jsonld-dataset-proxy/src/arrayProxy/ArrayProxy.ts new file mode 100644 index 0000000..e44b2ce --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/arrayProxy/ArrayProxy.ts @@ -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 & { + readonly [_getUnderlyingDataset]: Dataset; + readonly [_getUnderlyingMatch]: ArrayProxyTarget[0]; + readonly [_getNodeAtIndex]: (index: number) => ObjectType | undefined; + readonly [_getUnderlyingArrayTarget]: ArrayProxyTarget; + [_proxyContext]: ProxyContext; +}; diff --git a/packages/jsonld-dataset-proxy/src/arrayProxy/arrayMethods.ts b/packages/jsonld-dataset-proxy/src/arrayProxy/arrayMethods.ts new file mode 100644 index 0000000..c474e15 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/arrayProxy/arrayMethods.ts @@ -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 = ( + target: ArrayProxyTarget, + key: string, + proxyContext: ProxyContext, +) => Return; + +export interface ArrayMethodBuildersType { + copyWithin: methodBuilder["copyWithin"]>; + fill: methodBuilder["fill"]>; + pop: methodBuilder["pop"]>; + push: methodBuilder["push"]>; + reverse: methodBuilder["reverse"]>; + shift: methodBuilder["shift"]>; + sort: methodBuilder["sort"]>; + splice: methodBuilder["splice"]>; + unshift: methodBuilder["unshift"]>; +} + +export const methodNames: Set = 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, + ); + }; + }, +}; diff --git a/packages/jsonld-dataset-proxy/src/arrayProxy/createArrayHandler.ts b/packages/jsonld-dataset-proxy/src/arrayProxy/createArrayHandler.ts new file mode 100644 index 0000000..375db9b --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/arrayProxy/createArrayHandler.ts @@ -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 { + 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; + }, + }; +} diff --git a/packages/jsonld-dataset-proxy/src/arrayProxy/isArrayProxy.ts b/packages/jsonld-dataset-proxy/src/arrayProxy/isArrayProxy.ts new file mode 100644 index 0000000..17ce027 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/arrayProxy/isArrayProxy.ts @@ -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" + ); +} diff --git a/packages/jsonld-dataset-proxy/src/arrayProxy/modifyArray.ts b/packages/jsonld-dataset-proxy/src/arrayProxy/modifyArray.ts new file mode 100644 index 0000000..fabdf2c --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/arrayProxy/modifyArray.ts @@ -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( + 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[]; + 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); +} diff --git a/packages/jsonld-dataset-proxy/src/graphOf.ts b/packages/jsonld-dataset-proxy/src/graphOf.ts new file mode 100644 index 0000000..97afba5 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/graphOf.ts @@ -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: Subject, + predicate: Key, + object?: NonNullable extends Array + ? 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); +} diff --git a/packages/jsonld-dataset-proxy/src/index.ts b/packages/jsonld-dataset-proxy/src/index.ts new file mode 100644 index 0000000..a226868 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/index.ts @@ -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"; diff --git a/packages/jsonld-dataset-proxy/src/jsonldDatasetProxy.ts b/packages/jsonld-dataset-proxy/src/jsonldDatasetProxy.ts new file mode 100644 index 0000000..9c49be3 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/jsonldDatasetProxy.ts @@ -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); +} diff --git a/packages/jsonld-dataset-proxy/src/language/languageMapProxy.ts b/packages/jsonld-dataset-proxy/src/language/languageMapProxy.ts new file mode 100644 index 0000000..44fa2f8 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/language/languageMapProxy.ts @@ -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, { + 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; +} diff --git a/packages/jsonld-dataset-proxy/src/language/languageSet.ts b/packages/jsonld-dataset-proxy/src/language/languageSet.ts new file mode 100644 index 0000000..8026424 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/language/languageSet.ts @@ -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 { + 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 { + 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) => 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 { + const quads = this.matchThis(); + for (const curQuad of quads) { + yield curQuad.object.value; + } + } + + *values(): IterableIterator { + const quads = this.matchThis(); + for (const curQuad of quads) { + yield curQuad.object.value; + } + } + + *[Symbol.iterator](): IterableIterator { + const quads = this.matchThis(); + for (const curQuad of quads) { + yield curQuad.object.value; + } + } + + [Symbol.toStringTag] = "LanguageSet"; +} diff --git a/packages/jsonld-dataset-proxy/src/language/languageTypes.ts b/packages/jsonld-dataset-proxy/src/language/languageTypes.ts new file mode 100644 index 0000000..0efff8e --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/language/languageTypes.ts @@ -0,0 +1,3 @@ +export type LanguageOrdering = ("@none" | "@other" | string)[]; + +export type LanguageKey = "@none" | string; diff --git a/packages/jsonld-dataset-proxy/src/language/languageUtils.ts b/packages/jsonld-dataset-proxy/src/language/languageUtils.ts new file mode 100644 index 0000000..35cb639 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/language/languageUtils.ts @@ -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 { + const literalLanguage = languageKeyToLiteralLanguage(languageKey); + return dataset.match(subject, predicate).filter((quad) => { + return ( + isLanguageLiteral(quad.object) && quad.object.language === literalLanguage + ); + }) as Dataset; +} + +/** + * + * @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> { + const languageQuadMap: Record> = {}; + quads.forEach((quad) => { + const literal = quad.object; + if (isLanguageLiteral(literal)) { + const languageKey = literalLanguageToLanguageKey(literal.language); + if (!languageQuadMap[languageKey]) { + languageQuadMap[languageKey] = + createDataset() as Dataset; + } + 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 +// ) { +// 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 = {}; +// 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; +} diff --git a/packages/jsonld-dataset-proxy/src/language/languagesOf.ts b/packages/jsonld-dataset-proxy/src/language/languagesOf.ts new file mode 100644 index 0000000..0a111b2 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/language/languagesOf.ts @@ -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; + +export type LanguageOfConditionalReturn< + SubjectObject extends ObjectLike, + Key extends keyof SubjectObject, +> = NonNullable extends Array + ? LanguageSetMap + : LanguageMap; + +/** + * ----------------------------------------------------------------------------- + * Functions + * ----------------------------------------------------------------------------- + */ + +/** + * + * @param subject + * @param predicate + * @returns + */ +export function languagesOf< + SubjectObject extends ObjectLike, + Key extends keyof SubjectObject, +>( + subjectObject: SubjectObject, + key: Key, +): LanguageOfConditionalReturn { + const proxy = getSubjectProxyFromObject(subjectObject); + const proxyContext = proxy[_proxyContext]; + const subject = proxy[_getUnderlyingNode]; + const predicate = namedNode(proxyContext.contextUtil.keyToIri(key as string)); + return createLanguageMapProxy( + subject, + predicate, + proxyContext, + proxyContext.contextUtil.isArray(key as string), + ) as LanguageOfConditionalReturn; +} diff --git a/packages/jsonld-dataset-proxy/src/setLanguagePreferences.ts b/packages/jsonld-dataset-proxy/src/setLanguagePreferences.ts new file mode 100644 index 0000000..a6541c3 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/setLanguagePreferences.ts @@ -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); +} diff --git a/packages/jsonld-dataset-proxy/src/subjectProxy/SubjectProxy.ts b/packages/jsonld-dataset-proxy/src/subjectProxy/SubjectProxy.ts new file mode 100644 index 0000000..1c22472 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/subjectProxy/SubjectProxy.ts @@ -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[]; +}; diff --git a/packages/jsonld-dataset-proxy/src/subjectProxy/createSubjectHandler.ts b/packages/jsonld-dataset-proxy/src/subjectProxy/createSubjectHandler.ts new file mode 100644 index 0000000..bbf9df0 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/subjectProxy/createSubjectHandler.ts @@ -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 { + 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 = 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); + }, + }; +} diff --git a/packages/jsonld-dataset-proxy/src/subjectProxy/deleteFromDataset.ts b/packages/jsonld-dataset-proxy/src/subjectProxy/deleteFromDataset.ts new file mode 100644 index 0000000..381c226 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/subjectProxy/deleteFromDataset.ts @@ -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; +} diff --git a/packages/jsonld-dataset-proxy/src/subjectProxy/getValueForKey.ts b/packages/jsonld-dataset-proxy/src/subjectProxy/getValueForKey.ts new file mode 100644 index 0000000..37f5141 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/subjectProxy/getValueForKey.ts @@ -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]); + } +} diff --git a/packages/jsonld-dataset-proxy/src/subjectProxy/isSubjectProxy.ts b/packages/jsonld-dataset-proxy/src/subjectProxy/isSubjectProxy.ts new file mode 100644 index 0000000..d10aac3 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/subjectProxy/isSubjectProxy.ts @@ -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; +} diff --git a/packages/jsonld-dataset-proxy/src/types.ts b/packages/jsonld-dataset-proxy/src/types.ts new file mode 100644 index 0000000..b012d5e --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/types.ts @@ -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; + +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, +]; diff --git a/packages/jsonld-dataset-proxy/src/util/NodeSet.ts b/packages/jsonld-dataset-proxy/src/util/NodeSet.ts new file mode 100644 index 0000000..271c07b --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/util/NodeSet.ts @@ -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 = new Set(); + private map: Record = {}; + + 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]; + }); + } +} diff --git a/packages/jsonld-dataset-proxy/src/util/RawObject.ts b/packages/jsonld-dataset-proxy/src/util/RawObject.ts new file mode 100644 index 0000000..2d710b4 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/util/RawObject.ts @@ -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; diff --git a/packages/jsonld-dataset-proxy/src/util/addObjectToDataset.ts b/packages/jsonld-dataset-proxy/src/util/addObjectToDataset.ts new file mode 100644 index 0000000..7252503 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/util/addObjectToDataset.ts @@ -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, + ); +} diff --git a/packages/jsonld-dataset-proxy/src/util/createInteractOptions.ts b/packages/jsonld-dataset-proxy/src/util/createInteractOptions.ts new file mode 100644 index 0000000..7821695 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/util/createInteractOptions.ts @@ -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(...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(...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; + }); + }, + }; +} diff --git a/packages/jsonld-dataset-proxy/src/util/getNodeFromRaw.ts b/packages/jsonld-dataset-proxy/src/util/getNodeFromRaw.ts new file mode 100644 index 0000000..4ea9a73 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/util/getNodeFromRaw.ts @@ -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); + } +} diff --git a/packages/jsonld-dataset-proxy/src/util/isProxy.ts b/packages/jsonld-dataset-proxy/src/util/isProxy.ts new file mode 100644 index 0000000..776cbcb --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/util/isProxy.ts @@ -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; +} diff --git a/packages/jsonld-dataset-proxy/src/util/nodeToJsonldRepresentation.ts b/packages/jsonld-dataset-proxy/src/util/nodeToJsonldRepresentation.ts new file mode 100644 index 0000000..d1dcd08 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/util/nodeToJsonldRepresentation.ts @@ -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"); + } +} diff --git a/packages/jsonld-dataset-proxy/src/write.ts b/packages/jsonld-dataset-proxy/src/write.ts new file mode 100644 index 0000000..b0d90d4 --- /dev/null +++ b/packages/jsonld-dataset-proxy/src/write.ts @@ -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); +} diff --git a/packages/jsonld-dataset-proxy/test/ContextUtil.test.ts b/packages/jsonld-dataset-proxy/test/ContextUtil.test.ts new file mode 100644 index 0000000..eafff63 --- /dev/null +++ b/packages/jsonld-dataset-proxy/test/ContextUtil.test.ts @@ -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", + ); + }); + }); +}); diff --git a/packages/jsonld-dataset-proxy/test/isProxy.test.ts b/packages/jsonld-dataset-proxy/test/isProxy.test.ts new file mode 100644 index 0000000..133a2a0 --- /dev/null +++ b/packages/jsonld-dataset-proxy/test/isProxy.test.ts @@ -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); + }); +}); diff --git a/packages/jsonld-dataset-proxy/test/jsonldDatasetProxy.test.ts b/packages/jsonld-dataset-proxy/test/jsonldDatasetProxy.test.ts new file mode 100644 index 0000000..aacb30e --- /dev/null +++ b/packages/jsonld-dataset-proxy/test/jsonldDatasetProxy.test.ts @@ -0,0 +1,1659 @@ +import { createDataset, serializedToDataset } from "o-dataset-pack"; +import type { JsonldDatasetProxyBuilder, LanguageSet } from "../src"; +import { + graphOf, + jsonldDatasetProxy, + languagesOf, + setLanguagePreferences, + write, + _getNodeAtIndex, + _getUnderlyingArrayTarget, + _getUnderlyingDataset, + _getUnderlyingMatch, + _getUnderlyingNode, + _isSubjectOriented, + _proxyContext, + _writeGraphs, +} from "../src"; +import type { ObservationShape, PatientShape } from "./patientExampleData"; +import { + patientData, + patientContext, + tinyPatientData, + tinyArrayPatientData, + patientDataWithBlankNodes, + tinyPatientDataWithBlankNodes, + tinyPatientDataWithLanguageTags, +} from "./patientExampleData"; +import { namedNode, quad, literal, defaultGraph } from "@rdfjs/data-model"; +import type { Dataset, NamedNode } from "@rdfjs/types"; +import type { ContextDefinition } from "jsonld"; + +describe("jsonldDatasetProxy", () => { + async function getLoadedDataset(): Promise< + [Dataset, ObservationShape, JsonldDatasetProxyBuilder] + > { + const dataset = await serializedToDataset(patientData); + const builder = await jsonldDatasetProxy(dataset, patientContext); + return [ + dataset, + builder.fromSubject(namedNode("http://example.com/Observation1")), + builder, + ]; + } + + async function getLoadedDatasetWithBlankNodes(): Promise< + [Dataset, ObservationShape, JsonldDatasetProxyBuilder] + > { + const dataset = await serializedToDataset(patientDataWithBlankNodes); + const builder = await jsonldDatasetProxy(dataset, patientContext); + return [ + dataset, + builder.fromSubject(namedNode("http://example.com/Observation1")), + builder, + ]; + } + + async function getTinyLoadedDataset(): Promise< + [Dataset, ObservationShape, JsonldDatasetProxyBuilder] + > { + const dataset = await serializedToDataset(tinyPatientData); + const builder = await jsonldDatasetProxy(dataset, patientContext); + return [ + dataset, + builder.fromSubject(namedNode("http://example.com/Observation1")), + builder, + ]; + } + + async function getGraphLoadedDataset(): Promise< + [Dataset, ObservationShape, JsonldDatasetProxyBuilder] + > { + const tempDataset = await serializedToDataset(patientData); + const dataset = createDataset(); + const subjectGraphMap: Record = { + "http://example.com/Observation1": namedNode( + "http://example.com/Observation1Doc", + ), + "http://example.com/Patient1": namedNode( + "http://example.com/Patient1Doc", + ), + "http://example.com/Patient2": namedNode( + "http://example.com/Patient2Doc", + ), + "http://example.com/Patient3": namedNode( + "http://example.com/Patient3Doc", + ), + }; + tempDataset.forEach((tempQuad) => { + dataset.add( + quad( + tempQuad.subject, + tempQuad.predicate, + tempQuad.object, + subjectGraphMap[tempQuad.subject.value], + ), + ); + }); + const builder = await jsonldDatasetProxy(dataset, patientContext); + return [ + dataset, + builder.fromSubject(namedNode("http://example.com/Observation1")), + builder, + ]; + } + + async function getTinyLoadedDatasetWithBlankNodes(): Promise< + [Dataset, ObservationShape, JsonldDatasetProxyBuilder] + > { + const dataset = await serializedToDataset(tinyPatientDataWithBlankNodes); + const builder = await jsonldDatasetProxy(dataset, patientContext); + return [ + dataset, + builder.fromSubject(namedNode("http://example.com/Observation1")), + builder, + ]; + } + + async function getTinyLoadedDatasetWithLanguageTags(): Promise< + [Dataset, ObservationShape, JsonldDatasetProxyBuilder] + > { + const dataset = await serializedToDataset(tinyPatientDataWithLanguageTags); + const builder = await jsonldDatasetProxy(dataset, patientContext); + return [ + dataset, + builder.fromSubject(namedNode("http://example.com/Observation1")), + builder, + ]; + } + + async function getArrayLoadedDataset(): Promise< + [Dataset, PatientShape, JsonldDatasetProxyBuilder] + > { + const dataset = await serializedToDataset(tinyArrayPatientData); + const builder = await jsonldDatasetProxy(dataset, patientContext); + return [ + dataset, + builder.fromSubject(namedNode("http://example.com/Patient1")), + builder, + ]; + } + + async function getEmptyObservationDataset(): Promise< + [Dataset, ObservationShape, JsonldDatasetProxyBuilder] + > { + const dataset = await createDataset(); + const builder = await jsonldDatasetProxy(dataset, patientContext); + return [ + dataset, + builder.fromSubject(namedNode("http://example.com/Observation1")), + builder, + ]; + } + + async function getEmptyPatientDataset(): Promise< + [Dataset, PatientShape, JsonldDatasetProxyBuilder] + > { + const dataset = await createDataset(); + const builder = await jsonldDatasetProxy(dataset, patientContext); + return [ + dataset, + builder.fromSubject(namedNode("http://example.com/Patient1")), + builder, + ]; + } + + describe("read", () => { + it("retreives a primitive", async () => { + const [, observation] = await getLoadedDataset(); + expect(observation["@id"]).toBe("http://example.com/Observation1"); + expect(observation.notes).toBe("Cool Notes"); + }); + + it("retreives a primitive with blank nodes", async () => { + const [, observation] = await getLoadedDatasetWithBlankNodes(); + expect(observation.subject?.age).toBe(35); + }); + + it("retrieves a nested primitive", async () => { + const [, observation] = await getLoadedDataset(); + expect(observation?.subject && observation.subject["@id"]).toBe( + "http://example.com/Patient1", + ); + expect(observation?.subject?.age).toBe(35); + expect(observation?.subject?.birthdate).toBe("1986-01-01"); + expect(observation?.subject?.isHappy).toBe(true); + }); + + it("retrieves a nested primitive with a blank node", async () => { + const [, observation] = await getLoadedDatasetWithBlankNodes(); + expect(observation?.subject?.roommate?.[0].age).toBe(34); + }); + + it("retreives a @type value as rdf:type", async () => { + const [, observation] = await getLoadedDataset(); + expect(observation.subject?.type?.["@id"]).toBe("Patient"); + }); + + it("simulates the getter behavior of an array of primitives", async () => { + const [, observation] = await getLoadedDataset(); + const arr = observation?.subject?.name as string[]; + expect(Array.isArray(arr)).toBe(true); + expect(arr.length).toBe(3); + expect(arr[0]).toBe("Garrett"); + expect(arr[1]).toBe("Bobby"); + expect(arr[2]).toBe("Ferguson"); + expect(arr.at(0)).toBe("Garrett"); + expect(arr.at(-1)).toBe("Ferguson"); + expect(arr.concat(["Mimoey"])).toEqual([ + "Garrett", + "Bobby", + "Ferguson", + "Mimoey", + ]); + const entriesIterator = arr.entries(); + expect(entriesIterator.next()).toEqual({ + value: [0, "Garrett"], + done: false, + }); + expect(entriesIterator.next()).toEqual({ + value: [1, "Bobby"], + done: false, + }); + expect(entriesIterator.next()).toEqual({ + value: [2, "Ferguson"], + done: false, + }); + expect(entriesIterator.next()).toEqual({ + value: undefined, + done: true, + }); + expect(arr.every((val) => val.length > 2)).toBe(true); + expect(arr.every((val) => val.length > 6)).toBe(false); + expect(arr.filter((val) => val.length > 6)).toEqual([ + "Garrett", + "Ferguson", + ]); + expect(arr.find((val) => val.length < 6)).toBe("Bobby"); + expect(arr.findIndex((val) => val.length < 6)).toBe(1); + // arr.flat (Not included because there should never be nested arrays) + let concatTest = ""; + arr.forEach((value) => (concatTest += value)); + expect(concatTest).toBe("GarrettBobbyFerguson"); + expect(arr.includes("Bobby")).toBe(true); + expect(arr.indexOf("Bobby")).toBe(1); + expect(arr.join("-")).toBe("Garrett-Bobby-Ferguson"); + const keysIterator = arr.keys(); + expect(keysIterator.next()).toEqual({ + value: 0, + done: false, + }); + expect(keysIterator.next()).toEqual({ + value: 1, + done: false, + }); + expect(keysIterator.next()).toEqual({ + value: 2, + done: false, + }); + expect(keysIterator.next()).toEqual({ + value: undefined, + done: true, + }); + expect(arr.lastIndexOf("Bobby")).toBe(1); + expect(arr.map((val) => val.toUpperCase())).toEqual([ + "GARRETT", + "BOBBY", + "FERGUSON", + ]); + expect(arr.reduce((agg, val) => agg + val, "")).toBe( + "GarrettBobbyFerguson", + ); + expect(arr.slice(2)).toEqual(["Ferguson"]); + expect(arr.some((val) => val.startsWith("G"))).toBe(true); + expect(arr.toString()).toBe("Garrett,Bobby,Ferguson"); + const valuesIterator = arr.values(); + expect(valuesIterator.next()).toEqual({ + value: "Garrett", + done: false, + }); + expect(valuesIterator.next()).toEqual({ + value: "Bobby", + done: false, + }); + expect(valuesIterator.next()).toEqual({ + value: "Ferguson", + done: false, + }); + expect(valuesIterator.next()).toEqual({ + value: undefined, + done: true, + }); + expect(JSON.stringify(arr)).toBe(`["Garrett","Bobby","Ferguson"]`); + expect(arr.toString()).toBe("Garrett,Bobby,Ferguson"); + }); + + it("can traverse a circular graph", async () => { + const [, observation] = await getLoadedDataset(); + expect(observation.subject?.roommate?.[0].roommate?.[0]?.name?.[0]).toBe( + "Garrett", + ); + }); + + it("simulates getter object properties", async () => { + const [, observation] = await getLoadedDataset(); + const obj = observation.subject as PatientShape; + + expect(obj["@id"]).toEqual("http://example.com/Patient1"); + expect(obj.type).toEqual({ "@id": "Patient" }); + expect(obj.name).toEqual(["Garrett", "Bobby", "Ferguson"]); + expect(obj.birthdate).toEqual("1986-01-01"); + expect(obj.age).toEqual(35); + expect(obj.isHappy).toEqual(true); + const entries = Object.entries(obj); + expect(entries[0]).toEqual(["@id", "http://example.com/Patient1"]); + expect(entries[1]).toEqual(["type", { "@id": "Patient" }]); + expect(entries[2]).toEqual(["name", ["Garrett", "Bobby", "Ferguson"]]); + expect(entries[3]).toEqual(["birthdate", "1986-01-01"]); + expect(entries[4]).toEqual(["age", 35]); + expect(entries[5]).toEqual(["isHappy", true]); + expect(entries[6][0]).toEqual("roommate"); + expect(Object.keys(obj)).toEqual([ + "@id", + "type", + "name", + "birthdate", + "age", + "isHappy", + "roommate", + ]); + const values = Object.values(obj); + expect(values[0]).toEqual("http://example.com/Patient1"); + expect(values[1]).toEqual({ "@id": "Patient" }); + expect(values[2]).toEqual(["Garrett", "Bobby", "Ferguson"]); + expect(values[3]).toEqual("1986-01-01"); + expect(values[4]).toEqual(35); + expect(values[5]).toEqual(true); + }); + + it("handles stringification of a non circular object", async () => { + const [, observation] = await getLoadedDataset(); + const obj = observation.subject?.roommate?.[1] as PatientShape; + expect(obj.toString()).toBe("[object Object]"); + expect(JSON.stringify(obj)).toBe( + `{"@id":"http://example.com/Patient3","type":{"@id":"Patient"},"name":["Amy"],"birthdate":"1988-01-01","age":33,"isHappy":true}`, + ); + }); + + it("Returns an array for required array fields even if no data is in the dataset", async () => { + const [, observation] = await getLoadedDataset(); + const obj = observation.subject?.roommate?.[1] as PatientShape; + expect(obj.roommate).toEqual([]); + }); + + it("updates when the dataset is updated", async () => { + const [dataset, observation] = await getLoadedDataset(); + expect(observation.notes).toBe("Cool Notes"); + dataset.delete( + quad( + namedNode("http://example.com/Observation1"), + namedNode("http://hl7.org/fhir/notes"), + literal("Cool Notes", "http://www.w3.org/2001/XMLSchema#string"), + ), + ); + dataset.add( + quad( + namedNode("http://example.com/Observation1"), + namedNode("http://hl7.org/fhir/notes"), + literal("Bad Notes", "http://www.w3.org/2001/XMLSchema#string"), + ), + ); + expect(observation.notes).toBe("Bad Notes"); + }); + + it("handles stringfication of a circular object", async () => { + const [, observation] = await getLoadedDataset(); + const obj = observation.subject as PatientShape; + expect(obj.toString()).toBe("[object Object]"); + + expect(() => JSON.stringify(obj)).toThrow( + "Converting circular structure to JSON", + ); + }); + + it("returns undefined if called with an unrecognized symbol", async () => { + const [, observation] = await getLoadedDataset(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + expect(observation[Symbol.toPrimitive]).toBe(undefined); + }); + + it("returns an array object if multiple triples exist, even if @container is not @set", async () => { + const dataset = await serializedToDataset(patientData); + const fakePatientSContext: ContextDefinition = { + name: { + "@id": "http://hl7.org/fhir/name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + }; + const builder = jsonldDatasetProxy(dataset, fakePatientSContext); + const patient = builder.fromSubject( + namedNode("http://example.com/Patient1"), + ); + expect(patient.name).toEqual(["Garrett", "Bobby", "Ferguson"]); + }); + + it("returns context when the @context key is called", async () => { + const [, observation] = await getLoadedDataset(); + expect(observation["@context"]).toEqual(patientContext); + }); + }); + + describe("write", () => { + it("sets a primitive value that doesn't exist yet", async () => { + const [dataset, observation] = await getEmptyObservationDataset(); + observation.notes = "Cool Notes"; + expect(dataset.toString()).toBe( + ' "Cool Notes" .\n', + ); + }); + + it("sets primitive number and boolean values", async () => { + const [dataset, patient] = await getEmptyPatientDataset(); + patient.age = 35; + patient.isHappy = true; + expect(dataset.toString()).toBe( + ' "35"^^ .\n "true"^^ .\n', + ); + }); + + it("sets a @type value as rdf:type", async () => { + const [dataset, patient] = await getEmptyPatientDataset(); + patient.type = { "@id": "Patient" }; + expect(dataset.toString()).toBe( + " .\n", + ); + }); + + it("replaces a primitive value that currently exists", async () => { + const [dataset, observation] = await getEmptyObservationDataset(); + dataset.add( + quad( + namedNode("http://example.com/Observation1"), + namedNode("http://hl7.org/fhir/notes"), + literal("Cool Notes"), + ), + ); + observation.notes = "Lame Notes"; + expect(dataset.toString()).toBe( + ' "Lame Notes" .\n', + ); + }); + + it("adds all quads from a set object", async () => { + const [dataset, observation] = await getEmptyObservationDataset(); + const patient: PatientShape = { + "@id": "http://example.com/Patient1", + birthdate: "2001-01-01", + }; + observation.subject = patient; + expect(dataset.toString()).toBe( + ' .\n "2001-01-01"^^ .\n', + ); + }); + + it("sets a retrieved blank node object", async () => { + const [, observation] = await getTinyLoadedDatasetWithBlankNodes(); + const patient2 = observation.subject?.roommate?.[0] as PatientShape; + observation.subject = patient2; + expect(observation.subject.name).toEqual(["Rob"]); + expect(observation.subject.roommate?.[0]?.name).toEqual(["Garrett"]); + expect(observation.subject.roommate?.[0]?.roommate?.[0].name).toEqual([ + "Rob", + ]); + }); + + it("only removes the connection when a value is set to undefined", async () => { + const [dataset, observation] = await getTinyLoadedDataset(); + observation.subject = undefined; + expect(dataset.toString()).toBe( + ' "Garrett" .\n .\n "Rob" .\n .\n', + ); + }); + + it("Creates a blank node if the id is blank during set", async () => { + const [dataset, observation] = await getEmptyObservationDataset(); + observation.subject = { name: ["Joe"] }; + expect(observation.subject?.["@id"]).toBeUndefined(); + expect(observation.subject.name).toEqual(["Joe"]); + expect( + dataset + .match( + namedNode("http://example.com/Observation1"), + namedNode("http://hl7.org/fhir/subject"), + ) + .toArray()[0].object.termType, + ).toBe("BlankNode"); + }); + + it("adds all quads from a set object that includes an array", async () => { + const [dataset, observation] = await getEmptyObservationDataset(); + const patient: PatientShape = { + "@id": "http://example.com/Patient1", + birthdate: "2001-01-01", + name: ["Jon", "Bon", "Jovi"], + }; + observation.subject = patient; + expect(dataset.toString()).toBe( + ' .\n "2001-01-01"^^ .\n "Jon" .\n "Bon" .\n "Jovi" .\n', + ); + }); + + it("does not infinitely recurse if there is a loop when setting an object", async () => { + const [dataset, observation] = await getEmptyObservationDataset(); + const patient1: PatientShape = { + "@id": "http://example.com/Patient1", + name: ["jon"], + }; + const patient2: PatientShape = { + "@id": "http://example.com/patient2", + name: ["jane"], + roommate: [patient1], + }; + patient1.roommate = [patient2]; + observation.subject = patient1; + expect(dataset.toString()).toBe( + ' .\n "jon" .\n .\n "jane" .\n .\n', + ); + }); + + it("adds a proxy object to the array", async () => { + const [, , builder] = await getTinyLoadedDataset(); + const patient3 = builder.fromSubject( + namedNode("http://example.com/Patient3"), + ); + const patient1 = builder.fromSubject( + namedNode("http://example.com/Patient1"), + ); + patient3.roommate.push(patient1); + }); + + it("sets a primitive on an array", async () => { + const [dataset, patient] = await getEmptyPatientDataset(); + (patient.name as string[])[0] = "jon"; + expect(dataset.toString()).toBe( + ' "jon" .\n', + ); + }); + + it("sets a primitive on an array and overwrites one that already is there", async () => { + const [dataset, patient] = await getEmptyPatientDataset(); + dataset.add( + quad( + namedNode("http://example.com/Patient1"), + namedNode("http://hl7.org/fhir/name"), + literal("jon", "http://www.w3.org/2001/XMLSchema#string"), + ), + ); + (patient.name as string[])[0] = "not jon"; + expect(dataset.toString()).toBe( + ' "not jon" .\n', + ); + }); + + it("sets an array", async () => { + const [dataset, patient] = await getEmptyPatientDataset(); + patient.name = ["Joe", "Mama"]; + expect(dataset.toString()).toBe( + ' "Joe" .\n "Mama" .\n', + ); + }); + + it("Does not remove the full object when it is replaced on an object", async () => { + const [dataset, observation] = await getTinyLoadedDataset(); + const replacementPatient: PatientShape = { + "@id": "http://example.com/ReplacementPatient", + name: ["Jackson"], + }; + observation.subject = replacementPatient; + expect(dataset.toString()).toBe( + ' .\n "Garrett" .\n .\n "Rob" .\n .\n "Jackson" .\n', + ); + }); + + it("Does not remove the full object when it is replaced on an array", async () => { + const [dataset, observation] = await getTinyLoadedDataset(); + const replacementPatient: PatientShape = { + "@id": "http://example.com/ReplacementPatient", + name: ["Jackson"], + }; + const roommateArr = observation?.subject?.roommate as PatientShape[]; + roommateArr[0] = replacementPatient; + expect(dataset.toString()).toBe( + ' .\n "Garrett" .\n .\n "Rob" .\n .\n "Jackson" .\n', + ); + }); + + it("Keeps the correct array index when setting an index", async () => { + const [, observation] = await getLoadedDataset(); + const roommateArr = observation.subject?.roommate as PatientShape[]; + roommateArr[0] = { + "@id": "http://example.com/ReplacementPatient", + name: ["Jackson"], + }; + expect(roommateArr.length).toBe(2); + expect(roommateArr[0].name?.[0]).toBe("Jackson"); + }); + + it("Changes the subject name if the @id is changed", async () => { + const [dataset, observation] = await getTinyLoadedDataset(); + const patient = observation?.subject as PatientShape; + patient["@id"] = "http://example.com/RenamedPatient"; + expect(patient["@id"]).toBe("http://example.com/RenamedPatient"); + expect(dataset.toString()).toBe( + ' .\n "Rob" .\n .\n "Garrett" .\n .\n', + ); + }); + + it("Removes all adjoining triples when garbage collection is indicated via the delete operator on an object", async () => { + const [dataset, observation] = await getTinyLoadedDataset(); + delete observation.subject; + expect(dataset.toString()).toBe( + ' "Rob" .\n', + ); + }); + + it("Removes all adjoining triples in an array when garbage collection is indicated via the delete operator on an object", async () => { + const [dataset, observation] = await getTinyLoadedDataset(); + delete observation.subject?.name; + expect(dataset.toString()).toBe( + ' .\n .\n "Rob" .\n .\n', + ); + }); + + it("Removes all adjoining triples when garbage collection is indicated via the delete operator on an array", async () => { + const [dataset, observation] = await getTinyLoadedDataset(); + delete observation.subject?.roommate?.[0]; + expect(dataset.toString()).toBe( + ' .\n "Garrett" .\n', + ); + }); + + it("Removes all adjoining triples when garbage collection is indicated via the delete operator on an array with blank nodes", async () => { + const [dataset, observation] = await getTinyLoadedDatasetWithBlankNodes(); + delete observation.subject?.roommate?.[0]; + expect(dataset.toString()).toBe( + ' _:b25_Patient1 .\n_:b25_Patient1 "Garrett" .\n', + ); + }); + + it("Removes a literal in an array when using the delete operator", async () => { + const [dataset, observation] = await getTinyLoadedDataset(); + delete observation.subject?.name?.[0]; + expect(dataset.toString()).toBe( + ' .\n .\n "Rob" .\n .\n', + ); + }); + + it("Deletes itself if @id is deleted", async () => { + const [dataset, observation] = await getTinyLoadedDataset(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + delete observation["@id"]; + expect(observation).toEqual({ "@id": "http://example.com/Observation1" }); + expect(dataset.toString()).toBe( + ' "Garrett" .\n .\n "Rob" .\n .\n', + ); + }); + + it("Does nothing when deleting triples that don't exist", async () => { + const [dataset, observation] = await getEmptyObservationDataset(); + delete observation.subject; + expect(dataset.toString()).toBe(""); + }); + + it("Does nothing when deleting context", async () => { + const [, observation] = await getTinyLoadedDataset(); + delete observation["@context"]; + expect(observation["@context"]).toEqual(patientContext); + }); + + it("Does nothing when deleting toString", async () => { + const [, observation] = await getTinyLoadedDataset(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + delete observation.toString; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + delete observation[Symbol.toStringTag]; + expect(typeof observation.toString).toBe("function"); + }); + + it("Does nothing when deleting any symbol", async () => { + const [, observation] = await getTinyLoadedDataset(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + delete observation[Symbol.search]; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + expect(observation[Symbol.search]).toBe(undefined); + }); + + it("Removes old triples from a node that has the same id as the one it replaced", async () => { + const [dataset, observation] = await getTinyLoadedDataset(); + const replacementPatient: PatientShape = { + "@id": "http://example.com/Patient1", + name: ["Mister Sneaky"], + }; + observation.subject = replacementPatient; + expect(dataset.toString()).toBe( + ' .\n "Mister Sneaky" .\n "Rob" .\n .\n', + ); + }); + + it("handles Object.assign", async () => { + const [dataset, observation] = await getTinyLoadedDataset(); + Object.assign(observation, { + age: 35, + isHappy: true, + }); + expect(dataset.toString()).toBe( + ' .\n "35"^^ .\n "true"^^ .\n "Garrett" .\n .\n "Rob" .\n .\n', + ); + }); + + it("Adds elements to the array even if they were modified by the datastore", async () => { + const [dataset, patient] = await getEmptyPatientDataset(); + patient.name = ["Joe", "Blow"]; + dataset.add( + quad( + namedNode("http://example.com/Patient1"), + namedNode("http://hl7.org/fhir/name"), + literal("Tow"), + ), + ); + expect(patient.name).toEqual(["Joe", "Blow", "Tow"]); + }); + + it("Removes elements from the array even if they were modified by the datastore", async () => { + const [dataset, patient] = await getEmptyPatientDataset(); + patient.name = ["Joe", "Blow"]; + dataset.delete( + quad( + namedNode("http://example.com/Patient1"), + namedNode("http://hl7.org/fhir/name"), + literal("Blow"), + ), + ); + expect(patient.name).toEqual(["Joe"]); + }); + + it("Removes and adds from the array even if they were modified by the datastore", async () => { + const [dataset, patient] = await getEmptyPatientDataset(); + patient.name = ["Joe", "Blow"]; + dataset.delete( + quad( + namedNode("http://example.com/Patient1"), + namedNode("http://hl7.org/fhir/name"), + literal("Blow"), + ), + ); + dataset.add( + quad( + namedNode("http://example.com/Patient1"), + namedNode("http://hl7.org/fhir/name"), + literal("Tow"), + ), + ); + expect(patient.name).toEqual(["Joe", "Tow"]); + }); + + it("Prevents duplicates from being added to the array", async () => { + const [, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + arr[3] = "Garrett"; + expect(arr).toEqual(["Garrett", "Bobby", "Ferguson"]); + }); + + it("Prevents duplicates from being added when a value is overwritten", async () => { + const [, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + arr[1] = "Garrett"; + expect(arr).toEqual(["Garrett", "Ferguson"]); + }); + + it("Prevents duplicates for Objects", async () => { + const [, observation] = await getLoadedDataset(); + const roommates = observation.subject?.roommate as PatientShape[]; + roommates[0] = { "@id": "http://example.com/Patient3" }; + expect(roommates.length).toBe(1); + expect(roommates[0].name?.[0]).toBe("Amy"); + }); + + it("Does nothing when you try to set a symbol on an array", async () => { + const [, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + expect(() => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + arr[Symbol.search] = "Cool"; + }).not.toThrowError(); + }); + + it("Does nothing when you try to delete a symbol on an array", async () => { + const [, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + expect(() => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + delete arr[Symbol.search]; + }).not.toThrowError(); + }); + + it("Does nothing when you try to delete an index of the array that doesn't exist", async () => { + const [dataset, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + delete arr[5]; + expect(arr).toEqual(["Garrett", "Bobby", "Ferguson"]); + expect(dataset.toString()).toEqual( + ' "Garrett" .\n "Bobby" .\n "Ferguson" .\n', + ); + }); + + it("Can set a triple object named node with just a string", async () => { + const [dataset, observation] = await getEmptyObservationDataset(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + observation.subject = "http://example.com/Patient1"; + expect(observation.subject).toEqual({ + "@id": "http://example.com/Patient1", + }); + expect(dataset.toString()).toBe( + " .\n", + ); + }); + + describe("Array Methods", () => { + it("handles copyWithin", async () => { + const [dataset, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + arr.copyWithin(0, 2, 3); + expect(arr).toEqual(["Ferguson", "Bobby"]); + expect(dataset.toString()).toEqual( + ' "Bobby" .\n "Ferguson" .\n', + ); + }); + + it("handles copyWithin with the optional end variable missing", async () => { + const [dataset, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + arr.copyWithin(0, 2); + expect(arr).toEqual(["Ferguson", "Bobby"]); + expect(dataset.toString()).toEqual( + ' "Bobby" .\n "Ferguson" .\n', + ); + }); + + it("handles fill", async () => { + const [dataset, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + arr.fill("Beepy", 2, 5); + expect(arr).toEqual(["Garrett", "Bobby", "Beepy"]); + expect(dataset.toString()).toEqual( + ' "Garrett" .\n "Bobby" .\n "Beepy" .\n', + ); + }); + + it("handles pop", async () => { + const [dataset, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + expect(arr.pop()).toBe("Ferguson"); + expect(arr).toEqual(["Garrett", "Bobby"]); + expect(dataset.toString()).toEqual( + ' "Garrett" .\n "Bobby" .\n', + ); + }); + + it("returns undefined for pop on an empty collection", async () => { + const [, patient] = await getArrayLoadedDataset(); + patient.name = []; + expect(patient.name.pop()).toBe(undefined); + }); + + it("handles push", async () => { + const [dataset, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + arr.push("Beepy"); + expect(arr).toEqual(["Garrett", "Bobby", "Ferguson", "Beepy"]); + expect(dataset.toString()).toEqual( + ' "Garrett" .\n "Bobby" .\n "Ferguson" .\n "Beepy" .\n', + ); + }); + + it("handles reverse", async () => { + const [dataset, patient] = await getArrayLoadedDataset(); + patient.name?.reverse(); + expect(patient.name).toEqual(["Ferguson", "Bobby", "Garrett"]); + expect(dataset.toString()).toBe( + ' "Garrett" .\n "Bobby" .\n "Ferguson" .\n', + ); + }); + + it("handles shift", async () => { + const [dataset, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + expect(arr.shift()).toEqual("Garrett"); + expect(arr).toEqual(["Bobby", "Ferguson"]); + expect(dataset.toString()).toEqual( + ' "Bobby" .\n "Ferguson" .\n', + ); + }); + + it("returns undefined for shift on an empty collection", async () => { + const [, patient] = await getArrayLoadedDataset(); + patient.name = []; + expect(patient.name.shift()).toBe(undefined); + }); + + it("handles sort", async () => { + const [dataset, patient] = await getArrayLoadedDataset(); + patient.name?.sort((a, b) => { + return a.length - b.length; + }); + expect(patient.name).toEqual(["Bobby", "Garrett", "Ferguson"]); + expect(dataset.toString()).toBe( + ' "Garrett" .\n "Bobby" .\n "Ferguson" .\n', + ); + }); + + it("handles sort without a sort function", async () => { + const [, patient] = await getArrayLoadedDataset(); + patient.name?.sort(); + expect(patient.name).toEqual(["Bobby", "Ferguson", "Garrett"]); + }); + + it("handles sort without a sort function and there are two equal values", async () => { + const [dataset, patient] = await getArrayLoadedDataset(); + dataset.add( + quad( + namedNode("http://example.com/Patient1"), + namedNode("http://hl7.org/fhir/name"), + literal( + "Bobby", + namedNode("http://www.w3.org/2001/XMLSchema#token"), + ), + ), + ); + patient.name?.sort(); + expect(patient.name).toEqual(["Bobby", "Bobby", "Ferguson", "Garrett"]); + }); + + it("handles splice", async () => { + const [dataset, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + arr.splice(1, 0, "Beepy"); + expect(arr).toEqual(["Garrett", "Beepy", "Bobby", "Ferguson"]); + expect(dataset.toString()).toEqual( + ' "Garrett" .\n "Bobby" .\n "Ferguson" .\n "Beepy" .\n', + ); + }); + + it("handles splice with objects", async () => { + const [, observation] = await getLoadedDataset(); + const roommates = observation.subject?.roommate as PatientShape[]; + roommates.splice( + 0, + 1, + { + "@id": "http://example.com/Patient4", + type: { "@id": "Patient" }, + name: ["Dippy"], + age: 2, + }, + { + "@id": "http://example.com/Patient5", + type: { "@id": "Patient" }, + name: ["Licky"], + age: 3, + }, + ); + expect(roommates[0].name?.[0]).toBe("Dippy"); + expect(roommates[1].name?.[0]).toBe("Licky"); + expect(roommates[2].name?.[0]).toBe("Amy"); + }); + + it("handles splice with only two params", async () => { + const [dataset, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + arr.splice(1, 1); + expect(arr).toEqual(["Garrett", "Ferguson"]); + expect(dataset.toString()).toEqual( + ' "Garrett" .\n "Ferguson" .\n', + ); + }); + + it("handles unshift", async () => { + const [dataset, patient] = await getArrayLoadedDataset(); + const arr = patient.name as string[]; + arr.unshift("Beepy"); + expect(arr).toEqual(["Beepy", "Garrett", "Bobby", "Ferguson"]); + expect(dataset.toString()).toEqual( + ' "Garrett" .\n "Bobby" .\n "Ferguson" .\n "Beepy" .\n', + ); + }); + }); + }); + + describe("underlying data", () => { + it("retrieves underlying data", async () => { + const dataset = await serializedToDataset(patientData); + const entryNode = namedNode("http://example.com/Observation1"); + const context = patientContext; + const builder = jsonldDatasetProxy(dataset, context); + const observation = builder.fromSubject(entryNode); + expect(observation[_getUnderlyingDataset]).toBe(dataset); + expect(observation[_getUnderlyingNode].value).toBe( + "http://example.com/Observation1", + ); + expect(observation[_writeGraphs][0].termType).toBe("DefaultGraph"); + expect(observation[_proxyContext].writeGraphs[0].termType).toBe( + "DefaultGraph", + ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const roommateArr = observation.subject!.roommate!; + expect(roommateArr[_getUnderlyingDataset]).toBe(dataset); + expect(roommateArr[_isSubjectOriented]).toBe(false); + const match = roommateArr[_getUnderlyingMatch]; + expect(match[0].value).toBe("http://example.com/Patient1"); + expect(match[1].value).toBe("http://hl7.org/fhir/roommate"); + expect(roommateArr[_getNodeAtIndex](0).value).toBe( + "http://example.com/Patient2", + ); + expect(roommateArr[_getNodeAtIndex](10)).toBe(undefined); + expect(observation.subject.name[_getNodeAtIndex](0).value).toBe( + "Garrett", + ); + const underlyingArrayTarget = roommateArr[_getUnderlyingArrayTarget]; + expect(underlyingArrayTarget[1][0].value).toBe( + "http://example.com/Patient2", + ); + }); + }); + + describe("matchSubject", () => { + let patients: PatientShape[]; + let dataset: Dataset; + + beforeEach(async () => { + const [receivedDataset, , builder] = await getLoadedDataset(); + dataset = receivedDataset; + patients = builder.matchSubject( + namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), + namedNode("http://hl7.org/fhir/Patient"), + ); + }); + + it("creates a list of subjects that match a certain pattern", async () => { + expect(patients[0].name?.[0]).toBe("Garrett"); + expect(patients[1].name?.[0]).toBe("Rob"); + expect(patients[2].name?.[0]).toBe("Amy"); + }); + + it("Successfully adds a node to the list", async () => { + patients.push({ + "@id": "http://example.com/Patient4", + type: { "@id": "Patient" }, + name: ["Dippy"], + age: 2, + }); + expect( + dataset + .match( + null, + namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), + namedNode("http://hl7.org/fhir/Patient"), + ) + .some((quad) => { + return quad.subject.value === "http://example.com/Patient4"; + }), + ).toBe(true); + expect(patients[3].name?.[0]).toBe("Dippy"); + }); + + it("will read a new object if something has been added to the dataset after object creation", async () => { + dataset.add( + quad( + namedNode("http://example.com/Patient4"), + namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), + namedNode("http://hl7.org/fhir/Patient"), + ), + ); + dataset.add( + quad( + namedNode("http://example.com/Patient4"), + namedNode("http://hl7.org/fhir/name"), + literal("Dippy"), + ), + ); + + expect( + dataset + .match( + null, + namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), + namedNode("http://hl7.org/fhir/Patient"), + ) + .some((quad) => { + return quad.subject.value === "http://example.com/Patient4"; + }), + ).toBe(true); + expect(patients[3].name?.[0]).toBe("Dippy"); + }); + + it("errors if an object is added without the correct parameters", async () => { + expect(() => + patients.push({ + "@id": "http://example.com/Patient4", + name: ["Dippy"], + age: 2, + }), + ).toThrowError( + `Cannot add value to collection. This must contain a quad that matches (null, namedNode(http://www.w3.org/1999/02/22-rdf-syntax-ns#type), namedNode(http://hl7.org/fhir/Patient), null)`, + ); + }); + + it("errors if a literal is added to the collection", async () => { + // @ts-expect-error Purposely pushing an incorrect value to trigger an error + expect(() => patients.push("some string")).toThrowError( + `Cannot add a literal "some string"(string) to a subject-oriented collection.`, + ); + }); + + it("Removes all an object and replaces in upon set", async () => { + patients[0] = { + "@id": "http://example.com/Patient4", + type: { "@id": "Patient" }, + name: ["Dippy"], + age: 2, + }; + + expect(dataset.match(namedNode("http://example.com/Patient1")).size).toBe( + 0, + ); + expect(patients[0].name?.[0]).toBe("Dippy"); + }); + + it("Removes an object and replaces it upon splice", async () => { + patients.splice( + 1, + 1, + { + "@id": "http://example.com/Patient4", + type: { "@id": "Patient" }, + name: ["Dippy"], + age: 2, + }, + { + "@id": "http://example.com/Patient5", + type: { "@id": "Patient" }, + name: ["Licky"], + age: 3, + }, + ); + + expect(dataset.match(namedNode("http://example.com/Patient2")).size).toBe( + 0, + ); + expect(patients[1].name?.[0]).toBe("Dippy"); + expect(patients[2].name?.[0]).toBe("Licky"); + }); + + it("Removes an object completely when assigning it to undefined", async () => { + // @ts-expect-error This violates the typings + patients[0] = undefined; + + expect(dataset.match(namedNode("http://example.com/Patient1")).size).toBe( + 0, + ); + expect(patients[0].name?.[0]).toBe("Rob"); + }); + + it("Removes an object completely when using the delete parameter", async () => { + delete patients[0]; + + expect(dataset.match(namedNode("http://example.com/Patient1")).size).toBe( + 0, + ); + expect(patients[0].name?.[0]).toBe("Rob"); + }); + + it("creates a collection that matches only collections in a certain graph", async () => { + const [, , builder] = await getGraphLoadedDataset(); + patients = builder.matchSubject( + namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), + namedNode("http://hl7.org/fhir/Patient"), + namedNode("http://example.com/Patient1Doc"), + ); + expect(patients.length).toBe(1); + expect(patients[0]["@id"]).toBe("http://example.com/Patient1"); + }); + }); + + describe("matchObject", () => { + let patients: PatientShape[]; + let builder: JsonldDatasetProxyBuilder; + + beforeEach(async () => { + const [, , receivedBuilder] = await getLoadedDataset(); + builder = receivedBuilder; + patients = builder.matchObject( + null, + namedNode("http://hl7.org/fhir/roommate"), + null, + ); + }); + + it("create a collection that matches the null, predicate, null pattern", async () => { + expect(patients[0].name?.[0]).toBe("Garrett"); + expect(patients[1].name?.[0]).toBe("Amy"); + expect(patients[2].name?.[0]).toBe("Rob"); + }); + + it("cannot write to a collection that matches the null, predicate, null pattern", () => { + expect( + () => (patients[1] = { "@id": "http://example.com/Patient4" }), + ).toThrow( + "A collection that does not specify a match for both a subject or predicate cannot be modified directly.", + ); + }); + + it("creates a collection that matches the subject, null, null pattern", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const hodgePodge = builder.matchObject( + namedNode("http://example.com/Patient3"), + null, + null, + ); + expect(hodgePodge.length).toBe(5); + expect(hodgePodge[0]["@id"]).toBe("Patient"); + expect(hodgePodge[1]).toBe("Amy"); + expect(hodgePodge[2]).toBe("1988-01-01"); + expect(hodgePodge[3]).toBe(33); + expect(hodgePodge[4]).toBe(true); + }); + }); + + describe("fromJson", () => { + it("initializes a patient using the fromJSON method", async () => { + const [, , builder] = await getEmptyPatientDataset(); + const patient = builder.fromJson({ + name: ["Jack", "Horner"], + birthdate: "1725/11/03", + age: 298, + roommate: [ + { + name: ["Ethical", "Bug"], + }, + ], + }); + expect(patient.name?.[0]).toBe("Jack"); + expect(patient.name?.[1]).toBe("Horner"); + expect(patient.birthdate).toBe("1725/11/03"); + expect(patient.age).toBe(298); + expect(patient.roommate?.[0].name?.[0]).toBe("Ethical"); + expect(patient.roommate?.[0].name?.[1]).toBe("Bug"); + }); + + it("initializes a patient using the fromJSON method with a named node", async () => { + const [, , builder] = await getEmptyPatientDataset(); + const patient = builder.fromJson({ + "@id": "http://example.com/Patient13", + name: ["Jack", "Horner"], + birthdate: "1725/11/03", + age: 298, + roommate: [ + { + name: ["Ethical", "Bug"], + }, + ], + }); + expect(patient["@id"]).toBe("http://example.com/Patient13"); + expect(patient.name?.[0]).toBe("Jack"); + expect(patient.name?.[1]).toBe("Horner"); + expect(patient.birthdate).toBe("1725/11/03"); + expect(patient.age).toBe(298); + expect(patient.roommate?.[0].name?.[0]).toBe("Ethical"); + expect(patient.roommate?.[0].name?.[1]).toBe("Bug"); + }); + }); + + describe("Graph Methods", () => { + describe("builder", () => { + it("sets write graph", async () => { + const [dataset, , builder] = await getEmptyObservationDataset(); + const patient4 = builder + .write(namedNode("http://example.com/Patient4Doc")) + .fromSubject(namedNode("https://example.com/Patient4")); + patient4.name = ["Jackson"]; + expect(dataset.toString()).toBe( + ' "Jackson" .\n', + ); + }); + }); + + describe("graphOf", () => { + it("detects the graph of a single value", async () => { + const [, observation] = await getGraphLoadedDataset(); + expect(graphOf(observation, "subject")[0].value).toBe( + "http://example.com/Observation1Doc", + ); + expect( + graphOf(observation, "subject", observation.subject)[0].value, + ).toBe("http://example.com/Observation1Doc"); + expect( + graphOf(observation.subject as PatientShape, "age")[0].value, + ).toBe("http://example.com/Patient1Doc"); + }); + + it("detects the graph of an array value", async () => { + const [, observation] = await getGraphLoadedDataset(); + const patient1 = observation.subject as PatientShape; + expect(graphOf(patient1, "name", 0)[0].value).toBe( + "http://example.com/Patient1Doc", + ); + expect(graphOf(patient1, "roommate", 0)[0].value).toBe( + "http://example.com/Patient1Doc", + ); + expect( + graphOf(patient1, "roommate", patient1.roommate?.[1])[0].value, + ).toBe("http://example.com/Patient1Doc"); + }); + + it("detects the graph of a value in multiple graphs", async () => { + const [dataset, observation] = await getGraphLoadedDataset(); + dataset.add( + quad( + namedNode("http://example.com/Observation1"), + namedNode("http://hl7.org/fhir/subject"), + namedNode("http://example.com/Patient1"), + namedNode("http://example.com/SomeOtherDoc"), + ), + ); + expect(graphOf(observation, "subject")[0].value).toBe( + "http://example.com/Observation1Doc", + ); + expect(graphOf(observation, "subject")[1].value).toBe( + "http://example.com/SomeOtherDoc", + ); + }); + + it("throws an error if a number is provided as an object and the object is not an array", async () => { + const [, observation] = await getGraphLoadedDataset(); + // @ts-expect-error this should not be allowed + expect(() => graphOf(observation, "subject", 0)).toThrowError( + `Key "subject" of [object Object] is not an array.`, + ); + }); + + it("throws an error if the index is out of bounds", async () => { + const [, observation] = await getGraphLoadedDataset(); + expect(() => + graphOf(observation.subject as PatientShape, "name", 10), + ).toThrowError(`Index 10 does not exist.`); + }); + }); + + describe("write method", () => { + it("changes the write graph", async () => { + const [, observation] = await getGraphLoadedDataset(); + write(namedNode("http://example.com/SomeOtherDoc")).using(observation); + observation.notes = "Cool Notes"; + expect(graphOf(observation, "notes")[0].value).toBe( + "http://example.com/SomeOtherDoc", + ); + }); + + it("allows the write graph to be reset", async () => { + const doc1 = namedNode("http://example.com/Doc1"); + const doc2 = namedNode("http://example.com/Doc2"); + const doc3 = namedNode("http://example.com/Doc3"); + + const [, patient] = await getEmptyPatientDataset(); + patient.name?.push("default"); + const end1 = write(doc1).using(patient); + patient.name?.push("1"); + const end2 = write(doc2).using(patient); + patient.name?.push("2"); + const end3 = write(doc3).using(patient); + patient.name?.push("3"); + end3(); + patient.name?.push("2 again"); + end2(); + patient.name?.push("1 again"); + end1(); + patient.name?.push("default again"); + + expect(graphOf(patient, "name", 0)[0].value).toBe(defaultGraph().value); + expect(graphOf(patient, "name", 1)[0].value).toBe(doc1.value); + expect(graphOf(patient, "name", 2)[0].value).toBe(doc2.value); + expect(graphOf(patient, "name", 3)[0].value).toBe(doc3.value); + expect(graphOf(patient, "name", 4)[0].value).toBe(doc2.value); + expect(graphOf(patient, "name", 5)[0].value).toBe(doc1.value); + expect(graphOf(patient, "name", 6)[0].value).toBe(defaultGraph().value); + }); + + it("copies the proxy and changes the write graphs without modifying the original", async () => { + const doc1 = namedNode("http://example.com/Doc1"); + + const [, patient] = await getEmptyPatientDataset(); + patient.name?.push("Default"); + const [patientOnDoc1] = write(doc1).usingCopy(patient); + patientOnDoc1.name?.push("Doc1"); + expect(graphOf(patient, "name", 0)[0].value).toBe(defaultGraph().value); + expect(graphOf(patient, "name", 1)[0].value).toBe(doc1.value); + }); + + it("works with array proxies", async () => { + const [, , builder] = await getTinyLoadedDataset(); + const allRoommates = builder.matchObject( + namedNode("http://example.com/Patient1"), + namedNode("http://hl7.org/fhir/roommate"), + ); + write(namedNode("http://example.com/SomeGraph")).using( + allRoommates, + allRoommates, + ); + allRoommates[0].age = 20; + expect(graphOf(allRoommates[0], "age")[0].value).toBe( + "http://example.com/SomeGraph", + ); + }); + }); + }); + + describe("languageTag Support", () => { + it("Retrieves the proper language given the languageOrdering", async () => { + const [, , builder] = await getTinyLoadedDatasetWithLanguageTags(); + + const observation = builder + .setLanguagePreferences("fr", "en") + .fromSubject( + namedNode("http://example.com/Observation1"), + ); + + const patient = observation.subject as PatientShape; + + expect(observation.langNotes).toBe("Notes Sympas"); + expect(patient.langName?.[0]).toBe("Jean"); + + setLanguagePreferences("ru", "zh").using(observation, patient); + + expect(observation.langNotes).toBeUndefined(); + expect(patient.langName?.length).toBe(0); + + setLanguagePreferences("@other", "fr").using(observation, patient); + expect(observation.langNotes).not.toBe("Notes Sympas"); + expect(patient.langName?.[0]).not.toBe("Jean"); + + setLanguagePreferences().using(observation, patient); + expect(observation.langNotes).toBe(undefined); + expect(patient.langName?.length).toBe(0); + }); + + it("sets language strings based on the default language", async () => { + const [, , builder] = await getTinyLoadedDatasetWithLanguageTags(); + const observation = builder + .setLanguagePreferences("fr", "en") + .fromSubject( + namedNode("http://example.com/Observation1"), + ); + observation.langNotes = "quelques notes"; + expect(languagesOf(observation, "langNotes")).toEqual({ + fr: "quelques notes", + "@none": "Cool Notes", + en: "Cooler Notes", + es: "Notas Geniales", + }); + const patient = observation.subject as PatientShape; + patient.langName?.push("Luc"); + expect(languagesOf(patient, "langName").fr?.has("Jean")).toBe(true); + expect(languagesOf(patient, "langName").fr?.has("Luc")).toBe(true); + expect(languagesOf(patient, "langName")["@none"]?.has("Jon")).toBe(true); + expect(languagesOf(patient, "langName").en?.has("John")).toBe(true); + expect(languagesOf(patient, "langName").es?.has("Juan")).toBe(true); + + // Skips other in favor of setting the next language + setLanguagePreferences("@other", "es").using(observation, patient); + observation.langNotes = "algunas notas"; + expect(languagesOf(observation, "langNotes")).toEqual({ + fr: "quelques notes", + "@none": "Cool Notes", + en: "Cooler Notes", + es: "algunas notas", + }); + + // Does not set a language if only other + setLanguagePreferences("@other").using(observation, patient); + observation.langNotes = "Some Notes that will never be written"; + expect(languagesOf(observation, "langNotes")).toEqual({ + fr: "quelques notes", + "@none": "Cool Notes", + en: "Cooler Notes", + es: "algunas notas", + }); + + // Does not set a language if empty + setLanguagePreferences().using(observation, patient); + observation.langNotes = "Some Notes that will never be written"; + expect(languagesOf(observation, "langNotes")).toEqual({ + fr: "quelques notes", + "@none": "Cool Notes", + en: "Cooler Notes", + es: "algunas notas", + }); + + // Sets @none + setLanguagePreferences("@none").using(observation, patient); + observation.langNotes = "Other notes"; + expect(languagesOf(observation, "langNotes")).toEqual({ + fr: "quelques notes", + "@none": "Other notes", + en: "Cooler Notes", + es: "algunas notas", + }); + }); + + it("uses languageOf to make a languageMap", async () => { + const [, observation] = await getTinyLoadedDatasetWithLanguageTags(); + const languageMap = languagesOf(observation, "langNotes"); + expect(languageMap).toEqual({ + "@none": "Cool Notes", + en: "Cooler Notes", + es: "Notas Geniales", + fr: "Notes Sympas", + }); + }); + + it("uses languageOf to set values on a languageMap", async () => { + const [dataset, observation] = + await getTinyLoadedDatasetWithLanguageTags(); + const languageMap = languagesOf(observation, "langNotes"); + languageMap.zh = "很酷的笔记"; + languageMap.fr = "notes plus fraîches"; + expect(languageMap).toEqual({ + "@none": "Cool Notes", + en: "Cooler Notes", + es: "Notas Geniales", + fr: "notes plus fraîches", + zh: "很酷的笔记", + }); + const langNoteQuads = dataset.match( + namedNode("http://example.com/Observation1"), + namedNode("http://hl7.org/fhir/langNotes"), + ); + expect(langNoteQuads.size).toBe(5); + expect( + langNoteQuads.some( + (quad) => + quad.object.termType === "Literal" && + quad.object.language === "fr" && + quad.object.value === "notes plus fraîches", + ), + ).toBe(true); + expect( + langNoteQuads.some( + (quad) => + quad.object.termType === "Literal" && + quad.object.language === "zh" && + quad.object.value === "很酷的笔记", + ), + ).toBe(true); + }); + + it("uses languageOf to delete values on a languageMap", async () => { + const [dataset, observation] = + await getTinyLoadedDatasetWithLanguageTags(); + const languageMap = languagesOf(observation, "langNotes"); + delete languageMap.fr; + expect(languageMap).toEqual({ + "@none": "Cool Notes", + en: "Cooler Notes", + es: "Notas Geniales", + }); + const langNoteQuads = dataset.match( + namedNode("http://example.com/Observation1"), + namedNode("http://hl7.org/fhir/langNotes"), + ); + expect(langNoteQuads.size).toBe(3); + expect( + langNoteQuads.every( + (quad) => + !( + quad.object.termType === "Literal" && + quad.object.language === "fr" + ), + ), + ).toBe(true); + }); + + it("executes the methods of the LanguageSet", async () => { + const [dataset, observation] = + await getTinyLoadedDatasetWithLanguageTags(); + + const subject = namedNode("http://example.com/Patient1"); + const predicate = namedNode("http://hl7.org/fhir/langName"); + + const patient = observation.subject as PatientShape; + + const enSet = languagesOf(patient, "langName").en as LanguageSet; + + expect(enSet.size).toBe(1); + + enSet.add("Doe"); + expect(enSet.size).toBe(2); + expect(enSet.has("Doe")).toBe(true); + expect(dataset.has(quad(subject, predicate, literal("Doe", "en")))).toBe( + true, + ); + + const callbackMock = jest.fn(); + enSet.forEach(callbackMock); + expect(callbackMock).toHaveBeenCalledTimes(2); + expect(callbackMock).toHaveBeenCalledWith("John", "John", enSet); + + const entries = enSet.entries(); + const entriesVal1 = entries.next(); + const entriesVal2 = entries.next(); + const entriesVal3 = entries.next(); + expect(entriesVal1.value).toEqual(["John", "John"]); + expect(entriesVal2.value).toEqual(["Doe", "Doe"]); + expect(entriesVal3.done).toBe(true); + + const keys = enSet.keys(); + const keysVal1 = keys.next(); + const keysVal2 = keys.next(); + const keysVal3 = keys.next(); + expect(keysVal1.value).toBe("John"); + expect(keysVal2.value).toBe("Doe"); + expect(keysVal3.done).toBe(true); + + const values = enSet.values(); + const valuesVal1 = values.next(); + const valuesVal2 = values.next(); + const valuesVal3 = values.next(); + expect(valuesVal1.value).toBe("John"); + expect(valuesVal2.value).toBe("Doe"); + expect(valuesVal3.done).toBe(true); + + enSet.delete("John"); + expect(enSet.size).toBe(1); + expect(enSet.has("John")).toBe(false); + expect(dataset.has(quad(subject, predicate, literal("John", "en")))).toBe( + false, + ); + + enSet.clear(); + expect(enSet.size).toBe(0); + }); + }); +}); diff --git a/packages/jsonld-dataset-proxy/test/nodeToJsonRepresentation.test.ts b/packages/jsonld-dataset-proxy/test/nodeToJsonRepresentation.test.ts new file mode 100644 index 0000000..46707f1 --- /dev/null +++ b/packages/jsonld-dataset-proxy/test/nodeToJsonRepresentation.test.ts @@ -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( + "", + "http://www.w3.org/1999/02/22-rdf-syntax-ns#HTML", + ), + extraParams, + ), + ).toBe(""); + }); + + 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"); + }); +}); diff --git a/packages/jsonld-dataset-proxy/test/nodeToString.test.ts b/packages/jsonld-dataset-proxy/test/nodeToString.test.ts new file mode 100644 index 0000000..1d05021 --- /dev/null +++ b/packages/jsonld-dataset-proxy/test/nodeToString.test.ts @@ -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()"); + }); +}); diff --git a/packages/jsonld-dataset-proxy/test/patientExampleData.ts b/packages/jsonld-dataset-proxy/test/patientExampleData.ts new file mode 100644 index 0000000..b199c06 --- /dev/null +++ b/packages/jsonld-dataset-proxy/test/patientExampleData.ts @@ -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: . +@prefix fhir: . +@prefix xsd: . +@prefix rdf: . + +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: . +@prefix fhir: . +@prefix xsd: . + +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: . +@prefix fhir: . +@prefix xsd: . + +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: . +@prefix fhir: . +@prefix xsd: . + +example:Patient1 + fhir:name "Garrett"^^xsd:string, "Bobby"^^xsd:string, "Ferguson"^^xsd:string . +`; + +export const tinyPatientDataWithBlankNodes = ` +@prefix example: . +@prefix fhir: . +@prefix xsd: . + +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: . +@prefix fhir: . +@prefix xsd: . + +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 . +`; diff --git a/packages/jsonld-dataset-proxy/tsconfig.build.json b/packages/jsonld-dataset-proxy/tsconfig.build.json new file mode 100644 index 0000000..ce7be9c --- /dev/null +++ b/packages/jsonld-dataset-proxy/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": ["./src"] +} \ No newline at end of file diff --git a/packages/ldo/jest.config.js b/packages/ldo/jest.config.js index b28362a..bad5f64 100644 --- a/packages/ldo/jest.config.js +++ b/packages/ldo/jest.config.js @@ -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': './', -} \ No newline at end of file + rootDir: "./", +}; diff --git a/packages/ldo/package.json b/packages/ldo/package.json index d6933a5..c901b64 100644 --- a/packages/ldo/package.json +++ b/packages/ldo/package.json @@ -34,9 +34,9 @@ "ts-node": "^10.4.0" }, "dependencies": { + "@ldo/jsonld-dataset-proxy": "^0.0.0", "@rdfjs/data-model": "^1.2.0", "buffer": "^6.0.3", - "jsonld-dataset-proxy": "^1.2.1", "n3": "^1.16.2", "o-dataset-pack": "^0.2.11", "readable-stream": "^4.3.0" diff --git a/packages/ldo/src/LdoBuilder.ts b/packages/ldo/src/LdoBuilder.ts index a0a3bab..5e1862d 100644 --- a/packages/ldo/src/LdoBuilder.ts +++ b/packages/ldo/src/LdoBuilder.ts @@ -4,7 +4,7 @@ import type { QuadMatch, SubjectType, LanguageOrdering, -} from "jsonld-dataset-proxy"; +} from "@ldo/jsonld-dataset-proxy"; import type { ShapeType } from "./ShapeType"; import type { LdoBase } from "./util"; import { normalizeNodeName, normalizeNodeNames } from "./util"; diff --git a/packages/ldo/src/LdoDataset.ts b/packages/ldo/src/LdoDataset.ts index 7407566..78ad4df 100644 --- a/packages/ldo/src/LdoDataset.ts +++ b/packages/ldo/src/LdoDataset.ts @@ -1,5 +1,5 @@ import type { Quad } from "@rdfjs/types"; -import jsonldDatasetProxy from "jsonld-dataset-proxy"; +import jsonldDatasetProxy from "@ldo/jsonld-dataset-proxy"; import { WrapperSubscribableDataset } from "o-dataset-pack"; import { LdoBuilder } from "./LdoBuilder"; import type { ShapeType } from "./ShapeType"; diff --git a/packages/ldo/src/methods.ts b/packages/ldo/src/methods.ts index 315786b..15e45ed 100644 --- a/packages/ldo/src/methods.ts +++ b/packages/ldo/src/methods.ts @@ -1,12 +1,12 @@ import type { Dataset } from "@rdfjs/types"; import type { JsonLdDocument } from "jsonld"; -import type { GraphType, InteractOptions } from "jsonld-dataset-proxy"; +import type { GraphType, InteractOptions } from "@ldo/jsonld-dataset-proxy"; import { getProxyFromObject, _getUnderlyingDataset, _proxyContext, write as writeDependency, -} from "jsonld-dataset-proxy"; +} from "@ldo/jsonld-dataset-proxy"; import type { Quad, WriterOptions } from "n3"; import type { DatasetChanges, SubscribableDataset } from "o-dataset-pack"; import { datasetToString } from "./datasetConverters"; @@ -21,7 +21,7 @@ export { graphOf, languagesOf, setLanguagePreferences, -} from "jsonld-dataset-proxy"; +} from "@ldo/jsonld-dataset-proxy"; export function write(...graphs: (GraphType | string)[]): InteractOptions { return writeDependency(...normalizeNodeNames(graphs)); diff --git a/packages/ldo/src/util.ts b/packages/ldo/src/util.ts index a38ec6d..ba35adb 100644 --- a/packages/ldo/src/util.ts +++ b/packages/ldo/src/util.ts @@ -7,12 +7,12 @@ import type { PredicateType, SubjectProxy, SubjectType, -} from "jsonld-dataset-proxy"; +} from "@ldo/jsonld-dataset-proxy"; import { getProxyFromObject, _getUnderlyingDataset, _proxyContext, -} from "jsonld-dataset-proxy"; +} from "@ldo/jsonld-dataset-proxy"; import type { Quad } from "n3"; import type { SubscribableDataset, TransactionalDataset } from "o-dataset-pack"; diff --git a/packages/ldo/test/LdoDataset.test.ts b/packages/ldo/test/LdoDataset.test.ts index e6f5ed8..7b2fa2b 100644 --- a/packages/ldo/test/LdoDataset.test.ts +++ b/packages/ldo/test/LdoDataset.test.ts @@ -5,8 +5,8 @@ import { ProfileShapeType } from "./profileData"; import type { LdoBuilder, LdoDataset } from "../src"; import { createLdoDataset, graphOf, parseRdf, toTurtle } from "../src"; import { sampleJsonld, sampleTurtle } from "./sampleData"; -import type { SubjectProxy } from "jsonld-dataset-proxy"; -import { _proxyContext } from "jsonld-dataset-proxy"; +import type { SubjectProxy } from "@ldo/jsonld-dataset-proxy"; +import { _proxyContext } from "@ldo/jsonld-dataset-proxy"; describe("LdoDataset", () => { let ldoDataset: LdoDataset; diff --git a/packages/ldo/test/methods.test.ts b/packages/ldo/test/methods.test.ts index 7a85304..a202d9e 100644 --- a/packages/ldo/test/methods.test.ts +++ b/packages/ldo/test/methods.test.ts @@ -1,11 +1,11 @@ import { namedNode } from "@rdfjs/data-model"; -import type { SubjectProxy } from "jsonld-dataset-proxy"; +import type { SubjectProxy } from "@ldo/jsonld-dataset-proxy"; import { getProxyFromObject, graphOf, _getUnderlyingDataset, _proxyContext, -} from "jsonld-dataset-proxy"; +} from "@ldo/jsonld-dataset-proxy"; import { createDataset } from "o-dataset-pack"; import type { SolidProfileShape } from "./profileData"; import { ProfileShapeType } from "./profileData"; diff --git a/packages/schema-converter-shex/jest.config.js b/packages/schema-converter-shex/jest.config.js index b28362a..bad5f64 100644 --- a/packages/schema-converter-shex/jest.config.js +++ b/packages/schema-converter-shex/jest.config.js @@ -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': './', -} \ No newline at end of file + rootDir: "./", +}; diff --git a/packages/schema-converter-shex/package.json b/packages/schema-converter-shex/package.json index 2eb8f7c..d76a637 100644 --- a/packages/schema-converter-shex/package.json +++ b/packages/schema-converter-shex/package.json @@ -6,7 +6,8 @@ "scripts": { "build": "tsc --project tsconfig.build.json", "test": "jest --coverage", - "prepublishOnly": "npm run test && npm run build" + "prepublishOnly": "npm run test && npm run build", + "lint": "eslint src/** --fix --no-error-on-unmatched-pattern" }, "repository": { "type": "git", diff --git a/packages/schema-converter-shex/src/context/JsonLdContextBuilder.ts b/packages/schema-converter-shex/src/context/JsonLdContextBuilder.ts index aaa2776..5fdd2ed 100644 --- a/packages/schema-converter-shex/src/context/JsonLdContextBuilder.ts +++ b/packages/schema-converter-shex/src/context/JsonLdContextBuilder.ts @@ -1,5 +1,5 @@ -import { Annotation } from "shexj"; -import { ContextDefinition, ExpandedTermDefinition } from "jsonld"; +import type { Annotation } from "shexj"; +import type { ContextDefinition, ExpandedTermDefinition } from "jsonld"; /** * Name functions @@ -24,7 +24,7 @@ export function nameFromObject(obj: { }): string | undefined { const labelAnnotationObject = obj.annotations?.find( (annotation) => - annotation.predicate === "http://www.w3.org/2000/01/rdf-schema#label" + annotation.predicate === "http://www.w3.org/2000/01/rdf-schema#label", )?.object; if (labelAnnotationObject && typeof labelAnnotationObject === "string") { return toCamelCase(iriToName(labelAnnotationObject)); @@ -68,7 +68,7 @@ export class JsonLdContextBuilder { iri: string, expandedTermDefinition: ExpandedTermDefinition, isContainer: boolean, - annotations?: Annotation[] + annotations?: Annotation[], ) { this.addSubject(iri, annotations); if (!this.iriTypes[iri]) { @@ -115,7 +115,7 @@ export class JsonLdContextBuilder { const labelAnnotationObject = annotations.find( (annotation) => annotation.predicate === - "http://www.w3.org/2000/01/rdf-schema#label" + "http://www.w3.org/2000/01/rdf-schema#label", )?.object; if ( labelAnnotationObject && diff --git a/packages/schema-converter-shex/src/context/ShexJContextVisitor.ts b/packages/schema-converter-shex/src/context/ShexJContextVisitor.ts index a175a70..8651df1 100644 --- a/packages/schema-converter-shex/src/context/ShexJContextVisitor.ts +++ b/packages/schema-converter-shex/src/context/ShexJContextVisitor.ts @@ -1,5 +1,5 @@ import ShexJTraverser from "shexj-traverser"; -import { JsonLdContextBuilder } from "./JsonLdContextBuilder"; +import type { JsonLdContextBuilder } from "./JsonLdContextBuilder"; /** * Visitor @@ -7,9 +7,7 @@ import { JsonLdContextBuilder } from "./JsonLdContextBuilder"; export const ShexJNameVisitor = ShexJTraverser.createVisitor({ Shape: { - visitor: async (shape, context) => { - - } + visitor: async (_shape, _context) => {}, }, TripleConstraint: { visitor: async (tripleConstraint, context) => { @@ -26,7 +24,7 @@ export const ShexJNameVisitor = "@type": tripleConstraint.valueExpr.datatype, }, isContainer, - tripleConstraint.annotations + tripleConstraint.annotations, ); } else if ( tripleConstraint.valueExpr.nodeKind && @@ -36,14 +34,14 @@ export const ShexJNameVisitor = tripleConstraint.predicate, { "@type": "@id" }, isContainer, - tripleConstraint.annotations + tripleConstraint.annotations, ); } else { context.addPredicate( tripleConstraint.predicate, {}, isContainer, - tripleConstraint.annotations + tripleConstraint.annotations, ); } } else { @@ -53,13 +51,13 @@ export const ShexJNameVisitor = "@type": "@id", }, isContainer, - tripleConstraint.annotations + tripleConstraint.annotations, ); } } else { context.addSubject( tripleConstraint.predicate, - tripleConstraint.annotations + tripleConstraint.annotations, ); } }, diff --git a/packages/schema-converter-shex/src/context/shexjToContext.ts b/packages/schema-converter-shex/src/context/shexjToContext.ts index 32cc1f5..6a99aeb 100644 --- a/packages/schema-converter-shex/src/context/shexjToContext.ts +++ b/packages/schema-converter-shex/src/context/shexjToContext.ts @@ -1,11 +1,11 @@ -import { ContextDefinition } from "jsonld"; -import { Schema } from "shexj"; +import type { ContextDefinition } from "jsonld"; +import type { Schema } from "shexj"; import { JsonLdContextBuilder } from "./JsonLdContextBuilder"; import { ShexJNameVisitor } from "./ShexJContextVisitor"; import { jsonld2graphobject } from "jsonld2graphobject"; export async function shexjToContext( - shexj: Schema + shexj: Schema, ): Promise { const processedShexj: Schema = (await jsonld2graphobject( { @@ -13,7 +13,7 @@ export async function shexjToContext( "@id": "SCHEMA", "@context": "http://www.w3.org/ns/shex.jsonld", }, - "SCHEMA" + "SCHEMA", )) as unknown as Schema; const jsonLdContextBuilder = new JsonLdContextBuilder(); await ShexJNameVisitor.visit(processedShexj, "Schema", jsonLdContextBuilder); diff --git a/packages/schema-converter-shex/src/typing/ShapeInterfaceDeclaration.ts b/packages/schema-converter-shex/src/typing/ShapeInterfaceDeclaration.ts index ed1e3bb..037f300 100644 --- a/packages/schema-converter-shex/src/typing/ShapeInterfaceDeclaration.ts +++ b/packages/schema-converter-shex/src/typing/ShapeInterfaceDeclaration.ts @@ -1,4 +1,4 @@ -import { InterfaceDeclaration } from "dts-dom"; +import type { InterfaceDeclaration } from "dts-dom"; export interface ShapeInterfaceDeclaration extends InterfaceDeclaration { shapeId?: string; diff --git a/packages/schema-converter-shex/src/typing/ShexJTypingTransformer.ts b/packages/schema-converter-shex/src/typing/ShexJTypingTransformer.ts index cf1dabe..3b662d1 100644 --- a/packages/schema-converter-shex/src/typing/ShexJTypingTransformer.ts +++ b/packages/schema-converter-shex/src/typing/ShexJTypingTransformer.ts @@ -1,19 +1,19 @@ import ShexJTraverser from "shexj-traverser"; import * as dom from "dts-dom"; -import { Annotation } from "shexj"; +import type { Annotation } from "shexj"; import { nameFromObject } from "../context/JsonLdContextBuilder"; -import { ShapeInterfaceDeclaration } from "./ShapeInterfaceDeclaration"; +import type { ShapeInterfaceDeclaration } from "./ShapeInterfaceDeclaration"; export interface ShexJTypeTransformerContext { getNameFromIri: (iri: string) => string; } export function commentFromAnnotations( - annotations?: Annotation[] + annotations?: Annotation[], ): string | undefined { const commentAnnotationObject = annotations?.find( (annotation) => - annotation.predicate === "http://www.w3.org/2000/01/rdf-schema#comment" + annotation.predicate === "http://www.w3.org/2000/01/rdf-schema#comment", )?.object; if (typeof commentAnnotationObject === "string") { // It's an IRI @@ -49,7 +49,7 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< Schema: { transformer: async ( _schema, - getTransformedChildren + getTransformedChildren, ): Promise => { const transformedChildren = await getTransformedChildren(); const interfaces: dom.TopLevelDeclaration[] = []; @@ -67,7 +67,7 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< ShapeDecl: { transformer: async ( shapeDecl, - getTransformedChildren + getTransformedChildren, ): Promise => { const shapeName = nameFromObject(shapeDecl) || "Shape"; const { shapeExpr } = await getTransformedChildren(); @@ -80,7 +80,7 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< } else { // TODO: Handle other items throw new Error( - "Cannot handle ShapeOr, ShapeAnd, ShapeNot, ShapeExternal, or NodeConstraint" + "Cannot handle ShapeOr, ShapeAnd, ShapeNot, ShapeExternal, or NodeConstraint", ); } }, @@ -95,15 +95,15 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< dom.create.property( "@id", dom.type.string, - dom.DeclarationFlags.Optional - ) + dom.DeclarationFlags.Optional, + ), ); newInterface.members.push( dom.create.property( "@context", dom.create.namedTypeReference("ContextDefinition"), - dom.DeclarationFlags.Optional - ) + dom.DeclarationFlags.Optional, + ), ); if (typeof transformedChildren.expression === "string") { // TODO: handle string @@ -113,14 +113,14 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< "interface" ) { newInterface.members.push( - ...(transformedChildren.expression as dom.ObjectType).members + ...(transformedChildren.expression as dom.ObjectType).members, ); } else if ( (transformedChildren.expression as dom.PropertyDeclaration).kind === "property" ) { newInterface.members.push( - transformedChildren.expression as dom.PropertyDeclaration + transformedChildren.expression as dom.PropertyDeclaration, ); } // Use EXTENDS @@ -129,7 +129,7 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< transformedChildren.extends.forEach((extendsItem) => { if ((extendsItem as dom.InterfaceDeclaration).kind === "interface") { newInterface.baseTypes?.push( - extendsItem as dom.InterfaceDeclaration + extendsItem as dom.InterfaceDeclaration, ); } }); @@ -151,21 +151,21 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< transformedChildren.expressions .filter( ( - expression + expression, ): expression is dom.ObjectType | dom.PropertyDeclaration => { return ( (expression as dom.PropertyDeclaration).kind === "property" || (expression as dom.ObjectType).kind === "object" || (expression as dom.InterfaceDeclaration).kind === "interface" ); - } + }, ) .forEach( ( expression: | dom.ObjectType | dom.InterfaceDeclaration - | dom.PropertyDeclaration + | dom.PropertyDeclaration, ) => { if (expression.kind === "property") { inputPropertyExpressions.push(expression); @@ -176,7 +176,7 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< } }); } - } + }, ); // Merge property expressions @@ -206,7 +206,7 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< dom.type.array(dom.create.union([oldProeprtyType, propertyType])), isOptional ? dom.DeclarationFlags.Optional - : dom.DeclarationFlags.None + : dom.DeclarationFlags.None, ); // Set JS Comment properties[propertyDeclaration.name].jsDocComment = @@ -229,7 +229,7 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< tripleConstraint, getTransformedChildren, setReturnPointer, - context + context, ) => { const transformedChildren = await getTransformedChildren(); const propertyName = context.getNameFromIri(tripleConstraint.predicate); @@ -244,11 +244,11 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< const propertyDeclaration = dom.create.property( propertyName, isArray ? dom.type.array(type) : type, - isOptional ? dom.DeclarationFlags.Optional : dom.DeclarationFlags.None + isOptional ? dom.DeclarationFlags.Optional : dom.DeclarationFlags.None, ); propertyDeclaration.jsDocComment = commentFromAnnotations( - tripleConstraint.annotations + tripleConstraint.annotations, ); return propertyDeclaration; }, @@ -258,7 +258,7 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< nodeConstraint, _getTransformedChildren, setReturnPointer, - context + context, ) => { if (nodeConstraint.datatype) { switch (nodeConstraint.datatype) { @@ -325,7 +325,7 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< dom.create.property( "@id", dom.type.string, - dom.DeclarationFlags.Optional + dom.DeclarationFlags.Optional, ), ]); case "literal": @@ -341,9 +341,9 @@ export const ShexJTypingTransformer = ShexJTraverser.createTransformer< dom.create.objectType([ dom.create.property( "@id", - dom.type.stringLiteral(context.getNameFromIri(value)) + dom.type.stringLiteral(context.getNameFromIri(value)), ), - ]) + ]), ); } }); diff --git a/packages/schema-converter-shex/src/typing/shexjToTyping.ts b/packages/schema-converter-shex/src/typing/shexjToTyping.ts index 455eea5..9f99b3d 100644 --- a/packages/schema-converter-shex/src/typing/shexjToTyping.ts +++ b/packages/schema-converter-shex/src/typing/shexjToTyping.ts @@ -1,5 +1,5 @@ -import { ContextDefinition } from "jsonld"; -import { Schema } from "shexj"; +import type { ContextDefinition } from "jsonld"; +import type { Schema } from "shexj"; import { JsonLdContextBuilder } from "../context/JsonLdContextBuilder"; import { ShexJNameVisitor } from "../context/ShexJContextVisitor"; import { jsonld2graphobject } from "jsonld2graphobject"; @@ -15,7 +15,7 @@ export interface TypeingReturn { } export async function shexjToTyping( - shexj: Schema + shexj: Schema, ): Promise<[TypeingReturn, ContextDefinition]> { const processedShexj: Schema = (await jsonld2graphobject( { @@ -23,7 +23,7 @@ export async function shexjToTyping( "@id": "SCHEMA", "@context": "http://www.w3.org/ns/shex.jsonld", }, - "SCHEMA" + "SCHEMA", )) as unknown as Schema; const jsonLdContextBuilder = new JsonLdContextBuilder(); await ShexJNameVisitor.visit(processedShexj, "Schema", jsonLdContextBuilder); @@ -34,7 +34,7 @@ export async function shexjToTyping( { getNameFromIri: jsonLdContextBuilder.getNameFromIri.bind(jsonLdContextBuilder), - } + }, ); const typings = declarations.map((declaration) => { return { diff --git a/packages/solid-react/package.json b/packages/solid-react/package.json index b8b8c79..7884bf1 100644 --- a/packages/solid-react/package.json +++ b/packages/solid-react/package.json @@ -36,9 +36,9 @@ }, "dependencies": { "@inrupt/solid-client": "^1.29.0", + "@ldo/jsonld-dataset-proxy": "^0.0.0", "@ldo/ldo": "^0.0.0", "cross-fetch": "^3.1.6", - "jsonld-dataset-proxy": "^1.2.3", "o-dataset-pack": "^0.2.14", "solid-authn-react-native": "^2.0.3", "stream": "^0.0.2" diff --git a/packages/solid-react/src/ldoHooks/helpers/TrackingProxyContext.ts b/packages/solid-react/src/ldoHooks/helpers/TrackingProxyContext.ts index 7659f49..d127177 100644 --- a/packages/solid-react/src/ldoHooks/helpers/TrackingProxyContext.ts +++ b/packages/solid-react/src/ldoHooks/helpers/TrackingProxyContext.ts @@ -2,8 +2,8 @@ import type { ArrayProxyTarget, SubjectProxyTarget, ProxyContextOptions, -} from "jsonld-dataset-proxy"; -import { ProxyContext } from "jsonld-dataset-proxy"; +} from "@ldo/jsonld-dataset-proxy"; +import { ProxyContext } from "@ldo/jsonld-dataset-proxy"; import type { UpdateManager } from "./UpdateManager"; import { namedNode } from "@rdfjs/data-model"; diff --git a/packages/solid-react/src/ldoHooks/helpers/UpdateManager.ts b/packages/solid-react/src/ldoHooks/helpers/UpdateManager.ts index 0ad004f..dbdbd3e 100644 --- a/packages/solid-react/src/ldoHooks/helpers/UpdateManager.ts +++ b/packages/solid-react/src/ldoHooks/helpers/UpdateManager.ts @@ -5,8 +5,8 @@ import type { SubjectType, PredicateType, ObjectType, -} from "jsonld-dataset-proxy"; -import { nodeToString } from "jsonld-dataset-proxy"; +} from "@ldo/jsonld-dataset-proxy"; +import { nodeToString } from "@ldo/jsonld-dataset-proxy"; import type { Quad } from "@rdfjs/types"; export type TripleMatch = [QuadMatch[0], QuadMatch[1], QuadMatch[2]]; diff --git a/packages/solid-react/src/ldoHooks/useSubject.ts b/packages/solid-react/src/ldoHooks/useSubject.ts index 5909efa..704842a 100644 --- a/packages/solid-react/src/ldoHooks/useSubject.ts +++ b/packages/solid-react/src/ldoHooks/useSubject.ts @@ -1,5 +1,8 @@ -import type { SubjectType } from "jsonld-dataset-proxy"; -import { ContextUtil, JsonldDatasetProxyBuilder } from "jsonld-dataset-proxy"; +import type { SubjectType } from "@ldo/jsonld-dataset-proxy"; +import { + ContextUtil, + JsonldDatasetProxyBuilder, +} from "@ldo/jsonld-dataset-proxy"; import type { ShapeType, LdoBase } from "@ldo/ldo"; import { LdoBuilder } from "@ldo/ldo"; import { useLdoContext } from "../LdoContext"; diff --git a/packages/solid-react/src/useLdo.ts b/packages/solid-react/src/useLdo.ts index d2e4744..69158db 100644 --- a/packages/solid-react/src/useLdo.ts +++ b/packages/solid-react/src/useLdo.ts @@ -8,7 +8,7 @@ import type { DataResource } from "./document/resource/dataResource/DataResource import type { BinaryResource } from "./document/resource/binaryResource/BinaryResource"; import type { ContainerResource } from "./document/resource/dataResource/containerResource/ContainerResource"; import type { AccessRules } from "./document/accessRules/AccessRules"; -import type { SubjectType } from "jsonld-dataset-proxy"; +import type { SubjectType } from "@ldo/jsonld-dataset-proxy"; import type { DatasetChanges } from "o-dataset-pack"; import type { Quad } from "@rdfjs/types"; diff --git a/packages/solid-react/src/util/splitChangesByGraph.ts b/packages/solid-react/src/util/splitChangesByGraph.ts index 0d3184d..99fa321 100644 --- a/packages/solid-react/src/util/splitChangesByGraph.ts +++ b/packages/solid-react/src/util/splitChangesByGraph.ts @@ -1,6 +1,6 @@ import type { DatasetChanges } from "o-dataset-pack"; import { createDataset } from "o-dataset-pack"; -import type { GraphType } from "jsonld-dataset-proxy"; +import type { GraphType } from "@ldo/jsonld-dataset-proxy"; import type { Quad } from "@rdfjs/types"; import { defaultGraph, namedNode, quad as createQuad } from "@rdfjs/data-model";