diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aabd133..d5bd0d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,15 @@ jobs: node-version-file: .nvmrc - run: npm i - run: npm audit --omit=dev --audit-level=moderate + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 + with: + node-version-file: .nvmrc + - run: npm i + - run: npx typedoc test: strategy: matrix: @@ -34,11 +43,11 @@ jobs: summary: if: always() - needs: [audit, test] + needs: [audit, test, docs] runs-on: ubuntu-latest steps: - name: Summary run: | - if [[ "${{ needs.audit.result }}" != "success" ]] || [[ "${{ needs.test.result }}" != "success" ]]; then + if [[ "${{ needs.audit.result }}" != "success" ]] || [[ "${{ needs.test.result }}" != "success" ]] || [[ "${{ needs.docs.result }}" != "success" ]]; then exit 1 fi diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..070dc03 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,34 @@ +name: Docs → Pages + +on: + push: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 + with: + node-version-file: .nvmrc + - run: npm i + - run: npx typedoc + - uses: actions/upload-pages-artifact@v4 + + deploy: + needs: build + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - uses: actions/deploy-pages@v5 diff --git a/.gitignore b/.gitignore index b075d69..de02df4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ package-lock.json # Build output dist +_site # Generic ignore .local diff --git a/README.md b/README.md index f437041..aecafd6 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,10 @@ Practically, to map RDF to objects, you need to: 1. Each class property will have an associated RDF Property (a string, generally a URL, that is defined by an ontology/vocabulary) 1. Each class property will have an associated arity (singular, singular nullable or set) 1. Each class property depending on its type can have: - 1. a corresponding ValueMapping to get values, that is translating RDF Terms to JavaScript [primitive values](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Data_structures#primitive_values) (string, number, boolean...) - 1. a corresponding TermMapping to set values, that is translating Javascript primitive values to RDF Terms - 1. a corresponding ObjectMapping to wrap child objects as a TermWrapper - 1. a corresponding ValueMapping and TermMapping for sets of primitive values (both can be an ObjectMapping) + 1. a corresponding value mapping to get values, that is translating RDF Terms to JavaScript [primitive values](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Data_structures#primitive_values) (string, number, boolean...) + 1. a corresponding term mapping to set values, that is translating Javascript primitive values to RDF Terms + 1. a corresponding mapping to wrap child objects as a TermWrapper + 1. a corresponding mapping for sets of primitive values 1. Each class mutates the underlying Dataset that is passed to it at instantiation time @@ -55,15 +55,15 @@ A [term](https://www.w3.org/TR/rdf12-concepts/#section-terms) wrapper instantiat For example you can write a `Person` class with one `name` property: ```javascript -import { TermWrapper, ValueMapping, TermMapping } from "https://unpkg.com/@rdfjs/wrapper" +import { TermWrapper, LiteralAs, LiteralFrom } from "https://unpkg.com/@rdfjs/wrapper" class Person extends TermWrapper { get name() { - return this.singularNullable("https://example.org/name", ValueMapping.literalToString) + return this.singularNullable("https://example.org/name", LiteralAs.string) } set name(value) { - this.overwriteNullable("https://example.org/name", value, TermMapping.literalToString) + this.overwriteNullable("https://example.org/name", value, LiteralFrom.string) } } ``` @@ -134,23 +134,23 @@ for (const person of people) { For example you can write a `Person` class with one `name` and one `mum` property: ```javascript -import { TermWrapper, ValueMapping, TermMapping, ObjectMapping } from "https://unpkg.com/@rdfjs/wrapper" +import { TermWrapper, LiteralAs, LiteralFrom, TermAs, TermFrom } from "https://unpkg.com/@rdfjs/wrapper" class Person extends TermWrapper { get name() { - return this.singularNullable("https://example.org/name", ValueMapping.literalToString) + return this.singularNullable("https://example.org/name", LiteralAs.string) } set name(value) { - this.singularNullable("https://example.org/name", value, TermMapping.literalToString) + this.singularNullable("https://example.org/name", value, LiteralFrom.string) } get mum() { - return this.singularNullable("https://example.org/mum", ObjectMapping.as(Person)) + return this.singularNullable("https://example.org/mum", TermAs.instance(Person)) } set mum(value) { - this.overwriteNullable("https://example.org/mum", value, ObjectMapping.as(Person)) + this.overwriteNullable("https://example.org/mum", value, TermFrom.instance) } } ``` diff --git a/package.json b/package.json index f80a850..2d77afa 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,9 @@ "@types/node": "^25", "typescript": "^6", "@types/n3": "^1", - "n3": "^2" + "n3": "^2", + "typedoc": "^0.28.18", + "typedoc-plugin-mdn-links": "^5.1.1" }, "engines": { "node": ">=24.0.0" diff --git a/src/DatasetWrapper.ts b/src/DatasetWrapper.ts index 7325a4a..af7bbd6 100644 --- a/src/DatasetWrapper.ts +++ b/src/DatasetWrapper.ts @@ -3,7 +3,9 @@ import type { ITermWrapperConstructor } from "./type/ITermWrapperConstructor.js" import { RDF } from "./vocabulary/RDF.js" -abstract class DatasetCoreBase implements DatasetCore { +export class DatasetWrapper implements DatasetCore { + //#region DatasetCore + public constructor(private readonly dataset: DatasetCore, protected readonly factory: DataFactory) { } @@ -32,9 +34,11 @@ abstract class DatasetCoreBase implements DatasetCore { public match(subject?: Term, predicate?: Term, object?: Term, graph?: Term): DatasetCore { return this.dataset.match(subject, predicate, object, graph) } -} -export class DatasetWrapper extends DatasetCoreBase { + //#endregion + + //#region Utilities + protected* subjectsOf(predicate: string, termWrapper: ITermWrapperConstructor): Iterable { for (const q of this.matchSubjectsOf(termWrapper, this.factory.namedNode(predicate))) { yield q @@ -65,6 +69,8 @@ export class DatasetWrapper extends DatasetCoreBase { } } + //#endregion + get [Symbol.toStringTag]() { return this.constructor.name } diff --git a/src/mapping/LiteralAs.ts b/src/mapping/LiteralAs.ts index 7eda7a1..1cc52d9 100644 --- a/src/mapping/LiteralAs.ts +++ b/src/mapping/LiteralAs.ts @@ -97,12 +97,12 @@ export namespace LiteralAs { * - [`Uint8Array.fromHex()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/fromHex) * - [`Uint8Array.fromBase64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/fromBase64) * - * @throws {@link ReferenceError} If the term is `undefined` or `null`. - * @throws {@link TypeError} If the term is not a {@link TermWrapper}. + * @throws {@link !ReferenceError ReferenceError} If the term is `undefined` or `null`. + * @throws {@link !TypeError TypeError} If the term is not a {@link TermWrapper}. * @throws {@link TermTypeError} If the term is not a literal. * @throws {@link TermTypeError} If the term is not a literal. * @throws {@link LiteralDatatypeError} If the term's datatype is not one of the supported datatypes. - * @throws {@link SyntaxError} If the term's lexical value cannot be converted. + * @throws {@link !SyntaxError SyntaxError} If the term's lexical value cannot be converted. * * @example Convert a hexadecimal value * The RDF diff --git a/src/mod.ts b/src/mod.ts index 2a27f77..eb55312 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -2,6 +2,7 @@ export type * from "./type/ITermAsValueMapping.js" export type * from "./type/ITermWrapperConstructor.js" export type * from "./type/ITermFromValueMapping.js" export type * from "./type/ILangString.js" +export type * from "./type/IAnyTerm.js" export * from "./decorators/GetterArity.js" export * from "./decorators/SetterArity.js" @@ -18,6 +19,8 @@ export * from "./mapping/BlankNodeFrom.js" export * from "./DatasetWrapper.js" export * from "./TermWrapper.js" +export * from "./AnyTerm.js" +export * from "./AnyTermWithContext.js" export * from "./errors/WrapperError.js" export * from "./errors/TermError.js" diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 0000000..eaf63bf --- /dev/null +++ b/typedoc.json @@ -0,0 +1,29 @@ +{ + "out": "./_site", + "highlightLanguages": [ + "ts", + "javascript", + "turtle" + ], + "plugin": [ + "typedoc-plugin-mdn-links" + ], + "externalSymbolLinkMappings": { + "@rdfjs/types": { + "BlankNode": "https://rdf.js.org/data-model-spec/#blanknode-interface", + "DataFactory": "https://rdf.js.org/data-model-spec/#datafactory-interface", + "DatasetCore": "https://rdf.js.org/dataset-spec/#datasetcore-interface", + "DatasetCore.add": "https://rdf.js.org/data-model-spec/#datafactory-interface", + "DatasetCore.delete": "https://rdf.js.org/dataset-spec/#dom-datasetcore-delete", + "DatasetCore.match": "https://rdf.js.org/dataset-spec/#dom-datasetcore-match", + "DatasetCore.has": "https://rdf.js.org/dataset-spec/#dom-datasetcore-has", + "DatasetCore.size": "https://rdf.js.org/dataset-spec/#dom-datasetcore-size", + "DefaultGraph": "https://rdf.js.org/data-model-spec/#defaultgraph-interface", + "Literal": "https://rdf.js.org/data-model-spec/#literal-interface", + "NamedNode": "https://rdf.js.org/data-model-spec/#namednode-interface", + "Quad": "https://rdf.js.org/data-model-spec/#quad-interface", + "Term": "https://rdf.js.org/data-model-spec/#term-interface", + "Variable": "https://rdf.js.org/data-model-spec/#variable-interface" + } + } +}