From 6915dcadce113dc3a45f98782df0318ae5f28bb1 Mon Sep 17 00:00:00 2001 From: Jackson Morgan Date: Sun, 5 Jan 2025 14:56:38 -0500 Subject: [PATCH] init solid-type-index package --- packages/solid-type-index/.eslintrc | 3 + packages/solid-type-index/.gitignore | 1 + packages/solid-type-index/LICENSE.txt | 21 + packages/solid-type-index/README.md | 14 + packages/solid-type-index/jest.config.js | 6 + packages/solid-type-index/jest.setup.ts | 2 + packages/solid-type-index/package.json | 50 ++ .../src/.ldo/profile.context.ts | 19 + .../src/.ldo/profile.schema.ts | 64 +++ .../src/.ldo/profile.shapeTypes.ts | 19 + .../src/.ldo/profile.typings.ts | 27 ++ .../src/.ldo/typeIndex.context.ts | 47 ++ .../src/.ldo/typeIndex.schema.ts | 144 ++++++ .../src/.ldo/typeIndex.shapeTypes.ts | 28 ++ .../src/.ldo/typeIndex.typings.ts | 58 +++ .../solid-type-index/src/.shapes/profile.shex | 10 + .../src/.shapes/typeIndex.shex | 23 + packages/solid-type-index/src/index.ts | 0 .../src/react/useAddToTypeIndex.ts | 0 .../solid-type-index/src/react/useProfile.ts | 0 .../src/react/useRemoveFromTypeIndex.ts | 0 .../solid-type-index/src/react/useType.ts | 0 .../test/.ldo/post.context.ts | 32 ++ .../solid-type-index/test/.ldo/post.schema.ts | 155 +++++++ .../test/.ldo/post.shapeTypes.ts | 19 + .../test/.ldo/post.typings.ts | 45 ++ .../test/Integration.test.tsx | 431 ++++++++++++++++++ packages/solid-type-index/test/setUpServer.ts | 110 +++++ .../unauthenticatedServer.json | 52 +++ .../test-server/configs/solid-css-seed.json | 9 + .../test-server/configs/template/wac/.acl.hbs | 13 + .../configs/template/wac/profile/card.acl.hbs | 19 + .../test/test-server/runServer.ts | 7 + .../test/test-server/solidServer.helper.ts | 39 ++ packages/solid-type-index/tsconfig.build.json | 8 + 35 files changed, 1475 insertions(+) create mode 100644 packages/solid-type-index/.eslintrc create mode 100644 packages/solid-type-index/.gitignore create mode 100644 packages/solid-type-index/LICENSE.txt create mode 100644 packages/solid-type-index/README.md create mode 100644 packages/solid-type-index/jest.config.js create mode 100644 packages/solid-type-index/jest.setup.ts create mode 100644 packages/solid-type-index/package.json create mode 100644 packages/solid-type-index/src/.ldo/profile.context.ts create mode 100644 packages/solid-type-index/src/.ldo/profile.schema.ts create mode 100644 packages/solid-type-index/src/.ldo/profile.shapeTypes.ts create mode 100644 packages/solid-type-index/src/.ldo/profile.typings.ts create mode 100644 packages/solid-type-index/src/.ldo/typeIndex.context.ts create mode 100644 packages/solid-type-index/src/.ldo/typeIndex.schema.ts create mode 100644 packages/solid-type-index/src/.ldo/typeIndex.shapeTypes.ts create mode 100644 packages/solid-type-index/src/.ldo/typeIndex.typings.ts create mode 100644 packages/solid-type-index/src/.shapes/profile.shex create mode 100644 packages/solid-type-index/src/.shapes/typeIndex.shex create mode 100644 packages/solid-type-index/src/index.ts create mode 100644 packages/solid-type-index/src/react/useAddToTypeIndex.ts create mode 100644 packages/solid-type-index/src/react/useProfile.ts create mode 100644 packages/solid-type-index/src/react/useRemoveFromTypeIndex.ts create mode 100644 packages/solid-type-index/src/react/useType.ts create mode 100644 packages/solid-type-index/test/.ldo/post.context.ts create mode 100644 packages/solid-type-index/test/.ldo/post.schema.ts create mode 100644 packages/solid-type-index/test/.ldo/post.shapeTypes.ts create mode 100644 packages/solid-type-index/test/.ldo/post.typings.ts create mode 100644 packages/solid-type-index/test/Integration.test.tsx create mode 100644 packages/solid-type-index/test/setUpServer.ts create mode 100644 packages/solid-type-index/test/test-server/configs/components-config/unauthenticatedServer.json create mode 100644 packages/solid-type-index/test/test-server/configs/solid-css-seed.json create mode 100644 packages/solid-type-index/test/test-server/configs/template/wac/.acl.hbs create mode 100644 packages/solid-type-index/test/test-server/configs/template/wac/profile/card.acl.hbs create mode 100644 packages/solid-type-index/test/test-server/runServer.ts create mode 100644 packages/solid-type-index/test/test-server/solidServer.helper.ts create mode 100644 packages/solid-type-index/tsconfig.build.json diff --git a/packages/solid-type-index/.eslintrc b/packages/solid-type-index/.eslintrc new file mode 100644 index 0000000..83c51a9 --- /dev/null +++ b/packages/solid-type-index/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": ["../../.eslintrc"] +} \ No newline at end of file diff --git a/packages/solid-type-index/.gitignore b/packages/solid-type-index/.gitignore new file mode 100644 index 0000000..0c32b1f --- /dev/null +++ b/packages/solid-type-index/.gitignore @@ -0,0 +1 @@ +test/test-server/data \ No newline at end of file diff --git a/packages/solid-type-index/LICENSE.txt b/packages/solid-type-index/LICENSE.txt new file mode 100644 index 0000000..b87e67e --- /dev/null +++ b/packages/solid-type-index/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Jackson Morgan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/packages/solid-type-index/README.md b/packages/solid-type-index/README.md new file mode 100644 index 0000000..9f6f385 --- /dev/null +++ b/packages/solid-type-index/README.md @@ -0,0 +1,14 @@ +# @ldo/solid-type-index + +Alpha + +// TODO: Write readme + +## Sponsorship +This project was made possible by a grant from NGI Zero Entrust via nlnet. Learn more on the [NLnet project page](https://nlnet.nl/project/SolidUsableApps/). + +[nlnet foundation logo](https://nlnet.nl/) +[NGI Zero Entrust Logo](https://nlnet.nl/) + +## Liscense +MIT diff --git a/packages/solid-type-index/jest.config.js b/packages/solid-type-index/jest.config.js new file mode 100644 index 0000000..4275a3f --- /dev/null +++ b/packages/solid-type-index/jest.config.js @@ -0,0 +1,6 @@ +const sharedConfig = require("../../jest.config.js"); +module.exports = { + ...sharedConfig, + rootDir: "./", + testEnvironment: "jsdom", +}; diff --git a/packages/solid-type-index/jest.setup.ts b/packages/solid-type-index/jest.setup.ts new file mode 100644 index 0000000..22eed5f --- /dev/null +++ b/packages/solid-type-index/jest.setup.ts @@ -0,0 +1,2 @@ +import "@inrupt/jest-jsdom-polyfills"; +globalThis.fetch = async () => new Response(); diff --git a/packages/solid-type-index/package.json b/packages/solid-type-index/package.json new file mode 100644 index 0000000..7970b8a --- /dev/null +++ b/packages/solid-type-index/package.json @@ -0,0 +1,50 @@ +{ + "name": "@ldo/solid-type-index", + "version": "0.0.1-alpha.28", + "description": "Solid Type Index support for LDO", + "main": "dist/index.js", + "scripts": { + "build": "tsc --project tsconfig.build.json", + "watch": "tsc --watch", + "test": "npm run test:integration", + "test:watch": "jest --watch", + "prepublishOnly": "npm run test && npm run build", + "build:ldo": "ldo build --input src/.shapes --output src/.ldo", + "lint": "eslint src/** --fix --no-error-on-unmatched-pattern", + "test:integration": "start-server-and-test start-test-server http://localhost:3003 start-integration-test", + "start-test-server": "ts-node ./test/test-server/runServer.ts", + "start-integration-test": "jest --coverage" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/o-development/ldobjects.git" + }, + "author": "Jackson Morgan", + "license": "MIT", + "bugs": { + "url": "https://github.com/o-development/ldobjects/issues" + }, + "homepage": "https://github.com/o-development/ldobjects/tree/main/packages/solid-react#readme", + "devDependencies": { + "@ldo/rdf-utils": "^0.0.1-alpha.24", + "@rdfjs/types": "^1.0.1", + "@testing-library/react": "^14.1.2", + "@types/jest": "^27.0.3", + "jest-environment-jsdom": "^27.0.0", + "start-server-and-test": "^2.0.3", + "ts-jest": "^27.1.2", + "ts-node": "^10.9.2" + }, + "dependencies": { + "@ldo/solid": "^0.0.1-alpha.28", + "@ldo/solid-react": "^0.0.1-alpha.28" + }, + "files": [ + "dist", + "src" + ], + "publishConfig": { + "access": "public" + }, + "gitHead": "c63f055aab22155b60a5fdee4172979b9c287dfa" +} diff --git a/packages/solid-type-index/src/.ldo/profile.context.ts b/packages/solid-type-index/src/.ldo/profile.context.ts new file mode 100644 index 0000000..534da7b --- /dev/null +++ b/packages/solid-type-index/src/.ldo/profile.context.ts @@ -0,0 +1,19 @@ +import { ContextDefinition } from "jsonld"; + +/** + * ============================================================================= + * profileContext: JSONLD Context for profile + * ============================================================================= + */ +export const profileContext: ContextDefinition = { + privateTypeIndex: { + "@id": "http://www.w3.org/ns/solid/terms#privateTypeIndex", + "@type": "@id", + "@isCollection": true, + }, + publicTypeIndex: { + "@id": "http://www.w3.org/ns/solid/terms#publicTypeIndex", + "@type": "@id", + "@isCollection": true, + }, +}; diff --git a/packages/solid-type-index/src/.ldo/profile.schema.ts b/packages/solid-type-index/src/.ldo/profile.schema.ts new file mode 100644 index 0000000..20c665f --- /dev/null +++ b/packages/solid-type-index/src/.ldo/profile.schema.ts @@ -0,0 +1,64 @@ +import { Schema } from "shexj"; + +/** + * ============================================================================= + * profileSchema: ShexJ Schema for profile + * ============================================================================= + */ +export const profileSchema: Schema = { + type: "Schema", + shapes: [ + { + id: "https://shaperepo.com/schemas/solidProfile#TypeIndexProfile", + type: "ShapeDecl", + shapeExpr: { + type: "Shape", + expression: { + type: "EachOf", + expressions: [ + { + 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)", + }, + }, + ], + }, + ], + }, + }, + }, + ], +}; diff --git a/packages/solid-type-index/src/.ldo/profile.shapeTypes.ts b/packages/solid-type-index/src/.ldo/profile.shapeTypes.ts new file mode 100644 index 0000000..567e96a --- /dev/null +++ b/packages/solid-type-index/src/.ldo/profile.shapeTypes.ts @@ -0,0 +1,19 @@ +import { ShapeType } from "@ldo/ldo"; +import { profileSchema } from "./profile.schema"; +import { profileContext } from "./profile.context"; +import { TypeIndexProfile } from "./profile.typings"; + +/** + * ============================================================================= + * LDO ShapeTypes profile + * ============================================================================= + */ + +/** + * TypeIndexProfile ShapeType + */ +export const TypeIndexProfileShapeType: ShapeType = { + schema: profileSchema, + shape: "https://shaperepo.com/schemas/solidProfile#TypeIndexProfile", + context: profileContext, +}; diff --git a/packages/solid-type-index/src/.ldo/profile.typings.ts b/packages/solid-type-index/src/.ldo/profile.typings.ts new file mode 100644 index 0000000..bca3f3b --- /dev/null +++ b/packages/solid-type-index/src/.ldo/profile.typings.ts @@ -0,0 +1,27 @@ +import { ContextDefinition } from "jsonld"; + +/** + * ============================================================================= + * Typescript Typings for profile + * ============================================================================= + */ + +/** + * TypeIndexProfile Type + */ +export interface TypeIndexProfile { + "@id"?: string; + "@context"?: ContextDefinition; + /** + * 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; + }[]; +} diff --git a/packages/solid-type-index/src/.ldo/typeIndex.context.ts b/packages/solid-type-index/src/.ldo/typeIndex.context.ts new file mode 100644 index 0000000..d0dbefd --- /dev/null +++ b/packages/solid-type-index/src/.ldo/typeIndex.context.ts @@ -0,0 +1,47 @@ +import { ContextDefinition } from "jsonld"; + +/** + * ============================================================================= + * typeIndexContext: JSONLD Context for typeIndex + * ============================================================================= + */ +export const typeIndexContext: ContextDefinition = { + TypeIndex: { + "@id": "http://www.w3.org/ns/solid/terms#TypeIndex", + "@context": { + type: { + "@id": "@type", + }, + }, + }, + ListedDocument: { + "@id": "http://www.w3.org/ns/solid/terms#ListedDocument", + "@context": { + type: { + "@id": "@type", + }, + }, + }, + TypeRegistration: { + "@id": "http://www.w3.org/ns/solid/terms#TypeRegistration", + "@context": { + type: { + "@id": "@type", + }, + forClass: { + "@id": "http://www.w3.org/ns/solid/terms#forClass", + "@type": "@id", + }, + instance: { + "@id": "http://www.w3.org/ns/solid/terms#instance", + "@type": "@id", + "@isCollection": true, + }, + instanceContainer: { + "@id": "http://www.w3.org/ns/solid/terms#instanceContainer", + "@type": "@id", + "@isCollection": true, + }, + }, + }, +}; diff --git a/packages/solid-type-index/src/.ldo/typeIndex.schema.ts b/packages/solid-type-index/src/.ldo/typeIndex.schema.ts new file mode 100644 index 0000000..5d0c442 --- /dev/null +++ b/packages/solid-type-index/src/.ldo/typeIndex.schema.ts @@ -0,0 +1,144 @@ +import { Schema } from "shexj"; + +/** + * ============================================================================= + * typeIndexSchema: ShexJ Schema for typeIndex + * ============================================================================= + */ +export const typeIndexSchema: Schema = { + type: "Schema", + shapes: [ + { + id: "https://shaperepo.com/schemas/solidProfile#TypeIndexDocument", + 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/ns/solid/terms#TypeIndex"], + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Defines the node as a TypeIndex", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", + valueExpr: { + type: "NodeConstraint", + values: ["http://www.w3.org/ns/solid/terms#ListedDocument"], + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Defines the node as a Listed Document", + }, + }, + ], + }, + ], + }, + extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], + }, + }, + { + id: "https://shaperepo.com/schemas/solidProfile#TypeRegistration", + 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/ns/solid/terms#TypeRegistration"], + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Defines this node as a Type Registration", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/solid/terms#forClass", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "The class of object at this type.", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/solid/terms#instance", + 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 specific resource that contains the class.", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/solid/terms#instanceContainer", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + min: 0, + max: -1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Containers that contain resources with the class.", + }, + }, + ], + }, + ], + }, + extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], + }, + }, + ], +}; diff --git a/packages/solid-type-index/src/.ldo/typeIndex.shapeTypes.ts b/packages/solid-type-index/src/.ldo/typeIndex.shapeTypes.ts new file mode 100644 index 0000000..4896439 --- /dev/null +++ b/packages/solid-type-index/src/.ldo/typeIndex.shapeTypes.ts @@ -0,0 +1,28 @@ +import { ShapeType } from "@ldo/ldo"; +import { typeIndexSchema } from "./typeIndex.schema"; +import { typeIndexContext } from "./typeIndex.context"; +import { TypeIndexDocument, TypeRegistration } from "./typeIndex.typings"; + +/** + * ============================================================================= + * LDO ShapeTypes typeIndex + * ============================================================================= + */ + +/** + * TypeIndexDocument ShapeType + */ +export const TypeIndexDocumentShapeType: ShapeType = { + schema: typeIndexSchema, + shape: "https://shaperepo.com/schemas/solidProfile#TypeIndexDocument", + context: typeIndexContext, +}; + +/** + * TypeRegistration ShapeType + */ +export const TypeRegistrationShapeType: ShapeType = { + schema: typeIndexSchema, + shape: "https://shaperepo.com/schemas/solidProfile#TypeRegistration", + context: typeIndexContext, +}; diff --git a/packages/solid-type-index/src/.ldo/typeIndex.typings.ts b/packages/solid-type-index/src/.ldo/typeIndex.typings.ts new file mode 100644 index 0000000..53064ef --- /dev/null +++ b/packages/solid-type-index/src/.ldo/typeIndex.typings.ts @@ -0,0 +1,58 @@ +import { ContextDefinition } from "jsonld"; + +/** + * ============================================================================= + * Typescript Typings for typeIndex + * ============================================================================= + */ + +/** + * TypeIndexDocument Type + */ +export interface TypeIndexDocument { + "@id"?: string; + "@context"?: ContextDefinition; + /** + * Defines the node as a TypeIndex | Defines the node as a Listed Document + */ + type: ( + | { + "@id": "TypeIndex"; + } + | { + "@id": "ListedDocument"; + } + )[]; +} + +/** + * TypeRegistration Type + */ +export interface TypeRegistration { + "@id"?: string; + "@context"?: ContextDefinition; + /** + * Defines this node as a Type Registration + */ + type: { + "@id": "TypeRegistration"; + }; + /** + * The class of object at this type. + */ + forClass: { + "@id": string; + }; + /** + * A specific resource that contains the class. + */ + instance?: { + "@id": string; + }[]; + /** + * Containers that contain resources with the class. + */ + instanceContainer?: { + "@id": string; + }[]; +} diff --git a/packages/solid-type-index/src/.shapes/profile.shex b/packages/solid-type-index/src/.shapes/profile.shex new file mode 100644 index 0000000..1274b6e --- /dev/null +++ b/packages/solid-type-index/src/.shapes/profile.shex @@ -0,0 +1,10 @@ +PREFIX srs: +PREFIX solid: +PREFIX rdfs: + +srs:TypeIndexProfile { + 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)" ; +} diff --git a/packages/solid-type-index/src/.shapes/typeIndex.shex b/packages/solid-type-index/src/.shapes/typeIndex.shex new file mode 100644 index 0000000..b0fd3db --- /dev/null +++ b/packages/solid-type-index/src/.shapes/typeIndex.shex @@ -0,0 +1,23 @@ +PREFIX srs: +PREFIX rdf: +PREFIX solid: +PREFIX vcard: +PREFIX rdfs: + +srs:TypeIndexDocument EXTRA a { + a [ solid:TypeIndex ] + // rdfs:comment "Defines the node as a TypeIndex" ; + a [ solid:ListedDocument ] + // rdfs:comment "Defines the node as a Listed Document" ; +} + +srs:TypeRegistration EXTRA a { + a [ solid:TypeRegistration ] + // rdfs:comment "Defines this node as a Type Registration" ; + solid:forClass IRI + // rdfs:comment "The class of object at this type." ; + solid:instance IRI * + // rdfs:comment "A specific resource that contains the class." ; + solid:instanceContainer IRI * + // rdfs:comment "Containers that contain resources with the class." ; +} diff --git a/packages/solid-type-index/src/index.ts b/packages/solid-type-index/src/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/solid-type-index/src/react/useAddToTypeIndex.ts b/packages/solid-type-index/src/react/useAddToTypeIndex.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/solid-type-index/src/react/useProfile.ts b/packages/solid-type-index/src/react/useProfile.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/solid-type-index/src/react/useRemoveFromTypeIndex.ts b/packages/solid-type-index/src/react/useRemoveFromTypeIndex.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/solid-type-index/src/react/useType.ts b/packages/solid-type-index/src/react/useType.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/solid-type-index/test/.ldo/post.context.ts b/packages/solid-type-index/test/.ldo/post.context.ts new file mode 100644 index 0000000..5cb3a91 --- /dev/null +++ b/packages/solid-type-index/test/.ldo/post.context.ts @@ -0,0 +1,32 @@ +import { ContextDefinition } from "jsonld"; + +/** + * ============================================================================= + * postContext: JSONLD Context for post + * ============================================================================= + */ +export const postContext: ContextDefinition = { + type: { + "@id": "@type", + }, + SocialMediaPosting: "http://schema.org/SocialMediaPosting", + CreativeWork: "http://schema.org/CreativeWork", + Thing: "http://schema.org/Thing", + articleBody: { + "@id": "http://schema.org/articleBody", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + uploadDate: { + "@id": "http://schema.org/uploadDate", + "@type": "http://www.w3.org/2001/XMLSchema#date", + }, + image: { + "@id": "http://schema.org/image", + "@type": "@id", + }, + publisher: { + "@id": "http://schema.org/publisher", + "@type": "@id", + "@container": "@set", + }, +}; diff --git a/packages/solid-type-index/test/.ldo/post.schema.ts b/packages/solid-type-index/test/.ldo/post.schema.ts new file mode 100644 index 0000000..39e8b63 --- /dev/null +++ b/packages/solid-type-index/test/.ldo/post.schema.ts @@ -0,0 +1,155 @@ +import { Schema } from "shexj"; + +/** + * ============================================================================= + * postSchema: ShexJ Schema for post + * ============================================================================= + */ +export const postSchema: Schema = { + type: "Schema", + shapes: [ + { + id: "https://example.com/PostSh", + 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/SocialMediaPosting", + "http://schema.org/CreativeWork", + "http://schema.org/Thing", + ], + }, + }, + { + type: "TripleConstraint", + predicate: "http://schema.org/articleBody", + 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#label", + object: { + value: "articleBody", + }, + }, + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "The actual body of the article. ", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://schema.org/uploadDate", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#date", + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#label", + object: { + value: "uploadDate", + }, + }, + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "Date when this media object was uploaded to this site.", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://schema.org/image", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#label", + object: { + value: "image", + }, + }, + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "A media object that encodes this CreativeWork. This property is a synonym for encoding.", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://schema.org/publisher", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#label", + object: { + value: "publisher", + }, + }, + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "The publisher of the creative work.", + }, + }, + ], + }, + ], + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#label", + object: { + value: "SocialMediaPost", + }, + }, + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "A post to a social media platform, including blog posts, tweets, Facebook posts, etc.", + }, + }, + ], + }, + }, + ], +}; diff --git a/packages/solid-type-index/test/.ldo/post.shapeTypes.ts b/packages/solid-type-index/test/.ldo/post.shapeTypes.ts new file mode 100644 index 0000000..4c50683 --- /dev/null +++ b/packages/solid-type-index/test/.ldo/post.shapeTypes.ts @@ -0,0 +1,19 @@ +import { ShapeType } from "@ldo/ldo"; +import { postSchema } from "./post.schema"; +import { postContext } from "./post.context"; +import { PostSh } from "./post.typings"; + +/** + * ============================================================================= + * LDO ShapeTypes post + * ============================================================================= + */ + +/** + * PostSh ShapeType + */ +export const PostShShapeType: ShapeType = { + schema: postSchema, + shape: "https://example.com/PostSh", + context: postContext, +}; diff --git a/packages/solid-type-index/test/.ldo/post.typings.ts b/packages/solid-type-index/test/.ldo/post.typings.ts new file mode 100644 index 0000000..1425a9a --- /dev/null +++ b/packages/solid-type-index/test/.ldo/post.typings.ts @@ -0,0 +1,45 @@ +import { ContextDefinition } from "jsonld"; + +/** + * ============================================================================= + * Typescript Typings for post + * ============================================================================= + */ + +/** + * PostSh Type + */ +export interface PostSh { + "@id"?: string; + "@context"?: ContextDefinition; + type: + | { + "@id": "SocialMediaPosting"; + } + | { + "@id": "CreativeWork"; + } + | { + "@id": "Thing"; + }; + /** + * The actual body of the article. + */ + articleBody?: string; + /** + * Date when this media object was uploaded to this site. + */ + uploadDate: string; + /** + * A media object that encodes this CreativeWork. This property is a synonym for encoding. + */ + image?: { + "@id": string; + }; + /** + * The publisher of the creative work. + */ + publisher: { + "@id": string; + }[]; +} diff --git a/packages/solid-type-index/test/Integration.test.tsx b/packages/solid-type-index/test/Integration.test.tsx new file mode 100644 index 0000000..8c8ae0b --- /dev/null +++ b/packages/solid-type-index/test/Integration.test.tsx @@ -0,0 +1,431 @@ +import React, { useCallback, useEffect, useState } from "react"; +import type { FunctionComponent } from "react"; +import { render, screen, fireEvent, act } from "@testing-library/react"; +import { + SAMPLE_BINARY_URI, + SAMPLE_DATA_URI, + SERVER_DOMAIN, + setUpServer, +} from "./setUpServer"; +import { UnauthenticatedSolidLdoProvider } from "../src/UnauthenticatedSolidLdoProvider"; +import { useResource } from "../src/useResource"; +import { useRootContainerFor } from "../src/useRootContainer"; +import { useLdo } from "../src/SolidLdoProvider"; +import { PostShShapeType } from "./.ldo/post.shapeTypes"; +import type { PostSh } from "./.ldo/post.typings"; +import { useSubject } from "../src/useSubject"; + +// Use an increased timeout, since the CSS server takes too much setup time. +jest.setTimeout(40_000); + +describe("Integration Tests", () => { + setUpServer(); + + /** + * =========================================================================== + * useResource + * =========================================================================== + */ + describe("useResource", () => { + it("Fetches a resource and indicates it is loading while doing so", async () => { + const UseResourceTest: FunctionComponent = () => { + const resource = useResource(SAMPLE_DATA_URI); + if (resource?.isLoading()) return

Loading

; + return

{resource.status.type}

; + }; + render( + + + , + ); + await screen.findByText("Loading"); + const resourceStatus = await screen.findByRole("status"); + expect(resourceStatus.innerHTML).toBe("dataReadSuccess"); + }); + + it("returns undefined when no uri is provided, then rerenders when one is", async () => { + const UseResourceUndefinedTest: FunctionComponent = () => { + const [uri, setUri] = useState(undefined); + const resource = useResource(uri, { suppressInitialRead: true }); + if (!resource) + return ( +
+

Undefined

+ +
+ ); + return

{resource.status.type}

; + }; + render( + + + , + ); + await screen.findByText("Undefined"); + fireEvent.click(screen.getByText("Next")); + const resourceStatus = await screen.findByRole("status"); + expect(resourceStatus.innerHTML).toBe("unfetched"); + }); + + it("Reloads the data on mount", async () => { + const ReloadTest: FunctionComponent = () => { + const resource = useResource(SAMPLE_DATA_URI, { reloadOnMount: true }); + if (resource?.isLoading()) return

Loading

; + return

{resource.status.type}

; + }; + const ReloadParent: FunctionComponent = () => { + const [showComponent, setShowComponent] = useState(true); + return ( +
+ + {showComponent ? :

Hidden

} +
+ ); + }; + render( + + + , + ); + await screen.findByText("Loading"); + const resourceStatus1 = await screen.findByRole("status"); + expect(resourceStatus1.innerHTML).toBe("dataReadSuccess"); + fireEvent.click(screen.getByText("Show Component")); + await screen.findByText("Hidden"); + fireEvent.click(screen.getByText("Show Component")); + await screen.findByText("Loading"); + const resourceStatus2 = await screen.findByRole("status", undefined, { + timeout: 5000, + }); + expect(resourceStatus2.innerHTML).toBe("dataReadSuccess"); + }); + + it("handles swapping to a new resource", async () => { + const SwapResourceTest: FunctionComponent = () => { + const [uri, setUri] = useState(SAMPLE_DATA_URI); + const resource = useResource(uri); + if (resource?.isLoading()) return

Loading

; + return ( +
+

{resource.status.type}

+ +
+ ); + }; + render( + + + , + ); + await screen.findByText("Loading"); + const resourceStatus1 = await screen.findByRole("status"); + expect(resourceStatus1.innerHTML).toBe("dataReadSuccess"); + fireEvent.click(screen.getByText("Update URI")); + await screen.findByText("Loading"); + const resourceStatus2 = await screen.findByRole("status"); + expect(resourceStatus2.innerHTML).toBe("binaryReadSuccess"); + }); + }); + + /** + * =========================================================================== + * useRootContainer + * =========================================================================== + */ + describe("useRootContainer", () => { + it("gets the root container for a sub-resource", async () => { + const RootContainerTest: FunctionComponent = () => { + const rootContainer = useRootContainerFor(SAMPLE_DATA_URI, { + suppressInitialRead: true, + }); + return rootContainer ?

{rootContainer?.uri}

: <>; + }; + render( + + + , + ); + const container = await screen.findByRole("root"); + expect(container.innerHTML).toBe(SERVER_DOMAIN); + }); + + it("returns undefined when a URI is not provided", async () => { + const RootContainerTest: FunctionComponent = () => { + const rootContainer = useRootContainerFor(undefined, { + suppressInitialRead: true, + }); + return rootContainer ? ( +

{rootContainer?.uri}

+ ) : ( +

Undefined

+ ); + }; + render( + + + , + ); + const container = await screen.findByRole("undefined"); + expect(container.innerHTML).toBe("Undefined"); + }); + }); + + /** + * =========================================================================== + * useLdoMethods + * =========================================================================== + */ + describe("useLdoMethods", () => { + it("uses get subject to get a linked data object", async () => { + const GetSubjectTest: FunctionComponent = () => { + const [subject, setSubject] = useState(); + const { getSubject } = useLdo(); + useEffect(() => { + const someSubject = getSubject( + PostShShapeType, + "https://example.com/subject", + ); + setSubject(someSubject); + }, []); + return subject ?

{subject["@id"]}

: <>; + }; + render( + + + , + ); + const container = await screen.findByRole("subject"); + expect(container.innerHTML).toBe("https://example.com/subject"); + }); + + it("uses createData to create a new data object", async () => { + const GetSubjectTest: FunctionComponent = () => { + const [subject, setSubject] = useState(); + const { createData, getResource } = useLdo(); + useEffect(() => { + const someSubject = createData( + PostShShapeType, + "https://example.com/subject", + getResource("https://example.com/"), + ); + someSubject.articleBody = "Cool Article"; + setSubject(someSubject); + }, []); + return subject ?

{subject.articleBody}

: <>; + }; + render( + + + , + ); + const container = await screen.findByRole("subject"); + expect(container.innerHTML).toBe("Cool Article"); + }); + }); + + /** + * =========================================================================== + * useSubject + * =========================================================================== + */ + describe("useSubject", () => { + it("renders the article body from the useSubject value", async () => { + const UseSubjectTest: FunctionComponent = () => { + useResource(SAMPLE_DATA_URI); + const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); + + return

{post.articleBody}

; + }; + render( + + + , + ); + + await screen.findByText("test"); + }); + + it("renders the array value from the useSubject value", async () => { + const UseSubjectTest: FunctionComponent = () => { + const resource = useResource(SAMPLE_DATA_URI); + const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); + if (resource.isLoading() || !post) return

loading

; + + return ( +
+

{post.publisher[0]["@id"]}

+
    + {post.publisher.map((publisher) => { + return
  • {publisher["@id"]}
  • ; + })} +
+
+ ); + }; + render( + + + , + ); + + const single = await screen.findByRole("single"); + expect(single.innerHTML).toBe("https://example.com/Publisher1"); + const list = await screen.findByRole("list"); + expect(list.children[0].innerHTML).toBe("https://example.com/Publisher1"); + expect(list.children[1].innerHTML).toBe("https://example.com/Publisher2"); + }); + + it("returns undefined in the subject URI is undefined", async () => { + const UseSubjectTest: FunctionComponent = () => { + useResource(SAMPLE_DATA_URI, { suppressInitialRead: true }); + const post = useSubject(PostShShapeType, undefined); + + return ( +

+ {post === undefined ? "Undefined" : "Not Undefined"} +

+ ); + }; + render( + + + , + ); + + const article = await screen.findByRole("article"); + expect(article.innerHTML).toBe("Undefined"); + }); + + it("returns nothing if a symbol key is provided", async () => { + const UseSubjectTest: FunctionComponent = () => { + const resource = useResource(SAMPLE_DATA_URI); + const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); + if (resource.isLoading() || !post) return

loading

; + + return

{typeof post[Symbol.hasInstance]}

; + }; + render( + + + , + ); + + const article = await screen.findByRole("value"); + expect(article.innerHTML).toBe("undefined"); + }); + + it("returns an id if an id key is provided", async () => { + const UseSubjectTest: FunctionComponent = () => { + const resource = useResource(SAMPLE_DATA_URI); + const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); + if (resource.isLoading() || !post) return

loading

; + + return

{post["@id"]}

; + }; + render( + + + , + ); + + const article = await screen.findByRole("value"); + expect(article.innerHTML).toBe(`${SAMPLE_DATA_URI}#Post1`); + }); + + it("does not set a value if a value is attempted to be set", async () => { + const warn = jest.spyOn(console, "warn").mockImplementation(() => {}); + const UseSubjectTest: FunctionComponent = () => { + const resource = useResource(SAMPLE_DATA_URI); + const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); + if (resource.isLoading() || !post) return

loading

; + + return ( +
+

{post.articleBody}

+ +
+ ); + }; + render( + + + , + ); + + const article = await screen.findByRole("value"); + expect(article.innerHTML).toBe(`test`); + fireEvent.click(screen.getByText("Attempt Change")); + expect(article.innerHTML).not.toBe("bad"); + expect(warn).toHaveBeenCalledWith( + "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.", + ); + warn.mockReset(); + }); + + it("rerenders when asked to subscribe to a resource", async () => { + const NotificationTest: FunctionComponent = () => { + const resource = useResource(SAMPLE_DATA_URI, { subscribe: true }); + const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); + + const addPublisher = useCallback(async () => { + await fetch(SAMPLE_DATA_URI, { + method: "PATCH", + body: `INSERT DATA { <${SAMPLE_DATA_URI}#Post1> . }`, + headers: { + "Content-Type": "application/sparql-update", + }, + }); + }, []); + + if (resource.isLoading() || !post) return

loading

; + + return ( +
+
    + {post.publisher.map((publisher) => { + return
  • {publisher["@id"]}
  • ; + })} +
+ +
+ ); + }; + const { unmount } = render( + + + , + ); + + const list = await screen.findByRole("list"); + expect(list.children[0].innerHTML).toBe("https://example.com/Publisher1"); + expect(list.children[1].innerHTML).toBe("https://example.com/Publisher2"); + + // Wait for subscription to connect + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 1000)); + }); + + // Click button to add a publisher + await fireEvent.click(screen.getByText("Add Publisher")); + await screen.findByText("https://example.com/Publisher3"); + + // Verify the new publisher is in the list + const updatedList = await screen.findByRole("list"); + expect(updatedList.children[2].innerHTML).toBe( + "https://example.com/Publisher3", + ); + + unmount(); + + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 1000)); + }); + }); + }); +}); diff --git a/packages/solid-type-index/test/setUpServer.ts b/packages/solid-type-index/test/setUpServer.ts new file mode 100644 index 0000000..0551ccf --- /dev/null +++ b/packages/solid-type-index/test/setUpServer.ts @@ -0,0 +1,110 @@ +import type { ContainerUri, LeafUri } from "@ldo/solid"; +import fetch from "cross-fetch"; + +export const SERVER_DOMAIN = process.env.SERVER || "http://localhost:3001/"; +export const ROOT_ROUTE = process.env.ROOT_CONTAINER || "example/"; +export const ROOT_CONTAINER = `${SERVER_DOMAIN}${ROOT_ROUTE}`; +export const TEST_CONTAINER_SLUG = "test_ldo/"; +export const TEST_CONTAINER_URI = + `${ROOT_CONTAINER}${TEST_CONTAINER_SLUG}` as ContainerUri; +export const SAMPLE_DATA_URI = `${TEST_CONTAINER_URI}sample.ttl` as LeafUri; +export const SAMPLE2_DATA_SLUG = "sample2.ttl"; +export const SAMPLE2_DATA_URI = + `${TEST_CONTAINER_URI}${SAMPLE2_DATA_SLUG}` as LeafUri; +export const SAMPLE_BINARY_URI = `${TEST_CONTAINER_URI}sample.txt` as LeafUri; +export const SAMPLE2_BINARY_SLUG = `sample2.txt`; +export const SAMPLE2_BINARY_URI = + `${TEST_CONTAINER_URI}${SAMPLE2_BINARY_SLUG}` as LeafUri; +export const SAMPLE_CONTAINER_URI = + `${TEST_CONTAINER_URI}sample_container/` as ContainerUri; +export const EXAMPLE_POST_TTL = `@prefix schema: . + +<#Post1> + a schema:CreativeWork, schema:Thing, schema:SocialMediaPosting ; + schema:image ; + schema:articleBody "test" ; + schema:publisher , .`; +export const TEST_CONTAINER_TTL = `@prefix dc: . +@prefix ldp: . +@prefix posix: . +@prefix xsd: . + +<> "sample.txt"; + a ldp:Container, ldp:BasicContainer, ldp:Resource; + dc:modified "2023-10-20T13:57:14.000Z"^^xsd:dateTime. + a ldp:Resource, ; + dc:modified "2023-10-20T13:57:14.000Z"^^xsd:dateTime. + a ldp:Resource, ; + dc:modified "2023-10-20T13:57:14.000Z"^^xsd:dateTime. +<> posix:mtime 1697810234; + ldp:contains , . + posix:mtime 1697810234; + posix:size 522. + posix:mtime 1697810234; + posix:size 10.`; + +export interface SetUpServerReturn { + authFetch: typeof fetch; + fetchMock: jest.Mock< + Promise, + [input: RequestInfo | URL, init?: RequestInit | undefined] + >; +} + +export function setUpServer(): SetUpServerReturn { + // Ignore to build s + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const s: SetUpServerReturn = {}; + + beforeAll(async () => { + // s.authFetch = await getAuthenticatedFetch(); + s.authFetch = fetch; + }); + + beforeEach(async () => { + s.fetchMock = jest.fn(s.authFetch); + // Create a new document called sample.ttl + await s.authFetch(ROOT_CONTAINER, { + method: "POST", + headers: { + link: '; rel="type"', + slug: TEST_CONTAINER_SLUG, + }, + }); + await Promise.all([ + s.authFetch(TEST_CONTAINER_URI, { + method: "POST", + headers: { "content-type": "text/turtle", slug: "sample.ttl" }, + body: EXAMPLE_POST_TTL, + }), + s.authFetch(TEST_CONTAINER_URI, { + method: "POST", + headers: { "content-type": "text/plain", slug: "sample.txt" }, + body: "some text.", + }), + ]); + }); + + afterEach(async () => { + await Promise.all([ + s.authFetch(SAMPLE_DATA_URI, { + method: "DELETE", + }), + s.authFetch(SAMPLE2_DATA_URI, { + method: "DELETE", + }), + s.authFetch(SAMPLE_BINARY_URI, { + method: "DELETE", + }), + s.authFetch(SAMPLE2_BINARY_URI, { + method: "DELETE", + }), + s.authFetch(SAMPLE_CONTAINER_URI, { + method: "DELETE", + }), + ]); + }); + + return s; +} diff --git a/packages/solid-type-index/test/test-server/configs/components-config/unauthenticatedServer.json b/packages/solid-type-index/test/test-server/configs/components-config/unauthenticatedServer.json new file mode 100644 index 0000000..ff01914 --- /dev/null +++ b/packages/solid-type-index/test/test-server/configs/components-config/unauthenticatedServer.json @@ -0,0 +1,52 @@ +{ + "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", + "import": [ + "css:config/app/init/initialize-intro.json", + "css:config/app/main/default.json", + "css:config/app/variables/default.json", + "css:config/http/handler/default.json", + "css:config/http/middleware/default.json", + "css:config/http/notifications/all.json", + "css:config/http/server-factory/http.json", + "css:config/http/static/default.json", + "css:config/identity/access/public.json", + "css:config/identity/email/default.json", + "css:config/identity/handler/default.json", + "css:config/identity/oidc/default.json", + "css:config/identity/ownership/token.json", + "css:config/identity/pod/static.json", + "css:config/ldp/authentication/dpop-bearer.json", + "css:config/ldp/authorization/webacl.json", + "css:config/ldp/handler/default.json", + "css:config/ldp/metadata-parser/default.json", + "css:config/ldp/metadata-writer/default.json", + "css:config/ldp/modes/default.json", + "css:config/storage/backend/memory.json", + "css:config/storage/key-value/resource-store.json", + "css:config/storage/location/root.json", + "css:config/storage/middleware/default.json", + "css:config/util/auxiliary/acl.json", + "css:config/util/identifiers/suffix.json", + "css:config/util/index/default.json", + "css:config/util/logging/winston.json", + "css:config/util/representation-conversion/default.json", + "css:config/util/resource-locker/memory.json", + "css:config/util/variables/default.json" + ], + "@graph": [ + { + "comment": "A Solid server that stores its resources in memory and uses WAC for authorization." + }, + { + "comment": "The location of the new pod templates folder.", + "@type": "Override", + "overrideInstance": { + "@id": "urn:solid-server:default:PodResourcesGenerator" + }, + "overrideParameters": { + "@type": "StaticFolderGenerator", + "templateFolder": "./test/test-server/configs/template" + } + } + ] +} diff --git a/packages/solid-type-index/test/test-server/configs/solid-css-seed.json b/packages/solid-type-index/test/test-server/configs/solid-css-seed.json new file mode 100644 index 0000000..5894d0d --- /dev/null +++ b/packages/solid-type-index/test/test-server/configs/solid-css-seed.json @@ -0,0 +1,9 @@ +[ + { + "email": "hello@example.com", + "password": "abc123", + "pods": [ + { "name": "example" } + ] + } +] \ No newline at end of file diff --git a/packages/solid-type-index/test/test-server/configs/template/wac/.acl.hbs b/packages/solid-type-index/test/test-server/configs/template/wac/.acl.hbs new file mode 100644 index 0000000..48fd101 --- /dev/null +++ b/packages/solid-type-index/test/test-server/configs/template/wac/.acl.hbs @@ -0,0 +1,13 @@ +@prefix : <#>. +@prefix acl: . +@prefix foaf: . +@prefix eve: <./>. +@prefix c: <./profile/card#>. + +:ControlReadWrite + a acl:Authorization; + acl:accessTo eve:; + acl:agent c:me, ; + acl:agentClass foaf:Agent; + acl:default eve:; + acl:mode acl:Control, acl:Read, acl:Write. \ No newline at end of file diff --git a/packages/solid-type-index/test/test-server/configs/template/wac/profile/card.acl.hbs b/packages/solid-type-index/test/test-server/configs/template/wac/profile/card.acl.hbs new file mode 100644 index 0000000..ea7c2a8 --- /dev/null +++ b/packages/solid-type-index/test/test-server/configs/template/wac/profile/card.acl.hbs @@ -0,0 +1,19 @@ +# ACL resource for the WebID profile document +@prefix acl: . +@prefix foaf: . + +# The WebID profile is readable by the public. +# This is required for discovery and verification, +# e.g. when checking identity providers. +<#public> + a acl:Authorization; + acl:agentClass foaf:Agent; + acl:accessTo <./card>; + acl:mode acl:Read. + +# The owner has full access to the profile +<#owner> + a acl:Authorization; + acl:agent <{{webId}}>; + acl:accessTo <./card>; + acl:mode acl:Read, acl:Write, acl:Control. \ No newline at end of file diff --git a/packages/solid-type-index/test/test-server/runServer.ts b/packages/solid-type-index/test/test-server/runServer.ts new file mode 100644 index 0000000..9d91222 --- /dev/null +++ b/packages/solid-type-index/test/test-server/runServer.ts @@ -0,0 +1,7 @@ +import { createApp } from "./solidServer.helper"; + +async function run() { + const app = await createApp(); + await app.start(); +} +run(); diff --git a/packages/solid-type-index/test/test-server/solidServer.helper.ts b/packages/solid-type-index/test/test-server/solidServer.helper.ts new file mode 100644 index 0000000..5dd45d8 --- /dev/null +++ b/packages/solid-type-index/test/test-server/solidServer.helper.ts @@ -0,0 +1,39 @@ +// Taken from https://github.com/comunica/comunica/blob/b237be4265c353a62a876187d9e21e3bc05123a3/engines/query-sparql/test/QuerySparql-solid-test.ts#L9 + +import * as path from "path"; +import type { App } from "@solid/community-server"; +import { AppRunner, resolveModulePath } from "@solid/community-server"; + +export async function createApp(): Promise { + if (process.env.SERVER) { + return { + start: () => {}, + stop: () => {}, + } as App; + } + const appRunner = new AppRunner(); + + return appRunner.create({ + loaderProperties: { + mainModulePath: resolveModulePath(""), + typeChecking: false, + }, + config: path.join( + __dirname, + "configs", + "components-config", + "unauthenticatedServer.json", + ), + variableBindings: {}, + shorthand: { + port: 3_001, + loggingLevel: "off", + seedConfig: path.join(__dirname, "configs", "solid-css-seed.json"), + }, + }); +} + +export interface ISecretData { + id: string; + secret: string; +} diff --git a/packages/solid-type-index/tsconfig.build.json b/packages/solid-type-index/tsconfig.build.json new file mode 100644 index 0000000..e375629 --- /dev/null +++ b/packages/solid-type-index/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "lib": ["dom"] + }, + "include": ["./src"] +} \ No newline at end of file