json_schemer 0.2.18 → 2.1.1
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 +4 -4
 - data/.github/workflows/ci.yml +3 -7
 - data/CHANGELOG.md +69 -0
 - data/Gemfile.lock +28 -10
 - data/README.md +379 -4
 - data/bin/hostname_character_classes +42 -0
 - data/bin/rake +29 -0
 - data/exe/json_schemer +62 -0
 - data/json_schemer.gemspec +6 -12
 - data/lib/json_schemer/cached_resolver.rb +16 -0
 - data/lib/json_schemer/content.rb +18 -0
 - data/lib/json_schemer/draft201909/meta.rb +320 -0
 - data/lib/json_schemer/draft201909/vocab/applicator.rb +104 -0
 - data/lib/json_schemer/draft201909/vocab/core.rb +45 -0
 - data/lib/json_schemer/draft201909/vocab.rb +31 -0
 - data/lib/json_schemer/draft202012/meta.rb +364 -0
 - data/lib/json_schemer/draft202012/vocab/applicator.rb +382 -0
 - data/lib/json_schemer/draft202012/vocab/content.rb +52 -0
 - data/lib/json_schemer/draft202012/vocab/core.rb +160 -0
 - data/lib/json_schemer/draft202012/vocab/format_annotation.rb +23 -0
 - data/lib/json_schemer/draft202012/vocab/format_assertion.rb +23 -0
 - data/lib/json_schemer/draft202012/vocab/meta_data.rb +30 -0
 - data/lib/json_schemer/draft202012/vocab/unevaluated.rb +94 -0
 - data/lib/json_schemer/draft202012/vocab/validation.rb +286 -0
 - data/lib/json_schemer/draft202012/vocab.rb +105 -0
 - data/lib/json_schemer/draft4/meta.rb +161 -0
 - data/lib/json_schemer/draft4/vocab/validation.rb +39 -0
 - data/lib/json_schemer/draft4/vocab.rb +18 -0
 - data/lib/json_schemer/draft6/meta.rb +172 -0
 - data/lib/json_schemer/draft6/vocab.rb +16 -0
 - data/lib/json_schemer/draft7/meta.rb +183 -0
 - data/lib/json_schemer/draft7/vocab/validation.rb +69 -0
 - data/lib/json_schemer/draft7/vocab.rb +30 -0
 - data/lib/json_schemer/ecma_regexp.rb +51 -0
 - data/lib/json_schemer/errors.rb +1 -0
 - data/lib/json_schemer/format/duration.rb +23 -0
 - data/lib/json_schemer/format/email.rb +56 -0
 - data/lib/json_schemer/format/hostname.rb +58 -0
 - data/lib/json_schemer/format/json_pointer.rb +18 -0
 - data/lib/json_schemer/format/uri_template.rb +34 -0
 - data/lib/json_schemer/format.rb +129 -109
 - data/lib/json_schemer/keyword.rb +53 -0
 - data/lib/json_schemer/location.rb +25 -0
 - data/lib/json_schemer/openapi.rb +40 -0
 - data/lib/json_schemer/openapi30/document.rb +1672 -0
 - data/lib/json_schemer/openapi30/meta.rb +32 -0
 - data/lib/json_schemer/openapi30/vocab/base.rb +18 -0
 - data/lib/json_schemer/openapi30/vocab.rb +12 -0
 - data/lib/json_schemer/openapi31/document.rb +1557 -0
 - data/lib/json_schemer/openapi31/meta.rb +136 -0
 - data/lib/json_schemer/openapi31/vocab/base.rb +127 -0
 - data/lib/json_schemer/openapi31/vocab.rb +18 -0
 - data/lib/json_schemer/output.rb +56 -0
 - data/lib/json_schemer/result.rb +229 -0
 - data/lib/json_schemer/schema.rb +423 -0
 - data/lib/json_schemer/version.rb +1 -1
 - data/lib/json_schemer.rb +218 -28
 - metadata +98 -25
 - data/lib/json_schemer/cached_ref_resolver.rb +0 -14
 - data/lib/json_schemer/schema/base.rb +0 -658
 - data/lib/json_schemer/schema/draft4.rb +0 -44
 - data/lib/json_schemer/schema/draft6.rb +0 -25
 - data/lib/json_schemer/schema/draft7.rb +0 -32
 
| 
         @@ -0,0 +1,58 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module JSONSchemer
         
     | 
| 
      
 3 
     | 
    
         
            +
              module Format
         
     | 
| 
      
 4 
     | 
    
         
            +
                module Hostname
         
     | 
| 
      
 5 
     | 
    
         
            +
                  # https://datatracker.ietf.org/doc/html/rfc5892#section-2.1
         
     | 
| 
      
 6 
     | 
    
         
            +
                  MARKS = '\p{Mn}\p{Mc}'
         
     | 
| 
      
 7 
     | 
    
         
            +
                  LETTER_DIGITS = "\\p{Ll}\\p{Lu}\\p{Lo}\\p{Nd}\\p{Lm}#{MARKS}"
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # https://datatracker.ietf.org/doc/html/rfc5892#section-2.6
         
     | 
| 
      
 9 
     | 
    
         
            +
                  EXCEPTIONS_PVALID = '\u{06FD}\u{06FE}\u{0F0B}\u{3007}' # \u{00DF}\u{03C2} covered by \p{Ll}
         
     | 
| 
      
 10 
     | 
    
         
            +
                  EXCEPTIONS_DISALLOWED = '\u{0640}\u{07FA}\u{302E}\u{302F}\u{3031}\u{3032}\u{3033}\u{3034}\u{3035}\u{303B}'
         
     | 
| 
      
 11 
     | 
    
         
            +
                  LABEL_CHARACTER_CLASS = "[#{LETTER_DIGITS}#{EXCEPTIONS_PVALID}&&[^#{EXCEPTIONS_DISALLOWED}]]"
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # https://datatracker.ietf.org/doc/html/rfc5891#section-4.2.3.2
         
     | 
| 
      
 13 
     | 
    
         
            +
                  LEADING_CHARACTER_CLASS = "[#{LABEL_CHARACTER_CLASS}&&[^#{MARKS}]]"
         
     | 
| 
      
 14 
     | 
    
         
            +
                  LABEL_REGEX_STRING = "#{LEADING_CHARACTER_CLASS}([#{LABEL_CHARACTER_CLASS}\-]*#{LABEL_CHARACTER_CLASS})?"
         
     | 
| 
      
 15 
     | 
    
         
            +
                  HOSTNAME_REGEX = /\A(#{LABEL_REGEX_STRING}\.)*#{LABEL_REGEX_STRING}\z/i.freeze
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # bin/hostname_character_classes
         
     | 
| 
      
 17 
     | 
    
         
            +
                  VIRAMA_CHARACTER_CLASS = '[\u{094D}\u{09CD}\u{0A4D}\u{0ACD}\u{0B4D}\u{0BCD}\u{0C4D}\u{0CCD}\u{0D3B}\u{0D3C}\u{0D4D}\u{0DCA}\u{0E3A}\u{0EBA}\u{0F84}\u{1039}\u{103A}\u{1714}\u{1715}\u{1734}\u{17D2}\u{1A60}\u{1B44}\u{1BAA}\u{1BAB}\u{1BF2}\u{1BF3}\u{2D7F}\u{A806}\u{A82C}\u{A8C4}\u{A953}\u{A9C0}\u{AAF6}\u{ABED}\u{10A3F}\u{11046}\u{11070}\u{1107F}\u{110B9}\u{11133}\u{11134}\u{111C0}\u{11235}\u{112EA}\u{1134D}\u{11442}\u{114C2}\u{115BF}\u{1163F}\u{116B6}\u{1172B}\u{11839}\u{1193D}\u{1193E}\u{119E0}\u{11A34}\u{11A47}\u{11A99}\u{11C3F}\u{11D44}\u{11D45}\u{11D97}\u{11F41}\u{11F42}]'
         
     | 
| 
      
 18 
     | 
    
         
            +
                  JOINING_TYPE_L_CHARACTER_CLASS = '[\u{A872}\u{10ACD}\u{10AD7}\u{10D00}\u{10FCB}]'
         
     | 
| 
      
 19 
     | 
    
         
            +
                  JOINING_TYPE_D_CHARACTER_CLASS = '[\u{0620}\u{0626}\u{0628}\u{062A}-\u{062E}\u{0633}-\u{063F}\u{0641}-\u{0647}\u{0649}-\u{064A}\u{066E}-\u{066F}\u{0678}-\u{0687}\u{069A}-\u{06BF}\u{06C1}-\u{06C2}\u{06CC}\u{06CE}\u{06D0}-\u{06D1}\u{06FA}-\u{06FC}\u{06FF}\u{0712}-\u{0714}\u{071A}-\u{071D}\u{071F}-\u{0727}\u{0729}\u{072B}\u{072D}-\u{072E}\u{074E}-\u{0758}\u{075C}-\u{076A}\u{076D}-\u{0770}\u{0772}\u{0775}-\u{0777}\u{077A}-\u{077F}\u{07CA}-\u{07EA}\u{0841}-\u{0845}\u{0848}\u{084A}-\u{0853}\u{0855}\u{0860}\u{0862}-\u{0865}\u{0868}\u{0886}\u{0889}-\u{088D}\u{08A0}-\u{08A9}\u{08AF}-\u{08B0}\u{08B3}-\u{08B8}\u{08BA}-\u{08C8}\u{1807}\u{1820}-\u{1842}\u{1843}\u{1844}-\u{1878}\u{1887}-\u{18A8}\u{18AA}\u{A840}-\u{A871}\u{10AC0}-\u{10AC4}\u{10AD3}-\u{10AD6}\u{10AD8}-\u{10ADC}\u{10ADE}-\u{10AE0}\u{10AEB}-\u{10AEE}\u{10B80}\u{10B82}\u{10B86}-\u{10B88}\u{10B8A}-\u{10B8B}\u{10B8D}\u{10B90}\u{10BAD}-\u{10BAE}\u{10D01}-\u{10D21}\u{10D23}\u{10F30}-\u{10F32}\u{10F34}-\u{10F44}\u{10F51}-\u{10F53}\u{10F70}-\u{10F73}\u{10F76}-\u{10F81}\u{10FB0}\u{10FB2}-\u{10FB3}\u{10FB8}\u{10FBB}-\u{10FBC}\u{10FBE}-\u{10FBF}\u{10FC1}\u{10FC4}\u{10FCA}\u{1E900}-\u{1E943}]'
         
     | 
| 
      
 20 
     | 
    
         
            +
                  JOINING_TYPE_T_CHARACTER_CLASS = '[\u{00AD}\u{0300}-\u{036F}\u{0483}-\u{0487}\u{0488}-\u{0489}\u{0591}-\u{05BD}\u{05BF}\u{05C1}-\u{05C2}\u{05C4}-\u{05C5}\u{05C7}\u{0610}-\u{061A}\u{061C}\u{064B}-\u{065F}\u{0670}\u{06D6}-\u{06DC}\u{06DF}-\u{06E4}\u{06E7}-\u{06E8}\u{06EA}-\u{06ED}\u{070F}\u{0711}\u{0730}-\u{074A}\u{07A6}-\u{07B0}\u{07EB}-\u{07F3}\u{07FD}\u{0816}-\u{0819}\u{081B}-\u{0823}\u{0825}-\u{0827}\u{0829}-\u{082D}\u{0859}-\u{085B}\u{0898}-\u{089F}\u{08CA}-\u{08E1}\u{08E3}-\u{0902}\u{093A}\u{093C}\u{0941}-\u{0948}\u{094D}\u{0951}-\u{0957}\u{0962}-\u{0963}\u{0981}\u{09BC}\u{09C1}-\u{09C4}\u{09CD}\u{09E2}-\u{09E3}\u{09FE}\u{0A01}-\u{0A02}\u{0A3C}\u{0A41}-\u{0A42}\u{0A47}-\u{0A48}\u{0A4B}-\u{0A4D}\u{0A51}\u{0A70}-\u{0A71}\u{0A75}\u{0A81}-\u{0A82}\u{0ABC}\u{0AC1}-\u{0AC5}\u{0AC7}-\u{0AC8}\u{0ACD}\u{0AE2}-\u{0AE3}\u{0AFA}-\u{0AFF}\u{0B01}\u{0B3C}\u{0B3F}\u{0B41}-\u{0B44}\u{0B4D}\u{0B55}-\u{0B56}\u{0B62}-\u{0B63}\u{0B82}\u{0BC0}\u{0BCD}\u{0C00}\u{0C04}\u{0C3C}\u{0C3E}-\u{0C40}\u{0C46}-\u{0C48}\u{0C4A}-\u{0C4D}\u{0C55}-\u{0C56}\u{0C62}-\u{0C63}\u{0C81}\u{0CBC}\u{0CBF}\u{0CC6}\u{0CCC}-\u{0CCD}\u{0CE2}-\u{0CE3}\u{0D00}-\u{0D01}\u{0D3B}-\u{0D3C}\u{0D41}-\u{0D44}\u{0D4D}\u{0D62}-\u{0D63}\u{0D81}\u{0DCA}\u{0DD2}-\u{0DD4}\u{0DD6}\u{0E31}\u{0E34}-\u{0E3A}\u{0E47}-\u{0E4E}\u{0EB1}\u{0EB4}-\u{0EBC}\u{0EC8}-\u{0ECE}\u{0F18}-\u{0F19}\u{0F35}\u{0F37}\u{0F39}\u{0F71}-\u{0F7E}\u{0F80}-\u{0F84}\u{0F86}-\u{0F87}\u{0F8D}-\u{0F97}\u{0F99}-\u{0FBC}\u{0FC6}\u{102D}-\u{1030}\u{1032}-\u{1037}\u{1039}-\u{103A}\u{103D}-\u{103E}\u{1058}-\u{1059}\u{105E}-\u{1060}\u{1071}-\u{1074}\u{1082}\u{1085}-\u{1086}\u{108D}\u{109D}\u{135D}-\u{135F}\u{1712}-\u{1714}\u{1732}-\u{1733}\u{1752}-\u{1753}\u{1772}-\u{1773}\u{17B4}-\u{17B5}\u{17B7}-\u{17BD}\u{17C6}\u{17C9}-\u{17D3}\u{17DD}\u{180B}-\u{180D}\u{180F}\u{1885}-\u{1886}\u{18A9}\u{1920}-\u{1922}\u{1927}-\u{1928}\u{1932}\u{1939}-\u{193B}\u{1A17}-\u{1A18}\u{1A1B}\u{1A56}\u{1A58}-\u{1A5E}\u{1A60}\u{1A62}\u{1A65}-\u{1A6C}\u{1A73}-\u{1A7C}\u{1A7F}\u{1AB0}-\u{1ABD}\u{1ABE}\u{1ABF}-\u{1ACE}\u{1B00}-\u{1B03}\u{1B34}\u{1B36}-\u{1B3A}\u{1B3C}\u{1B42}\u{1B6B}-\u{1B73}\u{1B80}-\u{1B81}\u{1BA2}-\u{1BA5}\u{1BA8}-\u{1BA9}\u{1BAB}-\u{1BAD}\u{1BE6}\u{1BE8}-\u{1BE9}\u{1BED}\u{1BEF}-\u{1BF1}\u{1C2C}-\u{1C33}\u{1C36}-\u{1C37}\u{1CD0}-\u{1CD2}\u{1CD4}-\u{1CE0}\u{1CE2}-\u{1CE8}\u{1CED}\u{1CF4}\u{1CF8}-\u{1CF9}\u{1DC0}-\u{1DFF}\u{200B}\u{200E}-\u{200F}\u{202A}-\u{202E}\u{2060}-\u{2064}\u{206A}-\u{206F}\u{20D0}-\u{20DC}\u{20DD}-\u{20E0}\u{20E1}\u{20E2}-\u{20E4}\u{20E5}-\u{20F0}\u{2CEF}-\u{2CF1}\u{2D7F}\u{2DE0}-\u{2DFF}\u{302A}-\u{302D}\u{3099}-\u{309A}\u{A66F}\u{A670}-\u{A672}\u{A674}-\u{A67D}\u{A69E}-\u{A69F}\u{A6F0}-\u{A6F1}\u{A802}\u{A806}\u{A80B}\u{A825}-\u{A826}\u{A82C}\u{A8C4}-\u{A8C5}\u{A8E0}-\u{A8F1}\u{A8FF}\u{A926}-\u{A92D}\u{A947}-\u{A951}\u{A980}-\u{A982}\u{A9B3}\u{A9B6}-\u{A9B9}\u{A9BC}-\u{A9BD}\u{A9E5}\u{AA29}-\u{AA2E}\u{AA31}-\u{AA32}\u{AA35}-\u{AA36}\u{AA43}\u{AA4C}\u{AA7C}\u{AAB0}\u{AAB2}-\u{AAB4}\u{AAB7}-\u{AAB8}\u{AABE}-\u{AABF}\u{AAC1}\u{AAEC}-\u{AAED}\u{AAF6}\u{ABE5}\u{ABE8}\u{ABED}\u{FB1E}\u{FE00}-\u{FE0F}\u{FE20}-\u{FE2F}\u{FEFF}\u{FFF9}-\u{FFFB}\u{101FD}\u{102E0}\u{10376}-\u{1037A}\u{10A01}-\u{10A03}\u{10A05}-\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}-\u{10AE6}\u{10D24}-\u{10D27}\u{10EAB}-\u{10EAC}\u{10EFD}-\u{10EFF}\u{10F46}-\u{10F50}\u{10F82}-\u{10F85}\u{11001}\u{11038}-\u{11046}\u{11070}\u{11073}-\u{11074}\u{1107F}-\u{11081}\u{110B3}-\u{110B6}\u{110B9}-\u{110BA}\u{110C2}\u{11100}-\u{11102}\u{11127}-\u{1112B}\u{1112D}-\u{11134}\u{11173}\u{11180}-\u{11181}\u{111B6}-\u{111BE}\u{111C9}-\u{111CC}\u{111CF}\u{1122F}-\u{11231}\u{11234}\u{11236}-\u{11237}\u{1123E}\u{11241}\u{112DF}\u{112E3}-\u{112EA}\u{11300}-\u{11301}\u{1133B}-\u{1133C}\u{11340}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11438}-\u{1143F}\u{11442}-\u{11444}\u{11446}\u{1145E}\u{114B3}-\u{114B8}\u{114BA}\u{114BF}-\u{114C0}\u{114C2}-\u{114C3}\u{115B2}-\u{115B5}\u{115BC}-\u{115BD}\u{115BF}-\u{115C0}\u{115DC}-\u{115DD}\u{11633}-\u{1163A}\u{1163D}\u{1163F}-\u{11640}\u{116AB}\u{116AD}\u{116B0}-\u{116B5}\u{116B7}\u{1171D}-\u{1171F}\u{11722}-\u{11725}\u{11727}-\u{1172B}\u{1182F}-\u{11837}\u{11839}-\u{1183A}\u{1193B}-\u{1193C}\u{1193E}\u{11943}\u{119D4}-\u{119D7}\u{119DA}-\u{119DB}\u{119E0}\u{11A01}-\u{11A0A}\u{11A33}-\u{11A38}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A56}\u{11A59}-\u{11A5B}\u{11A8A}-\u{11A96}\u{11A98}-\u{11A99}\u{11C30}-\u{11C36}\u{11C38}-\u{11C3D}\u{11C3F}\u{11C92}-\u{11CA7}\u{11CAA}-\u{11CB0}\u{11CB2}-\u{11CB3}\u{11CB5}-\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}-\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{11D90}-\u{11D91}\u{11D95}\u{11D97}\u{11EF3}-\u{11EF4}\u{11F00}-\u{11F01}\u{11F36}-\u{11F3A}\u{11F40}\u{11F42}\u{13430}-\u{1343F}\u{13440}\u{13447}-\u{13455}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F4F}\u{16F8F}-\u{16F92}\u{16FE4}\u{1BC9D}-\u{1BC9E}\u{1BCA0}-\u{1BCA3}\u{1CF00}-\u{1CF2D}\u{1CF30}-\u{1CF46}\u{1D167}-\u{1D169}\u{1D173}-\u{1D17A}\u{1D17B}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D242}-\u{1D244}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}-\u{1E024}\u{1E026}-\u{1E02A}\u{1E08F}\u{1E130}-\u{1E136}\u{1E2AE}\u{1E2EC}-\u{1E2EF}\u{1E4EC}-\u{1E4EF}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{1E94B}\u{E0001}\u{E0020}-\u{E007F}\u{E0100}-\u{E01EF}]'
         
     | 
| 
      
 21 
     | 
    
         
            +
                  JOINING_TYPE_R_CHARACTER_CLASS = '[\u{0622}-\u{0625}\u{0627}\u{0629}\u{062F}-\u{0632}\u{0648}\u{0671}-\u{0673}\u{0675}-\u{0677}\u{0688}-\u{0699}\u{06C0}\u{06C3}-\u{06CB}\u{06CD}\u{06CF}\u{06D2}-\u{06D3}\u{06D5}\u{06EE}-\u{06EF}\u{0710}\u{0715}-\u{0719}\u{071E}\u{0728}\u{072A}\u{072C}\u{072F}\u{074D}\u{0759}-\u{075B}\u{076B}-\u{076C}\u{0771}\u{0773}-\u{0774}\u{0778}-\u{0779}\u{0840}\u{0846}-\u{0847}\u{0849}\u{0854}\u{0856}-\u{0858}\u{0867}\u{0869}-\u{086A}\u{0870}-\u{0882}\u{088E}\u{08AA}-\u{08AC}\u{08AE}\u{08B1}-\u{08B2}\u{08B9}\u{10AC5}\u{10AC7}\u{10AC9}-\u{10ACA}\u{10ACE}-\u{10AD2}\u{10ADD}\u{10AE1}\u{10AE4}\u{10AEF}\u{10B81}\u{10B83}-\u{10B85}\u{10B89}\u{10B8C}\u{10B8E}-\u{10B8F}\u{10B91}\u{10BA9}-\u{10BAC}\u{10D22}\u{10F33}\u{10F54}\u{10F74}-\u{10F75}\u{10FB4}-\u{10FB6}\u{10FB9}-\u{10FBA}\u{10FBD}\u{10FC2}-\u{10FC3}\u{10FC9}]'
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.1
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.2
         
     | 
| 
      
 24 
     | 
    
         
            +
                  ZERO_WIDTH_VIRAMA = "#{VIRAMA_CHARACTER_CLASS}[\\u{200C}\\u{200D}]"
         
     | 
| 
      
 25 
     | 
    
         
            +
                  ZERO_WIDTH_NON_JOINER_JOINING_TYPE = "[#{JOINING_TYPE_L_CHARACTER_CLASS}#{JOINING_TYPE_D_CHARACTER_CLASS}]#{JOINING_TYPE_T_CHARACTER_CLASS}*\\u{200C}#{JOINING_TYPE_T_CHARACTER_CLASS}*[#{JOINING_TYPE_R_CHARACTER_CLASS}#{JOINING_TYPE_D_CHARACTER_CLASS}]"
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.3
         
     | 
| 
      
 27 
     | 
    
         
            +
                  MIDDLE_DOT = '\u{006C}\u{00B7}\u{006C}'
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.4
         
     | 
| 
      
 29 
     | 
    
         
            +
                  GREEK_LOWER_NUMERAL_SIGN = '\u{0375}\p{Greek}'
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.5
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.6
         
     | 
| 
      
 32 
     | 
    
         
            +
                  HEBREW_PUNCTUATION = '\p{Hebrew}[\u{05F3}\u{05F4}]'
         
     | 
| 
      
 33 
     | 
    
         
            +
                  CONTEXT_REGEX = /(#{ZERO_WIDTH_VIRAMA}|#{ZERO_WIDTH_NON_JOINER_JOINING_TYPE}|#{MIDDLE_DOT}|#{GREEK_LOWER_NUMERAL_SIGN}|#{HEBREW_PUNCTUATION})/.freeze
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.7
         
     | 
| 
      
 35 
     | 
    
         
            +
                  KATAKANA_MIDDLE_DOT_REGEX = /\u{30FB}/.freeze
         
     | 
| 
      
 36 
     | 
    
         
            +
                  KATAKANA_MIDDLE_DOT_CONTEXT_REGEX = /[\p{Hiragana}\p{Katakana}\p{Han}]/.freeze
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.8
         
     | 
| 
      
 38 
     | 
    
         
            +
                  # https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.9
         
     | 
| 
      
 39 
     | 
    
         
            +
                  ARABIC_INDIC_DIGITS_REGEX = /[\u{0660}-\u{0669}]/.freeze
         
     | 
| 
      
 40 
     | 
    
         
            +
                  ARABIC_EXTENDED_DIGITS_REGEX = /[\u{06F0}-\u{06F9}]/.freeze
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  def valid_hostname?(data)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    data.split('.').map do |a_label|
         
     | 
| 
      
 44 
     | 
    
         
            +
                      return false if a_label.size > 63
         
     | 
| 
      
 45 
     | 
    
         
            +
                      u_label = SimpleIDN.to_unicode(a_label)
         
     | 
| 
      
 46 
     | 
    
         
            +
                      # https://datatracker.ietf.org/doc/html/rfc5891#section-4.2.3.1
         
     | 
| 
      
 47 
     | 
    
         
            +
                      return false if u_label.slice(2, 2) == '--'
         
     | 
| 
      
 48 
     | 
    
         
            +
                      return false if ARABIC_INDIC_DIGITS_REGEX.match?(u_label) && ARABIC_EXTENDED_DIGITS_REGEX.match?(u_label)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      u_label.gsub!(CONTEXT_REGEX, 'ok')
         
     | 
| 
      
 50 
     | 
    
         
            +
                      u_label.gsub!(KATAKANA_MIDDLE_DOT_REGEX, 'ok') if KATAKANA_MIDDLE_DOT_CONTEXT_REGEX.match?(u_label)
         
     | 
| 
      
 51 
     | 
    
         
            +
                      u_label
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end.join('.').match?(HOSTNAME_REGEX)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  rescue SimpleIDN::ConversionError
         
     | 
| 
      
 54 
     | 
    
         
            +
                    false
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module JSONSchemer
         
     | 
| 
      
 3 
     | 
    
         
            +
              module Format
         
     | 
| 
      
 4 
     | 
    
         
            +
                module JSONPointer
         
     | 
| 
      
 5 
     | 
    
         
            +
                  JSON_POINTER_REGEX_STRING = '(\/([^~\/]|~[01])*)*'
         
     | 
| 
      
 6 
     | 
    
         
            +
                  JSON_POINTER_REGEX = /\A#{JSON_POINTER_REGEX_STRING}\z/.freeze
         
     | 
| 
      
 7 
     | 
    
         
            +
                  RELATIVE_JSON_POINTER_REGEX = /\A(0|[1-9]\d*)(#|#{JSON_POINTER_REGEX_STRING})?\z/.freeze
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  def valid_json_pointer?(data)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    JSON_POINTER_REGEX.match?(data)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  def valid_relative_json_pointer?(data)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    RELATIVE_JSON_POINTER_REGEX.match?(data)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module JSONSchemer
         
     | 
| 
      
 3 
     | 
    
         
            +
              module Format
         
     | 
| 
      
 4 
     | 
    
         
            +
                module URITemplate
         
     | 
| 
      
 5 
     | 
    
         
            +
                  # https://datatracker.ietf.org/doc/html/rfc6570
         
     | 
| 
      
 6 
     | 
    
         
            +
                  PCT_ENCODED = '%\h{2}'                                         # pct-encoded     =  "%" HEXDIG HEXDIG
         
     | 
| 
      
 7 
     | 
    
         
            +
                  EXPLODE = '\*'                                                 # explode         =  "*"
         
     | 
| 
      
 8 
     | 
    
         
            +
                  MAX_LENGTH = '[1-9]\d{0,3}'                                    # max-length      =  %x31-39 0*3DIGIT   ; positive integer < 10000
         
     | 
| 
      
 9 
     | 
    
         
            +
                  PREFIX = ":#{MAX_LENGTH}"                                      # prefix          =  ":" max-length
         
     | 
| 
      
 10 
     | 
    
         
            +
                  MODIFIER_LEVEL4 = "#{PREFIX}|#{EXPLODE}"                       # modifier-level4 =  prefix / explode
         
     | 
| 
      
 11 
     | 
    
         
            +
                  VARCHAR = "(\\w|#{PCT_ENCODED})"                               # varchar         =  ALPHA / DIGIT / "_" / pct-encoded
         
     | 
| 
      
 12 
     | 
    
         
            +
                  VARNAME = "#{VARCHAR}(\\.?#{VARCHAR})*"                        # varname         =  varchar *( ["."] varchar )
         
     | 
| 
      
 13 
     | 
    
         
            +
                  VARSPEC = "#{VARNAME}(#{MODIFIER_LEVEL4})?"                    # varspec         =  varname [ modifier-level4 ]
         
     | 
| 
      
 14 
     | 
    
         
            +
                  VARIABLE_LIST = "#{VARSPEC}(,#{VARSPEC})*"                     # variable-list   =  varspec *( "," varspec )
         
     | 
| 
      
 15 
     | 
    
         
            +
                  OPERATOR = '[+#./;?&=,!@|]'                                    # operator        =  op-level2 / op-level3 / op-reserve
         
     | 
| 
      
 16 
     | 
    
         
            +
                                                                                 # op-level2       =  "+" / "#"
         
     | 
| 
      
 17 
     | 
    
         
            +
                                                                                 # op-level3       =  "." / "/" / ";" / "?" / "&"
         
     | 
| 
      
 18 
     | 
    
         
            +
                                                                                 # op-reserve      =  "=" / "," / "!" / "@" / "|"
         
     | 
| 
      
 19 
     | 
    
         
            +
                  EXPRESSION = "{#{OPERATOR}?#{VARIABLE_LIST}}"                  # expression      =  "{" [ operator ] variable-list "}"
         
     | 
| 
      
 20 
     | 
    
         
            +
                  LITERALS = "[^\\x00-\\x20\\x7F\"%'<>\\\\^`{|}]|#{PCT_ENCODED}" # literals        =  %x21 / %x23-24 / %x26 / %x28-3B / %x3D / %x3F-5B
         
     | 
| 
      
 21 
     | 
    
         
            +
                                                                                 #                 /  %x5D / %x5F / %x61-7A / %x7E / ucschar / iprivate
         
     | 
| 
      
 22 
     | 
    
         
            +
                                                                                 #                 /  pct-encoded
         
     | 
| 
      
 23 
     | 
    
         
            +
                                                                                 #                      ; any Unicode character except: CTL, SP,
         
     | 
| 
      
 24 
     | 
    
         
            +
                                                                                 #                      ;  DQUOTE, "'", "%" (aside from pct-encoded),
         
     | 
| 
      
 25 
     | 
    
         
            +
                                                                                 #                      ;  "<", ">", "\", "^", "`", "{", "|", "}"
         
     | 
| 
      
 26 
     | 
    
         
            +
                  URI_TEMPLATE = "(#{LITERALS}|#{EXPRESSION})*"                  # URI-Template    = *( literals / expression )
         
     | 
| 
      
 27 
     | 
    
         
            +
                  URI_TEMPLATE_REGEX = /\A#{URI_TEMPLATE}\z/
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def valid_uri_template?(data)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    URI_TEMPLATE_REGEX.match?(data)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/json_schemer/format.rb
    CHANGED
    
    | 
         @@ -1,128 +1,148 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         
             
            module JSONSchemer
         
     | 
| 
       3 
3 
     | 
    
         
             
              module Format
         
     | 
| 
       4 
     | 
    
         
            -
                #  
     | 
| 
       5 
     | 
    
         
            -
                 
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
                HOSTNAME_REGEX = /\A(#{LABEL_REGEX_STRING}\.)*#{LABEL_REGEX_STRING}\z/i.freeze
         
     | 
| 
       8 
     | 
    
         
            -
                JSON_POINTER_REGEX_STRING = '(\/([^~\/]|~[01])*)*'
         
     | 
| 
       9 
     | 
    
         
            -
                JSON_POINTER_REGEX = /\A#{JSON_POINTER_REGEX_STRING}\z/.freeze
         
     | 
| 
       10 
     | 
    
         
            -
                RELATIVE_JSON_POINTER_REGEX = /\A(0|[1-9]\d*)(#|#{JSON_POINTER_REGEX_STRING})?\z/.freeze
         
     | 
| 
       11 
     | 
    
         
            -
                DATE_TIME_OFFSET_REGEX = /(Z|[\+\-]([01][0-9]|2[0-3]):[0-5][0-9])\z/i.freeze
         
     | 
| 
       12 
     | 
    
         
            -
                INVALID_QUERY_REGEX = /[[:space:]]/.freeze
         
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
                def valid_spec_format?(data, format)
         
     | 
| 
       15 
     | 
    
         
            -
                  case format
         
     | 
| 
       16 
     | 
    
         
            -
                  when 'date-time'
         
     | 
| 
       17 
     | 
    
         
            -
                    valid_date_time?(data)
         
     | 
| 
       18 
     | 
    
         
            -
                  when 'date'
         
     | 
| 
       19 
     | 
    
         
            -
                    valid_date_time?("#{data}T04:05:06.123456789+07:00")
         
     | 
| 
       20 
     | 
    
         
            -
                  when 'time'
         
     | 
| 
       21 
     | 
    
         
            -
                    valid_date_time?("2001-02-03T#{data}")
         
     | 
| 
       22 
     | 
    
         
            -
                  when 'email'
         
     | 
| 
       23 
     | 
    
         
            -
                    data.ascii_only? && valid_email?(data)
         
     | 
| 
       24 
     | 
    
         
            -
                  when 'idn-email'
         
     | 
| 
       25 
     | 
    
         
            -
                    valid_email?(data)
         
     | 
| 
       26 
     | 
    
         
            -
                  when 'hostname'
         
     | 
| 
       27 
     | 
    
         
            -
                    data.ascii_only? && valid_hostname?(data)
         
     | 
| 
       28 
     | 
    
         
            -
                  when 'idn-hostname'
         
     | 
| 
       29 
     | 
    
         
            -
                    valid_hostname?(data)
         
     | 
| 
       30 
     | 
    
         
            -
                  when 'ipv4'
         
     | 
| 
       31 
     | 
    
         
            -
                    valid_ip?(data, :v4)
         
     | 
| 
       32 
     | 
    
         
            -
                  when 'ipv6'
         
     | 
| 
       33 
     | 
    
         
            -
                    valid_ip?(data, :v6)
         
     | 
| 
       34 
     | 
    
         
            -
                  when 'uri'
         
     | 
| 
       35 
     | 
    
         
            -
                    valid_uri?(data)
         
     | 
| 
       36 
     | 
    
         
            -
                  when 'uri-reference'
         
     | 
| 
       37 
     | 
    
         
            -
                    valid_uri_reference?(data)
         
     | 
| 
       38 
     | 
    
         
            -
                  when 'iri'
         
     | 
| 
       39 
     | 
    
         
            -
                    valid_uri?(iri_escape(data))
         
     | 
| 
       40 
     | 
    
         
            -
                  when 'iri-reference'
         
     | 
| 
       41 
     | 
    
         
            -
                    valid_uri_reference?(iri_escape(data))
         
     | 
| 
       42 
     | 
    
         
            -
                  when 'uri-template'
         
     | 
| 
       43 
     | 
    
         
            -
                    valid_uri_template?(data)
         
     | 
| 
       44 
     | 
    
         
            -
                  when 'json-pointer'
         
     | 
| 
       45 
     | 
    
         
            -
                    valid_json_pointer?(data)
         
     | 
| 
       46 
     | 
    
         
            -
                  when 'relative-json-pointer'
         
     | 
| 
       47 
     | 
    
         
            -
                    valid_relative_json_pointer?(data)
         
     | 
| 
       48 
     | 
    
         
            -
                  when 'regex'
         
     | 
| 
       49 
     | 
    
         
            -
                    EcmaReValidator.valid?(data)
         
     | 
| 
       50 
     | 
    
         
            -
                  end
         
     | 
| 
      
 4 
     | 
    
         
            +
                # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-7.3
         
     | 
| 
      
 5 
     | 
    
         
            +
                DATE_TIME = proc do |instance, _format|
         
     | 
| 
      
 6 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_date_time?(instance)
         
     | 
| 
       51 
7 
     | 
    
         
             
                end
         
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
                  JSON.parse(data)
         
     | 
| 
       55 
     | 
    
         
            -
                  true
         
     | 
| 
       56 
     | 
    
         
            -
                rescue JSON::ParserError
         
     | 
| 
       57 
     | 
    
         
            -
                  false
         
     | 
| 
      
 8 
     | 
    
         
            +
                DATE = proc do |instance, _format|
         
     | 
| 
      
 9 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_date_time?("#{instance}T04:05:06.123456789+07:00")
         
     | 
| 
       58 
10 
     | 
    
         
             
                end
         
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
                  DateTime.rfc3339(data)
         
     | 
| 
       62 
     | 
    
         
            -
                  DATE_TIME_OFFSET_REGEX.match?(data)
         
     | 
| 
       63 
     | 
    
         
            -
                rescue ArgumentError => e
         
     | 
| 
       64 
     | 
    
         
            -
                  false
         
     | 
| 
      
 11 
     | 
    
         
            +
                TIME = proc do |instance, _format|
         
     | 
| 
      
 12 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_date_time?("2001-02-03T#{instance}")
         
     | 
| 
       65 
13 
     | 
    
         
             
                end
         
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
                  EMAIL_REGEX.match?(data)
         
     | 
| 
      
 14 
     | 
    
         
            +
                DURATION = proc do |instance, _format|
         
     | 
| 
      
 15 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_duration?(instance)
         
     | 
| 
       69 
16 
     | 
    
         
             
                end
         
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
                 
     | 
| 
       72 
     | 
    
         
            -
                   
     | 
| 
      
 17 
     | 
    
         
            +
                # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-7.3.2
         
     | 
| 
      
 18 
     | 
    
         
            +
                EMAIL = proc do |instance, _format|
         
     | 
| 
      
 19 
     | 
    
         
            +
                  !instance.is_a?(String) || instance.ascii_only? && valid_email?(instance)
         
     | 
| 
       73 
20 
     | 
    
         
             
                end
         
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
                  ip_address = IPAddr.new(data)
         
     | 
| 
       77 
     | 
    
         
            -
                  type == :v4 ? ip_address.ipv4? : ip_address.ipv6?
         
     | 
| 
       78 
     | 
    
         
            -
                rescue IPAddr::InvalidAddressError
         
     | 
| 
       79 
     | 
    
         
            -
                  false
         
     | 
| 
      
 21 
     | 
    
         
            +
                IDN_EMAIL = proc do |instance, _format|
         
     | 
| 
      
 22 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_email?(instance)
         
     | 
| 
       80 
23 
     | 
    
         
             
                end
         
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
                 
     | 
| 
       83 
     | 
    
         
            -
                   
     | 
| 
       84 
     | 
    
         
            -
                  # URI::RFC3986_PARSER.parse allows spaces in these and I don't think it should
         
     | 
| 
       85 
     | 
    
         
            -
                  raise URI::InvalidURIError if INVALID_QUERY_REGEX.match?(query) || INVALID_QUERY_REGEX.match?(opaque)
         
     | 
| 
       86 
     | 
    
         
            -
                  scheme
         
     | 
| 
      
 24 
     | 
    
         
            +
                # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-7.3.3
         
     | 
| 
      
 25 
     | 
    
         
            +
                HOSTNAME = proc do |instance, _format|
         
     | 
| 
      
 26 
     | 
    
         
            +
                  !instance.is_a?(String) || instance.ascii_only? && valid_hostname?(instance)
         
     | 
| 
       87 
27 
     | 
    
         
             
                end
         
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
     | 
    
         
            -
                  !!parse_uri_scheme(data)
         
     | 
| 
       91 
     | 
    
         
            -
                rescue URI::InvalidURIError
         
     | 
| 
       92 
     | 
    
         
            -
                  false
         
     | 
| 
      
 28 
     | 
    
         
            +
                IDN_HOSTNAME = proc do |instance, _format|
         
     | 
| 
      
 29 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_hostname?(instance)
         
     | 
| 
       93 
30 
     | 
    
         
             
                end
         
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
                 
     | 
| 
       96 
     | 
    
         
            -
                   
     | 
| 
       97 
     | 
    
         
            -
                  true
         
     | 
| 
       98 
     | 
    
         
            -
                rescue URI::InvalidURIError
         
     | 
| 
       99 
     | 
    
         
            -
                  false
         
     | 
| 
      
 31 
     | 
    
         
            +
                # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-7.3.4
         
     | 
| 
      
 32 
     | 
    
         
            +
                IPV4 = proc do |instance, _format|
         
     | 
| 
      
 33 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_ip?(instance, Socket::AF_INET)
         
     | 
| 
       100 
34 
     | 
    
         
             
                end
         
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
             
     | 
| 
       103 
     | 
    
         
            -
                  data.gsub(/[^[:ascii:]]/) do |match|
         
     | 
| 
       104 
     | 
    
         
            -
                    us = match
         
     | 
| 
       105 
     | 
    
         
            -
                    tmp = +''
         
     | 
| 
       106 
     | 
    
         
            -
                    us.each_byte do |uc|
         
     | 
| 
       107 
     | 
    
         
            -
                      tmp << sprintf('%%%02X', uc)
         
     | 
| 
       108 
     | 
    
         
            -
                    end
         
     | 
| 
       109 
     | 
    
         
            -
                    tmp
         
     | 
| 
       110 
     | 
    
         
            -
                  end.force_encoding(Encoding::US_ASCII)
         
     | 
| 
      
 35 
     | 
    
         
            +
                IPV6 = proc do |instance, _format|
         
     | 
| 
      
 36 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_ip?(instance, Socket::AF_INET6)
         
     | 
| 
       111 
37 
     | 
    
         
             
                end
         
     | 
| 
       112 
     | 
    
         
            -
             
     | 
| 
       113 
     | 
    
         
            -
                 
     | 
| 
       114 
     | 
    
         
            -
                   
     | 
| 
       115 
     | 
    
         
            -
                  true
         
     | 
| 
       116 
     | 
    
         
            -
                rescue URITemplate::Invalid
         
     | 
| 
       117 
     | 
    
         
            -
                  false
         
     | 
| 
      
 38 
     | 
    
         
            +
                # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-7.3.5
         
     | 
| 
      
 39 
     | 
    
         
            +
                URI = proc do |instance, _format|
         
     | 
| 
      
 40 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_uri?(instance)
         
     | 
| 
       118 
41 
     | 
    
         
             
                end
         
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
       121 
     | 
    
         
            -
             
     | 
| 
      
 42 
     | 
    
         
            +
                URI_REFERENCE = proc do |instance, _format|
         
     | 
| 
      
 43 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_uri_reference?(instance)
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
                IRI = proc do |instance, _format|
         
     | 
| 
      
 46 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_uri?(iri_escape(instance))
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
                IRI_REFERENCE = proc do |instance, _format|
         
     | 
| 
      
 49 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_uri_reference?(iri_escape(instance))
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
                UUID = proc do |instance, _format|
         
     | 
| 
      
 52 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_uuid?(instance)
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
                # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-7.3.6
         
     | 
| 
      
 55 
     | 
    
         
            +
                URI_TEMPLATE = proc do |instance, _format|
         
     | 
| 
      
 56 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_uri_template?(instance)
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
                # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-7.3.7
         
     | 
| 
      
 59 
     | 
    
         
            +
                JSON_POINTER = proc do |instance, _format|
         
     | 
| 
      
 60 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_json_pointer?(instance)
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
                RELATIVE_JSON_POINTER = proc do |instance, _format|
         
     | 
| 
      
 63 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_relative_json_pointer?(instance)
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
                # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-7.3.8
         
     | 
| 
      
 66 
     | 
    
         
            +
                REGEX = proc do |instance, _format|
         
     | 
| 
      
 67 
     | 
    
         
            +
                  !instance.is_a?(String) || valid_regex?(instance)
         
     | 
| 
       122 
68 
     | 
    
         
             
                end
         
     | 
| 
       123 
69 
     | 
    
         | 
| 
       124 
     | 
    
         
            -
                 
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
      
 70 
     | 
    
         
            +
                DATE_TIME_OFFSET_REGEX = /(Z|[\+\-]([01][0-9]|2[0-3]):[0-5][0-9])\z/i.freeze
         
     | 
| 
      
 71 
     | 
    
         
            +
                DATE_TIME_SEPARATOR_CHARACTER_CLASS = '[Tt\s]'
         
     | 
| 
      
 72 
     | 
    
         
            +
                HOUR_24_REGEX = /#{DATE_TIME_SEPARATOR_CHARACTER_CLASS}24:/.freeze
         
     | 
| 
      
 73 
     | 
    
         
            +
                LEAP_SECOND_REGEX = /#{DATE_TIME_SEPARATOR_CHARACTER_CLASS}\d{2}:\d{2}:6/.freeze
         
     | 
| 
      
 74 
     | 
    
         
            +
                IP_REGEX = /\A[\h:.]+\z/.freeze
         
     | 
| 
      
 75 
     | 
    
         
            +
                INVALID_QUERY_REGEX = /\s/.freeze
         
     | 
| 
      
 76 
     | 
    
         
            +
                IRI_ESCAPE_REGEX = /[^[:ascii:]]/
         
     | 
| 
      
 77 
     | 
    
         
            +
                UUID_REGEX = /\A\h{8}-\h{4}-\h{4}-[89AB]\h{3}-\h{12}\z/i
         
     | 
| 
      
 78 
     | 
    
         
            +
                NIL_UUID = '00000000-0000-0000-0000-000000000000'
         
     | 
| 
      
 79 
     | 
    
         
            +
                ASCII_8BIT_TO_PERCENT_ENCODED = 256.times.each_with_object({}) do |byte, out|
         
     | 
| 
      
 80 
     | 
    
         
            +
                  out[-byte.chr] = -sprintf('%%%02X', byte)
         
     | 
| 
      
 81 
     | 
    
         
            +
                end.freeze
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 84 
     | 
    
         
            +
                  include Duration
         
     | 
| 
      
 85 
     | 
    
         
            +
                  include Email
         
     | 
| 
      
 86 
     | 
    
         
            +
                  include Hostname
         
     | 
| 
      
 87 
     | 
    
         
            +
                  include JSONPointer
         
     | 
| 
      
 88 
     | 
    
         
            +
                  include URITemplate
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  def percent_encode(data, regexp)
         
     | 
| 
      
 91 
     | 
    
         
            +
                    data = data.dup
         
     | 
| 
      
 92 
     | 
    
         
            +
                    data.force_encoding(Encoding::ASCII_8BIT)
         
     | 
| 
      
 93 
     | 
    
         
            +
                    data.gsub!(regexp, ASCII_8BIT_TO_PERCENT_ENCODED)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    data.force_encoding(Encoding::US_ASCII)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  def valid_date_time?(data)
         
     | 
| 
      
 98 
     | 
    
         
            +
                    return false if HOUR_24_REGEX.match?(data)
         
     | 
| 
      
 99 
     | 
    
         
            +
                    datetime = DateTime.rfc3339(data)
         
     | 
| 
      
 100 
     | 
    
         
            +
                    return false if LEAP_SECOND_REGEX.match?(data) && datetime.new_offset.strftime('%H:%M') != '23:59'
         
     | 
| 
      
 101 
     | 
    
         
            +
                    DATE_TIME_OFFSET_REGEX.match?(data)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  rescue ArgumentError
         
     | 
| 
      
 103 
     | 
    
         
            +
                    false
         
     | 
| 
      
 104 
     | 
    
         
            +
                  end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                  def valid_ip?(data, family)
         
     | 
| 
      
 107 
     | 
    
         
            +
                    IPAddr.new(data, family)
         
     | 
| 
      
 108 
     | 
    
         
            +
                    IP_REGEX.match?(data)
         
     | 
| 
      
 109 
     | 
    
         
            +
                  rescue IPAddr::Error
         
     | 
| 
      
 110 
     | 
    
         
            +
                    false
         
     | 
| 
      
 111 
     | 
    
         
            +
                  end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                  def parse_uri_scheme(data)
         
     | 
| 
      
 114 
     | 
    
         
            +
                    scheme, _userinfo, _host, _port, _registry, _path, opaque, query, _fragment = ::URI::RFC3986_PARSER.split(data)
         
     | 
| 
      
 115 
     | 
    
         
            +
                    # ::URI::RFC3986_PARSER.parse allows spaces in these and I don't think it should
         
     | 
| 
      
 116 
     | 
    
         
            +
                    raise ::URI::InvalidURIError if INVALID_QUERY_REGEX.match?(query) || INVALID_QUERY_REGEX.match?(opaque)
         
     | 
| 
      
 117 
     | 
    
         
            +
                    scheme
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                  def valid_uri?(data)
         
     | 
| 
      
 121 
     | 
    
         
            +
                    !!parse_uri_scheme(data)
         
     | 
| 
      
 122 
     | 
    
         
            +
                  rescue ::URI::InvalidURIError
         
     | 
| 
      
 123 
     | 
    
         
            +
                    false
         
     | 
| 
      
 124 
     | 
    
         
            +
                  end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                  def valid_uri_reference?(data)
         
     | 
| 
      
 127 
     | 
    
         
            +
                    parse_uri_scheme(data)
         
     | 
| 
      
 128 
     | 
    
         
            +
                    true
         
     | 
| 
      
 129 
     | 
    
         
            +
                  rescue ::URI::InvalidURIError
         
     | 
| 
      
 130 
     | 
    
         
            +
                    false
         
     | 
| 
      
 131 
     | 
    
         
            +
                  end
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                  def iri_escape(data)
         
     | 
| 
      
 134 
     | 
    
         
            +
                    Format.percent_encode(data, IRI_ESCAPE_REGEX)
         
     | 
| 
      
 135 
     | 
    
         
            +
                  end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                  def valid_regex?(data)
         
     | 
| 
      
 138 
     | 
    
         
            +
                    !!EcmaRegexp.ruby_equivalent(data)
         
     | 
| 
      
 139 
     | 
    
         
            +
                  rescue InvalidEcmaRegexp
         
     | 
| 
      
 140 
     | 
    
         
            +
                    false
         
     | 
| 
      
 141 
     | 
    
         
            +
                  end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                  def valid_uuid?(data)
         
     | 
| 
      
 144 
     | 
    
         
            +
                    UUID_REGEX.match?(data) || NIL_UUID == data
         
     | 
| 
      
 145 
     | 
    
         
            +
                  end
         
     | 
| 
       126 
146 
     | 
    
         
             
                end
         
     | 
| 
       127 
147 
     | 
    
         
             
              end
         
     | 
| 
       128 
148 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,53 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module JSONSchemer
         
     | 
| 
      
 3 
     | 
    
         
            +
              class Keyword
         
     | 
| 
      
 4 
     | 
    
         
            +
                include Output
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                attr_reader :value, :parent, :root, :parsed
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize(value, parent, keyword, schema = parent)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @value = value
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @parent = parent
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @root = parent.root
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @keyword = keyword
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @schema = schema
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @parsed = parse
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def validate(_instance, _instance_location, _keyword_location, _context)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  nil
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def absolute_keyword_location
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @absolute_keyword_location ||= "#{parent.absolute_keyword_location}/#{fragment_encode(escaped_keyword)}"
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def schema_pointer
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @schema_pointer ||= "#{parent.schema_pointer}/#{escaped_keyword}"
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def error_key
         
     | 
| 
      
 30 
     | 
    
         
            +
                  keyword
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def fetch(key)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  parsed.fetch(parsed.is_a?(Array) ? key.to_i : key)
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def parsed_schema
         
     | 
| 
      
 38 
     | 
    
         
            +
                  parsed.is_a?(Schema) ? parsed : nil
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              private
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                def parse
         
     | 
| 
      
 44 
     | 
    
         
            +
                  value
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                def subschema(value, keyword = nil, **options)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  options[:base_uri] ||= schema.base_uri
         
     | 
| 
      
 49 
     | 
    
         
            +
                  options[:meta_schema] ||= schema.meta_schema
         
     | 
| 
      
 50 
     | 
    
         
            +
                  Schema.new(value, self, root, keyword, **options)
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module JSONSchemer
         
     | 
| 
      
 3 
     | 
    
         
            +
              module Location
         
     | 
| 
      
 4 
     | 
    
         
            +
                JSON_POINTER_TOKEN_ESCAPE_CHARS = { '~' => '~0', '/' => '~1' }
         
     | 
| 
      
 5 
     | 
    
         
            +
                JSON_POINTER_TOKEN_ESCAPE_REGEX = Regexp.union(JSON_POINTER_TOKEN_ESCAPE_CHARS.keys)
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 8 
     | 
    
         
            +
                  def root
         
     | 
| 
      
 9 
     | 
    
         
            +
                    {}
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def join(location, name)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    location[name] ||= { :name => name, :parent => location }
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def resolve(location)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    location[:resolve] ||= location[:parent] ? "#{resolve(location[:parent])}/#{escape_json_pointer_token(location[:name])}" : ''
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def escape_json_pointer_token(token)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    token.gsub(JSON_POINTER_TOKEN_ESCAPE_REGEX, JSON_POINTER_TOKEN_ESCAPE_CHARS)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,40 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module JSONSchemer
         
     | 
| 
      
 3 
     | 
    
         
            +
              class OpenAPI
         
     | 
| 
      
 4 
     | 
    
         
            +
                def initialize(document, **options)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @document = document
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                  version = document['openapi']
         
     | 
| 
      
 8 
     | 
    
         
            +
                  case version
         
     | 
| 
      
 9 
     | 
    
         
            +
                  when /\A3\.1\.\d+\z/
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @document_schema = JSONSchemer.openapi31_document
         
     | 
| 
      
 11 
     | 
    
         
            +
                    json_schema_dialect = document.fetch('jsonSchemaDialect') { OpenAPI31::BASE_URI.to_s }
         
     | 
| 
      
 12 
     | 
    
         
            +
                  when /\A3\.0\.\d+\z/
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @document_schema = JSONSchemer.openapi30_document
         
     | 
| 
      
 14 
     | 
    
         
            +
                    json_schema_dialect = OpenAPI30::BASE_URI.to_s
         
     | 
| 
      
 15 
     | 
    
         
            +
                  else
         
     | 
| 
      
 16 
     | 
    
         
            +
                    raise UnsupportedOpenAPIVersion, version
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  meta_schema = META_SCHEMAS_BY_BASE_URI_STR[json_schema_dialect] || raise(UnsupportedMetaSchema, json_schema_dialect)
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  @schema = JSONSchemer.schema(@document, :meta_schema => meta_schema, **options)
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def valid?
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @document_schema.valid?(@document)
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def validate(**options)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @document_schema.validate(@document, **options)
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def ref(value)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @schema.ref(value)
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def schema(name)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  ref("#/components/schemas/#{name}")
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
            end
         
     |