Repository navigation
v1.1.0 - #5
Open
julianrojas87 wants to merge 22 commits into
Open
v1.1.0#5julianrojas87 wants to merge 22 commits into
julianrojas87 wants to merge 22 commits into
Conversation
- implement capacity to represent a shape as an IQuery - generalize shape parsing among SHACL and ShEx - rely on vocabulary factory to handle ontology terms
Adds a query-level result to the containment report and corrects three cases where the classification did not match the semantics of the input. Aggregate result - `solveShapeQueryContainment` and `solveShapeShapeContainment` now expose `report.result`, the least favorable result among the star patterns. It is `CONTAINED` only when every star pattern is. Explicit negation (sh:not / ShEx negative triple constraints) A predicate declared under `sh:not` was stored in the same predicate map as the positive ones, so a query asking for exactly the forbidden predicate bound against it and reported `CONTAINED` — the opposite of what the shape states. - Negative predicates never yield a match. - A shape that forbids a required predicate is definitive evidence of incompatibility, so it no longer counts as the open shape that softens `REJECTED` into `WEAKLY_REJECTED`. Such a query is now `REJECTED` even when the shape is open. OPTIONAL patterns Full binding was computed differently for open and closed shapes: open shapes required every triple pattern to bind, closed shapes only the non-optional ones. The same query against the same shape therefore returned `ALIGNED` when the shape was open and `CONTAINED` once `sh:closed true` was added — closing a shape made it look more relevant. - One rule for both: every *required* triple pattern must bind, and at least one triple pattern must bind. An unmatched OPTIONAL never blocks containment, since a resource can answer the query without it. MINUS and SERVICE Neither had an algebra handler, so their triple patterns were collected as ordinary required patterns. A MINUS block could lower `CONTAINED` to `UNALIGNED`, and a SERVICE clause could raise `REJECTED` to `UNALIGNED` — the result moved in both directions on patterns the resource is not expected to hold. - Both are discarded during normalization; MINUS keeps its left operand. - Discarded constructs are listed in `IQuery.unsupported` and surfaced on `IResult.unsupported`. Discarding only removes constraints, so the result can over-estimate relevance but never classifies a relevant resource lower. Queries with no triple pattern - An unrestricted triple pattern (`?s ?p ?o`) asks for every triple of the resource. It yields no star pattern, but it is not a discarded construct: the input is `CONTAINED` by any shape and every shape is marked visitable, flagged by `IQuery.matchesAnyTriple`. - An input with no triple pattern at all — an empty `WHERE`, a body of only `VALUES` or `FILTER`, or one left empty after discarding — never asks the resource for data, so relevance is undefined. It previously returned `CONTAINED`, ranking every resource maximally relevant on no evidence, and now throws `EmptyQueryError`. Docs - README: aggregate result documented, examples reorganised around the SPARQL-to-SHACL and shape-to-shape entry points, containment results illustrated with FILTER and constraint cases. - roadmap: record SHACL target support (sh:targetClass, sh:targetNode, sh:targetSubjectsOf, sh:targetObjectsOf) as future work. BREAKING CHANGE: `solveShapeQueryContainment` throws `EmptyQueryError` for an input that normalizes to no triple pattern, where it previously returned `CONTAINED`. Inputs using OPTIONAL, MINUS, SERVICE or `sh:not` may be assigned a different result than before. Implementors of `IBindings` must add `hasNegativeContradiction()`.
- `dependentShapes` was accepted but never passed to `groupShapeBydependencies`, so every `sh:node` pointing outside `shapes` was treated as satisfied and the root triple bound on an unchecked dependency. - An unresolved dependency is no longer always a refutation. A closed referenced shape that does not declare the nested pattern forbids it and still yields `REJECTED`; an open one is merely silent, so the predicate counts as a partial match and the pattern is `UNALINGED` or `ALIGNED`. - A nested star pattern is now classified against the shape that governs it through `sh:node`. Previously a fully satisfiable query could come out `REJECTED`. - Candidate and dependency-only placement of a referenced shape now give the same classification. - Update README
- Shapes outside `decidingShapes` are now passed on as `dependentShapes`: they still resolve `sh:node` references from the candidates, but their own `sh:node` references no longer count as evidence. Previously that evidence was still collected, so excluded shapes alone could raise a result, e.g. from `REJECTED` to `UNALINGED`. - Excluded shapes no longer get an entry in `visitShapeBoundedResource`. - Update README; add regression tests.
- An absent `sh:minCount` is now 0, as in SHACL. A source-shape property constraint without it becomes an optional triple pattern and no longer has to be matched for `CONTAINED`. Previously it was required. - An absent `sh:maxCount` is now unbounded (-1) instead of 1. Cardinality ranges are not compared during binding, so no result changes. SHACL predicates now always carry a `cardinality`. - `sh:maxCount 0` on its own now yields a negative predicate, like `sh:not`. It previously needed an explicit `sh:minCount 0`, and otherwise became a required positive predicate. - ShEx is unchanged: a triple constraint without a cardinality still occurs exactly once. - Update README; add parser and containment tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- FILTER NOT EXISTS, FILTER(!EXISTS) and MINUS over a single triple pattern (bound subject, fresh object) become excludedPredicates of the star pattern; a shape requiring the predicate (minCount >= 1) is a negative contradiction, so the pattern is REJECTED even on open shapes - translate sh:not of an input shape the same way - report other EXISTS/NOT EXISTS (kept as undecidable) and constructs discarded inside UNION branches in `unsupported` - count a contradicting shape reached through sh:node as closed - decide sh:pattern/regex contradictions only for flagless literal patterns that no string matches both (^foo vs bar was REJECTED) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Parse sh:targetNode, sh:targetClass, implicit class targets (a shape that is also an rdfs:Class), sh:targetSubjectsOf and sh:targetObjectsOf into `IShape.targets`, and sh:ignoredProperties into `IShape.ignoredProperties`. A shape that only declares targets is no longer a parse error. - Source shapes: shapeToQuery translates the targets of the given shape, not those of shapes reached through sh:node, into patterns about its focus node: rdf:type for a class target, the predicate for a subjects-of target, `subjectValues` for node targets, and an incoming star pattern for an objects-of target. Several class and subjects-of targets become UNION branches; other combinations are ignored and reported as `TARGETS`. - Candidate shapes: targets imply constraints (rdf:type with a target class, matched by IRI; the predicate of a subjects-of target) that match like declared ones and that a closed shape allows, as it allows its sh:ignoredProperties. They are only required when every focus node carries them. A shape with node targets only cannot contain a star pattern about another constant subject. Targets are ignored through sh:node. - A shape with targets only constrains its focus nodes, so its closedness and negative constraints count only for star patterns whose matching nodes are all in a target (`describesStarPattern`). Otherwise it counts as open, and only shapes that describe a pattern can make it REJECTED. Shapes without targets behave as before. - A star pattern whose triple patterns are all in UNION branches can now be contained, and star patterns without constraints are pruned from a translated shape. - Blank-node subjects of a query are variables (`isVariable: true`). - Add lib/target.ts and test/target.test.ts; update README. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Any other sh:not holds for some nodes that carry p (sh:minCount 2 allows one value), so reading it as forbidding p gave unsound REJECTED results. It is now ignored with an UNSUPPORTED_NOT diagnostic. Also keep every sh:or, sh:xone and sh:not value of a shape; the parser stored only one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both branches of `if (shape.closed === false)` had become the same code, so the condition only duplicated it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J27pqtwzm8UXeEv14dUbCX
Both parsers built IPredicates, converted them to IPredicateParts and then back to IPredicates through buildShapeFromRaw, which was the identity on everything they produce. The parsers now build IPredicates once and create the shape with tryCreateShape, which only turns the inconsistency error into a returned value. IConstraintFacets is shared by IConstraint and by the parser state, so the SHACL property data feeds buildConstraint directly and ShEx keeps one facet record per node instead of six parallel maps copied into every call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J27pqtwzm8UXeEv14dUbCX
…dules query.ts had grown to 1343 lines with three unrelated jobs and containment.ts to 1158. They are now: - lib/shapeToQuery.ts: shape to IQuery translation (also exported from the package index, as before through query.ts) - lib/negation.ts: reading MINUS / NOT EXISTS as negated triple patterns - lib/filter-compatibility.ts: FILTER compatibility with shape constraints query.ts (804 lines) now only normalizes SPARQL algebra and no longer depends on the shape model; containment.ts (470 lines) only classifies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J27pqtwzm8UXeEv14dUbCX
IShape inherited the optional fields of its serialized form, so callers filled in defaults the Shape constructor had already set (targetsOf and `?? []`). targets, ignoredProperties and negativePredicates are now required on IShape; only toObject leaves them out when they are empty. target.ts classifies the focus nodes of a shape once (focusNodes) and lists its target alternatives (targetAlternatives). impliedPredicates and the shape to query translation both use them, instead of each adding up array lengths and stating the "only alternative is required" rule apart. Node targets are keyed with nodeTargetKey: an IRI is its value, as before, and a literal or blank node is its N-Triples form. Plain string literals keep the key they had; typed and language-tagged literals no longer collide, and a blank node no longer matches an IRI with the same text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J27pqtwzm8UXeEv14dUbCX
SHACL ignores the targets of a shape reached through sh:node. Instead of a flag passed to Bindings, UnionBinding, impliedPredicates and impliedPredicateNames, the nested binding now receives withoutTargets(shape), a memoized copy without targets, at the one place sh:node is resolved. The Bindings constructor is back to its signature on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J27pqtwzm8UXeEv14dUbCX
The classification of a star pattern was written twice: once for candidate shapes in updateContainmentStats and once, inline, for the shapes reached through sh:node. Both now build an IShapeEvidence record and pass it to recordEvidence. Bindings.hasRootMatch replaces the two copies of the "bound triple or undecided dependency" check. For sh:node evidence, a contained pattern no longer needs to be added to the root matches separately: a nested binding has no UNION, so it is only fully bound when it binds a triple, which is already a root match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J27pqtwzm8UXeEv14dUbCX
`result` was defined as a non-enumerable property, so spreading, JSON.stringify or structuredClone lost it, and the 34 tests comparing whole reports with toStrictEqual never checked it. Both reports are now built by buildResult with `result` as an ordinary field, and those tests assert the aggregate result too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J27pqtwzm8UXeEv14dUbCX
The malformed-list check was copied into three places and the copies had drifted apart. ShEx returned a parse error under the strict policy; SHACL only labelled the diagnostic 'error' and went on with the partial list, although the policy says malformed lists are parse errors. ShEx also used walkRdfList only for its malformed flag and then walked the list again by hand, re-reading the rest of a oneOf from each member, and that loop never ended on a cyclic list under the non-strict policy. readRdfList (parser-policy.ts) now reads every list a parser uses: sh:or, sh:xone and sh:ignoredProperties in SHACL, eachOf and oneOf in ShEx. A malformed list is reported, is a parse error under the strict policy (the default), and otherwise contributes its members up to the defect. The ShEx handlers iterate those members once and share a parse context instead of forwarding the shape IRI and options as unused-looking arguments. BREAKING CHANGE: under the default policy, a SHACL shape with a malformed sh:or, sh:xone or sh:ignoredProperties list is now a PoorlyFormatedShapeError. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J27pqtwzm8UXeEv14dUbCX
parseShapeFromQuads lived in Shape.ts and used `await import('./shacl')` to
get around the import cycle with the parsers. It is now in
lib/shape-parser.ts (exported from the package index as before) and
imports them statically.
The stream handling was written three times, typing the stream error as
any. forEachQuad (parser-policy.ts) now feeds the quads of an array or a
stream to a callback and returns the stream error, as before. Both parsers
still consume a stream quad by quad.
The 170-line if/return chain of the SHACL parseQuad is now a table of
handlers by predicate IRI.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J27pqtwzm8UXeEv14dUbCX
termExpression and operatorExpression hand-built expression objects without their `type` and cast them through `as unknown as`. Traqula's AlgebraFactory builds the same expressions as valid algebra. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J27pqtwzm8UXeEv14dUbCX
collectFromAlgebra took eight positional arguments and every handler took its own subset in its own order. INormalizationState held two of the seven accumulators, and default values meant a recursive call that left one out silently dropped what it collected. The state now holds the triples, VALUES bindings, unions, filters, discarded constructs and negations. collectFromAlgebra and buildQuery take it as their one argument, and createNormalizationState makes the branch built for one alternative of a property path, which shares the VALUES bindings of its query as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J27pqtwzm8UXeEv14dUbCX
`npx eslint .` reported 14 errors and 2 warnings; it now reports none. The containment tests also use the real IResult instead of an alias that left out `result`, which no longer type-checked once the expectations assert the aggregate result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J27pqtwzm8UXeEv14dUbCX
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.