jsonschema_rs 0.49.9-aarch64-linux → 0.50.1-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2656e11a6b0c73563c4654d5bddc77dfbafce7bd9533dc537502da4e07e50ba
4
- data.tar.gz: ede529f84246513d55bd47c7875a6894f91fb756d55764054f614228f7c81dab
3
+ metadata.gz: 024531047540ae02d18065a5ebc16c86befcef12319892bc74634edd00d5783a
4
+ data.tar.gz: 705163665dfba247b7a98f66dfa38d4e0aa4e2f249485b9446c9d03ebee4dcc0
5
5
  SHA512:
6
- metadata.gz: 72f5be7b2725263ef888a4b4ea585d76c84fb960403efe931bd4007bdc8583869184794211cc2af92c10a9ba61b7141b7791f0303ed7a9730160a80055f4475b
7
- data.tar.gz: 9d56ecadae2bd0db1fe0dfb319f9996b78236c08623223115cc7b5a2314c43e7da1ab08fa7e30da3cd4573b10c816c6150bc5f1fb60f4f91f68cf9165bd6b111
6
+ metadata.gz: 925754bbefe9c0942a767976f0e8147c58aeba75a84ee5c0f6914eb9620aecfb41b66eee12823aaad393bb30a8fab3f2cc6e6f608e1e83edc13f5371ebb1986c
7
+ data.tar.gz: de0020b79fc16136a2dc5e96862318c8abfcd0ae4f908502797326c3a279b1622c4625f60634322b09ed1ecb13cf1ccc5b166d99785365d262165523805592e4
data/CHANGELOG.md CHANGED
@@ -2,6 +2,61 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.50.1] - 2026-08-22
6
+
7
+ ### Changed
8
+
9
+ - `CanonicalSchema#satisfiability` answers `:yes` for a string whose `pattern` or `format` a matching value can be built from.
10
+ - `CanonicalSchema#satisfiability` answers `:no` where a `format` takes no string of the length the schema asks for.
11
+
12
+ ### Fixed
13
+
14
+ - `CanonicalSchema#negate` taking apart a union that reads a definition through its own `if`, so a schema and its negation shared a value.
15
+ - `CanonicalSchema#subtract` dropping a Draft 4 array member an element demand partly takes.
16
+ - `CanonicalSchema#covers` answering `Yes` where a Draft 4 element demand refuses a member.
17
+ - A string schema whose `maxLength` is `0` keeping a `pattern`, `format`, or content facet unread, so the same constraints written in one object and written as an `allOf` reached different canonical forms.
18
+
19
+ ### Performance
20
+
21
+ - `not` over a wide object schema costs its branch count instead of its square.
22
+ - `not` over an object schema is linear in its property count, not quadratic.
23
+ - `not` over an object schema with `additionalProperties: false` no longer costs cubic time in its property count.
24
+
25
+ ## [0.50.0] - 2026-08-20
26
+
27
+ ### Added
28
+
29
+ - `CanonicalSchema#union` and `CanonicalSchema#subtract`.
30
+ - `JSONSchema::Canonical::UnsupportedResult`, raised where the canonical form does not support a set operation's result.
31
+ - `JSONSchema::Canonical::IncompatibleOperands` where both operands read `#` and it names a different document on each side.
32
+ - `JSONSchema::Canonical::Containment`, `Satisfiability`, `Distinctness` and `Kind`: named constants for the symbols `covers`, `satisfiability`, `kind` and `ArrayView#distinctness` return, each with an `ALL` list.
33
+
34
+ ### Changed
35
+
36
+ - `CanonicalSchema#satisfiable?` is now `CanonicalSchema#satisfiability`, answering `:yes`, `:no`, or `:unknown` - `:yes` wherever a value can be exhibited, not only for the forms listing their members.
37
+ - `CanonicalSchema#covers` decides through the difference as well: `:no` where the argument keeps values the receiver rejects, `:yes` where nothing is left over.
38
+ - `CanonicalSchema#is_subset_of` is now `CanonicalSchema#covers`, answering `:yes`, `:no`, or `:unknown` for whether the receiver admits every value the argument admits.
39
+ - `CanonicalSchema#negate` raises where it used to return `nil`.
40
+ - `JSONSchema::Canonical::UnmodeledOperand` is now `JSONSchema::Canonical::UnsupportedOperand`, and means only that an operand is a `Raw` pass-through.
41
+
42
+ ### Fixed
43
+
44
+ - Combining nodes of two different documents repointing a `$ref` to `#` at the combined result instead of the document it was written in, including one named by a definition the result keeps.
45
+ - Intersecting a `$ref` whose target is `true` or `false`, which crashed the interpreter.
46
+ - A key constraint left un-narrowed once a run is out of intersections, which crashed the interpreter on the next read of it.
47
+ - `CanonicalSchema#covers` answering `:unknown` for a schema against itself, where that schema is a `$ref`.
48
+ - `CanonicalSchema#union` keeping a `$ref` beside the schema it names, where the other three operations read through it.
49
+ - `CanonicalSchema#covers` and `CanonicalSchema#subtract` cancelling two nodes written the same way whose `#` names a different document.
50
+ - Two results accepting the same values comparing unequal over the part of their documents neither reads.
51
+ - `additionalProperties` reaching a key the pattern map matches when a finite key constraint closes the map, which dropped values both operands accept.
52
+ - A recursive definition stopping every other pointer in the document from being read through.
53
+ - One `$defs` entry read past a wider schema written out at every use, rather than kept as the pointer it was.
54
+ - Set operations rejecting one document canonicalized twice, and a pruned result rejected against the document it came from, where the maps resolve every shared reference the same way.
55
+ - Set operations comparing a `$ref` as a pointer instead of reading through it, so a schema written with a `$ref` did not cancel against the same schema written out.
56
+ - `CanonicalSchema#subtract` asking for a complement where the difference is one of the operands or empty, declining on schemas it can subtract.
57
+ - Set operations keeping `$defs` entries the result no longer references, which then showed up in the emitted schema.
58
+ - `CanonicalSchema#union` declining over an approximated intersection, where the union itself is exact.
59
+
5
60
  ## [0.49.9] - 2026-08-10
6
61
 
7
62
  ### Added
@@ -387,7 +442,9 @@
387
442
 
388
443
  - Initial public release
389
444
 
390
- [Unreleased]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.9...HEAD
445
+ [Unreleased]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.50.1...HEAD
446
+ [0.50.1]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.50.0...ruby-v0.50.1
447
+ [0.50.0]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.9...ruby-v0.50.0
391
448
  [0.49.9]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.8...ruby-v0.49.9
392
449
  [0.49.8]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.7...ruby-v0.49.8
393
450
  [0.49.7]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.6...ruby-v0.49.7
data/README.md CHANGED
@@ -50,6 +50,7 @@ end
50
50
  - 🔧 Custom keywords and format validators
51
51
  - ✨ Meta-schema validation for schema documents
52
52
  - 📦 Schema bundling into Compound Schema Documents
53
+ - 🧮 Experimental schema canonicalization
53
54
  - ♦️ Supports Ruby 3.2, 3.4 and 4.0
54
55
 
55
56
  ### Supported drafts
@@ -291,6 +292,85 @@ dump_a == dump_b # => true
291
292
 
292
293
  Main use case: deduplicating equivalent JSON Schemas.
293
294
 
295
+ ## Schema Canonicalization
296
+
297
+ > **Experimental**: the canonicalization API may change in minor releases.
298
+
299
+ `JSONSchema.canonicalize` reduces a schema to a normal form without changing the values it accepts. Schemas that accept the same values reduce to equal `CanonicalSchema` objects, and contradictions reduce to `false`:
300
+
301
+ ```ruby
302
+ canonical = JSONSchema.canonicalize(
303
+ { "allOf" => [{ "type" => "integer", "minimum" => 0 }, { "minimum" => 10, "maximum" => 100 }] }
304
+ )
305
+ canonical.to_json_schema
306
+ # => {"$schema"=>"https://json-schema.org/draft/2020-12/schema",
307
+ # "type"=>"integer", "minimum"=>10, "maximum"=>100}
308
+
309
+ # However they were written, equivalent schemas compare equal
310
+ canonical == JSONSchema.canonicalize({ "type" => "integer", "maximum" => 100, "minimum" => 10 })
311
+ # => true
312
+
313
+ # A schema no value can satisfy collapses
314
+ JSONSchema.canonicalize({ "type" => "integer", "minimum" => 10, "maximum" => 5 }).satisfiability
315
+ # => :no, which is JSONSchema::Canonical::Satisfiability::NO
316
+ ```
317
+
318
+ A canonical schema *is* the set of values it accepts, so canonical schemas combine as sets. Every emitted schema carries `$schema`, left out of the comments below:
319
+
320
+ ```ruby
321
+ positive = JSONSchema.canonicalize({ "type" => "integer", "minimum" => 0 })
322
+ bounded = JSONSchema.canonicalize({ "type" => "integer", "maximum" => 100 })
323
+
324
+ # Every value both admit
325
+ positive.intersect(bounded).to_json_schema
326
+ # => {"type"=>"integer", "minimum"=>0, "maximum"=>100}
327
+
328
+ # Every value either admits
329
+ positive.union(bounded).to_json_schema
330
+ # => {"type"=>"integer"}
331
+
332
+ # Every value `positive` admits and `bounded` rejects
333
+ positive.subtract(bounded).to_json_schema
334
+ # => {"type"=>"integer", "minimum"=>101}
335
+
336
+ # Every value `positive` rejects, across the types it never admitted
337
+ positive.negate.to_json_schema
338
+ # => {"anyOf"=>[{"type"=>["null", "boolean", "string", "array", "object"]},
339
+ # {"type"=>"integer", "maximum"=>-1},
340
+ # {"type"=>"number", "not"=>{"multipleOf"=>1}}]}
341
+
342
+ positive.covers(bounded) # => :no - `bounded` takes negative integers, `positive` does not
343
+ positive.satisfiability # => :yes
344
+ ```
345
+
346
+ ### Comparing two versions of a schema
347
+
348
+ The difference answers what editing a schema did to the values it takes: it accepts exactly what the old schema took and the new one turns away, and is unsatisfiable exactly when nothing was lost.
349
+
350
+ ```ruby
351
+ old = JSONSchema.canonicalize({ "type" => "string" })
352
+ new = JSONSchema.canonicalize({ "type" => "string", "maxLength" => 50 })
353
+
354
+ # What `new` stopped accepting, as a schema
355
+ old.subtract(new).to_json_schema
356
+ # => {"$schema"=>"https://json-schema.org/draft/2020-12/schema",
357
+ # "type"=>"string", "minLength"=>51}
358
+
359
+ # Nothing is accepted that was not accepted before, so the change only narrows
360
+ new.subtract(old).satisfiability
361
+ # => :no
362
+ ```
363
+
364
+ Compare a request schema new-against-old and a response schema old-against-new: narrowing a request turns away payloads a caller used to send, widening a response returns values a caller never agreed to read.
365
+
366
+ `:no` on the difference proves nothing was lost. `:unknown` proves nothing either way.
367
+
368
+ `:unknown` is undecided, not negative, so the two questions have opposite safe tests: only `satisfiability == :no` proves a schema admits nothing, and only `covers == :yes` proves containment.
369
+
370
+ `JSONSchema::Canonical::Containment`, `Satisfiability`, `Distinctness` and `Kind` hold these symbols as constants, each with an `ALL` list.
371
+
372
+ Both operands must share one setup - the same draft, format policy, regular-expression engine and definitions - or `IncompatibleOperands` is raised. `UnsupportedOperand` means an operand is a `Raw` pass-through, and `UnsupportedResult` that the canonical form does not support the result. All three live under `JSONSchema::Canonical`.
373
+
294
374
  ## Schema Bundling
295
375
 
296
376
  Produce a Compound Schema Document ([Appendix B](https://json-schema.org/draft/2020-12/json-schema-core#appendix-B)) by embedding all external `$ref` targets into a draft-appropriate container. The result validates identically to the original.
Binary file
Binary file
Binary file
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSONSchema
4
- VERSION = "0.49.9"
4
+ VERSION = "0.50.1"
5
5
  end
data/sig/jsonschema.rbs CHANGED
@@ -17,11 +17,66 @@ module JSONSchema
17
17
 
18
18
  # Structural discriminant of a canonical schema, one per view class.
19
19
  type kind = :multi_type | :typed_group | :string | :integer | :number | :array
20
- | :object | :const | :enum | :any_of | :true | :false | :raw
20
+ | :object | :const | :enum | :not | :all_of | :any_of | :one_of
21
+ | :reference | :true | :false | :raw
22
+
23
+ # What an array requires of its elements: all distinct, some repeated, or neither.
24
+ type distinctness = :unconstrained | :all_distinct | :some_repeated
25
+
26
+ # Whether one schema admits every value another does. Only `:yes` proves containment: test
27
+ # for `:yes`, and treat `:unknown` like `:no`.
28
+ type containment = :yes | :no | :unknown
29
+
30
+ # Whether any value satisfies a schema. Only `:no` proves it admits nothing: test for `:no`,
31
+ # and treat `:unknown` like `:yes`.
32
+ type satisfiability = :yes | :no | :unknown
21
33
 
22
34
  type view = TrueView | FalseView | MultiTypeView | TypedGroupView | StringView
23
- | IntegerView | NumberView | ArrayView | ObjectView | AnyOfView
24
- | ConstView | EnumView | RawView
35
+ | IntegerView | NumberView | ArrayView | ObjectView | NotView
36
+ | AllOfView | AnyOfView | OneOfView | ReferenceView | ConstView
37
+ | EnumView | RawView
38
+
39
+ module Containment
40
+ YES: :yes
41
+ NO: :no
42
+ UNKNOWN: :unknown
43
+ ALL: Array[containment]
44
+ end
45
+
46
+ module Satisfiability
47
+ YES: :yes
48
+ NO: :no
49
+ UNKNOWN: :unknown
50
+ ALL: Array[satisfiability]
51
+ end
52
+
53
+ module Distinctness
54
+ UNCONSTRAINED: :unconstrained
55
+ ALL_DISTINCT: :all_distinct
56
+ SOME_REPEATED: :some_repeated
57
+ ALL: Array[distinctness]
58
+ end
59
+
60
+ module Kind
61
+ MULTI_TYPE: :multi_type
62
+ TYPED_GROUP: :typed_group
63
+ STRING: :string
64
+ INTEGER: :integer
65
+ NUMBER: :number
66
+ ARRAY: :array
67
+ OBJECT: :object
68
+ CONST: :const
69
+ ENUM: :enum
70
+ NOT: :not
71
+ ALL_OF: :all_of
72
+ ANY_OF: :any_of
73
+ ONE_OF: :one_of
74
+ REFERENCE: :reference
75
+ TRUE: :true
76
+ FALSE: :false
77
+ RAW: :raw
78
+ ALL: Array[kind]
79
+ end
25
80
 
26
81
  class CanonicalizationError < StandardError
27
82
  end
@@ -35,7 +90,10 @@ module JSONSchema
35
90
  class IncompatibleOperands < CanonicalizationError
36
91
  end
37
92
 
38
- class UnmodeledOperand < CanonicalizationError
93
+ class UnsupportedOperand < CanonicalizationError
94
+ end
95
+
96
+ class UnsupportedResult < CanonicalizationError
39
97
  end
40
98
 
41
99
  # A schema reduced to canonical form: schemas accepting the same values share one form.
@@ -45,11 +103,13 @@ module JSONSchema
45
103
  def kind: () -> kind
46
104
  def view: () -> view
47
105
  def intersect: (CanonicalSchema other) -> CanonicalSchema
48
- def is_subset_of: (CanonicalSchema other) -> bool?
49
- def negate: () -> CanonicalSchema?
106
+ def union: (CanonicalSchema other) -> CanonicalSchema
107
+ def subtract: (CanonicalSchema other) -> CanonicalSchema
108
+ def covers: (CanonicalSchema other) -> containment
109
+ def negate: () -> CanonicalSchema
50
110
  def definition: (String uri) -> CanonicalSchema?
51
111
  def definitions: () -> Hash[String, CanonicalSchema]
52
- def satisfiable?: () -> bool
112
+ def satisfiability: () -> satisfiability
53
113
  def inspect: () -> String
54
114
  def ==: (untyped other) -> bool
55
115
  def eql?: (untyped other) -> bool
@@ -121,7 +181,7 @@ module JSONSchema
121
181
  class ArrayView
122
182
  def min_items: () -> Integer?
123
183
  def max_items: () -> Integer?
124
- def distinctness: () -> (:unconstrained | :all_distinct | :some_repeated)
184
+ def distinctness: () -> distinctness
125
185
  def prefix_items: () -> Array[CanonicalSchema]
126
186
  def items: () -> CanonicalSchema?
127
187
  def contains: () -> Array[ContainsView]
@@ -129,7 +189,7 @@ module JSONSchema
129
189
  def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
130
190
  end
131
191
 
132
- # One `contains` demand of an array; an absent minimum spells the default of one.
192
+ # One `contains` requirement of an array; an absent minimum means the default of one.
133
193
  class ContainsView
134
194
  def schema: () -> CanonicalSchema
135
195
  def min_contains: () -> Integer?
@@ -168,6 +228,34 @@ module JSONSchema
168
228
  def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
169
229
  end
170
230
 
231
+ # The complement of an opaque schema.
232
+ class NotView
233
+ def schema: () -> CanonicalSchema
234
+ def inspect: () -> String
235
+ def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
236
+ end
237
+
238
+ # A value matches iff every branch matches.
239
+ class AllOfView
240
+ def branches: () -> Array[CanonicalSchema]
241
+ def inspect: () -> String
242
+ def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
243
+ end
244
+
245
+ # A value matches iff exactly one branch matches.
246
+ class OneOfView
247
+ def branches: () -> Array[CanonicalSchema]
248
+ def inspect: () -> String
249
+ def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
250
+ end
251
+
252
+ # The target a `$ref` names.
253
+ class ReferenceView
254
+ def uri: () -> String
255
+ def inspect: () -> String
256
+ def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
257
+ end
258
+
171
259
  # A value matches iff at least one branch matches.
172
260
  class AnyOfView
173
261
  def branches: () -> Array[CanonicalSchema]
@@ -189,7 +277,7 @@ module JSONSchema
189
277
  def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
190
278
  end
191
279
 
192
- # A schema the canonical form does not model structurally, kept verbatim.
280
+ # A schema the canonical form does not support structurally, kept verbatim.
193
281
  class RawView
194
282
  def schema: () -> untyped
195
283
  def inspect: () -> String
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonschema_rs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.49.9
4
+ version: 0.50.1
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Dmitry Dygalo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-09 00:00:00.000000000 Z
11
+ date: 2026-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal