From f3d8886f8b5a1b52b7ec7c2f8b942bdf77573c8e Mon Sep 17 00:00:00 2001 From: Tanya Gray Date: Mon, 9 Feb 2026 13:44:17 +0000 Subject: [PATCH 1/5] add Meeting object --- src/solid/Meeting.ts | 53 ++++++++++++++++++++++++++++++++++++++++++ src/vocabulary/ical.ts | 1 + src/vocabulary/mod.ts | 1 + 3 files changed, 55 insertions(+) create mode 100644 src/solid/Meeting.ts diff --git a/src/solid/Meeting.ts b/src/solid/Meeting.ts new file mode 100644 index 0000000..d93f91c --- /dev/null +++ b/src/solid/Meeting.ts @@ -0,0 +1,53 @@ +import { TermMappings, ValueMappings, TermWrapper, DatasetWrapper, } from "rdfjs-wrapper" +import { ICAL } from "../vocabulary/mod.js" + + +export class MeetingDataset extends DatasetWrapper { + get meeting(): Iterable { + return this.instancesOf(ICAL.vevent, Meeting) + } +} + + +export class Meeting extends TermWrapper { + get summary(): string | undefined { + return this.singularNullable(ICAL.summary, ValueMappings.literalToString) + } + + set summary(value: string | undefined) { + this.overwriteNullable(ICAL.summary, value, TermMappings.stringToLiteral) + } + + get location(): string | undefined { + return this.singularNullable(ICAL.location, ValueMappings.literalToString) + } + + set location(value: string | undefined) { + this.overwriteNullable(ICAL.location, value, TermMappings.stringToLiteral) + } + + get comment(): string | undefined { + return this.singularNullable(ICAL.comment, ValueMappings.literalToString) + } + + set comment(value: string | undefined) { + this.overwriteNullable(ICAL.comment, value, TermMappings.stringToLiteral) + } + + get startDate(): Date | undefined { + return this.singularNullable(ICAL.dtstart, ValueMappings.literalToDate) + } + + set startDate(value: Date | undefined) { + this.overwriteNullable(ICAL.dtstart, value, TermMappings.dateToLiteral) + } + + get endDate(): Date | undefined { + return this.singularNullable(ICAL.dtend, ValueMappings.literalToDate) + } + + set endDate(value: Date | undefined) { + this.overwriteNullable(ICAL.dtend, value, TermMappings.dateToLiteral) + } + +} diff --git a/src/vocabulary/ical.ts b/src/vocabulary/ical.ts index d17c669..9d53fbe 100644 --- a/src/vocabulary/ical.ts +++ b/src/vocabulary/ical.ts @@ -4,4 +4,5 @@ export const ICAL = { dtstart: "http://www.w3.org/2002/12/cal/ical#dtstart", location: "http://www.w3.org/2002/12/cal/ical#location", summary: "http://www.w3.org/2002/12/cal/ical#summary", + vevent: "http://www.w3.org/2002/12/cal/ical#Vevent" } as const; diff --git a/src/vocabulary/mod.ts b/src/vocabulary/mod.ts index 412818c..ed63146 100644 --- a/src/vocabulary/mod.ts +++ b/src/vocabulary/mod.ts @@ -8,3 +8,4 @@ export * from "./rdf.js" export * from "./rdfs.js" export * from "./solid.js" export * from "./vcard.js" +export * from "./ical.js" \ No newline at end of file From fc6b44a6366fd0063a85c7bb284d6e1f31da366a Mon Sep 17 00:00:00 2001 From: Tanya Gray Date: Mon, 9 Feb 2026 15:23:21 +0000 Subject: [PATCH 2/5] add meeting test --- package.json | 1 + src/mod.ts | 2 + src/solid/Meeting.ts | 3 +- src/solid/mod.ts | 1 + test/unit/meeting.test.ts | 102 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 test/unit/meeting.test.ts diff --git a/package.json b/package.json index d24e608..d7bb8de 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ ], "license": "MIT", "dependencies": { + "@solid/object": "^0.4.0", "rdfjs-wrapper": "^0.15.0" }, "devDependencies": { diff --git a/src/mod.ts b/src/mod.ts index a8678e4..1d8f1af 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -1,3 +1,5 @@ export * from "./acp/mod.js" export * from "./solid/mod.js" export * from "./webid/mod.js" + + diff --git a/src/solid/Meeting.ts b/src/solid/Meeting.ts index d93f91c..aebbf52 100644 --- a/src/solid/Meeting.ts +++ b/src/solid/Meeting.ts @@ -1,4 +1,4 @@ -import { TermMappings, ValueMappings, TermWrapper, DatasetWrapper, } from "rdfjs-wrapper" +import { TermMappings, ValueMappings, TermWrapper, DatasetWrapper } from "rdfjs-wrapper" import { ICAL } from "../vocabulary/mod.js" @@ -8,7 +8,6 @@ export class MeetingDataset extends DatasetWrapper { } } - export class Meeting extends TermWrapper { get summary(): string | undefined { return this.singularNullable(ICAL.summary, ValueMappings.literalToString) diff --git a/src/solid/mod.ts b/src/solid/mod.ts index fb84c5f..36d1929 100644 --- a/src/solid/mod.ts +++ b/src/solid/mod.ts @@ -1,3 +1,4 @@ export * from "./Container.js" export * from "./ContainerDataset.js" export * from "./Resource.js" +export * from "./Meeting.js" \ No newline at end of file diff --git a/test/unit/meeting.test.ts b/test/unit/meeting.test.ts new file mode 100644 index 0000000..3329c60 --- /dev/null +++ b/test/unit/meeting.test.ts @@ -0,0 +1,102 @@ +import { DataFactory, Parser, Store } from "n3" +import assert from "node:assert" +import { describe, it } from "node:test" + +import { MeetingDataset } from "@solid/object"; + + +describe("MeetingDataset / Meeting tests", () => { + + const sampleRDF = ` +@prefix cal: . +@prefix xsd: . + + a cal:Vevent ; + cal:summary "Team Sync" ; + cal:location "Zoom Room 123" ; + cal:comment "Discuss project updates" ; + cal:dtstart "2026-02-09T10:00:00Z"^^xsd:dateTime ; + cal:dtend "2026-02-09T11:00:00Z"^^xsd:dateTime . +`; + + it("should parse and retrieve meeting properties", () => { + const store = new Store(); + store.addQuads(new Parser().parse(sampleRDF)); + + const dataset = new MeetingDataset(store, DataFactory); + const meetings = Array.from(dataset.meeting); + + const meeting = meetings[0]; + assert.ok(meeting, "No meeting found") + + // Check property types and values + + assert.equal(meeting.summary, "Team Sync"); + assert.equal(meeting.location, "Zoom Room 123"); + assert.equal(meeting.comment, "Discuss project updates"); + + + assert.ok(meeting.startDate instanceof Date); + assert.ok(meeting.endDate instanceof Date); + + assert.equal(meeting.startDate?.toISOString(), "2026-02-09T10:00:00.000Z"); + assert.equal(meeting.endDate?.toISOString(), "2026-02-09T11:00:00.000Z"); + }); + + + + it("should allow setting of meeting properties", () => { + const store = new Store(); + store.addQuads(new Parser().parse(sampleRDF)); + + const dataset = new MeetingDataset(store, DataFactory); + const meetings = Array.from(dataset.meeting); + + assert.ok(meetings.length > 0, "No meetings found"); + + const meeting = Array.from(dataset.meeting)[0]!; + + // Set new values + meeting.summary = "Updated Meeting"; + meeting.location = "Conference Room A"; + meeting.comment = "New agenda"; + const newStart = new Date("2026-02-09T12:00:00Z"); + const newEnd = new Date("2026-02-09T13:00:00Z"); + meeting.startDate = newStart; + meeting.endDate = newEnd; + + // Retrieve again + assert.equal(meeting.summary, "Updated Meeting"); + assert.equal(meeting.location, "Conference Room A"); + assert.equal(meeting.comment, "New agenda"); + assert.equal(meeting.startDate.toISOString(), newStart.toISOString()); + assert.equal(meeting.endDate.toISOString(), newEnd.toISOString()); + }); + + + + it("should ensure all properties are correct type", () => { + const store = new Store(); + store.addQuads(new Parser().parse(sampleRDF)); + + const dataset = new MeetingDataset(store, DataFactory); + const meeting = Array.from(dataset.meeting)[0]; + + assert.ok(meeting, "No meeting found") + + // Check property types + + assert.equal(typeof meeting.summary, "string"); + assert.equal(typeof meeting.location, "string"); + assert.equal(typeof meeting.comment, "string"); + + assert.ok(meeting.startDate instanceof Date, "startDate should be a Date"); + assert.ok(meeting.endDate instanceof Date, "endDate should be a Date"); + + + + + + }); + +}); \ No newline at end of file From 36dec9b9dffbb8aeafed30d24eff0f4728736816 Mon Sep 17 00:00:00 2001 From: Tanya Gray Date: Mon, 9 Feb 2026 15:44:57 +0000 Subject: [PATCH 3/5] add test for unique non array values --- test/unit/meeting.test.ts | 47 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/test/unit/meeting.test.ts b/test/unit/meeting.test.ts index 3329c60..3f44af9 100644 --- a/test/unit/meeting.test.ts +++ b/test/unit/meeting.test.ts @@ -12,6 +12,7 @@ describe("MeetingDataset / Meeting tests", () => { @prefix xsd: . a cal:Vevent ; + cal:summary "Team Sync" ; cal:location "Zoom Room 123" ; cal:comment "Discuss project updates" ; @@ -92,11 +93,53 @@ describe("MeetingDataset / Meeting tests", () => { assert.ok(meeting.startDate instanceof Date, "startDate should be a Date"); assert.ok(meeting.endDate instanceof Date, "endDate should be a Date"); + + }); - + it("should ensure all properties are unique text or date values", () => { - + const duplicateRDF = ` +@prefix cal: . +@prefix xsd: . + + a cal:Vevent ; + cal:summary "Team Sync" ; + cal:summary "Duplicate Summary" ; + cal:location "Zoom Room 123" ; + cal:location "Duplicate Location" ; + cal:comment "Discuss project updates" ; + cal:comment "Duplicate Comment" ; + cal:dtstart "2026-02-09T10:00:00Z"^^xsd:dateTime ; + cal:dtstart "2026-02-09T09:00:00Z"^^xsd:dateTime ; + cal:dtend "2026-02-09T11:00:00Z"^^xsd:dateTime ; + cal:dtend "2026-02-09T12:00:00Z"^^xsd:dateTime . +`; + + const store = new Store(); + store.addQuads(new Parser().parse(duplicateRDF)); + + const dataset = new MeetingDataset(store, DataFactory); + const meeting = Array.from(dataset.meeting)[0]; + + assert.ok(meeting, "No meeting found"); + + // Ensure exposed values are single (unique) and correct type + assert.equal(typeof meeting.summary, "string"); + assert.equal(typeof meeting.location, "string"); + assert.equal(typeof meeting.comment, "string"); + + assert.ok(meeting.startDate instanceof Date); + assert.ok(meeting.endDate instanceof Date); + + // Ensure no arrays are returned + assert.ok(!Array.isArray(meeting.summary)); + assert.ok(!Array.isArray(meeting.location)); + assert.ok(!Array.isArray(meeting.comment)); + assert.ok(!Array.isArray(meeting.startDate)); + assert.ok(!Array.isArray(meeting.endDate)); }); + + }); \ No newline at end of file From bbf6a2c5cc4cb9f890f67fb009cf9c736da9b0c9 Mon Sep 17 00:00:00 2001 From: Tanya Gray Date: Mon, 9 Feb 2026 17:10:39 +0000 Subject: [PATCH 4/5] start on Profile doc --- src/solid/Profile.ts | 158 +++++++++++++++++++++++++++++++++++++++++ src/vocabulary/foaf.ts | 4 ++ 2 files changed, 162 insertions(+) create mode 100644 src/solid/Profile.ts diff --git a/src/solid/Profile.ts b/src/solid/Profile.ts new file mode 100644 index 0000000..cb2d8f4 --- /dev/null +++ b/src/solid/Profile.ts @@ -0,0 +1,158 @@ +import { TermMappings, ValueMappings, TermWrapper, DatasetWrapper } from "rdfjs-wrapper" +import { FOAF } from "../vocabulary/mod.js" +import { URL } from "url" +import { WebIdDataset } from "../mod.js" + + +export class PersonalProfileDataset extends DatasetWrapper { + get profile(): Iterable { + return this.instancesOf(FOAF.PersonalProfileDocument, PersonalProfileDocument) + } +} + +export class PersonalProfileDocument extends TermWrapper { + get primaryTopic(): WebIdDataset | undefined { + return this.singularNullable(FOAF.primaryTopic, ValueMappings.iriToString) + } + set primaryTopic(value: WebIdDataset | undefined) { + this.overwriteNullable(FOAF.primaryTopic, value, TermMappings.stringToIri) + } + + + get maker(): WebIdDataset | undefined { + + return this.singularNullable(FOAF.maker, ValueMappings.iriToString) + } + set maker(value: WebIdDataset | undefined) { + this.overwriteNullable(FOAF.maker, value, TermMappings.stringToIri) + } + +} + + + + + + + + + +/* + + + +fields defined in + +https://pdsinterop.org/conventions/profile/ + +via + +/Users/tanyagray/Documents/code/ODI/task-solid-extract-classes/solid-panes/docs/conventions.md + + + + +```turtle +@prefix ab: . +@prefix acl: . +@prefix dc: . +@prefix dct: . +@prefix flow: . +@prefix foaf: . +@prefix ical: . +@prefix ldp: . +@prefix mee: . +@prefix pim: . +@prefix rdf: . +@prefix schema: . +@prefix sioc: . +@prefix solid: . +@prefix stat: . +@prefix ui: . +@prefix vcard: . +@prefix XML: . +``` + +One of the most important RDF documents on your pod is your profile, which is the document that people get when they dereference your webid. We'll look at that first. After that, we'll look at each of the tools that can be created with the databrowser's + button: Addressbook, Notepad, Chat, LongChat, Meeting, Event, Link, Document, Folder, and Source. + +## Profile +(see also [pdsinterop.org's description](https://pdsinterop.org/conventions/profile/)) + +### Profile document + +To add information to your webid profile, you can use the following triples. Suppose your webid is `/profile/card#me`, then your profile document is `/profile/card` (without the `#me`). Add the following triples to it: + +```turtle + a foaf:PersonalProfileDocument . + foaf:maker . + foaf:primaryTopic . +``` + +### You as a person + +Now say your name is "John Doe", then add these triples to your profile document to publish your identity as a person: + +```turtle + a foaf:Person . + a schema:Person . + foaf:name "John Doe" . +``` + +### Linking to your pod + +Say your pod is at `/pod`, with the LDN inbox at `/pod/inbox/`, to link from your identity to your pod: + +```turtle + solid:account . + pim:storage . + ldp:inbox . +``` + +### Preferences + +To publish some of your generic preferences to apps, use: + +```turtle + pim:preferencesFile . + solid:publicTypeIndex . + solid:privateTypeIndex . +``` + + + + +https://github.com/SolidOS/profile-pane/blob/main/src/ontology/profileForm.ttl + + + get location(): string | undefined { + return this.singularNullable(ICAL.location, ValueMappings.literalToString) + } + + set location(value: string | undefined) { + this.overwriteNullable(ICAL.location, value, TermMappings.stringToLiteral) + } + + get comment(): string | undefined { + return this.singularNullable(ICAL.comment, ValueMappings.literalToString) + } + + set comment(value: string | undefined) { + this.overwriteNullable(ICAL.comment, value, TermMappings.stringToLiteral) + } + + get startDate(): Date | undefined { + return this.singularNullable(ICAL.dtstart, ValueMappings.literalToDate) + } + + set startDate(value: Date | undefined) { + this.overwriteNullable(ICAL.dtstart, value, TermMappings.dateToLiteral) + } + + get endDate(): Date | undefined { + return this.singularNullable(ICAL.dtend, ValueMappings.literalToDate) + } + + set endDate(value: Date | undefined) { + this.overwriteNullable(ICAL.dtend, value, TermMappings.dateToLiteral) + } +*/ \ No newline at end of file diff --git a/src/vocabulary/foaf.ts b/src/vocabulary/foaf.ts index c375e39..a027d32 100644 --- a/src/vocabulary/foaf.ts +++ b/src/vocabulary/foaf.ts @@ -5,4 +5,8 @@ export const FOAF = { email: "http://xmlns.com/foaf/0.1/email", homepage: "http://xmlns.com/foaf/0.1/homepage", knows: "http://xmlns.com/foaf/0.1/knows", + Person: "http://xmlns.com/foaf/0.1/Person", + PersonalProfileDocument: "http://xmlns.com/foaf/0.1/PersonalProfileDocument", + maker: "http://xmlns.com/foaf/0.1/maker" + } as const; From 7910e57f709b9d28a4687bb88e0483f1da089a50 Mon Sep 17 00:00:00 2001 From: Tanya Gray Date: Mon, 9 Feb 2026 22:38:18 +0000 Subject: [PATCH 5/5] early commit of Profile.ts --- src/solid/Profile.ts | 158 --------------------------- src/vocabulary/foaf.ts | 21 ++-- src/vocabulary/mod.ts | 4 +- src/vocabulary/org.ts | 7 ++ src/vocabulary/schema.ts | 12 +++ src/vocabulary/soc.ts | 22 ++++ src/vocabulary/solid.ts | 17 ++- src/webid/Profile.ts | 224 +++++++++++++++++++++++++++++++++++++++ src/webid/mod.ts | 1 + 9 files changed, 300 insertions(+), 166 deletions(-) delete mode 100644 src/solid/Profile.ts create mode 100644 src/vocabulary/org.ts create mode 100644 src/vocabulary/schema.ts create mode 100644 src/vocabulary/soc.ts create mode 100644 src/webid/Profile.ts diff --git a/src/solid/Profile.ts b/src/solid/Profile.ts deleted file mode 100644 index cb2d8f4..0000000 --- a/src/solid/Profile.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { TermMappings, ValueMappings, TermWrapper, DatasetWrapper } from "rdfjs-wrapper" -import { FOAF } from "../vocabulary/mod.js" -import { URL } from "url" -import { WebIdDataset } from "../mod.js" - - -export class PersonalProfileDataset extends DatasetWrapper { - get profile(): Iterable { - return this.instancesOf(FOAF.PersonalProfileDocument, PersonalProfileDocument) - } -} - -export class PersonalProfileDocument extends TermWrapper { - get primaryTopic(): WebIdDataset | undefined { - return this.singularNullable(FOAF.primaryTopic, ValueMappings.iriToString) - } - set primaryTopic(value: WebIdDataset | undefined) { - this.overwriteNullable(FOAF.primaryTopic, value, TermMappings.stringToIri) - } - - - get maker(): WebIdDataset | undefined { - - return this.singularNullable(FOAF.maker, ValueMappings.iriToString) - } - set maker(value: WebIdDataset | undefined) { - this.overwriteNullable(FOAF.maker, value, TermMappings.stringToIri) - } - -} - - - - - - - - - -/* - - - -fields defined in - -https://pdsinterop.org/conventions/profile/ - -via - -/Users/tanyagray/Documents/code/ODI/task-solid-extract-classes/solid-panes/docs/conventions.md - - - - -```turtle -@prefix ab: . -@prefix acl: . -@prefix dc: . -@prefix dct: . -@prefix flow: . -@prefix foaf: . -@prefix ical: . -@prefix ldp: . -@prefix mee: . -@prefix pim: . -@prefix rdf: . -@prefix schema: . -@prefix sioc: . -@prefix solid: . -@prefix stat: . -@prefix ui: . -@prefix vcard: . -@prefix XML: . -``` - -One of the most important RDF documents on your pod is your profile, which is the document that people get when they dereference your webid. We'll look at that first. After that, we'll look at each of the tools that can be created with the databrowser's + button: Addressbook, Notepad, Chat, LongChat, Meeting, Event, Link, Document, Folder, and Source. - -## Profile -(see also [pdsinterop.org's description](https://pdsinterop.org/conventions/profile/)) - -### Profile document - -To add information to your webid profile, you can use the following triples. Suppose your webid is `/profile/card#me`, then your profile document is `/profile/card` (without the `#me`). Add the following triples to it: - -```turtle - a foaf:PersonalProfileDocument . - foaf:maker . - foaf:primaryTopic . -``` - -### You as a person - -Now say your name is "John Doe", then add these triples to your profile document to publish your identity as a person: - -```turtle - a foaf:Person . - a schema:Person . - foaf:name "John Doe" . -``` - -### Linking to your pod - -Say your pod is at `/pod`, with the LDN inbox at `/pod/inbox/`, to link from your identity to your pod: - -```turtle - solid:account . - pim:storage . - ldp:inbox . -``` - -### Preferences - -To publish some of your generic preferences to apps, use: - -```turtle - pim:preferencesFile . - solid:publicTypeIndex . - solid:privateTypeIndex . -``` - - - - -https://github.com/SolidOS/profile-pane/blob/main/src/ontology/profileForm.ttl - - - get location(): string | undefined { - return this.singularNullable(ICAL.location, ValueMappings.literalToString) - } - - set location(value: string | undefined) { - this.overwriteNullable(ICAL.location, value, TermMappings.stringToLiteral) - } - - get comment(): string | undefined { - return this.singularNullable(ICAL.comment, ValueMappings.literalToString) - } - - set comment(value: string | undefined) { - this.overwriteNullable(ICAL.comment, value, TermMappings.stringToLiteral) - } - - get startDate(): Date | undefined { - return this.singularNullable(ICAL.dtstart, ValueMappings.literalToDate) - } - - set startDate(value: Date | undefined) { - this.overwriteNullable(ICAL.dtstart, value, TermMappings.dateToLiteral) - } - - get endDate(): Date | undefined { - return this.singularNullable(ICAL.dtend, ValueMappings.literalToDate) - } - - set endDate(value: Date | undefined) { - this.overwriteNullable(ICAL.dtend, value, TermMappings.dateToLiteral) - } -*/ \ No newline at end of file diff --git a/src/vocabulary/foaf.ts b/src/vocabulary/foaf.ts index a027d32..d8b7bd2 100644 --- a/src/vocabulary/foaf.ts +++ b/src/vocabulary/foaf.ts @@ -1,12 +1,21 @@ export const FOAF = { - isPrimaryTopicOf: "http://xmlns.com/foaf/0.1/isPrimaryTopicOf", - primaryTopic: "http://xmlns.com/foaf/0.1/primaryTopic", - name: "http://xmlns.com/foaf/0.1/name", + account: "http://xmlns.com/foaf/0.1/account", + accountName: "http://xmlns.com/foaf/0.1/accountName", email: "http://xmlns.com/foaf/0.1/email", homepage: "http://xmlns.com/foaf/0.1/homepage", + icon: "http://xmlns.com/foaf/0.1/icon", + isPrimaryTopicOf: "http://xmlns.com/foaf/0.1/isPrimaryTopicOf", knows: "http://xmlns.com/foaf/0.1/knows", + maker: "http://xmlns.com/foaf/0.1/maker", + name: "http://xmlns.com/foaf/0.1/name", + nick: "http://xmlns.com/foaf/0.1/nick", + primaryTopic: "http://xmlns.com/foaf/0.1/primaryTopic", + + + Account: "http://xmlns.com/foaf/0.1/Account", + OnlineAccount: "http://xmlns.com/foaf/0.1/OnlineAccount", Person: "http://xmlns.com/foaf/0.1/Person", - PersonalProfileDocument: "http://xmlns.com/foaf/0.1/PersonalProfileDocument", - maker: "http://xmlns.com/foaf/0.1/maker" - + PersonalProfileDocument: "http://xmlns.com/foaf/0.1/PersonalProfileDocument", + + } as const; diff --git a/src/vocabulary/mod.ts b/src/vocabulary/mod.ts index ed63146..482db9c 100644 --- a/src/vocabulary/mod.ts +++ b/src/vocabulary/mod.ts @@ -8,4 +8,6 @@ export * from "./rdf.js" export * from "./rdfs.js" export * from "./solid.js" export * from "./vcard.js" -export * from "./ical.js" \ No newline at end of file +export * from "./ical.js" +export * from "./schema.js" +export * from "./org.js" \ No newline at end of file diff --git a/src/vocabulary/org.ts b/src/vocabulary/org.ts new file mode 100644 index 0000000..b54d77e --- /dev/null +++ b/src/vocabulary/org.ts @@ -0,0 +1,7 @@ +export const ORG = { + + + member: "http://www.w3.org/ns/org#member", + organization: "http://www.w3.org/ns/org#organization", + role: "http://www.w3.org/ns/org#role" +} \ No newline at end of file diff --git a/src/vocabulary/schema.ts b/src/vocabulary/schema.ts new file mode 100644 index 0000000..32b2af0 --- /dev/null +++ b/src/vocabulary/schema.ts @@ -0,0 +1,12 @@ +export const SCHEMA = { + knowsLanguage: "https://schema.org/knowsLanguage", + Organization: "https://schema.org/Organization", + skills: "https://schema.org/skills", + startDate: "https://schema.org/startDate", + endDate: "https://schema.org/endDate", + description: "https://schema.org/description", + name: "https://schema.org/name", + uri: "https://schema.org/uri", +} as const; + + diff --git a/src/vocabulary/soc.ts b/src/vocabulary/soc.ts new file mode 100644 index 0000000..23258fa --- /dev/null +++ b/src/vocabulary/soc.ts @@ -0,0 +1,22 @@ +export const SOC = { + BlueSkyAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#BlueSkyAccount", + Digg: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#Digg", + FacebookAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#FacebookAccount", + GithubAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#GithubAccount", + InstagramAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#InstagramAccount", + LinkedInAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#LinkedInAccount", + MastodonAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#MastodonAccount", + MatrixAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#MatrixAccount", + MediumAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#MediumAccount", + NostrAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#NostrAccount", + OrcidAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#OrcidAccount", + PinterestAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#PinterestAccount", + RedditAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#RedditAccount", + SnapchatAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#SnapchatAccount", + StravaAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#StravaAccount", + TiktokAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#TiktokAccount", + TumblrAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#TumblrAccount", + TwitterAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#TwitterAccount", + OtherAccount: "https://solidos.github.io/profile-pane/src/ontology/socialMedia.ttl#OtherAccount", + }; + \ No newline at end of file diff --git a/src/vocabulary/solid.ts b/src/vocabulary/solid.ts index ead7f18..d69cec7 100644 --- a/src/vocabulary/solid.ts +++ b/src/vocabulary/solid.ts @@ -1,4 +1,19 @@ export const SOLID = { oidcIssuer: "http://www.w3.org/ns/solid/terms#oidcIssuer", storage: "http://www.w3.org/ns/solid/terms#storage", -} as const; + preferredSubjectPronoun: "http://www.w3.org/ns/solid/terms#preferredSubjectPronoun", + preferredObjectPronoun: "http://www.w3.org/ns/solid/terms#preferredObjectPronoun", + preferredRelativePronoun: "http://www.w3.org/ns/solid/terms#preferredRelativePronoun", + publicId: "http://www.w3.org/ns/solid/terms#publicId", + + + // the following terms are not defined but are present in https://github.com/SolidOS/profile-pane/blob/main/src/ontology/profileForm.ttl + Role: "http://www.w3.org/ns/solid/terms#Role", + CurrentRole: "http://www.w3.org/ns/solid/terms#CurrentRole", + FormerRole: "http://www.w3.org/ns/solid/terms#FormerRole", + FutureRole: "http://www.w3.org/ns/solid/terms#FutureRole", + + + +} + diff --git a/src/webid/Profile.ts b/src/webid/Profile.ts new file mode 100644 index 0000000..a906a6f --- /dev/null +++ b/src/webid/Profile.ts @@ -0,0 +1,224 @@ +import { TermMappings, ValueMappings, TermWrapper, DatasetWrapper } from "rdfjs-wrapper" +import { FOAF, SOLID, SCHEMA, ORG, VCARD } from "../vocabulary/mod.js" + +import { Agent } from "@solid/object" + +export class ProfileDataset extends DatasetWrapper { + + get profile(): Iterable { + return this.instancesOf(FOAF.PersonalProfileDocument, Profile) + } +} + +export class Profile extends TermWrapper { + + get primaryTopic(): string | undefined { + return this.singularNullable(FOAF.primaryTopic, ValueMappings.iriToString ) + } + set primaryTopic(value: string | undefined) { + this.overwriteNullable(FOAF.primaryTopic, value, TermMappings.stringToIri) + } + + get maker(): string | undefined { + return this.singularNullable(FOAF.maker, ValueMappings.iriToString) + } + set maker(value: string | undefined) { + this.overwriteNullable(FOAF.maker, value, TermMappings.stringToIri) + } + + /* Nickname */ + get nickname(): string | undefined { + return this.singularNullable(FOAF.nick, ValueMappings.literalToString) + } + set nickname(value: string | undefined) { + this.overwriteNullable(FOAF.nick, value, TermMappings.stringToLiteral) + } + + /* Pronouns */ + get preferredSubjectPronoun(): string | undefined { + return this.singularNullable(SOLID.preferredSubjectPronoun, ValueMappings.literalToString) + } + set preferredSubjectPronoun(value: string | undefined) { + this.overwriteNullable(SOLID.preferredSubjectPronoun, value, TermMappings.stringToLiteral) + } + get preferredObjectPronoun(): string | undefined { + return this.singularNullable(SOLID.preferredObjectPronoun, ValueMappings.literalToString) + } + set preferredObjectPronoun(value: string | undefined) { + this.overwriteNullable(SOLID.preferredObjectPronoun, value, TermMappings.stringToLiteral) + } + get preferredRelativePronoun(): string | undefined { + return this.singularNullable(SOLID.preferredRelativePronoun, ValueMappings.literalToString) + } + set preferredRelativePronoun(value: string | undefined) { + this.overwriteNullable(SOLID.preferredRelativePronoun, value, TermMappings.stringToLiteral) + } + + + /* Roles (inverse org:member) */ + + get roles(): Iterable { + return this.objects(ORG.member, Role) + } + + + /* Skills */ + get skills(): Iterable { + + return this.objects(SCHEMA.skills, Skill) + } + + /* Languages */ + get languages(): Iterable { + return this.objects(SCHEMA.knowsLanguage, Language) + } + + /* Online Accounts */ + get accounts(): Iterable { + return this.objects(FOAF.account, OnlineAccount) + } + } + + + export class Organization extends TermWrapper { + + get name(): string | undefined { + return this.singularNullable(SCHEMA.name, ValueMappings.literalToString) + } + + set name(value: string | undefined) { + this.overwriteNullable(SCHEMA.name, value, TermMappings.stringToLiteral) + } + + get uri(): string | undefined { + return this.singularNullable(SCHEMA.uri, ValueMappings.iriToString) + } + + set uri(value: string | undefined) { + this.overwriteNullable(SCHEMA.uri, value, TermMappings.stringToIri) + } + + get publicId(): string | undefined { + return this.singularNullable(SOLID.publicId, ValueMappings.iriToString) + } + + set publicId(value: string | undefined) { + this.overwriteNullable(SOLID.publicId, value, TermMappings.stringToIri) + } + + } + + export class Role extends TermWrapper { + + get organization(): Organization | undefined { + return this.singularNullable(ORG.organization, Organization) + } + + set organization(value: Organization | undefined) { + this.overwriteNullable(ORG.organization, value) + } + + + + /* Role Name */ + get roleName(): string | undefined { + return this.singularNullable(VCARD.role, ValueMappings.literalToString) + } + + set roleName(value: string | undefined) { + this.overwriteNullable(VCARD.role, value, TermMappings.stringToLiteral) + } + + /* Occupation */ + get occupation(): Role | undefined { + return this.singularNullable(ORG.role, Role) + } + + set occupation(value: Role | undefined) { + this.overwriteNullable(ORG.role, value) + } + + + /* Start Date */ + get startDate(): Date | undefined { + return this.singularNullable(SCHEMA.startDate, ValueMappings.literalToDate) + } + + set startDate(value: Date | undefined) { + this.overwriteNullable(SCHEMA.startDate, value, TermMappings.dateToLiteral) + } + + /* End Date */ + get endDate(): Date | undefined { + return this.singularNullable(SCHEMA.endDate, ValueMappings.literalToDate) + } + + set endDate(value: Date | undefined) { + this.overwriteNullable(SCHEMA.endDate, value, TermMappings.dateToLiteral) + } + + /* Description */ + get description(): string | undefined { + return this.singularNullable(SCHEMA.description, ValueMappings.literalToString) + } + + set description(value: string | undefined) { + this.overwriteNullable(SCHEMA.description, value, TermMappings.stringToLiteral) + } + + } + + + + export class OnlineAccount extends TermWrapper { + + get accountName(): string | undefined { + return this.singularNullable(FOAF.accountName, ValueMappings.literalToString) + } + + set accountName(value: string | undefined) { + this.overwriteNullable(FOAF.accountName, value, TermMappings.stringToLiteral) + } + + get homepage(): string | undefined { + return this.singularNullable(FOAF.homepage, ValueMappings.iriToString) + } + + set homepage(value: string | undefined) { + this.overwriteNullable(FOAF.homepage, value, TermMappings.stringToIri) + } + + get icon(): string | undefined { + return this.singularNullable(FOAF.icon, ValueMappings.literalToString) + } + + set icon(value: string | undefined) { + this.overwriteNullable(FOAF.icon, value, TermMappings.stringToLiteral) + } + + } + + +export class Skill extends TermWrapper { + + get publicId(): string | undefined { + return this.singularNullable(SOLID.publicId, ValueMappings.iriToString) + } + + set publicId(value: string | undefined) { + this.overwriteNullable(SOLID.publicId, value, TermMappings.stringToIri) + } + +} + +export class Language extends TermWrapper { + + get publicId(): string | undefined { + return this.singularNullable(SOLID.publicId, ValueMappings.iriToString) + } + + set publicId(value: string | undefined) { + this.overwriteNullable(SOLID.publicId, value, TermMappings.stringToIri) + } + +} diff --git a/src/webid/mod.ts b/src/webid/mod.ts index 45f65a9..4eb2baa 100644 --- a/src/webid/mod.ts +++ b/src/webid/mod.ts @@ -1,2 +1,3 @@ export * from "./Agent.js" export * from "./WebIdDataset.js" +export * from "./Profile.js"