parent
b7397f8f6d
commit
1db769103f
@ -0,0 +1,5 @@ |
|||||||
|
module.exports = { |
||||||
|
preset: "ts-jest", |
||||||
|
testEnvironment: "node", |
||||||
|
coveragePathIgnorePatterns: ["/test/"], |
||||||
|
}; |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,28 @@ |
|||||||
|
// 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,23 +1,18 @@ |
|||||||
import React, { FunctionComponent } from "react"; |
import React, { FunctionComponent } from "react"; |
||||||
|
import Profile from "./Profile"; |
||||||
|
import { SolidAuthProvider, LdoProvider } from "@ldo/solid-react"; |
||||||
|
import { fetch } from "solid-authn-react-native"; |
||||||
|
import Layout from "./Layout"; |
||||||
|
|
||||||
const App: FunctionComponent = () => { |
const ProfileApp: FunctionComponent = () => { |
||||||
return ( |
return ( |
||||||
<div className="App"> |
<SolidAuthProvider> |
||||||
<header className="App-header"> |
<LdoProvider fetch={fetch}> |
||||||
<p> |
<Layout> |
||||||
Edit <code>src/App.js</code> and save to reload. |
<Profile /> |
||||||
</p> |
</Layout> |
||||||
<a |
</LdoProvider> |
||||||
className="App-link" |
</SolidAuthProvider> |
||||||
href="https://reactjs.org" |
|
||||||
target="_blank" |
|
||||||
rel="noopener noreferrer" |
|
||||||
> |
|
||||||
Learn React |
|
||||||
</a> |
|
||||||
</header> |
|
||||||
</div> |
|
||||||
); |
); |
||||||
}; |
}; |
||||||
|
export default ProfileApp; |
||||||
export default App; |
|
||||||
|
@ -0,0 +1,34 @@ |
|||||||
|
import React, { FunctionComponent, useState } from "react"; |
||||||
|
|
||||||
|
interface BlurTextInputProps { |
||||||
|
value: string; |
||||||
|
onBlurText: (text: string) => void; |
||||||
|
} |
||||||
|
|
||||||
|
const BlurTextInput: FunctionComponent<BlurTextInputProps> = ({ |
||||||
|
value, |
||||||
|
onBlurText, |
||||||
|
}) => { |
||||||
|
const [isFocused, setIsFocused] = useState(false); |
||||||
|
const [text, setText] = useState(value); |
||||||
|
|
||||||
|
return ( |
||||||
|
<input |
||||||
|
type="text" |
||||||
|
value={isFocused ? text : value} |
||||||
|
onChange={(e) => setText(e.target.value)} |
||||||
|
onFocus={() => { |
||||||
|
setIsFocused(true); |
||||||
|
setText(value); |
||||||
|
}} |
||||||
|
onBlur={(e) => { |
||||||
|
setIsFocused(false); |
||||||
|
if (e.target.value !== value) { |
||||||
|
onBlurText(e.target.value); |
||||||
|
} |
||||||
|
}} |
||||||
|
/> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default BlurTextInput; |
@ -0,0 +1,40 @@ |
|||||||
|
import React, { |
||||||
|
FunctionComponent, |
||||||
|
PropsWithChildren, |
||||||
|
useCallback, |
||||||
|
} from "react"; |
||||||
|
import { useSolidAuth } from "@ldo/solid-react"; |
||||||
|
|
||||||
|
const Layout: FunctionComponent<PropsWithChildren> = ({ children }) => { |
||||||
|
const { login, session, logout } = useSolidAuth(); |
||||||
|
|
||||||
|
const loginCb = useCallback(async () => { |
||||||
|
const issuer = prompt( |
||||||
|
"Enter your Pod Provider", |
||||||
|
"https://solidcommunity.net", |
||||||
|
); |
||||||
|
if (issuer) { |
||||||
|
await login(issuer); |
||||||
|
} |
||||||
|
}, []); |
||||||
|
|
||||||
|
return ( |
||||||
|
<div> |
||||||
|
<h1>LDO Solid React Test</h1> |
||||||
|
{session.isLoggedIn ? ( |
||||||
|
<div> |
||||||
|
<p> |
||||||
|
Logged in as {session.webId}{" "} |
||||||
|
<button onClick={logout}>Log Out</button> |
||||||
|
</p> |
||||||
|
<hr /> |
||||||
|
{children} |
||||||
|
</div> |
||||||
|
) : ( |
||||||
|
<button onClick={loginCb}>Log In</button> |
||||||
|
)} |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default Layout; |
@ -0,0 +1,39 @@ |
|||||||
|
import React, { FunctionComponent } from "react"; |
||||||
|
import { SolidProfileShapeShapeType } from "./ldo/solidProfile.shapeTypes"; |
||||||
|
import BlurTextInput from "./BlurTextInput"; |
||||||
|
import { |
||||||
|
useSolidAuth, |
||||||
|
useLdo, |
||||||
|
useDataResource, |
||||||
|
useSubject, |
||||||
|
} from "@ldo/solid-react"; |
||||||
|
|
||||||
|
const Profile: FunctionComponent = () => { |
||||||
|
const { changeData, commitData } = useLdo(); |
||||||
|
const { session } = useSolidAuth(); |
||||||
|
const webId = session.webId!; |
||||||
|
const webIdResource = useDataResource(webId); |
||||||
|
const [profile, profileError] = useSubject(SolidProfileShapeShapeType, webId); |
||||||
|
|
||||||
|
if (webIdResource.isLoading) { |
||||||
|
return <p>Loading</p>; |
||||||
|
} else if (profileError) { |
||||||
|
return <p>profileError.message</p>; |
||||||
|
} else { |
||||||
|
return ( |
||||||
|
<div> |
||||||
|
<label>Name:</label> |
||||||
|
<BlurTextInput |
||||||
|
value={profile.name || ""} |
||||||
|
onBlurText={async (text) => { |
||||||
|
const cProfile = changeData(profile, webIdResource); |
||||||
|
cProfile.name = text; |
||||||
|
await commitData(cProfile); |
||||||
|
}} |
||||||
|
/> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
export default Profile; |
@ -0,0 +1,156 @@ |
|||||||
|
import { ContextDefinition } from "jsonld"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* solidProfileContext: JSONLD Context for solidProfile |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
export const solidProfileContext: ContextDefinition = { |
||||||
|
type: { |
||||||
|
"@id": "@type", |
||||||
|
"@container": "@set", |
||||||
|
}, |
||||||
|
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", |
||||||
|
"@container": "@set", |
||||||
|
}, |
||||||
|
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", |
||||||
|
}, |
||||||
|
}; |
@ -0,0 +1,749 @@ |
|||||||
|
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", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}; |
@ -0,0 +1,71 @@ |
|||||||
|
import { ShapeType } from "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, |
||||||
|
}; |
@ -0,0 +1,293 @@ |
|||||||
|
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; |
||||||
|
} |
@ -0,0 +1,121 @@ |
|||||||
|
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" ; |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
{ |
||||||
|
"sourceType": "unambiguous", |
||||||
|
"presets": [ |
||||||
|
[ |
||||||
|
"@babel/preset-env", |
||||||
|
{ |
||||||
|
"targets": { |
||||||
|
"chrome": 100 |
||||||
|
} |
||||||
|
} |
||||||
|
], |
||||||
|
"@babel/preset-typescript", |
||||||
|
"@babel/preset-react" |
||||||
|
], |
||||||
|
"plugins": [] |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
{ |
||||||
|
"extends": ["../../eslintrc"] |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
# Solid-Dataset |
||||||
|
|
||||||
|
Pre-alpha |
||||||
|
|
||||||
|
## Notes: |
||||||
|
- Any quads in the default graph will not be synced to Solid Pods, but will be added to the dataset without any syncing |
||||||
|
|
||||||
|
|
||||||
|
## TODO: |
||||||
|
- Create an event for each thing that happens. Like "Loaded" |
||||||
|
- You should be able to initialize classes with loading = true so that there isn't a flash on initial load |
||||||
|
- Access rule stuff just doesn't work. I might need to program a custom implementation for that |
||||||
|
- There should probably be separate libraries for React native and Web React |
@ -0,0 +1,5 @@ |
|||||||
|
const sharedConfig = require('../../jest.config.js'); |
||||||
|
module.exports = { |
||||||
|
...sharedConfig, |
||||||
|
'rootDir': './', |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
{ |
||||||
|
"name": "@ldo/solid-react", |
||||||
|
"version": "0.0.0", |
||||||
|
"description": "A React library for LDO and Solid", |
||||||
|
"main": "dist/index.js", |
||||||
|
"scripts": { |
||||||
|
"build": "tsc --project tsconfig.build.json", |
||||||
|
"watch": "tsc --watch", |
||||||
|
"test": "jest --coverage", |
||||||
|
"test:watch": "jest --watch", |
||||||
|
"docs": "typedoc", |
||||||
|
"prepublishOnly": "npm run test && npm run build && npm run docs", |
||||||
|
"storybook": "storybook dev -p 6006", |
||||||
|
"build-storybook": "storybook build", |
||||||
|
"storybook:ldo": "ldo build --input stories/shapes --output stories/ldo", |
||||||
|
"build:ldo": "ldo build --input lib/shapes --output lib/ldo" |
||||||
|
}, |
||||||
|
"repository": { |
||||||
|
"type": "git", |
||||||
|
"url": "git+https://github.com/o-development/devtool-boilerplate.git" |
||||||
|
}, |
||||||
|
"author": "Jackson Morgan", |
||||||
|
"license": "MIT", |
||||||
|
"bugs": { |
||||||
|
"url": "https://github.com/o-development/devtool-boilerplate/issues" |
||||||
|
}, |
||||||
|
"homepage": "https://github.com/o-development/devtool-boilerplate#readme", |
||||||
|
"devDependencies": { |
||||||
|
"@babel/preset-env": "^7.22.10", |
||||||
|
"@babel/preset-react": "^7.22.5", |
||||||
|
"@babel/preset-typescript": "^7.22.11", |
||||||
|
"@rdfjs/types": "^1.1.0", |
||||||
|
"@types/jest": "^29.0.3", |
||||||
|
"@types/jsonld": "^1.5.8", |
||||||
|
"@types/n3": "^1.10.4", |
||||||
|
"@types/shexj": "^2.1.4", |
||||||
|
"ldo-cli": "^3.0.1", |
||||||
|
"prop-types": "^15.8.1", |
||||||
|
"ts-jest": "^29.0.2" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"@inrupt/solid-client": "^1.29.0", |
||||||
|
"cross-fetch": "^3.1.6", |
||||||
|
"jsonld-dataset-proxy": "^1.2.3", |
||||||
|
"ldo": "^1.0.3", |
||||||
|
"o-dataset-pack": "^0.2.14", |
||||||
|
"solid-authn-react-native": "^2.0.3", |
||||||
|
"stream": "^0.0.2" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
import { createContext, useContext } from "react"; |
||||||
|
import { BinaryResourceStoreDependencies } from "./document/resource/binaryResource/BinaryResourceStore"; |
||||||
|
import { DataResourceStoreDependencies } from "./document/resource/dataResource/DataResourceStore"; |
||||||
|
import { AccessRulesStoreDependencies } from "./document/accessRules/AccessRulesStore"; |
||||||
|
import { ContainerResourceStoreDependencies } from "./document/resource/dataResource/containerResource/ContainerResourceStore"; |
||||||
|
|
||||||
|
export interface LdoContextData |
||||||
|
extends BinaryResourceStoreDependencies, |
||||||
|
DataResourceStoreDependencies, |
||||||
|
AccessRulesStoreDependencies, |
||||||
|
ContainerResourceStoreDependencies {} |
||||||
|
|
||||||
|
// No default parameter is required as it will be set in the provider
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
const LdoContext = createContext<LdoContextData>({}); |
||||||
|
|
||||||
|
export const LdoContextProvider = LdoContext.Provider; |
||||||
|
export const useLdoContext = () => useContext(LdoContext); |
@ -0,0 +1,66 @@ |
|||||||
|
import React, { |
||||||
|
FunctionComponent, |
||||||
|
PropsWithChildren, |
||||||
|
useEffect, |
||||||
|
useMemo, |
||||||
|
} from "react"; |
||||||
|
import crossFetch from "cross-fetch"; |
||||||
|
import { createLdoDataset } from "ldo"; |
||||||
|
import { LdoContextData, LdoContextProvider } from "./LdoContext"; |
||||||
|
import { UpdateManager } from "./ldoHooks/helpers/UpdateManager"; |
||||||
|
import { BinaryResourceStore } from "./document/resource/binaryResource/BinaryResourceStore"; |
||||||
|
import { DataResourceStore } from "./document/resource/dataResource/DataResourceStore"; |
||||||
|
import { ContainerResourceStore } from "./document/resource/dataResource/containerResource/ContainerResourceStore"; |
||||||
|
import { AccessRulesStore } from "./document/accessRules/AccessRulesStore"; |
||||||
|
import { Dataset } from "@rdfjs/types"; |
||||||
|
|
||||||
|
export interface LdoProviderProps extends PropsWithChildren { |
||||||
|
fetch?: typeof fetch; |
||||||
|
dataset?: Dataset; |
||||||
|
onDocumentError?: LdoContextData["onDocumentError"]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Main Ldo Provider |
||||||
|
*/ |
||||||
|
export const LdoProvider: FunctionComponent< |
||||||
|
PropsWithChildren<LdoProviderProps> |
||||||
|
> = ({ dataset, fetch, onDocumentError, children }) => { |
||||||
|
const finalFetch = useMemo(() => fetch || crossFetch, [fetch]); |
||||||
|
const ldoDataset = useMemo(() => createLdoDataset(dataset), [dataset]); |
||||||
|
|
||||||
|
// Initialize storeDependencies before render
|
||||||
|
const storeDependencies = useMemo(() => { |
||||||
|
// Ingnoring this because we're setting up circular dependencies
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
const dependencies: LdoContextData = { |
||||||
|
onDocumentError, |
||||||
|
fetch: finalFetch, |
||||||
|
dataset: ldoDataset, |
||||||
|
updateManager: new UpdateManager(), |
||||||
|
}; |
||||||
|
const binaryResourceStore = new BinaryResourceStore(dependencies); |
||||||
|
const dataResourceStore = new DataResourceStore(dependencies); |
||||||
|
const containerResourceStore = new ContainerResourceStore(dependencies); |
||||||
|
const accessRulesStore = new AccessRulesStore(dependencies); |
||||||
|
dependencies.binaryResourceStore = binaryResourceStore; |
||||||
|
dependencies.dataResourceStore = dataResourceStore; |
||||||
|
dependencies.containerResourceStore = containerResourceStore; |
||||||
|
dependencies.accessRulesStore = accessRulesStore; |
||||||
|
return dependencies; |
||||||
|
}, []); |
||||||
|
|
||||||
|
// Update the resource manager in case fetch or ldo dataset changes
|
||||||
|
useEffect(() => { |
||||||
|
storeDependencies.fetch = finalFetch; |
||||||
|
storeDependencies.dataset = ldoDataset; |
||||||
|
storeDependencies.onDocumentError = onDocumentError; |
||||||
|
}, [finalFetch, ldoDataset, onDocumentError]); |
||||||
|
|
||||||
|
return ( |
||||||
|
<LdoContextProvider value={storeDependencies}> |
||||||
|
{children} |
||||||
|
</LdoContextProvider> |
||||||
|
); |
||||||
|
}; |
@ -0,0 +1,98 @@ |
|||||||
|
import { useCallback, useEffect, useMemo, useState } from "react"; |
||||||
|
import { |
||||||
|
ISessionInfo, |
||||||
|
handleIncomingRedirect, |
||||||
|
login as libraryLogin, |
||||||
|
getDefaultSession, |
||||||
|
logout as libraryLogout, |
||||||
|
fetch as libraryFetch, |
||||||
|
} from "solid-authn-react-native"; |
||||||
|
|
||||||
|
import { createGlobalHook } from "./util/createGlobalHook"; |
||||||
|
|
||||||
|
const PRE_REDIRECT_URI = "PRE_REDIRECT_URI"; |
||||||
|
|
||||||
|
interface AuthGlobalHookReturn { |
||||||
|
runInitialAuthCheck: () => Promise<void>; |
||||||
|
login: (issuer: string) => Promise<void>; |
||||||
|
logout: () => Promise<void>; |
||||||
|
signUp: (issuer: string) => Promise<void>; |
||||||
|
fetch: typeof fetch; |
||||||
|
session: ISessionInfo; |
||||||
|
ranInitialAuthCheck: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
function useAuthGlobalHookFunc(): AuthGlobalHookReturn { |
||||||
|
const [session, setSession] = useState<ISessionInfo>( |
||||||
|
getDefaultSession().info |
||||||
|
); |
||||||
|
const [ranInitialAuthCheck, setRanInitialAuthCheck] = useState(false); |
||||||
|
|
||||||
|
const runInitialAuthCheck = useCallback(async () => { |
||||||
|
// TODO: Change this to dependency injection so it works in React Native
|
||||||
|
if (!window.localStorage.getItem(PRE_REDIRECT_URI)) { |
||||||
|
window.localStorage.setItem(PRE_REDIRECT_URI, window.location.href); |
||||||
|
} |
||||||
|
|
||||||
|
await handleIncomingRedirect({ |
||||||
|
restorePreviousSession: true, |
||||||
|
}); |
||||||
|
setSession({ ...getDefaultSession().info }); |
||||||
|
|
||||||
|
window.history.replaceState( |
||||||
|
{}, |
||||||
|
"", |
||||||
|
window.localStorage.getItem(PRE_REDIRECT_URI) |
||||||
|
); |
||||||
|
window.localStorage.removeItem(PRE_REDIRECT_URI); |
||||||
|
|
||||||
|
setRanInitialAuthCheck(true); |
||||||
|
}, []); |
||||||
|
|
||||||
|
const login = useCallback( |
||||||
|
async (issuer: string, clientName = "Solid App") => { |
||||||
|
window.localStorage.setItem(PRE_REDIRECT_URI, window.location.href); |
||||||
|
await libraryLogin({ |
||||||
|
oidcIssuer: issuer, |
||||||
|
// TODO: this ties this to in-browser use
|
||||||
|
redirectUrl: window.location.href, |
||||||
|
clientName, |
||||||
|
}); |
||||||
|
setSession({ ...getDefaultSession().info }); |
||||||
|
}, |
||||||
|
[] |
||||||
|
); |
||||||
|
|
||||||
|
const logout = useCallback(async () => { |
||||||
|
await libraryLogout(); |
||||||
|
setSession({ ...getDefaultSession().info }); |
||||||
|
}, []); |
||||||
|
|
||||||
|
const signUp = useCallback(async (issuer: string) => { |
||||||
|
/* Do nothing for now */ |
||||||
|
console.log(`Signup Pressed with issuer ${issuer}`); |
||||||
|
}, []); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
runInitialAuthCheck(); |
||||||
|
}, []); |
||||||
|
|
||||||
|
return useMemo( |
||||||
|
() => ({ |
||||||
|
runInitialAuthCheck, |
||||||
|
login, |
||||||
|
logout, |
||||||
|
signUp, |
||||||
|
session, |
||||||
|
ranInitialAuthCheck, |
||||||
|
fetch: libraryFetch, |
||||||
|
}), |
||||||
|
[login, logout, ranInitialAuthCheck, runInitialAuthCheck, session, signUp] |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
const authGlobalHook = createGlobalHook(useAuthGlobalHookFunc); |
||||||
|
|
||||||
|
export const SolidAuthContext = authGlobalHook.Context; |
||||||
|
export const SolidAuthProvider = authGlobalHook.Provider; |
||||||
|
export const useSolidAuth = authGlobalHook.useGlobal; |
@ -0,0 +1,36 @@ |
|||||||
|
import { FetchableDocument } from "./FetchableDocument"; |
||||||
|
|
||||||
|
// This may eventually have fields
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
|
export interface DocumentStoreDependencies {} |
||||||
|
|
||||||
|
export abstract class DocumentStore< |
||||||
|
DocumentType extends FetchableDocument, |
||||||
|
Initializer, |
||||||
|
Dependencies extends DocumentStoreDependencies |
||||||
|
> { |
||||||
|
protected documentMap: Map<Initializer, DocumentType>; |
||||||
|
protected dependencies: Dependencies; |
||||||
|
|
||||||
|
constructor(dependencies: Dependencies) { |
||||||
|
this.documentMap = new Map(); |
||||||
|
this.dependencies = dependencies; |
||||||
|
} |
||||||
|
|
||||||
|
get(initializerInput: Initializer): DocumentType { |
||||||
|
const initializer = this.normalizeInitializer(initializerInput); |
||||||
|
const document = this.documentMap.get(initializer); |
||||||
|
if (document) { |
||||||
|
return document; |
||||||
|
} |
||||||
|
const newDocument = this.create(initializer); |
||||||
|
this.documentMap.set(initializer, newDocument); |
||||||
|
return newDocument; |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract create(initializer: Initializer): DocumentType; |
||||||
|
|
||||||
|
protected normalizeInitializer(initializer: Initializer): Initializer { |
||||||
|
return initializer; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,107 @@ |
|||||||
|
import EventEmitter from "events"; |
||||||
|
import { DocumentError } from "./errors/DocumentError"; |
||||||
|
|
||||||
|
export interface FetchableDocumentDependencies { |
||||||
|
onDocumentError?: (error: DocumentError) => void; |
||||||
|
} |
||||||
|
|
||||||
|
const STATE_UPDATE = "stateUpdate"; |
||||||
|
|
||||||
|
export abstract class FetchableDocument extends EventEmitter { |
||||||
|
protected _isLoading: boolean; |
||||||
|
protected _isWriting: boolean; |
||||||
|
protected _didInitialFetch: boolean; |
||||||
|
protected _error?: DocumentError; |
||||||
|
private dependencies; |
||||||
|
|
||||||
|
constructor(dependencies: FetchableDocumentDependencies) { |
||||||
|
super(); |
||||||
|
this._isLoading = false; |
||||||
|
this._isWriting = false; |
||||||
|
this._didInitialFetch = false; |
||||||
|
this.dependencies = dependencies; |
||||||
|
} |
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* Getters |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
get isLoading() { |
||||||
|
return this._isLoading; |
||||||
|
} |
||||||
|
|
||||||
|
get didInitialFetch() { |
||||||
|
return this._didInitialFetch; |
||||||
|
} |
||||||
|
|
||||||
|
get error() { |
||||||
|
return this._error; |
||||||
|
} |
||||||
|
|
||||||
|
get isWriting() { |
||||||
|
return this._isWriting; |
||||||
|
} |
||||||
|
|
||||||
|
protected get onDocumentError() { |
||||||
|
return this.dependencies.onDocumentError; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* Methods |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
async read() { |
||||||
|
this._isLoading = true; |
||||||
|
this.emitStateUpdate(); |
||||||
|
const documentError = await this.fetchDocument(); |
||||||
|
this._isLoading = false; |
||||||
|
this._didInitialFetch = true; |
||||||
|
if (documentError) { |
||||||
|
this.setError(documentError); |
||||||
|
} |
||||||
|
this.emitStateUpdate(); |
||||||
|
} |
||||||
|
|
||||||
|
async reload() { |
||||||
|
return this.read(); |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract fetchDocument(): Promise<DocumentError | undefined>; |
||||||
|
|
||||||
|
protected beginWrite() { |
||||||
|
this._isWriting = true; |
||||||
|
this.emitStateUpdate(); |
||||||
|
} |
||||||
|
|
||||||
|
protected endWrite(error?: DocumentError) { |
||||||
|
if (error) { |
||||||
|
this.setError(error); |
||||||
|
} |
||||||
|
this._isWriting = false; |
||||||
|
this.emitStateUpdate(); |
||||||
|
} |
||||||
|
|
||||||
|
setError(error: DocumentError) { |
||||||
|
this._error = error; |
||||||
|
this.emitStateUpdate(); |
||||||
|
if (this.onDocumentError) { |
||||||
|
this.onDocumentError(error); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Emitter Information |
||||||
|
*/ |
||||||
|
protected emitStateUpdate() { |
||||||
|
this.emit(STATE_UPDATE); |
||||||
|
} |
||||||
|
|
||||||
|
onStateUpdate(callback: () => void) { |
||||||
|
this.on(STATE_UPDATE, callback); |
||||||
|
} |
||||||
|
|
||||||
|
offStateUpdate(callback: () => void) { |
||||||
|
this.off(STATE_UPDATE, callback); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
import { |
||||||
|
universalAccess, |
||||||
|
AccessModes as IAccessModes, |
||||||
|
} from "@inrupt/solid-client"; |
||||||
|
import { |
||||||
|
FetchableDocument, |
||||||
|
FetchableDocumentDependencies, |
||||||
|
} from "../FetchableDocument"; |
||||||
|
import { Resource } from "../resource/Resource"; |
||||||
|
import { DocumentError } from "../errors/DocumentError"; |
||||||
|
|
||||||
|
export type AccessModes = IAccessModes; |
||||||
|
|
||||||
|
export interface AccessRulesDependencies extends FetchableDocumentDependencies { |
||||||
|
fetch: typeof fetch; |
||||||
|
} |
||||||
|
|
||||||
|
export class AccessRules extends FetchableDocument { |
||||||
|
readonly resource: Resource; |
||||||
|
private _publicAccess: IAccessModes | null; |
||||||
|
private _agentAccess: Record<string, IAccessModes> | null; |
||||||
|
private dependencies0; |
||||||
|
|
||||||
|
constructor(resource: Resource, dependencies: AccessRulesDependencies) { |
||||||
|
super(dependencies); |
||||||
|
this._publicAccess = null; |
||||||
|
this._agentAccess = null; |
||||||
|
this.dependencies0 = dependencies; |
||||||
|
this.resource = resource; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* Getters |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
get publicAccess() { |
||||||
|
return this._publicAccess; |
||||||
|
} |
||||||
|
|
||||||
|
get agentAccess() { |
||||||
|
return this._agentAccess; |
||||||
|
} |
||||||
|
|
||||||
|
protected get fetch() { |
||||||
|
return this.dependencies0.fetch; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* Methods |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
protected async fetchDocument() { |
||||||
|
try { |
||||||
|
const [publicAccess, agentAccess] = await Promise.all([ |
||||||
|
universalAccess.getPublicAccess(this.resource.uri, { |
||||||
|
fetch: this.fetch, |
||||||
|
}), |
||||||
|
universalAccess.getAgentAccessAll(this.resource.uri, { |
||||||
|
fetch: this.fetch, |
||||||
|
}), |
||||||
|
]); |
||||||
|
this._publicAccess = publicAccess || { |
||||||
|
read: false, |
||||||
|
write: false, |
||||||
|
append: false, |
||||||
|
controlRead: false, |
||||||
|
controlWrite: false, |
||||||
|
}; |
||||||
|
this._agentAccess = agentAccess || {}; |
||||||
|
return undefined; |
||||||
|
} catch (err: unknown) { |
||||||
|
if (typeof err === "object" && (err as Error).message) { |
||||||
|
this.setError(new DocumentError(this, (err as Error).message)); |
||||||
|
} |
||||||
|
this.setError(new DocumentError(this, "Error Fetching Access Rules")); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
import { DocumentStore, DocumentStoreDependencies } from "../DocumentStore"; |
||||||
|
import { Resource } from "../resource/Resource"; |
||||||
|
import { AccessRules, AccessRulesDependencies } from "./AccessRules"; |
||||||
|
|
||||||
|
export interface AccessRulesStoreDependencies |
||||||
|
extends DocumentStoreDependencies, |
||||||
|
AccessRulesDependencies {} |
||||||
|
|
||||||
|
export class AccessRulesStore extends DocumentStore< |
||||||
|
AccessRules, |
||||||
|
Resource, |
||||||
|
AccessRulesStoreDependencies |
||||||
|
> { |
||||||
|
create(initializer: Resource) { |
||||||
|
return new AccessRules(initializer, this.dependencies); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
import { FetchableDocument } from "../FetchableDocument"; |
||||||
|
|
||||||
|
export class DocumentError extends Error { |
||||||
|
public readonly document: FetchableDocument; |
||||||
|
|
||||||
|
constructor(document: FetchableDocument, message: string) { |
||||||
|
super(message); |
||||||
|
this.document = document; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
import { FetchableDocument } from "../FetchableDocument"; |
||||||
|
import { DocumentError } from "./DocumentError"; |
||||||
|
|
||||||
|
export class DocumentFetchError extends DocumentError { |
||||||
|
public readonly status: number; |
||||||
|
|
||||||
|
constructor(document: FetchableDocument, status: number, message: string) { |
||||||
|
super(document, message); |
||||||
|
this.status = status; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
import { |
||||||
|
FetchableDocument, |
||||||
|
FetchableDocumentDependencies, |
||||||
|
} from "../FetchableDocument"; |
||||||
|
import { AccessRulesStore } from "../accessRules/AccessRulesStore"; |
||||||
|
import { DocumentFetchError } from "../errors/DocumentFetchError"; |
||||||
|
import { ContainerResource } from "./dataResource/containerResource/ContainerResource"; |
||||||
|
import { ContainerResourceStore } from "./dataResource/containerResource/ContainerResourceStore"; |
||||||
|
|
||||||
|
export interface ResourceDependencies extends FetchableDocumentDependencies { |
||||||
|
fetch: typeof fetch; |
||||||
|
accessRulesStore: AccessRulesStore; |
||||||
|
containerResourceStore: ContainerResourceStore; |
||||||
|
} |
||||||
|
|
||||||
|
export abstract class Resource extends FetchableDocument { |
||||||
|
public readonly uri: string; |
||||||
|
private dependencies1; |
||||||
|
|
||||||
|
constructor(uri: string, dependencies: ResourceDependencies) { |
||||||
|
super(dependencies); |
||||||
|
this.uri = uri; |
||||||
|
this.dependencies1 = dependencies; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* Getters |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
get accessRules() { |
||||||
|
return this.accessRulesStore.get(this); |
||||||
|
} |
||||||
|
|
||||||
|
get parentContainer(): ContainerResource | undefined { |
||||||
|
return this.containerResourceStore.getContainerForResouce(this); |
||||||
|
} |
||||||
|
|
||||||
|
get ["@id"]() { |
||||||
|
return this.uri; |
||||||
|
} |
||||||
|
|
||||||
|
protected get fetch() { |
||||||
|
return this.dependencies1.fetch; |
||||||
|
} |
||||||
|
|
||||||
|
protected get accessRulesStore() { |
||||||
|
return this.dependencies1.accessRulesStore; |
||||||
|
} |
||||||
|
|
||||||
|
protected get containerResourceStore() { |
||||||
|
return this.dependencies1.containerResourceStore; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* Methods |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
async delete() { |
||||||
|
this.beginWrite(); |
||||||
|
const response = await this.fetch(this.uri, { |
||||||
|
method: "DELETE", |
||||||
|
}); |
||||||
|
if (response.status >= 200 && response.status < 300) { |
||||||
|
this.endWrite(); |
||||||
|
this.parentContainer?.removeContainedResources(this); |
||||||
|
return; |
||||||
|
} |
||||||
|
this.endWrite( |
||||||
|
new DocumentFetchError( |
||||||
|
this, |
||||||
|
response.status, |
||||||
|
`Could not delete ${this.uri}` |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* Static Methods |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
/** |
||||||
|
* Takes in a URL and will normalize it to the document it's fetching |
||||||
|
*/ |
||||||
|
static normalizeUri(uri: string): string { |
||||||
|
const [strippedHashUri] = uri.split("#"); |
||||||
|
return strippedHashUri; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
import { DocumentError } from "../../errors/DocumentError"; |
||||||
|
import { Resource, ResourceDependencies } from "../Resource"; |
||||||
|
|
||||||
|
export declare type BinaryResourceDependencies = ResourceDependencies; |
||||||
|
|
||||||
|
export class BinaryResource extends Resource { |
||||||
|
fetchDocument(): Promise<DocumentError | undefined> { |
||||||
|
throw new Error("Method not implemented."); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
import { DocumentStore, DocumentStoreDependencies } from "../../DocumentStore"; |
||||||
|
import { BinaryResource, BinaryResourceDependencies } from "./BinaryResource"; |
||||||
|
|
||||||
|
export interface BinaryResourceStoreDependencies |
||||||
|
extends DocumentStoreDependencies, |
||||||
|
BinaryResourceDependencies {} |
||||||
|
|
||||||
|
export class BinaryResourceStore extends DocumentStore< |
||||||
|
BinaryResource, |
||||||
|
string, |
||||||
|
BinaryResourceStoreDependencies |
||||||
|
> { |
||||||
|
create(initializer: string) { |
||||||
|
return new BinaryResource(initializer, this.dependencies); |
||||||
|
} |
||||||
|
|
||||||
|
protected normalizeInitializer(initializer: string): string { |
||||||
|
return BinaryResource.normalizeUri(initializer); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,127 @@ |
|||||||
|
import { LdoDataset, parseRdf } from "ldo"; |
||||||
|
import { Resource, ResourceDependencies } from "../Resource"; |
||||||
|
import { DocumentFetchError } from "../../errors/DocumentFetchError"; |
||||||
|
import { DocumentError } from "../../errors/DocumentError"; |
||||||
|
import { namedNode, quad as createQuad } from "@rdfjs/data-model"; |
||||||
|
import { DatasetChanges } from "o-dataset-pack"; |
||||||
|
import { Quad } from "@rdfjs/types"; |
||||||
|
import { changesToSparqlUpdate } from "../../../util/changesToSparqlUpdate"; |
||||||
|
import { UpdateManager } from "../../../ldoHooks/helpers/UpdateManager"; |
||||||
|
|
||||||
|
export interface DataResourceDependencies extends ResourceDependencies { |
||||||
|
dataset: LdoDataset; |
||||||
|
updateManager: UpdateManager; |
||||||
|
} |
||||||
|
|
||||||
|
export class DataResource extends Resource { |
||||||
|
private dependencies2; |
||||||
|
|
||||||
|
constructor(uri: string, dependencies: DataResourceDependencies) { |
||||||
|
super(uri, dependencies); |
||||||
|
this.dependencies2 = dependencies; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* Getters |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
protected get dataset() { |
||||||
|
return this.dependencies2.dataset; |
||||||
|
} |
||||||
|
|
||||||
|
protected get updateManager() { |
||||||
|
return this.dependencies2.updateManager; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* Methods |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
async create() { |
||||||
|
// TODO
|
||||||
|
} |
||||||
|
|
||||||
|
protected async fetchDocument(): Promise<DocumentError | undefined> { |
||||||
|
// Fetch the document using auth fetch
|
||||||
|
const response = await this.fetch(this.uri, { |
||||||
|
headers: { |
||||||
|
accept: "text/turtle", |
||||||
|
}, |
||||||
|
}); |
||||||
|
// Handle Error
|
||||||
|
if (response.status !== 200) { |
||||||
|
// TODO: Handle edge cases
|
||||||
|
return new DocumentFetchError( |
||||||
|
this, |
||||||
|
response.status, |
||||||
|
`Error fetching resource ${this.uri}` |
||||||
|
); |
||||||
|
} |
||||||
|
// Parse the incoming turtle into a dataset
|
||||||
|
const rawTurtle = await response.text(); |
||||||
|
let loadedDataset; |
||||||
|
try { |
||||||
|
loadedDataset = await parseRdf(rawTurtle, { |
||||||
|
baseIRI: this.uri, |
||||||
|
}); |
||||||
|
} catch (err) { |
||||||
|
if (typeof err === "object" && (err as Error).message) { |
||||||
|
return new DocumentError(this, (err as Error).message); |
||||||
|
} |
||||||
|
return new DocumentError(this, "Server returned poorly formatted Turtle"); |
||||||
|
} |
||||||
|
// Start transaction
|
||||||
|
const transactionalDataset = this.dataset.startTransaction(); |
||||||
|
const graphNode = namedNode(this.uri); |
||||||
|
// Destroy all triples that were once a part of this resouce
|
||||||
|
loadedDataset.deleteMatches(undefined, undefined, undefined, graphNode); |
||||||
|
// Add the triples from the fetched item
|
||||||
|
loadedDataset.forEach((quad) => { |
||||||
|
transactionalDataset.add( |
||||||
|
createQuad(quad.subject, quad.predicate, quad.object, graphNode) |
||||||
|
); |
||||||
|
}); |
||||||
|
const changes = transactionalDataset.getChanges(); |
||||||
|
this.updateManager.notifyListenersOfChanges(changes); |
||||||
|
transactionalDataset.commit(); |
||||||
|
return undefined; |
||||||
|
} |
||||||
|
|
||||||
|
async update( |
||||||
|
changes: DatasetChanges<Quad> |
||||||
|
): Promise<DocumentError | undefined> { |
||||||
|
this.beginWrite(); |
||||||
|
// Convert changes to transactional Dataset
|
||||||
|
const transactionalDataset = this.dataset.startTransaction(); |
||||||
|
changes.added?.forEach((quad) => transactionalDataset.add(quad)); |
||||||
|
changes.removed?.forEach((quad) => transactionalDataset.delete(quad)); |
||||||
|
// Commit data optimistically
|
||||||
|
transactionalDataset.commit(); |
||||||
|
this.updateManager.notifyListenersOfChanges(changes); |
||||||
|
// Make request
|
||||||
|
const sparqlUpdate = await changesToSparqlUpdate(changes); |
||||||
|
const response = await this.fetch(this.uri, { |
||||||
|
method: "PATCH", |
||||||
|
body: sparqlUpdate, |
||||||
|
headers: { |
||||||
|
"Content-Type": "application/sparql-update", |
||||||
|
}, |
||||||
|
}); |
||||||
|
if (response.status < 200 || response.status > 299) { |
||||||
|
// Handle Error by rollback
|
||||||
|
transactionalDataset.rollback(); |
||||||
|
this.updateManager.notifyListenersOfChanges(changes); |
||||||
|
this.endWrite( |
||||||
|
new DocumentFetchError( |
||||||
|
this, |
||||||
|
response.status, |
||||||
|
`Problem writing to ${this.uri}` |
||||||
|
) |
||||||
|
); |
||||||
|
return; |
||||||
|
} |
||||||
|
this.endWrite(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
import { DocumentStore, DocumentStoreDependencies } from "../../DocumentStore"; |
||||||
|
import { DataResource, DataResourceDependencies } from "./DataResource"; |
||||||
|
|
||||||
|
export interface DataResourceStoreDependencies |
||||||
|
extends DocumentStoreDependencies, |
||||||
|
DataResourceDependencies {} |
||||||
|
|
||||||
|
export class DataResourceStore extends DocumentStore< |
||||||
|
DataResource, |
||||||
|
string, |
||||||
|
DataResourceStoreDependencies |
||||||
|
> { |
||||||
|
protected create(initializer: string) { |
||||||
|
return new DataResource(initializer, this.dependencies); |
||||||
|
} |
||||||
|
|
||||||
|
protected normalizeInitializer(initializer: string): string { |
||||||
|
return DataResource.normalizeUri(initializer); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
import { ContainerShapeType } from "../../../../ldo/solid.shapeTypes"; |
||||||
|
import { Resource } from "../../Resource"; |
||||||
|
import { BinaryResourceStore } from "../../binaryResource/BinaryResourceStore"; |
||||||
|
import { DataResource, DataResourceDependencies } from "../DataResource"; |
||||||
|
import { DataResourceStore } from "../DataResourceStore"; |
||||||
|
|
||||||
|
export interface ContainerResourceDependencies |
||||||
|
extends DataResourceDependencies { |
||||||
|
dataResourceStore: DataResourceStore; |
||||||
|
binaryResourceStore: BinaryResourceStore; |
||||||
|
} |
||||||
|
|
||||||
|
export class ContainerResource extends DataResource { |
||||||
|
private _contains: Set<Resource>; |
||||||
|
private dependencies3: ContainerResourceDependencies; |
||||||
|
|
||||||
|
constructor(uri: string, dependencies: ContainerResourceDependencies) { |
||||||
|
super(uri, dependencies); |
||||||
|
this._contains = new Set(); |
||||||
|
this.dependencies3 = dependencies; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* Getters |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
get contains() { |
||||||
|
return Array.from(this._contains); |
||||||
|
} |
||||||
|
|
||||||
|
protected get binaryResourceStore() { |
||||||
|
return this.dependencies3.binaryResourceStore; |
||||||
|
} |
||||||
|
|
||||||
|
protected get dataResourceStore() { |
||||||
|
return this.dependencies3.dataResourceStore; |
||||||
|
} |
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* Methods |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
protected async fetchDocument() { |
||||||
|
const error = await super.fetchDocument(); |
||||||
|
if (error) { |
||||||
|
return error; |
||||||
|
} |
||||||
|
// Update the contains
|
||||||
|
const container = this.dataset |
||||||
|
.usingType(ContainerShapeType) |
||||||
|
.fromSubject(this.uri); |
||||||
|
const resourcesToAdd: Resource[] = []; |
||||||
|
container.contains?.forEach((resourceData) => { |
||||||
|
if (resourceData["@id"]) { |
||||||
|
if (resourceData.type?.some((type) => type["@id"] === "Container")) { |
||||||
|
resourcesToAdd.push( |
||||||
|
this.containerResourceStore.get(resourceData["@id"]) |
||||||
|
); |
||||||
|
} else { |
||||||
|
if (resourceData["@id"].endsWith(".ttl")) { |
||||||
|
resourcesToAdd.push( |
||||||
|
this.dataResourceStore.get(resourceData["@id"]) |
||||||
|
); |
||||||
|
} else { |
||||||
|
resourcesToAdd.push( |
||||||
|
this.binaryResourceStore.get(resourceData["@id"]) |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
this.addContainedResources(...resourcesToAdd); |
||||||
|
} |
||||||
|
|
||||||
|
public addContainedResources(...resources: Resource[]) { |
||||||
|
let someResourceUpdated = false; |
||||||
|
resources.forEach((resource) => { |
||||||
|
if (!this._contains.has(resource)) { |
||||||
|
someResourceUpdated = true; |
||||||
|
this._contains.add(resource); |
||||||
|
this.parentContainer?.addContainedResources(this); |
||||||
|
} |
||||||
|
}); |
||||||
|
if (someResourceUpdated) { |
||||||
|
this.emitStateUpdate(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public removeContainedResources(...resources: Resource[]) { |
||||||
|
let someResourceUpdated = false; |
||||||
|
resources.forEach((resource) => { |
||||||
|
if (this._contains.has(resource)) { |
||||||
|
someResourceUpdated = true; |
||||||
|
this._contains.delete(resource); |
||||||
|
} |
||||||
|
}); |
||||||
|
if (someResourceUpdated) { |
||||||
|
this.emitStateUpdate(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
import { |
||||||
|
DocumentStore, |
||||||
|
DocumentStoreDependencies, |
||||||
|
} from "../../../DocumentStore"; |
||||||
|
import { Resource } from "../../Resource"; |
||||||
|
import { |
||||||
|
ContainerResource, |
||||||
|
ContainerResourceDependencies, |
||||||
|
} from "./ContainerResource"; |
||||||
|
|
||||||
|
export interface ContainerResourceStoreDependencies |
||||||
|
extends ContainerResourceDependencies, |
||||||
|
DocumentStoreDependencies {} |
||||||
|
|
||||||
|
export class ContainerResourceStore extends DocumentStore< |
||||||
|
ContainerResource, |
||||||
|
string, |
||||||
|
ContainerResourceStoreDependencies |
||||||
|
> { |
||||||
|
protected create(initializer: string) { |
||||||
|
return new ContainerResource(initializer, this.dependencies); |
||||||
|
} |
||||||
|
|
||||||
|
protected normalizeInitializer(initializer: string) { |
||||||
|
return ContainerResource.normalizeUri(initializer); |
||||||
|
} |
||||||
|
|
||||||
|
getContainerForResouce(resource: Resource) { |
||||||
|
const parentUri = ContainerResourceStore.getParentUri(resource.uri); |
||||||
|
return parentUri ? this.get(parentUri) : undefined; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the parent container URI |
||||||
|
*/ |
||||||
|
static getParentUri(uri: string) { |
||||||
|
const urlObject = new URL(uri); |
||||||
|
const pathItems = urlObject.pathname.split("/"); |
||||||
|
if ( |
||||||
|
pathItems.length < 2 || |
||||||
|
(pathItems.length === 2 && pathItems[1].length === 0) |
||||||
|
) { |
||||||
|
return undefined; |
||||||
|
} |
||||||
|
if (pathItems[pathItems.length - 1] === "") { |
||||||
|
pathItems.pop(); |
||||||
|
} |
||||||
|
pathItems.pop(); |
||||||
|
urlObject.pathname = `${pathItems.join("/")}/`; |
||||||
|
return urlObject.toString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
import { useMemo } from "react"; |
||||||
|
import { useLdoContext } from "../LdoContext"; |
||||||
|
import { UseDocumentOptions, useDocument } from "./useDocument"; |
||||||
|
import { Resource } from "../document/resource/Resource"; |
||||||
|
|
||||||
|
export function useAccessRules( |
||||||
|
resource: string | Resource, |
||||||
|
options?: UseDocumentOptions |
||||||
|
) { |
||||||
|
const { dataResourceStore, accessRulesStore } = useLdoContext(); |
||||||
|
const realResource = useMemo(() => { |
||||||
|
return typeof resource === "string" |
||||||
|
? dataResourceStore.get(resource) |
||||||
|
: resource; |
||||||
|
}, [resource]); |
||||||
|
return useDocument(realResource, accessRulesStore, options); |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
import { useLdoContext } from "../LdoContext"; |
||||||
|
import { UseDocumentOptions, useDocument } from "./useDocument"; |
||||||
|
|
||||||
|
export function useBinaryResource(uri: string, options?: UseDocumentOptions) { |
||||||
|
const { binaryResourceStore } = useLdoContext(); |
||||||
|
return useDocument(uri, binaryResourceStore, options); |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
import { useLdoContext } from "../LdoContext"; |
||||||
|
import { UseDocumentOptions, useDocument } from "./useDocument"; |
||||||
|
|
||||||
|
export function useContainerResource( |
||||||
|
uri: string, |
||||||
|
options?: UseDocumentOptions |
||||||
|
) { |
||||||
|
const { containerResourceStore } = useLdoContext(); |
||||||
|
return useDocument(uri, containerResourceStore, options); |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
import { useLdoContext } from "../LdoContext"; |
||||||
|
import { UseDocumentOptions, useDocument } from "./useDocument"; |
||||||
|
|
||||||
|
export function useDataResource(uri: string, options?: UseDocumentOptions) { |
||||||
|
const { dataResourceStore } = useLdoContext(); |
||||||
|
return useDocument(uri, dataResourceStore, options); |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
import { useEffect, useMemo } from "react"; |
||||||
|
import { |
||||||
|
DocumentStore, |
||||||
|
DocumentStoreDependencies, |
||||||
|
} from "../document/DocumentStore"; |
||||||
|
import { FetchableDocument } from "../document/FetchableDocument"; |
||||||
|
import { useForceUpdate } from "../util/useForceReload"; |
||||||
|
|
||||||
|
export interface UseDocumentOptions { |
||||||
|
suppressLoadOnMount: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
export function useDocument< |
||||||
|
DocumentType extends FetchableDocument, |
||||||
|
Initializer |
||||||
|
>( |
||||||
|
initializer: Initializer, |
||||||
|
documentStore: DocumentStore< |
||||||
|
DocumentType, |
||||||
|
Initializer, |
||||||
|
DocumentStoreDependencies |
||||||
|
>, |
||||||
|
options?: UseDocumentOptions |
||||||
|
) { |
||||||
|
const document = useMemo(() => { |
||||||
|
return documentStore.get(initializer); |
||||||
|
}, [initializer, documentStore]); |
||||||
|
|
||||||
|
const forceUpdate = useForceUpdate(); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
// Set up the listener for state update
|
||||||
|
function onStateUpdateCallback() { |
||||||
|
forceUpdate(); |
||||||
|
} |
||||||
|
document.onStateUpdate(onStateUpdateCallback); |
||||||
|
// Load the resource if load on mount is true
|
||||||
|
if (!options?.suppressLoadOnMount) { |
||||||
|
document.read(); |
||||||
|
} |
||||||
|
return () => document.offStateUpdate(onStateUpdateCallback); |
||||||
|
}, []); |
||||||
|
|
||||||
|
return document; |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
// document
|
||||||
|
export * from "./document/FetchableDocument"; |
||||||
|
export * from "./document/DocumentStore"; |
||||||
|
|
||||||
|
// document/errors
|
||||||
|
export * from "./document/errors/DocumentError"; |
||||||
|
export * from "./document/errors/DocumentFetchError"; |
||||||
|
|
||||||
|
// document/accessRules
|
||||||
|
export * from "./document/accessRules/AccessRules"; |
||||||
|
export * from "./document/accessRules/AccessRulesStore"; |
||||||
|
|
||||||
|
// document/resource
|
||||||
|
export * from "./document/resource/Resource"; |
||||||
|
|
||||||
|
// document/resource/binaryResource
|
||||||
|
export * from "./document/resource/binaryResource/BinaryResource"; |
||||||
|
export * from "./document/resource/binaryResource/BinaryResourceStore"; |
||||||
|
|
||||||
|
// document/resource/dataResource
|
||||||
|
export * from "./document/resource/dataResource/DataResource"; |
||||||
|
export * from "./document/resource/dataResource/DataResourceStore"; |
||||||
|
|
||||||
|
// document/resource/containerResource
|
||||||
|
export * from "./document/resource/dataResource/containerResource/ContainerResource"; |
||||||
|
export * from "./document/resource/dataResource/containerResource/ContainerResourceStore"; |
||||||
|
|
||||||
|
// documentHooks
|
||||||
|
export * from "./documentHooks/useAccessRules"; |
||||||
|
export * from "./documentHooks/useBinaryResource"; |
||||||
|
export * from "./documentHooks/useContainerResource"; |
||||||
|
export * from "./documentHooks/useDataResource"; |
||||||
|
export * from "./documentHooks/useDocument"; |
||||||
|
|
||||||
|
// ldoHooks
|
||||||
|
export * from "./ldoHooks/useSubject"; |
||||||
|
|
||||||
|
// export
|
||||||
|
export * from "./useLdo"; |
||||||
|
export * from "./LdoProvider"; |
||||||
|
export * from "./SolidAuthProvider"; |
@ -0,0 +1,36 @@ |
|||||||
|
import { ContextDefinition } from "jsonld"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* solidContext: JSONLD Context for solid |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
export const solidContext: ContextDefinition = { |
||||||
|
type: { |
||||||
|
"@id": "@type", |
||||||
|
"@container": "@set", |
||||||
|
}, |
||||||
|
Container: "http://www.w3.org/ns/ldp#Container", |
||||||
|
Resource: "http://www.w3.org/ns/ldp#Resource", |
||||||
|
modified: { |
||||||
|
"@id": "http://purl.org/dc/terms/modified", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
"@container": "@set", |
||||||
|
}, |
||||||
|
contains: { |
||||||
|
"@id": "http://www.w3.org/ns/ldp#contains", |
||||||
|
"@type": "@id", |
||||||
|
"@container": "@set", |
||||||
|
}, |
||||||
|
Resource2: "http://www.w3.org/ns/iana/media-types/text/turtle#Resource", |
||||||
|
mtime: { |
||||||
|
"@id": "http://www.w3.org/ns/posix/stat#mtime", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#decimal", |
||||||
|
"@container": "@set", |
||||||
|
}, |
||||||
|
size: { |
||||||
|
"@id": "http://www.w3.org/ns/posix/stat#size", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#integer", |
||||||
|
"@container": "@set", |
||||||
|
}, |
||||||
|
}; |
@ -0,0 +1,214 @@ |
|||||||
|
import { Schema } from "shexj"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* solidSchema: ShexJ Schema for solid |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
export const solidSchema: Schema = { |
||||||
|
type: "Schema", |
||||||
|
shapes: [ |
||||||
|
{ |
||||||
|
id: "http://www.w3.org/ns/lddps#Container", |
||||||
|
type: "ShapeDecl", |
||||||
|
shapeExpr: { |
||||||
|
type: "Shape", |
||||||
|
expression: { |
||||||
|
id: "http://www.w3.org/ns/lddps#ContainerShape", |
||||||
|
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/ns/ldp#Container", |
||||||
|
"http://www.w3.org/ns/ldp#Resource", |
||||||
|
], |
||||||
|
}, |
||||||
|
min: 0, |
||||||
|
max: -1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "A container on a Solid server", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://purl.org/dc/terms/modified", |
||||||
|
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: "Date modified", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/ns/ldp#contains", |
||||||
|
valueExpr: "http://www.w3.org/ns/lddps#Resource", |
||||||
|
min: 0, |
||||||
|
max: -1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "Defines a Solid Resource", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/ns/posix/stat#mtime", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
datatype: "http://www.w3.org/2001/XMLSchema#decimal", |
||||||
|
}, |
||||||
|
min: 0, |
||||||
|
max: 1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "?", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/ns/posix/stat#size", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
datatype: "http://www.w3.org/2001/XMLSchema#integer", |
||||||
|
}, |
||||||
|
min: 0, |
||||||
|
max: 1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "size of this container", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: "http://www.w3.org/ns/lddps#Resource", |
||||||
|
type: "ShapeDecl", |
||||||
|
shapeExpr: { |
||||||
|
type: "Shape", |
||||||
|
expression: { |
||||||
|
id: "http://www.w3.org/ns/lddps#ResourceShape", |
||||||
|
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/ns/ldp#Resource", |
||||||
|
"http://www.w3.org/ns/iana/media-types/text/turtle#Resource", |
||||||
|
], |
||||||
|
}, |
||||||
|
min: 0, |
||||||
|
max: -1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "Any resource on a Solid server", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://purl.org/dc/terms/modified", |
||||||
|
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: "Date modified", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/ns/posix/stat#mtime", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
datatype: "http://www.w3.org/2001/XMLSchema#decimal", |
||||||
|
}, |
||||||
|
min: 0, |
||||||
|
max: 1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "?", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/ns/posix/stat#size", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
datatype: "http://www.w3.org/2001/XMLSchema#integer", |
||||||
|
}, |
||||||
|
min: 0, |
||||||
|
max: 1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "size of this container", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}; |
@ -0,0 +1,28 @@ |
|||||||
|
import { ShapeType } from "ldo"; |
||||||
|
import { solidSchema } from "./solid.schema"; |
||||||
|
import { solidContext } from "./solid.context"; |
||||||
|
import { Container, Resource } from "./solid.typings"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* LDO ShapeTypes solid |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Container ShapeType |
||||||
|
*/ |
||||||
|
export const ContainerShapeType: ShapeType<Container> = { |
||||||
|
schema: solidSchema, |
||||||
|
shape: "http://www.w3.org/ns/lddps#Container", |
||||||
|
context: solidContext, |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* Resource ShapeType |
||||||
|
*/ |
||||||
|
export const ResourceShapeType: ShapeType<Resource> = { |
||||||
|
schema: solidSchema, |
||||||
|
shape: "http://www.w3.org/ns/lddps#Resource", |
||||||
|
context: solidContext, |
||||||
|
}; |
@ -0,0 +1,73 @@ |
|||||||
|
import { ContextDefinition } from "jsonld"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* Typescript Typings for solid |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Container Type |
||||||
|
*/ |
||||||
|
export interface Container { |
||||||
|
"@id"?: string; |
||||||
|
"@context"?: ContextDefinition; |
||||||
|
/** |
||||||
|
* A container on a Solid server |
||||||
|
*/ |
||||||
|
type?: ( |
||||||
|
| { |
||||||
|
"@id": "Container"; |
||||||
|
} |
||||||
|
| { |
||||||
|
"@id": "Resource"; |
||||||
|
} |
||||||
|
)[]; |
||||||
|
/** |
||||||
|
* Date modified |
||||||
|
*/ |
||||||
|
modified?: string; |
||||||
|
/** |
||||||
|
* Defines a Solid Resource |
||||||
|
*/ |
||||||
|
contains?: (Resource | Container)[]; |
||||||
|
/** |
||||||
|
* ? |
||||||
|
*/ |
||||||
|
mtime?: number; |
||||||
|
/** |
||||||
|
* size of this container |
||||||
|
*/ |
||||||
|
size?: number; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resource Type |
||||||
|
*/ |
||||||
|
export interface Resource { |
||||||
|
"@id"?: string; |
||||||
|
"@context"?: ContextDefinition; |
||||||
|
/** |
||||||
|
* Any resource on a Solid server |
||||||
|
*/ |
||||||
|
type?: ( |
||||||
|
| { |
||||||
|
"@id": "Resource"; |
||||||
|
} |
||||||
|
| { |
||||||
|
"@id": "Resource2"; |
||||||
|
} |
||||||
|
)[]; |
||||||
|
/** |
||||||
|
* Date modified |
||||||
|
*/ |
||||||
|
modified?: string; |
||||||
|
/** |
||||||
|
* ? |
||||||
|
*/ |
||||||
|
mtime?: number; |
||||||
|
/** |
||||||
|
* size of this container |
||||||
|
*/ |
||||||
|
size?: number; |
||||||
|
} |
@ -0,0 +1,98 @@ |
|||||||
|
import { |
||||||
|
ArrayProxyTarget, |
||||||
|
ProxyContext, |
||||||
|
SubjectProxyTarget, |
||||||
|
ProxyContextOptions, |
||||||
|
} from "jsonld-dataset-proxy"; |
||||||
|
import { UpdateManager } from "./UpdateManager"; |
||||||
|
import { namedNode } from "@rdfjs/data-model"; |
||||||
|
|
||||||
|
export class TrackingProxyContext extends ProxyContext { |
||||||
|
private updateManager: UpdateManager; |
||||||
|
private listener: () => void; |
||||||
|
|
||||||
|
constructor( |
||||||
|
options: ProxyContextOptions, |
||||||
|
updateManager: UpdateManager, |
||||||
|
listener: () => void |
||||||
|
) { |
||||||
|
super(options); |
||||||
|
this.updateManager = updateManager; |
||||||
|
this.listener = listener; |
||||||
|
} |
||||||
|
|
||||||
|
protected createSubjectHandler(): ProxyHandler<SubjectProxyTarget> { |
||||||
|
const baseHandler = super.createSubjectHandler(); |
||||||
|
const oldGetFunction = baseHandler.get; |
||||||
|
const newGetFunction: ProxyHandler<SubjectProxyTarget>["get"] = ( |
||||||
|
target: SubjectProxyTarget, |
||||||
|
key: string | symbol, |
||||||
|
receiver |
||||||
|
) => { |
||||||
|
const subject = target["@id"]; |
||||||
|
if (typeof key === "symbol") { |
||||||
|
// Do Nothing
|
||||||
|
} else if (key === "@id") { |
||||||
|
this.updateManager.registerListener( |
||||||
|
[subject, null, null], |
||||||
|
this.listener |
||||||
|
); |
||||||
|
} else if (!this.contextUtil.isArray(key)) { |
||||||
|
const predicate = namedNode(this.contextUtil.keyToIri(key)); |
||||||
|
this.updateManager.registerListener( |
||||||
|
[subject, predicate, null], |
||||||
|
this.listener |
||||||
|
); |
||||||
|
} |
||||||
|
return oldGetFunction && oldGetFunction(target, key, receiver); |
||||||
|
}; |
||||||
|
baseHandler.get = newGetFunction; |
||||||
|
baseHandler.set = () => { |
||||||
|
console.warn( |
||||||
|
"You've attempted to set a value on a Linked Data Object from the useSubject, useMatchingSubject, or useMatchingObject hooks. These linked data objects should only be used to render data, not modify it. To modify data, use the `changeData` function." |
||||||
|
); |
||||||
|
return true; |
||||||
|
}; |
||||||
|
return baseHandler; |
||||||
|
} |
||||||
|
|
||||||
|
protected createArrayHandler(): ProxyHandler<ArrayProxyTarget> { |
||||||
|
const baseHandler = super.createArrayHandler(); |
||||||
|
const oldGetFunction = baseHandler.get; |
||||||
|
const newGetFunction: ProxyHandler<ArrayProxyTarget>["get"] = ( |
||||||
|
target: ArrayProxyTarget, |
||||||
|
key: string | symbol, |
||||||
|
receiver |
||||||
|
) => { |
||||||
|
if (qualifiedArrayMethods.has(key)) { |
||||||
|
this.updateManager.registerListener( |
||||||
|
[target[0][0], target[0][1], target[0][2]], |
||||||
|
this.listener |
||||||
|
); |
||||||
|
} |
||||||
|
return oldGetFunction && oldGetFunction(target, key, receiver); |
||||||
|
}; |
||||||
|
baseHandler.get = newGetFunction; |
||||||
|
return baseHandler; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const qualifiedArrayMethods = new Set([ |
||||||
|
"forEach", |
||||||
|
"map", |
||||||
|
"reduce", |
||||||
|
Symbol.iterator, |
||||||
|
"entries", |
||||||
|
"every", |
||||||
|
"filter", |
||||||
|
"find", |
||||||
|
"findIndex", |
||||||
|
"findLast", |
||||||
|
"findLastIndex", |
||||||
|
"includes, indexOf", |
||||||
|
"keys", |
||||||
|
"lastIndexOf", |
||||||
|
"reduceRight", |
||||||
|
"some", |
||||||
|
"values", |
||||||
|
]); |
@ -0,0 +1,83 @@ |
|||||||
|
import { DatasetChanges, createDataset } from "o-dataset-pack"; |
||||||
|
import { |
||||||
|
QuadMatch, |
||||||
|
SubjectType, |
||||||
|
PredicateType, |
||||||
|
ObjectType, |
||||||
|
nodeToString, |
||||||
|
} from "jsonld-dataset-proxy"; |
||||||
|
import { Quad } from "@rdfjs/types"; |
||||||
|
|
||||||
|
export type TripleMatch = [QuadMatch[0], QuadMatch[1], QuadMatch[2]]; |
||||||
|
|
||||||
|
export class UpdateManager { |
||||||
|
private tripleMatchListenerMap: Record<string, Set<() => void>> = {}; |
||||||
|
private listenerHashMap: Map<() => void, Set<string>> = new Map(); |
||||||
|
|
||||||
|
private tripleMatchToHash(tripleMatch: TripleMatch): string { |
||||||
|
return `${nodeToString(tripleMatch[0])}${nodeToString( |
||||||
|
tripleMatch[1] |
||||||
|
)}${nodeToString(tripleMatch[2])}`;
|
||||||
|
} |
||||||
|
|
||||||
|
registerListener(tripleMatch: TripleMatch, callback: () => void): void { |
||||||
|
const hash = this.tripleMatchToHash(tripleMatch); |
||||||
|
if (!this.tripleMatchListenerMap[hash]) { |
||||||
|
this.tripleMatchListenerMap[hash] = new Set(); |
||||||
|
} |
||||||
|
if (!this.listenerHashMap.has(callback)) { |
||||||
|
this.listenerHashMap.set(callback, new Set()); |
||||||
|
} |
||||||
|
this.tripleMatchListenerMap[hash].add(callback); |
||||||
|
this.listenerHashMap.get(callback)?.add(hash); |
||||||
|
} |
||||||
|
|
||||||
|
removeListener(callback: () => void) { |
||||||
|
const hashSet = this.listenerHashMap.get(callback); |
||||||
|
if (hashSet) { |
||||||
|
hashSet.forEach((hash) => { |
||||||
|
this.tripleMatchListenerMap[hash]?.delete(callback); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
notifyListenersOfChanges(changes: DatasetChanges<Quad>): void { |
||||||
|
const listenersToNotify = new Set<() => void>(); |
||||||
|
|
||||||
|
const allQuads = createDataset(); |
||||||
|
allQuads.addAll(changes.added || []); |
||||||
|
allQuads.addAll(changes.removed || []); |
||||||
|
|
||||||
|
// Iterate through all quads looking for any dataset match they effect
|
||||||
|
allQuads.forEach((tempQuad) => { |
||||||
|
// Cast the input because RDFJS types assume RDF 1.2 where a Subject can
|
||||||
|
// be a Quad
|
||||||
|
const quad = tempQuad as { |
||||||
|
subject: SubjectType; |
||||||
|
predicate: PredicateType; |
||||||
|
object: ObjectType; |
||||||
|
}; |
||||||
|
const tripleMatches: TripleMatch[] = [ |
||||||
|
[null, null, null], |
||||||
|
[quad.subject, null, null], |
||||||
|
[quad.subject, quad.predicate, null], |
||||||
|
[quad.subject, null, quad.object], |
||||||
|
[null, quad.predicate, null], |
||||||
|
[null, quad.predicate, quad.object], |
||||||
|
[null, null, quad.object], |
||||||
|
[quad.subject, quad.predicate, quad.object], |
||||||
|
]; |
||||||
|
|
||||||
|
tripleMatches.forEach((tripleMatch) => { |
||||||
|
const hash = this.tripleMatchToHash(tripleMatch); |
||||||
|
this.tripleMatchListenerMap[hash]?.forEach((callback) => { |
||||||
|
listenersToNotify.add(callback); |
||||||
|
}); |
||||||
|
delete this.tripleMatchListenerMap[hash]; |
||||||
|
}); |
||||||
|
}); |
||||||
|
listenersToNotify.forEach((listener) => { |
||||||
|
listener(); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
import { |
||||||
|
ContextUtil, |
||||||
|
JsonldDatasetProxyBuilder, |
||||||
|
SubjectType, |
||||||
|
} from "jsonld-dataset-proxy"; |
||||||
|
import { LdoBuilder, ShapeType } from "ldo"; |
||||||
|
import { LdoBase } from "ldo/dist/util"; |
||||||
|
import { useLdoContext } from "../LdoContext"; |
||||||
|
import { useCallback, useEffect, useMemo, useState } from "react"; |
||||||
|
import { TrackingProxyContext } from "./helpers/TrackingProxyContext"; |
||||||
|
import { defaultGraph } from "@rdfjs/data-model"; |
||||||
|
|
||||||
|
export function useSubject<Type extends LdoBase>( |
||||||
|
shapeType: ShapeType<Type>, |
||||||
|
subject: string | SubjectType |
||||||
|
): [Type, undefined] | [undefined, Error] { |
||||||
|
const { dataset, updateManager } = useLdoContext(); |
||||||
|
|
||||||
|
const [forceUpdateCounter, setForceUpdateCounter] = useState(0); |
||||||
|
const forceUpdate = useCallback( |
||||||
|
() => setForceUpdateCounter((val) => val + 1), |
||||||
|
[setForceUpdateCounter] |
||||||
|
); |
||||||
|
|
||||||
|
// The main linked data object
|
||||||
|
const linkedDataObject = useMemo(() => { |
||||||
|
// Rebuild the LdoBuilder from scratch to inject TrackingProxyContext
|
||||||
|
const contextUtil = new ContextUtil(shapeType.context); |
||||||
|
const proxyContext = new TrackingProxyContext( |
||||||
|
{ |
||||||
|
dataset, |
||||||
|
contextUtil, |
||||||
|
writeGraphs: [defaultGraph()], |
||||||
|
languageOrdering: ["none", "en", "other"], |
||||||
|
}, |
||||||
|
updateManager, |
||||||
|
forceUpdate |
||||||
|
); |
||||||
|
const builder = new LdoBuilder( |
||||||
|
new JsonldDatasetProxyBuilder(proxyContext), |
||||||
|
shapeType |
||||||
|
); |
||||||
|
return builder.fromSubject(subject); |
||||||
|
}, [ |
||||||
|
shapeType, |
||||||
|
subject, |
||||||
|
dataset, |
||||||
|
updateManager, |
||||||
|
forceUpdateCounter, |
||||||
|
forceUpdate, |
||||||
|
]); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
// Unregister force update listener upon unmount
|
||||||
|
return () => updateManager.removeListener(forceUpdate); |
||||||
|
}, [shapeType, subject]); |
||||||
|
|
||||||
|
return [linkedDataObject, undefined]; |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> |
||||||
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> |
||||||
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> |
||||||
|
PREFIX ldp: <http://www.w3.org/ns/ldp#> |
||||||
|
PREFIX ldps: <http://www.w3.org/ns/lddps#> |
||||||
|
PREFIX dct: <http://purl.org/dc/terms/> |
||||||
|
PREFIX stat: <http://www.w3.org/ns/posix/stat#> |
||||||
|
PREFIX tur: <http://www.w3.org/ns/iana/media-types/text/turtle#> |
||||||
|
|
||||||
|
ldps:Container EXTRA a { |
||||||
|
$ldps:ContainerShape ( |
||||||
|
a [ ldp:Container ldp:Resource ]* |
||||||
|
// rdfs:comment "A container on a Solid server"; |
||||||
|
dct:modified xsd:string? |
||||||
|
// rdfs:comment "Date modified"; |
||||||
|
ldp:contains @ldps:Resource* |
||||||
|
// rdfs:comment "Defines a Solid Resource"; |
||||||
|
stat:mtime xsd:decimal? |
||||||
|
// rdfs:comment "?"; |
||||||
|
stat:size xsd:integer? |
||||||
|
// rdfs:comment "size of this container"; |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
ldps:Resource EXTRA a { |
||||||
|
$ldps:ResourceShape ( |
||||||
|
a [ ldp:Resource tur:Resource ]* |
||||||
|
// rdfs:comment "Any resource on a Solid server"; |
||||||
|
dct:modified xsd:string? |
||||||
|
// rdfs:comment "Date modified"; |
||||||
|
stat:mtime xsd:decimal? |
||||||
|
// rdfs:comment "?"; |
||||||
|
stat:size xsd:integer? |
||||||
|
// rdfs:comment "size of this container"; |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,117 @@ |
|||||||
|
import { useCallback, useMemo } from "react"; |
||||||
|
import { useLdoContext } from "./LdoContext"; |
||||||
|
import { |
||||||
|
LdoDataset, |
||||||
|
ShapeType, |
||||||
|
startTransaction, |
||||||
|
transactionChanges, |
||||||
|
write, |
||||||
|
} from "ldo"; |
||||||
|
import { splitChangesByGraph } from "./util/splitChangesByGraph"; |
||||||
|
import { LdoBase } from "ldo/dist/util"; |
||||||
|
import { Resource } from "./document/resource/Resource"; |
||||||
|
import { DataResource } from "./document/resource/dataResource/DataResource"; |
||||||
|
import { BinaryResource } from "./document/resource/binaryResource/BinaryResource"; |
||||||
|
import { ContainerResource } from "./document/resource/dataResource/containerResource/ContainerResource"; |
||||||
|
import { AccessRules } from "./document/accessRules/AccessRules"; |
||||||
|
import { SubjectType } from "jsonld-dataset-proxy"; |
||||||
|
import { DatasetChanges } from "o-dataset-pack"; |
||||||
|
import { Quad } from "@rdfjs/types"; |
||||||
|
|
||||||
|
export interface UseLdoReturn { |
||||||
|
changeData<Type extends LdoBase>(input: Type, ...resources: Resource[]): Type; |
||||||
|
commitData(input: LdoBase): Promise<void>; |
||||||
|
createData<Type extends LdoBase>( |
||||||
|
shapeType: ShapeType<Type>, |
||||||
|
subject: string | SubjectType, |
||||||
|
...resources: Resource[] |
||||||
|
): Type; |
||||||
|
dataset: LdoDataset; |
||||||
|
getDataResource: (uri: string) => DataResource; |
||||||
|
getBinaryResource: (uri: string) => BinaryResource; |
||||||
|
getContainerResource: (uri: string) => ContainerResource; |
||||||
|
getAccessRules: (resource: Resource) => AccessRules; |
||||||
|
} |
||||||
|
|
||||||
|
export function useLdo(): UseLdoReturn { |
||||||
|
const { |
||||||
|
dataResourceStore, |
||||||
|
containerResourceStore, |
||||||
|
binaryResourceStore, |
||||||
|
accessRulesStore, |
||||||
|
dataset, |
||||||
|
} = useLdoContext(); |
||||||
|
/** |
||||||
|
* Begins tracking changes to eventually commit |
||||||
|
*/ |
||||||
|
const changeData = useCallback( |
||||||
|
<Type extends LdoBase>(input: Type, ...resources: Resource[]) => { |
||||||
|
// Clone the input and set a graph
|
||||||
|
const [transactionLdo] = write(...resources.map((r) => r.uri)).usingCopy( |
||||||
|
input |
||||||
|
); |
||||||
|
// Start a transaction with the input
|
||||||
|
startTransaction(transactionLdo); |
||||||
|
// Return
|
||||||
|
return transactionLdo; |
||||||
|
}, |
||||||
|
[dataset] |
||||||
|
); |
||||||
|
/** |
||||||
|
* Begins tracking changes to eventually commit for a new subject |
||||||
|
*/ |
||||||
|
const createData = useCallback( |
||||||
|
<Type extends LdoBase>( |
||||||
|
shapeType: ShapeType<Type>, |
||||||
|
subject: string | SubjectType, |
||||||
|
...resources: Resource[] |
||||||
|
) => { |
||||||
|
const linkedDataObject = dataset |
||||||
|
.usingType(shapeType) |
||||||
|
.write(...resources.map((r) => r.uri)) |
||||||
|
.fromSubject(subject); |
||||||
|
startTransaction(linkedDataObject); |
||||||
|
return linkedDataObject; |
||||||
|
}, |
||||||
|
[] |
||||||
|
); |
||||||
|
/** |
||||||
|
* Commits the transaction to the global dataset, syncing all subscribing |
||||||
|
* components and Solid Pods |
||||||
|
*/ |
||||||
|
const commitData = useCallback( |
||||||
|
async (input: LdoBase) => { |
||||||
|
const changes = transactionChanges(input); |
||||||
|
const changesByGraph = splitChangesByGraph( |
||||||
|
changes as DatasetChanges<Quad> |
||||||
|
); |
||||||
|
// Make queries
|
||||||
|
await Promise.all( |
||||||
|
Array.from(changesByGraph.entries()).map( |
||||||
|
async ([graph, datasetChanges]) => { |
||||||
|
if (graph.termType === "DefaultGraph") { |
||||||
|
return; |
||||||
|
} |
||||||
|
const resource = dataResourceStore.get(graph.value); |
||||||
|
await resource.update(datasetChanges); |
||||||
|
} |
||||||
|
) |
||||||
|
); |
||||||
|
}, |
||||||
|
[dataset, fetch] |
||||||
|
); |
||||||
|
// Returns the values
|
||||||
|
return useMemo( |
||||||
|
() => ({ |
||||||
|
dataset, |
||||||
|
changeData, |
||||||
|
createData, |
||||||
|
commitData, |
||||||
|
getDataResource: (uri) => dataResourceStore.get(uri), |
||||||
|
getBinaryResource: (uri) => binaryResourceStore.get(uri), |
||||||
|
getContainerResource: (uri) => containerResourceStore.get(uri), |
||||||
|
getAccessRules: (resource) => accessRulesStore.get(resource), |
||||||
|
}), |
||||||
|
[dataset, changeData, commitData] |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
import { DatasetChanges } from "o-dataset-pack"; |
||||||
|
import { datasetToString } from "ldo/dist/datasetConverters"; |
||||||
|
import { Quad } from "@rdfjs/types"; |
||||||
|
import { quad as createQuad } from "@rdfjs/data-model"; |
||||||
|
|
||||||
|
// TODO: This file is a clone from the one in the base ldo library. This resused
|
||||||
|
// code should be put into a helper library once everything becomes a monorepo.
|
||||||
|
export async function changesToSparqlUpdate(changes: DatasetChanges<Quad>) { |
||||||
|
let output = ""; |
||||||
|
if (changes.removed) { |
||||||
|
const removedTriples = changes.removed.map((quad) => |
||||||
|
createQuad(quad.subject, quad.predicate, quad.object) |
||||||
|
); |
||||||
|
output += `DELETE DATA { ${await datasetToString(removedTriples, { |
||||||
|
format: "N-Triples", |
||||||
|
})} }`;
|
||||||
|
} |
||||||
|
if (changes.added && changes.removed) { |
||||||
|
output += "; "; |
||||||
|
} |
||||||
|
if (changes.added) { |
||||||
|
const addedTriples = changes.added.map((quad) => |
||||||
|
createQuad(quad.subject, quad.predicate, quad.object) |
||||||
|
); |
||||||
|
output += `INSERT DATA { ${await datasetToString(addedTriples, { |
||||||
|
format: "N-Triples", |
||||||
|
})} }`;
|
||||||
|
} |
||||||
|
return output.replaceAll("\n", " "); |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
import React, { |
||||||
|
createContext, |
||||||
|
FunctionComponent, |
||||||
|
useContext, |
||||||
|
Context, |
||||||
|
PropsWithChildren, |
||||||
|
} from "react"; |
||||||
|
|
||||||
|
export function createGlobalHook<ReturnValues>(useHook: () => ReturnValues): { |
||||||
|
Provider: FunctionComponent<PropsWithChildren>; |
||||||
|
useGlobal: () => ReturnValues; |
||||||
|
Context: Context<ReturnValues>; |
||||||
|
} { |
||||||
|
// The initial value will be set immediately
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
const CreatedContext = createContext<ReturnValues>(undefined); |
||||||
|
|
||||||
|
const Provider: FunctionComponent<PropsWithChildren> = ({ children }) => { |
||||||
|
const returnValues = useHook(); |
||||||
|
return ( |
||||||
|
<CreatedContext.Provider value={returnValues}> |
||||||
|
{children} |
||||||
|
</CreatedContext.Provider> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
const useGlobal = () => { |
||||||
|
return useContext(CreatedContext); |
||||||
|
}; |
||||||
|
|
||||||
|
return { Provider, useGlobal, Context: CreatedContext }; |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
import { createDataset, DatasetChanges } from "o-dataset-pack"; |
||||||
|
import { GraphType } from "jsonld-dataset-proxy"; |
||||||
|
import { Quad } from "@rdfjs/types"; |
||||||
|
import { defaultGraph, namedNode, quad as createQuad } from "@rdfjs/data-model"; |
||||||
|
|
||||||
|
export function graphNodeToString(graphNode: GraphType): string { |
||||||
|
return graphNode.termType === "DefaultGraph" |
||||||
|
? "defaultGraph()" |
||||||
|
: graphNode.value; |
||||||
|
} |
||||||
|
|
||||||
|
export function stringToGraphNode(input: string): GraphType { |
||||||
|
return input === "defaultGraph()" ? defaultGraph() : namedNode(input); |
||||||
|
} |
||||||
|
|
||||||
|
export function splitChangesByGraph( |
||||||
|
changes: DatasetChanges<Quad> |
||||||
|
): Map<GraphType, DatasetChanges<Quad>> { |
||||||
|
const changesMap: Record<string, DatasetChanges<Quad>> = {}; |
||||||
|
changes.added?.forEach((quad) => { |
||||||
|
const graphHash = graphNodeToString(quad.graph as GraphType); |
||||||
|
if (!changesMap[graphHash]) { |
||||||
|
changesMap[graphHash] = {}; |
||||||
|
} |
||||||
|
if (!changesMap[graphHash].added) { |
||||||
|
changesMap[graphHash].added = createDataset(); |
||||||
|
} |
||||||
|
changesMap[graphHash].added?.add( |
||||||
|
createQuad(quad.subject, quad.predicate, quad.object, quad.graph) |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
changes.removed?.forEach((quad) => { |
||||||
|
const graphHash = graphNodeToString(quad.graph as GraphType); |
||||||
|
if (!changesMap[graphHash]) { |
||||||
|
changesMap[graphHash] = {}; |
||||||
|
} |
||||||
|
if (!changesMap[graphHash].removed) { |
||||||
|
changesMap[graphHash].removed = createDataset(); |
||||||
|
} |
||||||
|
changesMap[graphHash].removed?.add( |
||||||
|
createQuad(quad.subject, quad.predicate, quad.object, quad.graph) |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
const finalMap = new Map<GraphType, DatasetChanges<Quad>>(); |
||||||
|
Object.entries(changesMap).forEach(([key, value]) => { |
||||||
|
finalMap.set(stringToGraphNode(key), value); |
||||||
|
}); |
||||||
|
return finalMap; |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
import { useState } from "react"; |
||||||
|
|
||||||
|
export function useForceUpdate() { |
||||||
|
const [, setValue] = useState(0); |
||||||
|
return () => setValue((value) => value + 1); |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
describe("Trivial", () => { |
||||||
|
it("Trivial", () => { |
||||||
|
expect(true).toBe(true); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,8 @@ |
|||||||
|
{ |
||||||
|
"extends": "../../tsconfig.base.json", |
||||||
|
"compilerOptions": { |
||||||
|
"outDir": "./dist", |
||||||
|
"jsx": "react-jsx" |
||||||
|
}, |
||||||
|
"include": ["./src"] |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
{ |
||||||
|
"compilerOptions": { |
||||||
|
"module": "commonjs", |
||||||
|
"baseUrl": ".", |
||||||
|
"strict": true, |
||||||
|
"declaration": true, |
||||||
|
"esModuleInterop": true, |
||||||
|
"noImplicitAny": false, |
||||||
|
"removeComments": true, |
||||||
|
"noLib": false, |
||||||
|
"emitDecoratorMetadata": true, |
||||||
|
"experimentalDecorators": true, |
||||||
|
"target": "ES2021", |
||||||
|
"sourceMap": true, |
||||||
|
"jsx": "react-jsx", |
||||||
|
}, |
||||||
|
"exclude": [ |
||||||
|
"node_modules", |
||||||
|
"**/*.spec.ts" |
||||||
|
] |
||||||
|
} |
@ -1,22 +1,8 @@ |
|||||||
{ |
{ |
||||||
|
"extends": "./tsconfig.base.json", |
||||||
"compilerOptions": { |
"compilerOptions": { |
||||||
"module": "commonjs", |
"paths": { |
||||||
"strict": true, |
"@ldo/*": ["packages/*/src"] |
||||||
"declaration": true, |
} |
||||||
"esModuleInterop": true, |
} |
||||||
"noImplicitAny": false, |
|
||||||
"removeComments": true, |
|
||||||
"noLib": false, |
|
||||||
"emitDecoratorMetadata": true, |
|
||||||
"experimentalDecorators": true, |
|
||||||
"target": "es6", |
|
||||||
"sourceMap": true, |
|
||||||
"lib": [ |
|
||||||
"es6" |
|
||||||
] |
|
||||||
}, |
|
||||||
"exclude": [ |
|
||||||
"node_modules", |
|
||||||
"**/*.spec.ts" |
|
||||||
] |
|
||||||
} |
} |
Loading…
Reference in new issue