Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .changeset/typed-subsumption.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ Now that the structural contract holds, `from()`/`to()`/`fromDynamic()`/`toDynam

An alias states its expansion as ONE option, `expansion: "exact" | "subclasses" | "narrower"` (default `"subclasses"`), on `from()`/`to()`/`fromDynamic()`/`toDynamic()`. `"narrower"` expands a kind through its `broader`/`narrower` kind-taxonomy relations instead of `subClassOf` — the alias type is untyped, since no schema relationship is claimed. Because it is one value, an alias cannot state two axes at once; a value outside the three is refused (`QUERY_ALIAS_EXPANSION_INVALID`), as is a narrower expansion naming an unregistered kind or, on `to()`/`toDynamic()`, a kind the traversed edge does not admit as an endpoint (`ONTOLOGY_NARROWER_KIND_NOT_REGISTERED` / `ONTOLOGY_NARROWER_ENDPOINT_NOT_ADMITTED`). Omitting the options argument, passing `{}`, and passing an explicit `undefined` all mean "take the store default", so an options bag can be forwarded through a caller's own helper without special-casing the empty case. A bag that can carry an axis the call site does not pin — `{ expansion?: "exact" }` from a wrapper that only ever narrows, or the option type itself — forwards as well, and types its alias conservatively (untyped, as `"narrower"` does), since the axis is unknown at compile time. `queryDefaults.expansion` and `createQueryBuilder`'s `defaultExpansion` accept a stated `undefined` for the same forwarding reason. `store.search()` spells the same axis under the same name (`expansion: "exact" | "subclasses"`, default `"exact"` — the search facade is not polymorphic by default).

Every meta-edge factory — not only `subClassOf`/`equivalentTo` — now returns a relation typed with its meta-edge name literal and its endpoint kinds, the IRI overload (`equivalentTo(Kind, iri)`, typed `from: Kind, to: string`) included. A kind whose only equivalence is IRI-routed therefore widens its alias like any other equivalenced kind, and a graph whose ontology mixes `subClassOf` with `broader` / `inverseOf` / `disjointWith` keeps exact alias types for the kinds no subsumption relation names. The flip side: a relation a caller annotates as the bare `OntologyRelation` has lost the literals the alias computation reads, so it now widens EVERY alias in that graph conservatively rather than reporting an affected kind as unaffected.
Every meta-edge factory — not only `subClassOf`/`equivalentTo` — now returns a relation typed with its meta-edge name literal and its endpoint kinds, the IRI overload (`equivalentTo(Kind, iri)`, typed `from: Kind, to: string`) included. A kind whose only equivalence is IRI-routed therefore widens its alias like any other equivalenced kind, and a graph whose ontology mixes `subClassOf` with `broader` / `inverseOf` / `disjointWith` keeps exact alias types for the kinds no subsumption relation names. The flip side: a relation a caller annotates as the bare `OntologyRelation` has lost the literals the alias computation reads, so it now widens EVERY alias in that graph conservatively rather than reporting an affected kind as unaffected. An endpoint declared as a union of node types keeps its literals and stays precise — it widens exactly the kinds the union can hold, and nothing else.

## Breaking changes

- **Annotating a relation as `OntologyRelation` widens every alias in that graph.** Such code still compiles unchanged, but its aliases' `kind` and `NodeId` types widen to the polymorphic form — let the factory's return type be inferred to keep the narrow types.
- **`subClassOf`/`equivalentTo` are now generic and carry a structural contract.** Code that annotates a relation's result as `OntologyRelation` still compiles unchanged; code declaring a pair whose child does not structurally extend the parent no longer compiles, and the identical pair is refused at registry build even for callers that bypass the compile-time check (an extension's raw `{ metaEdge: "subClassOf", from, to }` relation, or a persisted document).
- **Supertype queries now return subtype rows.** `from()`/`to()`/`fromDynamic()`/`toDynamic()` default to `expansion: "subclasses"`. `search()` and the collection APIs are unchanged and stay exact-kind.
- **The `includeSubClasses` / `includeNarrower` booleans are replaced by one `expansion` option** on `from()`/`to()`/`fromDynamic()`/`toDynamic()`: `{ includeSubClasses: false }` becomes `{ expansion: "exact" }`, `{ includeSubClasses: true }` becomes `{ expansion: "subclasses" }`, and `{ includeNarrower: true }` becomes `{ expansion: "narrower" }`. `createStore(..., { queryDefaults: { includeSubClasses } })` becomes `queryDefaults: { expansion }`, `createQueryBuilder`'s `defaultIncludeSubClasses` becomes `defaultExpansion`, and `store.search()`'s `includeSubClasses: true` becomes `expansion: "subclasses"`. `QUERY_ALIAS_EXPANSION_CONFLICT` is removed with the contradiction it described; `QUERY_ALIAS_EXPANSION_INVALID` replaces it for an out-of-domain value.
- **Alias types widen under the polymorphic axis.** For a kind that some other kind declares itself a subclass of, or that participates in a registered-kind `equivalentTo`, the alias's `kind` is `string` and its `NodeId` brand widens, because the row may be a subtype. A graph that declares no subsumption relations is unaffected.
- **Alias types widen under the polymorphic axis.** For a kind that some other kind declares itself a subclass of, or that participates in a registered-kind `equivalentTo`, the alias's `kind` is `string` and its `NodeId` brand widens, because the row may be a subtype. A relation endpoint declared as a UNION of kinds — a caller helper that declares a subclass of either media root, whether spelled `typeof Audio | typeof Video` or `NodeType<"Audio" | "Video">` — widens every kind the union can hold, since the declared type does not say which one the call passed. A graph that declares no subsumption relations is unaffected.
- **`QueryAst`'s `start.includeSubClasses: boolean` becomes `start.expansion: "exact" | "subclasses" | "narrower"`**, so a `narrower`-expanded alias is describable. Only affects code inspecting a compiled AST directly.
- **`equivalentTo(nodeKind, edgeKind)` no longer compiles.** The new overload set accepts `(NodeType, NodeType)`, `(NodeType | AnyEdgeType, string)` (the IRI form), and `(AnyEdgeType, NodeType)`, but not a node kind followed by an edge kind — this pairing has always been refused at registry build (`ONTOLOGY_EQUIVALENCE_INVALID_CLASS`) and is now also refused at compile time.

Expand Down
10 changes: 10 additions & 0 deletions apps/docs/src/content/docs/queries/source.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,16 @@ then widens every kind in the graph conservatively rather than risk typing a
polymorphic alias as exact. Let the relation types be inferred, or annotate
with `typeof` the factory call, if you want the narrow types back.

**An endpoint declared as a union of kinds widens every kind the union can
hold.** A helper that declares a subclass of either media root —
`(root: typeof Audio | typeof Video) => subClassOf(Episode, root)`, or the
same helper annotated `NodeType<"Audio" | "Video">` — keeps its meta-edge name
and its endpoint kind literals, so nothing is erased, but its declared type no
longer says which root the call passed. Both `Audio` and `Video` therefore get
the polymorphic alias type, while a kind the union cannot hold stays exact.
Pass the root directly (`subClassOf(Episode, Audio)`) if you want only the
root you named to widen.

**Limitation — `evolve()`-declared subsumption isn't visible to the alias
type.** The `kind`/`NodeId` widening above is computed from your
compile-time graph definition. A `subClassOf` an [extension](/graph-extensions)
Expand Down
34 changes: 12 additions & 22 deletions packages/typegraph/etc/typegraph-graph-merge.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7239,30 +7239,20 @@ type SubgraphResult<G extends GraphDef, NK extends NodeKinds<G> = NodeKinds<G>,
type SubgraphResultEdgeKinds<G extends GraphDef, EK extends EdgeKinds<G>, C extends boolean | undefined> = true extends C ? EdgeKinds<G> : EK;

// @public
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : [
Extract<G["ontology"][number], {
metaEdge: {
name: "subClassOf";
};
to: {
kind: K;
};
} | {
metaEdge: {
name: "equivalentTo" | "sameAs";
};
from: {
kind: K;
};
} | {
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : true extends SubsumptionElementNamesKind<G["ontology"][number], K> ? true : false;

// @public
type SubsumptionElementNamesKind<Relation, K extends string> = Relation extends unknown ? Relation extends ({
metaEdge: {
name: "equivalentTo" | "sameAs";
};
to: {
kind: K;
name: infer Name extends string;
};
}>
] extends [never] ? false : true;
from: infer From;
to: infer To;
}) ? [
Extract<Extract<MatchedEndpoints<Name, From, To>, {
kind: string;
}>["kind"], K>
] extends [never] ? false : true : false : never;

// @public
type SubsumptionLiteralsErased<Relation> = Relation extends unknown ? Relation extends ({
Expand Down
34 changes: 12 additions & 22 deletions packages/typegraph/etc/typegraph-interchange.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6329,30 +6329,20 @@ type SubgraphResult<G extends GraphDef, NK extends NodeKinds<G> = NodeKinds<G>,
type SubgraphResultEdgeKinds<G extends GraphDef, EK extends EdgeKinds<G>, C extends boolean | undefined> = true extends C ? EdgeKinds<G> : EK;

// @public
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : [
Extract<G["ontology"][number], {
metaEdge: {
name: "subClassOf";
};
to: {
kind: K;
};
} | {
metaEdge: {
name: "equivalentTo" | "sameAs";
};
from: {
kind: K;
};
} | {
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : true extends SubsumptionElementNamesKind<G["ontology"][number], K> ? true : false;

// @public
type SubsumptionElementNamesKind<Relation, K extends string> = Relation extends unknown ? Relation extends ({
metaEdge: {
name: "equivalentTo" | "sameAs";
};
to: {
kind: K;
name: infer Name extends string;
};
}>
] extends [never] ? false : true;
from: infer From;
to: infer To;
}) ? [
Extract<Extract<MatchedEndpoints<Name, From, To>, {
kind: string;
}>["kind"], K>
] extends [never] ? false : true : false : never;

// @public
type SubsumptionLiteralsErased<Relation> = Relation extends unknown ? Relation extends ({
Expand Down
34 changes: 12 additions & 22 deletions packages/typegraph/etc/typegraph-postgres-pglite.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5762,30 +5762,20 @@ type SubgraphResult<G extends GraphDef, NK extends NodeKinds<G> = NodeKinds<G>,
type SubgraphResultEdgeKinds<G extends GraphDef, EK extends EdgeKinds<G>, C extends boolean | undefined> = true extends C ? EdgeKinds<G> : EK;

// @public
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : [
Extract<G["ontology"][number], {
metaEdge: {
name: "subClassOf";
};
to: {
kind: K;
};
} | {
metaEdge: {
name: "equivalentTo" | "sameAs";
};
from: {
kind: K;
};
} | {
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : true extends SubsumptionElementNamesKind<G["ontology"][number], K> ? true : false;

// @public
type SubsumptionElementNamesKind<Relation, K extends string> = Relation extends unknown ? Relation extends ({
metaEdge: {
name: "equivalentTo" | "sameAs";
};
to: {
kind: K;
name: infer Name extends string;
};
}>
] extends [never] ? false : true;
from: infer From;
to: infer To;
}) ? [
Extract<Extract<MatchedEndpoints<Name, From, To>, {
kind: string;
}>["kind"], K>
] extends [never] ? false : true : false : never;

// @public
type SubsumptionLiteralsErased<Relation> = Relation extends unknown ? Relation extends ({
Expand Down
34 changes: 12 additions & 22 deletions packages/typegraph/etc/typegraph-profiler.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5782,30 +5782,20 @@ type SubgraphResult<G extends GraphDef, NK extends NodeKinds<G> = NodeKinds<G>,
type SubgraphResultEdgeKinds<G extends GraphDef, EK extends EdgeKinds<G>, C extends boolean | undefined> = true extends C ? EdgeKinds<G> : EK;

// @public
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : [
Extract<G["ontology"][number], {
metaEdge: {
name: "subClassOf";
};
to: {
kind: K;
};
} | {
metaEdge: {
name: "equivalentTo" | "sameAs";
};
from: {
kind: K;
};
} | {
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : true extends SubsumptionElementNamesKind<G["ontology"][number], K> ? true : false;

// @public
type SubsumptionElementNamesKind<Relation, K extends string> = Relation extends unknown ? Relation extends ({
metaEdge: {
name: "equivalentTo" | "sameAs";
};
to: {
kind: K;
name: infer Name extends string;
};
}>
] extends [never] ? false : true;
from: infer From;
to: infer To;
}) ? [
Extract<Extract<MatchedEndpoints<Name, From, To>, {
kind: string;
}>["kind"], K>
] extends [never] ? false : true : false : never;

// @public
type SubsumptionLiteralsErased<Relation> = Relation extends unknown ? Relation extends ({
Expand Down
34 changes: 12 additions & 22 deletions packages/typegraph/etc/typegraph-provenance.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5776,30 +5776,20 @@ type SubgraphResult<G extends GraphDef, NK extends NodeKinds<G> = NodeKinds<G>,
type SubgraphResultEdgeKinds<G extends GraphDef, EK extends EdgeKinds<G>, C extends boolean | undefined> = true extends C ? EdgeKinds<G> : EK;

// @public
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : [
Extract<G["ontology"][number], {
metaEdge: {
name: "subClassOf";
};
to: {
kind: K;
};
} | {
metaEdge: {
name: "equivalentTo" | "sameAs";
};
from: {
kind: K;
};
} | {
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : true extends SubsumptionElementNamesKind<G["ontology"][number], K> ? true : false;

// @public
type SubsumptionElementNamesKind<Relation, K extends string> = Relation extends unknown ? Relation extends ({
metaEdge: {
name: "equivalentTo" | "sameAs";
};
to: {
kind: K;
name: infer Name extends string;
};
}>
] extends [never] ? false : true;
from: infer From;
to: infer To;
}) ? [
Extract<Extract<MatchedEndpoints<Name, From, To>, {
kind: string;
}>["kind"], K>
] extends [never] ? false : true : false : never;

// @public
type SubsumptionLiteralsErased<Relation> = Relation extends unknown ? Relation extends ({
Expand Down
34 changes: 12 additions & 22 deletions packages/typegraph/etc/typegraph-sqlite-local.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5789,30 +5789,20 @@ type SubgraphResult<G extends GraphDef, NK extends NodeKinds<G> = NodeKinds<G>,
type SubgraphResultEdgeKinds<G extends GraphDef, EK extends EdgeKinds<G>, C extends boolean | undefined> = true extends C ? EdgeKinds<G> : EK;

// @public
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : [
Extract<G["ontology"][number], {
metaEdge: {
name: "subClassOf";
};
to: {
kind: K;
};
} | {
metaEdge: {
name: "equivalentTo" | "sameAs";
};
from: {
kind: K;
};
} | {
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : true extends SubsumptionElementNamesKind<G["ontology"][number], K> ? true : false;

// @public
type SubsumptionElementNamesKind<Relation, K extends string> = Relation extends unknown ? Relation extends ({
metaEdge: {
name: "equivalentTo" | "sameAs";
};
to: {
kind: K;
name: infer Name extends string;
};
}>
] extends [never] ? false : true;
from: infer From;
to: infer To;
}) ? [
Extract<Extract<MatchedEndpoints<Name, From, To>, {
kind: string;
}>["kind"], K>
] extends [never] ? false : true : false : never;

// @public
type SubsumptionLiteralsErased<Relation> = Relation extends unknown ? Relation extends ({
Expand Down
34 changes: 12 additions & 22 deletions packages/typegraph/etc/typegraph.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7975,30 +7975,20 @@ export type SubsetNode<G extends GraphDef, K extends NodeKinds<G>> = {
}[K];

// @public
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : [
Extract<G["ontology"][number], {
metaEdge: {
name: "subClassOf";
};
to: {
kind: K;
};
} | {
metaEdge: {
name: "equivalentTo" | "sameAs";
};
from: {
kind: K;
};
} | {
type SubsumptionAffected<G extends GraphDef, K extends string> = OntologyTypeErased<G> extends true ? true : true extends SubsumptionLiteralsErased<G["ontology"][number]> ? true : true extends SubsumptionElementNamesKind<G["ontology"][number], K> ? true : false;

// @public
type SubsumptionElementNamesKind<Relation, K extends string> = Relation extends unknown ? Relation extends ({
metaEdge: {
name: "equivalentTo" | "sameAs";
};
to: {
kind: K;
name: infer Name extends string;
};
}>
] extends [never] ? false : true;
from: infer From;
to: infer To;
}) ? [
Extract<Extract<MatchedEndpoints<Name, From, To>, {
kind: string;
}>["kind"], K>
] extends [never] ? false : true : false : never;

// @public
type SubsumptionLiteralsErased<Relation> = Relation extends unknown ? Relation extends ({
Expand Down
Loading
Loading