Merge pull request #12 from o-development/access-control

Access control
main
jaxoncreed 2 years ago committed by GitHub
commit 78cadc3eb8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      lerna.json
  2. 42079
      package-lock.json
  3. 6
      packages/cli/package.json
  4. 6
      packages/dataset/package.json
  5. 3
      packages/demo-react/.eslintrc
  6. 23
      packages/demo-react/.gitignore
  7. 21
      packages/demo-react/LICENSE.txt
  8. 12
      packages/demo-react/README.md
  9. 28
      packages/demo-react/craco.config.js
  10. 50
      packages/demo-react/package.json
  11. 41
      packages/demo-react/public/index.html
  12. 25
      packages/demo-react/public/manifest.json
  13. 3
      packages/demo-react/public/robots.txt
  14. 31
      packages/demo-react/src/.ldo/post.context.ts
  15. 19
      packages/demo-react/src/.ldo/post.shapeTypes.ts
  16. 45
      packages/demo-react/src/.ldo/post.typings.ts
  17. 154
      packages/demo-react/src/.ldo/solidProfile.context.ts
  18. 749
      packages/demo-react/src/.ldo/solidProfile.schema.ts
  19. 71
      packages/demo-react/src/.ldo/solidProfile.shapeTypes.ts
  20. 293
      packages/demo-react/src/.ldo/solidProfile.typings.ts
  21. 23
      packages/demo-react/src/.shapes/post.shex
  22. 121
      packages/demo-react/src/.shapes/solidProfile.shex
  23. 13
      packages/demo-react/src/App-old.tsx
  24. 65
      packages/demo-react/src/App.tsx
  25. 54
      packages/demo-react/src/Header.tsx
  26. 50
      packages/demo-react/src/Layout.tsx
  27. 67
      packages/demo-react/src/MainContainerProvider.tsx
  28. 30
      packages/demo-react/src/blog/Blog.tsx
  29. 92
      packages/demo-react/src/blog/MakePost.tsx
  30. 8
      packages/demo-react/src/index.tsx
  31. 42
      packages/demo-react/src/post/Post.tsx
  32. 16
      packages/demo-react/src/post/PostPage.tsx
  33. 16
      packages/demo-react/src/post/PostedBy.tsx
  34. 34
      packages/demo-react/src/profile/Profile.tsx
  35. 20
      packages/demo-react/tsconfig.json
  36. 8
      packages/jsonld-dataset-proxy/package.json
  37. 12
      packages/ldo/package.json
  38. 6
      packages/rdf-utils/package.json
  39. 6
      packages/schema-converter-shex/package.json
  40. 24
      packages/solid-react/package.json
  41. 10
      packages/solid-react/test/Integration.test.tsx
  42. 21
      packages/solid/package.json
  43. 45
      packages/solid/src/.ldo/wac.context.ts
  44. 116
      packages/solid/src/.ldo/wac.schema.ts
  45. 19
      packages/solid/src/.ldo/wac.shapeTypes.ts
  46. 73
      packages/solid/src/.ldo/wac.typings.ts
  47. 23
      packages/solid/src/.shapes/wac.shex
  48. 11
      packages/solid/src/index.ts
  49. 13
      packages/solid/src/requester/requests/getAccessRules.ts
  50. 88
      packages/solid/src/requester/requests/setAccessRules.ts
  51. 42
      packages/solid/src/requester/results/error/HttpErrorResult.ts
  52. 11
      packages/solid/src/requester/results/success/AccessRule.ts
  53. 8
      packages/solid/src/resource/Leaf.ts
  54. 184
      packages/solid/src/resource/Resource.ts
  55. 18
      packages/solid/src/resource/wac/WacRule.ts
  56. 118
      packages/solid/src/resource/wac/getWacRule.ts
  57. 70
      packages/solid/src/resource/wac/getWacUri.ts
  58. 13
      packages/solid/src/resource/wac/results/GetWacRuleSuccess.ts
  59. 13
      packages/solid/src/resource/wac/results/GetWacUriSuccess.ts
  60. 13
      packages/solid/src/resource/wac/results/SetWacRuleSuccess.ts
  61. 8
      packages/solid/src/resource/wac/results/WacRuleAbsent.ts
  62. 109
      packages/solid/src/resource/wac/setWacRule.ts
  63. 35
      packages/solid/src/util/rdfUtils.ts
  64. 303
      packages/solid/test/Integration.test.ts
  65. 8
      packages/subscribable-dataset/package.json
  66. 12
      packages/traverser-shexj/package.json
  67. 4
      packages/type-traverser/package.json

@ -1,4 +1,4 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "0.0.1-alpha.19"
"version": "0.0.1-alpha.23"
}

42079
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{
"name": "@ldo/cli",
"version": "0.0.1-alpha.19",
"version": "0.0.1-alpha.23",
"description": "A Command Line Interface for Linked Data Objects",
"main": "./dist/index.js",
"bin": {
@ -43,7 +43,7 @@
"ts-jest": "^27.0.7"
},
"dependencies": {
"@ldo/schema-converter-shex": "^0.0.1-alpha.17",
"@ldo/schema-converter-shex": "^0.0.1-alpha.23",
"@shexjs/parser": "^1.0.0-alpha.24",
"child-process-promise": "^2.2.1",
"commander": "^9.3.0",
@ -59,5 +59,5 @@
"publishConfig": {
"access": "public"
},
"gitHead": "4548985c0de9b0ec83cf5ee93f2d7c1ca2c1b8d8"
"gitHead": "d2364cd2f8da5f0b673b1202d29df5b7c071a17c"
}

@ -1,6 +1,6 @@
{
"name": "@ldo/dataset",
"version": "0.0.1-alpha.17",
"version": "0.0.1-alpha.23",
"description": "An RDFJS dataset implementation",
"main": "dist/index.js",
"scripts": {
@ -34,7 +34,7 @@
"ts-node": "^9.1.1"
},
"dependencies": {
"@ldo/rdf-utils": "^0.0.1-alpha.17",
"@ldo/rdf-utils": "^0.0.1-alpha.23",
"@rdfjs/dataset": "^1.1.0",
"buffer": "^6.0.3",
"readable-stream": "^4.2.0"
@ -46,5 +46,5 @@
"publishConfig": {
"access": "public"
},
"gitHead": "4548985c0de9b0ec83cf5ee93f2d7c1ca2c1b8d8"
"gitHead": "d2364cd2f8da5f0b673b1202d29df5b7c071a17c"
}

@ -1,3 +0,0 @@
{
"extends": ["../../.eslintrc"]
}

@ -1,23 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 Jackson Morgan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -1,12 +0,0 @@
# LDO Demo-React
A demo app to show off the use of LDO.
## Sponsorship
This project was made possible by a grant from NGI Zero Entrust via nlnet. Learn more on the [NLnet project page](https://nlnet.nl/project/SolidUsableApps/).
[<img src="https://nlnet.nl/logo/banner.png" alt="nlnet foundation logo" width="300" />](https://nlnet.nl/)
[<img src="https://nlnet.nl/image/logos/NGI0Entrust_tag.svg" alt="NGI Zero Entrust Logo" width="300" />](https://nlnet.nl/)
## Liscense
MIT

@ -1,28 +0,0 @@
// this file overrides the default CRA configurations (webpack, eslint, babel, etc)
// Ingnore because config scripts can't use the import variable
// eslint-disable-next-line @typescript-eslint/no-var-requires
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
module.exports = {
webpack: {
configure: (config) => {
// Remove ModuleScopePlugin which throws when we try to import something
// outside of src/.
config.resolve.plugins.pop();
// Resolve the path aliases.
config.resolve.plugins.push(new TsconfigPathsPlugin());
// Let Babel compile outside of src/.
const oneOfRule = config.module.rules.find((rule) => rule.oneOf);
const tsRule = oneOfRule.oneOf.find((rule) =>
rule.test.toString().includes("ts|tsx"),
);
tsRule.include = undefined;
tsRule.exclude = /node_modules/;
return config;
},
},
};

@ -1,50 +0,0 @@
{
"name": "@ldo/demo-react",
"version": "0.0.1-alpha.19",
"dependencies": {
"@inrupt/solid-client-authn-browser": "^2.0.0",
"@ldo/solid-react": "^0.0.1-alpha.19",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.15.0",
"react-scripts": "5.0.1",
"uuid": "^9.0.1"
},
"scripts": {
"start": "craco start",
"build": "craco build",
"eject": "react-scripts eject",
"lint": "eslint src/** --fix --no-error-on-unmatched-pattern",
"build:ldo": "ldo build --input src/.shapes --output src/.ldo"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 firefox version",
"last 1 chrome version",
"last 1 safari version"
]
},
"devDependencies": {
"@craco/craco": "^7.1.0",
"@ldo/cli": "^0.0.1-alpha.19",
"@types/jsonld": "^1.5.9",
"@types/react": "^18.2.21",
"@types/shexj": "^2.1.4",
"tsconfig-paths-webpack-plugin": "^4.1.0"
},
"gitHead": "4548985c0de9b0ec83cf5ee93f2d7c1ca2c1b8d8",
"publishConfig": {
"access": "public"
}
}

@ -1,41 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

@ -1,25 +0,0 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

@ -1,3 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

@ -1,31 +0,0 @@
import { ContextDefinition } from "jsonld";
/**
* =============================================================================
* postContext: JSONLD Context for post
* =============================================================================
*/
export const postContext: ContextDefinition = {
type: {
"@id": "@type",
},
SocialMediaPosting: "http://schema.org/SocialMediaPosting",
CreativeWork: "http://schema.org/CreativeWork",
Thing: "http://schema.org/Thing",
articleBody: {
"@id": "http://schema.org/articleBody",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
uploadDate: {
"@id": "http://schema.org/uploadDate",
"@type": "http://www.w3.org/2001/XMLSchema#date",
},
image: {
"@id": "http://schema.org/image",
"@type": "@id",
},
publisher: {
"@id": "http://schema.org/publisher",
"@type": "@id",
},
};

@ -1,19 +0,0 @@
import { ShapeType } from "@ldo/ldo";
import { postSchema } from "./post.schema";
import { postContext } from "./post.context";
import { PostSh } from "./post.typings";
/**
* =============================================================================
* LDO ShapeTypes post
* =============================================================================
*/
/**
* PostSh ShapeType
*/
export const PostShShapeType: ShapeType<PostSh> = {
schema: postSchema,
shape: "https://example.com/PostSh",
context: postContext,
};

@ -1,45 +0,0 @@
import { ContextDefinition } from "jsonld";
/**
* =============================================================================
* Typescript Typings for post
* =============================================================================
*/
/**
* PostSh Type
*/
export interface PostSh {
"@id"?: string;
"@context"?: ContextDefinition;
type:
| {
"@id": "SocialMediaPosting";
}
| {
"@id": "CreativeWork";
}
| {
"@id": "Thing";
};
/**
* The actual body of the article.
*/
articleBody?: string;
/**
* Date when this media object was uploaded to this site.
*/
uploadDate: string;
/**
* A media object that encodes this CreativeWork. This property is a synonym for encoding.
*/
image?: {
"@id": string;
};
/**
* The publisher of the creative work.
*/
publisher: {
"@id": string;
};
}

@ -1,154 +0,0 @@
import { ContextDefinition } from "jsonld";
/**
* =============================================================================
* solidProfileContext: JSONLD Context for solidProfile
* =============================================================================
*/
export const solidProfileContext: ContextDefinition = {
type: {
"@id": "@type",
},
Person: "http://schema.org/Person",
Person2: "http://xmlns.com/foaf/0.1/Person",
fn: {
"@id": "http://www.w3.org/2006/vcard/ns#fn",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
name: {
"@id": "http://xmlns.com/foaf/0.1/name",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
hasAddress: {
"@id": "http://www.w3.org/2006/vcard/ns#hasAddress",
"@type": "@id",
"@container": "@set",
},
countryName: {
"@id": "http://www.w3.org/2006/vcard/ns#country-name",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
locality: {
"@id": "http://www.w3.org/2006/vcard/ns#locality",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
postalCode: {
"@id": "http://www.w3.org/2006/vcard/ns#postal-code",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
region: {
"@id": "http://www.w3.org/2006/vcard/ns#region",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
streetAddress: {
"@id": "http://www.w3.org/2006/vcard/ns#street-address",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
hasEmail: {
"@id": "http://www.w3.org/2006/vcard/ns#hasEmail",
"@type": "@id",
"@container": "@set",
},
Dom: "http://www.w3.org/2006/vcard/ns#Dom",
Home: "http://www.w3.org/2006/vcard/ns#Home",
ISDN: "http://www.w3.org/2006/vcard/ns#ISDN",
Internet: "http://www.w3.org/2006/vcard/ns#Internet",
Intl: "http://www.w3.org/2006/vcard/ns#Intl",
Label: "http://www.w3.org/2006/vcard/ns#Label",
Parcel: "http://www.w3.org/2006/vcard/ns#Parcel",
Postal: "http://www.w3.org/2006/vcard/ns#Postal",
Pref: "http://www.w3.org/2006/vcard/ns#Pref",
Work: "http://www.w3.org/2006/vcard/ns#Work",
X400: "http://www.w3.org/2006/vcard/ns#X400",
value: {
"@id": "http://www.w3.org/2006/vcard/ns#value",
"@type": "@id",
},
hasPhoto: {
"@id": "http://www.w3.org/2006/vcard/ns#hasPhoto",
"@type": "@id",
},
img: {
"@id": "http://xmlns.com/foaf/0.1/img",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
hasTelephone: {
"@id": "http://www.w3.org/2006/vcard/ns#hasTelephone",
"@type": "@id",
"@container": "@set",
},
phone: {
"@id": "http://www.w3.org/2006/vcard/ns#phone",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
organizationName: {
"@id": "http://www.w3.org/2006/vcard/ns#organization-name",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
role: {
"@id": "http://www.w3.org/2006/vcard/ns#role",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
trustedApp: {
"@id": "http://www.w3.org/ns/auth/acl#trustedApp",
"@type": "@id",
"@container": "@set",
},
mode: {
"@id": "http://www.w3.org/ns/auth/acl#mode",
"@container": "@set",
},
Append: "http://www.w3.org/ns/auth/acl#Append",
Control: "http://www.w3.org/ns/auth/acl#Control",
Read: "http://www.w3.org/ns/auth/acl#Read",
Write: "http://www.w3.org/ns/auth/acl#Write",
origin: {
"@id": "http://www.w3.org/ns/auth/acl#origin",
"@type": "@id",
},
key: {
"@id": "http://www.w3.org/ns/auth/cert#key",
"@type": "@id",
"@container": "@set",
},
modulus: {
"@id": "http://www.w3.org/ns/auth/cert#modulus",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
exponent: {
"@id": "http://www.w3.org/ns/auth/cert#exponent",
"@type": "http://www.w3.org/2001/XMLSchema#integer",
},
inbox: {
"@id": "http://www.w3.org/ns/ldp#inbox",
"@type": "@id",
},
preferencesFile: {
"@id": "http://www.w3.org/ns/pim/space#preferencesFile",
"@type": "@id",
},
storage: {
"@id": "http://www.w3.org/ns/pim/space#storage",
"@type": "@id",
"@container": "@set",
},
account: {
"@id": "http://www.w3.org/ns/solid/terms#account",
"@type": "@id",
},
privateTypeIndex: {
"@id": "http://www.w3.org/ns/solid/terms#privateTypeIndex",
"@type": "@id",
"@container": "@set",
},
publicTypeIndex: {
"@id": "http://www.w3.org/ns/solid/terms#publicTypeIndex",
"@type": "@id",
"@container": "@set",
},
knows: {
"@id": "http://xmlns.com/foaf/0.1/knows",
"@type": "@id",
"@container": "@set",
},
};

@ -1,749 +0,0 @@
import { Schema } from "shexj";
/**
* =============================================================================
* solidProfileSchema: ShexJ Schema for solidProfile
* =============================================================================
*/
export const solidProfileSchema: Schema = {
type: "Schema",
shapes: [
{
id: "https://shaperepo.com/schemas/solidProfile#SolidProfileShape",
type: "ShapeDecl",
shapeExpr: {
type: "Shape",
expression: {
type: "EachOf",
expressions: [
{
type: "TripleConstraint",
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
valueExpr: {
type: "NodeConstraint",
values: ["http://schema.org/Person"],
},
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "Defines the node as a Person (from Schema.org)",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
valueExpr: {
type: "NodeConstraint",
values: ["http://xmlns.com/foaf/0.1/Person"],
},
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "Defines the node as a Person (from foaf)",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#fn",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"The formatted name of a person. Example: John Smith",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://xmlns.com/foaf/0.1/name",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "An alternate way to define a person's name.",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#hasAddress",
valueExpr:
"https://shaperepo.com/schemas/solidProfile#AddressShape",
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The person's street address.",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#hasEmail",
valueExpr:
"https://shaperepo.com/schemas/solidProfile#EmailShape",
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The person's email.",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#hasPhoto",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "A link to the person's photo",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://xmlns.com/foaf/0.1/img",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "Photo link but in string form",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#hasTelephone",
valueExpr:
"https://shaperepo.com/schemas/solidProfile#PhoneNumberShape",
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "Person's telephone number",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#phone",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"An alternative way to define a person's telephone number using a string",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#organization-name",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"The name of the organization with which the person is affiliated",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#role",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"The name of the person's role in their organization",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/auth/acl#trustedApp",
valueExpr:
"https://shaperepo.com/schemas/solidProfile#TrustedAppShape",
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"A list of app origins that are trusted by this user",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/auth/cert#key",
valueExpr:
"https://shaperepo.com/schemas/solidProfile#RSAPublicKeyShape",
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"A list of RSA public keys that are associated with private keys the user holds.",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/ldp#inbox",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"The user's LDP inbox to which apps can post notifications",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/pim/space#preferencesFile",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The user's preferences",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/pim/space#storage",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"The location of a Solid storage server related to this WebId",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/solid/terms#account",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The user's account",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/solid/terms#privateTypeIndex",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"A registry of all types used on the user's Pod (for private access only)",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/solid/terms#publicTypeIndex",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"A registry of all types used on the user's Pod (for public access)",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://xmlns.com/foaf/0.1/knows",
valueExpr:
"https://shaperepo.com/schemas/solidProfile#SolidProfileShape",
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"A list of WebIds for all the people this user knows.",
},
},
],
},
],
},
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"],
},
},
{
id: "https://shaperepo.com/schemas/solidProfile#AddressShape",
type: "ShapeDecl",
shapeExpr: {
type: "Shape",
expression: {
type: "EachOf",
expressions: [
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#country-name",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The name of the user's country of residence",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#locality",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"The name of the user's locality (City, Town etc.) of residence",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#postal-code",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The user's postal code",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#region",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"The name of the user's region (State, Province etc.) of residence",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#street-address",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The user's street address",
},
},
],
},
],
},
},
},
{
id: "https://shaperepo.com/schemas/solidProfile#EmailShape",
type: "ShapeDecl",
shapeExpr: {
type: "Shape",
expression: {
type: "EachOf",
expressions: [
{
type: "TripleConstraint",
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
valueExpr: {
type: "NodeConstraint",
values: [
"http://www.w3.org/2006/vcard/ns#Dom",
"http://www.w3.org/2006/vcard/ns#Home",
"http://www.w3.org/2006/vcard/ns#ISDN",
"http://www.w3.org/2006/vcard/ns#Internet",
"http://www.w3.org/2006/vcard/ns#Intl",
"http://www.w3.org/2006/vcard/ns#Label",
"http://www.w3.org/2006/vcard/ns#Parcel",
"http://www.w3.org/2006/vcard/ns#Postal",
"http://www.w3.org/2006/vcard/ns#Pref",
"http://www.w3.org/2006/vcard/ns#Work",
"http://www.w3.org/2006/vcard/ns#X400",
],
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The type of email.",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#value",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"The value of an email as a mailto link (Example <mailto:jane@example.com>)",
},
},
],
},
],
},
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"],
},
},
{
id: "https://shaperepo.com/schemas/solidProfile#PhoneNumberShape",
type: "ShapeDecl",
shapeExpr: {
type: "Shape",
expression: {
type: "EachOf",
expressions: [
{
type: "TripleConstraint",
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
valueExpr: {
type: "NodeConstraint",
values: [
"http://www.w3.org/2006/vcard/ns#Dom",
"http://www.w3.org/2006/vcard/ns#Home",
"http://www.w3.org/2006/vcard/ns#ISDN",
"http://www.w3.org/2006/vcard/ns#Internet",
"http://www.w3.org/2006/vcard/ns#Intl",
"http://www.w3.org/2006/vcard/ns#Label",
"http://www.w3.org/2006/vcard/ns#Parcel",
"http://www.w3.org/2006/vcard/ns#Postal",
"http://www.w3.org/2006/vcard/ns#Pref",
"http://www.w3.org/2006/vcard/ns#Work",
"http://www.w3.org/2006/vcard/ns#X400",
],
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "They type of Phone Number",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/2006/vcard/ns#value",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"The value of a phone number as a tel link (Example <tel:555-555-5555>)",
},
},
],
},
],
},
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"],
},
},
{
id: "https://shaperepo.com/schemas/solidProfile#TrustedAppShape",
type: "ShapeDecl",
shapeExpr: {
type: "Shape",
expression: {
type: "EachOf",
expressions: [
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/auth/acl#mode",
valueExpr: {
type: "NodeConstraint",
values: [
"http://www.w3.org/ns/auth/acl#Append",
"http://www.w3.org/ns/auth/acl#Control",
"http://www.w3.org/ns/auth/acl#Read",
"http://www.w3.org/ns/auth/acl#Write",
],
},
min: 1,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The level of access provided to this origin",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/auth/acl#origin",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The app origin the user trusts",
},
},
],
},
],
},
},
},
{
id: "https://shaperepo.com/schemas/solidProfile#RSAPublicKeyShape",
type: "ShapeDecl",
shapeExpr: {
type: "Shape",
expression: {
type: "EachOf",
expressions: [
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/auth/cert#modulus",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "RSA Modulus",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/auth/cert#exponent",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#integer",
},
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "RSA Exponent",
},
},
],
},
],
},
},
},
],
};

@ -1,71 +0,0 @@
import { ShapeType } from "@ldo/ldo";
import { solidProfileSchema } from "./solidProfile.schema";
import { solidProfileContext } from "./solidProfile.context";
import {
SolidProfileShape,
AddressShape,
EmailShape,
PhoneNumberShape,
TrustedAppShape,
RSAPublicKeyShape,
} from "./solidProfile.typings";
/**
* =============================================================================
* LDO ShapeTypes solidProfile
* =============================================================================
*/
/**
* SolidProfileShape ShapeType
*/
export const SolidProfileShapeShapeType: ShapeType<SolidProfileShape> = {
schema: solidProfileSchema,
shape: "https://shaperepo.com/schemas/solidProfile#SolidProfileShape",
context: solidProfileContext,
};
/**
* AddressShape ShapeType
*/
export const AddressShapeShapeType: ShapeType<AddressShape> = {
schema: solidProfileSchema,
shape: "https://shaperepo.com/schemas/solidProfile#AddressShape",
context: solidProfileContext,
};
/**
* EmailShape ShapeType
*/
export const EmailShapeShapeType: ShapeType<EmailShape> = {
schema: solidProfileSchema,
shape: "https://shaperepo.com/schemas/solidProfile#EmailShape",
context: solidProfileContext,
};
/**
* PhoneNumberShape ShapeType
*/
export const PhoneNumberShapeShapeType: ShapeType<PhoneNumberShape> = {
schema: solidProfileSchema,
shape: "https://shaperepo.com/schemas/solidProfile#PhoneNumberShape",
context: solidProfileContext,
};
/**
* TrustedAppShape ShapeType
*/
export const TrustedAppShapeShapeType: ShapeType<TrustedAppShape> = {
schema: solidProfileSchema,
shape: "https://shaperepo.com/schemas/solidProfile#TrustedAppShape",
context: solidProfileContext,
};
/**
* RSAPublicKeyShape ShapeType
*/
export const RSAPublicKeyShapeShapeType: ShapeType<RSAPublicKeyShape> = {
schema: solidProfileSchema,
shape: "https://shaperepo.com/schemas/solidProfile#RSAPublicKeyShape",
context: solidProfileContext,
};

@ -1,293 +0,0 @@
import { ContextDefinition } from "jsonld";
/**
* =============================================================================
* Typescript Typings for solidProfile
* =============================================================================
*/
/**
* SolidProfileShape Type
*/
export interface SolidProfileShape {
"@id"?: string;
"@context"?: ContextDefinition;
/**
* Defines the node as a Person (from Schema.org) | Defines the node as a Person (from foaf)
*/
type: (
| {
"@id": "Person";
}
| {
"@id": "Person2";
}
)[];
/**
* The formatted name of a person. Example: John Smith
*/
fn?: string;
/**
* An alternate way to define a person's name.
*/
name?: string;
/**
* The person's street address.
*/
hasAddress?: AddressShape[];
/**
* The person's email.
*/
hasEmail?: EmailShape[];
/**
* A link to the person's photo
*/
hasPhoto?: {
"@id": string;
};
/**
* Photo link but in string form
*/
img?: string;
/**
* Person's telephone number
*/
hasTelephone?: PhoneNumberShape[];
/**
* An alternative way to define a person's telephone number using a string
*/
phone?: string;
/**
* The name of the organization with which the person is affiliated
*/
organizationName?: string;
/**
* The name of the person's role in their organization
*/
role?: string;
/**
* A list of app origins that are trusted by this user
*/
trustedApp?: TrustedAppShape[];
/**
* A list of RSA public keys that are associated with private keys the user holds.
*/
key?: RSAPublicKeyShape[];
/**
* The user's LDP inbox to which apps can post notifications
*/
inbox: {
"@id": string;
};
/**
* The user's preferences
*/
preferencesFile?: {
"@id": string;
};
/**
* The location of a Solid storage server related to this WebId
*/
storage?: {
"@id": string;
}[];
/**
* The user's account
*/
account?: {
"@id": string;
};
/**
* A registry of all types used on the user's Pod (for private access only)
*/
privateTypeIndex?: {
"@id": string;
}[];
/**
* A registry of all types used on the user's Pod (for public access)
*/
publicTypeIndex?: {
"@id": string;
}[];
/**
* A list of WebIds for all the people this user knows.
*/
knows?: SolidProfileShape[];
}
/**
* AddressShape Type
*/
export interface AddressShape {
"@id"?: string;
"@context"?: ContextDefinition;
/**
* The name of the user's country of residence
*/
countryName?: string;
/**
* The name of the user's locality (City, Town etc.) of residence
*/
locality?: string;
/**
* The user's postal code
*/
postalCode?: string;
/**
* The name of the user's region (State, Province etc.) of residence
*/
region?: string;
/**
* The user's street address
*/
streetAddress?: string;
}
/**
* EmailShape Type
*/
export interface EmailShape {
"@id"?: string;
"@context"?: ContextDefinition;
/**
* The type of email.
*/
type?:
| {
"@id": "Dom";
}
| {
"@id": "Home";
}
| {
"@id": "ISDN";
}
| {
"@id": "Internet";
}
| {
"@id": "Intl";
}
| {
"@id": "Label";
}
| {
"@id": "Parcel";
}
| {
"@id": "Postal";
}
| {
"@id": "Pref";
}
| {
"@id": "Work";
}
| {
"@id": "X400";
};
/**
* The value of an email as a mailto link (Example <mailto:jane@example.com>)
*/
value: {
"@id": string;
};
}
/**
* PhoneNumberShape Type
*/
export interface PhoneNumberShape {
"@id"?: string;
"@context"?: ContextDefinition;
/**
* They type of Phone Number
*/
type?:
| {
"@id": "Dom";
}
| {
"@id": "Home";
}
| {
"@id": "ISDN";
}
| {
"@id": "Internet";
}
| {
"@id": "Intl";
}
| {
"@id": "Label";
}
| {
"@id": "Parcel";
}
| {
"@id": "Postal";
}
| {
"@id": "Pref";
}
| {
"@id": "Work";
}
| {
"@id": "X400";
};
/**
* The value of a phone number as a tel link (Example <tel:555-555-5555>)
*/
value: {
"@id": string;
};
}
/**
* TrustedAppShape Type
*/
export interface TrustedAppShape {
"@id"?: string;
"@context"?: ContextDefinition;
/**
* The level of access provided to this origin
*/
mode: (
| {
"@id": "Append";
}
| {
"@id": "Control";
}
| {
"@id": "Read";
}
| {
"@id": "Write";
}
)[];
/**
* The app origin the user trusts
*/
origin: {
"@id": string;
};
}
/**
* RSAPublicKeyShape Type
*/
export interface RSAPublicKeyShape {
"@id"?: string;
"@context"?: ContextDefinition;
/**
* RSA Modulus
*/
modulus: string;
/**
* RSA Exponent
*/
exponent: number;
}

@ -1,23 +0,0 @@
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX ex: <https://example.com/>
BASE <http://schema.org/>
ex:PostSh {
a [<SocialMediaPosting> <CreativeWork> <Thing>] ;
<articleBody> xsd:string?
// rdfs:label '''articleBody'''
// rdfs:comment '''The actual body of the article. ''' ;
<uploadDate> xsd:date
// rdfs:label '''uploadDate'''
// rdfs:comment '''Date when this media object was uploaded to this site.''' ;
<image> IRI ?
// rdfs:label '''image'''
// rdfs:comment '''A media object that encodes this CreativeWork. This property is a synonym for encoding.''' ;
<publisher> IRI
// rdfs:label '''publisher'''
// rdfs:comment '''The publisher of the creative work.''' ;
}
// rdfs:label '''SocialMediaPost'''
// rdfs:comment '''A post to a social media platform, including blog posts, tweets, Facebook posts, etc.'''

@ -1,121 +0,0 @@
PREFIX srs: <https://shaperepo.com/schemas/solidProfile#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX schem: <http://schema.org/>
PREFIX vcard: <http://www.w3.org/2006/vcard/ns#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX acl: <http://www.w3.org/ns/auth/acl#>
PREFIX cert: <http://www.w3.org/ns/auth/cert#>
PREFIX ldp: <http://www.w3.org/ns/ldp#>
PREFIX sp: <http://www.w3.org/ns/pim/space#>
PREFIX solid: <http://www.w3.org/ns/solid/terms#>
srs:SolidProfileShape EXTRA a {
a [ schem:Person ]
// rdfs:comment "Defines the node as a Person (from Schema.org)" ;
a [ foaf:Person ]
// rdfs:comment "Defines the node as a Person (from foaf)" ;
vcard:fn xsd:string ?
// rdfs:comment "The formatted name of a person. Example: John Smith" ;
foaf:name xsd:string ?
// rdfs:comment "An alternate way to define a person's name." ;
vcard:hasAddress @srs:AddressShape *
// rdfs:comment "The person's street address." ;
vcard:hasEmail @srs:EmailShape *
// rdfs:comment "The person's email." ;
vcard:hasPhoto IRI ?
// rdfs:comment "A link to the person's photo" ;
foaf:img xsd:string ?
// rdfs:comment "Photo link but in string form" ;
vcard:hasTelephone @srs:PhoneNumberShape *
// rdfs:comment "Person's telephone number" ;
vcard:phone xsd:string ?
// rdfs:comment "An alternative way to define a person's telephone number using a string" ;
vcard:organization-name xsd:string ?
// rdfs:comment "The name of the organization with which the person is affiliated" ;
vcard:role xsd:string ?
// rdfs:comment "The name of the person's role in their organization" ;
acl:trustedApp @srs:TrustedAppShape *
// rdfs:comment "A list of app origins that are trusted by this user" ;
cert:key @srs:RSAPublicKeyShape *
// rdfs:comment "A list of RSA public keys that are associated with private keys the user holds." ;
ldp:inbox IRI
// rdfs:comment "The user's LDP inbox to which apps can post notifications" ;
sp:preferencesFile IRI ?
// rdfs:comment "The user's preferences" ;
sp:storage IRI *
// rdfs:comment "The location of a Solid storage server related to this WebId" ;
solid:account IRI ?
// rdfs:comment "The user's account" ;
solid:privateTypeIndex IRI *
// rdfs:comment "A registry of all types used on the user's Pod (for private access only)" ;
solid:publicTypeIndex IRI *
// rdfs:comment "A registry of all types used on the user's Pod (for public access)" ;
foaf:knows @srs:SolidProfileShape *
// rdfs:comment "A list of WebIds for all the people this user knows." ;
}
srs:AddressShape {
vcard:country-name xsd:string ?
// rdfs:comment "The name of the user's country of residence" ;
vcard:locality xsd:string ?
// rdfs:comment "The name of the user's locality (City, Town etc.) of residence" ;
vcard:postal-code xsd:string ?
// rdfs:comment "The user's postal code" ;
vcard:region xsd:string ?
// rdfs:comment "The name of the user's region (State, Province etc.) of residence" ;
vcard:street-address xsd:string ?
// rdfs:comment "The user's street address" ;
}
srs:EmailShape EXTRA a {
a [
vcard:Dom
vcard:Home
vcard:ISDN
vcard:Internet
vcard:Intl
vcard:Label
vcard:Parcel
vcard:Postal
vcard:Pref
vcard:Work
vcard:X400
] ?
// rdfs:comment "The type of email." ;
vcard:value IRI
// rdfs:comment "The value of an email as a mailto link (Example <mailto:jane@example.com>)" ;
}
srs:PhoneNumberShape EXTRA a {
a [
vcard:Dom
vcard:Home
vcard:ISDN
vcard:Internet
vcard:Intl
vcard:Label
vcard:Parcel
vcard:Postal
vcard:Pref
vcard:Work
vcard:X400
] ?
// rdfs:comment "They type of Phone Number" ;
vcard:value IRI
// rdfs:comment "The value of a phone number as a tel link (Example <tel:555-555-5555>)" ;
}
srs:TrustedAppShape {
acl:mode [acl:Append acl:Control acl:Read acl:Write] +
// rdfs:comment "The level of access provided to this origin" ;
acl:origin IRI
// rdfs:comment "The app origin the user trusts"
}
srs:RSAPublicKeyShape {
cert:modulus xsd:string
// rdfs:comment "RSA Modulus" ;
cert:exponent xsd:integer
// rdfs:comment "RSA Exponent" ;
}

@ -1,13 +0,0 @@
import type { FunctionComponent } from "react";
import React from "react";
import { Router } from "./Layout";
import { BrowserSolidLdoProvider } from "@ldo/solid-react";
const ProfileApp: FunctionComponent = () => {
return (
<BrowserSolidLdoProvider>
<Router />
</BrowserSolidLdoProvider>
);
};
export default ProfileApp;

@ -1,65 +0,0 @@
import type { FunctionComponent } from "react";
import React, { useCallback } from "react";
import {
BrowserSolidLdoProvider,
useResource,
useSolidAuth,
useSubject,
} from "@ldo/solid-react";
import { SolidProfileShapeShapeType } from "./.ldo/solidProfile.shapeTypes";
import { changeData, commitData } from "@ldo/solid";
// The base component for the app
const App: FunctionComponent = () => {
return (
/* The application should be surrounded with the BrowserSolidLdoProvider
this will set up all the underlying infrastructure for the application */
<BrowserSolidLdoProvider>
<Login />
</BrowserSolidLdoProvider>
);
};
// A component that handles login
const Login: FunctionComponent = () => {
// Get login information using the "useSolidAuth" hook
const { login, logout, session } = useSolidAuth();
const onLogin = useCallback(() => {
const issuer = prompt("What is your Solid IDP?");
// Call the "login" function to initiate login
if (issuer) login(issuer);
}, []);
// You can use session.isLoggedIn to check if the user is logged in
if (session.isLoggedIn) {
return (
<div>
{/* Get the user's webId from session.webId */}
<p>Logged in as {session.webId}</p>
{/* Use the logout function to log out */}
<button onClick={logout}>Log Out</button>
<Profile />
</div>
);
}
return <button onClick={onLogin}>Log In</button>;
};
const Profile: FunctionComponent = () => {
const { session } = useSolidAuth();
const resource = useResource(session.webId);
const profile = useSubject(SolidProfileShapeShapeType, session.webId);
const onNameChange = useCallback(async (e) => {
// Ensure that the
if (!profile || !resource) return;
const cProfile = changeData(profile, resource);
cProfile.name = e.target.value;
await commitData(cProfile);
}, []);
return <input type="text" value={profile?.name} onChange={onNameChange} />;
};
export default App;

@ -1,54 +0,0 @@
import { useState } from "react";
import type { FunctionComponent } from "react";
import React from "react";
import { useResource, useSolidAuth, useSubject } from "@ldo/solid-react";
import { SolidProfileShapeShapeType } from "./.ldo/solidProfile.shapeTypes";
import { Link } from "react-router-dom";
const DEFAULT_ISSUER = "https://solidweb.me";
export const LoggedInHeader: FunctionComponent<{ webId: string }> = ({
webId,
}) => {
const webIdResource = useResource(webId);
const profile = useSubject(SolidProfileShapeShapeType, webId);
const { logout } = useSolidAuth();
return (
<>
<span>
Logged in as {webId}. Welcome{" "}
{webIdResource.isReading() ? "LOADING NAME" : profile.fn}
</span>
<button onClick={logout}>Log Out</button>
</>
);
};
export const Header: FunctionComponent = () => {
const [issuer, setIssuer] = useState(DEFAULT_ISSUER);
const { login, signUp, session } = useSolidAuth();
return (
<header>
<div style={{ display: "flex" }}>
{session.isLoggedIn ? (
<LoggedInHeader webId={session.webId!} />
) : (
<>
<input
type="text"
value={issuer}
onChange={(e) => setIssuer(e.target.value)}
/>
<button onClick={() => login(issuer)}>Log In</button>
<button onClick={() => signUp(issuer)}>Sign Up</button>
</>
)}
</div>
<p>
<Link to="/">Blog</Link>
{" "}
<Link to="/profile">Profile</Link>
</p>
</header>
);
};

@ -1,50 +0,0 @@
import { useSolidAuth } from "@ldo/solid-react";
import React, { Fragment } from "react";
import type { FunctionComponent } from "react";
import { createBrowserRouter, Outlet, RouterProvider } from "react-router-dom";
import { Blog } from "./blog/Blog";
import { PostPage } from "./post/PostPage";
import { Header } from "./Header";
import { MainContainerProvider } from "./MainContainerProvider";
import { Profile } from "./profile/Profile";
export const Layout: FunctionComponent = () => {
const { session } = useSolidAuth();
return (
<div>
<Header />
<hr />
<MainContainerProvider>
{session.isLoggedIn ? <Outlet /> : <Fragment />}
</MainContainerProvider>
</div>
);
};
const router = createBrowserRouter([
{
element: <Layout />,
children: [
{
path: "/",
element: <Blog />,
},
{
path: "/media/:uri",
element: <PostPage />,
},
{
path: "/profile",
element: <Profile />,
},
],
},
]);
export const Router: FunctionComponent = () => {
const { ranInitialAuthCheck } = useSolidAuth();
if (!ranInitialAuthCheck) {
return <p>Loading</p>;
}
return <RouterProvider router={router} />;
};

@ -1,67 +0,0 @@
import React, { useState, useEffect, createContext } from "react";
import type { FunctionComponent, PropsWithChildren } from "react";
import type { Container, LeafUri } from "@ldo/solid";
import { useSolidAuth, useLdo, useResource } from "@ldo/solid-react";
export const MainContainerContext = createContext<Container | undefined>(
undefined,
);
const MainContainerSubProvider: FunctionComponent<
PropsWithChildren<{ uri?: string }>
> = ({ uri, children }) => {
const mainContainer = useResource(uri);
return (
<MainContainerContext.Provider value={mainContainer as Container}>
{children}
</MainContainerContext.Provider>
);
};
export const MainContainerProvider: FunctionComponent<PropsWithChildren> = ({
children,
}) => {
const [mainContainer, setMainContainer] = useState<Container | undefined>();
const { session } = useSolidAuth();
const { getResource } = useLdo();
useEffect(() => {
if (session.webId) {
const webIdResource = getResource(session.webId as LeafUri);
webIdResource.getRootContainer().then(async (rootContainer) => {
if (rootContainer.isError) {
alert(rootContainer.message);
return;
}
const mainContainer = getResource(`${rootContainer.uri}demo-react/`);
setMainContainer(mainContainer);
const createResult = await mainContainer.createIfAbsent();
// Only set the access rules if the create was a success.
if (createResult.type === "createSuccess") {
await mainContainer.setAccessRules({
public: {
read: true,
write: false,
append: false,
control: false,
},
agent: {
[session.webId!]: {
read: true,
write: true,
append: true,
control: true,
},
},
});
}
});
}
}, [session.webId]);
return (
<MainContainerSubProvider uri={mainContainer?.uri}>
{children}
</MainContainerSubProvider>
);
};

@ -1,30 +0,0 @@
import React, { Fragment, useContext } from "react";
import type { FunctionComponent } from "react";
import { MainContainerContext } from "../MainContainerProvider";
import { Post } from "../post/Post";
import { MakePost } from "./MakePost";
export const Blog: FunctionComponent = () => {
const mainContainer = useContext(MainContainerContext);
if (mainContainer === undefined) {
return <p>Loading...</p>;
}
if (mainContainer.isDoingInitialFetch()) {
return <p>Loading Blob</p>;
}
return (
<div>
<div>
<MakePost mainContainer={mainContainer} />
</div>
<hr />
{mainContainer.children().map((child) => (
<Fragment key={child.uri}>
<Post uri={child.uri} />
<hr />
</Fragment>
))}
</div>
);
};

@ -1,92 +0,0 @@
import React, { useCallback, useState, useRef } from "react";
import type { FunctionComponent, FormEvent } from "react";
import type { Container, Leaf, LeafUri } from "@ldo/solid";
import { v4 } from "uuid";
import { useLdo, useSolidAuth } from "@ldo/solid-react";
import { PostShShapeType } from "../.ldo/post.shapeTypes";
export const MakePost: FunctionComponent<{ mainContainer: Container }> = ({
mainContainer,
}) => {
const [message, setMessage] = useState("");
const [selectedFile, setSelectedFile] = useState<File | undefined>();
const fileInputRef = useRef<HTMLInputElement | null>(null);
const { createData, commitData } = useLdo();
const { session } = useSolidAuth();
const onSubmit = useCallback(
async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
// Create the container file
const mediaContainerResult = await mainContainer.createChildAndOverwrite(
`${v4()}/`,
);
if (mediaContainerResult.isError) {
alert(mediaContainerResult.message);
return;
}
const mediaContainer = mediaContainerResult.resource;
// Upload Image
let uploadedImage: Leaf | undefined;
if (selectedFile) {
const result = await mediaContainer.uploadChildAndOverwrite(
selectedFile.name as LeafUri,
selectedFile,
selectedFile.type,
);
if (result.isError) {
alert(result.message);
await mediaContainer.delete();
return;
}
uploadedImage = result.resource;
}
// Create Post
const indexResource = mediaContainer.child("index.ttl");
const post = createData(
PostShShapeType,
indexResource.uri,
indexResource,
);
post.articleBody = message;
if (uploadedImage) {
post.image = { "@id": uploadedImage.uri };
}
if (session.webId) {
post.publisher = { "@id": session.webId };
}
post.type = { "@id": "SocialMediaPosting" };
post.uploadDate = new Date().toISOString();
const result = await commitData(post);
if (result.isError) {
alert(result.message);
}
// Clear the UI after Upload
setMessage("");
setSelectedFile(undefined);
if (fileInputRef.current) fileInputRef.current.value = "";
},
[message, selectedFile, session.webId],
);
return (
<form onSubmit={onSubmit}>
<input
type="text"
placeholder="Make a Post"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<input
type="file"
accept="image/*"
ref={fileInputRef}
onChange={(e) => setSelectedFile(e.target.files?.[0])}
/>
<input type="submit" value="Post" />
</form>
);
};

@ -1,8 +0,0 @@
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement,
);
root.render(<App />);

@ -1,42 +0,0 @@
import React, { useCallback } from "react";
import type { FunctionComponent } from "react";
import { useLdo, useResource, useSubject } from "@ldo/solid-react";
import { PostShShapeType } from "../.ldo/post.shapeTypes";
import { useNavigate } from "react-router-dom";
import { PostedBy } from "./PostedBy";
export const Post: FunctionComponent<{ uri: string }> = ({ uri }) => {
const navigate = useNavigate();
const mediaResource = useResource(`${uri}index.ttl`);
const post = useSubject(PostShShapeType, mediaResource.uri);
const { getResource } = useLdo();
const deletePost = useCallback(async () => {
const postContainer = getResource(uri);
const result = await postContainer.delete();
if (result.isError) {
alert(result.message);
}
}, [uri]);
if (mediaResource.isReading()) {
return <p>Loading Post...</p>;
} else if (mediaResource.status.isError) {
return <p>Error: {mediaResource.status.message}</p>;
} else if (mediaResource.isAbsent()) {
return <p>Post does not exist.</p>;
}
return (
<div>
{post.publisher?.["@id"] && <PostedBy webId={post.publisher["@id"]} />}
<div
onClick={() => navigate(`/media/${encodeURIComponent(uri)}`)}
style={{ cursor: "pointer" }}
>
{post.articleBody && <p>{post.articleBody}</p>}
{post.image && <img src={post.image["@id"]} style={{ height: 300 }} />}
</div>
<button onClick={deletePost}>Delete Post</button>
</div>
);
};

@ -1,16 +0,0 @@
import React from "react";
import type { FunctionComponent } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Post } from "./Post";
export const PostPage: FunctionComponent = () => {
const navigate = useNavigate();
const { uri } = useParams();
return (
<div>
<button onClick={() => navigate("/")}>Back to Feed</button>
{uri ? <Post uri={uri} /> : <p>No URI Present</p>}
</div>
);
};

@ -1,16 +0,0 @@
import type { FunctionComponent } from "react";
import React from "react";
import { useResource, useSubject } from "@ldo/solid-react";
import { SolidProfileShapeShapeType } from "../.ldo/solidProfile.shapeTypes";
export const PostedBy: FunctionComponent<{ webId: string }> = ({ webId }) => {
const webIdResource = useResource(webId);
const profile = useSubject(SolidProfileShapeShapeType, webId);
if (webIdResource.isReading()) {
return <p>Loading Profile...</p>;
} else if (webIdResource.status.isError) {
return <p>Error: {webIdResource.status.message}</p>;
}
return <p>Posted By: {profile.fn}</p>;
};

@ -1,34 +0,0 @@
import {
useLdo,
useResource,
useSolidAuth,
useSubject,
} from "@ldo/solid-react";
import type { ChangeEvent } from "react";
import React, { useCallback, type FunctionComponent } from "react";
import { SolidProfileShapeShapeType } from "../.ldo/solidProfile.shapeTypes";
export const Profile: FunctionComponent = () => {
const { session } = useSolidAuth();
const profile = useSubject(SolidProfileShapeShapeType, session.webId);
const webIdResource = useResource(session.webId);
const { changeData, commitData } = useLdo();
const onNameChange = useCallback(
async (e: ChangeEvent<HTMLInputElement>) => {
if (profile && webIdResource) {
const cProfile = changeData(profile, webIdResource);
cProfile.fn = e.target.value;
await commitData(cProfile);
}
},
[profile, webIdResource],
);
return (
<>
<label>Name</label>
<input type="text" value={profile?.fn || ""} onChange={onNameChange} />
</>
);
};

@ -1,20 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"./src"
]
}

@ -1,6 +1,6 @@
{
"name": "@ldo/jsonld-dataset-proxy",
"version": "0.0.1-alpha.19",
"version": "0.0.1-alpha.23",
"description": "",
"main": "dist/index.js",
"scripts": {
@ -40,8 +40,8 @@
"src"
],
"dependencies": {
"@ldo/rdf-utils": "^0.0.1-alpha.17",
"@ldo/subscribable-dataset": "^0.0.1-alpha.19",
"@ldo/rdf-utils": "^0.0.1-alpha.23",
"@ldo/subscribable-dataset": "^0.0.1-alpha.23",
"@rdfjs/data-model": "^1.2.0",
"@rdfjs/dataset": "^1.1.0",
"jsonld2graphobject": "^0.0.4"
@ -49,5 +49,5 @@
"publishConfig": {
"access": "public"
},
"gitHead": "4548985c0de9b0ec83cf5ee93f2d7c1ca2c1b8d8"
"gitHead": "d2364cd2f8da5f0b673b1202d29df5b7c071a17c"
}

@ -1,6 +1,6 @@
{
"name": "@ldo/ldo",
"version": "0.0.1-alpha.19",
"version": "0.0.1-alpha.23",
"description": "",
"main": "dist/index.js",
"scripts": {
@ -23,7 +23,7 @@
},
"homepage": "https://github.com/o-development/ldobjects/tree/main/packages/ldo#readme",
"devDependencies": {
"@ldo/rdf-utils": "^0.0.1-alpha.17",
"@ldo/rdf-utils": "^0.0.1-alpha.23",
"@rdfjs/types": "^1.0.1",
"@types/jest": "^27.0.3",
"@types/jsonld": "^1.5.6",
@ -38,9 +38,9 @@
"typedoc-plugin-markdown": "^3.17.1"
},
"dependencies": {
"@ldo/dataset": "^0.0.1-alpha.17",
"@ldo/jsonld-dataset-proxy": "^0.0.1-alpha.19",
"@ldo/subscribable-dataset": "^0.0.1-alpha.19",
"@ldo/dataset": "^0.0.1-alpha.23",
"@ldo/jsonld-dataset-proxy": "^0.0.1-alpha.23",
"@ldo/subscribable-dataset": "^0.0.1-alpha.23",
"@rdfjs/data-model": "^1.2.0",
"buffer": "^6.0.3",
"readable-stream": "^4.3.0"
@ -55,5 +55,5 @@
"publishConfig": {
"access": "public"
},
"gitHead": "4548985c0de9b0ec83cf5ee93f2d7c1ca2c1b8d8"
"gitHead": "d2364cd2f8da5f0b673b1202d29df5b7c071a17c"
}

@ -1,6 +1,6 @@
{
"name": "@ldo/rdf-utils",
"version": "0.0.1-alpha.17",
"version": "0.0.1-alpha.23",
"description": "Some RDF Utilities to support LDO librariers",
"main": "dist/index.js",
"scripts": {
@ -24,7 +24,7 @@
"devDependencies": {
"@rdfjs/types": "^1.0.1",
"@types/jsonld": "^1.5.9",
"ts-jest": "^29.1.1"
"ts-jest": "^27.1.2"
},
"dependencies": {
"@rdfjs/data-model": "^1.2.0",
@ -38,5 +38,5 @@
"publishConfig": {
"access": "public"
},
"gitHead": "4548985c0de9b0ec83cf5ee93f2d7c1ca2c1b8d8"
"gitHead": "d2364cd2f8da5f0b673b1202d29df5b7c071a17c"
}

@ -1,6 +1,6 @@
{
"name": "@ldo/schema-converter-shex",
"version": "0.0.1-alpha.17",
"version": "0.0.1-alpha.23",
"description": "",
"main": "dist/index.js",
"scripts": {
@ -33,12 +33,12 @@
"dist"
],
"dependencies": {
"@ldo/traverser-shexj": "^0.0.1-alpha.17",
"@ldo/traverser-shexj": "^0.0.1-alpha.23",
"dts-dom": "^3.6.0",
"jsonld2graphobject": "^0.0.5"
},
"publishConfig": {
"access": "public"
},
"gitHead": "4548985c0de9b0ec83cf5ee93f2d7c1ca2c1b8d8"
"gitHead": "d2364cd2f8da5f0b673b1202d29df5b7c071a17c"
}

@ -1,6 +1,6 @@
{
"name": "@ldo/solid-react",
"version": "0.0.1-alpha.19",
"version": "0.0.1-alpha.23",
"description": "A React library for LDO and Solid",
"main": "dist/index.js",
"scripts": {
@ -26,22 +26,22 @@
},
"homepage": "https://github.com/o-development/ldobjects/tree/main/packages/solid-react#readme",
"devDependencies": {
"@ldo/rdf-utils": "^0.0.1-alpha.17",
"@ldo/rdf-utils": "^0.0.1-alpha.23",
"@rdfjs/types": "^1.0.1",
"@testing-library/react": "^14.1.2",
"@types/jest": "^29.0.3",
"jest-environment-jsdom": "^29.7.0",
"@types/jest": "^27.0.3",
"jest-environment-jsdom": "^27.0.0",
"start-server-and-test": "^2.0.3",
"ts-jest": "^29.1.2",
"ts-jest": "^27.1.2",
"ts-node": "^10.9.2"
},
"dependencies": {
"@inrupt/solid-client": "^2.0.0",
"@ldo/dataset": "^0.0.1-alpha.17",
"@ldo/jsonld-dataset-proxy": "^0.0.1-alpha.19",
"@ldo/ldo": "^0.0.1-alpha.19",
"@ldo/solid": "^0.0.1-alpha.19",
"@ldo/subscribable-dataset": "^0.0.1-alpha.19",
"@inrupt/solid-client-authn-browser": "^2.0.0",
"@ldo/dataset": "^0.0.1-alpha.23",
"@ldo/jsonld-dataset-proxy": "^0.0.1-alpha.23",
"@ldo/ldo": "^0.0.1-alpha.23",
"@ldo/solid": "^0.0.1-alpha.23",
"@ldo/subscribable-dataset": "^0.0.1-alpha.23",
"@rdfjs/data-model": "^1.2.0",
"cross-fetch": "^3.1.6"
},
@ -52,5 +52,5 @@
"publishConfig": {
"access": "public"
},
"gitHead": "1c242d645fc488f8d0e9f4da7425d9928abf1e9d"
"gitHead": "d2364cd2f8da5f0b673b1202d29df5b7c071a17c"
}

@ -135,9 +135,7 @@ describe("Integration Tests", () => {
const rootContainer = useRootContainerFor(SAMPLE_DATA_URI, {
suppressInitialRead: true,
});
return rootContainer ? (
<p role="root">{rootContainer?.uri}</p>
) : undefined;
return rootContainer ? <p role="root">{rootContainer?.uri}</p> : <></>;
};
render(
<UnauthenticatedSolidLdoProvider>
@ -181,7 +179,7 @@ describe("Integration Tests", () => {
);
setSubject(someSubject);
}, []);
return subject ? <p role="subject">{subject["@id"]}</p> : undefined;
return subject ? <p role="subject">{subject["@id"]}</p> : <></>;
};
render(
<UnauthenticatedSolidLdoProvider>
@ -205,9 +203,7 @@ describe("Integration Tests", () => {
someSubject.articleBody = "Cool Article";
setSubject(someSubject);
}, []);
return subject ? (
<p role="subject">{subject.articleBody}</p>
) : undefined;
return subject ? <p role="subject">{subject.articleBody}</p> : <></>;
};
render(
<UnauthenticatedSolidLdoProvider>

@ -1,6 +1,6 @@
{
"name": "@ldo/solid",
"version": "0.0.1-alpha.19",
"version": "0.0.1-alpha.23",
"description": "A library for LDO and Solid",
"main": "dist/index.js",
"scripts": {
@ -26,28 +26,25 @@
"homepage": "https://github.com/o-development/ldobjects/tree/main/packages/solid#readme",
"devDependencies": {
"@inrupt/solid-client-authn-core": "^1.17.1",
"@ldo/cli": "^0.0.1-alpha.19",
"@ldo/cli": "^0.0.1-alpha.23",
"@rdfjs/data-model": "^1.2.0",
"@rdfjs/types": "^1.0.1",
"@solid/community-server": "^6.0.2",
"@types/jest": "^29.0.3",
"@types/jest": "^27.0.3",
"dotenv": "^16.3.1",
"jest-rdf": "^1.8.0",
"ts-jest": "^29.0.2",
"ts-jest": "^27.1.2",
"ts-node": "^10.9.1",
"typed-emitter": "^2.1.0",
"typedoc": "^0.25.4",
"typedoc-plugin-markdown": "^3.17.1"
},
"dependencies": {
"@inrupt/solid-client": "^1.30.0",
"@ldo/dataset": "^0.0.1-alpha.17",
"@ldo/ldo": "^0.0.1-alpha.19",
"@ldo/rdf-utils": "^0.0.1-alpha.17",
"@types/parse-link-header": "^2.0.1",
"@ldo/dataset": "^0.0.1-alpha.23",
"@ldo/ldo": "^0.0.1-alpha.23",
"@ldo/rdf-utils": "^0.0.1-alpha.23",
"cross-fetch": "^3.1.6",
"http-link-header": "^1.1.1",
"ts-mixer": "^6.0.3"
"http-link-header": "^1.1.1"
},
"files": [
"dist",
@ -56,5 +53,5 @@
"publishConfig": {
"access": "public"
},
"gitHead": "1c242d645fc488f8d0e9f4da7425d9928abf1e9d"
"gitHead": "d2364cd2f8da5f0b673b1202d29df5b7c071a17c"
}

@ -0,0 +1,45 @@
import { ContextDefinition } from "jsonld";
/**
* =============================================================================
* wacContext: JSONLD Context for wac
* =============================================================================
*/
export const wacContext: ContextDefinition = {
type: {
"@id": "@type",
},
Authorization: "http://www.w3.org/ns/auth/acl#Authorization",
accessTo: {
"@id": "http://www.w3.org/ns/auth/acl#accessTo",
"@type": "@id",
},
default: {
"@id": "http://www.w3.org/ns/auth/acl#default",
"@type": "@id",
},
agent: {
"@id": "http://www.w3.org/ns/auth/acl#agent",
"@type": "@id",
"@container": "@set",
},
agentGroup: {
"@id": "http://www.w3.org/ns/auth/acl#agentGroup",
"@type": "@id",
"@container": "@set",
},
agentClass: {
"@id": "http://www.w3.org/ns/auth/acl#agentClass",
"@container": "@set",
},
AuthenticatedAgent: "http://www.w3.org/ns/auth/acl#AuthenticatedAgent",
Agent: "http://xmlns.com/foaf/0.1/Agent",
mode: {
"@id": "http://www.w3.org/ns/auth/acl#mode",
"@container": "@set",
},
Read: "http://www.w3.org/ns/auth/acl#Read",
Write: "http://www.w3.org/ns/auth/acl#Write",
Append: "http://www.w3.org/ns/auth/acl#Append",
Control: "http://www.w3.org/ns/auth/acl#Control",
};

@ -2,18 +2,19 @@ import { Schema } from "shexj";
/**
* =============================================================================
* postSchema: ShexJ Schema for post
* wacSchema: ShexJ Schema for wac
* =============================================================================
*/
export const postSchema: Schema = {
export const wacSchema: Schema = {
type: "Schema",
shapes: [
{
id: "https://example.com/PostSh",
id: "http://www.w3.org/ns/auth/acls#Authorization",
type: "ShapeDecl",
shapeExpr: {
type: "Shape",
expression: {
id: "http://www.w3.org/ns/auth/acls#AuthorizationShape",
type: "EachOf",
expressions: [
{
@ -21,135 +22,148 @@ export const postSchema: Schema = {
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
valueExpr: {
type: "NodeConstraint",
values: [
"http://schema.org/SocialMediaPosting",
"http://schema.org/CreativeWork",
"http://schema.org/Thing",
],
values: ["http://www.w3.org/ns/auth/acl#Authorization"],
},
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "Denotes this as an acl:Authorization",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://schema.org/articleBody",
predicate: "http://www.w3.org/ns/auth/acl#accessTo",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
nodeKind: "iri",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#label",
object: {
value: "articleBody",
},
},
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The actual body of the article. ",
value: "The subject of this authorization",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://schema.org/uploadDate",
predicate: "http://www.w3.org/ns/auth/acl#default",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#date",
nodeKind: "iri",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#label",
object: {
value: "uploadDate",
},
},
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"Date when this media object was uploaded to this site.",
value: "The container subject of this authorization",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://schema.org/image",
predicate: "http://www.w3.org/ns/auth/acl#agent",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
min: 0,
max: 1,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#label",
object: {
value: "image",
},
},
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"A media object that encodes this CreativeWork. This property is a synonym for encoding.",
"An agent is a person, social entity or software identified by a URI, e.g., a WebID denotes an agent",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://schema.org/publisher",
predicate: "http://www.w3.org/ns/auth/acl#agentGroup",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#label",
object: {
value: "publisher",
},
},
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The publisher of the creative work.",
value:
"Denotes a group of agents being given the access permission",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/auth/acl#agentClass",
valueExpr: {
type: "NodeConstraint",
values: [
"http://www.w3.org/ns/auth/acl#AuthenticatedAgent",
"http://xmlns.com/foaf/0.1/Agent",
],
},
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#label",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "SocialMediaPost",
value:
"An agent class is a class of persons or entities identified by a URI.",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/auth/acl#mode",
valueExpr: {
type: "NodeConstraint",
values: [
"http://www.w3.org/ns/auth/acl#Read",
"http://www.w3.org/ns/auth/acl#Write",
"http://www.w3.org/ns/auth/acl#Append",
"http://www.w3.org/ns/auth/acl#Control",
],
},
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"A post to a social media platform, including blog posts, tweets, Facebook posts, etc.",
"Denotes a class of operations that the agents can perform on a resource.",
},
},
],
},
],
},
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"],
},
},
],
};

@ -0,0 +1,19 @@
import { ShapeType } from "@ldo/ldo";
import { wacSchema } from "./wac.schema";
import { wacContext } from "./wac.context";
import { Authorization } from "./wac.typings";
/**
* =============================================================================
* LDO ShapeTypes wac
* =============================================================================
*/
/**
* Authorization ShapeType
*/
export const AuthorizationShapeType: ShapeType<Authorization> = {
schema: wacSchema,
shape: "http://www.w3.org/ns/auth/acls#Authorization",
context: wacContext,
};

@ -0,0 +1,73 @@
import { ContextDefinition } from "jsonld";
/**
* =============================================================================
* Typescript Typings for wac
* =============================================================================
*/
/**
* Authorization Type
*/
export interface Authorization {
"@id"?: string;
"@context"?: ContextDefinition;
/**
* Denotes this as an acl:Authorization
*/
type: {
"@id": "Authorization";
};
/**
* The subject of this authorization
*/
accessTo?: {
"@id": string;
};
/**
* The container subject of this authorization
*/
default?: {
"@id": string;
};
/**
* An agent is a person, social entity or software identified by a URI, e.g., a WebID denotes an agent
*/
agent?: {
"@id": string;
}[];
/**
* Denotes a group of agents being given the access permission
*/
agentGroup?: {
"@id": string;
}[];
/**
* An agent class is a class of persons or entities identified by a URI.
*/
agentClass?: (
| {
"@id": "AuthenticatedAgent";
}
| {
"@id": "Agent";
}
)[];
/**
* Denotes a class of operations that the agents can perform on a resource.
*/
mode?: (
| {
"@id": "Read";
}
| {
"@id": "Write";
}
| {
"@id": "Append";
}
| {
"@id": "Control";
}
)[];
}

@ -0,0 +1,23 @@
PREFIX acl: <http://www.w3.org/ns/auth/acl#>
PREFIX acls: <http://www.w3.org/ns/auth/acls#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
acls:Authorization EXTRA a {
$acls:AuthorizationShape (
a [ acl:Authorization ]
// rdfs:comment "Denotes this as an acl:Authorization";
acl:accessTo IRI?
// rdfs:comment "The subject of this authorization";
acl:default IRI?
// rdfs:comment "The container subject of this authorization";
acl:agent IRI*
// rdfs:comment "An agent is a person, social entity or software identified by a URI, e.g., a WebID denotes an agent";
acl:agentGroup IRI*
// rdfs:comment "Denotes a group of agents being given the access permission";
acl:agentClass [ acl:AuthenticatedAgent foaf:Agent ]*
// rdfs:comment "An agent class is a class of persons or entities identified by a URI.";
acl:mode [ acl:Read acl:Write acl:Append acl:Control ]*
// rdfs:comment "Denotes a class of operations that the agents can perform on a resource.";
)
}

@ -14,9 +14,16 @@ export * from "./methods";
export * from "./requester/requests/checkRootContainer";
export * from "./requester/requests/createDataResource";
export * from "./requester/requests/deleteResource";
export * from "./requester/requests/getAccessRules";
export * from "./requester/requests/readResource";
export * from "./requester/requests/requestOptions";
export * from "./requester/requests/setAccessRules";
export * from "./requester/requests/updateDataResource";
export * from "./requester/requests/uploadResource";
export * from "./resource/wac/WacRule";
export * from "./resource/wac/getWacRule";
export * from "./resource/wac/getWacUri";
export * from "./resource/wac/setWacRule";
export * from "./resource/wac/results/GetWacRuleSuccess";
export * from "./resource/wac/results/GetWacUriSuccess";
export * from "./resource/wac/results/SetWacRuleSuccess";
export * from "./resource/wac/results/WacRuleAbsent";

@ -1,13 +0,0 @@
/* istanbul ignore file */
export async function getAccessRules(): Promise<undefined> {
throw new Error("Not Implemented");
// const [publicAccess, agentAccess] = await Promise.all([
// universalAccess.getPublicAccess(uri, { fetch }),
// universalAccess.getAgentAccessAll(uri, { fetch }),
// ]);
// if (agentAccess === null || publicAccess === null) {
// return new AccessRuleFetchError(uri);
// }
// return new AccessRuleResult(uri, publicAccess, agentAccess);
}

@ -1,88 +0,0 @@
/* istanbul ignore file */
import type { AclDataset, WithChangeLog } from "@inrupt/solid-client";
import {
getSolidDatasetWithAcl,
hasResourceAcl,
hasFallbackAcl,
hasAccessibleAcl,
createAclFromFallbackAcl,
getResourceAcl,
setAgentResourceAccess,
saveAclFor,
setPublicDefaultAccess,
setPublicResourceAccess,
setAgentDefaultAccess,
} from "@inrupt/solid-client";
import { guaranteeFetch } from "../../util/guaranteeFetch";
import { isContainerUri } from "../../util/uriTypes";
import type { AccessRule } from "../results/success/AccessRule";
import type { SetAccessRuleSuccess } from "../results/success/AccessRule";
import { AccessRuleFetchError } from "../results/error/AccessControlError";
import type { BasicRequestOptions } from "./requestOptions";
export type SetAccessRulesResult =
| SetAccessRuleSuccess
| SetAccessRulesResultError;
export type SetAccessRulesResultError = AccessRuleFetchError;
export async function setAccessRules(
uri: string,
newAccessRules: AccessRule,
options?: BasicRequestOptions,
): Promise<SetAccessRulesResult> {
console.warn("Access Control is stil underdeveloped. Use with caution.");
const fetch = guaranteeFetch(options?.fetch);
const isContainer = isContainerUri(uri);
// Code Copied from https://docs.inrupt.com/developer-tools/javascript/client-libraries/tutorial/manage-wac/
// Fetch the SolidDataset and its associated ACLs, if available:
const myDatasetWithAcl = await getSolidDatasetWithAcl(uri, { fetch });
// Obtain the SolidDataset's own ACL, if available,
// or initialise a new one, if possible:
let resourceAcl;
if (!hasResourceAcl(myDatasetWithAcl)) {
if (!hasAccessibleAcl(myDatasetWithAcl)) {
return new AccessRuleFetchError(
uri,
"The current user does not have permission to change access rights to this Resource.",
);
}
if (!hasFallbackAcl(myDatasetWithAcl)) {
return new AccessRuleFetchError(
"The current user does not have permission to see who currently has access to this Resource.",
);
}
resourceAcl = createAclFromFallbackAcl(myDatasetWithAcl);
} else {
resourceAcl = getResourceAcl(myDatasetWithAcl);
}
// Give someone Control access to the given Resource:
let updatedAcl: AclDataset & WithChangeLog = resourceAcl;
if (newAccessRules.public) {
if (isContainer) {
updatedAcl = setPublicDefaultAccess(updatedAcl, newAccessRules.public);
} else {
updatedAcl = setPublicResourceAccess(updatedAcl, newAccessRules.public);
}
}
if (newAccessRules.agent) {
const setAgentAccess = isContainer
? setAgentDefaultAccess
: setAgentResourceAccess;
Object.entries(newAccessRules.agent).forEach(([webId, rules]) => {
updatedAcl = setAgentAccess(updatedAcl, webId, rules);
});
}
// Now save the ACL:
await saveAclFor(myDatasetWithAcl, updatedAcl, { fetch });
return {
isError: false,
uri,
type: "setAccessRuleSuccess",
};
}

@ -6,7 +6,8 @@ import { ResourceError } from "./ErrorResult";
export type HttpErrorResultType =
| ServerHttpError
| UnexpectedHttpError
| UnauthenticatedHttpError;
| UnauthenticatedHttpError
| UnauthorizedHttpError;
/**
* An error caused by an HTTP request
@ -70,6 +71,9 @@ export abstract class HttpErrorResult extends ResourceError {
if (UnauthenticatedHttpError.is(response)) {
return new UnauthenticatedHttpError(uri, response);
}
if (UnauthorizedHttpError.is(response)) {
return new UnauthorizedHttpError(uri, response);
}
if (HttpErrorResult.isnt(response)) {
return new UnexpectedHttpError(uri, response);
}
@ -102,6 +106,42 @@ export class UnauthenticatedHttpError extends HttpErrorResult {
}
}
/**
* An UnauthenticatedHttpError triggers when a Solid server returns a 403 status
* indicating that the request is not authorized.
*/
export class UnauthorizedHttpError extends HttpErrorResult {
readonly type = "unauthorizedError" as const;
/**
* Indicates if a specific response constitutes an UnauthenticatedHttpError
* @param response - The request response
* @returns true if this response constitutes an UnauthenticatedHttpError
*/
static is(response: Response) {
return response.status === 403;
}
}
/**
* An NotFoundHttpError triggers when a Solid server returns a 404 status. This
* error is not returned in most cases as a "absent" resource is not considered
* an error, but it is thrown while trying for find a WAC rule for a resource
* that does not exist.
*/
export class NotFoundHttpError extends HttpErrorResult {
readonly type = "notFoundError" as const;
/**
* Indicates if a specific response constitutes an NotFoundHttpError
* @param response - The request response
* @returns true if this response constitutes an NotFoundHttpError
*/
static is(response: Response) {
return response.status === 404;
}
}
/**
* A ServerHttpError triggers when a Solid server returns a 5XX status,
* indicating that an error happened on the server.

@ -1,11 +0,0 @@
import type { Access } from "@inrupt/solid-client";
import type { ResourceSuccess } from "./SuccessResult";
export interface AccessRule {
public?: Access;
agent?: Record<string, Access>;
}
export interface SetAccessRuleSuccess extends ResourceSuccess {
type: "setAccessRuleSuccess";
}

@ -309,14 +309,14 @@ export class Leaf extends Resource {
* ```typescript
* const leaf = solidLdoDataset
* .getResource("https://example.com/container/resource.ttl");
* const leafParent = leaf.getParentContainer();
* const leafParent = await leaf.getParentContainer();
* if (!leafParent.isError) {
* // Logs "https://example.com/container/"
* console.log(leafParent.uri);
* }
* ```
*/
getParentContainer(): Container {
async getParentContainer(): Promise<Container> {
const parentUri = getParentUri(this.uri)!;
return this.context.resourceStore.get(parentUri);
}
@ -338,8 +338,8 @@ export class Leaf extends Resource {
* }
* ```
*/
getRootContainer(): Promise<Container | CheckRootResultError> {
const parent = this.getParentContainer();
async getRootContainer(): Promise<Container | CheckRootResultError> {
const parent = await this.getParentContainer();
return parent.getRootContainer();
}

@ -11,9 +11,6 @@ import type {
} from "../requester/requests/readResource";
import type { BatchedRequester } from "../requester/BatchedRequester";
import type { CheckRootResultError } from "../requester/requests/checkRootContainer";
import type { AccessRule } from "../requester/results/success/AccessRule";
import type { SetAccessRulesResult } from "../requester/requests/setAccessRules";
import { setAccessRules } from "../requester/requests/setAccessRules";
import type TypedEmitter from "typed-emitter";
import EventEmitter from "events";
import { getParentUri } from "../util/rdfUtils";
@ -28,6 +25,13 @@ import type { CreateSuccess } from "../requester/results/success/CreateSuccess";
import type { ResourceResult } from "./resourceResult/ResourceResult";
import type { Container } from "./Container";
import type { Leaf } from "./Leaf";
import type { WacRule } from "./wac/WacRule";
import type { GetWacUriError, GetWacUriResult } from "./wac/getWacUri";
import { getWacUri } from "./wac/getWacUri";
import { getWacRuleWithAclUri, type GetWacRuleResult } from "./wac/getWacRule";
import { NoncompliantPodError } from "../requester/results/error/NoncompliantPodError";
import { setWacRuleForAclUri, type SetWacRuleResult } from "./wac/setWacRule";
import type { LeafUri } from "../util/uriTypes";
/**
* Statuses shared between both Leaf and Container
@ -79,6 +83,18 @@ export abstract class Resource extends (EventEmitter as new () => TypedEmitter<{
*/
protected absent: boolean | undefined;
/**
* @internal
* If a wac uri is fetched, it is cached here
*/
protected wacUri?: LeafUri;
/**
* @internal
* If a wac rule was fetched, it is cached here
*/
protected wacRule?: WacRule;
/**
* @param context - SolidLdoDatasetContext for the parent dataset
*/
@ -510,18 +526,160 @@ export abstract class Resource extends (EventEmitter as new () => TypedEmitter<{
*/
abstract getRootContainer(): Promise<Container | CheckRootResultError>;
// Access Rules Methods
// async getAccessRules(): Promise<AccessRuleResult | AccessRuleFetchError> {
// return getAccessRules({ uri: this.uri, fetch: this.context.fetch });
// }
/* istanbul ignore next */
async setAccessRules(
newAccessRules: AccessRule,
): Promise<ResourceResult<SetAccessRulesResult, Leaf | Container>> {
const result = await setAccessRules(this.uri, newAccessRules, {
abstract getParentContainer(): Promise<
Container | CheckRootResultError | undefined
>;
/**
* ===========================================================================
* WEB ACCESS CONTROL METHODS
* ===========================================================================
*/
/**
* Retrieves the URI for the web access control (WAC) rules for this resource
* @param options - set the "ignoreCache" field to true to ignore any cached
* information on WAC rules.
* @returns WAC Rules results
*/
protected async getWacUri(options?: {
ignoreCache: boolean;
}): Promise<GetWacUriResult> {
// Get the wacUri if not already present
if (!options?.ignoreCache && this.wacUri) {
return {
type: "getWacUriSuccess",
wacUri: this.wacUri,
isError: false,
uri: this.uri,
};
}
const wacUriResult = await getWacUri(this.uri, {
fetch: this.context.fetch,
});
if (wacUriResult.isError) {
return wacUriResult;
}
this.wacUri = wacUriResult.wacUri;
return wacUriResult;
}
/**
* Retrieves web access control (WAC) rules for this resource
* @param options - set the "ignoreCache" field to true to ignore any cached
* information on WAC rules.
* @returns WAC Rules results
*
* @example
* ```typescript
* const resource = ldoSolidDataset
* .getResource("https://example.com/container/resource.ttl");
* const wacRulesResult = await resource.getWac();
* if (!wacRulesResult.isError) {
* const wacRules = wacRulesResult.wacRule;
* // True if the resource is publicly readable
* console.log(wacRules.public.read);
* // True if authenticated agents can write to the resource
* console.log(wacRules.authenticated.write);
* // True if the given WebId has append access
* console.log(
* wacRules.agent[https://example.com/person1/profile/card#me].append
* );
* // True if the given WebId has control access
* console.log(
* wacRules.agent[https://example.com/person1/profile/card#me].control
* );
* }
* ```
*/
async getWac(options?: {
ignoreCache: boolean;
}): Promise<GetWacUriError | GetWacRuleResult> {
// Return the wac rule if it's already cached
if (!options?.ignoreCache && this.wacRule) {
return {
type: "getWacRuleSuccess",
uri: this.uri,
isError: false,
wacRule: this.wacRule,
};
}
// Get the wac uri
const wacUriResult = await this.getWacUri(options);
if (wacUriResult.isError) return wacUriResult;
// Get the wac rule
const wacResult = await getWacRuleWithAclUri(wacUriResult.wacUri, {
fetch: this.context.fetch,
});
if (wacResult.isError) return wacResult;
// If the wac rules was successfully found
if (wacResult.type === "getWacRuleSuccess") {
this.wacRule = wacResult.wacRule;
return wacResult;
}
// If the WacRule is absent
const parentResource = await this.getParentContainer();
if (parentResource?.isError) return parentResource;
if (!parentResource) {
return new NoncompliantPodError(
this.uri,
`Resource "${this.uri}" has no Effective ACL resource`,
);
}
return parentResource.getWac();
}
/**
* Sets access rules for a specific resource
* @param wacRule - the access rules to set
* @returns SetWacRuleResult
*
* @example
* ```typescript
* const resource = ldoSolidDataset
* .getResource("https://example.com/container/resource.ttl");
* const wacRulesResult = await resource.setWac({
* public: {
* read: true,
* write: false,
* append: false,
* control: false
* },
* authenticated: {
* read: true,
* write: false,
* append: true,
* control: false
* },
* agent: {
* "https://example.com/person1/profile/card#me": {
* read: true,
* write: true,
* append: true,
* control: true
* }
* }
* });
* ```
*/
async setWac(wacRule: WacRule): Promise<GetWacUriError | SetWacRuleResult> {
const wacUriResult = await this.getWacUri();
if (wacUriResult.isError) return wacUriResult;
const result = await setWacRuleForAclUri(
wacUriResult.wacUri,
wacRule,
this.uri,
{
fetch: this.context.fetch,
},
);
if (result.isError) return result;
return { ...result, resource: this as unknown as Leaf | Container };
this.wacRule = result.wacRule;
return result;
}
}

@ -0,0 +1,18 @@
/**
* A list of modes that a certain agent has access to
*/
export interface AccessModeList {
read: boolean;
append: boolean;
write: boolean;
control: boolean;
}
/**
* A list of modes for each kind of agent
*/
export interface WacRule {
public: AccessModeList;
authenticated: AccessModeList;
agent: Record<string, AccessModeList>;
}

@ -0,0 +1,118 @@
import type { GetWacRuleSuccess } from "./results/GetWacRuleSuccess";
import { guaranteeFetch } from "../../util/guaranteeFetch";
import type { BasicRequestOptions } from "../../requester/requests/requestOptions";
import type { HttpErrorResultType } from "../../requester/results/error/HttpErrorResult";
import { HttpErrorResult } from "../../requester/results/error/HttpErrorResult";
import type { NoncompliantPodError } from "../../requester/results/error/NoncompliantPodError";
import type { UnexpectedResourceError } from "../../requester/results/error/ErrorResult";
import { rawTurtleToDataset } from "../../util/rdfUtils";
import { AuthorizationShapeType } from "../../.ldo/wac.shapeTypes";
import type { AccessModeList, WacRule } from "./WacRule";
import type { Authorization } from "../../.ldo/wac.typings";
import type { WacRuleAbsent } from "./results/WacRuleAbsent";
export type GetWacRuleError =
| HttpErrorResultType
| NoncompliantPodError
| UnexpectedResourceError;
export type GetWacRuleResult =
| GetWacRuleSuccess
| GetWacRuleError
| WacRuleAbsent;
/**
* Given the URI of an ACL document, return the Web Access Control (WAC) rules
* @param aclUri: The URI for the ACL document
* @param options: Options object to include an authenticated fetch function
* @returns GetWacRuleResult
*/
export async function getWacRuleWithAclUri(
aclUri: string,
options?: BasicRequestOptions,
): Promise<GetWacRuleResult> {
const fetch = guaranteeFetch(options?.fetch);
const response = await fetch(aclUri);
const errorResult = HttpErrorResult.checkResponse(aclUri, response);
if (errorResult) return errorResult;
if (response.status === 404) {
return {
type: "wacRuleAbsent",
uri: aclUri,
isError: false,
};
}
// Parse Turtle
const rawTurtle = await response.text();
const rawTurtleResult = await rawTurtleToDataset(rawTurtle, aclUri);
if (rawTurtleResult.isError) return rawTurtleResult;
const dataset = rawTurtleResult.dataset;
const authorizations = dataset
.usingType(AuthorizationShapeType)
.matchSubject(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
"http://www.w3.org/ns/auth/acl#Authorization",
);
const wacRule: WacRule = {
public: {
read: false,
write: false,
append: false,
control: false,
},
authenticated: {
read: false,
write: false,
append: false,
control: false,
},
agent: {},
};
function applyAccessModesToList(
accessModeList: AccessModeList,
authorization: Authorization,
): void {
authorization.mode?.forEach((mode) => {
accessModeList[mode["@id"].toLowerCase()] = true;
});
}
authorizations.forEach((authorization) => {
if (
authorization.agentClass?.some(
(agentClass) => agentClass["@id"] === "Agent",
)
) {
applyAccessModesToList(wacRule.public, authorization);
applyAccessModesToList(wacRule.authenticated, authorization);
}
if (
authorization.agentClass?.some(
(agentClass) => agentClass["@id"] === "AuthenticatedAgent",
)
) {
applyAccessModesToList(wacRule.authenticated, authorization);
}
authorization.agent?.forEach((agent) => {
if (!wacRule.agent[agent["@id"]]) {
wacRule.agent[agent["@id"]] = {
read: false,
write: false,
append: false,
control: false,
};
}
applyAccessModesToList(wacRule.agent[agent["@id"]], authorization);
});
});
return {
type: "getWacRuleSuccess",
uri: aclUri,
isError: false,
wacRule,
};
}

@ -0,0 +1,70 @@
import type { GetWacUriSuccess } from "./results/GetWacUriSuccess";
import type { HttpErrorResultType } from "../../requester/results/error/HttpErrorResult";
import {
HttpErrorResult,
NotFoundHttpError,
} from "../../requester/results/error/HttpErrorResult";
import { UnexpectedResourceError } from "../../requester/results/error/ErrorResult";
import { guaranteeFetch } from "../../util/guaranteeFetch";
import type { BasicRequestOptions } from "../../requester/requests/requestOptions";
import { NoncompliantPodError } from "../../requester/results/error/NoncompliantPodError";
import { parse as parseLinkHeader } from "http-link-header";
import type { LeafUri } from "../../util/uriTypes";
export type GetWacUriError =
| HttpErrorResultType
| NotFoundHttpError
| NoncompliantPodError
| UnexpectedResourceError;
export type GetWacUriResult = GetWacUriSuccess | GetWacUriError;
/**
* Get the URI for the WAC rules of a specific resource
* @param resourceUri: the URI of the resource
* @param options: Options object to include an authenticated fetch function
* @returns GetWacUriResult
*/
export async function getWacUri(
resourceUri: string,
options?: BasicRequestOptions,
): Promise<GetWacUriResult> {
try {
const fetch = guaranteeFetch(options?.fetch);
const response = await fetch(resourceUri, {
method: "head",
});
const errorResult = HttpErrorResult.checkResponse(resourceUri, response);
if (errorResult) return errorResult;
if (NotFoundHttpError.is(response)) {
return new NotFoundHttpError(
resourceUri,
response,
"Could not get access control rules because the resource does not exist.",
);
}
// Get the URI from the link header
const linkHeader = response.headers.get("link");
if (!linkHeader) {
return new NoncompliantPodError(
resourceUri,
"No link header present in request.",
);
}
const parsedLinkHeader = parseLinkHeader(linkHeader);
const aclUris = parsedLinkHeader.get("rel", "acl");
if (aclUris.length !== 1) {
return new NoncompliantPodError(
resourceUri,
`There must be one link with a rel="acl"`,
);
}
return {
type: "getWacUriSuccess",
isError: false,
uri: resourceUri,
wacUri: aclUris[0].uri as LeafUri,
};
} catch (err: unknown) {
return UnexpectedResourceError.fromThrown(resourceUri, err);
}
}

@ -0,0 +1,13 @@
import type { ResourceSuccess } from "../../../requester/results/success/SuccessResult";
import type { WacRule } from "../WacRule";
/**
* Returned when a WAC rule is successfully retrieved
*/
export interface GetWacRuleSuccess extends ResourceSuccess {
type: "getWacRuleSuccess";
/**
* The rule that was retrieved
*/
wacRule: WacRule;
}

@ -0,0 +1,13 @@
import type { ResourceSuccess } from "../../../requester/results/success/SuccessResult";
import type { LeafUri } from "../../../util/uriTypes";
/**
* Returned when the URI for a resources ACL document was successfully retried
*/
export interface GetWacUriSuccess extends ResourceSuccess {
type: "getWacUriSuccess";
/**
* The URI of the ACL document
*/
wacUri: LeafUri;
}

@ -0,0 +1,13 @@
import type { ResourceSuccess } from "../../../requester/results/success/SuccessResult";
import type { WacRule } from "../WacRule";
/**
* Returned when rules were successfully written
*/
export interface SetWacRuleSuccess extends ResourceSuccess {
type: "setWacRuleSuccess";
/**
* The written rule
*/
wacRule: WacRule;
}

@ -0,0 +1,8 @@
import type { ResourceSuccess } from "../../../requester/results/success/SuccessResult";
/**
* Returned if no WAC rule was returned from the server
*/
export interface WacRuleAbsent extends ResourceSuccess {
type: "wacRuleAbsent";
}

@ -0,0 +1,109 @@
import { createLdoDataset } from "@ldo/ldo";
import type { BasicRequestOptions } from "../../requester/requests/requestOptions";
import type { UnexpectedResourceError } from "../../requester/results/error/ErrorResult";
import {
HttpErrorResult,
type HttpErrorResultType,
} from "../../requester/results/error/HttpErrorResult";
import { isContainerUri, type LeafUri } from "../../util/uriTypes";
import type { AccessModeList, WacRule } from "./WacRule";
import type { SetWacRuleSuccess } from "./results/SetWacRuleSuccess";
import type { Authorization } from "../../.ldo/wac.typings";
import { AuthorizationShapeType } from "../../.ldo/wac.shapeTypes";
import { v4 } from "uuid";
import { guaranteeFetch } from "../../util/guaranteeFetch";
export type SetWacRuleError = HttpErrorResultType | UnexpectedResourceError;
export type SetWacRuleResult = SetWacRuleSuccess | SetWacRuleError;
/**
* Given the URI of an ACL document and some WAC rules, set the WAC rules of
* that document
* @param aclUri: The URI for the ACL document
* @param newRule: A new WAC rule to set. This will overwrite old rules
* @param accessTo: The document this rule refers to
* @param options: Options object to include an authenticated fetch function
* @returns SetWacRuleResult
*/
export async function setWacRuleForAclUri(
aclUri: LeafUri,
newRule: WacRule,
accessTo: string,
options?: BasicRequestOptions,
): Promise<SetWacRuleResult> {
const fetch = guaranteeFetch(options?.fetch);
// The rule map keeps track of all the rules that are currently being used
// so that similar rules can be grouped together
const ruleMap: Record<string, Authorization> = {};
// The dataset that will eventually be sent to the Pod
const dataset = createLdoDataset();
// Helper function to add rules to the dataset by grouping them in the ruleMap
function addRuleToDataset(
type: "public" | "authenticated" | "agent",
accessModeList: AccessModeList,
agentId?: string,
) {
const accessModeListHash = hashAccessModeList(accessModeList);
// No need to add if all access is false
if (accessModeListHash === "") return;
if (!ruleMap[accessModeListHash]) {
const authorization = dataset
.usingType(AuthorizationShapeType)
.fromSubject(`${aclUri}#${v4()}`);
authorization.type = { "@id": "Authorization" };
if (accessModeList.read) authorization.mode?.push({ "@id": "Read" });
if (accessModeList.write) authorization.mode?.push({ "@id": "Write" });
if (accessModeList.append) authorization.mode?.push({ "@id": "Append" });
if (accessModeList.control)
authorization.mode?.push({ "@id": "Control" });
authorization.accessTo = { "@id": accessTo };
if (isContainerUri(accessTo)) {
authorization.default = { "@id": accessTo };
}
ruleMap[accessModeListHash] = authorization;
}
const authorization = ruleMap[accessModeListHash];
// Add agents to the rule
if (type === "public") {
authorization.agentClass?.push({ "@id": "Agent" });
} else if (type === "authenticated") {
authorization.agentClass?.push({ "@id": "AuthenticatedAgent" });
} else if (type === "agent" && agentId) {
authorization.agent?.push({ "@id": agentId });
}
}
// Add each rule to the dataset
addRuleToDataset("public", newRule.public);
addRuleToDataset("authenticated", newRule.authenticated);
Object.entries(newRule.agent).forEach(([agentUri, accessModeList]) => {
addRuleToDataset("agent", accessModeList, agentUri);
});
// Save to Pod
const response = await fetch(aclUri, {
method: "PUT",
headers: {
"content-type": "text/turtle",
},
body: dataset.toString(),
});
const errorResult = HttpErrorResult.checkResponse(aclUri, response);
if (errorResult) return errorResult;
return {
type: "setWacRuleSuccess",
uri: aclUri,
isError: false,
wacRule: newRule,
};
}
// Hashes the access mode list for use in the rule map
function hashAccessModeList(list: AccessModeList): string {
return Object.entries(list).reduce(
(agg, [key, isPresent]) => (isPresent ? agg + key : agg),
"",
);
}

@ -1,3 +1,4 @@
import type { LdoDataset } from "@ldo/ldo";
import { parseRdf } from "@ldo/ldo";
import { namedNode, quad as createQuad } from "@rdfjs/data-model";
import type { Dataset } from "@rdfjs/types";
@ -115,19 +116,9 @@ export async function addRawTurtleToDataset(
dataset: Dataset,
baseUri: string,
): Promise<undefined | NoncompliantPodError> {
let loadedDataset: Dataset;
try {
loadedDataset = await parseRdf(rawTurtle, {
baseIRI: baseUri,
});
} catch (err) {
const error = UnexpectedResourceError.fromThrown(baseUri, err);
return new NoncompliantPodError(
baseUri,
`Request returned noncompliant turtle: ${error.message}`,
);
}
const rawTurtleResult = await rawTurtleToDataset(rawTurtle, baseUri);
if (rawTurtleResult.isError) return rawTurtleResult;
const loadedDataset = rawTurtleResult.dataset;
const graphNode = namedNode(baseUri);
// Destroy all triples that were once a part of this resouce
dataset.deleteMatches(undefined, undefined, undefined, graphNode);
@ -138,3 +129,21 @@ export async function addRawTurtleToDataset(
),
);
}
export async function rawTurtleToDataset(
rawTurtle: string,
baseUri: string,
): Promise<{ isError: false; dataset: LdoDataset } | NoncompliantPodError> {
try {
const loadedDataset = await parseRdf(rawTurtle, {
baseIRI: baseUri,
});
return { isError: false, dataset: loadedDataset };
} catch (err) {
const error = UnexpectedResourceError.fromThrown(baseUri, err);
return new NoncompliantPodError(
baseUri,
`Request returned noncompliant turtle: ${error.message}\n${rawTurtle}`,
);
}
}

@ -43,6 +43,8 @@ import type {
UnexpectedHttpError,
} from "../src/requester/results/error/HttpErrorResult";
import type { NoncompliantPodError } from "../src/requester/results/error/NoncompliantPodError";
import type { GetWacRuleSuccess } from "../src/resource/wac/results/GetWacRuleSuccess";
import type { WacRule } from "../src/resource/wac/WacRule";
const TEST_CONTAINER_SLUG = "test_ldo/";
const TEST_CONTAINER_URI =
@ -94,7 +96,8 @@ const TEST_CONTAINER_ACL = `<#b30e3fd1-b5a8-4763-ad9d-e95de9cf7933> a <http://ww
<http://www.w3.org/ns/auth/acl#accessTo> <${TEST_CONTAINER_URI}>;
<http://www.w3.org/ns/auth/acl#default> <${TEST_CONTAINER_URI}>;
<http://www.w3.org/ns/auth/acl#mode> <http://www.w3.org/ns/auth/acl#Read>, <http://www.w3.org/ns/auth/acl#Write>, <http://www.w3.org/ns/auth/acl#Append>, <http://www.w3.org/ns/auth/acl#Control>;
<http://www.w3.org/ns/auth/acl#agent> <${WEB_ID}>.`;
<http://www.w3.org/ns/auth/acl#agent> <${WEB_ID}>;
<http://www.w3.org/ns/auth/acl#agentClass> <http://xmlns.com/foaf/0.1/Agent>, <http://www.w3.org/ns/auth/acl#AuthenticatedAgent>.`;
async function testRequestLoads<ReturnVal>(
request: () => Promise<ReturnVal>,
@ -209,6 +212,9 @@ describe("Integration", () => {
method: "DELETE",
}),
]);
await authFetch(TEST_CONTAINER_URI, {
method: "DELETE",
});
});
/**
@ -304,6 +310,18 @@ describe("Integration", () => {
expect(result.type).toBe("serverError");
});
it("Returns an Unauthorized error if a 403 error is returned", async () => {
fetchMock.mockResolvedValueOnce(new Response("Error", { status: 403 }));
const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI);
const result = await testRequestLoads(() => resource.read(), resource, {
isLoading: true,
isReading: true,
isDoingInitialFetch: true,
});
expect(result.isError).toBe(true);
expect(result.type).toBe("unauthorizedError");
});
it("Returns an UnauthenticatedError on an 401 error is returned", async () => {
fetchMock.mockResolvedValueOnce(new Response("Error", { status: 401 }));
const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI);
@ -1612,4 +1630,287 @@ describe("Integration", () => {
).toBe(true);
});
});
/**
* ===========================================================================
* ACCESS CONTROL
* ===========================================================================
*/
describe("getWacRule", () => {
it("Fetches a wac rules for a container that has a corresponding acl", async () => {
const container = solidLdoDataset.getResource(TEST_CONTAINER_URI);
const wacResult = await container.getWac();
expect(wacResult.isError).toBe(false);
const wacSuccess = wacResult as GetWacRuleSuccess;
expect(wacSuccess.wacRule.public).toEqual({
read: true,
write: true,
append: true,
control: true,
});
expect(wacSuccess.wacRule.authenticated).toEqual({
read: true,
write: true,
append: true,
control: true,
});
expect(wacSuccess.wacRule.agent[WEB_ID]).toEqual({
read: true,
write: true,
append: true,
control: true,
});
});
it("Gets wac rules of a parent resource for a resource that does not have a corresponding acl", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(false);
const wacSuccess = wacResult as GetWacRuleSuccess;
expect(wacSuccess.wacRule.public).toEqual({
read: true,
write: true,
append: true,
control: true,
});
expect(wacSuccess.wacRule.authenticated).toEqual({
read: true,
write: true,
append: true,
control: true,
});
expect(wacSuccess.wacRule.agent[WEB_ID]).toEqual({
read: true,
write: true,
append: true,
control: true,
});
});
it("uses cached values for a retrieved resource", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
await resource.getWac();
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(false);
const wacSuccess = wacResult as GetWacRuleSuccess;
expect(wacSuccess.wacRule.public).toEqual({
read: true,
write: true,
append: true,
control: true,
});
expect(wacSuccess.wacRule.authenticated).toEqual({
read: true,
write: true,
append: true,
control: true,
});
expect(wacSuccess.wacRule.agent[WEB_ID]).toEqual({
read: true,
write: true,
append: true,
control: true,
});
});
it("returns an error when an error is encountered fetching the aclUri", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce(new Response("Error", { status: 500 }));
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("serverError");
});
it("returns an error when a document is not found", async () => {
const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI);
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("notFoundError");
});
it("returns a non-compliant error if a response is returned without a link header", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce(
new Response("Error", {
status: 200,
}),
);
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("noncompliantPodError");
expect((wacResult as NoncompliantPodError).message).toBe(
`Response from ${SAMPLE_DATA_URI} is not compliant with the Solid Specification: No link header present in request.`,
);
});
it("returns a non-compliant error if a response is returned without an ACL link", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce(
new Response("Error", {
status: 200,
headers: { link: `<card.meta>; rel="describedBy"` },
}),
);
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("noncompliantPodError");
expect((wacResult as NoncompliantPodError).message).toBe(
`Response from ${SAMPLE_DATA_URI} is not compliant with the Solid Specification: There must be one link with a rel="acl"`,
);
});
it("Returns an UnexpectedResourceError if an unknown error is triggered while getting the wac URI", async () => {
fetchMock.mockRejectedValueOnce(new Error("Something happened."));
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
const result = await resource.getWac();
expect(result.isError).toBe(true);
if (!result.isError) return;
expect(result.type).toBe("unexpectedResourceError");
expect(result.message).toBe("Something happened.");
});
it("Returns an error if the request to get the ACL fails", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce(
new Response("", {
status: 200,
headers: { link: `<card.acl>; rel="acl"` },
}),
);
fetchMock.mockResolvedValueOnce(new Response("Error", { status: 500 }));
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("serverError");
});
it("Returns a non-compliant error if the root uri has no ACL", () => {});
it("Returns an error if the request to the ACL resource returns invalid turtle", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce(
new Response("", {
status: 200,
headers: { link: `<card.acl>; rel="acl"` },
}),
);
fetchMock.mockResolvedValueOnce(
new Response("BAD TURTLE", { status: 200 }),
);
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("noncompliantPodError");
expect((wacResult as NoncompliantPodError).message).toBe(
`Response from card.acl is not compliant with the Solid Specification: Request returned noncompliant turtle: Unexpected "BAD" on line 1.\nBAD TURTLE`,
);
});
it("Returns an error if there was a problem getting the parent resource", async () => {
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
fetchMock.mockResolvedValueOnce(
new Response("", {
status: 200,
headers: { link: `<card.acl>; rel="acl"` },
}),
);
fetchMock.mockResolvedValueOnce(new Response("", { status: 404 }));
fetchMock.mockResolvedValueOnce(new Response("", { status: 500 }));
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("serverError");
});
it("returns a NonCompliantPodError when this is the root resource and it doesn't have an ACL", async () => {
const resource = solidLdoDataset.getResource(ROOT_CONTAINER);
fetchMock.mockResolvedValueOnce(
new Response("", {
status: 200,
headers: { link: `<card.acl>; rel="acl"` },
}),
);
fetchMock.mockResolvedValueOnce(new Response("", { status: 404 }));
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("noncompliantPodError");
expect((wacResult as NoncompliantPodError).message).toBe(
`Response from ${ROOT_CONTAINER} is not compliant with the Solid Specification: Resource "${ROOT_CONTAINER}" has no Effective ACL resource`,
);
});
});
describe("setWacRule", () => {
const newRules: WacRule = {
public: { read: true, write: false, append: false, control: false },
authenticated: {
read: true,
write: false,
append: true,
control: false,
},
agent: {
[WEB_ID]: { read: true, write: true, append: true, control: true },
},
};
it("sets wac rules for a resource that didn't have one before", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
const result = await resource.setWac(newRules);
expect(result.isError).toBe(false);
expect(result.type).toBe("setWacRuleSuccess");
const readResult = await resource.getWac({ ignoreCache: true });
expect(readResult.isError).toBe(false);
expect(readResult.type).toBe("getWacRuleSuccess");
const rules = (readResult as GetWacRuleSuccess).wacRule;
expect(rules).toEqual(newRules);
});
it("overwrites an existing access control rule", async () => {
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
const result = await resource.setWac(newRules);
expect(result.isError).toBe(false);
expect(result.type).toBe("setWacRuleSuccess");
const readResult = await resource.getWac({ ignoreCache: true });
expect(readResult.isError).toBe(false);
expect(readResult.type).toBe("getWacRuleSuccess");
const rules = (readResult as GetWacRuleSuccess).wacRule;
expect(rules).toEqual(newRules);
});
it("Does not write a rule when access is not granted to an agent", async () => {
const moreRules = {
...newRules,
public: { read: false, write: false, append: false, control: false },
};
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
const result = await resource.setWac(moreRules);
expect(result.isError).toBe(false);
expect(result.type).toBe("setWacRuleSuccess");
const readResult = await resource.getWac({ ignoreCache: true });
expect(readResult.isError).toBe(false);
expect(readResult.type).toBe("getWacRuleSuccess");
const rules = (readResult as GetWacRuleSuccess).wacRule;
expect(rules).toEqual(moreRules);
});
it("returns an error when an error is encountered fetching the aclUri", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce(new Response("Error", { status: 500 }));
const wacResult = await resource.setWac(newRules);
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("serverError");
});
it("Returns an error when the request to write the access rules throws an error", async () => {
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
fetchMock.mockResolvedValueOnce(
new Response("", {
status: 200,
headers: { link: `<card.acl>; rel="acl"` },
}),
);
fetchMock.mockResolvedValueOnce(new Response("", { status: 500 }));
const wacResult = await resource.setWac(newRules);
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("serverError");
});
});
});

@ -1,6 +1,6 @@
{
"name": "@ldo/subscribable-dataset",
"version": "0.0.1-alpha.19",
"version": "0.0.1-alpha.23",
"description": "An RDFJS dataset implementation that can be subscribed to for updates",
"main": "dist/index.js",
"scripts": {
@ -33,8 +33,8 @@
"ts-node": "^9.1.1"
},
"dependencies": {
"@ldo/dataset": "^0.0.1-alpha.17",
"@ldo/rdf-utils": "^0.0.1-alpha.17"
"@ldo/dataset": "^0.0.1-alpha.23",
"@ldo/rdf-utils": "^0.0.1-alpha.23"
},
"files": [
"dist",
@ -43,5 +43,5 @@
"publishConfig": {
"access": "public"
},
"gitHead": "4548985c0de9b0ec83cf5ee93f2d7c1ca2c1b8d8"
"gitHead": "d2364cd2f8da5f0b673b1202d29df5b7c071a17c"
}

@ -1,6 +1,6 @@
{
"name": "@ldo/traverser-shexj",
"version": "0.0.1-alpha.17",
"version": "0.0.1-alpha.23",
"description": "A type-traverser for ShexJ",
"main": "dist/index.js",
"scripts": {
@ -20,20 +20,20 @@
},
"homepage": "https://github.com/o-development/ldobjects/tree/main/packages/traverser-shexj#readme",
"devDependencies": {
"@types/jest": "^29.0.3",
"@types/jest": "^27.0.3",
"@types/shexj": "^2.1.3",
"jest": "^29.0.3",
"ts-jest": "^29.0.2"
"jest": "^27.4.5",
"ts-jest": "^27.1.2"
},
"files": [
"dist",
"src"
],
"dependencies": {
"@ldo/type-traverser": "^0.0.1-alpha.17"
"@ldo/type-traverser": "^0.0.1-alpha.23"
},
"publishConfig": {
"access": "public"
},
"gitHead": "4548985c0de9b0ec83cf5ee93f2d7c1ca2c1b8d8"
"gitHead": "d2364cd2f8da5f0b673b1202d29df5b7c071a17c"
}

@ -1,6 +1,6 @@
{
"name": "@ldo/type-traverser",
"version": "0.0.1-alpha.17",
"version": "0.0.1-alpha.23",
"description": "An organized way to traverse over objects using typescript",
"main": "dist/index.js",
"scripts": {
@ -37,5 +37,5 @@
"publishConfig": {
"access": "public"
},
"gitHead": "4548985c0de9b0ec83cf5ee93f2d7c1ca2c1b8d8"
"gitHead": "d2364cd2f8da5f0b673b1202d29df5b7c071a17c"
}

Loading…
Cancel
Save