diff --git a/src/solid/ContainerDataset.ts b/src/solid/ContainerDataset.ts index a505155..bfa3dc7 100644 --- a/src/solid/ContainerDataset.ts +++ b/src/solid/ContainerDataset.ts @@ -3,13 +3,24 @@ import { Container } from "./Container.js" import { LDP } from "../vocabulary/mod.js" export class ContainerDataset extends DatasetWrapper { - // TODO: Consider that this might be undefined if there are no contained resources. We might need different matching. + get container(): Container | undefined { // Return the first container in the dataset for (const s of this.subjectsOf(LDP.contains, Container)) { return s } + // If the container is empty, then the only instance of `ldp:Container` + // and `ldp:BasicContainer` in this dataset should be the container + // itself. + for (const s of this.instancesOf(LDP.Container, Container)) { + return s + } + + for (const s of this.instancesOf(LDP.BasicContainer, Container)) { + return s + } + return } } diff --git a/src/vocabulary/ldp.ts b/src/vocabulary/ldp.ts index 7ce70b9..557cd4d 100644 --- a/src/vocabulary/ldp.ts +++ b/src/vocabulary/ldp.ts @@ -1,3 +1,5 @@ export const LDP = { contains: "http://www.w3.org/ns/ldp#contains", + Container: "http://www.w3.org/ns/ldp#Container", + BasicContainer: "http://www.w3.org/ns/ldp#BasicContainer", } as const; diff --git a/test/unit/container.test.ts b/test/unit/container.test.ts new file mode 100644 index 0000000..34453b2 --- /dev/null +++ b/test/unit/container.test.ts @@ -0,0 +1,50 @@ +import { DataFactory, Parser, Store } from "n3"; +import assert from "node:assert"; +import { describe, it } from "node:test"; +import { ContainerDataset } from "@solid/object"; + +describe("ContainerDataset", () => { + const emptyContainerRDF = ` + @prefix dc: . + @prefix ldp: . + @prefix posix: . + @prefix xsd: . + <> a ldp:Container, ldp:BasicContainer, ldp:Resource; + dc:modified "2026-07-23T10:41:12.846Z"^^xsd:dateTime; + posix:mtime 1784803272. + `; + + const nonEmptyContainerRDF = ` + @prefix ldp: . + + a ldp:Container; + ldp:contains . + + a ldp:Resource . + `; + + it("resolves an empty container via rdf:type when ldp:contains is absent", () => { + const store = new Store(); + const parser = new Parser({ baseIRI: "https://pod.example/empty/" }); + store.addQuads(parser.parse(emptyContainerRDF)); + const dataset = new ContainerDataset(store, DataFactory); + const container = dataset.container; + assert.ok(container !== undefined); + assert.equal(container.id, "https://pod.example/empty/"); + assert.equal(container.contains.size, 0); + }); + + it("still resolves a non-empty container via ldp:contains", () => { + const store = new Store(); + store.addQuads(new Parser().parse(nonEmptyContainerRDF)); + const dataset = new ContainerDataset(store, DataFactory); + const container = dataset.container; + assert.ok(container !== undefined); + assert.equal(container.id, "https://pod.example/container/"); + assert.equal(container.contains.size, 1); + assert.equal( + container.contains.values().next().value?.id, + "https://pod.example/container/file.txt", + ); + }); +});