Graph
draftStatus of this Document
This report was published by the User Journal Graph Community Group . It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups .
1. Overview
This module defines the vocabulary for intended user flow. It extends [UJG Core] to support structured, interactive graphs with composition via sub-journey references, exported exits from nested journeys, organization tags, and reusable outgoing navigation patterns.
2. Normative Artifacts
This module is published through the following artifacts:
graph.ttl: ontology, published athttps://ujg.specs.openuji.org/ed/ns/graphgraph.context.jsonld: JSON-LD term mappings, published athttps://ujg.specs.openuji.org/ed/ns/graph.context.jsonldgraph.shape.ttl: SHACL validation rules, published athttps://ujg.specs.openuji.org/ed/ns/graph.shape
Examples in this page use explicit Core and Graph context arrays for module clarity. The aggregate context https://ujg.specs.openuji.org/ed/ns/context.jsonld also preserves Graph's compact stateRef term for JourneyEntry.
3. Terminology
Journey: A named container for local traversable user flow topology.
JourneyEntry: An explicit entry contract for a Journey.
JourneyEntryIndex: A catalogue of addressable journey entry states that does not define traversal.
LocalVertex: An abstract local topology vertex of a Journey.
State: A discrete node in the experience (e.g., a screen, modal).
Transition: A structural directed edge between local vertices of a Journey.
CompositeState: A state that encapsulates another Journey (sub-journey).
JourneyExit: A terminal local graph vertex and exported completion contract declared by a Journey.
OutgoingTransition: A navigational affordance pointing to a next possible State or CompositeState.
OutgoingTransitionGroup: A reusable set of outgoing transitions that a Consumer can treat as present on multiple states (e.g., global nav).
4. LocalVertex
A LocalVertex is the abstract topology node type for the local topology of a Journey.
State and JourneyExit are sibling kinds of LocalVertex. A CompositeState is a specialized State. A JourneyExit is not a State.
A State MUST be a LocalVertex.
A CompositeState MUST be a State.
A JourneyExit MUST be a LocalVertex.
A JourneyExit MUST NOT be a State.
classDiagram class LocalVertex class State class CompositeState class JourneyExit LocalVertex <|-- State State <|-- CompositeState LocalVertex <|-- JourneyExit
5. State
A State is the base graph node for a discrete point in an intended user experience, such as a page, screen, modal, or other user-visible step.
Later sections define navigation affordances that can be attached to eligible states. This section defines only the base state node.
classDiagram
class State {
id
label
tags
} Example JSON node:
{
"@type": "State",
"@id": "urn:ujg:state:search-form",
"label": "Search form",
"tags": [
"phase:search"
]
} {
"@type": "State",
"@id": "urn:ujg:state:search-form",
"label": "Search form",
"tags": [
"phase:search"
]
} 6. Transition
A Transition is a structural directed edge between local vertices of a Journey. It models ordinary progression inside the local topology of a Journey.
A Transition is not owned by either endpoint state. It is owned by a journey through transitionRefs.
A Transition MUST be identified by an IRI.
A Transition MUST declare exactly one
fromvalue.A Transition MUST declare exactly one
tovalue.The
fromvalue of a Transition listed in a Journey'stransitionRefsMUST reference a State or CompositeState listed in that same journey'sstateRefs.The
tovalue of a Transition listed in a Journey'stransitionRefsMUST reference either a State or CompositeState listed in that same journey'sstateRefs, or a JourneyExit listed in that same journey'sexitRefs.Equivalently,
fromis a member ofstateRefs, andtois a member ofstateRefsunionexitRefs.A Transition MUST NOT use a JourneyExit as its
fromvalue.A Transition MUST NOT use
fromortoto reference local vertices belonging to another journey.A Transition MAY declare
toEntryRefwhen itstovalue references a CompositeState.A Transition MUST NOT declare more than one
toEntryRef.toEntryRefMUST NOT weaken, replace, or bypass local transition endpoint validation.A Transition MUST NOT declare more than one
label.
classDiagram
class State
class JourneyExit
class Transition {
id
label
from
to
}
Transition --> State : from
Transition --> State : to
Transition --> JourneyExit : to Example JSON node:
{
"@type": "Transition",
"@id": "urn:ujg:transition:search-form-to-results",
"label": "Submit search",
"from": "urn:ujg:state:search-form",
"to": "urn:ujg:state:results"
} {
"@type": "Transition",
"@id": "urn:ujg:transition:search-form-to-results",
"label": "Submit search",
"from": "urn:ujg:state:search-form",
"to": "urn:ujg:state:results"
} 7. Journey
A Journey is the local container for intended flow topology. It lists the experiential states that belong to the journey, the terminal exits exported by the journey, and, when present, the transitions that connect those local vertices.
Use Journey when the modeled object owns local traversal, progression, or structural order. If the model only needs to list known entry points into pages, surfaces, flows, or journeys, use JourneyEntryIndex instead.
A Journey MUST be identified by an IRI.
A Journey MUST declare at least one
entryRefsvalue.A Journey MUST declare exactly one
defaultEntryRef.The
defaultEntryRefvalue MUST be listed in the same Journey'sentryRefs.A Journey MUST declare at least one
stateRefsvalue.A Journey MAY declare
transitionRefs.A Journey MAY declare
exitRefs.Each
entryRefsvalue MUST reference a JourneyEntry.Each
stateRefsvalue MUST reference a State or a valid State subclass defined by this module.If present, each
transitionRefsvalue MUST reference a Transition.If present, each
exitRefsvalue MUST reference a JourneyExit.A Journey MUST contain one or more States and MAY connect local vertices with Transitions.
The derived local vertex set of a Journey is
stateRefsunionexitRefs.stateRefscontains experiential local vertices.exitRefscontains terminal exported local vertices that participate in local topology but do not represent UX states.Every
stateRefsvalue of a Journey SHOULD belong to that journey's local experiential topology.A state belongs to a journey when it is referenced by a JourneyEntry listed in the journey's
entryRefs, afromor state-valuedtoendpoint of a transition listed in that journey'stransitionRefs, or a local observable segment or condition connected by the journey's structural order.A Journey MUST NOT list a linked destination page, surface, flow, or journey entry inside
stateRefsmerely because an OutgoingTransition can reach it.
classDiagram
class Journey {
id
label
defaultEntryRef
entryRefs
stateRefs
transitionRefs
exitRefs
}
class JourneyEntry {
id
stateRef
}
class State {
id
label
}
class JourneyExit {
id
label
}
class Transition {
id
from
to
}
Journey --> JourneyEntry : defaultEntryRef
Journey --> JourneyEntry : entryRefs
JourneyEntry --> State : stateRef
Journey --> State : stateRefs
Journey --> JourneyExit : exitRefs
Journey --> Transition : transitionRefs
Transition --> State : from
Transition --> State : to
Transition --> JourneyExit : to Example JSON node:
[
{
"@type": "Journey",
"@id": "urn:ujg:journey:site-search",
"label": "Site search",
"defaultEntryRef": "urn:ujg:entry:site-search-default",
"entryRefs": [
"urn:ujg:entry:site-search-default"
],
"stateRefs": [
"urn:ujg:state:search-form",
"urn:ujg:state:results"
],
"transitionRefs": [
"urn:ujg:transition:search-form-to-results"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:site-search-default",
"stateRef": "urn:ujg:state:search-form"
}
] [
{
"@type": "Journey",
"@id": "urn:ujg:journey:site-search",
"label": "Site search",
"defaultEntryRef": "urn:ujg:entry:site-search-default",
"entryRefs": [
"urn:ujg:entry:site-search-default"
],
"stateRefs": [
"urn:ujg:state:search-form",
"urn:ujg:state:results"
],
"transitionRefs": [
"urn:ujg:transition:search-form-to-results"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:site-search-default",
"stateRef": "urn:ujg:state:search-form"
}
] A single-state journey can omit transitionRefs:
[
{
"@type": "Journey",
"@id": "urn:ujg:journey:privacy-policy",
"label": "Privacy policy",
"defaultEntryRef": "urn:ujg:entry:privacy-policy-default",
"entryRefs": [
"urn:ujg:entry:privacy-policy-default"
],
"stateRefs": [
"urn:ujg:state:privacy-policy"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:privacy-policy-default",
"stateRef": "urn:ujg:state:privacy-policy"
}
] [
{
"@type": "Journey",
"@id": "urn:ujg:journey:privacy-policy",
"label": "Privacy policy",
"defaultEntryRef": "urn:ujg:entry:privacy-policy-default",
"entryRefs": [
"urn:ujg:entry:privacy-policy-default"
],
"stateRefs": [
"urn:ujg:state:privacy-policy"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:privacy-policy-default",
"stateRef": "urn:ujg:state:privacy-policy"
}
] 8. JourneyEntry
A JourneyEntry is an explicit entry contract for a Journey. It identifies the local State or CompositeState where traversal begins without making that entry node part of the local transition topology.
A JourneyEntry MUST be identified by an IRI.
A JourneyEntry MUST declare exactly one
stateRef.The
stateRefvalue MUST reference a State or CompositeState.The
stateRefvalue MUST be listed in thestateRefsof the same Journey that lists the JourneyEntry inentryRefs.A JourneyEntry MUST be listed in exactly one Journey's
entryRefs.A JourneyEntry MUST NOT reference a JourneyExit.
A JourneyEntry MUST NOT be used as a Transition's
fromortovalue.A JourneyEntry MAY declare one
label.A JourneyEntry MAY declare one or more
tags.
Top-level execution of a Journey begins at the stateRef of the Journey's defaultEntryRef. Child journey execution can select a more specific entry through a parent transition's toEntryRef; otherwise it also begins at the child journey's defaultEntryRef.
classDiagram
class Journey {
defaultEntryRef
entryRefs
stateRefs
}
class JourneyEntry {
id
stateRef
}
class State
class CompositeState
Journey --> JourneyEntry : defaultEntryRef
Journey --> JourneyEntry : entryRefs
JourneyEntry --> State : stateRef
JourneyEntry --> CompositeState : stateRef Example JSON node:
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:site-search-default",
"label": "Default search entry",
"stateRef": "urn:ujg:state:search-form"
} {
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:site-search-default",
"label": "Default search entry",
"stateRef": "urn:ujg:state:search-form"
} 9. JourneyEntryIndex
A JourneyEntryIndex is a Graph class and Core Node that acts as a catalogue of addressable journey entry states. It is not a subclass of Journey and does not define traversal. A Consumer MUST NOT infer that indexed states are reachable from one another, ordered as a path, or part of a parent-owned progression.
Use Journey when modeling local topology. Use JourneyEntryIndex when listing known entry points into modeled journeys. A root Journey should only be used when the root itself owns real traversal, progression, or structural order.
In common use, a JourneyEntryIndex lists CompositeState entries whose subjourneyId values point to modeled pages, surfaces, flows, or journeys.
A JourneyEntryIndex MUST be identified by an IRI.
A JourneyEntryIndex MUST declare at least one
stateRefsvalue.Each
stateRefsvalue MUST reference a resolvable State or CompositeState.Each referenced state MUST be a top-level node in the same document or resolvable through imports.
Each referenced state SHOULD be a CompositeState when it represents a page, surface, or nested journey entry.
A JourneyEntryIndex MUST NOT reference a JourneyExit in
stateRefs.A JourneyEntryIndex MUST NOT declare
defaultEntryRef.A JourneyEntryIndex MUST NOT declare
entryRefs.A JourneyEntryIndex MUST NOT declare
stateRef.A JourneyEntryIndex MUST NOT declare
transitionRefs.A JourneyEntryIndex MUST NOT declare
exitRefs.A JourneyEntryIndex MUST NOT declare
outgoingTransitionGroupRefs.A JourneyEntryIndex MUST NOT declare
from.A JourneyEntryIndex MUST NOT declare
to.A JourneyEntryIndex MUST NOT declare
fromExitRef.A JourneyEntryIndex MUST NOT declare
toEntryRef.A JourneyEntryIndex MUST NOT declare
subjourneyId.A JourneyEntryIndex MUST NOT declare
outgoingTransitionRefs.stateRefson a JourneyEntryIndex MUST NOT imply traversal order, reachability, user path, progression, or parent continuation.The order of values in
stateRefsMUST NOT be interpreted normatively unless a future ordering mechanism is explicitly added.
JourneyEntryIndex is intended for top-level page maps, product surface indexes, search-result target indexes, documentation indexes, route or catalogue manifests, and other collections of known journey entry points. Do not use JourneyEntryIndex to model page-segment order, local journey progression, child completion, runtime observations, or experience annotations.
classDiagram
class JourneyEntryIndex {
id
label
stateRefs
}
class State {
id
label
}
class CompositeState {
subjourneyId
}
JourneyEntryIndex --> State : stateRefs
JourneyEntryIndex --> CompositeState : stateRefs
CompositeState --> Journey : subjourneyId Example JSON node:
{
"@type": "JourneyEntryIndex",
"@id": "urn:ujg:index:site-pages",
"label": "Site page index",
"stateRefs": [
"urn:ujg:state:home-page",
"urn:ujg:state:search-page",
"urn:ujg:state:profile-page"
]
} {
"@type": "JourneyEntryIndex",
"@id": "urn:ujg:index:site-pages",
"label": "Site page index",
"stateRefs": [
"urn:ujg:state:home-page",
"urn:ujg:state:search-page",
"urn:ujg:state:profile-page"
]
} The indexed states are known entry points. Their order above does not define a path from the home page to search to profile.
10. CompositeState
A CompositeState is a State that represents nested composition. It references a child Journey through subjourneyId, allowing consumers to interpret the referenced journey as a zoomable or nested graph.
The parent journey treats the CompositeState as a parent-local state. The parent journey does not list the child journey's states directly.
A CompositeState MUST be a State.
A CompositeState MUST declare exactly one
subjourneyId.The
subjourneyIdvalue MUST resolve to a Journey.A CompositeState MUST NOT list child states directly with
stateRefs.
classDiagram
class State
class CompositeState {
subjourneyId
}
class Journey {
stateRefs
transitionRefs
}
State <|-- CompositeState
Journey --> CompositeState : stateRefs
CompositeState --> Journey : subjourneyId Example JSON node:
{
"@type": "CompositeState",
"@id": "urn:ujg:state:checkout-flow",
"label": "Checkout flow",
"subjourneyId": "urn:ujg:journey:checkout"
} {
"@type": "CompositeState",
"@id": "urn:ujg:state:checkout-flow",
"label": "Checkout flow",
"subjourneyId": "urn:ujg:journey:checkout"
} 11. JourneyExit
A JourneyExit is a terminal local graph vertex and exported completion contract declared by a Journey. It represents a terminal journey outcome that can be reached by a local Transition.
A parent journey can use exported exits to distinguish which outcome of a child journey was reached, without directly referencing child states.
A Journey MAY declare
exitRefs.Each value of
exitRefsMUST reference a JourneyExit.A JourneyExit MUST be declared in exactly one Journey's
exitRefs.A JourneyExit MAY be the
toendpoint of a Transition listed in the declaring journey'stransitionRefs.A JourneyExit MUST NOT be the
fromendpoint of a Transition.A JourneyExit MUST NOT declare
outgoingTransitionRefs.Outgoing transition group injection MUST NOT create effective outgoing transitions from a JourneyExit.
A JourneyExit MUST NOT be used for ordinary internal child transitions when a normal State or Transition preserves the same meaning.
Runtime observations, user actions, form values, clicked elements, submitted values, selected values, analytics facts, or other runtime facts MUST NOT be modeled in the Graph module through JourneyExit.
If a completion point is an actual user-visible screen, page, modal, or step, it should remain a normal State. A journey can then transition from that state to a JourneyExit that represents the exported terminal outcome.
Informative pattern:
CheckoutForm -> SuccessScreen -> CheckoutCompleteExit CheckoutForm -> SuccessScreen -> CheckoutCompleteExit classDiagram
class Journey {
stateRefs
exitRefs
}
class State
class JourneyExit
class Transition
Journey --> State : stateRefs
Journey --> JourneyExit : exitRefs
Transition --> State : from
Transition --> JourneyExit : to Example JSON graph:
{
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld"
],
"@id": "https://example.com/ujg/checkout-exit.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "Journey",
"@id": "urn:ujg:journey:checkout",
"label": "Checkout journey",
"defaultEntryRef": "urn:ujg:entry:checkout-default",
"entryRefs": [
"urn:ujg:entry:checkout-default"
],
"stateRefs": [
"urn:ujg:state:checkout-form"
],
"transitionRefs": [
"urn:ujg:transition:checkout-form-to-complete"
],
"exitRefs": [
"urn:ujg:exit:checkout-complete"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:checkout-default",
"stateRef": "urn:ujg:state:checkout-form"
},
{
"@type": "State",
"@id": "urn:ujg:state:checkout-form",
"label": "Checkout form"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:checkout-form-to-complete",
"label": "Submit checkout",
"from": "urn:ujg:state:checkout-form",
"to": "urn:ujg:exit:checkout-complete"
},
{
"@type": "JourneyExit",
"@id": "urn:ujg:exit:checkout-complete",
"label": "Checkout complete"
}
]
} {
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld"
],
"@id": "https://example.com/ujg/checkout-exit.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "Journey",
"@id": "urn:ujg:journey:checkout",
"label": "Checkout journey",
"defaultEntryRef": "urn:ujg:entry:checkout-default",
"entryRefs": [
"urn:ujg:entry:checkout-default"
],
"stateRefs": [
"urn:ujg:state:checkout-form"
],
"transitionRefs": [
"urn:ujg:transition:checkout-form-to-complete"
],
"exitRefs": [
"urn:ujg:exit:checkout-complete"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:checkout-default",
"stateRef": "urn:ujg:state:checkout-form"
},
{
"@type": "State",
"@id": "urn:ujg:state:checkout-form",
"label": "Checkout form"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:checkout-form-to-complete",
"label": "Submit checkout",
"from": "urn:ujg:state:checkout-form",
"to": "urn:ujg:exit:checkout-complete"
},
{
"@type": "JourneyExit",
"@id": "urn:ujg:exit:checkout-complete",
"label": "Checkout complete"
}
]
} 11.1. Boundary Entry and Exit Mapping
toEntryRef and fromExitRef are mapping properties on parent Transition resources. They describe how a parent-local CompositeState connects to the entry and exit contracts of its child Journey.
These properties are not transition endpoints. The transition's from and to values remain local to the enclosing journey.
A Transition MAY declare
toEntryRef.A Transition MUST NOT declare more than one
toEntryRef.A Transition with
toEntryRefMUST have atovalue that references a CompositeState.The CompositeState referenced by
toMUST declare exactly onesubjourneyId.The
subjourneyIdvalue of thattoCompositeState MUST resolve to a Journey.The
toEntryRefvalue MUST be listed in theentryRefsof the journey referenced by thetocomposite state'ssubjourneyId.A Transition MAY declare
fromExitRef.A Transition MUST NOT declare more than one
fromExitRef.A Transition with
fromExitRefMUST have afromvalue that references a CompositeState.The CompositeState referenced by
fromMUST declare exactly onesubjourneyId.The
subjourneyIdvalue of thatfromCompositeState MUST resolve to a Journey.The
fromExitRefvalue MUST be listed in theexitRefsof the journey referenced by thefromcomposite state'ssubjourneyId.toEntryRefandfromExitRefMUST NOT weaken, replace, or bypass local transition endpoint validation.A parent transition MUST NOT directly reference a child journey's state as
from,to, or through any child-state-specific transition property.A journey MUST NOT contain more than one transition with the same
fromvalue and the samefromExitRefvalue.
When a Consumer enters a CompositeState, it MAY resolve the composite state's subjourneyId and interpret the referenced child Journey.
If the parent transition whose to value enters the CompositeState declares toEntryRef, child traversal begins at the stateRef of that JourneyEntry.
If the parent transition does not declare toEntryRef, child traversal begins at the stateRef of the child Journey's defaultEntryRef.
If interpretation of the child journey reaches a JourneyExit listed in that child journey's exitRefs, that JourneyExit becomes the exported exit of the child journey.
The enclosing journey may then continue only by taking a parent transition whose:
fromvalue is the active parent-local CompositeState; andfromExitRefvalue is the exported JourneyExit.
If exactly one matching parent transition exists, the Consumer MAY continue to that transition's parent-local to state.
If no matching parent transition exists, the Consumer MUST NOT synthesize an implicit parent transition.
If more than one matching parent transition exists, the graph is invalid. A Consumer MUST NOT choose one arbitrarily.
A Consumer MUST NOT treat toEntryRef or fromExitRef as a replacement for from or to.
A Consumer MUST NOT treat a parent transition without fromExitRef as a fallback for an exported child journey exit.
Use toEntryRef when a parent transition must choose a specific child entry other than the child journey's default entry. Use JourneyExit and fromExitRef when a nested journey has multiple explicit child outcomes that the parent journey needs to distinguish. Do not use JourneyExit for ordinary transitions inside the child journey; use normal child Transition resources for internal child movement.
classDiagram
class Journey
class CompositeState {
subjourneyId
}
class JourneyEntry
class JourneyExit
class Transition {
from
to
toEntryRef
fromExitRef
}
CompositeState --> Journey : subjourneyId
Journey --> JourneyEntry : entryRefs
Journey --> JourneyExit : exitRefs
Transition --> CompositeState : to
Transition --> JourneyEntry : toEntryRef
Transition --> CompositeState : from
Transition --> JourneyExit : fromExitRef Example JSON graph:
{
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld"
],
"@id": "https://example.com/ujg/checkout-with-exit.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "Journey",
"@id": "urn:ujg:journey:shop",
"label": "Shop journey",
"defaultEntryRef": "urn:ujg:entry:shop-default",
"entryRefs": [
"urn:ujg:entry:shop-default"
],
"stateRefs": [
"urn:ujg:state:cart",
"urn:ujg:state:checkout-flow",
"urn:ujg:state:confirmation"
],
"transitionRefs": [
"urn:ujg:transition:cart-to-checkout",
"urn:ujg:transition:checkout-to-confirmation"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:shop-default",
"stateRef": "urn:ujg:state:cart"
},
{
"@type": "State",
"@id": "urn:ujg:state:cart",
"label": "Cart"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:checkout-flow",
"label": "Checkout flow",
"subjourneyId": "urn:ujg:journey:checkout"
},
{
"@type": "State",
"@id": "urn:ujg:state:confirmation",
"label": "Confirmation"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:cart-to-checkout",
"label": "Start checkout",
"from": "urn:ujg:state:cart",
"to": "urn:ujg:state:checkout-flow",
"toEntryRef": "urn:ujg:entry:checkout-default"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:checkout-to-confirmation",
"label": "Show confirmation",
"from": "urn:ujg:state:checkout-flow",
"to": "urn:ujg:state:confirmation",
"fromExitRef": "urn:ujg:exit:checkout-complete"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:checkout",
"label": "Checkout child journey",
"defaultEntryRef": "urn:ujg:entry:checkout-default",
"entryRefs": [
"urn:ujg:entry:checkout-default"
],
"stateRefs": [
"urn:ujg:state:checkout-form"
],
"transitionRefs": [
"urn:ujg:transition:checkout-form-to-complete"
],
"exitRefs": [
"urn:ujg:exit:checkout-complete"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:checkout-default",
"stateRef": "urn:ujg:state:checkout-form"
},
{
"@type": "State",
"@id": "urn:ujg:state:checkout-form",
"label": "Checkout form"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:checkout-form-to-complete",
"label": "Submit checkout",
"from": "urn:ujg:state:checkout-form",
"to": "urn:ujg:exit:checkout-complete"
},
{
"@type": "JourneyExit",
"@id": "urn:ujg:exit:checkout-complete",
"label": "Checkout complete"
}
]
} {
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld"
],
"@id": "https://example.com/ujg/checkout-with-exit.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "Journey",
"@id": "urn:ujg:journey:shop",
"label": "Shop journey",
"defaultEntryRef": "urn:ujg:entry:shop-default",
"entryRefs": [
"urn:ujg:entry:shop-default"
],
"stateRefs": [
"urn:ujg:state:cart",
"urn:ujg:state:checkout-flow",
"urn:ujg:state:confirmation"
],
"transitionRefs": [
"urn:ujg:transition:cart-to-checkout",
"urn:ujg:transition:checkout-to-confirmation"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:shop-default",
"stateRef": "urn:ujg:state:cart"
},
{
"@type": "State",
"@id": "urn:ujg:state:cart",
"label": "Cart"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:checkout-flow",
"label": "Checkout flow",
"subjourneyId": "urn:ujg:journey:checkout"
},
{
"@type": "State",
"@id": "urn:ujg:state:confirmation",
"label": "Confirmation"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:cart-to-checkout",
"label": "Start checkout",
"from": "urn:ujg:state:cart",
"to": "urn:ujg:state:checkout-flow",
"toEntryRef": "urn:ujg:entry:checkout-default"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:checkout-to-confirmation",
"label": "Show confirmation",
"from": "urn:ujg:state:checkout-flow",
"to": "urn:ujg:state:confirmation",
"fromExitRef": "urn:ujg:exit:checkout-complete"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:checkout",
"label": "Checkout child journey",
"defaultEntryRef": "urn:ujg:entry:checkout-default",
"entryRefs": [
"urn:ujg:entry:checkout-default"
],
"stateRefs": [
"urn:ujg:state:checkout-form"
],
"transitionRefs": [
"urn:ujg:transition:checkout-form-to-complete"
],
"exitRefs": [
"urn:ujg:exit:checkout-complete"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:checkout-default",
"stateRef": "urn:ujg:state:checkout-form"
},
{
"@type": "State",
"@id": "urn:ujg:state:checkout-form",
"label": "Checkout form"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:checkout-form-to-complete",
"label": "Submit checkout",
"from": "urn:ujg:state:checkout-form",
"to": "urn:ujg:exit:checkout-complete"
},
{
"@type": "JourneyExit",
"@id": "urn:ujg:exit:checkout-complete",
"label": "Checkout complete"
}
]
} Example JSON graph with an explicit child entry selection:
{
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld"
],
"@id": "https://example.com/ujg/mfa-entry.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "Journey",
"@id": "urn:ujg:journey:account-access",
"label": "Account access",
"defaultEntryRef": "urn:ujg:entry:account-access-default",
"entryRefs": [
"urn:ujg:entry:account-access-default"
],
"stateRefs": [
"urn:ujg:state:password-check",
"urn:ujg:state:mfa-challenge"
],
"transitionRefs": [
"urn:ujg:transition:password-to-mfa"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:account-access-default",
"stateRef": "urn:ujg:state:password-check"
},
{
"@type": "State",
"@id": "urn:ujg:state:password-check",
"label": "Password check"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:mfa-challenge",
"label": "MFA challenge",
"subjourneyId": "urn:ujg:journey:mfa"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:password-to-mfa",
"label": "Require MFA",
"from": "urn:ujg:state:password-check",
"to": "urn:ujg:state:mfa-challenge",
"toEntryRef": "urn:ujg:entry:mfa-code-entry"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:mfa",
"label": "MFA",
"defaultEntryRef": "urn:ujg:entry:mfa-default",
"entryRefs": [
"urn:ujg:entry:mfa-default",
"urn:ujg:entry:mfa-code-entry"
],
"stateRefs": [
"urn:ujg:state:mfa-method-choice",
"urn:ujg:state:mfa-code"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:mfa-default",
"label": "Choose MFA method",
"stateRef": "urn:ujg:state:mfa-method-choice"
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:mfa-code-entry",
"label": "Enter MFA code",
"stateRef": "urn:ujg:state:mfa-code"
},
{
"@type": "State",
"@id": "urn:ujg:state:mfa-method-choice",
"label": "MFA method choice"
},
{
"@type": "State",
"@id": "urn:ujg:state:mfa-code",
"label": "MFA code entry"
}
]
} {
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld"
],
"@id": "https://example.com/ujg/mfa-entry.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "Journey",
"@id": "urn:ujg:journey:account-access",
"label": "Account access",
"defaultEntryRef": "urn:ujg:entry:account-access-default",
"entryRefs": [
"urn:ujg:entry:account-access-default"
],
"stateRefs": [
"urn:ujg:state:password-check",
"urn:ujg:state:mfa-challenge"
],
"transitionRefs": [
"urn:ujg:transition:password-to-mfa"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:account-access-default",
"stateRef": "urn:ujg:state:password-check"
},
{
"@type": "State",
"@id": "urn:ujg:state:password-check",
"label": "Password check"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:mfa-challenge",
"label": "MFA challenge",
"subjourneyId": "urn:ujg:journey:mfa"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:password-to-mfa",
"label": "Require MFA",
"from": "urn:ujg:state:password-check",
"to": "urn:ujg:state:mfa-challenge",
"toEntryRef": "urn:ujg:entry:mfa-code-entry"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:mfa",
"label": "MFA",
"defaultEntryRef": "urn:ujg:entry:mfa-default",
"entryRefs": [
"urn:ujg:entry:mfa-default",
"urn:ujg:entry:mfa-code-entry"
],
"stateRefs": [
"urn:ujg:state:mfa-method-choice",
"urn:ujg:state:mfa-code"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:mfa-default",
"label": "Choose MFA method",
"stateRef": "urn:ujg:state:mfa-method-choice"
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:mfa-code-entry",
"label": "Enter MFA code",
"stateRef": "urn:ujg:state:mfa-code"
},
{
"@type": "State",
"@id": "urn:ujg:state:mfa-method-choice",
"label": "MFA method choice"
},
{
"@type": "State",
"@id": "urn:ujg:state:mfa-code",
"label": "MFA code entry"
}
]
} 12. OutgoingTransition
An OutgoingTransition is a navigational affordance. It defines a possible effective target state but does not declare a structural transition in a journey's local topology.
An OutgoingTransition has no explicit from property. Its effective source comes from either a state-scoped outgoingTransitionRefs value or an injected OutgoingTransitionGroup.
An OutgoingTransition MUST be identified by an IRI.
An OutgoingTransition MUST declare exactly one effective target mechanism: either exactly one
tovalue, ortoCurrentState: true.An OutgoingTransition with
toCurrentState: trueMUST NOT also declareto.An OutgoingTransition with neither
tonortoCurrentState: trueis invalid.toCurrentState: falseis equivalent to the property being absent and does not satisfy the target requirement.OutgoingTransition.toMAY reference a resolvable State or CompositeState outside the journey that contributes the affordance.OutgoingTransition.toMUST NOT reference a JourneyExit.An OutgoingTransition MUST NOT be listed in a Journey's
transitionRefs.An OutgoingTransition MUST NOT be used for ordinary internal journey progression when a local Transition is appropriate.
An OutgoingTransition MUST NOT declare more than one
label.
If the to target is a known page, surface, or flow entry, it should normally be listed in a JourneyEntryIndex. Do not list that target in the source Journey's stateRefs unless it also belongs to the source journey's local topology.
classDiagram
class OutgoingTransition {
id
label
to
toCurrentState
}
class State
class CompositeState
OutgoingTransition --> State : effective target
OutgoingTransition --> CompositeState : effective target
note for OutgoingTransition "target mechanism is either to or toCurrentState" Example JSON nodes:
Fixed target navigation:
{
"@type": "OutgoingTransition",
"@id": "urn:ujg:ot:go-home",
"label": "Home",
"to": "urn:ujg:state:home"
} {
"@type": "OutgoingTransition",
"@id": "urn:ujg:ot:go-home",
"label": "Home",
"to": "urn:ujg:state:home"
} 12.1. Relative Current-State Targeting
Some outgoing affordances do not target a fixed state. Instead, they preserve the current effective state and modify some non-topological dimension such as locale, presentation mode, or filter context. Such affordances MAY use toCurrentState: true. When used, the outgoing transition resolves to the effective source where the affordance is available, which can be a State or CompositeState. This allows reusable outgoing transition groups, such as global language switchers, to be attached across journeys without duplicating per-page transitions.
toCurrentState changes graph target resolution, but it does not imply any runtime event, click, URL, locale, payload, or private extension behavior.
Current-state targeting:
{
"@type": "OutgoingTransition",
"@id": "urn:ujg:ot:keep-current-state",
"label": "Keep current state",
"toCurrentState": true
} {
"@type": "OutgoingTransition",
"@id": "urn:ujg:ot:keep-current-state",
"label": "Keep current state",
"toCurrentState": true
} 13. OutgoingTransitionGroup
An OutgoingTransitionGroup defines a reusable set of outgoing affordances, such as header or footer navigation, that a Consumer can treat as present on multiple eligible states.
Group injection does not add structural Transition resources to transitionRefs.
An OutgoingTransitionGroup MUST be identified by an IRI.
An OutgoingTransitionGroup MUST declare at least one
outgoingTransitionRefsvalue.Each group
outgoingTransitionRefsvalue MUST reference an OutgoingTransition.A Journey MAY reference an OutgoingTransitionGroup through
outgoingTransitionGroupRefs.
For journey-level group injection:
The Consumer MUST resolve each referenced OutgoingTransitionGroup and each group
outgoingTransitionRefsentry to an OutgoingTransition.The Consumer MUST iterate over every State and CompositeState ID in
stateRefs.The Consumer MUST resolve each OutgoingTransition target at the iterated state where the group is applied.
For a resolved OutgoingTransition with
toCurrentState: true, the effective target is the current iterated State or CompositeState.For a resolved OutgoingTransition with
to, the effective target is the referencedtostate.The Consumer MUST NOT create effective outgoing transitions from a JourneyExit.
A Consumer SHOULD treat duplicate effective outgoing edges with the same effective source state and same effective target as one effective edge.
classDiagram
class Journey {
outgoingTransitionGroupRefs
}
class OutgoingTransitionGroup {
outgoingTransitionRefs
}
class OutgoingTransition {
to
toCurrentState
}
class State
class CompositeState
Journey --> OutgoingTransitionGroup : outgoingTransitionGroupRefs
OutgoingTransitionGroup --> OutgoingTransition : outgoingTransitionRefs
OutgoingTransition --> State : effective target
OutgoingTransition --> CompositeState : effective target
note for OutgoingTransition "target mechanism is either to or toCurrentState" Example JSON node:
{
"@type": "OutgoingTransitionGroup",
"@id": "urn:ujg:otg:global-header",
"outgoingTransitionRefs": [
"urn:ujg:ot:go-home",
"urn:ujg:ot:go-profile"
]
} {
"@type": "OutgoingTransitionGroup",
"@id": "urn:ujg:otg:global-header",
"outgoingTransitionRefs": [
"urn:ujg:ot:go-home",
"urn:ujg:ot:go-profile"
]
} Example JSON nodes for a shared language switcher:
{
"@type": "OutgoingTransitionGroup",
"@id": "urn:ankommenskreis:otg:shared-header-language-switcher",
"label": "Shared header language switcher",
"outgoingTransitionRefs": [
"urn:ankommenskreis:ot:lang-de",
"urn:ankommenskreis:ot:lang-en",
"urn:ankommenskreis:ot:lang-ar",
"urn:ankommenskreis:ot:lang-fa",
"urn:ankommenskreis:ot:lang-tr",
"urn:ankommenskreis:ot:lang-uk",
"urn:ankommenskreis:ot:lang-ru"
]
} {
"@type": "OutgoingTransitionGroup",
"@id": "urn:ankommenskreis:otg:shared-header-language-switcher",
"label": "Shared header language switcher",
"outgoingTransitionRefs": [
"urn:ankommenskreis:ot:lang-de",
"urn:ankommenskreis:ot:lang-en",
"urn:ankommenskreis:ot:lang-ar",
"urn:ankommenskreis:ot:lang-fa",
"urn:ankommenskreis:ot:lang-tr",
"urn:ankommenskreis:ot:lang-uk",
"urn:ankommenskreis:ot:lang-ru"
]
} {
"@type": "OutgoingTransition",
"@id": "urn:ankommenskreis:ot:lang-en",
"label": "English",
"toCurrentState": true,
"l10n:targetLocale": "en"
} {
"@type": "OutgoingTransition",
"@id": "urn:ankommenskreis:ot:lang-en",
"label": "English",
"toCurrentState": true,
"l10n:targetLocale": "en"
} The l10n:targetLocale value in this example is locale metadata from the Localization module. The current-state target behavior is defined only by Graph's toCurrentState.
14. State-scoped Outgoing Affordances
A State can also declare outgoing affordances directly. These affordances apply only to that state and are not injected into other states.
Direct state-scoped affordances are for local navigational options, not for ordinary internal progression through a journey.
A State MAY declare
outgoingTransitionRefs.A CompositeState MUST NOT declare
outgoingTransitionRefs.A JourneyExit MUST NOT declare
outgoingTransitionRefs.Each state-scoped
outgoingTransitionRefsvalue MUST reference an OutgoingTransition.The effective source of a state-scoped OutgoingTransition is the State that declares the
outgoingTransitionRefsvalue.For a state-scoped OutgoingTransition with
toCurrentState: true, the effective target is the State that declares theoutgoingTransitionRefsvalue.For a state-scoped OutgoingTransition with
to, the effective target is the referencedtostate.A Consumer MUST NOT treat a state-scoped OutgoingTransition as a member of the enclosing Journey's
transitionRefs.A Consumer SHOULD treat duplicate effective outgoing edges with the same effective source state and same effective target as one effective edge.
If navigation should be available while a CompositeState is active, model it either as an OutgoingTransitionGroup referenced by the enclosing journey or as direct outgoingTransitionRefs on concrete states inside the composite state's subjourney.
classDiagram
class State {
outgoingTransitionRefs
}
class CompositeState
class JourneyExit
class OutgoingTransition {
to
toCurrentState
}
State --> OutgoingTransition : outgoingTransitionRefs
OutgoingTransition --> State : effective target
OutgoingTransition --> CompositeState : effective target
note for OutgoingTransition "target mechanism is either to or toCurrentState"
note for CompositeState "MUST NOT declare outgoingTransitionRefs"
note for JourneyExit "MUST NOT declare outgoingTransitionRefs" Example JSON nodes:
{
"@type": "State",
"@id": "urn:ujg:state:w3c-searchpage-form",
"label": "Search form",
"outgoingTransitionRefs": [
"urn:ujg:ot:w3c-searchpage-form-back-home"
]
} {
"@type": "State",
"@id": "urn:ujg:state:w3c-searchpage-form",
"label": "Search form",
"outgoingTransitionRefs": [
"urn:ujg:ot:w3c-searchpage-form-back-home"
]
} {
"@type": "OutgoingTransition",
"@id": "urn:ujg:ot:w3c-searchpage-form-back-home",
"label": "Back to home page",
"to": "urn:ujg:state:w3c-root-homepage"
} {
"@type": "OutgoingTransition",
"@id": "urn:ujg:ot:w3c-searchpage-form-back-home",
"label": "Back to home page",
"to": "urn:ujg:state:w3c-root-homepage"
} This example shows a search form state with a local "Back to home page" affordance. This is not a structural Transition from the SearchPage journey to a root journey or JourneyEntryIndex. It is a state-scoped navigational affordance. The to target must resolve to a known State or CompositeState, but it does not need to be listed in the current journey's stateRefs. If the home page is a known page entry, it should normally be listed in a JourneyEntryIndex.
15. Ontology
The normative Graph ontology is defined below and is published at https://ujg.specs.openuji.org/ed/ns/graph. It is the authoritative structural definition for Graph classes and properties, including Journey, JourneyEntry, JourneyEntryIndex, LocalVertex, State, CompositeState, Transition, JourneyExit, OutgoingTransition, OutgoingTransitionGroup, defaultEntryRef, entryRefs, stateRef, exitRefs, toEntryRef, fromExitRef, toCurrentState, and outgoingTransitionRefs.
@prefix ujg: <https://ujg.specs.openuji.org/ed/ns/core#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix dct: <http://purl.org/dc/terms/> .
<https://ujg.specs.openuji.org/ed/ns/graph#> a owl:Ontology ;
rdfs:label "UJG Graph Editor's Draft Vocabulary"@en ;
dct:description "UJG Graph ontology declaration" .
### Classes
ujggraph:Journey a owl:Class ;
rdfs:subClassOf ujg:Node .
ujggraph:JourneyEntry a owl:Class ;
rdfs:subClassOf ujg:Node ;
rdfs:label "Journey Entry" ;
rdfs:comment "An explicit entry contract for a Journey. A JourneyEntry points to the local State or CompositeState where traversal begins." .
ujggraph:JourneyEntryIndex a owl:Class ;
rdfs:subClassOf ujg:Node ;
rdfs:label "Journey Index" ;
rdfs:comment "A catalogue of addressable journey entry states. A JourneyEntryIndex does not define traversal, ordering, reachability, or parent-owned progression." .
ujggraph:LocalVertex a owl:Class ;
rdfs:subClassOf ujg:Node ;
rdfs:label "Local Vertex" ;
rdfs:comment "An abstract local topology vertex of a Journey. State, CompositeState, and JourneyExit are local vertex kinds." .
ujggraph:State a owl:Class ;
rdfs:subClassOf ujggraph:LocalVertex .
ujggraph:CompositeState a owl:Class ;
rdfs:subClassOf ujggraph:State .
ujggraph:Transition a owl:Class ;
rdfs:subClassOf ujg:Node .
ujggraph:JourneyExit a owl:Class ;
rdfs:subClassOf ujggraph:LocalVertex ;
owl:disjointWith ujggraph:State ;
rdfs:label "Journey Exit" ;
rdfs:comment "A terminal local graph vertex and exported completion contract of a journey." .
ujggraph:OutgoingTransition a owl:Class ;
rdfs:subClassOf ujg:Node .
ujggraph:OutgoingTransitionGroup a owl:Class ;
rdfs:subClassOf ujg:Node .
### Properties
ujggraph:label a owl:DatatypeProperty ;
rdfs:domain ujg:Node ;
rdfs:range xsd:string .
ujggraph:tags a owl:DatatypeProperty ;
rdfs:domain ujg:Node ;
rdfs:range xsd:string .
ujggraph:defaultEntryRef a owl:ObjectProperty ;
rdfs:domain ujggraph:Journey ;
rdfs:range ujggraph:JourneyEntry ;
rdfs:label "default entry ref" ;
rdfs:comment "References the JourneyEntry used when traversal starts without a more specific entry selection." .
ujggraph:entryRefs a owl:ObjectProperty ;
rdfs:domain ujggraph:Journey ;
rdfs:range ujggraph:JourneyEntry ;
rdfs:label "entry refs" ;
rdfs:comment "References explicit entry contracts declared by a journey." .
ujggraph:stateRef a owl:ObjectProperty ;
rdfs:domain ujggraph:JourneyEntry ;
rdfs:range ujggraph:State ;
rdfs:label "state ref" ;
rdfs:comment "References the local State or CompositeState where a JourneyEntry begins traversal." .
ujggraph:stateRefs a owl:ObjectProperty ;
rdfs:range ujggraph:State ;
rdfs:comment "References graph states. On Journey, stateRefs lists experiential local vertices. On JourneyEntryIndex, stateRefs lists addressable entry states without traversal semantics." .
ujggraph:transitionRefs a owl:ObjectProperty ;
rdfs:domain ujggraph:Journey ;
rdfs:range ujggraph:Transition .
ujggraph:exitRefs a owl:ObjectProperty ;
rdfs:domain ujggraph:Journey ;
rdfs:range ujggraph:JourneyExit ;
rdfs:label "exit refs" ;
rdfs:comment "References the terminal exported local vertices declared by a journey." .
ujggraph:outgoingTransitionGroupRefs a owl:ObjectProperty ;
rdfs:domain ujggraph:Journey ;
rdfs:range ujggraph:OutgoingTransitionGroup .
ujggraph:from a owl:ObjectProperty ;
rdfs:domain ujggraph:Transition ;
rdfs:range ujggraph:State .
ujggraph:to a owl:ObjectProperty ;
rdfs:range ujggraph:LocalVertex .
ujggraph:toCurrentState a owl:DatatypeProperty ;
rdfs:domain ujggraph:OutgoingTransition ;
rdfs:range xsd:boolean .
ujggraph:fromExitRef a owl:ObjectProperty ;
rdfs:domain ujggraph:Transition ;
rdfs:range ujggraph:JourneyExit ;
rdfs:label "from exit ref" ;
rdfs:comment "Identifies the exported JourneyExit completed by the child journey of the CompositeState used as the transition's from value. This property is not a transition endpoint." .
ujggraph:toEntryRef a owl:ObjectProperty ;
rdfs:domain ujggraph:Transition ;
rdfs:range ujggraph:JourneyEntry ;
rdfs:label "to entry ref" ;
rdfs:comment "Identifies the JourneyEntry selected when entering the child journey of the CompositeState used as the transition's to value. This property is not a transition endpoint." .
ujggraph:subjourneyId a owl:ObjectProperty ;
rdfs:domain ujggraph:CompositeState ;
rdfs:range ujggraph:Journey .
ujggraph:outgoingTransitionRefs a owl:ObjectProperty ;
rdfs:domain [
a owl:Class ;
owl:unionOf (
ujggraph:State
ujggraph:OutgoingTransitionGroup
)
] ;
rdfs:range ujggraph:OutgoingTransition ;
rdfs:label "outgoing transition refs" ;
rdfs:comment "References outgoing navigational affordances. When used on an OutgoingTransitionGroup, the references define a reusable group. When used on a non-composite State, the references define outgoing affordances available from that specific state." . @prefix ujg: <https://ujg.specs.openuji.org/ed/ns/core#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix dct: <http://purl.org/dc/terms/> .
<https://ujg.specs.openuji.org/ed/ns/graph#> a owl:Ontology ;
rdfs:label "UJG Graph Editor's Draft Vocabulary"@en ;
dct:description "UJG Graph ontology declaration" .
### Classes
ujggraph:Journey a owl:Class ;
rdfs:subClassOf ujg:Node .
ujggraph:JourneyEntry a owl:Class ;
rdfs:subClassOf ujg:Node ;
rdfs:label "Journey Entry" ;
rdfs:comment "An explicit entry contract for a Journey. A JourneyEntry points to the local State or CompositeState where traversal begins." .
ujggraph:JourneyEntryIndex a owl:Class ;
rdfs:subClassOf ujg:Node ;
rdfs:label "Journey Index" ;
rdfs:comment "A catalogue of addressable journey entry states. A JourneyEntryIndex does not define traversal, ordering, reachability, or parent-owned progression." .
ujggraph:LocalVertex a owl:Class ;
rdfs:subClassOf ujg:Node ;
rdfs:label "Local Vertex" ;
rdfs:comment "An abstract local topology vertex of a Journey. State, CompositeState, and JourneyExit are local vertex kinds." .
ujggraph:State a owl:Class ;
rdfs:subClassOf ujggraph:LocalVertex .
ujggraph:CompositeState a owl:Class ;
rdfs:subClassOf ujggraph:State .
ujggraph:Transition a owl:Class ;
rdfs:subClassOf ujg:Node .
ujggraph:JourneyExit a owl:Class ;
rdfs:subClassOf ujggraph:LocalVertex ;
owl:disjointWith ujggraph:State ;
rdfs:label "Journey Exit" ;
rdfs:comment "A terminal local graph vertex and exported completion contract of a journey." .
ujggraph:OutgoingTransition a owl:Class ;
rdfs:subClassOf ujg:Node .
ujggraph:OutgoingTransitionGroup a owl:Class ;
rdfs:subClassOf ujg:Node .
### Properties
ujggraph:label a owl:DatatypeProperty ;
rdfs:domain ujg:Node ;
rdfs:range xsd:string .
ujggraph:tags a owl:DatatypeProperty ;
rdfs:domain ujg:Node ;
rdfs:range xsd:string .
ujggraph:defaultEntryRef a owl:ObjectProperty ;
rdfs:domain ujggraph:Journey ;
rdfs:range ujggraph:JourneyEntry ;
rdfs:label "default entry ref" ;
rdfs:comment "References the JourneyEntry used when traversal starts without a more specific entry selection." .
ujggraph:entryRefs a owl:ObjectProperty ;
rdfs:domain ujggraph:Journey ;
rdfs:range ujggraph:JourneyEntry ;
rdfs:label "entry refs" ;
rdfs:comment "References explicit entry contracts declared by a journey." .
ujggraph:stateRef a owl:ObjectProperty ;
rdfs:domain ujggraph:JourneyEntry ;
rdfs:range ujggraph:State ;
rdfs:label "state ref" ;
rdfs:comment "References the local State or CompositeState where a JourneyEntry begins traversal." .
ujggraph:stateRefs a owl:ObjectProperty ;
rdfs:range ujggraph:State ;
rdfs:comment "References graph states. On Journey, stateRefs lists experiential local vertices. On JourneyEntryIndex, stateRefs lists addressable entry states without traversal semantics." .
ujggraph:transitionRefs a owl:ObjectProperty ;
rdfs:domain ujggraph:Journey ;
rdfs:range ujggraph:Transition .
ujggraph:exitRefs a owl:ObjectProperty ;
rdfs:domain ujggraph:Journey ;
rdfs:range ujggraph:JourneyExit ;
rdfs:label "exit refs" ;
rdfs:comment "References the terminal exported local vertices declared by a journey." .
ujggraph:outgoingTransitionGroupRefs a owl:ObjectProperty ;
rdfs:domain ujggraph:Journey ;
rdfs:range ujggraph:OutgoingTransitionGroup .
ujggraph:from a owl:ObjectProperty ;
rdfs:domain ujggraph:Transition ;
rdfs:range ujggraph:State .
ujggraph:to a owl:ObjectProperty ;
rdfs:range ujggraph:LocalVertex .
ujggraph:toCurrentState a owl:DatatypeProperty ;
rdfs:domain ujggraph:OutgoingTransition ;
rdfs:range xsd:boolean .
ujggraph:fromExitRef a owl:ObjectProperty ;
rdfs:domain ujggraph:Transition ;
rdfs:range ujggraph:JourneyExit ;
rdfs:label "from exit ref" ;
rdfs:comment "Identifies the exported JourneyExit completed by the child journey of the CompositeState used as the transition's from value. This property is not a transition endpoint." .
ujggraph:toEntryRef a owl:ObjectProperty ;
rdfs:domain ujggraph:Transition ;
rdfs:range ujggraph:JourneyEntry ;
rdfs:label "to entry ref" ;
rdfs:comment "Identifies the JourneyEntry selected when entering the child journey of the CompositeState used as the transition's to value. This property is not a transition endpoint." .
ujggraph:subjourneyId a owl:ObjectProperty ;
rdfs:domain ujggraph:CompositeState ;
rdfs:range ujggraph:Journey .
ujggraph:outgoingTransitionRefs a owl:ObjectProperty ;
rdfs:domain [
a owl:Class ;
owl:unionOf (
ujggraph:State
ujggraph:OutgoingTransitionGroup
)
] ;
rdfs:range ujggraph:OutgoingTransition ;
rdfs:label "outgoing transition refs" ;
rdfs:comment "References outgoing navigational affordances. When used on an OutgoingTransitionGroup, the references define a reusable group. When used on a non-composite State, the references define outgoing affordances available from that specific state." . 16. JSON-LD Context
The normative Graph JSON-LD context is defined below and is published at https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld. It provides the compact JSON-LD term mappings for the Graph vocabulary used by the examples on this page.
{
"@context": {
"@version": 1.1,
"ujggraph": "https://ujg.specs.openuji.org/ed/ns/graph#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"Journey": "ujggraph:Journey",
"JourneyEntry": "ujggraph:JourneyEntry",
"JourneyEntryIndex": "ujggraph:JourneyEntryIndex",
"LocalVertex": "ujggraph:LocalVertex",
"State": "ujggraph:State",
"CompositeState": "ujggraph:CompositeState",
"Transition": "ujggraph:Transition",
"JourneyExit": "ujggraph:JourneyExit",
"OutgoingTransition": "ujggraph:OutgoingTransition",
"OutgoingTransitionGroup": "ujggraph:OutgoingTransitionGroup",
"label": "ujggraph:label",
"tags": {
"@id": "ujggraph:tags",
"@container": "@set"
},
"defaultEntryRef": {
"@id": "ujggraph:defaultEntryRef",
"@type": "@id"
},
"entryRefs": {
"@id": "ujggraph:entryRefs",
"@type": "@id",
"@container": "@set"
},
"stateRef": {
"@id": "ujggraph:stateRef",
"@type": "@id"
},
"stateRefs": {
"@id": "ujggraph:stateRefs",
"@type": "@id",
"@container": "@set"
},
"transitionRefs": {
"@id": "ujggraph:transitionRefs",
"@type": "@id",
"@container": "@set"
},
"exitRefs": {
"@id": "ujggraph:exitRefs",
"@type": "@id",
"@container": "@set"
},
"outgoingTransitionGroupRefs": {
"@id": "ujggraph:outgoingTransitionGroupRefs",
"@type": "@id",
"@container": "@set"
},
"from": {
"@id": "ujggraph:from",
"@type": "@id"
},
"to": {
"@id": "ujggraph:to",
"@type": "@id"
},
"toCurrentState": {
"@id": "ujggraph:toCurrentState",
"@type": "xsd:boolean"
},
"fromExitRef": {
"@id": "ujggraph:fromExitRef",
"@type": "@id"
},
"toEntryRef": {
"@id": "ujggraph:toEntryRef",
"@type": "@id"
},
"subjourneyId": {
"@id": "ujggraph:subjourneyId",
"@type": "@id"
},
"outgoingTransitionRefs": {
"@id": "ujggraph:outgoingTransitionRefs",
"@type": "@id",
"@container": "@set"
}
}
} {
"@context": {
"@version": 1.1,
"ujggraph": "https://ujg.specs.openuji.org/ed/ns/graph#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"Journey": "ujggraph:Journey",
"JourneyEntry": "ujggraph:JourneyEntry",
"JourneyEntryIndex": "ujggraph:JourneyEntryIndex",
"LocalVertex": "ujggraph:LocalVertex",
"State": "ujggraph:State",
"CompositeState": "ujggraph:CompositeState",
"Transition": "ujggraph:Transition",
"JourneyExit": "ujggraph:JourneyExit",
"OutgoingTransition": "ujggraph:OutgoingTransition",
"OutgoingTransitionGroup": "ujggraph:OutgoingTransitionGroup",
"label": "ujggraph:label",
"tags": {
"@id": "ujggraph:tags",
"@container": "@set"
},
"defaultEntryRef": {
"@id": "ujggraph:defaultEntryRef",
"@type": "@id"
},
"entryRefs": {
"@id": "ujggraph:entryRefs",
"@type": "@id",
"@container": "@set"
},
"stateRef": {
"@id": "ujggraph:stateRef",
"@type": "@id"
},
"stateRefs": {
"@id": "ujggraph:stateRefs",
"@type": "@id",
"@container": "@set"
},
"transitionRefs": {
"@id": "ujggraph:transitionRefs",
"@type": "@id",
"@container": "@set"
},
"exitRefs": {
"@id": "ujggraph:exitRefs",
"@type": "@id",
"@container": "@set"
},
"outgoingTransitionGroupRefs": {
"@id": "ujggraph:outgoingTransitionGroupRefs",
"@type": "@id",
"@container": "@set"
},
"from": {
"@id": "ujggraph:from",
"@type": "@id"
},
"to": {
"@id": "ujggraph:to",
"@type": "@id"
},
"toCurrentState": {
"@id": "ujggraph:toCurrentState",
"@type": "xsd:boolean"
},
"fromExitRef": {
"@id": "ujggraph:fromExitRef",
"@type": "@id"
},
"toEntryRef": {
"@id": "ujggraph:toEntryRef",
"@type": "@id"
},
"subjourneyId": {
"@id": "ujggraph:subjourneyId",
"@type": "@id"
},
"outgoingTransitionRefs": {
"@id": "ujggraph:outgoingTransitionRefs",
"@type": "@id",
"@container": "@set"
}
}
} 17. Validation
The normative Graph SHACL shape is defined below and is published at https://ujg.specs.openuji.org/ed/ns/graph.shape. It is the authoritative validation artifact for Graph structural constraints.
@prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix ujggraphshape: <https://ujg.specs.openuji.org/ed/ns/graph.shape#> .
ujggraphshape:StateLikeShape a sh:NodeShape ;
sh:nodeKind sh:IRI ;
sh:or (
[ sh:class ujggraph:State ]
[ sh:class ujggraph:CompositeState ]
) .
ujggraphshape:LocalVertexShape a sh:NodeShape ;
sh:nodeKind sh:IRI ;
sh:or (
[ sh:class ujggraph:State ]
[ sh:class ujggraph:CompositeState ]
[ sh:class ujggraph:JourneyExit ]
) .
ujggraphshape:OutgoingTargetShape a sh:NodeShape ;
sh:nodeKind sh:IRI ;
sh:or (
[ sh:class ujggraph:State ]
[ sh:class ujggraph:CompositeState ]
) .
ujggraphshape:IndexStateShape a sh:NodeShape ;
sh:nodeKind sh:IRI ;
sh:or (
[ sh:class ujggraph:State ]
[ sh:class ujggraph:CompositeState ]
) .
ujggraphshape:JourneyShape a sh:NodeShape ;
sh:targetClass ujggraph:Journey ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:defaultEntryRef ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:class ujggraph:JourneyEntry ;
sh:nodeKind sh:IRI ;
] ;
sh:property [
sh:path ujggraph:entryRefs ;
sh:minCount 1 ;
sh:class ujggraph:JourneyEntry ;
sh:nodeKind sh:IRI ;
] ;
sh:property [
sh:path ujggraph:stateRefs ;
sh:minCount 1 ;
sh:node ujggraphshape:StateLikeShape ;
] ;
sh:property [
sh:path ujggraph:transitionRefs ;
sh:class ujggraph:Transition ;
sh:nodeKind sh:IRI ;
] ;
sh:property [
sh:path ujggraph:exitRefs ;
sh:class ujggraph:JourneyExit ;
sh:nodeKind sh:IRI ;
] ;
sh:property [
sh:path ujggraph:outgoingTransitionGroupRefs ;
sh:class ujggraph:OutgoingTransitionGroup ;
sh:nodeKind sh:IRI ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A journey's defaultEntryRef must be listed in the same journey's entryRefs." ;
sh:select """
SELECT $this ?entry
WHERE {
$this ujggraph:defaultEntryRef ?entry .
FILTER (NOT EXISTS {
$this ujggraph:entryRefs ?entry .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyEntry listed in entryRefs must have a stateRef listed in the same journey's stateRefs." ;
sh:select """
SELECT $this ?entry ?state
WHERE {
$this ujggraph:entryRefs ?entry .
?entry ujggraph:stateRef ?state .
FILTER (NOT EXISTS {
$this ujggraph:stateRefs ?state .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyEntry must be listed in the entryRefs of exactly one journey." ;
sh:select """
SELECT $this ?entry
WHERE {
$this ujggraph:entryRefs ?entry .
FILTER (
EXISTS {
?otherJourney ujggraph:entryRefs ?entry .
FILTER ($this != ?otherJourney)
}
|| NOT EXISTS {
?declaringJourney ujggraph:entryRefs ?entry .
}
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition in transitionRefs has a from value that is not listed in this journey's stateRefs." ;
sh:select """
SELECT $this ?transition ?from
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:from ?from .
FILTER (NOT EXISTS {
$this ujggraph:stateRefs ?from .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition in transitionRefs has a from value that references a JourneyExit." ;
sh:select """
SELECT $this ?transition ?from
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:from ?from .
?from a ujggraph:JourneyExit .
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition in transitionRefs has a to value that is not listed in this journey's stateRefs or exitRefs." ;
sh:select """
SELECT $this ?transition ?to
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:to ?to .
FILTER (
NOT EXISTS { $this ujggraph:stateRefs ?to . }
&& NOT EXISTS { $this ujggraph:exitRefs ?to . }
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyExit must be listed in the exitRefs of exactly one journey." ;
sh:select """
SELECT $this ?exit
WHERE {
$this ujggraph:exitRefs ?exit .
FILTER (
EXISTS {
?otherJourney ujggraph:exitRefs ?exit .
FILTER ($this != ?otherJourney)
}
|| NOT EXISTS {
?declaringJourney ujggraph:exitRefs ?exit .
}
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyExit used as Transition.to must be listed in the same enclosing journey's exitRefs." ;
sh:select """
SELECT $this ?transition ?exit
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:to ?exit .
?exit a ujggraph:JourneyExit .
FILTER (NOT EXISTS {
$this ujggraph:exitRefs ?exit .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition with fromExitRef must have a from value listed in the enclosing journey's stateRefs." ;
sh:select """
SELECT $this ?transition ?from
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:fromExitRef ?exit ;
ujggraph:from ?from .
FILTER (NOT EXISTS {
$this ujggraph:stateRefs ?from .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition with toEntryRef must have a to value listed in the enclosing journey's stateRefs." ;
sh:select """
SELECT $this ?transition ?to
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:toEntryRef ?entry ;
ujggraph:to ?to .
FILTER (NOT EXISTS {
$this ujggraph:stateRefs ?to .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition with toEntryRef must have a to value that references a CompositeState." ;
sh:select """
SELECT $this ?transition ?to
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:toEntryRef ?entry ;
ujggraph:to ?to .
FILTER (NOT EXISTS {
?to a ujggraph:CompositeState .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "The CompositeState used as to by a transition with toEntryRef must declare exactly one subjourneyId." ;
sh:select """
SELECT $this ?transition ?to
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:toEntryRef ?entry ;
ujggraph:to ?to .
FILTER (
NOT EXISTS { ?to ujggraph:subjourneyId ?subjourney . }
|| EXISTS {
?to ujggraph:subjourneyId ?subjourneyA, ?subjourneyB .
FILTER (?subjourneyA != ?subjourneyB)
}
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "The subjourneyId of the CompositeState used by a transition with toEntryRef must resolve to a Journey." ;
sh:select """
SELECT $this ?transition ?to ?subjourney
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:toEntryRef ?entry ;
ujggraph:to ?to .
?to ujggraph:subjourneyId ?subjourney .
FILTER (NOT EXISTS {
?subjourney a ujggraph:Journey .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "The toEntryRef of a parent transition must be listed in the entryRefs of the child journey referenced by the to CompositeState." ;
sh:select """
SELECT $this ?transition ?to ?subjourney ?entry
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:toEntryRef ?entry ;
ujggraph:to ?to .
?to ujggraph:subjourneyId ?subjourney .
FILTER (NOT EXISTS {
?subjourney ujggraph:entryRefs ?entry .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition with fromExitRef must have a from value that references a CompositeState." ;
sh:select """
SELECT $this ?transition ?from
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:fromExitRef ?exit ;
ujggraph:from ?from .
FILTER (NOT EXISTS {
?from a ujggraph:CompositeState .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "The CompositeState used as from by a transition with fromExitRef must declare exactly one subjourneyId." ;
sh:select """
SELECT $this ?transition ?from
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:fromExitRef ?exit ;
ujggraph:from ?from .
FILTER (
NOT EXISTS { ?from ujggraph:subjourneyId ?subjourney . }
|| EXISTS {
?from ujggraph:subjourneyId ?subjourneyA, ?subjourneyB .
FILTER (?subjourneyA != ?subjourneyB)
}
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "The subjourneyId of the CompositeState used by a transition with fromExitRef must resolve to a Journey." ;
sh:select """
SELECT $this ?transition ?from ?subjourney
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:fromExitRef ?exit ;
ujggraph:from ?from .
?from ujggraph:subjourneyId ?subjourney .
FILTER (NOT EXISTS {
?subjourney a ujggraph:Journey .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "The fromExitRef of a parent transition must be listed in the exitRefs of the child journey referenced by the from CompositeState." ;
sh:select """
SELECT $this ?transition ?from ?subjourney ?exit
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:fromExitRef ?exit ;
ujggraph:from ?from .
?from ujggraph:subjourneyId ?subjourney .
FILTER (NOT EXISTS {
?subjourney ujggraph:exitRefs ?exit .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition with fromExitRef must have a to value listed in the enclosing journey's stateRefs or exitRefs." ;
sh:select """
SELECT $this ?transition ?to
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:fromExitRef ?exit ;
ujggraph:to ?to .
FILTER (
NOT EXISTS { $this ujggraph:stateRefs ?to . }
&& NOT EXISTS { $this ujggraph:exitRefs ?to . }
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A journey must not contain more than one transition with the same from value and the same fromExitRef value." ;
sh:select """
SELECT $this ?from ?exit ?transition ?otherTransition
WHERE {
$this ujggraph:transitionRefs ?transition, ?otherTransition .
?transition ujggraph:from ?from ;
ujggraph:fromExitRef ?exit .
?otherTransition ujggraph:from ?from ;
ujggraph:fromExitRef ?exit .
FILTER (?transition != ?otherTransition)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:severity sh:Warning ;
sh:message "A state in Journey.stateRefs is not an entry state or a local transition endpoint. Verify that it belongs to the journey's experiential local topology and is not only a linked destination." ;
sh:select """
SELECT $this ?state
WHERE {
$this ujggraph:stateRefs ?state .
FILTER (NOT EXISTS {
$this ujggraph:entryRefs ?entry .
?entry ujggraph:stateRef ?state .
})
FILTER (NOT EXISTS {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:from ?state .
})
FILTER (NOT EXISTS {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:to ?state .
})
}
""" ;
] .
ujggraphshape:JourneyEntryShape a sh:NodeShape ;
sh:targetClass ujggraph:JourneyEntry ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:stateRef ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:node ujggraphshape:StateLikeShape ;
] ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:tags ;
sh:datatype xsd:string ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyEntry must be listed in the entryRefs of exactly one journey." ;
sh:select """
SELECT $this
WHERE {
FILTER (
NOT EXISTS { ?journey ujggraph:entryRefs $this . }
|| EXISTS {
?journeyA ujggraph:entryRefs $this .
?journeyB ujggraph:entryRefs $this .
FILTER (?journeyA != ?journeyB)
}
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyEntry must not be used as the from or to value of a Transition." ;
sh:select """
SELECT $this ?transition
WHERE {
{
?transition ujggraph:from $this .
}
UNION
{
?transition ujggraph:to $this .
}
}
""" ;
] .
ujggraphshape:JourneyEntryIndexShape a sh:NodeShape ;
sh:targetClass ujggraph:JourneyEntryIndex ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:stateRefs ;
sh:minCount 1 ;
sh:node ujggraphshape:IndexStateShape ;
] ;
sh:property [
sh:path ujggraph:transitionRefs ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare transitionRefs." ;
] ;
sh:property [
sh:path ujggraph:exitRefs ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare exitRefs." ;
] ;
sh:property [
sh:path ujggraph:outgoingTransitionGroupRefs ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare outgoingTransitionGroupRefs." ;
] ;
sh:property [
sh:path ujggraph:from ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare from." ;
] ;
sh:property [
sh:path ujggraph:to ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare to." ;
] ;
sh:property [
sh:path ujggraph:fromExitRef ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare fromExitRef." ;
] ;
sh:property [
sh:path ujggraph:toEntryRef ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare toEntryRef." ;
] ;
sh:property [
sh:path ujggraph:defaultEntryRef ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare defaultEntryRef." ;
] ;
sh:property [
sh:path ujggraph:entryRefs ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare entryRefs." ;
] ;
sh:property [
sh:path ujggraph:stateRef ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare stateRef." ;
] ;
sh:property [
sh:path ujggraph:subjourneyId ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare subjourneyId." ;
] ;
sh:property [
sh:path ujggraph:outgoingTransitionRefs ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare outgoingTransitionRefs." ;
] .
ujggraphshape:StateShape a sh:NodeShape ;
sh:targetClass ujggraph:State ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:tags ;
sh:datatype xsd:string ;
] ;
sh:property [
sh:path ujggraph:outgoingTransitionRefs ;
sh:class ujggraph:OutgoingTransition ;
sh:nodeKind sh:IRI ;
] .
ujggraphshape:CompositeStateShape a sh:NodeShape ;
sh:targetClass ujggraph:CompositeState ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:tags ;
sh:datatype xsd:string ;
] ;
sh:property [
sh:path ujggraph:stateRefs ;
sh:maxCount 0 ;
sh:message "A CompositeState must not use stateRefs; use subjourneyId to reference a Journey." ;
] ;
sh:property [
sh:path ujggraph:outgoingTransitionRefs ;
sh:maxCount 0 ;
sh:message "A CompositeState must not declare outgoingTransitionRefs. Use an OutgoingTransitionGroup on the enclosing Journey, or attach outgoingTransitionRefs to concrete states inside the referenced subjourney." ;
] ;
sh:property [
sh:path ujggraph:subjourneyId ;
sh:class ujggraph:Journey ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] .
ujggraphshape:TransitionShape a sh:NodeShape ;
sh:targetClass ujggraph:Transition ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:from ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:node ujggraphshape:StateLikeShape ;
] ;
sh:property [
sh:path ujggraph:to ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:node ujggraphshape:LocalVertexShape ;
] ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:fromExitRef ;
sh:class ujggraph:JourneyExit ;
sh:nodeKind sh:IRI ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:toEntryRef ;
sh:class ujggraph:JourneyEntry ;
sh:nodeKind sh:IRI ;
sh:maxCount 1 ;
] .
ujggraphshape:JourneyExitShape a sh:NodeShape ;
sh:targetClass ujggraph:JourneyExit ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:tags ;
sh:datatype xsd:string ;
] ;
sh:property [
sh:path ujggraph:outgoingTransitionRefs ;
sh:maxCount 0 ;
sh:message "A JourneyExit must not declare outgoingTransitionRefs." ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyExit must not also be typed State." ;
sh:select """
SELECT $this
WHERE {
$this a ujggraph:State .
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyExit must be listed in the exitRefs of exactly one journey." ;
sh:select """
SELECT $this
WHERE {
FILTER (
NOT EXISTS { ?journey ujggraph:exitRefs $this . }
|| EXISTS {
?journeyA ujggraph:exitRefs $this .
?journeyB ujggraph:exitRefs $this .
FILTER (?journeyA != ?journeyB)
}
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyExit must not be used as the from value of a Transition." ;
sh:select """
SELECT $this ?transition
WHERE {
?transition ujggraph:from $this .
}
""" ;
] .
ujggraphshape:OutgoingTransitionShape a sh:NodeShape ;
sh:targetClass ujggraph:OutgoingTransition ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:to ;
sh:maxCount 1 ;
sh:node ujggraphshape:OutgoingTargetShape ;
] ;
sh:property [
sh:path ujggraph:toCurrentState ;
sh:datatype xsd:boolean ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "An OutgoingTransition must declare exactly one effective target mechanism: either exactly one to value or toCurrentState true." ;
sh:select """
SELECT $this
WHERE {
FILTER (
NOT EXISTS { $this ujggraph:to ?to . }
&& NOT EXISTS { $this ujggraph:toCurrentState true . }
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "An OutgoingTransition with toCurrentState true must not also declare to." ;
sh:select """
SELECT $this ?to
WHERE {
$this ujggraph:to ?to ;
ujggraph:toCurrentState true .
}
""" ;
] .
ujggraphshape:OutgoingTransitionGroupShape a sh:NodeShape ;
sh:targetClass ujggraph:OutgoingTransitionGroup ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:outgoingTransitionRefs ;
sh:class ujggraph:OutgoingTransition ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
] . @prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix ujggraphshape: <https://ujg.specs.openuji.org/ed/ns/graph.shape#> .
ujggraphshape:StateLikeShape a sh:NodeShape ;
sh:nodeKind sh:IRI ;
sh:or (
[ sh:class ujggraph:State ]
[ sh:class ujggraph:CompositeState ]
) .
ujggraphshape:LocalVertexShape a sh:NodeShape ;
sh:nodeKind sh:IRI ;
sh:or (
[ sh:class ujggraph:State ]
[ sh:class ujggraph:CompositeState ]
[ sh:class ujggraph:JourneyExit ]
) .
ujggraphshape:OutgoingTargetShape a sh:NodeShape ;
sh:nodeKind sh:IRI ;
sh:or (
[ sh:class ujggraph:State ]
[ sh:class ujggraph:CompositeState ]
) .
ujggraphshape:IndexStateShape a sh:NodeShape ;
sh:nodeKind sh:IRI ;
sh:or (
[ sh:class ujggraph:State ]
[ sh:class ujggraph:CompositeState ]
) .
ujggraphshape:JourneyShape a sh:NodeShape ;
sh:targetClass ujggraph:Journey ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:defaultEntryRef ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:class ujggraph:JourneyEntry ;
sh:nodeKind sh:IRI ;
] ;
sh:property [
sh:path ujggraph:entryRefs ;
sh:minCount 1 ;
sh:class ujggraph:JourneyEntry ;
sh:nodeKind sh:IRI ;
] ;
sh:property [
sh:path ujggraph:stateRefs ;
sh:minCount 1 ;
sh:node ujggraphshape:StateLikeShape ;
] ;
sh:property [
sh:path ujggraph:transitionRefs ;
sh:class ujggraph:Transition ;
sh:nodeKind sh:IRI ;
] ;
sh:property [
sh:path ujggraph:exitRefs ;
sh:class ujggraph:JourneyExit ;
sh:nodeKind sh:IRI ;
] ;
sh:property [
sh:path ujggraph:outgoingTransitionGroupRefs ;
sh:class ujggraph:OutgoingTransitionGroup ;
sh:nodeKind sh:IRI ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A journey's defaultEntryRef must be listed in the same journey's entryRefs." ;
sh:select """
SELECT $this ?entry
WHERE {
$this ujggraph:defaultEntryRef ?entry .
FILTER (NOT EXISTS {
$this ujggraph:entryRefs ?entry .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyEntry listed in entryRefs must have a stateRef listed in the same journey's stateRefs." ;
sh:select """
SELECT $this ?entry ?state
WHERE {
$this ujggraph:entryRefs ?entry .
?entry ujggraph:stateRef ?state .
FILTER (NOT EXISTS {
$this ujggraph:stateRefs ?state .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyEntry must be listed in the entryRefs of exactly one journey." ;
sh:select """
SELECT $this ?entry
WHERE {
$this ujggraph:entryRefs ?entry .
FILTER (
EXISTS {
?otherJourney ujggraph:entryRefs ?entry .
FILTER ($this != ?otherJourney)
}
|| NOT EXISTS {
?declaringJourney ujggraph:entryRefs ?entry .
}
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition in transitionRefs has a from value that is not listed in this journey's stateRefs." ;
sh:select """
SELECT $this ?transition ?from
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:from ?from .
FILTER (NOT EXISTS {
$this ujggraph:stateRefs ?from .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition in transitionRefs has a from value that references a JourneyExit." ;
sh:select """
SELECT $this ?transition ?from
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:from ?from .
?from a ujggraph:JourneyExit .
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition in transitionRefs has a to value that is not listed in this journey's stateRefs or exitRefs." ;
sh:select """
SELECT $this ?transition ?to
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:to ?to .
FILTER (
NOT EXISTS { $this ujggraph:stateRefs ?to . }
&& NOT EXISTS { $this ujggraph:exitRefs ?to . }
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyExit must be listed in the exitRefs of exactly one journey." ;
sh:select """
SELECT $this ?exit
WHERE {
$this ujggraph:exitRefs ?exit .
FILTER (
EXISTS {
?otherJourney ujggraph:exitRefs ?exit .
FILTER ($this != ?otherJourney)
}
|| NOT EXISTS {
?declaringJourney ujggraph:exitRefs ?exit .
}
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyExit used as Transition.to must be listed in the same enclosing journey's exitRefs." ;
sh:select """
SELECT $this ?transition ?exit
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:to ?exit .
?exit a ujggraph:JourneyExit .
FILTER (NOT EXISTS {
$this ujggraph:exitRefs ?exit .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition with fromExitRef must have a from value listed in the enclosing journey's stateRefs." ;
sh:select """
SELECT $this ?transition ?from
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:fromExitRef ?exit ;
ujggraph:from ?from .
FILTER (NOT EXISTS {
$this ujggraph:stateRefs ?from .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition with toEntryRef must have a to value listed in the enclosing journey's stateRefs." ;
sh:select """
SELECT $this ?transition ?to
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:toEntryRef ?entry ;
ujggraph:to ?to .
FILTER (NOT EXISTS {
$this ujggraph:stateRefs ?to .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition with toEntryRef must have a to value that references a CompositeState." ;
sh:select """
SELECT $this ?transition ?to
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:toEntryRef ?entry ;
ujggraph:to ?to .
FILTER (NOT EXISTS {
?to a ujggraph:CompositeState .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "The CompositeState used as to by a transition with toEntryRef must declare exactly one subjourneyId." ;
sh:select """
SELECT $this ?transition ?to
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:toEntryRef ?entry ;
ujggraph:to ?to .
FILTER (
NOT EXISTS { ?to ujggraph:subjourneyId ?subjourney . }
|| EXISTS {
?to ujggraph:subjourneyId ?subjourneyA, ?subjourneyB .
FILTER (?subjourneyA != ?subjourneyB)
}
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "The subjourneyId of the CompositeState used by a transition with toEntryRef must resolve to a Journey." ;
sh:select """
SELECT $this ?transition ?to ?subjourney
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:toEntryRef ?entry ;
ujggraph:to ?to .
?to ujggraph:subjourneyId ?subjourney .
FILTER (NOT EXISTS {
?subjourney a ujggraph:Journey .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "The toEntryRef of a parent transition must be listed in the entryRefs of the child journey referenced by the to CompositeState." ;
sh:select """
SELECT $this ?transition ?to ?subjourney ?entry
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:toEntryRef ?entry ;
ujggraph:to ?to .
?to ujggraph:subjourneyId ?subjourney .
FILTER (NOT EXISTS {
?subjourney ujggraph:entryRefs ?entry .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition with fromExitRef must have a from value that references a CompositeState." ;
sh:select """
SELECT $this ?transition ?from
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:fromExitRef ?exit ;
ujggraph:from ?from .
FILTER (NOT EXISTS {
?from a ujggraph:CompositeState .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "The CompositeState used as from by a transition with fromExitRef must declare exactly one subjourneyId." ;
sh:select """
SELECT $this ?transition ?from
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:fromExitRef ?exit ;
ujggraph:from ?from .
FILTER (
NOT EXISTS { ?from ujggraph:subjourneyId ?subjourney . }
|| EXISTS {
?from ujggraph:subjourneyId ?subjourneyA, ?subjourneyB .
FILTER (?subjourneyA != ?subjourneyB)
}
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "The subjourneyId of the CompositeState used by a transition with fromExitRef must resolve to a Journey." ;
sh:select """
SELECT $this ?transition ?from ?subjourney
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:fromExitRef ?exit ;
ujggraph:from ?from .
?from ujggraph:subjourneyId ?subjourney .
FILTER (NOT EXISTS {
?subjourney a ujggraph:Journey .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "The fromExitRef of a parent transition must be listed in the exitRefs of the child journey referenced by the from CompositeState." ;
sh:select """
SELECT $this ?transition ?from ?subjourney ?exit
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:fromExitRef ?exit ;
ujggraph:from ?from .
?from ujggraph:subjourneyId ?subjourney .
FILTER (NOT EXISTS {
?subjourney ujggraph:exitRefs ?exit .
})
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A transition with fromExitRef must have a to value listed in the enclosing journey's stateRefs or exitRefs." ;
sh:select """
SELECT $this ?transition ?to
WHERE {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:fromExitRef ?exit ;
ujggraph:to ?to .
FILTER (
NOT EXISTS { $this ujggraph:stateRefs ?to . }
&& NOT EXISTS { $this ujggraph:exitRefs ?to . }
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A journey must not contain more than one transition with the same from value and the same fromExitRef value." ;
sh:select """
SELECT $this ?from ?exit ?transition ?otherTransition
WHERE {
$this ujggraph:transitionRefs ?transition, ?otherTransition .
?transition ujggraph:from ?from ;
ujggraph:fromExitRef ?exit .
?otherTransition ujggraph:from ?from ;
ujggraph:fromExitRef ?exit .
FILTER (?transition != ?otherTransition)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:severity sh:Warning ;
sh:message "A state in Journey.stateRefs is not an entry state or a local transition endpoint. Verify that it belongs to the journey's experiential local topology and is not only a linked destination." ;
sh:select """
SELECT $this ?state
WHERE {
$this ujggraph:stateRefs ?state .
FILTER (NOT EXISTS {
$this ujggraph:entryRefs ?entry .
?entry ujggraph:stateRef ?state .
})
FILTER (NOT EXISTS {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:from ?state .
})
FILTER (NOT EXISTS {
$this ujggraph:transitionRefs ?transition .
?transition ujggraph:to ?state .
})
}
""" ;
] .
ujggraphshape:JourneyEntryShape a sh:NodeShape ;
sh:targetClass ujggraph:JourneyEntry ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:stateRef ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:node ujggraphshape:StateLikeShape ;
] ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:tags ;
sh:datatype xsd:string ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyEntry must be listed in the entryRefs of exactly one journey." ;
sh:select """
SELECT $this
WHERE {
FILTER (
NOT EXISTS { ?journey ujggraph:entryRefs $this . }
|| EXISTS {
?journeyA ujggraph:entryRefs $this .
?journeyB ujggraph:entryRefs $this .
FILTER (?journeyA != ?journeyB)
}
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyEntry must not be used as the from or to value of a Transition." ;
sh:select """
SELECT $this ?transition
WHERE {
{
?transition ujggraph:from $this .
}
UNION
{
?transition ujggraph:to $this .
}
}
""" ;
] .
ujggraphshape:JourneyEntryIndexShape a sh:NodeShape ;
sh:targetClass ujggraph:JourneyEntryIndex ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:stateRefs ;
sh:minCount 1 ;
sh:node ujggraphshape:IndexStateShape ;
] ;
sh:property [
sh:path ujggraph:transitionRefs ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare transitionRefs." ;
] ;
sh:property [
sh:path ujggraph:exitRefs ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare exitRefs." ;
] ;
sh:property [
sh:path ujggraph:outgoingTransitionGroupRefs ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare outgoingTransitionGroupRefs." ;
] ;
sh:property [
sh:path ujggraph:from ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare from." ;
] ;
sh:property [
sh:path ujggraph:to ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare to." ;
] ;
sh:property [
sh:path ujggraph:fromExitRef ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare fromExitRef." ;
] ;
sh:property [
sh:path ujggraph:toEntryRef ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare toEntryRef." ;
] ;
sh:property [
sh:path ujggraph:defaultEntryRef ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare defaultEntryRef." ;
] ;
sh:property [
sh:path ujggraph:entryRefs ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare entryRefs." ;
] ;
sh:property [
sh:path ujggraph:stateRef ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare stateRef." ;
] ;
sh:property [
sh:path ujggraph:subjourneyId ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare subjourneyId." ;
] ;
sh:property [
sh:path ujggraph:outgoingTransitionRefs ;
sh:maxCount 0 ;
sh:message "A JourneyEntryIndex must not declare outgoingTransitionRefs." ;
] .
ujggraphshape:StateShape a sh:NodeShape ;
sh:targetClass ujggraph:State ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:tags ;
sh:datatype xsd:string ;
] ;
sh:property [
sh:path ujggraph:outgoingTransitionRefs ;
sh:class ujggraph:OutgoingTransition ;
sh:nodeKind sh:IRI ;
] .
ujggraphshape:CompositeStateShape a sh:NodeShape ;
sh:targetClass ujggraph:CompositeState ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:tags ;
sh:datatype xsd:string ;
] ;
sh:property [
sh:path ujggraph:stateRefs ;
sh:maxCount 0 ;
sh:message "A CompositeState must not use stateRefs; use subjourneyId to reference a Journey." ;
] ;
sh:property [
sh:path ujggraph:outgoingTransitionRefs ;
sh:maxCount 0 ;
sh:message "A CompositeState must not declare outgoingTransitionRefs. Use an OutgoingTransitionGroup on the enclosing Journey, or attach outgoingTransitionRefs to concrete states inside the referenced subjourney." ;
] ;
sh:property [
sh:path ujggraph:subjourneyId ;
sh:class ujggraph:Journey ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] .
ujggraphshape:TransitionShape a sh:NodeShape ;
sh:targetClass ujggraph:Transition ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:from ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:node ujggraphshape:StateLikeShape ;
] ;
sh:property [
sh:path ujggraph:to ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:node ujggraphshape:LocalVertexShape ;
] ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:fromExitRef ;
sh:class ujggraph:JourneyExit ;
sh:nodeKind sh:IRI ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:toEntryRef ;
sh:class ujggraph:JourneyEntry ;
sh:nodeKind sh:IRI ;
sh:maxCount 1 ;
] .
ujggraphshape:JourneyExitShape a sh:NodeShape ;
sh:targetClass ujggraph:JourneyExit ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:tags ;
sh:datatype xsd:string ;
] ;
sh:property [
sh:path ujggraph:outgoingTransitionRefs ;
sh:maxCount 0 ;
sh:message "A JourneyExit must not declare outgoingTransitionRefs." ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyExit must not also be typed State." ;
sh:select """
SELECT $this
WHERE {
$this a ujggraph:State .
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyExit must be listed in the exitRefs of exactly one journey." ;
sh:select """
SELECT $this
WHERE {
FILTER (
NOT EXISTS { ?journey ujggraph:exitRefs $this . }
|| EXISTS {
?journeyA ujggraph:exitRefs $this .
?journeyB ujggraph:exitRefs $this .
FILTER (?journeyA != ?journeyB)
}
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A JourneyExit must not be used as the from value of a Transition." ;
sh:select """
SELECT $this ?transition
WHERE {
?transition ujggraph:from $this .
}
""" ;
] .
ujggraphshape:OutgoingTransitionShape a sh:NodeShape ;
sh:targetClass ujggraph:OutgoingTransition ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:to ;
sh:maxCount 1 ;
sh:node ujggraphshape:OutgoingTargetShape ;
] ;
sh:property [
sh:path ujggraph:toCurrentState ;
sh:datatype xsd:boolean ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "An OutgoingTransition must declare exactly one effective target mechanism: either exactly one to value or toCurrentState true." ;
sh:select """
SELECT $this
WHERE {
FILTER (
NOT EXISTS { $this ujggraph:to ?to . }
&& NOT EXISTS { $this ujggraph:toCurrentState true . }
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "An OutgoingTransition with toCurrentState true must not also declare to." ;
sh:select """
SELECT $this ?to
WHERE {
$this ujggraph:to ?to ;
ujggraph:toCurrentState true .
}
""" ;
] .
ujggraphshape:OutgoingTransitionGroupShape a sh:NodeShape ;
sh:targetClass ujggraph:OutgoingTransitionGroup ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:outgoingTransitionRefs ;
sh:class ujggraph:OutgoingTransition ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
] . 18. Graph Integrity and Resolution
The rules below define additional graph integrity and resolution behavior beyond the structural constraints captured by the SHACL shape.
To ensure graph integrity, the following constraints MUST be met:
Reference Integrity: All
defaultEntryRef,entryRefs,stateRef,stateRefs,transitionRefs,exitRefs,outgoingTransitionGroupRefs, andoutgoingTransitionRefsIDs MUST resolve to valid Nodes within the current scope or imported modules.Transition Endpoint Resolution: The
fromID of a Transition MUST resolve to a State or CompositeState listed in the enclosing Journey'sstateRefs. ThetoID of a Transition MUST resolve to a State or CompositeState listed in the enclosing Journey'sstateRefs, or a JourneyExit listed in the enclosing Journey'sexitRefs. A transition MUST NOT reference local vertices belonging to other journeys.Entry Resolution: Every ID in
entryRefsMUST resolve to a JourneyEntry, and each JourneyEntry'sstateRefMUST resolve to a State or CompositeState listed in the same Journey'sstateRefs.Composition Safety:
subjourneyIdMUST resolve to a valid Journey.Boundary Mapping Resolution: A
toEntryRefvalue MUST resolve to a JourneyEntry of the child journey referenced by the transition'stoCompositeState, and afromExitRefvalue MUST resolve to a JourneyExit of the child journey referenced by the transition'sfromCompositeState.Journey Exit Resolution: Every ID in
exitRefsMUST resolve to a JourneyExit.Group Resolution: Every ID in
outgoingTransitionGroupRefsMUST resolve to an OutgoingTransitionGroup.Outgoing Resolution: Every ID in
outgoingTransitionRefsMUST resolve to an OutgoingTransition.Outgoing Target Resolution: Each OutgoingTransition MUST resolve through exactly one effective target mechanism: a fixed
totarget, ortoCurrentState: trueresolved at the current effective source where the outgoing affordance is available.
19. Examples
19.1. JourneyEntryIndex with External Outgoing Target
This example lists known page entries in a JourneyEntryIndex. The search page journey has a state-scoped OutgoingTransition to the profile page entry, but the profile page is not part of the search page journey's local stateRefs.
{
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld"
],
"@id": "https://example.com/ujg/graph/page-index.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "JourneyEntryIndex",
"@id": "urn:ujg:index:pages",
"label": "Page index",
"stateRefs": [
"urn:ujg:state:search-page",
"urn:ujg:state:profile-page"
]
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:search-page",
"label": "Search page",
"subjourneyId": "urn:ujg:journey:search-page"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:profile-page",
"label": "Profile page",
"subjourneyId": "urn:ujg:journey:profile-page"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:search-page",
"label": "Search page journey",
"defaultEntryRef": "urn:ujg:entry:search-page-default",
"entryRefs": [
"urn:ujg:entry:search-page-default"
],
"stateRefs": [
"urn:ujg:state:search-form",
"urn:ujg:state:search-results"
],
"transitionRefs": [
"urn:ujg:transition:search-form-to-results"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:search-page-default",
"stateRef": "urn:ujg:state:search-form"
},
{
"@type": "State",
"@id": "urn:ujg:state:search-form",
"label": "Search form"
},
{
"@type": "State",
"@id": "urn:ujg:state:search-results",
"label": "Search results",
"outgoingTransitionRefs": [
"urn:ujg:ot:search-results-to-profile"
]
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:search-form-to-results",
"label": "Submit search",
"from": "urn:ujg:state:search-form",
"to": "urn:ujg:state:search-results"
},
{
"@type": "OutgoingTransition",
"@id": "urn:ujg:ot:search-results-to-profile",
"label": "Open profile",
"to": "urn:ujg:state:profile-page"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:profile-page",
"label": "Profile page journey",
"defaultEntryRef": "urn:ujg:entry:profile-page-default",
"entryRefs": [
"urn:ujg:entry:profile-page-default"
],
"stateRefs": [
"urn:ujg:state:profile-summary"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:profile-page-default",
"stateRef": "urn:ujg:state:profile-summary"
},
{
"@type": "State",
"@id": "urn:ujg:state:profile-summary",
"label": "Profile summary"
}
]
} {
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld"
],
"@id": "https://example.com/ujg/graph/page-index.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "JourneyEntryIndex",
"@id": "urn:ujg:index:pages",
"label": "Page index",
"stateRefs": [
"urn:ujg:state:search-page",
"urn:ujg:state:profile-page"
]
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:search-page",
"label": "Search page",
"subjourneyId": "urn:ujg:journey:search-page"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:profile-page",
"label": "Profile page",
"subjourneyId": "urn:ujg:journey:profile-page"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:search-page",
"label": "Search page journey",
"defaultEntryRef": "urn:ujg:entry:search-page-default",
"entryRefs": [
"urn:ujg:entry:search-page-default"
],
"stateRefs": [
"urn:ujg:state:search-form",
"urn:ujg:state:search-results"
],
"transitionRefs": [
"urn:ujg:transition:search-form-to-results"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:search-page-default",
"stateRef": "urn:ujg:state:search-form"
},
{
"@type": "State",
"@id": "urn:ujg:state:search-form",
"label": "Search form"
},
{
"@type": "State",
"@id": "urn:ujg:state:search-results",
"label": "Search results",
"outgoingTransitionRefs": [
"urn:ujg:ot:search-results-to-profile"
]
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:search-form-to-results",
"label": "Submit search",
"from": "urn:ujg:state:search-form",
"to": "urn:ujg:state:search-results"
},
{
"@type": "OutgoingTransition",
"@id": "urn:ujg:ot:search-results-to-profile",
"label": "Open profile",
"to": "urn:ujg:state:profile-page"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:profile-page",
"label": "Profile page journey",
"defaultEntryRef": "urn:ujg:entry:profile-page-default",
"entryRefs": [
"urn:ujg:entry:profile-page-default"
],
"stateRefs": [
"urn:ujg:state:profile-summary"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:profile-page-default",
"stateRef": "urn:ujg:state:profile-summary"
},
{
"@type": "State",
"@id": "urn:ujg:state:profile-summary",
"label": "Profile summary"
}
]
} 19.2. Form Child Journey with Parent Continuation
This example models a form as a child journey. The form exports submitted through a JourneyExit. The enclosing journey continues from the form CompositeState to a result-page CompositeState using fromExitRef.
{
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld"
],
"@id": "https://example.com/ujg/graph/form-continuation.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "JourneyEntryIndex",
"@id": "urn:ujg:index:form-example-pages",
"label": "Form example pages",
"stateRefs": [
"urn:ujg:state:contact-page",
"urn:ujg:state:result-page"
]
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:contact-page",
"label": "Contact page journey",
"defaultEntryRef": "urn:ujg:entry:contact-page-default",
"entryRefs": [
"urn:ujg:entry:contact-page-default"
],
"stateRefs": [
"urn:ujg:state:contact-form",
"urn:ujg:state:result-page"
],
"transitionRefs": [
"urn:ujg:transition:contact-form-to-result"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:contact-page-default",
"stateRef": "urn:ujg:state:contact-form"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:contact-page",
"label": "Contact page",
"subjourneyId": "urn:ujg:journey:contact-page"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:contact-form",
"label": "Contact form",
"subjourneyId": "urn:ujg:journey:contact-form"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:result-page",
"label": "Result page",
"subjourneyId": "urn:ujg:journey:result-page"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:contact-form-to-result",
"label": "Show result page",
"from": "urn:ujg:state:contact-form",
"to": "urn:ujg:state:result-page",
"fromExitRef": "urn:ujg:exit:contact-form-submitted"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:contact-form",
"label": "Contact form journey",
"defaultEntryRef": "urn:ujg:entry:contact-form-default",
"entryRefs": [
"urn:ujg:entry:contact-form-default"
],
"stateRefs": [
"urn:ujg:state:contact-form-editing"
],
"transitionRefs": [
"urn:ujg:transition:contact-form-submit"
],
"exitRefs": [
"urn:ujg:exit:contact-form-submitted"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:contact-form-default",
"stateRef": "urn:ujg:state:contact-form-editing"
},
{
"@type": "State",
"@id": "urn:ujg:state:contact-form-editing",
"label": "Contact form editing"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:contact-form-submit",
"label": "Submit form",
"from": "urn:ujg:state:contact-form-editing",
"to": "urn:ujg:exit:contact-form-submitted"
},
{
"@type": "JourneyExit",
"@id": "urn:ujg:exit:contact-form-submitted",
"label": "Submitted"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:result-page",
"label": "Result page journey",
"defaultEntryRef": "urn:ujg:entry:result-page-default",
"entryRefs": [
"urn:ujg:entry:result-page-default"
],
"stateRefs": [
"urn:ujg:state:result-summary"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:result-page-default",
"stateRef": "urn:ujg:state:result-summary"
},
{
"@type": "State",
"@id": "urn:ujg:state:result-summary",
"label": "Result summary"
}
]
} {
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld"
],
"@id": "https://example.com/ujg/graph/form-continuation.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "JourneyEntryIndex",
"@id": "urn:ujg:index:form-example-pages",
"label": "Form example pages",
"stateRefs": [
"urn:ujg:state:contact-page",
"urn:ujg:state:result-page"
]
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:contact-page",
"label": "Contact page journey",
"defaultEntryRef": "urn:ujg:entry:contact-page-default",
"entryRefs": [
"urn:ujg:entry:contact-page-default"
],
"stateRefs": [
"urn:ujg:state:contact-form",
"urn:ujg:state:result-page"
],
"transitionRefs": [
"urn:ujg:transition:contact-form-to-result"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:contact-page-default",
"stateRef": "urn:ujg:state:contact-form"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:contact-page",
"label": "Contact page",
"subjourneyId": "urn:ujg:journey:contact-page"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:contact-form",
"label": "Contact form",
"subjourneyId": "urn:ujg:journey:contact-form"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:result-page",
"label": "Result page",
"subjourneyId": "urn:ujg:journey:result-page"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:contact-form-to-result",
"label": "Show result page",
"from": "urn:ujg:state:contact-form",
"to": "urn:ujg:state:result-page",
"fromExitRef": "urn:ujg:exit:contact-form-submitted"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:contact-form",
"label": "Contact form journey",
"defaultEntryRef": "urn:ujg:entry:contact-form-default",
"entryRefs": [
"urn:ujg:entry:contact-form-default"
],
"stateRefs": [
"urn:ujg:state:contact-form-editing"
],
"transitionRefs": [
"urn:ujg:transition:contact-form-submit"
],
"exitRefs": [
"urn:ujg:exit:contact-form-submitted"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:contact-form-default",
"stateRef": "urn:ujg:state:contact-form-editing"
},
{
"@type": "State",
"@id": "urn:ujg:state:contact-form-editing",
"label": "Contact form editing"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:contact-form-submit",
"label": "Submit form",
"from": "urn:ujg:state:contact-form-editing",
"to": "urn:ujg:exit:contact-form-submitted"
},
{
"@type": "JourneyExit",
"@id": "urn:ujg:exit:contact-form-submitted",
"label": "Submitted"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:result-page",
"label": "Result page journey",
"defaultEntryRef": "urn:ujg:entry:result-page-default",
"entryRefs": [
"urn:ujg:entry:result-page-default"
],
"stateRefs": [
"urn:ujg:state:result-summary"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:result-page-default",
"stateRef": "urn:ujg:state:result-summary"
},
{
"@type": "State",
"@id": "urn:ujg:state:result-summary",
"label": "Result summary"
}
]
} 19.3. Anti-Example: Linked Destination in Source Journey
The following source journey incorrectly lists urn:ujg:state:profile-page in stateRefs merely because an outgoing affordance can reach it. The profile page should be listed in a JourneyEntryIndex and referenced by OutgoingTransition.to; it should not be promoted into the search page journey's local topology.
[
{
"@type": "Journey",
"@id": "urn:ujg:journey:search-page",
"label": "Search page journey",
"defaultEntryRef": "urn:ujg:entry:search-page-default",
"entryRefs": [
"urn:ujg:entry:search-page-default"
],
"stateRefs": [
"urn:ujg:state:search-form",
"urn:ujg:state:search-results",
"urn:ujg:state:profile-page"
],
"transitionRefs": [
"urn:ujg:transition:search-form-to-results"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:search-page-default",
"stateRef": "urn:ujg:state:search-form"
}
] [
{
"@type": "Journey",
"@id": "urn:ujg:journey:search-page",
"label": "Search page journey",
"defaultEntryRef": "urn:ujg:entry:search-page-default",
"entryRefs": [
"urn:ujg:entry:search-page-default"
],
"stateRefs": [
"urn:ujg:state:search-form",
"urn:ujg:state:search-results",
"urn:ujg:state:profile-page"
],
"transitionRefs": [
"urn:ujg:transition:search-form-to-results"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:search-page-default",
"stateRef": "urn:ujg:state:search-form"
}
] 19.4. Combined JSON Example
{
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld"
],
"@id": "https://example.com/ujg/graph/main-site.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "JourneyEntryIndex",
"@id": "urn:ujg:index:main-site-pages",
"label": "Main site pages",
"stateRefs": [
"urn:ujg:state:home-page",
"urn:ujg:state:checkout-flow",
"urn:ujg:state:profile-page"
]
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:home-page",
"label": "Home page",
"subjourneyId": "urn:ujg:journey:home-page"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:checkout-flow",
"label": "Checkout process",
"subjourneyId": "urn:ujg:journey:checkout"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:profile-page",
"label": "Profile page",
"subjourneyId": "urn:ujg:journey:profile"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:home-page",
"label": "Home page journey",
"defaultEntryRef": "urn:ujg:entry:home-page-default",
"entryRefs": [
"urn:ujg:entry:home-page-default"
],
"stateRefs": [
"urn:ujg:state:home",
"urn:ujg:state:checkout-flow"
],
"transitionRefs": [
"urn:ujg:transition:home-to-checkout"
],
"outgoingTransitionGroupRefs": [
"urn:ujg:otg:global-header"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:home-page-default",
"stateRef": "urn:ujg:state:home"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:home-to-checkout",
"from": "urn:ujg:state:home",
"to": "urn:ujg:state:checkout-flow",
"label": "Buy Now"
},
{
"@type": "State",
"@id": "urn:ujg:state:home",
"label": "Home Page",
"tags": [
"phase:landing"
]
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:checkout",
"label": "Checkout journey",
"defaultEntryRef": "urn:ujg:entry:checkout-default",
"entryRefs": [
"urn:ujg:entry:checkout-default"
],
"stateRefs": [
"urn:ujg:state:checkout-cart"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:checkout-default",
"stateRef": "urn:ujg:state:checkout-cart"
},
{
"@type": "State",
"@id": "urn:ujg:state:checkout-cart",
"label": "Checkout cart"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:profile",
"label": "Profile journey",
"defaultEntryRef": "urn:ujg:entry:profile-default",
"entryRefs": [
"urn:ujg:entry:profile-default"
],
"stateRefs": [
"urn:ujg:state:profile-summary"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:profile-default",
"stateRef": "urn:ujg:state:profile-summary"
},
{
"@type": "State",
"@id": "urn:ujg:state:profile-summary",
"label": "Profile summary"
},
{
"@type": "OutgoingTransition",
"@id": "urn:ujg:ot:go-home",
"to": "urn:ujg:state:home-page",
"label": "Home"
},
{
"@type": "OutgoingTransition",
"@id": "urn:ujg:ot:go-profile",
"to": "urn:ujg:state:profile-page",
"label": "Profile"
},
{
"@type": "OutgoingTransitionGroup",
"@id": "urn:ujg:otg:global-header",
"outgoingTransitionRefs": [
"urn:ujg:ot:go-home",
"urn:ujg:ot:go-profile"
]
}
]
} {
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld"
],
"@id": "https://example.com/ujg/graph/main-site.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "JourneyEntryIndex",
"@id": "urn:ujg:index:main-site-pages",
"label": "Main site pages",
"stateRefs": [
"urn:ujg:state:home-page",
"urn:ujg:state:checkout-flow",
"urn:ujg:state:profile-page"
]
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:home-page",
"label": "Home page",
"subjourneyId": "urn:ujg:journey:home-page"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:checkout-flow",
"label": "Checkout process",
"subjourneyId": "urn:ujg:journey:checkout"
},
{
"@type": "CompositeState",
"@id": "urn:ujg:state:profile-page",
"label": "Profile page",
"subjourneyId": "urn:ujg:journey:profile"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:home-page",
"label": "Home page journey",
"defaultEntryRef": "urn:ujg:entry:home-page-default",
"entryRefs": [
"urn:ujg:entry:home-page-default"
],
"stateRefs": [
"urn:ujg:state:home",
"urn:ujg:state:checkout-flow"
],
"transitionRefs": [
"urn:ujg:transition:home-to-checkout"
],
"outgoingTransitionGroupRefs": [
"urn:ujg:otg:global-header"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:home-page-default",
"stateRef": "urn:ujg:state:home"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:home-to-checkout",
"from": "urn:ujg:state:home",
"to": "urn:ujg:state:checkout-flow",
"label": "Buy Now"
},
{
"@type": "State",
"@id": "urn:ujg:state:home",
"label": "Home Page",
"tags": [
"phase:landing"
]
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:checkout",
"label": "Checkout journey",
"defaultEntryRef": "urn:ujg:entry:checkout-default",
"entryRefs": [
"urn:ujg:entry:checkout-default"
],
"stateRefs": [
"urn:ujg:state:checkout-cart"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:checkout-default",
"stateRef": "urn:ujg:state:checkout-cart"
},
{
"@type": "State",
"@id": "urn:ujg:state:checkout-cart",
"label": "Checkout cart"
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:profile",
"label": "Profile journey",
"defaultEntryRef": "urn:ujg:entry:profile-default",
"entryRefs": [
"urn:ujg:entry:profile-default"
],
"stateRefs": [
"urn:ujg:state:profile-summary"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:profile-default",
"stateRef": "urn:ujg:state:profile-summary"
},
{
"@type": "State",
"@id": "urn:ujg:state:profile-summary",
"label": "Profile summary"
},
{
"@type": "OutgoingTransition",
"@id": "urn:ujg:ot:go-home",
"to": "urn:ujg:state:home-page",
"label": "Home"
},
{
"@type": "OutgoingTransition",
"@id": "urn:ujg:ot:go-profile",
"to": "urn:ujg:state:profile-page",
"label": "Profile"
},
{
"@type": "OutgoingTransitionGroup",
"@id": "urn:ujg:otg:global-header",
"outgoingTransitionRefs": [
"urn:ujg:ot:go-home",
"urn:ujg:ot:go-profile"
]
}
]
}