Skip to content

Bookstore OWL taxonomy

This page extends the bookstore domain with OWL-style class taxonomy - subClassOf, disjointWith, and registration patterns for the book hierarchy. Prerequisite: Bookstore domain and Graph concepts.

Book taxonomy and OWL axioms

Beyond the structural entities, the bookstore registry carries seven additional schemas plus two ABox identity assertions that together exercise every Compose class-axiom, every OWL restriction the library supports, and the runtime sameAs surface. These are the declarations that drive the live BookstoreGraph visualization below: the TBox tab shows this class taxonomy; the ABox tab shows the instance data typed by it.

Nodes

  • Entity — registered top-level class (Customer, Order, Book, Review, …)
  • Primitive — named scalar / constrained type (Email, Iso8601, Amount, …)

Edges

  • subClassOfrdfs:subClassOf (taxonomic narrowing)
  • equivalentClassowl:equivalentClass (alias / structural identity)
  • rangerdfs:range (property → typed target)
  • domainrdfs:domain (explicit override)
  • disjointWithowl:disjointWith (no shared instances)
  • complementOfowl:complementOf (negation)
  • restrictionowl:Restriction (cardinality / hasValue / …)
Loading graph data...
Schema or axiomSurface usedEdge / effect in graph
EBookSchemaCompose.subClassOf(Book)subClassOf → Book
PrintBookSchemaCompose.subClassOf(Book) + Compose.disjointWith(EBook)subClassOf → Book, disjointWith ↔ EBook
RareBookSchemaCompose.subClassOf(PrintBook) + Compose.someValuesFrom + Compose.maxCardinalitysubClassOf → PrintBook, two restriction edges
SignedFirstEditionSchemaCompose.subClassOf(RareBook) + registered signedFirstEditionIsSoloAuthored invariantsubClassOf → RareBook (the cardinality axiom lives off-graph as an invariant)
InPrintBookSchemaCompose.subClassOf(Book) + Compose.hasValue(printStatus, 'inPrint')subClassOf → Book, restriction on printStatus
OutOfPrintBookSchemaCompose.complementOf(InPrintBook) with body allOf bounding to BooksubClassOf → Book, complementOf → InPrintBook
orderTotalMatchesItemsbookstoreEntities.addInvariant(OrderSchema, ...)runtime cross-field rule on Order.orderTotal
signedFirstEditionIsSoloAuthoredbookstoreEntities.addInvariant(SignedFirstEditionSchema, ...)runtime cardinality rule on SignedFirstEdition.authors
sameAs(bastian-bux, cust-00042)JsonTology.prototype.sameAs, customer migration (ABox)sameAs between two customer-individual nodes
sameAs(neverending-1979-thienemann, oclc/5705614)JsonTology.prototype.sameAs, cross-catalog rare-book identity (ABox)sameAs between two book-individual nodes

Each entities/*.ts file is the single source of truth for one schema.

entities/EBook.ts: subClassOf

In plain English: an EBook is a Book with three extra fields (fileFormat, downloadUrl, fileSizeBytes). Validation against EBookSchema accepts only values that already validate against BookSchema and additionally carry the new fields. Tooling that reads the OWL output (RDF stores, reasoners) sees EBook as a kind of Book.

Use this when:

  • You want to derive a new type that adds fields to an existing one.
  • You want downstream RDF / OWL consumers to see the parent-child relationship.

Don't use this when:

/**
 * Bookstore taxonomy — EBookSchema as a Compose.subClassOf(Book)
 *
 * The canonical `EBookSchema` is defined in
 * `examples/docs/bookstore/entities/EBook.ts`. Reading the entity file
 * shows the full declaration; this runnable demo imports it and
 * exercises both the structural validation and the OWL TBox
 * subclass edge it emits.
 */

import {
  bookstoreEntities, EBookSchema
} from '../bookstore/index.js';

const ebook = {
  'authors': ['Michael Ende'],
  'downloadUrl': 'https://bookstore.example/dl/9783522128001.epub',
  'epubVersion': '3.2',
  'fileFormat': 'epub' as const,
  'fileSizeBytes': 5_872_000,
  'inStock': true,
  'isbn': '9783522128001',
  'pageCount': 428,
  'price': {
    'amount': 12.99,
    'currency': 'EUR'
  },
  'printStatus': 'inPrint' as const,
  'publishedOn': '1979-09-01',
  'stockLevel': 0,
  'title': 'Die unendliche Geschichte'
};

// Structural validation: passes against EBookSchema.
const ebookErrs = bookstoreEntities.validate(EBookSchema.$id, ebook);

console.assert(ebookErrs.length === 0);
// 0 — epub ebook passes EBook constraints
console.log('validation errors:', ebookErrs.length);

// The TBox emits `urn:bookstore:EBook rdfs:subClassOf urn:bookstore:Book`.
// Materialize the OWL JSON-LD and verify the subClassOf assertion lives
// alongside the structural validation in a single source of truth.
const owl = bookstoreEntities.ontology().jsonLdObject();
const graphNodes = owl['@graph'] as ReadonlyArray<Record<string, unknown>>;
const ebookNode = graphNodes.find((node) => {
  return node['@id'] === EBookSchema.$id;
});

console.assert(ebookNode !== undefined);
// The rdfs:subClassOf edge is the OWL projection of Compose.subClassOf.
const RDFS_SUB_CLASS_OF = 'http://www.w3.org/2000/01/rdf-schema#subClassOf';
const subClassOf = ebookNode?.[RDFS_SUB_CLASS_OF] as ReadonlyArray<{ readonly '@id': string }>;

console.assert(Array.isArray(subClassOf));
console.assert(subClassOf.some((ref) => {
  return ref['@id'] === 'urn:bookstore:Book';
}));
// urn:bookstore:EBook
console.log('EBook $id:', EBookSchema.$id);
// true
console.log('subClassOf Book:', subClassOf.some((ref) => {
  return ref['@id'] === 'urn:bookstore:Book';
}));
Output
Press Execute to run this example against the real library.

→ See: Compose.subClassOf reference · Compose.extend (property-merge alternative) · Graph concepts (TBox / ABox)

entities/PrintBook.ts: subClassOf + disjointWith

In plain English: a PrintBook is a physical-format Book with binding, page count, and weight. The disjointWith declaration asserts that no single value can be both a PrintBook and an EBook at the same time - they are mutually exclusive formats. JsonTology.validate(PrintBookSchema, value) enforces the constraint at runtime: after a value passes PrintBook's structural check, the registry runs EBookSchema against it; if both succeed it surfaces a disjointWith violation. Reasoners and OWL-aware query engines see the same assertion in the TBox.

Use this when:

  • Two classes are siblings under a parent and a single individual cannot belong to both at once (hardcover vs ebook, fiction vs non-fiction in your domain rules, etc.).

Don't use this when:

  • The two classes overlap intentionally - use plain Compose.subClassOf for both without disjointWith.
  • You want one class to be the negation of another - use Compose.complementOf instead.
/**
 * Bookstore taxonomy — PrintBookSchema with subClassOf(Book) + disjointWith(EBook)
 *
 * The canonical `PrintBookSchema` is defined in
 * `examples/docs/bookstore/entities/PrintBook.ts`. The disjointWith
 * declaration asserts that no single value can be both a `PrintBook`
 * and an `EBook` at the same time — physical and digital formats are
 * mutually exclusive.
 *
 * `validate` enforces the constraint at runtime: after a value passes
 * `PrintBook`'s structural check, the registry runs `EBookSchema`
 * against it; if both succeed it surfaces a disjointWith violation.
 */

import {
  aboxFixtures, bookstoreEntities, PrintBookSchema
} from '../bookstore/index.js';

// rareBook is a print book — passes PrintBookSchema cleanly.
const errs = bookstoreEntities.validate(PrintBookSchema.$id, aboxFixtures.rareBook);

console.assert(errs.length === 0);
// 0 — rare book passes PrintBook constraints
console.log('validation errors:', errs.length);

// The TBox emits `urn:bookstore:PrintBook owl:disjointWith
// urn:bookstore:EBook`. Verify via the OWL JSON-LD projection.
const owl = bookstoreEntities.ontology().jsonLdObject();
const graphNodes = owl['@graph'] as ReadonlyArray<Record<string, unknown>>;
const printNode = graphNodes.find((node) => {
  return node['@id'] === PrintBookSchema.$id;
});

const OWL_DISJOINT_WITH = 'http://www.w3.org/2002/07/owl#disjointWith';
const disjointWith = printNode?.[OWL_DISJOINT_WITH] as undefined | { readonly '@id': string };

if (disjointWith === undefined) {
  throw new TypeError('PrintBook node is missing owl:disjointWith');
}

console.assert(disjointWith['@id'] === 'urn:bookstore:EBook');
// 'urn:bookstore:EBook' — physical XOR digital
console.log('disjointWith:', disjointWith['@id']);
Output
Press Execute to run this example against the real library.

→ See: Compose.disjointWith reference · Graph concepts (TBox / ABox)

entities/RareBook.ts: someValuesFrom + maxCardinality

In plain English: a RareBook is a PrintBook with two extra rules about its authors array. someValuesFrom(authors, AuthorName) asserts that at least one value in the array is an AuthorName (rather than some other type). maxCardinality(authors, 1) says there is at most one author. Together: a rare book has exactly the right kind of author and never more than one. These are TBox (schema-level) rules; the reasoner uses them to derive facts and find contradictions, while JSON Schema validation handles structural checks.

Use this when:

  • You want to express "at least one value of property P is of class C" - that's Compose.someValuesFrom.
  • You want to cap how many values a property can have - that's Compose.maxCardinality.

Don't use this when:

  • You only want to enforce array length at validation time - JSON Schema's native minItems / maxItems already cover that. Restrictions are for TBox semantic content that reasoners read.
  • You want to require every value to satisfy a class - use Compose.allValuesFrom (see AnthologyBook below).
/**
 * Bookstore taxonomy — RareBookSchema with someValuesFrom + maxCardinality
 *
 * The canonical `RareBookSchema` is defined in
 * `examples/docs/bookstore/entities/RareBook.ts`. Two OWL restrictions
 * are layered onto the parent `PrintBook` axis:
 *
 *   - `someValuesFrom(authors, AuthorName)` — at least one element of
 *     `authors` is an AuthorName instance.
 *   - `maxCardinality(authors, 1)` — at most one author.
 *
 * The 1979 Thienemann first edition of Die unendliche Geschichte (sole
 * author: Michael Ende) satisfies both restrictions and validates.
 */

import {
  aboxFixtures, bookstoreEntities, RareBookSchema
} from '../bookstore/index.js';

const errs = bookstoreEntities.validate(RareBookSchema.$id, aboxFixtures.rareBook);

console.assert(errs.length === 0);
// 0 — sole-author first edition passes restrictions
console.log('validation errors:', errs.length);

// The TBox carries two anonymous owl:Restriction blank nodes referenced
// via rdfs:subClassOf, one per restriction.
const owl = bookstoreEntities.ontology().jsonLdObject();
const graphNodes = owl['@graph'] as ReadonlyArray<Record<string, unknown>>;
const rareNode = graphNodes.find((node) => {
  return node['@id'] === RareBookSchema.$id;
});

console.assert(rareNode !== undefined);
// urn:bookstore:RareBook
console.log('RareBook $id:', RareBookSchema.$id);
// true — restrictions emitted
console.log('TBox node found:', rareNode !== undefined);
Output
Press Execute to run this example against the real library.

→ See: OWL class restrictions · Compose.subClassOf reference · Graph concepts (TBox / ABox)

entities/SignedFirstEdition.ts + invariant: subclass with a cross-field axiom

In plain English: a SignedFirstEdition is a RareBook (it inherits every RareBook restriction) plus two new fields, signedBy and provenance. The "exactly one author" rule isn't a structural shape (it's a relation between two properties), so it's registered as an invariant on the schema, not encoded as a separate OWL class. Invariants surface in ValidationErrors with keyword: 'jt:invariant', in the same collection as structural errors.

Use this when:

  • Your subclass adds fields and a cross-field rule. The subClassOf declaration carries the OWL TBox; the invariant carries the rule that has no structural form.
  • Pairing one schema's structure with a cardinality rule that doesn't earn its own OWL class identity (single-authorship is a fact about the authors array, not a separate Kind).

Don't use this when:

  • The constraint is structural: use minItems/maxItems on the array directly.
  • The constraint is fixing a property to a literal value: use Compose.hasValue (an OWL class axiom).
/**
 * Bookstore taxonomy — SignedFirstEdition: subclass with a cross-field invariant
 *
 * The canonical `SignedFirstEditionSchema` declares
 * `Compose.subClassOf(RareBook)` and registers a
 * `signedFirstEditionIsSoloAuthored` invariant on the same `$id`. The
 * subclass relation lives in the OWL TBox; the cardinality rule fires
 * on every `validate` / `instantiate` and surfaces in
 * `ValidationErrors` with `keyword: 'jt:invariant'`.
 */

import {
  bookstoreEntities, SignedFirstEditionSchema
} from '../bookstore/index.js';

// Valid single-author signed first edition.
const signed = {
  'authors': ['Michael Ende'],
  'binding': 'hardcover' as const,
  'estimatedAgeYears': 47,
  'firstEditionYear': 1979,
  'inStock': true,
  'isbn': '9783522128001',
  'pageCount': 428,
  'price': {
    'amount': 25_000,
    'currency': 'EUR'
  },
  'printStatus': 'outOfPrint' as const,
  'provenance': 'Signed at Thienemann Verlag launch, Stuttgart, 1979.',
  'publishedOn': '1979-09-01',
  'signedBy': 'Michael Ende',
  'stockLevel': 5,
  'title': 'Die unendliche Geschichte',
  'weightGrams': 980
};

const okErrs = bookstoreEntities.validate(SignedFirstEditionSchema.$id, signed);

console.assert(okErrs.length === 0);
// 0 — solo-authored passes invariant
console.log('valid signed edition errors:', okErrs.length);

// Multi-author candidate breaks the invariant.
const badErrs = [...bookstoreEntities.validate(SignedFirstEditionSchema.$id, {
  ...signed,
  'authors': [
    'Michael Ende',
    'Co-Author'
  ]
})];

const invariantErr = badErrs.find((err) => {
  return err.keyword === 'jt:invariant';
});

console.assert(invariantErr !== undefined);
console.assert(invariantErr?.path === '/authors');
// 'jt:invariant'
console.log('invariant error keyword:', invariantErr?.keyword);
// '/authors' — cross-field rule fired
console.log('invariant error path:', invariantErr?.path);
Output
Press Execute to run this example against the real library.

The pair encodes the full domain rule: the OWL TBox sees a clean rdfs:subClassOf RareBook triple, and validate() rejects any candidate SignedFirstEdition that fails the cross-field check.

→ See: addInvariant reference · Compose.subClassOf reference · OWL class restrictions

entities/InPrintBook.ts: hasValue on printStatus

In plain English: an InPrintBook is a Book whose printStatus property is fixed to the literal 'inPrint'. Compose.hasValue(prop, literal) pins a property to a specific scalar (string, number, or boolean). It's the TBox way of saying "every member of this class has property X equal to value Y".

printStatus is the publisher-state primitive ('inPrint' | 'outOfPrint' | 'limitedRun'), editorial state that changes rarely. Inventory state (inStock) is orthogonal and daily-mutable, so the InPrint/OutOfPrint axis discriminates on printStatus, not inStock.

Use this when:

  • You want a class defined by a fixed scalar value on a property (status flag, fixed currency code, role enum value).

Don't use this when:

  • The fixed value is a class instance - use Compose.someValuesFrom or Compose.allValuesFrom (those work with class IRIs, not literals).
  • The constraint should only apply at runtime - JSON Schema's native const keyword is simpler.
/**
 * Bookstore taxonomy — InPrintBookSchema: hasValue on printStatus
 *
 * The canonical `InPrintBookSchema` pins `Book.printStatus` to the
 * literal `'inPrint'` via `Compose.hasValue`. The TBox emits an
 * anonymous `owl:Restriction` referenced from the class via
 * `rdfs:subClassOf`; this is a TBox semantic for reasoners, not a
 * structural validation rule. Editorial state (`printStatus`) is
 * orthogonal to inventory state (`inStock`).
 */

import {
  bookstoreEntities, InPrintBookSchema
} from '../bookstore/index.js';

const inPrint = {
  'authors': ['Cornelia Funke'],
  'inStock': true,
  'isbn': '9783791504650',
  'price': {
    'amount': 18.99,
    'currency': 'EUR'
  },
  'printStatus': 'inPrint',
  'publishedOn': '2002-09-01',
  'stockLevel': 25,
  'title': 'Tintenherz'
};

const okErrs = bookstoreEntities.validate(InPrintBookSchema.$id, inPrint);

console.assert(okErrs.length === 0);
// 0 — inPrint book passes hasValue constraint
console.log('validation errors:', okErrs.length);

// The TBox carries an owl:Restriction with owl:hasValue 'inPrint' on
// https://bookstore.example/printStatus.
const owl = bookstoreEntities.ontology().jsonLdObject();
const graphNodes = owl['@graph'] as ReadonlyArray<Record<string, unknown>>;
const inPrintNode = graphNodes.find((node) => {
  return node['@id'] === InPrintBookSchema.$id;
});

console.assert(inPrintNode !== undefined);

const subClassOf = inPrintNode?.['http://www.w3.org/2000/01/rdf-schema#subClassOf'];
const subClassEntries = Array.isArray(subClassOf) ? subClassOf as ReadonlyArray<Record<string, unknown>> : [];
const hasValueRestriction = subClassEntries.find((entry) => {
  return entry['http://www.w3.org/2002/07/owl#hasValue'] === 'inPrint';
});

console.assert(hasValueRestriction !== undefined);
// urn:bookstore:InPrintBook
console.log('InPrintBook $id:', InPrintBookSchema.$id);
// true — OWL axiom emitted
console.log('hasValue restriction found:', hasValueRestriction !== undefined);
Output
Press Execute to run this example against the real library.

→ See: OWL class restrictions · Compose.subClassOf reference

entities/OutOfPrintBook.ts: complementOf bounded to Book

In plain English: an OutOfPrintBook is the negation of an InPrintBook, but only within the world of Books. The Compose.complementOf declaration says "this class is everything that is not the other class". The body's allOf: [{ $ref: Book }] adds the structural rule "and it must also be a Book" so the complement is bounded - without it, OWL's open-world rules would match anything that isn't an InPrintBook, including a customer or an order.

Use this when:

  • Two classes naturally tile a parent's universe and you want one defined as "the rest" (banned vs. approved books, returnable vs. non-returnable items, etc.). Combine complementOf with subClassOf/allOf to bound the universe explicitly.

Don't use this when:

  • You want the unbounded OWL complement (every non-X in the universe). Pass the body without allOf and document the open-world semantic clearly.
  • You want a runtime "not these specific values" check - JSON Schema's not at the top level (without OWL annotations) is simpler.
/**
 * Bookstore taxonomy — OutOfPrintBookSchema as Compose.complementOf
 *
 * The canonical `OutOfPrintBookSchema` is the OWL complement of
 * `InPrintBookSchema`, bounded to the Book universe via
 * `allOf: [{ $ref: BookSchema }]`. Without that bound, OWL's
 * open-world semantics would match anything that is not an
 * `InPrintBook` — including customers and orders.
 */

import {
  bookstoreEntities, OutOfPrintBookSchema
} from '../bookstore/index.js';

// The TBox carries `owl:complementOf` pointing at InPrintBook.
// Verify the projection emits the expected complement edge.
const owl = bookstoreEntities.ontology().jsonLdObject();
const graphNodes = owl['@graph'] as ReadonlyArray<Record<string, unknown>>;
const outNode = graphNodes.find((node) => {
  return node['@id'] === OutOfPrintBookSchema.$id;
});

console.assert(outNode !== undefined);

const OWL_COMPLEMENT_OF = 'http://www.w3.org/2002/07/owl#complementOf';
const complementOf = outNode?.[OWL_COMPLEMENT_OF] as undefined | { readonly '@id': string };

if (complementOf === undefined) {
  throw new TypeError('OutOfPrintBook node is missing owl:complementOf');
}

console.assert(complementOf['@id'] === 'urn:bookstore:InPrintBook');
// urn:bookstore:OutOfPrintBook
console.log('OutOfPrintBook $id:', OutOfPrintBookSchema.$id);
// 'urn:bookstore:InPrintBook'
console.log('complementOf:', complementOf['@id']);
Output
Press Execute to run this example against the real library.

The body's allOf: [{ $ref: Book }] is what bounds the OWL complement to the Book universe. Without it, OWL's open-world complementOf would match anything that is not an InPrintBook: including non-books - which is the right OWL semantic but rarely what authors want.

→ See: Compose.complementOf reference · Graph concepts (TBox / ABox)

index.ts: ABox identity, a customer who ordered a rare book

All the schemas above are TBox declarations: they describe kinds of thing. sameAs is different: it operates on individuals (concrete records, the ABox), and asserts "these two IRIs name the same person/object/thing". The bookstore demonstrates two such assertions tied to one coherent narrative.

The scenario. Customer Bastian Balthazar Bux placed an order on 2026-04-12 containing a single line item: a rare first edition of Michael Ende's Die unendliche Geschichte (Thienemann Verlag, Stuttgart, 1979, ISBN-13 9783522128001). Two identity assertions register against this scenario:

  1. Customer-CRM migration. When the bookstore migrated systems in 2024, the legacy CRM record (urn:coreander-antiquariat:cust-00042) carried over alongside the new bookstore IRI. Both still resolve to the same person, so sameAs lets a reasoner merge purchase history from both sources.
  2. Cross-catalog rare-book identity. The 1965 Chilton first edition is also catalogued by WorldCat under OCLC 463127. Declaring sameAs unifies bibliographic facts (publisher, page count, condition notes) regardless of which authority emitted them.

Use this when:

  • Two IRIs in your data refer to the same real-world entity (records merged after a migration, alias systems, cross-org identifiers).

Don't use this when:

  • You want class-level identity (two classes that have the same instances): use Compose.equivalent instead. sameAs is for individuals, not classes.
  • You want to express "these two records should be merged" as a workflow step. sameAs is an OWL assertion that they already refer to one entity; downstream reasoners will treat their property values as belonging to a single individual.
/**
 * Bookstore taxonomy — sameAs ABox identity (two pairs)
 *
 * `JsonTology.prototype.sameAs` declares that two IRIs name the same
 * individual. The canonical bookstore registry declares two such
 * pairs in `examples/docs/bookstore/index.ts`:
 *
 *   urn:bookstore:customer:bastian-bux ↔ urn:coreander-antiquariat:cust-00042
 *   urn:bookstore:rarebook:neverending-1979-thienemann ↔ http://www.worldcat.org/oclc/5705614
 *
 * `toQuads` emits both directions of each pair (four sameAs quads
 * total) into the ABox alongside structural triples.
 */

import {
  aboxFixtures, bookstoreEntities, OrderSchema
} from '../bookstore/index.js';

// Emit the full graph: schema-level rules + sameAs assertions + ABox quads.
// OrderSchema is used here because it references the full entity graph and
// the sameAs pairs are registry-level — they appear regardless of which ABox
// schema is projected. instantiate first to obtain the branded order value
// toQuads's typed signature expects.
const order = bookstoreEntities.instantiate(OrderSchema.$id, aboxFixtures.order);
const quads = bookstoreEntities.toQuads(OrderSchema, order);

console.assert(Array.isArray(quads));
console.assert(quads.length > 0);

const OWL_SAME_AS = 'http://www.w3.org/2002/07/owl#sameAs';
const sameAsQuads = quads.filter((quad) => {
  return quad.predicate.value === OWL_SAME_AS;
});

// Four quads = two pairs × two directions (sameAs is symmetric).
console.assert(sameAsQuads.length >= 2);
// ABox + TBox quads
console.log('total quads:', quads.length);
// >= 2 (both directions of at least one pair)
console.log('sameAs quads:', sameAsQuads.length);
// one of the paired IRIs
console.log('sample sameAs subject:', sameAsQuads[0]?.subject.value);
Output
Press Execute to run this example against the real library.

The order Bastian placed, the customer record, the rare-book metadata, and their later review are all defined as runtime values on the aboxFixtures export. instantiate() and toQuads() accept those fixtures directly so the same scenario can be used end-to-end across docs pages and integration tests.

/**
 * Bookstore taxonomy — using aboxFixtures across schemas
 *
 * The `aboxFixtures` export carries concrete instance data for the
 * Bastian-orders-Neverending-Story scenario. Each fixture matches a
 * registered schema verbatim; `instantiate` and `toQuads` accept the
 * fixtures directly so the same scenario can be reused across docs
 * pages and integration tests.
 */

import {
  aboxFixtures, bookstoreEntities, OrderSchema, RareBookSchema
} from '../bookstore/index.js';

// Validate the rare-book record itself (passes RareBook's hierarchy:
// Book + PrintBook structural rules + someValuesFrom + maxCardinality(authors=1)).
const rareBook = bookstoreEntities.instantiate(RareBookSchema, aboxFixtures.rareBook);

console.assert(typeof rareBook === 'object');
// 'Die unendliche Geschichte'
console.log('rareBook title:', (rareBook as { 'title': string }).title);

// Validate Bastian's order containing one line for that rare book.
// The branded instantiate result feeds toQuads's typed signature directly.
const order = bookstoreEntities.instantiate(OrderSchema, aboxFixtures.order);

console.assert(typeof order === 'object');
// fixture customerId
console.log('order customerId:', (order as { 'customerId': string }).customerId);

// Emit the full RDF graph: schema-level rules + sameAs assertions + ABox quads.
const quads = bookstoreEntities.toQuads(OrderSchema, order);

console.assert(quads.length > 0);
// all quads including sameAs pairs
console.log('ABox quad count:', quads.length);
Output
Press Execute to run this example against the real library.

→ See: sameAs (ABox identity) reference · Compose.equivalent (the class-level counterpart) · Graph concepts (TBox / ABox)

Edge styles in the live graph

Edge kindVisualSource
subClassOfsolid gray with arrowCompose.subClassOf (single or multi-parent)
equivalentClassgreen dashedCompose.equivalent
disjointWithred dashedCompose.disjointWith
complementOfpurple with tee terminatorCompose.complementOf
restrictionteal dotted, label = prop ∃ / / card =N / card ≥N / card ≤N / prop = literaluser-authored jt:restrictions (any of the six factory methods)
sameAsgold dashed (symmetric)JsonTology.prototype.sameAs
rangeblue with vee arrowproperty $ref / items.$ref

See:

See also

Released under the MIT License.