Skip to content

Add @enum schema property for sh:in literal union types - #179

Merged
karelklima merged 4 commits into
karelklima:mainfrom
thenonameguy:feat/enum-value-sets
Sep 14, 2026
Merged

karelklima merged 4 commits into
karelklima:mainfrom
thenonameguy:feat/enum-value-sets

Conversation

@thenonameguy

Copy link
Copy Markdown
Contributor

No description provided.

Carry sh:in string literal values through the schema pipeline so
TypeScript consumers get literal union types instead of plain string.

- Add "@enum" to Property and ExpandedProperty types
- Narrow SchemaInterface via @enum (skipped for @multilang)
- Extract sh:in literal values in the SHACL converter
- Emit "@enum" in the schema printer

Co-Authored-By: Claude <noreply@anthropic.com>
`deno fmt --check` failed CI on the nested conditional in
`ConvertPropertyType`, so lint and test never ran.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StkJxf1Y3wKTe4cm2oo6bZ

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new enum plumbing has a couple of concrete edge-case bugs (mixed-type sh:in lists and empty enum arrays) that can produce incorrect output and/or narrow types to never.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR introduces enum support across the SHACL→Schema and Schema→Script pipeline by translating sh:in string-literal lists into a schema enumValues field and emitting @enum in generated scripts, plus updating the runtime schema/type interfaces to narrow string properties to literal unions.

Changes:

  • Convert sh:in string-literal lists into enumValues during SHACL→Schema conversion.
  • Emit @enum (as as const) when printing schemas to TS scripts, based on enumValues.
  • Extend schema runtime types/utilities to accept @enum and narrow property types to literal unions; add/adjust tests accordingly.
File summaries
File Description
tests/scripts/shacl_to_schema.test.ts Updates expectations to include enumValues for sh:in string literal lists.
tests/scripts/schema_to_script.test.ts Adds tests ensuring enumValues emits @enum (including with @optional).
tests/schema.test.ts Adds type-level/runtime tests for enum narrowing, optional/array enums, and multilang interaction.
scripts/shacl_to_schema.ts Extracts enumValues from sh:in constraints and merges enum constraints across shapes.
scripts/schema_to_script.ts Adds enumValues to PropertySpec and prints it as @enum in generated scripts.
library/schema/utils.ts Allows @enum to pass through schema expansion.
library/schema/schema.ts Adds @enum to Property and ExpandedProperty types.
library/schema/interface.ts Uses @enum to narrow string properties to literal union types (except for @multilang).
Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread scripts/schema_to_script.ts
Comment thread scripts/shacl_to_schema.ts Outdated
Comment thread scripts/shacl_to_schema.ts Outdated
@thenonameguy

thenonameguy commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor Author

Addressed PR review comments, hopefully it's good like this. ShEx could be done as a follow-up.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Resolve enum/type compatibility, intersect conjunctive enum constraints, and document @enum in the canonical schema reference.

Review details

Suppressed comments (4)

Previously missed (2) — in code that hasn't changed since the last review.

library/schema/interface.ts:43

  • Because @enum is declared as readonly string[], this branch currently overrides any declared @type. A schema such as { "@type": xsd.integer, "@enum": ["1", "2"] as const } is accepted and inferred as "1" | "2", while expandSchema and the encoder/decoder still use xsd:integer and produce numbers. Restrict @enum to string-compatible properties or validate/resolve this combination instead of silently changing the public type.
    scripts/shacl_to_schema.ts:312
  • When two sh:property shapes constrain the same path, SHACL applies both constraints (the surrounding comment at lines 296-298 calls this an AND), so two sh:in lists must be intersected. Choosing b.enumValues here can generate a union containing values rejected by a (for example, ("a" "b") plus ("b" "c") produces "b" | "c" instead of only "b"). Combine both non-empty lists by intersection, and only fall back to the single list when the other shape has no enum constraint.

library/schema/schema.ts:16

  • The new public @enum schema key is not documented in the canonical schema reference: docs/components/schema.md says its example contains all supported variations (lines 192-236), but the feature list and complete example omit @enum and its literal-union behavior. Please document hand-authored schemas alongside this type change; the generator-specific note does not cover that API.
  "@enum"?: readonly string[];

scripts/shacl_to_schema.ts:441

  • sh:and branches are conjunctive, but this assignment overwrites any earlier inValues. With branches allowing ("a" "b") and ("b" "c"), the output keeps only the last branch instead of the intersection ("b"), producing an unsound literal union. Accumulate/intersect enum values across the direct constraints and all sh:and branches.
        if (sub.inValues !== undefined) c.inValues = sub.inValues;
  • Files reviewed: 9/9 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@karelklima

Copy link
Copy Markdown
Owner

@thenonameguy Thanks for the PR!

Since this PR extends LDkit schema, it needs careful consideration. One of the things I see right now - the new @enum only supports strings. It would be great to also support other datatypes that are supported in LDkit - specifically at least numbers / integers. I imagine that can be a really valid use case for that.

I will test this PR in the upcoming days and will let you know what I think, whether we could eventually merge it to the project.

Overall I like the idea very much. But it is a big change and I need to make sure that it plays nice with the rest of LDkit and that it is fully backwards compatible - especially in terms of TypeScript typings.

@karelklima karelklima left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again for the PR, I was able to test it and it generally works as I expected.

There is one important thing to improve that is related to how TypeScript schema is inferred, otherwise it looks great.

Comment thread library/schema/interface.ts Outdated
@karelklima
karelklima merged commit 6ea705a into karelklima:main Sep 14, 2026
1 check passed
@karelklima

Copy link
Copy Markdown
Owner

Published in 2.8.0
https://www.npmjs.com/package/ldkit/v/2.8.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants