Unreleased

v0.11.2 (2026-11-05)

Bug Fixes and Improvements

Fix 26 format-validation failures exposed by the updated official JSON Schema Test Suite commit 245a45a across Draft 7 and Draft 2020-12. (#21)

  • Fix email validation to accept empty quoted local parts and case-insensitive IPv6 address-literal tags. Reject non-ASCII quoted content, including escaped characters, while preserving Unicode support for idn-email.
  • Apply non-transitional UTS 46 mapping and NFC normalization before validating idn-hostname labels, supporting fullwidth characters, ignored zero-width spaces, and ACE prefixes produced by mapping. Preserve encoded label/domain length limits, canonical Punycode validation, and domain-wide Bidi checks.
  • Validate uri-template literal characters and percent-encoded triplets according to RFC 6570, rejecting spaces, control characters, DEL, and malformed percent escapes while preserving permitted Unicode literals. Keep literal segments separate so percent escapes cannot span expressions.

v0.11.1 (2026-11-04)

Bug Fixes and Improvements

Fix compiler and test warnings on Elixir 1.20 and add reproducible Alpine-based testing. (#20)

v0.11.0 (2026-11-04)

Security

Fix validation bypass on regex match-limit exhaustion. (#19)

  • Fix regex match-limit exhaustion being treated as a non-match in both supported drafts, preventing patternProperties subschema bypasses and not/combinator inversions of regex failures.
  • Report regex engine failures as :indeterminate_pattern errors and stop validation, preserving the affected string, instance path, and error reason. Cover pattern, propertyNames, and all cached and uncached patternProperties/additionalProperties matching paths. Existing execution limits remain unchanged; rewrite excessive-backtracking patterns to avoid rejecting valid data.

Bug Fixes and Improvements

Fix nested numeric equality and streamline uniqueItems validation. (#17)

  • Fix uniqueItems numeric equality recursively inside arrays and objects, so nested values such as [1] and [1.0] are recognized as duplicates. Retain exact object-key semantics, hash-collision checks, and original validation inputs.
  • Replace uniqueness reduction callbacks with early-exit tail recursion, avoiding continuation tuples while retaining numeric-aware scalar hashing. Reuse unchanged branches when normalizing composite hash keys.

Reduce compilation, scope-scanning, validation, and fragment-bundling overhead while preserving resource ownership and URI semantics. (#16)

  • Skip empty definition-compilation work when $defs is absent, avoid generic pointer encoding for plain ASCII definition names, and share identical definition and local-reference maps. Preserve canonical escaping and collisions between escaped and legacy definition keys.
  • Store Draft 2020-12 scanner registry entries directly, tracking lexical resource contexts only when requested and avoiding intermediate entry wrappers and registry conversions. Preserve discovery order and duplicate-identity precedence.
  • Avoid redundant URI-base extraction when reference owners differ or source IDs already match, retaining strict raw-owner and resource-base checks.
  • Fast-path compiled references with resolved registry keys, avoiding intermediate lookup tuples and binary-prefix match allocations while preserving fragment-only lookup, resource ownership, dynamic scope, and missing-target fallback.
  • Reject non-string $ref values during schema compilation with structured invalid_keyword_value errors, including nested schemas, selected fragments, and compile-time macros. Keep empty-string references valid and make compiled reference keys explicitly binary-only.
  • Skip URI-base extraction for anonymous (nil-base) resources during fragment bundling, preserving the distinction between missing and explicit empty bases in bundled output.

Fix resource-local reference ownership, and simplify URI and validation context handling. (#15)

  • Isolate eagerly compiled JSON Pointer aliases by resource in both Draft 7 and Draft 2020-12, preventing caller-local aliases from shadowing remote, embedded-resource, or built-in targets. Preserve same-resource cache reuse, selected-fragment pointer roots, escaped definition names, and finalized caches across retrieval and canonical URIs.
  • Preserve enclosing pointer roots and resource-owned caches when references enter named-fragment IDs or anchors, including embedded resources and anchors retained from older snapshots sharing a canonical URI.
  • Resolve Draft 7 plain-name fragment IDs under anonymous or relative bases without resolving compiled registry keys twice, including loaded fragments and retained resource snapshots. Keep JSON Pointer lookups scoped to the active resource; $id does not define virtual pointer paths.
  • Preserve inherited vocabulary behavior when $dynamicRef re-enters an anonymous root, recognizing its nil and "" base representations only when raw ownership matches.
  • Simplify compiled reference metadata: keep registered targets' pointer roots and caches in raw and local_refs, and retain each target's own $dynamicAnchor name in dynamic_anchor.
  • Centralize reference lookup, resource ownership, and context preparation in JSONSchex.Validator.Reference, removing reference-entry flags from JSONSchex.Validator while preserving structural and dynamic-scope transitions.
  • Rename internal scope-scanner entry points from scan/2 and scan_all/3 to scan_schema/2 and scan_document/3, clarifying schema-tree versus containing-document discovery. Remove the old names without compatibility aliases.
  • Add JSONSchex.URIUtil.base/1 for fragment-free URI extraction, distinguishing missing (nil) from empty ("") bases. Reuse it across compilation, scope discovery, reference resolution, and bundling.
  • Rename JSONSchex.URIUtil.with_fragment/2 to join_fragment/2 to clarify its concatenation-only behavior. Retain with_fragment/2 as a deprecated compatibility alias; joining behavior is unchanged.
  • Simplify JSONSchex.URIUtil.split_fragment/1 with :binary.split/3, preserving first-delimiter splitting and empty-fragment normalization.

Add Draft 7 support, fix pre-existing issues that affect both supported drafts, and improve shared infrastructure and dependencies. (#14)

Draft 7 support and correctness

These changes implement Draft 7 behavior. Fixes for pre-existing shared defects are listed separately below.

  • Support tuple-form items, additionalItems, and Draft 7 $ref sibling semantics alongside Draft 2020-12.
  • Embed the canonical Draft 7 meta-schema and cache its compiled reference registry by format/content assertion options. Both canonical HTTP URI forms, with and without a trailing #, share the appropriate cache variant and resolve without a loader or runtime file access.
  • Make Draft 7 scope discovery ignore $ref siblings and newer-draft annotations, skip eager $defs compilation, and register resources under tuple additionalItems. Preserve explicit JSON Pointer references into annotation content.
  • Discover nested $id resources in explicitly referenced pointer targets, including targets under ignored content or in other registered resources, without activating unrelated siblings or loading external schemas. Follow transitive pointers, retry lookups after resource discovery or promotion, preserve fragment-ID pointer roots even with relative retrieval bases, and terminate discovery cycles.
  • Register ancestor resources crossed by explicit pointer and selected-entry routes so targets can refer back to them. Keep these metadata-only registrations lower priority than ordinary discovery, including through transitive references and regardless of pointer order.
  • Compile selected fragments and eager pointer targets against their inherited ancestor $id base, applying each resource ID once. Retain the containing document and retrieval base for discovery, and use the owning resource as the JSON Pointer root without promoting fragment-only IDs or ignored $ref sibling IDs to resource roots.
  • Replace inherited URI fragments in references beneath Draft 7 named-fragment IDs, so # selects the enclosing resource root rather than an ancestor's named subschema.
  • Ensure an ignored Draft 7 contentSchema does not suppress enabled contentEncoding and contentMediaType assertions.
  • Refactor Draft 7 scope scanning to use named state and owner maps with focused traversal and merging helpers. Preserve fallback-resource precedence, reference replay, owner-change detection, and cycle termination.
  • Run an independent official Draft 7 suite and add compatibility, reference, cache, and compile-time embedding regressions.

Shared fixes for Draft 7 and Draft 2020-12

These fixes apply to both drafts and address defects already present in the Draft 2020-12 implementation on main. They are not regressions introduced by Draft 7 support.

  • Honor :base_uri during schema scope discovery, resolving relative $id resources and Draft 2020-12 anchors against the supplied retrieval base instead of registering unqualified identities.
  • Preserve ancestor $id scopes and the owning raw resource during JIT pointer compilation without reapplying the resource root's ID. Subsequent local and absolute references resolve against that resource rather than the selected leaf, fixing reference resolution within embedded resources.
  • Resolve empty self-references against the current raw resource without loading an unregistered retrieval URI.
  • Resolve absolute JSON Pointer references to the current URN resource locally instead of passing the full URN to JSON Pointer lookup.
  • Assert standalone contentEncoding and contentMediaType when content_assertion: true, even without contentSchema. Content handling remains annotation-only by default.

Shared infrastructure and maintenance

These changes support both drafts; they are infrastructure and maintenance updates rather than fixes for pre-existing defects on main.

  • Select the draft from a recognized root $schema or the string-valued :draft option ("draft7" or "draft2020_12"), retaining Draft 2020-12 as the fallback. Return structured errors for conflicting declarations or invalid options; compile-time schema macros raise CompileError. Fragment compilation uses the containing document's root declaration.
  • Dispatch scope discovery through draft-specific scanners behind the shared scope-scanner interface.
  • Upgrade ex_json_pointer to ~> 0.9 and pass decoded reference tokens directly to ancestor traversal.
  • Update the optional decimal requirement to ~> 3.2 (previously ~> 1.0 or ~> 2.0 or ~> 3.0).
  • Expand shared regression coverage and update the README, guides, and changelog. Both official draft suites retain the optional/cross-draft exclusion; complete cross-draft support is not claimed.

v0.10.0 (2026-09-14)

Bug Fixes and Improvements

  • Add a Git-baseline JSONSchex before/after benchmark suite covering broad validation cases and focused bundle, compile, scan, and validation workloads. (#13)

  • Optimize two-rule validation dispatch by reserving the pattern-match-cache path for exact sibling patternProperties and additionalProperties pairs; other two-rule schemas retain their direct fast path. (#12)

  • Reduce repeated validation work and allocations. (#11)

    • Count Unicode code points for minLength and maxLength without allocating a charlist for inputs larger than 256 bytes, while retaining the standard conversion for shorter inputs. Malformed UTF-8 now produces a validation error with error_detail: "invalid_utf8" instead of raising UnicodeConversionError.
    • Compile each patternProperties regex once when the same schema also uses additionalProperties, with a bounded per-schema cache that preserves vocabulary gates, error behavior, and validation semantics.
    • Reuse runtime pattern-match classification between sibling patternProperties and additionalProperties rules within one validation invocation, reducing repeated regex matching for wide objects while preserving rule order, annotations, and diagnostics.
    • Adaptively merge evaluated keys from parent-dominated anyOf branches with balanced MapSet unions, avoiding repeated conversion of shared incoming evaluated keys while preserving full branch traversal, evaluated-key inputs, diagnostics, and legacy list-key behavior.
    • Avoid rebuilding already-flat validation error lists at the public validation boundary, and fuse nested error flattening with legacy error formatting while preserving diagnostic order, values, and internal error grouping.
    • Remove redundant input-key presence checks when validating required, while preserving declaration order, duplicate entries, non-object behavior, and diagnostic shape.
    • Avoid rescanning built-in Draft 2020-12 resources after compilation, and reduce scope-scanner traversal intermediates while preserving resource identities, anchor targets, and traversal precedence.
  • Reuse already-compiled schema nodes when their compilation context matches, reducing repeated compilation while preserving resource scope, rule order, and context-sensitive behavior. (#10)

  • Improve fragment-bundling performance by avoiding repeated descendant metadata indexing, accumulating fallback-anchor candidates without repeated list scans, and skipping deep reference-rewrite passes when external-reference aliases are identities. (#9)

v0.9.3 (2026-09-07)

Bug Fixes and Improvements

  • Explicitly use ex_json_pointer ~> 0.8, which is a performance-improved version.

v0.9.2 (2026-08-07)

Bug Fixes and Improvements

  • Fix arbitrary-precision multipleOf validation by checking Decimal coefficients and exponents exactly, avoiding context-dependent rounding, quotient-size errors, and validation crashes for large integers

v0.9.1 (2026-08-06)

Bug Fixes and Improvements

  • Update Draft 2020-12 optional format validation to match the current official test suite (commit cc73f5f):
    • Validate regex strings against ECMA-262 syntax rather than PCRE extensions, including support for empty character classes and variable-width lookbehind
    • Require complete input for duration, uuid, and relative-json-pointer formats; require RFC 3339 numeric timezone offsets to include minutes
    • Enforce RFC 6570 URI-template prefix modifiers as non-zero values with at most four digits

v0.9.0 (2026-07-23)

Bug Fixes and Improvements

  • Update JSONSchex.bundle_fragment/2 to bundle only the schema graph reachable from the selected entrypoint, without loading unrelated refs from other components, examples, or extension data. Local pointers, anchors, nested $id resources, recursive graphs, and loader-provided base URIs remain supported; caller-owned $defs entries are preserved with collision-safe internal keys, while invalid $defs and ambiguous anchors return structured invalid_defs or ambiguous_anchor errors
  • Change JSONSchex.Ref.resolve_selected/2 so an unselected $ref map is preserved but no longer terminates traversal: its sibling values are walked, :select is invoked for descendant $ref nodes that earlier releases skipped, and every descendant ref left unselected is rebased through loader-provided :base_uri and nested $id boundaries without loading its target
  • Some Dialyzer compile warnings fixed
  • Improved ip format validation
  • Update the official Draft 2020-12 optional format-suite coverage and align format validation:
    • Adapt the idn-email lone UTF-16 surrogate case for Elixir JSON decoders, reject ill-formed UTF-8 input, and accept valid non-NFC domain labels by normalizing only the domain before IDNA hostname validation
    • Reject signed years in the RFC 3339 date format, which requires exactly four unsigned year digits
    • Strengthen idn-hostname validation: reject malformed or non-canonical A-labels, encoded hostnames longer than 253 characters, and Bidi domains with invalid label directionality; Unicode and Punycode forms are validated equivalently

v0.8.1 (2026-06-15)

Bug Fixes and Improvements

v0.8.0 (2026-06-07)

Breaking Changes

v0.7.0 (2026-05-23)

Bug Fixes and Improvements

  • Add JSONSchex.compile_fragment/2 and JSONSchex.Schema.compile_fragment!/2 for compiling JSON Schema fragments while preserving the containing document as the local reference context, useful for OpenAPI 3.1 schemas under paths and components. Fragment entrypoints support exactly one of :entry_pointer or :entry_ref, with :entry_ref providing a base URI/path when :base_uri is omitted
  • Add JSONSchex.bundle_fragment/2 for producing standalone raw schemas from document fragments, including reachable external resources mounted under $defs
  • Add JSONSchex.Ref.resolve_selected/2 for selector-driven $ref resolution in JSON-like documents, allowing callers such as OpenAPI tooling to choose which $ref nodes are replaced while preserving unselected refs

Breaking Changes

  • Rename the remote/reference loading option from :external_loader to :loader

v0.6.0 (2026-05-09)

Bug Fixes and Improvements

  • Add first-class support for compiling static JSON Schema literals during module compilation via JSONSchex.Schema.compile!/2 and the ~X sigil in JSONSchex.Sigil
  • Refactor validation and compiled rule execution to support static compile-time embedding by making rules data-driven and dispatching them through JSONSchex.Validator.Rules
  • Precompute legacy dependencies execution modes during compilation and simplify runtime rule matching
  • Prune several compile-time no-op keyword rules such as empty required, properties, patternProperties, dependentRequired, dependentSchemas, and prefixItems
  • Relax decimal dependency requirement

v0.5.0 (2026-04-23)

Bug Fixes and Improvements

  • Embed the Draft 2020-12 built-in meta-schema family directly in code and cache compiled built-in defs for $ref and $dynamicRef resolution
  • Refactor built-in Draft 2020-12 support into draft-specific modules: JSONSchex.Draft202012.Schemas, JSONSchex.Draft202012.Vocabulary, and JSONSchex.Draft202012.Dialect
  • Simplify and de-duplicate reference resolution logic, including URI fragment handling and loaded-schema validation flow
  • Fix built-in meta-schema validation so schemas can be validated against the canonical Draft 2020-12 meta-schema without an external loader
  • Update official test suite, and adapt to pass the official Draft 2020-12 optional format cases for duration, time, and uri:
    • Tighten duration format validation to match the RFC 3339 Appendix A grammar used by the official Draft 2020-12 optional format suite
    • Accept RFC 3339 time values with unknown local offset (-00:00)
    • Tighten uri and uri-reference format validation to reject invalid percent-encoding triplets

v0.4.0 (2026-04-05)

Bug Fixes and Improvements

  • Fixed an issue where $ref could cause an infinite loop during validation
  • Refactor compile-time and validation-time failures to use the unified JSONSchex.Types.Error model with structured ErrorContext
  • Improve error formatting so invalid keyword and compile errors produce clearer, more contextual messages
  • Treat the canonical Draft 2020-12 meta-schema as a built-in dialect without requiring a loader, while still honoring explicit $vocabulary declarations
  • Split vocabulary handling between supported vocabularies and the built-in Draft 2020-12 default active vocabulary set
  • Optimize local $ref compilation by batching JSON Pointer resolution for explicit local fragment references
  • Improve scope scanning so $id, anchors, and explicit local fragment refs are registered more consistently
  • Consolidate time format validation into the date-time format implementation

v0.3.0(2026-02-21)

Bug Fixes and Improvements

  • Refactor the error context to make it more consistent

v0.2.1 (2026-02-20)

Bug Fixes and Improvements

  • Enhance some keywords validation.

v0.2.0 (2026-02-20)

Bug Fixes and Improvements

  • Enhance some keywords checking in compile.

v0.1.0 (2026-02-16)

  • Initial Release