# SHACL-C shape for component-interop manifests (the RDF view a JSON-LD
# processor produces from a manifest via ../context.jsonld).
# Canonical, human-readable serialization — manifest.shacl.ttl is the same
# shape in Turtle for tooling (rdf-validate-shacl); keep the two in lockstep.
#
# This file defines only the manifest ENVELOPE. The item-level shapes —
# ui:Component and ui:Link — are SHARED with menus and palette cards and live
# in sol-components' shapes/ui.shacl; validators load both files into one
# shapes graph and the shared shapes bind via sh:targetClass. An entry opts
# into that validation by carrying an explicit type ("@type": "ui:Component"
# or "ui:Link" — compact IRIs the published context already resolves).

BASE <https://jeff-zucker.github.io/component-interop/shapes/manifest>

PREFIX ci:     <https://jeff-zucker.github.io/component-interop/ns#>
PREFIX schema: <http://schema.org/>
PREFIX rdfs:   <http://www.w3.org/2000/01/rdf-schema#>

shape <#ManifestShape> -> ci:Manifest {
	schema:name xsd:string [1..1] .          # the library identity
	rdfs:seeAlso IRI [0..*] .                # optional custom-elements.json cross-link
	ci:component    @<#ManifestEntryShape> [0..*] .
	ci:sharedModule @<#SharedModuleShape>  [0..*] .
	ci:attribute    @<#AttributeShape>     [0..*] .
	ci:stage        @<#StageShape>         [0..*] .
	ci:provides     @<#ProvidesShape>      [0..*] .
	ci:consumes     @<#ConsumesShape>      [0..*] .
	ci:accepts      @<#AcceptsShape>       [0..*] .
}

# The manifest-side HALF of an entry's shape: only the loader plumbing
# (ci:module). Everything generic about an entry (schema:url, ui:label, ui:icon,
# schema:additionalProperty, descriptive metadata) lives in the shared shapes in
# sol-components' shapes/ui.shacl. Deliberately NO targetClass (ci:module
# must not constrain components outside a manifest) and no cross-file IRI
# references. An entry may be a ui:Component (its schema:url module filename
# names the tag) or a ui:Link (schema:url, no module to load).
shape <#ManifestEntryShape> {
	ci:module IRI [0..1] .                   # the ES module to import; absent when the node
	                                         # itself IS the module IRI (string-form entry)
	                                         # or when stages carry the URLs
}

shape <#SharedModuleShape> {
	schema:name xsd:string [1..1] .          # bare import specifier; node is the module IRI
}

shape <#AttributeShape> {
	schema:name xsd:string [1..1] .          # the data-* attribute (or space-separated set)
	ci:module xsd:string [1..*] .            # handler module specifier(s)
}

shape <#StageShape> {
	schema:name xsd:string [1..1] .          # "local" | "cdn"
	ci:component    @<#ManifestEntryShape> [0..*] .
	ci:sharedModule @<#SharedModuleShape>  [0..*] .
}

shape <#ProvidesShape> {
	schema:name xsd:string [1..1] .          # capability key (store, auth, navigation…)
	ci:service xsd:string [0..1] .           # one of service / respondTo is expected
	ci:respondTo xsd:string [0..*] .         # (xor not enforced here — kept simple)
	ci:sendValue xsd:string [0..1] .
	ci:priority xsd:integer [0..1] .
}

shape <#ConsumesShape> {
	schema:name xsd:string [1..1] .
	ci:call xsd:string [1..1] .
	ci:from xsd:string [0..1] .
	ci:module xsd:string [0..1] .            # eager-loaded consumer module
}

shape <#AcceptsShape> {
	schema:name xsd:string [1..1] .
	ci:onElement xsd:string [1..1] .
	ci:applyValueTo xsd:string [1..1] .
	ci:transform xsd:string [0..1] .
}
