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
- subClassOf —
rdfs:subClassOf(taxonomic narrowing) - equivalentClass —
owl:equivalentClass(alias / structural identity) - range —
rdfs:range(property → typed target) - domain —
rdfs:domain(explicit override) - disjointWith —
owl:disjointWith(no shared instances) - complementOf —
owl:complementOf(negation) - restriction —
owl:Restriction(cardinality / hasValue / …)
| Schema or axiom | Surface used | Edge / effect in graph |
|---|---|---|
EBookSchema | Compose.subClassOf(Book) | subClassOf → Book |
PrintBookSchema | Compose.subClassOf(Book) + Compose.disjointWith(EBook) | subClassOf → Book, disjointWith ↔ EBook |
RareBookSchema | Compose.subClassOf(PrintBook) + Compose.someValuesFrom + Compose.maxCardinality | subClassOf → PrintBook, two restriction edges |
SignedFirstEditionSchema | Compose.subClassOf(RareBook) + registered signedFirstEditionIsSoloAuthored invariant | subClassOf → RareBook (the cardinality axiom lives off-graph as an invariant) |
InPrintBookSchema | Compose.subClassOf(Book) + Compose.hasValue(printStatus, 'inPrint') | subClassOf → Book, restriction on printStatus |
OutOfPrintBookSchema | Compose.complementOf(InPrintBook) with body allOf bounding to Book | subClassOf → Book, complementOf → InPrintBook |
orderTotalMatchesItems | bookstoreEntities.addInvariant(OrderSchema, ...) | runtime cross-field rule on Order.orderTotal |
signedFirstEditionIsSoloAuthored | bookstoreEntities.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:
- You want to remove fields - use
Compose.omit. - You want every field to become optional - use
Compose.partial. - You only want a structural alias (no parent-child semantic) - use
Compose.equivalent.
/**
* 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';
}));
→ 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.subClassOffor both withoutdisjointWith. - You want one class to be the negation of another - use
Compose.complementOfinstead.
/**
* 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']);
→ 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/maxItemsalready cover that. Restrictions are for TBox semantic content that reasoners read. - You want to require every value to satisfy a class - use
Compose.allValuesFrom(seeAnthologyBookbelow).
/**
* 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);
→ 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
subClassOfdeclaration 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
authorsarray, not a separateKind).
Don't use this when:
- The constraint is structural: use
minItems/maxItemson 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);
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.someValuesFromorCompose.allValuesFrom(those work with class IRIs, not literals). - The constraint should only apply at runtime - JSON Schema's native
constkeyword 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);
→ 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
complementOfwithsubClassOf/allOfto 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
allOfand document the open-world semantic clearly. - You want a runtime "not these specific values" check - JSON Schema's
notat 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']);
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:
- 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, sosameAslets a reasoner merge purchase history from both sources. - Cross-catalog rare-book identity. The 1965 Chilton first edition is also catalogued by WorldCat under OCLC
463127. DeclaringsameAsunifies 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.equivalentinstead.sameAsis for individuals, not classes. - You want to express "these two records should be merged" as a workflow step.
sameAsis 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);
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);
→ See: sameAs (ABox identity) reference · Compose.equivalent (the class-level counterpart) · Graph concepts (TBox / ABox)
Edge styles in the live graph
Edge kind | Visual | Source |
|---|---|---|
subClassOf | solid gray with arrow | Compose.subClassOf (single or multi-parent) |
equivalentClass | green dashed | Compose.equivalent |
disjointWith | red dashed | Compose.disjointWith |
complementOf | purple with tee terminator | Compose.complementOf |
restriction | teal dotted, label = prop ∃ / ∀ / card =N / card ≥N / card ≤N / prop = literal | user-authored jt:restrictions (any of the six factory methods) |
sameAs | gold dashed (symmetric) | JsonTology.prototype.sameAs |
range | blue with vee arrow | property $ref / items.$ref |
See:
See also
- Bookstore domain - the base schema definitions this taxonomy extends
- Graph concepts - TBox/ABox, OWL semantics, and graph-native authoring