imfr?p8SH(KyZ-uj+RWWLvEqFVv79m9;iJ*u4Ktm_ctEBEqO@!U9_ z?J(bh8k*+MLM@pv!g)AwCre55_GQ4Mn6s7u6Dax>1sWsV&2D_+Lke70&nQeC{Rms7 zUEmSak@B9ba4dT!@D`kr?dj|AXNT+CIBt7v4AyH{6arP%G{S_XL&11L6q9-6japmOi3wq@ZPHWQ|YETZHVmg6jt&KSRCp1SZ+NUpZ8qh zYr$#7m{HYjKKRrsNPF;!m*vx!Vjmc={~G!QwTtWSrv&AujII%Xno)S~&Yr?`TW6p} z%R41NZ2(I;?bL?u$mYJV{%W`hv8h=>)A zU40EQrFm&&&%F2XH|J-hDG;B`MQL+IDf1DJ#4| z^IjsDrYP_HN>^@Xz*{NgR6($&AW}m_NEbe6#2#q7fLvK3*8I9x+FZ5zZ6MS)Erq_1 zV`9C7UK`SIb(U+#0c8+tRfOQeZ twI-wSmYG#(jg~t*0JE};VHk#h1K%9+O V>r7dzU-U_T)VX{B?%%5Q;{`UI=fvHxlB`djOuQ zOm5|F-NqL3ko( )Eg|`!}b6_6S`Pt L864$UYh@E=- zYcW5?ZhLFM=TLzuEu^(h6htdo(TJ$S0;uDuRn^%A)}nhI;0CQH#i0U}9Zg_L(}~IX zNG<%NOyLX|yAB)`6SIxAmB@@@c8MU{wZJxj$@Jc55&l$b;O|rT-{&yzsKBJl=H`rW zb6UbJ!S)-W#zXYp63aPBV%x0D(5N*{9Z;v^X7+G-N3R0ZO~h}GoyY(VSH9^!rYYmX z+EK)*h#6l^Y6!5v27=)g`Vt69$kMs;ea4WG;X3%?J1qp9AlIhddZDJyy=XG(m fjJ7HQSMvGdForG_H;Ex|%0hUcn!-WER!-`69&pqTJKB1XyB;0jSycC67fS2N@ z g!FLWEg-qL$4h#ZL) l-t&Y8k00Q2uu(tssGWCi`*gWG>8$e=p{;Uz|At6tPA}vmP>=E z6b0cV{(uKqfcN@PQ^-|UDP)wOxu6S`kXZP}#)|a-tE;@TAp*6>6lE%XZA=kD;ai6H zJ<|ji^?QjHd huiXTHQ&e=)|88YK8v`I3xLxh*nm^Ww? zd#+T!_LgagGRe=B9zRvO3T9N6lTR1vBb a80MkemL<7T}TK@in1mpjw3IMJ_NaU zJ{V1&)kuvG`cohxm%rjg5EY!n+fSCG38-e0KH-^Zy@2!t`3Bn$tpUJ-pP1ku)3+@~ zx#Xnb)Le*|3h*tk23x70i}DLMnYX-Z7~ieav&epzZrv(l^Ojoj=SQY5_;!NgS|$mB zklrViMTf@?{>E3m0;JT5i*|*>JZMN!7%-doOh>W+=lwvPBQyRH@8N(TYMp{-RtC5; z{jrs3jg{O;*!!9?{Q?d1P0RsVr4NOyiiE_yf*b?RfH(ktq=T&OWn $d!=hU;0UmpnQ1N5p!g7sRZN%=U1=P=OScBZ` zws{eGXX5qe-yyIIh}lS5pL&YHe8<}zkzpl81Ndn2#zwzoYfEF(fRZ4~Qnm?EN}~x1 zUA{<;$2Z;6?~WLSLLZFf92SlwFD@8LBHl~xv|7Mhq-xC9gyu^)FLO^2v&;8O8-kT{ zYSC`` 8e3A*SwSzy zg*`96w4}?ZVhjs$=9T)fgaF;oyyKN0^lgYH YxV!nVy=S6S>z#pmE zlOcn2h=qhZ)#ZIurkIPYiNHQic$*(17!kXADpbLdcQ5pOavI)v?9GxeS4iA|uqv~D zzCrBY;^Yp(y(jLtQ=UIO`0^wDK$q_+l(vRg%I*T`i}dwf|5#u*nxVgMSWt1Owhhu5 zI`URWt=570RIr4{6nTc5cpF|ABVDkJ^TcHVZzR?oyrG_dsuOuuz?93SSkMs=L9mfY z#*n;}NF`UIlc;$mxn_uN1X3rgocbAjjPyYI9=;@zo8ruXk@q+lrM$n2GpOLQL^@`L z+^qfZvSJnfSNlDTJyKpsHv+4QD&Uvz=LWuG88%`gCU>caX(Q?_aUZ6c_mj)%-)A4` z5Na45ry5LDa4~-3Q;0g``i+r?rBk*X(qx{0XoDQ1D|?l>Ej7%P;CHGo*@KCe3s01_ zx5>7rVc&^C^cb+3RMCYYQ(0sbM=;1OK&ce@#(HKrO^)=c2X1yJU)-~EVqI}YpH4kn zE4D@tzrFz(r}rcHy;`1HAo#@cMDZT^(QPit9EoO&+B+!>2Z`dFb=VRU6XKel2aa^i z^uu*9_)+aV0pB!eIzm|2WE6}HcJJhJNw$MvvTV9f4;$e=UvHr_D#5p|-cu-2Epqy& zE?g )?&c@oyz7O_Nyl z4W$X^rw&q+a<-5 J5eX&fH4tTtGtNlL z7H{-0GLsTDow%La8-d0Ra9>zB0@V2^qV}aVQY*JpiM^_}3$;*=N`6Qj8mSFoeJA-) z!{6&*tJ0rOfpvBh*JGDIVS&B` o3At7Q5h( _E8<-iA+(o78oS~nv!hYXEpg?0 znQUo|9=j#{ro_QB%WWi+E?h5JDwMHxAXERXQm6hAV<6TxY4Hjo1c+vICSr t?u^;h+I#~g=Azai zy*gp|y zfsG0`VoY|B LTVnAiNDzfmW zWd2TN!EWxj)tb1&;49z#buk3o)&qqD$X8-P?GT}R_Fnd^POOIILyy=P2OkUF1;&%` z2`g-pnZu2f@C#Dt-mODM0=01mK>xd?7J^$L@|7^0DH2wN_@DD7L8QoE#;#A2;cc}= zu*qkVAOI!nK6Brz5s XCTUdPBM(GBTs}zp4od3S) i-%g>bkT49$w>S!~Nr0B#j)ZB)SSmxO@AK_!~^R^)2Q&Drcym|e2y zuPP#^(VI#7ZCP97>?_WDDH=>b0VO@#exu3J#8;l(D# l#?U70HTDj^yob$S)5CAoU=pjv=#b|X5;twm6|?g z88JT76EjU6TZynMfMwNX`s W^8k67KNDZnTer9GPOGZ>e$h`vxtydx#^8!Q2_;b}4 zET|tMjAh_n6{-fw7_|hpw4{TWBeCDgFKwycG)Qmm4s9NUQ(+!E5Ql8!bsR?k$R1+1 zD#G!xu?TjNQIDb(2DowF$b=6MS;LR8049kH0|j;_i7ZQl=zH;YG6I(aX^3C2?`D;f z@Map=&$oY(E4%V?Oy8rpj{qQC+iu90QWpx4n#qNl#Tgpn7~)~CcLuD(Wg4*HBnp8_ z(%WG8CTLqfhUCALQw4?+Cgqw%Qr=(efd}~pAr2mAm5?F55!$HEY)hjNaE1ANr;ah6 z0L*H8+($)LL2W>n?c%8uV*^$42bkh-fizi?%LXwExJ_W~s69o6l6;q}IB!T>d0`jY zpYG8)T(%UzaDEjYSz^GU|KKJyj4VIm3wPf~hs395dEAq;OS1f?U{R}Lv1 VNiQNrVV^xn52O)v- zjI#Ty2MSO9v*ENBjwetqgd62K!K>MDP z#8wc+E+1H(Ip6 zug`f`W$e8By7@knoO9JVE8G%?_gEa+Q^y}oXk`04haq12+OI{ia5=@K*|WoB7xw%C zlWhohWX|3=dT*liLDIf=^Pzi(C!|+Lq%w R;a8;2_N@ZH%~g{OBGt9)(vH)_`Po!tGd&{#R{5C2E#SU;=98>4ha)bgD_z}mzguUSxsXD z@h9f^u>J>o*~4y#Z^XJKvkDVmEbZmXI)1U-!EDs4#3XMj+g0!HxWeoTuEaj!H7bnm ze1H2o4Yrfhof`%yKGcliEu5P@SzypyZw+Zxw@W7`L(1JK-6s^#3Cx 8)~68XR7#&*T9P zgH&}k6`@y)N*KQ&EIF^uj$((_|j;p JL|PWLv_U~Di2h4$rDhjUB%yB zXyb(x*WsIx#4tD!h88&3aXjrpPm^yY7bV?4=XzjT)I0j49|{x$rf6BwB7N7d+-iQ{ zgJ%WJi5mg&s|f8g?HsCOuUZ7SBcqFQZ*1fzRbdu#0;6BBIZfl=ESM*2js_ IRo7JN&nyiK=;3!nMjHMkN~auNj2n^h(+z4Oo`bU*%_G`#N91j zSxE)piTRvN%m6B4690ezJ@J!T0D%qwCMGvGH%2!$Mtdi7CKetZ9wug1CRSDk5Cwy? zhaJ$+ox#qT>^H<;7-FW*#!i+FKudc&;@_BtM)od1eo|6UKk+}nLA}P}?x4nh4DkE; z54 Qv{oV9OZW|Lb zCXi5nM*cgGq_n)!zj%JD(cIG3;Sa}e>c3N(82^jT!Ntku4~>a2lc|lVEl3b&5HicZ zqyK#Z{4b7wH=o}<|3MI-rP=?1{CCfPAO}hJpM(EXy?;diV+Mejy|K%08KuSeNq (uCLkyfxgm!kw<&`$H!~*#J3EUJ0}m%V=wigq%*kS6%Ee-2@(1d-*#N>y z()^^XjLiSmqGV$TG_!ZI RPo!U0FWj?Rx$h?haj>3aQ$ij?cp{qzneeVS%Sv>m-YanPNs%H zd#8`~_BQ;azjZ+TThu?1P0aU?m<7n$8~?%lLu_jDJM8}v_#%epOn)BvnEor`|A3@w zVee-5{|)EgNdJW*>;!bPcd}M=QZ%wMH3t6IIR6>(zmQZwDc2e3 M>{{-P<`p-uCi{o#vUl8kGZJ^gW zXqCY9uVuntn7;$}|K;C5-2MOM5X8j)Gsu5SzyG7I|54X}O9THc;{Wlk|54X}O9THc z;{Wlk|KHRF|8Io`Q#;T?(+yNcU}2F@2YrDYY$+ m@z(JX%%r4z2FbwzHX4J*Yjt}yZ*1+AfBM%MOstqw zosYUj2&g0kZ6qrx_NO=m_8{8s2fBlEkkWJp1N%Vn`yX6dh5Q`U2m_Rs7l+w_eGgB8 zZ!pr|0tQA5CM_oX(S7MS%gq~Oc4?rStzDY4QyrG1aDwD|jd$wfw|5J*I?J }DHB2+Ifq~zg84=a`$i5< zI>c(uXkKHXia5Y31O8=dDJ9Lg2kyN? J1w_ zF*$X7s* F3ybMBsFMc=@{jfuT z^9#x9yV^!&jhs_}P^#(H*7jnfHJ8^N0sGT;_Sya213I19u6IIVw z3SjMUdan Ev=NDx&p4--RP}+c!x0X=*OgKOd+ 8M6|BwOY7JyZqtjdyHi0aBPZ7CpPo2O z cQ#U_i;EN7~P20t)Yqg$4RXc5wGmKbzWLNvqXid{`b;_q! zrRSv`B58We>kzJ4{d4nR()bx72&%FsUwFc*(YxNnvH%u&+lWyh;o(Y j7}(0 zhP`4Hv*#d0^5Iratq(dcG;ZjGHISGL7L6h?_U^Wq?Ih_1pF1O1jcWA*c`pK;D(&hq zqqn#JW!IR7k!EvSQDD1y-S=$-G7VFWUHo{ZF{4K^mgCV8WUl+Y4Esq7_GTOFDN6b* za_8$GE+1?nNm$i&vv9wM3sIQ}8(r0?8TBGyj)z*IZ?)l>Sx_5Gtut-H(S((Md ((mbb^N1B0kNZ-np2Ie9);bBbR{>Igr4Dj`J?$xQj#MHf6F#6`&f zu=~i5@va2pow$~dF&AEfPK?QnRMjfAX&*x8qAy HfR zy3AwS2a8djOFE8B4q|`70w?Z_BE_||nK{f#NI~77!eH;eVme(q;oc$J_vD6~J-1w; z(UGx%!9l_)Paq@{jZs`#&v5ABu7skl_9!jB*7wLD6Y}*Y7LEM0JKNbA?1kGtm~TeT z&dxRym!(OO@%HgPzq!bh6@yHol;4;+WUJ7AqnocTPU9LAm2_}*+S&{=*Kn4oXdzQp zQ5oK2Se9#Qf9o2O%VZ`D$$w0S3K0p&$sxL1;g1L(%hJ3aGMZvzEw?>7J?%G+69_IY zmYkjul9>g#;wx?i32qNjmrr2RZs)xx PlK{x_GiIZLln=H`czBJmaJo zKg-j=^Rcr=yL^t#PoRJlHhXQt_H4T9SszJLKRX)Iy*TvlIw2jHzC!9k?UYa{NpW|Z zhw|ki7;z>8OmlQVZ3!}0u!FMYc-Q@sP5tb>qwyDZN0G`+pS^EuYuT?ak5dI+T<`I@ z2P1RTL9?=Q>aO%VCC=3ZE0Bn-8KYENaSV@(D=sOyCC+ClkYu*pf~?2Vtki &--BcCm+4UBh2v2Wa}GL0^fdh9-PBHqS@@rbZkYF0TD})Ybu-Jlx3soi z?yd01 8;Ej#2->rCbu2g@k339&tf`ZEW2uwG8v?MD<3=GHsN4J>D z`q0zu?a?SEKI@fg-Q_uN<}(69?~PZhkOBRw0P>>UtJOJdHQWP9NXX7TgD)fLi*%r> z6J03K+E;e({<|~Hu^i*n83ZD{gMmY{P{Eeb@klPS9PUKqr(zW;Ml~{6)*J$9Ba8VD zkPofk1_d9PE3~Qkm)Q54dz8JM`95R`8>2$vD5ox_eHmLb&G0DH#!h;4?Vp|8w0|L% zN$ed?DIXdgVOwl6RYEWu^@Jm`Ka;?VYi{PM9`#7EPo?KbYoL}!%kg_aBKqkCBPl6) zRAJPYMJ6O?lHP2+jLXDS?Ri7+;X^+evV0H7^j$trUrd7UW!dAONjXNEb
@Y9dEZPx! oVvY>Ez@Zn+N!$6CGV$ z_roGA+!&Sy%A`5;I6(=}(m}52<>XGju&7AW9FwksdBgQ`aP s4PLXYJuAgi9Y4bIu>vY+i&IFXm6-Tw zDP8f32J4NNNMIB5Vt#H3GAWf+=|!PH1uz;0c;DW;>k+MCs`E~^RCnp84QAr&J-VPr zaoJKZ?Dwzlk?^`cL#CxI1*~k=iqqzP)ubJ6dtfS0 z^r_n>6O^?1c&=EIPMXdZ %MYqi?OtCUZ6n4nixhj^kup5G!dYGKxo{GQTkjY5NIgeW^wzwADcs&;}-l zw?qh2WN>aPMZbn6?W&hBwB63jdwE|H;LT?H?ghdJ1@S90C>h01N&!-{0At z)IgQ#Lh*>`=q*nz&q~|O%*-#$zAO4;q05!p>PyYms@8Z}3Ii9$1C?x1m~N2iT*xIZ zUdoOv=3|*JUhpWORmr_KEwZn7aH}6>Vs#P3XSQ9RTal&v5A`|TGiodg$7Y}F3Z zX%YkZF)S(g_~*Y`lroR)8}H!qZJvo&er25BIP9%M^jzCQrg>MUy1 ol#dp%kyzv*1uP8WOtTgz{5hE9LqlW)6itR zX_{*l=QDF90Ydh%w1&ZIW?j1?r89Nq!ovI)VyZ{i%j%{|gYa)<_#rNA{nv<8y^C{S zjxMGj+>KO!as`FwOU0ct3#45Tk6)jLbc#UII!QvBjF}d)KE+|uFLy>{26j+hobT@K zIbJVBXf2m~biF>9EEwuwHZ(L04uzawqGV%>+SxHVIyUMPBxKNnOut6@@B#B;tphnC zD$4%x9XW4xG8321PE{Y(16dW=96^Q_ zvyNcRFTo<$HX6U0daPdby@f4G?@r}>wOaX 9uu1uGmJ4yy1Q z1H#}S-(p`F4w>l1RfS*Od|Rh_t6u)JHG18*ZqG*)!jJW2(#-dqC{}YqV4e*~MuVjm zw^ZgNuxK! 4BQnr)ssDP zPkY7W8!H`k2Y;a0lc^ZuTy1x=c4CqTZM<$MC$xSPjc4(W>lzPyIhIW|g+R^HDR9+P zEsUZc0VTkM bgZf9T;$(6OV)V5tdK<4!?E#t<778aUR>2=B&sj!BG zgn;%o`6{L77q^r(;>5xXS`!@}^|-CsuS3{;7R22zT| |t9Q&Y#6V4*)ZQX?QD0Cyf7Hye;{ZIl)l?q~P}1c0k3 z@)R3}WS1M~HA|&);)@$Asn+~WA{ZXt?p*H~*1r-pgXy2}2=A+nIx5X(vFd<{K%`&o ztxj{}p!Z#(w-3=UFwYU9LZ;1Z;eR;Rm3He!p}fNucUJ6Pe%#37+|*H}O=+;fnc309 zMn*>dAtKs>rdkiy0pv+foKiXdRHpUR=CDb6g~eFrXmoXTW&8?r_9-tAw2jhfaZeb{ zXm))4A)d6|SgXuxVc;i$NTlH4FtY4fSyx}*ijC8kKqkrK_L?spqgtdgS?s3R=5{30 zetY5otU>G%k+Gt!?s)W!IV&sApvz&>3s%u&K?1$q9PZCiUl&>yO>$v`q@ 3%+ zn*z6F;){z5!^_SjjZU*p%@b!QyYoJES&dFt{<}4{h-`jO4U+&>bMVAWy>C+t>i0af z#Y-o73Y@=pDzB)cHOibnMJT0N*KN82n^c iquX3%;lR;=z`ijO!O}57it;Gs`&7hLUV!fN7 z*X`-R&`<(R(wX6TZUB;g>98k5VUw5E17M)H_k4DK-HCx>_E=P>6L(ya$WNv^d)4E3 zGR)rk$B}+at+9mj>vJC<&91)QS>y8L?sqAnKMt?w8~Dq`Rrwp~+%Zm9rmx9Q(0VMg zO}TG?gw&uK#lu BZe9LQ(Z2{x`b`S%M8l0n1>%QW zn)R`CY%%-gu8Ic66C4XR73?Kb_>(-}8*Z*DvN4ws a)I$S)wdL0FfT}ekKRr%j0B~^`@_K%!3w_qr z-O|Ui 5j%U-vV-zE5Xxxq9_n zlzh()eKn|_B9QiT2P(|NT#?l(%jV~PJmShHHh^c%&2IKj>{VQI$&m#W+2IEI1@}Io zxT0}D4u=4S7cW~fWza3jm%IQl_ZIHUYVfR^S9p_QAYcx^PKUrwpNlD*P#}dXR8THF zBWIHv4V$ztawdeHw=opC+4o1v5lc$TclxNUEwm)t4tt38Pd zY5fb6DCyGF(yDZhF5|FGWUI_I$H=Y&ENp`%;g6Mz@pBW l_O15hSxsgp6`?!nC# }InxkGt$uxdRr&*~sG7$A4 zX >F!> zX$x ysOFfd+_ z%7rB*;VUaTAoo7s&*2@c63>-R{0Z8}Z}cFq^-WJt8{J(wU1I{ po&|+x-1 n?7_!N3xj!ICQtsC6evl;;i9l{3 zmf(ml74+73V5z&sY^tneh!bE90+n-&%-xQR#`_j=`~(G;nk;)t637-oB}&29hBArG zOns$q)p)+!5d%a6D$bXqKSWU=C1UUu-h6$2FUV+9hN%r)00f|~Qt<_S181%0@h;u1 z`qS9 tyGI;$nY zv|#>|7AyLJAThxOE2v-jG?Os}*e`v5_vPcOPR-Q9oD_#j=`9a5QLx3mwgl^?$!>DW z`N1WHr;~(mRpPXAm!HR!Q6DzaN7~uO&*eBln>yqTp#aK{OJCo5cg5@O&;1UkZ6FJl z6niy4xpB<)_XEKKC00SzM@`MNp+5g2wgYkHomxpVW|P4Uts{Es6nGI)QTNkDzsYuW zrVl=ktwZG1H{CY5K2NtUQz9xVDw=mAp<2uL_wH;*yM7s2 }-q@0{gxhg~oU>(?bHRy;Dr*^BIg8aDa>WXtu=tF;tpvLJ z_HbU123gTA8GPeNv&6C`@`cC}%4grY+Z08cKOZ7aN{32+AddtB;9UTbK&{9!?;}A9 z0E0My0d%EERMQ{F_rcN9oKr5FR-v_|NnwAq&)Ve5N>$O6O)C^9vsA_t__#HN1_@WG zQ!CG4Y4>1Zf`ls_q&1bFz-~d__{!Bf^+v&=m4#cW6RM_Gkiu>OdMcHBWB5!`z>(P; z>qHdmKgRt&o;$rQ<*0T3*NxEPVyRpc4L!Y09g~<{mE~J#dqzfIET|k)ujiWpTBd%{ zEtEuo^t?V8%T#|eR@rU`KFDN=nsV7MYFRg=WMwf_k9oH04dsHud_S!?yQFb+Ot+oE zS3iT}YX4^5e4d)mPU32B)Z^m8Tt2<{lwD9t9(2rQ{PRHB`+4%6p7*9cWFu^aGZAPH zfsExX_fy_OBcIsdXZ^%HDwf@5UD-1O*Osd=3W`{?2U?^|McZy8SDAM-VhiRIw)GbT zOl6G}_3Qo@NJI~uI^}leQ?lV^3or>gI$_S!; zClZ%_FRPz6;@1D<|PD{d(dKz=(vXMVgr{M63F zM>%JcGn579<%Rk3c)R=Jady6RtQi~@GN&2tU3c#c4^R6cO=j_nImyQ-hQW7huB2`u z?IfJ}f>w94d}xsz5~|a=U@%%9rDc1-WW{U|7e86(HuN;Xs=|YTSTScw?KZBvsXOJ- zR}my1_`u%2=p&Ql54iFw;IU&3x0jWsM6JV)(SGYrazTl<&+_*4GecUW^@?OexIwp$ z^+O76MP1UXYNS;y+`LKpN=n8V2Xrihbt~jqad2>Ol*{fOn*Bp~*bA8nWT9LoaRCr8 zH6A0JDk|0U3wz)4dgRia>@fjx=_ilo)*PD;kB<}8_Bqfont4i~;>rT 0(9uD4qQ-A5XLoxLW%!pvAv^&Kf?zyP=;G2Pds$*6JfI=!}cAnf7psA~2SK7scfe z&3e9SUnbf^x(akgYO~rDQ>uDR@gZ5n!w2cbwslt
qqbjo_ruju zAX(@vxBCKo$CHv<$Lg=KA<(Jn^4?pt)|534|GQ3)!_!B3$o=i=%iEmp1CPcD^H@Pn zAm43MYMhYlP-}lbLifQ7alX!wx$0(4>G4r1vW<-mETWB?^_q~5&O0b5DEa&kgQIsh z&kqQcRDw)QOi}n}+0y8^a?BRi)+3-Q{)6X`8%NX7@GvDE-3UD(0-drU1yrEd3dUgJ z<3mi5`DCmx8WM%YKq>TiccgySCnF?u;=%$1WF`1dM*f15E{-LzT>gsmbVp?H5Se1O ztNe2NL>Y>V7a0}?lfQOClJQK>lQjf7gwtrJ3(ZFx0cfe8MN%l1H^0%vySunMbOuWn z9rkZ5a5CS|p%Z8~<@;Tw9%#ByOhsNTcwGdGkMnnv=O7i&vG|=W>kJEM_w6g^xg1=H zUTdlKT>dYb&M~@@CR)QYnK%>Mwr$(CZQBzY6Wg5FPA0ZJv2FYIckjCAXU dz|oy?ols&!Tn9 ya^gG&|ZKtH`U(S|+bp)*e_x4=T9b%@+h`iSXn9Spd%N9jf|DcG4=& zp12%C+-n!Rv<9`pd58B?#n0Bp93)-C;_zy#cf#btj-PEr526xfmVaIoOXZLeXWJxs z_fre#(xd;L&XM*Jm9(h!_O`MyU{>G&XV%t^wghM57Bgk(k`tfU%JXxq!osne!Bl-i zV`EIJ{C~m!V%BwT?_jR4EifGOrHZZMODa^T5Jc PTGL3v5ZnG;2i0xMac9+7!rJTU)PdN^|# P>zrb4Zsps= zlqwTPN9m0Z{9kV`%Ls6i3-pK?P0H+}=?&J8N}u1CVN8w|vmGu)7F^i|k1z|XhQius z3vC60=!Fg>Xr*}sjoa@BC#R+`dYgzpS#B_0{Wa(B=3YLxmH4 ZSgZh8agjQqi z$j!PAEj5R%XlOdIKx2D}>eU7kXrda6EGQ@_gW ~bOdrz!C0w1`&^uM zIWalLCs=snGk>o;4fAO#Pa-bK!$e}sXl;k!7>Hd8)lBn3ddm6R*+UDIP}OFo_;k+p zsuv%1G8TlfRlAVT(h2T0Zl ~Gb(E`Paxv$My=hLm8no+ij< %&!S8X6&*)OMzwcmL%C-!y*6+n1-%u~ztf{B z%R?c@{=xz $nn6N|@XR1W5=P#mu|gbl`~XJ^~aIx~oN7}|>^#S7VJ;h4&% z*KJ>glvtYF-P|x@YUwE{RW1wUofb6vi!h6Ua$))F`;(>u_W?^yc#Lv7xjg-_d*|J6 zQKIxKpAT{*YlP *oSil{SfLlhY(p3R+2cU{ zu>zJBCb9d{>3G{al5dvgk4(p_JhfIX)qT0j4Q%0RHS>wN99A9_c=Yk#sL%yD&Qgte z-@aRHRHA->tU0qBfIu>(7jxot`ULObLa0~j!8osY`B8^e<5urKFOie|qgIW!XHM56 z^sQ4>i%F3jm);S$tr?ndE4JtlUrL14$`O2{C}pYpTTm<>cL~a7FIFFsL7wY-gL?IT zMeF11YgZFd)S5Qk^Zbuwu?0AF8H>d=AJ^^eB2&=N46L^8u-ZA@-CD_#fL`BUGpxpC zYG3?oQ!3vN^1NcP5c&fHG=pmU?so|n7rJVW`p8*Xll9r(r}F#7)4zH U>BlUaLPeA74sU0k9QoLqZmAsn*3_SGSR=l(a3;Y%nu}L{RtX>>3%F z_~SSsDjEViR#96(m5`92NR FosFMCzSn{S-3MOfXr`=+^7Z;-)Ju$lF`}G6p zAvb9)9lR1TQv>d-1zKwy9301OwZT$fW2r4548|rD)ocZhYn}YaqhnQiq%d#}rklvT z&nNkCE^aN%&tmsvHP)n9Id2b4)4rbZqW(GyVLz~)u=H@|;kdPnMeRF1nAfVocFqnb zi`aw_V2|=rq%<8}P_JxWe(4vmsX)T_WAyQAF zO-TF)a^RM45d1JeMNH&K?kCR7v`NQvu*jz8lP|#4V1E?TQc>+z)bub$A$5sH9sb1T zDpDM(^L?uZzoU>$FiG&4nOt0?xsc0Om?;#$h@AxL)a#1L&scYuIxU~*dfCjWru;ND zEp2p+l%pot&ZGR&Qc^43z7X`&UdgaMwv|F1_^y;0$rVc)Nl$0*ZFm3exxG%AXRWhn zcyZF1J3|zyM-|UBnko)KkwYkEItuxz^)AQXjJ0;Hh%v~OLI{8kUc4{m%g`ubxdKnr z_d)yl4I}Y3{x{fokAm CqG!zJ;iYBj(5I2u(vR}Uwyt3QuyC+}n(6MY2q@?5y zzc$l{cZf@b!)=qKa(oX@#0twfoF8$HrgH5S@PYI5(Rtgy6Ls*P$JT5g5~*~EO?F%U z+qHvZ2b<+&rSqlJ3~x>B8ZCZ55=a~`-$mUnHkE5W;qaNdAfZ`=jVDGXOqQr?>h9kT zSgpW-cru?anM6Y*0!kp2dL7+MYzG RTx;4lT?}WMAhRT#Tbl4USx8 zNiujyV~3i)X{(DzXlffy4Y9eIyriZnaffAnf7T%3waQCS{K;#qPJJaPP>GPW V94J$GtC>57UDc5+_{ z`)Pa<1J{IxEo^8=*z@^{-C#aV%11a`>6_zq3KbZn&2}1m`>?!~kdvcV mivEy$z1gPOW%NUQ^J~%Okf 3qi#}xm_tu_Q zWcDOwe`dDxM7>)`TY}#pav5WVN5l+SzdnD>M8v}CgPHysKi3JWT64SJm9DAXY1tZ` zn5b+KbJ`Q5Tl-v}iu%eKJZ(Cg^aC%EYYY4S@y7aE C+0p=qq3B_NdGni@L)qhB<{dHqj2W+pI z&K8$BG=hc*B$4nZzQPm9< JNqSEY_dOjYVd6?1CLU}B zMg8x7)bh#lsLN6-!LP7}$a^K(rpo#ou;TZ(W#pfnnEexoh{0^ut-_53TKT5IVI )|9X0Q Q!{_NO;aQWD6>9W=}KwoX@g5_dq(5ep!>^37<2PdEtWi}=5@?+3sZQgv!@pXdp_2FXh*XIWmv{eAWk2GCH1AH+d;qQ+(_*&ys zPrk-q-~5d!%^EtFp%u4JI |8K>AQ#OD#9>lylO}N5}kK}1scBax>mIe1IbMunBUveiUP$0xZ}nH*M?;( zRNe1Z5vwIInkj7#j0rH{sQTq9y9Xx$9Ua(gY|RuXM5d-zz|M7KWTa@|XZvx6!xb-b z+fz=* zwW!!86uUo@=QsO_ad%5=S4*$;=11$>g`!cGn>4=^iNlc5&_Zi!=-u3!#qx{iPbjG9 z4!urYi3$r=y3Fy@7ezdQ#vgpy`GtMd(vy0g@i1I84r9^ K%8k19Ud4;|JZD_#1+eIwmoH;0>Gc&@$rA__6|`Axm}(y!r<_M+BL}ghs8V> zj=H+Kjjb&O1qBHuXL{NSM+3z=^ W*58_UoH0CDwY1Gs@w=YpYp474$LAx0VKQ8oP{8S{5W`BrK zSKFwu=F58&)wAN|tNbzAG2^qBjq^xu5zz=;-&{9nFyuA4BK m~XgCw~FgHc1`v$;IKuVrv*Y5o$IQC2pAlB{C0NL zKJPb1iINJ7f)$GiDLH~#UZI0Sq>)6-xgNp!!lCPEll@Ce;6TMgdqTjfDWmHRuGW1k z0Q}(!;7p@W=1*lwR!Us0@jN}a#{D!5>o1y;RJyev3l&?rIXMAH0^t{c6Euv0{_wdu zU@tpAKizM5=@38wEs;x~SZz46{+ZZ~IXE~-_JhQVdlUFQg(bze#=5$lQF8Ny {9yT>$k%q2q%Dr{6fI=*N)Tx*u} *Hdn51Z})rbJBMoX zh3zv}#|BqJ&~v}k3mFE149x)4^VvB{%@#%wWwM>*vqQY??C=11MMqwmUEAZ=Cj5W2 z!^rsfUM?g$I=Y_Uizv?d1@?!=JYJfVhK5DgqpO>f5m10}xmr4C)-Uw)^=9_G?NZ$o zu3Lw2a>kQzy>@9jYMSj8gRAt38Ib2TvO?j6m?n3kpxwf7Wd#z*l%eVenc-wt`qPXA zn%uc840#9D1B1~WOxnX6-C@<5jFMQ&D!CS3pEf#vIL)|@O#8%_?Rygb^t)JjbCFcA zYMHmR8N6r2i7);@fBES+f7t_q&V^3QFi%o_j5X(?IC8R8 5PCKgs)tK*`&u=kF)1{U07X1!-urqDS#~8?7UsgR_m^Cp`XBcUfuH834O#)@AN#3n+d3{vS;VTqAW!4VEV1GkOlB_ T{HN!a{z0$0gSg(btw6>rIfif%fY%)QqU0- z-@eBL1zZXd4|j7Ef!$|5iZt^60->)(UT%Bv^P)~!p*U@Bvq;74^B#uU?TWmnY(xfD zWKKCfZRdyNq~zXR_PA#2m$y }wJp?dXlgPAV#BBNS{NvpI<2w% zs@CPYyX3aNxfY(ccGY5M6N|-|YJ|9IkkHS;yL`W@Y7;vuESB*`CXYU9T5Xt+lc<0P zHcs0cU*4JV@jZIPwl=|`q1T%oUxlt7X@hUw_G4WWX`cjA#eGCqhrY_C*RR$!+solB z!p+TW|51;4-uF9J%o~%B0ZGE9725|lhCHq3owN2ggDKKJw<`Kck f4vOgiG~T|0HH+w*nQiIa+i&5mVP%yE z5}agDf;5>GpeLc!f4uv#4dVz1nX}|W1qR1ZFf>0 mQ*UGqB%{OAaQE{+Sy~u|PLG-Y|_C>TAai09~F=hkMbu%s}dT ztTR5slaiv*h8IE+1&2Ex58vsBgoU+5x<4qOU;==0Aj1QdZl%OV6jsCIc)@l5tp%=m zwX|XyC=f)%Y)uA#ytQWAqZWQP&FJHq`Tn4j`O8xEnw%D1hn*UcUp@TJ`ni(;>8#mg z#ydC&TUc$sS`P-#&ywwCyQcNt7JIy&{(?WXH 9D;>=@JKfE8l~GfG7UaAuWjg U4h$I{8m?6D4TrC%j!Fx!B)1B>Kuj!@$kH^l{={@c()gY*CAw7uQ?V@sTW?cRC-# zRLflx3n;0TI&3G9xc_-PGC=3Pb7t0Uq8=_q=Hl^-KDN7@pAIRrz@K2Eh1QMjab)DY zvmP#6$Li *U|3C4?!NKZ28L}V(z)o`S>=>T^>k8ZQ+cHsGh@^=8dAyYhv=b0<*6U}% z3ZkN5luPBzuD1noIGutoxZTnxgv(ES)*9iA2f}w;E|6xovF@6(-Ggc(2nZsD3UP#s zl^CrSB)lHadw2T9SL&SE%a`X*7sNB!cK& $JQuTiAqTJi#iTF#1)kDrw&|46t*qr;7{ zRSQ%j6lBd&5P8GvfRdc~Im06_*}fxqNE|8O8PisFx+#lpdD0Zm)|+;gYLm68R(8h0 z@DTAhX*f9Y(#$nI q37JNdmMp+^0xd5R{SGl59iBE%&m@#om0%WVkjKYI4sK3F zl!d_U6v1bR=e?t&u!^X0+hsPnY_7>L7`|%9j?`3g0MJvZLhdSROlqxg1jq~|^jrbp zCrOKoS1MGVfzDW`CbN)`)@;4$IC!d$EhK<-Q gTmzA8|AXK!!gnAF=^NYavW zxkjIyl{ESJ85EGj{I?w~YsCP%H0wILjxH{LH#T4aH>W`D<|0i1c 81$Qbes(|tVUiRzwc$aM9X7CE5-C2hl#V~yIUD#jRdwo1Xp;mD?tUq;T#LY# z&C&ZW>L`96ny{XpGKF}#nyE>dT}<@8Pti1vLPA1}y!~@WYI@SBzZ@5*-7Q~ekHLp~ zb%n8MUc)Qw;jh?gd~)F5k> ~E zx%f#HC*+~gLR!rR5( iZyOvB!%23|dU%i`pym(K+qXjV%x z@{TxR>~ i_a*E * z-E5T!EL5{BjK%ucanC`q9Y=d?eJ)T44VS+Gn|u=)4)peMITG -9uHi$zkO zYiWC)BNcJDJ<8h-5f72iu#@*ywB#|zf^kPNmai&w&+N#WM=zhP3VQ9bY$kn$Vgfz} z6?WDF`V))19aRm0)*nEMc-&{wgf&` fxSxB+if{Vp-f&F%3XdS6X0!?5h=b+F8{p^*R@FV>z>psc%f*Ly`Wi za7>Inx3R%oEk@6k@r $($PO!`#y6%CZN1Uw>8l71UE$|g!*J^jE zV1g?yGtfA0 kd%Y *2V;X3;Z&)5h20ayuW zgfr&v&IaG_*m9q8f|7!T(X!N*&&-e9pn_o(65vNIoBocBd*pL>$frJuV{Zs`uL8hL zMV1f=j4ihX{vuE)eZ?%cqRqAE*EemdQGy&ws2`WtCu9UXl7G 9Z zvsC6;mQ+naK@VQMXRr=GQvV_ncR)rHl1C3^69R +)X1x_$A z8N(Y@AL-l$QH;TALc7CV }jn1u(ASyi+K< zhA>(S?=!;U3L)g-J=0PlcqQ{1^oxy@kwh~<{J1)@;@qal$u1&ArQFdfU{)}{lJ)X1 zagN|!w8BgO#QaY9@aom8Hp^k^n7+N%{oNyu7Y *6J(?k0{axg!+vFO9`NX(PTv}vM)t|8!J!+ZlhJO6|-D7tXRF+DFxt-_9 zq*EpZND!hK>TO049bpWlTNPCOp}JstMQiDaPDxb4&V#}Ki_K4`?*+LIq{3#+I@I2_ zrj^_d0U%XF$9Ep>>Kxzo>{cjHc{m$0H{lIuK)N@blayRHCPmUc*){6-+z;(O)1^6= zshzmv9TomvYsAgI+wU^F{It>yA=8^4s+jI7JD;yp{l}Ci>%WP2rymLi&kWfXsS`7A z3Grg^gySY+S0JMM^zGL~pGs_rpFPDl3-V~A|7QV6cgpTd%x84JfqJ`^6t7VjE0!6_ z()_06o f1c25IJyc7?o*}Gf zG3I~w;-gU{qwD#_dqkJPTZZI{nJJIATkN`3Ps~@JFqB`4-$vbu4RhrSKnHIC*Qz}! zsT%vYy~!Wl8~yTvF`ZL*t0>!C9JG!|OIm2MxuQa&)f~<|F_u`8t6_*{oT fW05M{$`5;TQ#Iz{r`tl0t^%zhF`W5+#*zgP8t$AS+} ?hE9i0I+TI )XpY(GVT`gj?z~@L_2YPDr(;k zt(i1D{RpMurinSpr}jgl$J-@rF4;uxGqP%g&Hq8Dkm}yKSjj>33`ckW)TG ;}g8{=+KdVcoT=AI@cO=fy{yvu(}EB1A2 z{h@`!*5~Yu#4+$+8k27we#BiITLRJrTgzLs>FVI}hskyDi2KGFej-;S>{mV_f5oXsR z;hB0LbC|oZ=8wD6)*Rhy1xiH>9~x{rQN&!eCJJdg!|(i;8&l&wpCjdsB%mb5WNq0( zz5Tincb{F;GqgorFE$CuP687T=qr?}KdpEZ)Wdv4rhh4h9@AwLGRY2)Hp2RNJzK&J z+X`3FR@7guCVlKD=`DnvjxiZt8e8sY`hKVN)<$m!=ZgXvMMx%fBz(~((bSHLPlU~g z$)79u-*HC%RlmNkJY}Js31z)@BTa&qjKLa2c>!X%(bqHdNEZvovswcI^K{9ck*J2V zKE3WuRycpcf#8~hFNaekp&Uve1-=!+H)bIs0YT=9ADt?_Va=;v{-ij(*q#{#k=Vf} zHJue;)Af!V0ahCGVYx5VJm}jOapup! r(}N-5;~ zhMYQ^-B+I&E+9Bcn7|x|v_Ibi{=MMI`xpw9v;v|iz+&+aDgY6dAEx$+y~LI20#Bet z@K@52zlzFH1>6bei;pMCm_<;FI^;TkZN=zlrT?3o4Uc chpdy*hjrjnKR}rgfFy*913b^_QfsznLgrRa<3jilT)bXLAqjrL?Z+#F1sXM zL)m?QNNL>IpgE|RB-`lfQ?zSPyx7mC%pwu&c2M5Id}{LO{I=QhsGFzoziKBg*51M? zz9H}fk)gmgIp%VsLz6xjh?j+6(xgm@pKaialX6V8PFtE|F743G8{HQ-j_kVP{c7d= zb>BZ=yAeK$x?lS!3$H0k^`G1vwhsjN(z$O8g|G>DqOrnfA(X!SPznO&CcW>3dIo>m zzL2m%WBU$d5ELSSA>FzoW#3^#gNi5)%?_p7(GF!Sw&ZGSXSB&OnxqBENfBMXrf5%x zNSQ>2+E;+q{g)(yzXjZ`LwEHETtSpajz=uN94IBs)*$TL43}MAOt51 t_2<<)8c3{c@T@sg;_?g=2X2xRp7{zA zArWmKp#)G+P2ib^{e@73q#|Bcm3l-+Nzth}B*Tm~ybjPf2>Wl96#)$w{DBDT7oV9o zd-7~{L|Mo$h_Jnyzu};eP8U_4ND?cqe|VtEpP;9tATG$^dZGU2U^w2NWSoFiykJ2;Zrk4L2$SaM~S` zl~35C*WOs4^29UM$hc$WrT4n@m+mfOtBO$3RV?rVZK5A<9b5*cH|BTARS{*?*zM?@ zS;G$P2*U#n!lU~}97#b)GejQseUqz>wn&J-{#ag2*t1Y&8UcCu+B*WOa^Mu^_B^ z;8c98+pQ7P)5F{HdE=Gt{>W*}B%-crEo6!R^T13c-wHNZ?9=@e?Z{e#V1kD|hb&6F z)5s1Dt J)yZfuZ$)53xnm4m{yg(#HzfWW>L roXTdSx&Br~Nar+rfY_u3;6zQvFfsZGuL>UmE@>kUa-%#9dO zUtefqay2~dou)_tU2ii1QC_1E9T8)?)_f83E-?y}qEvudR+W7!s@ggfND&w8zSbdZ zN=~bd$*F6zDnpz9vUVwq=An-d&lWC+gpS{(Z1Oi#aEPW|fFrzqn#ug*73!XM@zvtK zd84>+g6}_lUh*U-K~EBoa6GF1c8_P6wuOScV&4Mn4=5E2`hq%-XG(F z _TV<|>nWU`VP)ir`VeT(TtvbHA6BLBsJ2{KaE}k~`WS>>h8d7+~moJ%Z zX~wvasCm!zf?5#);}?ym_XA AY(<>a@9B+15S ojE=C?X8SdfBbjUi<7}DMB`&@wAnut(&Y*a+>SW(wmRV8W~=t_wghw}SmfSk z_loSB{UN|9LQ%^*_I}Rvbp>ME3}0|7@5U@rfMmiQfwzl~w84Q{lN6s&($(ouCF+%A zc$!a4@WsR^5N}|EREBT4pA`!~4x+sL0}1SxiiG6}7RZmQ1Z#T~waOoD2Zy829^Tsi zqEOo>Cx+e)HnlrXc>)vO0T`M#aR*uEzHa$8=^~!341Rf|LS@z}H$B2(FLV!S-SBN$ zy~X{LdZX+4{g|sy;OEJ1sht)w3LM6LueEPJT!93NC4bDsy`YuBqw8XSB0tdKTWb*{ z6cKYi@k3htTvg{v=))V-^K^Vn%kKOA9A6&%2D_*7`INL!DS*=WYD1k@?TA}$&Nka` z&9@WKVr*Yp(+s>eE-^F|AF+r^ @acHy z)5A{_hB{w@wp8#Ra=P1(y~Yosj>k0(T)xx?P033Q0u!(K+CMl=%TX5Wep65V&=EsA z;eW0_YUOxk;sBg?ylDsf9b0wy3tEV@C@MbF8t2^=RoXGLtHB_=@j-mhuwrKk3rafg znNci$e#p9nTi8}Cfn!%A%H>Y&cU-B}Iv>$3SUp_%@o5bU{OW8MW7cwx0Cvjvad6~& zKHsI?$eas0b-w&(2iRFj7~ f% z5uCZ)1(y$Q7XFbXO#7LgO4C7-h)#&2&`@LDyT%Q6Omm~8T#?q8ASTe^&;GRyQIRqn z%`N|fC!-KId~=5@?t3BCn4y9a7~E`{81Q2}so1p8vc5O8BJS?xrSpF5?AXOCeXdN2 z(e$=xs{<@hlO7>kGmOu7#?42FCbxR6Pq|{rsi3n`mDE#So&1x}5s6-|r%-sTKl}!& z|Hj3EsrK-6#q| %ImlakhK{wYs6ZISFO{qmuNN|#f?n2>qf>xiHS(@ETl^t|(bz80f znzT9R6G&8YwWC#4TiXHcY8hiUM()@&sbA+*4~F5x~2L`A7fd@x8~Ro1F;wbcub9 zsz<9F`@<`OAfXfstDn5>!y1KX!>RLk;q36w@e%=GQk@c0F@cbiD;LP`&JePpr$odo zF97PUsNxh7(Cmn71py-?70`yzI0C?mh>D$oq@7=z$vsg$F^)hYt9)!n!j4L~fNMI1 zN*g~AuBduY04`fMMorzitQX-J{h7ipwp-WF4!zXCi0Dg7c9J$J$>*0^qSfw7-%5%) z`JD+SEow-wQDZMS_CIl3TiW#W*HGKKI!3@l#?_04pP&EpW$ElcHPqj~0=s*AeouE5 zA{MyJ3~F3aL
LGs9obK8So9TJP5+$^;#iYDihdW30nswaK9;j zf^2NaNg8QTa1M&@C!t^k=Q4__C2WX!rG3&J!-IP2XyF>KOG+O*DEyl(m{;#wW>K#* z`g#iX3Q5Z*$1JY4grLKyAtcn7ijP+qzscGivT-x}G~*gN*XWH$qSkrtV9d|6O3lCO z*kp2Of u%l;F3k+|2 z1k5 Ow>Yn|oxU7YOImv-sTtu#c^4BzEtD; xZ zZ6xDUI!F9a6!nJOm?areXEg1pY&%sQiZR}yhd@n3lsU0p+p;?ZQ#2jlL)9iL^rk<$ z^`v=M{?*I_WG+tGVZU|NKXD~5+Y0zM9c%^3@oN|sJTYFH?w^Q8_9#F{X~0&%fzi#^ zra(wM=mkp&v-zFJJU*A8^>BxvHK-6R(; -AMA-eKC) zW;`}Wx>u$CX$QgVV=KBAHu{HrdoZd?dM ?zDlXLA}F12ZR`mxcMIec0GB-xV-!3Dji@1cK%Vv z8}Q@vc^Htb&B@iW91y_%Lu+V9NEE=w!|B6~VBg(2cj`qrtcz`)5hKuUeO^AFWA<@2 zsk0evUE#djA3=9$bZ$Ry9r6YY%t*uZib@W6K)Ad-W6iAA`_XZimuwf aJvO1C)z`+F z{tKWW7#N)M^E4)pZxMB~?@0-L|Du&8%<5nS<0U93h@E{IokL@Z%H8$PQO2m9gn#IQ zJ0AN$lT#}pf jmp!+n(%n&~^BH`oH*`X3-@mI>y)lUcSMUM`mc{Q6(9&3^XQvO?n)dX7)d& =VA0CS$$>tPtO5AULEg}MhAw6^A#w#TxbD9B4l~qD6J~P-ggL0em|Ym%dUcgK=IFK z5`ybn{!gi`?weGf0DZ3m*2pO7bQVXNJG_jw(?MCe&GA2r*7x+D+E)cay|l_?jIO7& zx@cMnB)_w>`%|j4SKFvDs~&F~1%tpJkCwFU$mpu$7EvTsRaraSugpF|WmQ!4F55)O zqVb0t*X#y|hwEie>-L5{uk`WtZvE|h?rPYkVW3Y(->yCyHq6(%jmf2Z^g=5tV59-_ zgu{{@CkvJGmE8K=t V+U19AiunNH#v7gi06hW za0GwaO>C_ S=w)NK6<|1%5~uD`Btpd3s{UCZ*Zdl zlL`eIY;tlkYjWIJe+)P C9GP9- zTp1bn%T6vX7S8iKyEZ$A>Ipw{J%>@lNF %z5VbE zVNiI)Nl0lJL?Ff7`htj?8yqDB+}zB}NL S3WZbc>jxisACW$PRP?^PdMH79hd z{@8I>GN+S78Xfh1xxX8 MHgiz2@8|wC@tkmiXEiAXp`jjSv z$@NUYwv%Y(dmjuPNCXUg^?WaANk|T-+TslRLw9x@PmXeYr#Fg<&do=c$IHsf_M6x@ zcVm|I$SBH;C;vu9MR{I*z;n3M;s44P2LCTTh=_|D+S~h&I-j1Mar*6oQUjJ3p7zk3 zoNb-gM@Nu9&pmkLDi-Uk7OB*}I?;ah27x=g;g=v{VmeCZ^V$AWiHwW_1VpGf93E-x zTV7#+y9Wd!;!bMU6c9`=w|DaUThm#ihsEl85Zm_j-2x&W9;NIcx=oGC@AWCk{GXt} zUohO)8MB{T0b@zJs?N)9YcwFm+iZ0QJ)E)E0rKFvdHLJ(26<#7vH@DhOl)?GZ@|>J zJ=DJ11?)YEVepKO;|b9<(Z1~s$L)2$nKf8H;iY^$aYMsI0bVvOo*zxAH+K&gX&{Vp zPPy?CdT?3OoAcr?e6Gl_8Dec{A_BWE2by|Y?}+T8KMxN5kgwe?-eU*l42Kt j{bDNSdzC3Wwv*-#17*t$awz_K6RE5NDO6ye(=-ZL;c?!p2O6qogq895HSn8P@ zttVTH2@t;S?7yJn@D3A)!>Q}0sj8|r8KGUg%IOS@kIxsRI@}%7`ZV41f0N1K$l<=q z@*D;8A$ 3xjg#gdv zu=w@UQ_nePY;0^aCUeHji~ hq3?c0UpjGe~l;tC|<_fx4ogw zg=!e4T5 n zl2KBVt}nf#B#yC%1H+D*tQEPqxtTrZM1+I`JwGP>l~h$H0p`W?@Pr&u^tar>?mx;4 z3|#)C?})w{xy?ba=8r__s!A|{vDu#9lB4`17dOfnh} Tm^T z6z83CK0LKCQGKo7=xBm!yR*#XCbhqp?R!OS2FP!p&%6kArKCWQN&DyJf5IeTP;JEw zMja!7fyGt>-!j95Y&20r`T+4g#)5>2tFxe(`!7hbL pG!FbUf6EL}v89NK3FzGs??@QF z?^t=X43OzGi&z!`XB9d _IiD5 MiSU(1_qV(i@+9$HT zokybaQEYx9*XFjQfPvB#w?ZWJD)j2_C)UN71KT%2M%5I3Ie!#&a|MkCDg6LCsaCE5 z#Kl7A0M+d3i2C}ik@0jv;}3HRSl*d~47l&-xTQOo_tbX0cwHr9ds1!1|BR<<{`@)I zljj#tAhB!nR4PphqtU>@N#nPt>oWGvSF3n=j~Z-jY;-x} cO*6m32y#M`ztAr;; zLIy`lB68LKuh} vJM;1k z#;+`PosJ@5HD`N!{ea0Z*Zuki7|;Qr`FiyJhL`hX0Q`T%oE$u!yZb~Bk5*+Rt)a*( zbt27$Y9V?s_ba)>{n4L?)0u082ZjRxoVQ=u%IlrLec5LPL)gvh>C`8TGc9dydWtoM zH9pz;hh$8(*r)CbdAEKqed)?Au#4Nw>!l5hiRzxtE$0nbjIhcBE> W;}%u(&d-JNJ4u4Ebhw>3RD)Cy-*yJ;u9#9WmbqcI3=O@mCb~wo@?&t zgbl3*J>iZDgXb3xs3FcY2?E0_D#lIYG~do@78V!hiT~zun@t@z-ZJ!!jz)OGPaXiy zeIx1TI**sqhJf?NhFaP_;138;iCj264DP+r*xtxn@8+CgaYag7GruaN*PQkA^{3sb z{k|FO$Mj3$a5aukGuWMLz~ORDG+%IYn|P9vk|v~aG|oGQ`hD1Kw|v=di@Zt#3ln$o zubPOn)1Iw@M3PWA%>M?ggQq07hW-xUEB 2CCx-4O={Cq)wyx{)(<7q2wE(4@U-vgvhuxll;E>)k&Pqc8pxn6>aD(-jN z9!6n583Ugk)1_QWDM@U2*2K=PzU4v^0pvzBLHD0uJWRF8(gB@^oYbUEML&Ss*p)r4~LPp-DJw&6L^acsp=uH#KcbeY4Y zS`;@MS?Kn5UWHp%eLWMK%{s_mUiax!C)iNL{Cq;09F`ncVEub7WsSz*JI{D-qaBPP z4s}0y9~prR8muU+Z2Y+#-i$gD5ENEcV-K+Xc9~vLs(}Mi#ewO}C5dk5BBeSW*`jg3 z@WAsc3p+b^+Z6~fF-tj )vIo5>5fkG&8bI-yyzU$F=PVH~kgG_f#R zW<%`qIytb__5A@XTTAk9j*ZSHK7AT`Ik)a&!6dpODdBm>%WbZmO%GrR4Q_c$T8C~9 z&F-d7_wQ h5}oa&t|PtR-!eg=LR8Q hmRaZtJUJ}?#6%t{jECo7{ku4 zq@ &Yg>k zvuv?8G}K=gYkt@6-2e>ee}h%$U!hQN^OVT|?A^DY%1X;vI(kJ%;p5#k*SWDCM-ZU4 zuI|d^jrzEHGkf^GJUjY%B0M7Xf?qW8Yc*@@`RUYRKG^XRr;AQl g>JLz$kt@iR zhQdGxhjI7@t;Q$lX_WT<*q|3?DJcgGu9Oj%&7$cHLPCP^_xIC(wx=?>?L7499n)pE zowKuxypwNiXl(Pis;Vlo49ax)utck-s45f+Mhw5YfoxG!AR3;*SVPjC(bUw$zLbN8 z`A@QXig0Xf41yrYg`t+9cWg|TuWO!itZGzU*}?+>fk^)-o*MTY_w}ETyS>{54We|E zs&ZL!_*Ir2_>hv?cB(Q(C6jv1HPM>`yK+CLS=0JOlKXlXViD%3ZpbIM{7X5V)?VRH zOwb?N^&wW*93$h*6BL&|4^pvQv2JW6rBp`Z<^L%GVT*8d=?%879j1k*Qju{wvy};K zO(+x!`uDp*uRt=ouLAM*?t0>cCj-OwKfZFhX9Wgzh!ojZXRJcY}A^FAsp`l@= zS-{;CpjQfxxO* =GJw=P&0-$!|=G znn%CjVJJnri>#Vak#~|)`5Tg?LLtD>!Y}C4V+0<~{;~p5T!IU$uquu6x?c?~YbD$q z1{3Nx@{;$e*3?m0@*Cw f8w>>Y!!;qvCZ5$zREbrv3meE7yj z6&j7^ie2Y=fl(++l7yYo`bh;&&Q7um#ZoJD!-h?)`sruXYRyH$@KWZ&1 zPj3V>myoBkAHzZ(VgLC>hBRFj2lG}kzRzO@7Z$RxH=d=@Hj|$Jsj2HA&?op&6c;Ec zour|xBAJ4cMM%1H7xYTkD-Q?zM11|9z|}3m=Bl!2w ZD>nWsr>iY z B=pVdKypg9)6{Z8Rt5nc zGx76`Yo$-C>rRtb_&F^tyDqXe6apv}{`mMlkEhodMA04_EZnV{2i{bwD{_t6x;g+H z9UQuK8t>JDgJIVz2tq3h&}vqtN@bDYQmfT`^zkQTWo4sODtY{m3rI{H+F>STwOZY3 z6WQJ2=wK?;Yi@38w??WnsIPATz{|_?nmy+=cNE(z92g!ofoDd1%Fw{+t*Y6Lsj;Uk ze_`q22T0ETi6*sv(j1gdj0k^JX0#OrICOR~TAikKOIQI^%~fO-f2i* #*{;9^qpa zu(mYKwnOeCv+S#okl<_foZTkU= `Tu75p#@|V z?L{YPiSi#zpxY?fEmf`BM{enUGn#aL?MX(#YLKerzlHfdhO1K`8f_!Fg+Gy+btlzz z%TX9Is>~@!T%2zsJp5+@gXW^N^TdWu?8Y1&iI3|8K!%~P%9_yWbR11P20&a~dsH2I zfp5&{=?WSe8@lbB`}+FGiuByMb5 Z?c zEX g=w-MLuIlEn|N|0wHa|XZ6jMz7DcL blCQUxuv;V>`sV`Dm zn?q8>gD3<+?yGUU;NNJq lT8Y&(Ej0xCDUsxVW}ehakwV9aUr3 zU1@~{h27Zd^B}HIEC73w_sLms)__*4W!LUK?JL-ZMmg@0)CsrM^$Z7ldq#{H2EeXe zyRAC^`i +h3G+m->dYB zT8yXX;7&D;vEgD2=-&^eLP1MQ3kMG#vg+77ckTk9XJC)ZHd_(|f#Bet0F;!LcKN!8 zj~wl`^DPJhqetsA46}1`$;r*Ns`Zhb)Bdhnqfw*L=!4##UF@W&R;#UIHC{~^X=}31 z #3YdrzjlWmcE~i1Y#VpO&biAkgJYu3(5#T=+g~AaVtm#tmW%A?+0DSlTN@{9q ztTJvvK_TlmYzAP)j6bv+K#z^lSL3D~J1z^i&Y+~Egp~aUySdUHfU}{~-Y>uWYL#8E zMx$Zns-NVl@XHDpv)&wCWlAnBD{Hs?Zd-~}##K&MiizpP4GH=-$KU_3(&BoT_Hg9L zQBGxKA_xLgrcAoFoBiuS_;|VW;O4$hb8Gyk_&E1(6{j$7U2FQAV0MhTg<)zTYg8-> zLBhpp9KFJSAUyOj9PE8<(*6oz>eO3tb91Gprk3U3uIQ2g2Yr3X;;)vV)oSU}JBFmh z#CFe#kJr1n%F8P_d-iOnuB+4O_<7A*9336HZ4e#wbprR^a~A;n4;*4o^4>0wxpnI{ z($bD|$IR*NCM!5QyU6u9XY=y9blqLMc6aJL^9(?ZMtxQ1Ulc{|oqac=D3X3MgZ1k- zba~A3@^ZfXYAFDdCXTzN?yKuYff)7@gm}kud;j+s7XAtj3SWIzl~HLR$@C&}>sWK; zfz-+cCL&>P7e!dm$HYdxjjL-fY`DT30Z&gJdvpPUAh2`S9#(w+L#LHUOG^u1E%}<0 zr%vPO=)lAC=UJ4#+q-wHoc6f(*WWs|E8n?mH`})FWaf<9yR`xV7&U4neR}I-A)kM- zm}AF|cY3US`%+l?&3{Qs9KxtkH@2I`t5Ap}CFx^ddy-Q+ZFQ!nr}Mvc8#{Hbo4cE= z=*r5 v4j z9-MO@0KfjWo{v8Ml&Y$#4!bk{_5KI!+MSHOogFXy={bUedRla@qobp|D^J;fh;{4M zTl_+6wOTfB-ooc!d`16$3EVhhgw=zi)`q>kJx@ITC;|R{Xf#^hdi!0rZ{N`&cTh`9 z3%{)Wl|>(ZOlW8@b03_8AP5#c`xrwUrm3lk72kKvjU!1ChYug&t$+TDguZc|3c|a& zx)L3&kBc2Te56e;RzqW>)ss7nm^g6)x8AI0&;4TYQdX?^fyNHjLuF-U@$xILb0#|% zKVKjI^!&nZL20kNUImepy$3@g? 2Ny*N4c zuu1c4gh>-8U~gx~;w9g(FXaHoj;AwY`ZN-U4#n5k=aRd6Ns=flD`)S%ef;$EFElkZ z Z)uggf1%4NCnuNHKmE+%BdG)j z_2iKS^Rcs2B1uxWw?ce 8)>t>gnl0sf9@h;o;%jG4pnQT=g^Cw(q3AzMebpm`O-TaGUd{)9J`N zcaH!4Z#}zqCleML!b5ZC@a*${>A2rQ5EwshESZ^SNZy;m$Qws6aNvN8+(Msy{w2*V zEj+jIX{&QriK58t*>~gQ?8K_oYgn`PH})Sm$n@K%FbJ? 4JTO w*<0sNNej*)D zPY?RW_aQVSMBd@Iw5U0goz1CJr)gBF5CpjOmdVVVF+&c@>vWbWPC*d3{SVVnx2RdO z_BRe3Jj{WEhv^j^MSOf8dIkpK;NXBLiZrTJ6c!e8;NW43427YC2KMLS`SY;1x9ha) zCn+(Jty{NqIx~xR-dn`@abpPTskd0q=jXF?*B;br4eoAk)YaEt5%NQYLf?ohHa3PY z7cZr#xP(tW`x2!>K|(@2y`rP>^zuZhP|(oWNKsJ{M^lf f_w%O;Q0l4C7U>3_922)iwM{&0tpRxngE}Xh~n>?;-b5$&B(|IUViEC z96oY{_3Jm1aXO3BnfkA`UczBXLzy&j0x>bYy0r6mbaZ6Fg89V9$Fcmo@2RP&Wl!=x z_9XASw8iV>$n@K%F=^rixu3M9ZXlV{iEGQz<>BGMf`{iZX3Qwot=qu<1Ba-tu3=Ad z3XpQKa8N3lFn%l(CyXO3EbNlcWXZ}>DwW)Q_bd_;`m*|`pE-HzG+Ae|<$fiXR(Ss0 zb>~bH6NjQyDygll>$C!KbaddEXP)G<&%Pk-*a sO?Q{oG`LnVTXO_w8mJp=> zhs<-T=5q3if5cAVPDsF1>@P#t{(C}gZ7qd{>eaCDFq|B_PpYycNtBkBl6&?nWu;|Q z*VNF|)P$R>8=jsX^z7M_u&_`(x>!%pmaFDeMh3@^r<0kL#o4oYsMQ+$e0@kr=u5vF z66o7E9yhnvbp1_DO{Axv1Rx?J0&j1xOOD^z*r;bNRVcdLOiQQJQCg~}gK=|n?b2?v zy1JTk=gw19R76>MIqDWQf+*nO?oL!x6yf1vmvyfgDN;N ^hR7ilGQrTr@V2wsYd3lB5b9d+e07c%GRZm}Z761SM07*qo IM6N<$f~WvRm;e9( literal 0 HcmV?d00001 diff --git a/docs/img/reserved.svg b/docs/img/reserved.svg new file mode 100644 index 000000000..529135e09 --- /dev/null +++ b/docs/img/reserved.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/docs/json-schema.md b/docs/json-schema.md index d1d789d1c..c8689f04d 100644 --- a/docs/json-schema.md +++ b/docs/json-schema.md @@ -1,4 +1,4 @@ -# JSON Schema keywords +# JSON Schema In a simple way, JSON Schema is an object with validation keywords. From e493e0ff749b43450627683f4ac8457616886d1a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Thu, 4 Mar 2021 22:17:25 +0000 Subject: [PATCH 05/25] vuepress site: guide --- .gitignore | 5 +- .tonic_example.js | 10 +- CODE_OF_CONDUCT.md | 4 + CONTRIBUTING.md | 4 + benchmark/jtd.js | 10 + docs/.vuepress/config.js | 35 ++- docs/LICENSE.md | 21 ++ docs/OLD_README.md | 281 +++++++++++++++++ docs/README.md | 538 ++------------------------------- docs/api.md | 1 + docs/guide/environments.md | 120 ++++++++ docs/guide/getting-started.md | 164 ++++++++++ docs/guide/managing-schemas.md | 60 ++++ docs/guide/modular-schemas.md | 233 ++++++++++++++ docs/guide/schema-language.md | 54 ++++ docs/guide/typescript.md | 205 +++++++++++++ docs/json-schema.md | 3 +- docs/validation.md | 259 ---------------- scripts/publish-site | 9 + 19 files changed, 1227 insertions(+), 789 deletions(-) create mode 100644 docs/LICENSE.md create mode 100644 docs/OLD_README.md create mode 100644 docs/guide/environments.md create mode 100644 docs/guide/getting-started.md create mode 100644 docs/guide/managing-schemas.md create mode 100644 docs/guide/modular-schemas.md create mode 100644 docs/guide/schema-language.md create mode 100644 docs/guide/typescript.md create mode 100755 scripts/publish-site diff --git a/.gitignore b/.gitignore index 8fc84d941..9b3f4a121 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,7 @@ bundle/ package-lock.json -spec/_json/*.js \ No newline at end of file +spec/_json/*.js + +docs/CODE_OF_CONDUCT.md +docs/CONTRIBUTING.md diff --git a/.tonic_example.js b/.tonic_example.js index 2b0d6683e..7579d21c5 100644 --- a/.tonic_example.js +++ b/.tonic_example.js @@ -1,20 +1,20 @@ -var Ajv = require("ajv") -var ajv = new Ajv({allErrors: true}) +const Ajv = require("ajv").default +const ajv = new Ajv({allErrors: true}) -var schema = { +const schema = { properties: { foo: {type: "string"}, bar: {type: "number", maximum: 3}, }, } -var validate = ajv.compile(schema) +const validate = ajv.compile(schema) test({foo: "abc", bar: 2}) test({foo: 2, bar: 4}) function test(data) { - var valid = validate(data) + const valid = validate(data) if (valid) console.log("Valid!") else console.log("Invalid: " + ajv.errorsText(validate.errors)) } diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index b8026a2cf..c96b8e5dc 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +--- +permalink: /code_of_conduct +--- + # Contributor Covenant Code of Conduct ## Our Pledge diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b105d2f8a..a4aa54052 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,7 @@ +--- +permalink: /contributing +--- + # Contributing Thank you for your help making Ajv better! Every contribution is appreciated. If you plan to implement a new feature or some other change please create an issue first, to make sure that your work is not lost. diff --git a/benchmark/jtd.js b/benchmark/jtd.js index 88c2a5ae2..0127fdf5b 100644 --- a/benchmark/jtd.js +++ b/benchmark/jtd.js @@ -13,6 +13,7 @@ for (const testName in jtdValidationTests) { const valid = errors.length === 0 if (!valid) continue tests.push({ + validate: ajv.compile(schema), serialize: ajv.compileSerializer(schema), parse: ajv.compileParser(schema), data: instance, @@ -77,6 +78,12 @@ suite.add("JTD test suite: JSON.parse", () => { } }) +suite.add("JTD test suite: JSON.parse + validate", () => { + for (const test of tests) { + JSON.parse(test.json) + } +}) + const validTestData = JSON.stringify(testData) const invalidTestData = JSON.stringify({ @@ -91,11 +98,14 @@ const invalidTestData = JSON.stringify({ }) const parse = ajv.compileParser(testSchema) +const validate = ajv.compile(testSchema) suite.add("valid test data: compiled JTD parser", () => parse(validTestData)) suite.add("valid test data: JSON.parse", () => JSON.parse(validTestData)) +suite.add("valid test data: JSON.parse + validate", () => validate(JSON.parse(validTestData))) suite.add("invalid test data: compiled JTD parser", () => parse(invalidTestData)) suite.add("invalid test data: JSON.parse", () => JSON.parse(invalidTestData)) +suite.add("invalid test data: JSON.parse + validate", () => validate(JSON.parse(invalidTestData))) console.log() diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index b872f9364..1cf1a3379 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -5,7 +5,7 @@ module.exports = { description: "Just playing around", markdown: { slugify: (str) => slugify(str.replace(/ ]*\/>/, "")), - toc: {includeLevel: [2, 3, 4]} + toc: {includeLevel: [2, 3, 4]}, }, themeConfig: { logo: "/ajv.svg", @@ -33,17 +33,27 @@ module.exports = { ], sidebar: [ "/", - "/faq", - "/security", { - title: "Validation", + title: "Guide", + collapsable: false, + children: [ + "/guide/getting-started", + "/guide/typescript", + "/guide/schema-language", + "/guide/managing-schemas", + "/guide/modular-schemas", + "/guide/environments", + ] + }, + { + title: "Reference", collapsable: false, children: [ "/validation", - "/strict-mode", + "/api", "/json-schema", "/json-type-definition", - "/api", + "/strict-mode", "/keywords", "/coercion", ], @@ -53,9 +63,18 @@ module.exports = { collapsable: false, children: ["/standalone", "/codegen", "/components"], }, + { + title: "Information", + collapsable: false, + children: [ + "/faq", + "/security", + "/CONTRIBUTING", + ["/CODE_OF_CONDUCT", "Code of conduct"], + ["/LICENSE", "License"] + ], + }, ], - nextLinks: false, - prevLinks: false, repo: "ajv-validator/ajv", docsDir: "docs", editLinks: true, diff --git a/docs/LICENSE.md b/docs/LICENSE.md new file mode 100644 index 000000000..a0f827cbd --- /dev/null +++ b/docs/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2017 Evgeny Poberezkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docs/OLD_README.md b/docs/OLD_README.md new file mode 100644 index 000000000..8e50db426 --- /dev/null +++ b/docs/OLD_README.md @@ -0,0 +1,281 @@ +# Ajv: Another JSON validator + + + +The fastest JSON schema validator for Node.js and browser. + +Supports JSON Schema draft-06/07/2019-09 (draft-04 is supported in [version 6](https://github.com/ajv-validator/ajv/tree/v6)) and JSON Type Definition [RFC8927](https://datatracker.ietf.org/doc/rfc8927/). + +::: v-pre +[![build](https://github.com/ajv-validator/ajv/workflows/build/badge.svg)](https://github.com/ajv-validator/ajv/actions?query=workflow%3Abuild) +[![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv) +[![npm downloads](https://img.shields.io/npm/dm/ajv.svg)](https://www.npmjs.com/package/ajv) +[![Coverage Status](https://coveralls.io/repos/github/ajv-validator/ajv/badge.svg?branch=master)](https://coveralls.io/github/ajv-validator/ajv?branch=master) +[![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) +[![GitHub Sponsors](https://img.shields.io/badge/$-sponsors-brightgreen)](https://github.com/sponsors/epoberezkin) +::: + +## Platinum sponsors + +::: v-pre +[](https://www.mozilla.org)[](https://opencollective.com/ajv) +::: + +## Using version 7 + +Ajv version 7 has these new features: + +- NEW: support of JSON Type Definition [RFC8927](https://datatracker.ietf.org/doc/rfc8927/) (from [v7.1.0](https://github.com/ajv-validator/ajv-keywords/releases/tag/v7.1.0)), including generation of [serializers](./docs/api.md#jtd-serialize) and [parsers](./docs/api.md#jtd-parse) from JTD schemas that are more efficient than native JSON serialization/parsing, combining JSON string parsing and validation in one function. +- support of JSON Schema draft-2019-09 features: [`unevaluatedProperties`](./docs/json-schema.md#unevaluatedproperties) and [`unevaluatedItems`](./docs/json-schema.md#unevaluateditems), [dynamic recursive references](./docs/validation.md#extending-recursive-schemas) and other [additional keywords](./docs/json-schema.md#json-schema-draft-2019-09). +- to reduce the mistakes in JSON schemas and unexpected validation results, [strict mode](./docs/strict-mode.md) is added - it prohibits ignored or ambiguous JSON Schema elements. +- to make code injection from untrusted schemas impossible, [code generation](./docs/codegen.md) is fully re-written to be safe and to allow code optimization (compiled schema code size is reduced by more than 10%). +- to simplify Ajv extensions, the new keyword API that is used by pre-defined keywords is available to user-defined keywords - it is much easier to define any keywords now, especially with subschemas. [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) package was updated to use the new API (in [v4.0.0](https://github.com/ajv-validator/ajv-keywords/releases/tag/v4.0.0)) +- schemas are compiled to ES6 code (ES5 code generation is also supported with an option). +- to improve reliability and maintainability the code is migrated to TypeScript. + +**Please note**: + +- the support for JSON-Schema draft-04 is removed - if you have schemas using "id" attributes you have to replace them with "\$id" (or continue using [Ajv v6](https://github.com/ajv-validator/ajv/tree/v6) that will be supported until 02/28/2021). +- all formats are separated to ajv-formats package - they have to be explicitly added if you use them. + +See [release notes](https://github.com/ajv-validator/ajv/releases/tag/v7.0.0) for the details. + +To install the new version: + +```bash +npm install ajv +``` + +See [Getting started](#usage) for code example. + +## Contributing + +100+ people contributed to Ajv. You are very welcome to join by implementing new features that are valuable to many users and by improving documentation. + +Please do not be disappointed if your suggestion is not accepted - it is important to maintain the balance between the library size, performance and functionality. If it seems that a feature would benefit only a small number of users, its addition may be delayed until there is more support from the users community - so please submit the issue first to explain why this feature is important. + +Please include documentation and test coverage with any new feature implementations. + +To run tests: + +```bash +npm install +git submodule update --init +npm test +``` + +`npm run build` - compiles typescript to `dist` folder. + +Please review [Contributing guidelines](./CONTRIBUTING.md) and [Code components](./docs/components.md). + +## Contents + +- [Platinum sponsors](#platinum-sponsors) +- [Using version 7](#using-version-7) +- [Contributing](#contributing) +- [Mozilla MOSS grant and OpenJS Foundation](#mozilla-moss-grant-and-openjs-foundation) +- [Sponsors](#sponsors) +- [Performance](#performance) +- [Features](#features) +- [Frequently Asked Questions](./docs/faq.md) +- [Using in browser](#using-in-browser) + - [Content Security Policy](./docs/security.md#content-security-policy) +- [Using in ES5 environment](#using-in-es5-environment) +- [Command line interface](#command-line-interface) +- [API reference](./docs/api.md) + - [Methods](./docs/api.md#ajv-constructor-and-methods) + - [Options](./docs/api.md#options) + - [Validation errors](./docs/api.md#validation-errors) +- NEW: [Strict mode](./docs/strict-mode.md#strict-mode) + - [Prohibit ignored keywords](./docs/strict-mode.md#prohibit-ignored-keywords) + - [Prevent unexpected validation](./docs/strict-mode.md#prevent-unexpected-validation) + - [Strict types](./docs/strict-mode.md#strict-types) + - [Strict number validation](./docs/strict-mode.md#strict-number-validation) +- [Data validation](./docs/validation.md) + - [Validation basics](./docs/validation.md#validation-basics): [JSON Schema keywords](./docs/validation.md#validation-keywords), [annotations](./docs/validation.md#annotation-keywords), [formats](./docs/validation.md#formats) + - [Modular schemas](./docs/validation.md#modular-schemas): [combining with \$ref](./docs/validation.md#ref), [\$data reference](./docs/validation.md#data-reference), [$merge and $patch](./docs/validation.md#merge-and-patch-keywords) + - [Asynchronous schema compilation](./docs/validation.md#asynchronous-schema-compilation) + - [Standalone validation code](./docs/standalone.md) + - [Asynchronous validation](./docs/validation.md#asynchronous-validation) + - [Modifying data](./docs/validation.md#modifying-data-during-validation): [additional properties](./docs/validation.md#removing-additional-properties), [defaults](./docs/validation.md#assigning-defaults), [type coercion](./docs/validation.md#coercing-data-types) +- [Extending Ajv](#extending-ajv) + - User-defined keywords: + - [basics](./docs/validation.md#user-defined-keywords) + - [guide](./docs/keywords.md) + - [Plugins](#plugins) + - [Related packages](#related-packages) +- [Security considerations](./docs/security.md) + - [Security contact](./docs/security.md#security-contact) + - [Untrusted schemas](./docs/security.md#untrusted-schemas) + - [Circular references in objects](./docs/security.md#circular-references-in-javascript-objects) + - [Trusted schemas](./docs/security.md#security-risks-of-trusted-schemas) + - [ReDoS attack](./docs/security.md#redos-attack) + - [Content Security Policy](./docs/security.md#content-security-policy) +- [Some packages using Ajv](#some-packages-using-ajv) +- [Changes history](#changes-history) +- [Support, Code of conduct, Contacts, License](#open-source-software-support) + +## Mozilla MOSS grant and OpenJS Foundation + +::: v-pre +[](https://www.mozilla.org/en-US/moss/) [](https://openjsf.org/blog/2020/08/14/ajv-joins-openjs-foundation-as-an-incubation-project/) +::: + +Ajv has been awarded a grant from Mozilla’s [Open Source Support (MOSS) program](https://www.mozilla.org/en-US/moss/) in the “Foundational Technology” track! It will sponsor the development of Ajv support of [JSON Schema version 2019-09](https://tools.ietf.org/html/draft-handrews-json-schema-02) and of [JSON Type Definition (RFC8927)](https://datatracker.ietf.org/doc/rfc8927/). + +Ajv also joined [OpenJS Foundation](https://openjsf.org/) – having this support will help ensure the longevity and stability of Ajv for all its users. + +This [blog post](https://www.poberezkin.com/posts/2020-08-14-ajv-json-validator-mozilla-open-source-grant-openjs-foundation.html) has more details. + +I am looking for the long term maintainers of Ajv – working with [ReadySet](https://www.thereadyset.co/), also sponsored by Mozilla, to establish clear guidelines for the role of a "maintainer" and the contribution standards, and to encourage a wider, more inclusive, contribution from the community. + +## Please [sponsor Ajv development](https://github.com/sponsors/epoberezkin) + +Since I asked to support Ajv development 40 people and 6 organizations contributed via GitHub and OpenCollective - this support helped receiving the MOSS grant! + +Your continuing support is very important - the funds will be used to develop and maintain Ajv once the next major version is released. + +Please sponsor Ajv via: + +- [GitHub sponsors page](https://github.com/sponsors/epoberezkin) (GitHub will match it) +- [Ajv Open Collective️](https://opencollective.com/ajv) + +Thank you. + +#### Open Collective sponsors + + + + + + + + + + + + + + +## Performance + +Ajv generates code to turn JSON Schemas into super-fast validation functions that are efficient for v8 optimization. + +Currently Ajv is the fastest and the most standard compliant validator according to these benchmarks: + +- [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark) - 50% faster than the second place +- [jsck benchmark](https://github.com/pandastrike/jsck#benchmarks) - 20-190% faster +- [z-schema benchmark](https://rawgit.com/zaggino/z-schema/master/benchmark/results.html) +- [themis benchmark](https://cdn.rawgit.com/playlyfe/themis/master/benchmark/results.html) + +Performance of different validators by [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark): + +[![performance](https://chart.googleapis.com/chart?chxt=x,y&cht=bhs&chco=76A4FB&chls=2.0&chbh=32,4,1&chs=600x416&chxl=-1:|djv|ajv|json-schema-validator-generator|jsen|is-my-json-valid|themis|z-schema|jsck|skeemas|json-schema-library|tv4&chd=t:100,98,72.1,66.8,50.1,15.1,6.1,3.8,1.2,0.7,0.2)](https://github.com/ebdrup/json-schema-benchmark/blob/master/README.md#performance) + +## Features + +- Ajv implements JSON Schema [draft-06/07/2019-09](http://json-schema.org/) standards (draft-04 is supported in v6): + - all validation keywords (see [JSON Schema validation keywords](./docs/json-schema.md)) + - keyword "nullable" from [Open API 3 specification](https://swagger.io/docs/specification/data-models/data-types/). + - full support of remote references (remote schemas have to be added with `addSchema` or compiled to be available) + - support of circular references between schemas + - correct string lengths for strings with unicode pairs + - [formats](#formats) defined by JSON Schema draft-07 standard (with [ajv-formats](https://github.com/ajv-validator/ajv-formats) plugin) and additional formats (can be turned off) + - [validates schemas against meta-schema](./docs/api.md#api-validateschema) +- NEW: supports [JSON Type Definition](https://datatracker.ietf.org/doc/rfc8927/): + - all forms (see [JSON Type Definition schema forms](./docs/json-type-definition.md)) + - meta-schema for JTD schemas + - "union" keyword and user-defined keywords (can be used inside "metadata" member of the schema) +- supports [browsers](#using-in-browser) and Node.js 0.10-14.x +- [asynchronous loading](./docs/validation.md#asynchronous-schema-compilation) of referenced schemas during compilation +- "All errors" validation mode with [option allErrors](./docs/api.md#options) +- [error messages with parameters](./docs/api.md#validation-errors) describing error reasons to allow error message generation +- i18n error messages support with [ajv-i18n](https://github.com/ajv-validator/ajv-i18n) package +- [removing-additional-properties](./docs/validation.md#removing-additional-properties) +- [assigning defaults](./docs/validation.md#assigning-defaults) to missing properties and items +- [coercing data](./docs/validation.md#coercing-data-types) to the types specified in `type` keywords +- [user-defined keywords](#user-defined-keywords) +- draft-06/07 keywords `const`, `contains`, `propertyNames` and `if/then/else` +- draft-06 boolean schemas (`true`/`false` as a schema to always pass/fail). +- additional extension keywords with [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) package +- [\$data reference](./docs/validation.md#data-reference) to use values from the validated data as values for the schema keywords +- [asynchronous validation](./docs/api.md#asynchronous-validation) of user-defined formats and keywords + +## Extending Ajv + +### User defined keywords + +See section in [data validation](./docs/validation.md#user-defined-keywords) and the [detailed guide](./docs/keywords.md). + +### Plugins + +Ajv can be extended with plugins that add keywords, formats or functions to process generated code. When such plugin is published as npm package it is recommended that it follows these conventions: + +- it exports a function that accepts ajv instance as the first parameter - it allows using plugins with [ajv-cli](#command-line-interface). +- this function returns the same instance to allow chaining. +- this function can accept an optional configuration as the second parameter. + +You can import `Plugin` interface from ajv if you use Typescript. + +If you have published a useful plugin please submit a PR to add it to the next section. + +### Related packages + +- [ajv-bsontype](https://github.com/BoLaMN/ajv-bsontype) - plugin to validate mongodb's bsonType formats +- [ajv-cli](https://github.com/jessedc/ajv-cli) - command line interface +- [ajv-formats](https://github.com/ajv-validator/ajv-formats) - formats defined in JSON Schema specification +- [ajv-errors](https://github.com/ajv-validator/ajv-errors) - plugin for defining error messages in the schema +- [ajv-i18n](https://github.com/ajv-validator/ajv-i18n) - internationalised error messages +- [ajv-istanbul](https://github.com/ajv-validator/ajv-istanbul) - plugin to instrument generated validation code to measure test coverage of your schemas +- [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) - plugin with additional validation keywords (select, typeof, etc.) +- [ajv-merge-patch](https://github.com/ajv-validator/ajv-merge-patch) - plugin with keywords $merge and $patch +- [ajv-formats-draft2019](https://github.com/luzlab/ajv-formats-draft2019) - format validators for draft2019 that aren't included in [ajv-formats](https://github.com/ajv-validator/ajv-formats) (ie. `idn-hostname`, `idn-email`, `iri`, `iri-reference` and `duration`) + +## Some packages using Ajv + +- [webpack](https://github.com/webpack/webpack) - a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser +- [jsonscript-js](https://github.com/JSONScript/jsonscript-js) - the interpreter for [JSONScript](http://www.jsonscript.org) - scripted processing of existing endpoints and services +- [osprey-method-handler](https://github.com/mulesoft-labs/osprey-method-handler) - Express middleware for validating requests and responses based on a RAML method object, used in [osprey](https://github.com/mulesoft/osprey) - validating API proxy generated from a RAML definition +- [har-validator](https://github.com/ahmadnassri/har-validator) - HTTP Archive (HAR) validator +- [jsoneditor](https://github.com/josdejong/jsoneditor) - a web-based tool to view, edit, format, and validate JSON http://jsoneditoronline.org +- [JSON Schema Lint](https://github.com/nickcmaynard/jsonschemalint) - a web tool to validate JSON/YAML document against a single JSON Schema http://jsonschemalint.com +- [objection](https://github.com/vincit/objection.js) - SQL-friendly ORM for Node.js +- [table](https://github.com/gajus/table) - formats data into a string table +- [ripple-lib](https://github.com/ripple/ripple-lib) - a JavaScript API for interacting with [Ripple](https://ripple.com) in Node.js and the browser +- [restbase](https://github.com/wikimedia/restbase) - distributed storage with REST API & dispatcher for backend services built to provide a low-latency & high-throughput API for Wikipedia / Wikimedia content +- [hippie-swagger](https://github.com/CacheControl/hippie-swagger) - [Hippie](https://github.com/vesln/hippie) wrapper that provides end to end API testing with swagger validation +- [react-form-controlled](https://github.com/seeden/react-form-controlled) - React controlled form components with validation +- [rabbitmq-schema](https://github.com/tjmehta/rabbitmq-schema) - a schema definition module for RabbitMQ graphs and messages +- [@query/schema](https://www.npmjs.com/package/@query/schema) - stream filtering with a URI-safe query syntax parsing to JSON Schema +- [chai-ajv-json-schema](https://github.com/peon374/chai-ajv-json-schema) - chai plugin to us JSON Schema with expect in mocha tests +- [grunt-jsonschema-ajv](https://github.com/SignpostMarv/grunt-jsonschema-ajv) - Grunt plugin for validating files against JSON Schema +- [extract-text-webpack-plugin](https://github.com/webpack-contrib/extract-text-webpack-plugin) - extract text from bundle into a file +- [electron-builder](https://github.com/electron-userland/electron-builder) - a solution to package and build a ready for distribution Electron app +- [addons-linter](https://github.com/mozilla/addons-linter) - Mozilla Add-ons Linter +- [gh-pages-generator](https://github.com/epoberezkin/gh-pages-generator) - multi-page site generator converting markdown files to GitHub pages +- [ESLint](https://github.com/eslint/eslint) - the pluggable linting utility for JavaScript and JSX +- [Spectral](https://github.com/stoplightio/spectral) - the customizable linting utility for JSON/YAML, OpenAPI, AsyncAPI, and JSON Schema + +## Changes history + +See https://github.com/ajv-validator/ajv/releases + +**Please note**: [Changes in version 7.0.0](https://github.com/ajv-validator/ajv/releases/tag/v7.0.0) + +[Version 6.0.0](https://github.com/ajv-validator/ajv/releases/tag/v6.0.0). + +## Code of conduct + +Please review and follow the [Code of conduct](./CODE_OF_CONDUCT.md). + +Please report any unacceptable behaviour to ajv.validator@gmail.com - it will be reviewed by the project team. + +## Security contact + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure. Please do NOT report security vulnerabilities via GitHub issues. + +## Open-source software support + +Ajv is a part of [Tidelift subscription](https://tidelift.com/subscription/pkg/npm-ajv?utm_source=npm-ajv&utm_medium=referral&utm_campaign=readme) - it provides a centralised support to open-source software users, in addition to the support provided by software maintainers. diff --git a/docs/README.md b/docs/README.md index 60ef492a0..1f03ab021 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,537 +1,45 @@ -# Ajv: Another JSON validator +# Ajv JSON Validator - +Safety, security and reliability for JavaScript applications -The fastest JSON schema validator for Node.js and browser. +## Ajv News -Supports JSON Schema draft-06/07/2019-09 (draft-04 is supported in [version 6](https://github.com/ajv-validator/ajv/tree/v6)) and JSON Type Definition [RFC8927](https://datatracker.ietf.org/doc/rfc8927/). +This section will include the last update and the headlines of several previous updates, e.g. these sections: -::: v-pre -[![build](https://github.com/ajv-validator/ajv/workflows/build/badge.svg)](https://github.com/ajv-validator/ajv/actions?query=workflow%3Abuild) -[![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv) -[![npm downloads](https://img.shields.io/npm/dm/ajv.svg)](https://www.npmjs.com/package/ajv) -[![Coverage Status](https://coveralls.io/repos/github/ajv-validator/ajv/badge.svg?branch=master)](https://coveralls.io/github/ajv-validator/ajv?branch=master) -[![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) -[![GitHub Sponsors](https://img.shields.io/badge/$-sponsors-brightgreen)](https://github.com/sponsors/epoberezkin) -::: +https://github.com/ajv-validator/ajv#using-version-7 -## Platinum sponsors +https://github.com/ajv-validator/ajv#mozilla-moss-grant-and-openjs-foundation -::: v-pre -[](https://www.mozilla.org)[](https://opencollective.com/ajv) -::: +### Write less code -## Using version 7 +**Ensure your data is valid once it's received** -Ajv version 7 has these new features: +Instead of having your data validation and sanitization logic scattered around your code, you can express the requirements to your data with concise, easy to read and cross-platform [JSON Schema](https://json-schema.org) or [JSON Type Definition](https://jsontypedef.com) specifications and validate the data as soon as it arrives to your application. TypeScript users can use validation functions as type guards, having type level guarantee that if your data is validated - it is correct. -- NEW: support of JSON Type Definition [RFC8927](https://datatracker.ietf.org/doc/rfc8927/) (from [v7.1.0](https://github.com/ajv-validator/ajv-keywords/releases/tag/v7.1.0)), including generation of [serializers](./docs/api.md#jtd-serialize) and [parsers](./docs/api.md#jtd-parse) from JTD schemas that are more efficient than native JSON serialization/parsing, combining JSON string parsing and validation in one function. -- support of JSON Schema draft-2019-09 features: [`unevaluatedProperties`](./docs/json-schema.md#unevaluatedproperties) and [`unevaluatedItems`](./docs/json-schema.md#unevaluateditems), [dynamic recursive references](./docs/validation.md#extending-recursive-schemas) and other [additional keywords](./docs/json-schema.md#json-schema-draft-2019-09). -- to reduce the mistakes in JSON schemas and unexpected validation results, [strict mode](./docs/strict-mode.md) is added - it prohibits ignored or ambiguous JSON Schema elements. -- to make code injection from untrusted schemas impossible, [code generation](./docs/codegen.md) is fully re-written to be safe and to allow code optimization (compiled schema code size is reduced by more than 10%). -- to simplify Ajv extensions, the new keyword API that is used by pre-defined keywords is available to user-defined keywords - it is much easier to define any keywords now, especially with subschemas. [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) package was updated to use the new API (in [v4.0.0](https://github.com/ajv-validator/ajv-keywords/releases/tag/v4.0.0)) -- schemas are compiled to ES6 code (ES5 code generation is also supported with an option). -- to improve reliability and maintainability the code is migrated to TypeScript. +### Super fast and secure -**Please note**: +**The fastest and the most secure JSON validator** -- the support for JSON-Schema draft-04 is removed - if you have schemas using "id" attributes you have to replace them with "\$id" (or continue using [Ajv v6](https://github.com/ajv-validator/ajv/tree/v6) that will be supported until 02/28/2021). -- all formats are separated to ajv-formats package - they have to be explicitly added if you use them. +Ajv was designed at the time when there were no validators fully complying with JSON Schema specification, aiming to achieve the best possibly validation performance via just-in-time compilation of JSON schemas to code. Ajv achieved both speed and rigour, but initially security was an afterthought - many security flaws have been fixed thanks to the reports from its users. Ajv version 7 was rebuilt to have secure code generation embedded in its design as the primary objective - even if you use untrusted schemas (which is still not recommended) there are type-level guarantees against remote code execution. -See [release notes](https://github.com/ajv-validator/ajv/releases/tag/v7.0.0) for the details. +### Multi-specification -To install the new version: +**Choose your JSON schema standard** -```bash -npm install ajv -``` +In addition to the latest JSON Schema draft 2020-12, Ajv version 8 added support for JSON Type Definition - a new [RFC8927](https://datatracker.ietf.org/doc/rfc8927/) that offers a much simpler and less error-prone alternative to JSON Schema. Designed to be well-aligned with type systems, JTD has tools for both validation and type code generation for multiple languages. -See [Getting started](#usage) for code example. +## Introduction (no section heading) -## Contributing +Ajv is a widely used library that provides reliability, safety and security to millions of JavaScript applications and other libraries. It can be used in all JavaScript environments - node.js, browsers, Electron apps, etc. If your environment or security policy prohibit run-time function construction you can compile your schemas during build time into a standalone validation code (it may still have dependencies on small parts of Ajv code, but doesn't use the whole library) - since version 7 it is fully supported for all JSON schemas. -100+ people contributed to Ajv. You are very welcome to join by implementing new features that are valuable to many users and by improving documentation. +Installation -Please do not be disappointed if your suggestion is not accepted - it is important to maintain the balance between the library size, performance and functionality. If it seems that a feature would benefit only a small number of users, its addition may be delayed until there is more support from the users community - so please submit the issue first to explain why this feature is important. +Usage example / or small playground -Please include documentation and test coverage with any new feature implementations. +Try in the playground (TBC) -To run tests: +## Who uses Ajv -```bash -npm install -git submodule update --init -npm test -``` +## Contributors -`npm run build` - compiles typescript to `dist` folder. - -Please review [Contributing guidelines](./CONTRIBUTING.md) and [Code components](./docs/components.md). - -## Contents - -- [Platinum sponsors](#platinum-sponsors) -- [Using version 7](#using-version-7) -- [Contributing](#contributing) -- [Mozilla MOSS grant and OpenJS Foundation](#mozilla-moss-grant-and-openjs-foundation) -- [Sponsors](#sponsors) -- [Performance](#performance) -- [Features](#features) -- [Getting started](#usage) -- [Choosing schema language](#choosing-schema-language) - - [JSON Schema](#json-schema) - - [JSON Type Definition](#json-type-definition) -- [Frequently Asked Questions](./docs/faq.md) -- [Using in browser](#using-in-browser) - - [Content Security Policy](./docs/security.md#content-security-policy) -- [Using in ES5 environment](#using-in-es5-environment) -- [Command line interface](#command-line-interface) -- [API reference](./docs/api.md) - - [Methods](./docs/api.md#ajv-constructor-and-methods) - - [Options](./docs/api.md#options) - - [Validation errors](./docs/api.md#validation-errors) -- NEW: [Strict mode](./docs/strict-mode.md#strict-mode) - - [Prohibit ignored keywords](./docs/strict-mode.md#prohibit-ignored-keywords) - - [Prevent unexpected validation](./docs/strict-mode.md#prevent-unexpected-validation) - - [Strict types](./docs/strict-mode.md#strict-types) - - [Strict number validation](./docs/strict-mode.md#strict-number-validation) -- [Data validation](./docs/validation.md) - - [Validation basics](./docs/validation.md#validation-basics): [JSON Schema keywords](./docs/validation.md#validation-keywords), [annotations](./docs/validation.md#annotation-keywords), [formats](./docs/validation.md#formats) - - [Modular schemas](./docs/validation.md#modular-schemas): [combining with \$ref](./docs/validation.md#ref), [\$data reference](./docs/validation.md#data-reference), [$merge and $patch](./docs/validation.md#merge-and-patch-keywords) - - [Asynchronous schema compilation](./docs/validation.md#asynchronous-schema-compilation) - - [Standalone validation code](./docs/standalone.md) - - [Asynchronous validation](./docs/validation.md#asynchronous-validation) - - [Modifying data](./docs/validation.md#modifying-data-during-validation): [additional properties](./docs/validation.md#removing-additional-properties), [defaults](./docs/validation.md#assigning-defaults), [type coercion](./docs/validation.md#coercing-data-types) -- [Extending Ajv](#extending-ajv) - - User-defined keywords: - - [basics](./docs/validation.md#user-defined-keywords) - - [guide](./docs/keywords.md) - - [Plugins](#plugins) - - [Related packages](#related-packages) -- [Security considerations](./docs/security.md) - - [Security contact](./docs/security.md#security-contact) - - [Untrusted schemas](./docs/security.md#untrusted-schemas) - - [Circular references in objects](./docs/security.md#circular-references-in-javascript-objects) - - [Trusted schemas](./docs/security.md#security-risks-of-trusted-schemas) - - [ReDoS attack](./docs/security.md#redos-attack) - - [Content Security Policy](./docs/security.md#content-security-policy) -- [Some packages using Ajv](#some-packages-using-ajv) -- [Changes history](#changes-history) -- [Support, Code of conduct, Contacts, License](#open-source-software-support) - -## Mozilla MOSS grant and OpenJS Foundation - -::: v-pre -[](https://www.mozilla.org/en-US/moss/) [](https://openjsf.org/blog/2020/08/14/ajv-joins-openjs-foundation-as-an-incubation-project/) -::: - -Ajv has been awarded a grant from Mozilla’s [Open Source Support (MOSS) program](https://www.mozilla.org/en-US/moss/) in the “Foundational Technology” track! It will sponsor the development of Ajv support of [JSON Schema version 2019-09](https://tools.ietf.org/html/draft-handrews-json-schema-02) and of [JSON Type Definition (RFC8927)](https://datatracker.ietf.org/doc/rfc8927/). - -Ajv also joined [OpenJS Foundation](https://openjsf.org/) – having this support will help ensure the longevity and stability of Ajv for all its users. - -This [blog post](https://www.poberezkin.com/posts/2020-08-14-ajv-json-validator-mozilla-open-source-grant-openjs-foundation.html) has more details. - -I am looking for the long term maintainers of Ajv – working with [ReadySet](https://www.thereadyset.co/), also sponsored by Mozilla, to establish clear guidelines for the role of a "maintainer" and the contribution standards, and to encourage a wider, more inclusive, contribution from the community. - -## Please [sponsor Ajv development](https://github.com/sponsors/epoberezkin) - -Since I asked to support Ajv development 40 people and 6 organizations contributed via GitHub and OpenCollective - this support helped receiving the MOSS grant! - -Your continuing support is very important - the funds will be used to develop and maintain Ajv once the next major version is released. - -Please sponsor Ajv via: - -- [GitHub sponsors page](https://github.com/sponsors/epoberezkin) (GitHub will match it) -- [Ajv Open Collective️](https://opencollective.com/ajv) - -Thank you. - -#### Open Collective sponsors - - - - - - - - - - - - - - -## Performance - -Ajv generates code to turn JSON Schemas into super-fast validation functions that are efficient for v8 optimization. - -Currently Ajv is the fastest and the most standard compliant validator according to these benchmarks: - -- [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark) - 50% faster than the second place -- [jsck benchmark](https://github.com/pandastrike/jsck#benchmarks) - 20-190% faster -- [z-schema benchmark](https://rawgit.com/zaggino/z-schema/master/benchmark/results.html) -- [themis benchmark](https://cdn.rawgit.com/playlyfe/themis/master/benchmark/results.html) - -Performance of different validators by [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark): - -[![performance](https://chart.googleapis.com/chart?chxt=x,y&cht=bhs&chco=76A4FB&chls=2.0&chbh=32,4,1&chs=600x416&chxl=-1:|djv|ajv|json-schema-validator-generator|jsen|is-my-json-valid|themis|z-schema|jsck|skeemas|json-schema-library|tv4&chd=t:100,98,72.1,66.8,50.1,15.1,6.1,3.8,1.2,0.7,0.2)](https://github.com/ebdrup/json-schema-benchmark/blob/master/README.md#performance) - -## Features - -- Ajv implements JSON Schema [draft-06/07/2019-09](http://json-schema.org/) standards (draft-04 is supported in v6): - - all validation keywords (see [JSON Schema validation keywords](./docs/json-schema.md)) - - keyword "nullable" from [Open API 3 specification](https://swagger.io/docs/specification/data-models/data-types/). - - full support of remote references (remote schemas have to be added with `addSchema` or compiled to be available) - - support of circular references between schemas - - correct string lengths for strings with unicode pairs - - [formats](#formats) defined by JSON Schema draft-07 standard (with [ajv-formats](https://github.com/ajv-validator/ajv-formats) plugin) and additional formats (can be turned off) - - [validates schemas against meta-schema](./docs/api.md#api-validateschema) -- NEW: supports [JSON Type Definition](https://datatracker.ietf.org/doc/rfc8927/): - - all forms (see [JSON Type Definition schema forms](./docs/json-type-definition.md)) - - meta-schema for JTD schemas - - "union" keyword and user-defined keywords (can be used inside "metadata" member of the schema) -- supports [browsers](#using-in-browser) and Node.js 0.10-14.x -- [asynchronous loading](./docs/validation.md#asynchronous-schema-compilation) of referenced schemas during compilation -- "All errors" validation mode with [option allErrors](./docs/api.md#options) -- [error messages with parameters](./docs/api.md#validation-errors) describing error reasons to allow error message generation -- i18n error messages support with [ajv-i18n](https://github.com/ajv-validator/ajv-i18n) package -- [removing-additional-properties](./docs/validation.md#removing-additional-properties) -- [assigning defaults](./docs/validation.md#assigning-defaults) to missing properties and items -- [coercing data](./docs/validation.md#coercing-data-types) to the types specified in `type` keywords -- [user-defined keywords](#user-defined-keywords) -- draft-06/07 keywords `const`, `contains`, `propertyNames` and `if/then/else` -- draft-06 boolean schemas (`true`/`false` as a schema to always pass/fail). -- additional extension keywords with [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) package -- [\$data reference](./docs/validation.md#data-reference) to use values from the validated data as values for the schema keywords -- [asynchronous validation](./docs/api.md#asynchronous-validation) of user-defined formats and keywords - -## Install - -To install version 7: - -``` -npm install ajv -``` - -## Getting started - -Try it in the Node.js REPL: https://runkit.com/npm/ajv - -In JavaScript: - -```javascript -// or ESM/TypeScript import -import Ajv from "ajv" -// Node.js require: -const Ajv = require("ajv").default - -const ajv = new Ajv() // options can be passed, e.g. {allErrors: true} -const validate = ajv.compile(schema) -const valid = validate(data) -if (!valid) console.log(validate.errors) -``` - -In TypeScript: - -```typescript -import Ajv, {JSONSchemaType, DefinedError} from "ajv" - -const ajv = new Ajv() - -type MyData = {foo: number} - -// Optional schema type annotation for schema to match MyData type. -// To use JSONSchemaType set `strictNullChecks: true` in tsconfig `compilerOptions`. -const schema: JSONSchemaType = { - type: "object", - properties: { - foo: {type: "number", minimum: 0}, - }, - required: ["foo"], - additionalProperties: false, -} - -// validate is a type guard for MyData - type is inferred from schema type -const validate = ajv.compile(schema) - -// or, if you did not use type annotation for the schema, -// type parameter can be used to make it type guard: -// const validate = ajv.compile (schema) - -const data: any = {foo: 1} - -if (validate(data)) { - // data is MyData here - console.log(data.foo) -} else { - // The type cast is needed to allow user-defined keywords and errors - // You can extend this type to include your error types as needed. - for (const err of validate.errors as DefinedError[]) { - switch (err.keyword) { - case "minimum": - // err type is narrowed here to have "minimum" error params properties - console.log(err.params.limit) - break - // ... - } - } -} -``` - -With JSON Type Definition schema: - -In JavaScript: - -```javascript -const Ajv = require("ajv").default - -const ajv = new Ajv() // options can be passed, e.g. {allErrors: true} -const schema = { - properties: { - foo: {type: "float64"}, - }, -} -const validate = ajv.compile(schema) -const valid = validate({foo: 1}) // true -if (!valid) console.log(validate.errors) -// Unlike JSON Schema: -const valid1 = validate(1) // false, bot an object -const valid2 = validate({}) // false, foo is required -const valid3 = validate({foo: 1, bar: 2}) // false, bar is additional -``` - -In TypeScript: - -```typescript -import {JTDSchemaType} from "ajv" - -type MyData = {foo: number} - -// Optional schema type annotation for schema to match MyData type. -// To use JTDSchemaType set `strictNullChecks: true` in tsconfig `compilerOptions`. -const schema: JTDSchemaType = { - properties: { - foo: {type: "float64"}, - }, -} -``` - -See [this test](./spec/types/json-schema.spec.ts) for an advanced example, [API reference](./docs/api.md) and [Options](./docs/api.md#options) for more details. - -Ajv compiles schemas to functions and caches them in all cases (using schema itself as a key for Map) or another function passed via options), so that the next time the same schema is used (not necessarily the same object instance) it won't be compiled again. - -The best performance is achieved when using compiled functions returned by `compile` or `getSchema` methods (there is no additional function call). - -**Please note**: every time a validation function or `ajv.validate` are called `errors` property is overwritten. You need to copy `errors` array reference to another variable if you want to use it later (e.g., in the callback). See [Validation errors](./docs/api.md#validation-errors) - -## Using in browser - -See [Content Security Policy](./docs/security.md#content-security-policy) to decide the best approach how to use Ajv in the browser. - -Whether you use Ajv or compiled schemas, it is recommended that you bundle them together with your code. - -If you need to use Ajv in several bundles you can create a separate UMD bundles using `npm run bundle` script. - -Then you need to load Ajv with support of JSON Schema draft-07 in the browser: - -```html - - -``` - -To load the bundle that supports JSON Schema draft-2019-09: - -```html - - -``` - -To load the bundle that supports JSON Type Definition: - -```html - - -``` - -This bundle can be used with different module systems; it creates global `ajv` (or `ajv2019`) if no module system is found. - -The browser bundle is available on [cdnjs](https://cdnjs.com/libraries/ajv). - -**Please note**: some frameworks, e.g. Dojo, may redefine global require in a way that is not compatible with CommonJS module format. In this case Ajv bundle has to be loaded before the framework and then you can use global `ajv` (see issue [#234](https://github.com/ajv-validator/ajv/issues/234)). - -## Choosing schema language - -Both JSON Schema and JSON Type Definition are cross-platform specifications with implementations in multiple programming languages that help you define the shape and requirements to your JSON data. - -This section compares their pros/cons to help decide which specification fits your application better. - -### JSON Schema - -- Pros - - Wide specification adoption. - - Used as part of OpenAPI specification. - - Support of complex validation scenarios: - - untagged unions and boolean logic - - conditional schemas and dependencies - - restrictions on the number ranges and the size of strings, arrays and objects - - semantic validation with formats, patterns and content keywords - - distribute strict record definitions across multiple schemas (with unevaluatedProperties) - - Can be effectively used for validation of any JavaScript objects and configuration files. -- Cons - - Defines the collection of restrictions on the data, rather than the shape of the data. - - No standard support for tagged unions. - - Complex and error prone for the new users (Ajv has [strict mode](./docs/strict-mode) to compensate for it, but it is not cross-platform). - - Some parts of specification are difficult to implement, creating the risk of implementations divergence: - - reference resolution model - - unevaluatedProperties/unevaluatedItems - - dynamic recursive references - - Internet draft status (rather than RFC) - -See [JSON Schema](./docs/json-schema.md) for the list of defined keywords. - -### JSON Type Definition - -- Pros: - - Aligned with type systems of many languages - can be used to generate type definitions and efficient parsers and serializers to/from these types. - - Very simple, enforcing the best practices for cross-platform JSON API modelling. - - Simple to implement, ensuring consistency across implementations. - - Defines the shape of JSON data via strictly defined schema forms (rather than the collection of restrictions). - - Effective support for tagged unions. - - Designed to protect against user mistakes. - - Approved as [RFC8927](https://datatracker.ietf.org/doc/rfc8927/) -- Cons: - - Limited, compared with JSON Schema - no support for untagged unions\*, conditionals, references between different schema files\*\*, etc. - - No meta-schema in the specification\*. - - Brand new - limited industry adoption (as of January 2021). - -\* Ajv defines meta-schema for JTD schemas and non-standard keyword "union" that can be used inside "metadata" object. - -\*\* You can still combine schemas from multiple files in the application code. - -See [JSON Type Definition](./docs/json-type-definition.md) for the list of defined schema forms. - -## Using in ES5 environment - -You need to: - -- recompile Typescript to ES5 target - it is set to 2018 in the bundled compiled code. -- generate ES5 validation code: - -```javascript -const ajv = new Ajv({code: {es5: true}}) -``` - -See [Advanced options](https://github.com/ajv-validator/ajv/blob/master/docs/api.md#advanced-options). - -## Command line interface - -CLI is available as a separate npm package [ajv-cli](https://github.com/ajv-validator/ajv-cli). It supports: - -- compiling JSON Schemas to test their validity -- generating [standalone validation code](./docs/standalone.md) that exports validation function(s) to be used without Ajv -- migrating schemas to draft-07 and draft-2019-09 (using [json-schema-migrate](https://github.com/epoberezkin/json-schema-migrate)) -- validating data file(s) against JSON Schema -- testing expected validity of data against JSON Schema -- referenced schemas -- user-defined meta-schemas, validation keywords and formats -- files in JSON, JSON5, YAML, and JavaScript format -- all Ajv options -- reporting changes in data after validation in [JSON-patch](https://datatracker.ietf.org/doc/rfc6902/) format - -## Extending Ajv - -### User defined keywords - -See section in [data validation](./docs/validation.md#user-defined-keywords) and the [detailed guide](./docs/keywords.md). - -### Plugins - -Ajv can be extended with plugins that add keywords, formats or functions to process generated code. When such plugin is published as npm package it is recommended that it follows these conventions: - -- it exports a function that accepts ajv instance as the first parameter - it allows using plugins with [ajv-cli](#command-line-interface). -- this function returns the same instance to allow chaining. -- this function can accept an optional configuration as the second parameter. - -You can import `Plugin` interface from ajv if you use Typescript. - -If you have published a useful plugin please submit a PR to add it to the next section. - -### Related packages - -- [ajv-bsontype](https://github.com/BoLaMN/ajv-bsontype) - plugin to validate mongodb's bsonType formats -- [ajv-cli](https://github.com/jessedc/ajv-cli) - command line interface -- [ajv-formats](https://github.com/ajv-validator/ajv-formats) - formats defined in JSON Schema specification -- [ajv-errors](https://github.com/ajv-validator/ajv-errors) - plugin for defining error messages in the schema -- [ajv-i18n](https://github.com/ajv-validator/ajv-i18n) - internationalised error messages -- [ajv-istanbul](https://github.com/ajv-validator/ajv-istanbul) - plugin to instrument generated validation code to measure test coverage of your schemas -- [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) - plugin with additional validation keywords (select, typeof, etc.) -- [ajv-merge-patch](https://github.com/ajv-validator/ajv-merge-patch) - plugin with keywords $merge and $patch -- [ajv-formats-draft2019](https://github.com/luzlab/ajv-formats-draft2019) - format validators for draft2019 that aren't included in [ajv-formats](https://github.com/ajv-validator/ajv-formats) (ie. `idn-hostname`, `idn-email`, `iri`, `iri-reference` and `duration`) - -## Some packages using Ajv - -- [webpack](https://github.com/webpack/webpack) - a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser -- [jsonscript-js](https://github.com/JSONScript/jsonscript-js) - the interpreter for [JSONScript](http://www.jsonscript.org) - scripted processing of existing endpoints and services -- [osprey-method-handler](https://github.com/mulesoft-labs/osprey-method-handler) - Express middleware for validating requests and responses based on a RAML method object, used in [osprey](https://github.com/mulesoft/osprey) - validating API proxy generated from a RAML definition -- [har-validator](https://github.com/ahmadnassri/har-validator) - HTTP Archive (HAR) validator -- [jsoneditor](https://github.com/josdejong/jsoneditor) - a web-based tool to view, edit, format, and validate JSON http://jsoneditoronline.org -- [JSON Schema Lint](https://github.com/nickcmaynard/jsonschemalint) - a web tool to validate JSON/YAML document against a single JSON Schema http://jsonschemalint.com -- [objection](https://github.com/vincit/objection.js) - SQL-friendly ORM for Node.js -- [table](https://github.com/gajus/table) - formats data into a string table -- [ripple-lib](https://github.com/ripple/ripple-lib) - a JavaScript API for interacting with [Ripple](https://ripple.com) in Node.js and the browser -- [restbase](https://github.com/wikimedia/restbase) - distributed storage with REST API & dispatcher for backend services built to provide a low-latency & high-throughput API for Wikipedia / Wikimedia content -- [hippie-swagger](https://github.com/CacheControl/hippie-swagger) - [Hippie](https://github.com/vesln/hippie) wrapper that provides end to end API testing with swagger validation -- [react-form-controlled](https://github.com/seeden/react-form-controlled) - React controlled form components with validation -- [rabbitmq-schema](https://github.com/tjmehta/rabbitmq-schema) - a schema definition module for RabbitMQ graphs and messages -- [@query/schema](https://www.npmjs.com/package/@query/schema) - stream filtering with a URI-safe query syntax parsing to JSON Schema -- [chai-ajv-json-schema](https://github.com/peon374/chai-ajv-json-schema) - chai plugin to us JSON Schema with expect in mocha tests -- [grunt-jsonschema-ajv](https://github.com/SignpostMarv/grunt-jsonschema-ajv) - Grunt plugin for validating files against JSON Schema -- [extract-text-webpack-plugin](https://github.com/webpack-contrib/extract-text-webpack-plugin) - extract text from bundle into a file -- [electron-builder](https://github.com/electron-userland/electron-builder) - a solution to package and build a ready for distribution Electron app -- [addons-linter](https://github.com/mozilla/addons-linter) - Mozilla Add-ons Linter -- [gh-pages-generator](https://github.com/epoberezkin/gh-pages-generator) - multi-page site generator converting markdown files to GitHub pages -- [ESLint](https://github.com/eslint/eslint) - the pluggable linting utility for JavaScript and JSX -- [Spectral](https://github.com/stoplightio/spectral) - the customizable linting utility for JSON/YAML, OpenAPI, AsyncAPI, and JSON Schema - -## Changes history - -See https://github.com/ajv-validator/ajv/releases - -**Please note**: [Changes in version 7.0.0](https://github.com/ajv-validator/ajv/releases/tag/v7.0.0) - -[Version 6.0.0](https://github.com/ajv-validator/ajv/releases/tag/v6.0.0). - -## Code of conduct - -Please review and follow the [Code of conduct](./CODE_OF_CONDUCT.md). - -Please report any unacceptable behaviour to ajv.validator@gmail.com - it will be reviewed by the project team. - -## Security contact - -To report a security vulnerability, please use the -[Tidelift security contact](https://tidelift.com/security). -Tidelift will coordinate the fix and disclosure. Please do NOT report security vulnerabilities via GitHub issues. - -## Open-source software support - -Ajv is a part of [Tidelift subscription](https://tidelift.com/subscription/pkg/npm-ajv?utm_source=npm-ajv&utm_medium=referral&utm_campaign=readme) - it provides a centralised support to open-source software users, in addition to the support provided by software maintainers. - -## License - -[MIT](./LICENSE) +Ajv is free to use and open-source that many developers contributed to. Join us! diff --git a/docs/api.md b/docs/api.md index cd94f85ad..a6cf8da99 100644 --- a/docs/api.md +++ b/docs/api.md @@ -169,6 +169,7 @@ Ajv return it instance for method chaining from all methods with the prefix `add ```javascript const validate = new Ajv().addSchema(schema).addFormat(name, regex).getSchema(uri) ``` + ::: #### ajv.addMetaSchema(schema: object | object[], key?: string): Ajv diff --git a/docs/guide/environments.md b/docs/guide/environments.md new file mode 100644 index 000000000..550fed108 --- /dev/null +++ b/docs/guide/environments.md @@ -0,0 +1,120 @@ +# Execution environments + +[[toc]] + +## Server-side Node.js + +The main consideration for using Ajv server-side is to [manage compiled schemas](./managing-schemas) correctly, ensuring that the same schema is not compiled more than once. + +## Short-lived environments + +Depending on the life-time of the environments, the benefits from "compile once - validate many times" model can be limited - you can consider using [standalone validation code](../standalone). + +If you have a pre-defined set of schemas, you can: + +1. compile all schemas in the build step - you can either write your own script or use [ajv-cli](https://github.com/ajv-validator/ajv). +2. generate and beautify standalone validation code - you can have all your schemas exported from one file. +3. additionally, you can inline all dependencies on Ajv or ajv-formats using any bundling tools. +4. deploy compiled schemas as part of your application or library (with or without dependency on Ajv, depending on whether you did step 3 and which validation keywords are used in the schemas) + +Please see [gajus/table](https://github.com/gajus/table) package that pre-compiles schemas in this way. + +Even if your schemas need to be stored in the database, you can still compile schemas once and store your validation functions alongside schemas in the database as well, loading them on demand. + +## Browsers + +See [Content Security Policy](../security.md#content-security-policy) to decide how best to use Ajv in the browser for your use case. + +Whether you compile schemas in the browser or use [standalone validation code](../standalone), it is recommended that you bundle them together with your application code. + +If you need to use Ajv in several application bundles you can create a separate UMD bundles of Ajv using `npm run bundle` script. + +In this case you need to load Ajv using the correct bundle, depending on which schema language and which version you need to use: + + + + +This bundle can be used with different module systems; it creates global `ajv`/`ajv2019`/`ajvJTD` if no module system is found. + +The browser bundles are available on [cdnjs](https://cdnjs.com/libraries/ajv). + +::: warning Please note +Some frameworks, e.g. Dojo, may redefine global require in a way that is not compatible with CommonJS module format. In this case Ajv bundle has to be loaded before the framework and then you can use global `ajv` (see issue [#234](https://github.com/ajv-validator/ajv/issues/234)). +::: + +## ES5 environments + +You need to: + +- recompile Typescript to ES5 target - it is set to 2018 in the bundled compiled code. +- generate ES5 validation code: + +```javascript +const ajv = new Ajv({code: {es5: true}}) +``` + +See [Advanced options](https://github.com/ajv-validator/ajv/blob/master/docs/api.md#advanced-options). + +## Other JavaScript environments + +Ajv is used in other JavaScript environments, including Electron apps, WeChat mini-apps and many others, where the same considerations apply as above: + +- compilation performance +- restrictive content security policy +- bundle size + +If any of this is important, you may have better results with pre-compiled [standalone validation code](../standalone). + +## Command line interface + +Ajv can be used from the terminal in any operating system supported by Node.js + +CLI is available as a separate npm package [ajv-cli](https://github.com/ajv-validator/ajv-cli). + +It supports: + +- compiling JSON Schemas to test their validity +- generating [standalone validation code](./docs/standalone.md) that exports validation function(s) +- migrating schemas to draft-07 and draft-2019-09 (using [json-schema-migrate](https://github.com/epoberezkin/json-schema-migrate)) +- validating data file(s) against JSON Schema +- testing expected validity of data against JSON Schema +- referenced schemas +- user-defined meta-schemas, validation keywords and formats +- files in JSON, JSON5, YAML, and JavaScript format +- all Ajv options +- reporting changes in data after validation in [JSON-patch](https://datatracker.ietf.org/doc/rfc6902/) format diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md new file mode 100644 index 000000000..fcb0b7ccd --- /dev/null +++ b/docs/guide/getting-started.md @@ -0,0 +1,164 @@ +# Getting started + +[[toc]] + +## Install + +::: tip Node REPL +You can try Ajv without installing it in the Node.js REPL: [https://runkit.com/npm/ajv](https://runkit.com/npm/ajv) +::: + +To install Ajv version 7: + +``` +npm install ajv +``` + +If you need to use Ajv with [JSON Schema draft-04](../json-schema#draft-04), you need to install version 6: + +``` +npm install ajv@6 +``` + +See [Contributing](../CONTRIBUTING.md) on how to run the tests locally + +## Basic data validation + +Ajv takes a schema for your JSON data and converts it into a very efficient JavaScript code +that validates your data according to the schema. To create schema you can use either +[JSON Schema](../json-schema) or [JSON Type Definition](../json-type-definition) - check out [Choosing schema language](./schema-language), they have +different advantages and disadvantages. + +For example, to validate an object that has a required property "foo" (an integer number), an optional property "bar" (a string) and no other properties: + ++```html + + +``` + + ++```html + + +``` + + ++```html + + +``` + ++ + +Ajv compiles schemas to functions and caches them in all cases (using schema itself as a key for Map), so that the next time the same schema object is used it won't be compiled again. + +::: tip Please note +The best performance is achieved when using compiled functions returned by `compile` or `getSchema` methods. + +While execution of the compiled validation function is very fast, its compilation is +relatively slow, so you need to make sure that you compile schemas only once and +re-use compiled validation functions. See [Managing multiple schemas](./managing-schemas). +::: + +::: warning Please note +Every time a validation function (or `ajv.validate`) is called `errors` property is overwritten. You need to copy `errors` array reference to another variable if you want to use it later (e.g., in the callback). See [Validation errors](../api.md#validation-errors) +::: + +## Parsing and serializing JSON+```javascript +const Ajv = require("ajv").default +const ajv = new Ajv() // options can be passed, e.g. {allErrors: true} + +const schema = { + type: "object", + properties: { + foo: {type: "integer"}, + bar: {type: "string"} + }, + required: ["foo"], + additionalProperties: false +} + +const validate = ajv.compile(schema) + +const validData = { + foo: 1, + bar: "abc" +} + +const valid = validate(data) +if (!valid) console.log(validate.errors) +``` + + ++```javascript +const Ajv = require("ajv/dist/jtd").default +const ajv = new Ajv() // options can be passed, e.g. {allErrors: true} + +const schema = { + properties: { + foo: {type: "int32"} + }, + optionalProperties: { + bar: {type: "string"} + } +} + + +const validate = ajv.compile(schema) + +const validData = { + foo: 1, + bar: "abc" +} + +const valid = validate(data) +if (!valid) console.log(validate.errors) +`````` + ++ +Ajv can compile efficient parsers and serializers from [JSON Type Definition](../json-type-definition) schemas. + +Serializing the data with a function specialized to your data shape can be more than 10x compared with `JSON.stringify`. + +Parsing the data replaces the need for a separate validation after generic parsing with `JSON.parse` (although validation itself is usually much faster than parsing). In case your JSON string is valid specialized parsing is as approximately fast as JSON.parse, but in case your JSON is invalid, specialized parsing would fail much faster - so it can be very efficient in some scenarios. + +For the same data structure, you can compile parser and serializer in this way: + + + + +::: tip Please note +You would have smaller performance benefits in case your schema contains some properties or other parts that are empty schemas (`{}`) - parser would call `JSON.parse` in this case. +::: + +::: warning Please note +Compiled parsers, unlike JSON.parse, do not throw the exception in case JSON string is not a valid JSON or in case data is invalid according to the schema. As soon as the parser determines that either JSON or data is invalid, it returns `undefined` and reports error and position via parsers properties `message` and `position`. +::: \ No newline at end of file diff --git a/docs/guide/managing-schemas.md b/docs/guide/managing-schemas.md new file mode 100644 index 000000000..16197ae31 --- /dev/null +++ b/docs/guide/managing-schemas.md @@ -0,0 +1,60 @@ +# Managing schemas + +[[toc]] + +## Re-using validation functions + +Ajv validation model is optimized for server side execution, when schema compilation happens only once and validation happens multiple times - this has a substantial performance benefit comparing with validators that interpret the schema in the process of validation. + +Transition from template-based code generation in Ajv v6 to the tree-based in v7 brought: +- type-level safety against code injection via untrusted schemas +- more efficient validation code (via [tree optimizations](../codegen.md#code-optimization)) +- smaller memory footprint of compiled functions (schemas are no longer serialized) +- smaller bundle size +- more maintainable code + +These improvements cost slower schema compilation, and increased chance of re-compilation in case you pass a different schema object (see [#1413](https://github.com/ajv-validator/ajv/issues/1413)), so it is very important to [manage schemas correctly](./managing-schemas), so they are only compiled once, or use standalone validation code. + +There are several approaches to manage compiled schemas. + +## Standalone validation code + +## Compiling during initialization + +## Using Ajv instance cache + +### Cache key: schema vs $id + +### Pre-adding all schemas + +### Adding schemas on-demand + +### Asynchronous schema loading + +TODO motivation + +During asynchronous compilation remote references are loaded using supplied function. See `compileAsync` [method](./api.md#api-compileAsync) and `loadSchema` [option](./api.md#options). + +Example: + +```javascript +const ajv = new Ajv({loadSchema: loadSchema}) + +ajv.compileAsync(schema).then(function (validate) { + const valid = validate(data) + // ... +}) + +function loadSchema(uri) { + return request.json(uri).then(function (res) { + if (res.statusCode >= 400) throw new Error("Loading error: " + res.statusCode) + return res.body + }) +} +``` + +::: warning Please note +[Option](./api.md#options) `missingRefs` should NOT be set to `"ignore"` or `"fail"` for asynchronous compilation to work. +::: + +## Caching in your applications diff --git a/docs/guide/modular-schemas.md b/docs/guide/modular-schemas.md new file mode 100644 index 000000000..d6b0e3c97 --- /dev/null +++ b/docs/guide/modular-schemas.md @@ -0,0 +1,233 @@ +# Modular schemas + +## Combining schemas with $ref + +You can structure your validation logic across multiple schema files and have schemas reference each other using `$ref` keyword. + +Example: + +```javascript +const schema = { + $id: "http://example.com/schemas/schema.json", + type: "object", + properties: { + foo: {$ref: "defs.json#/definitions/int"}, + bar: {$ref: "defs.json#/definitions/str"}, + }, +} + +const defsSchema = { + $id: "http://example.com/schemas/defs.json", + definitions: { + int: {type: "integer"}, + str: {type: "string"}, + }, +} +``` + +Now to compile your schema you can either pass all schemas to Ajv instance: + +```javascript +const ajv = new Ajv({schemas: [schema, defsSchema]}) +const validate = ajv.getSchema("http://example.com/schemas/schema.json") +``` + +or use `addSchema` method: + +```javascript +const ajv = new Ajv() +const validate = ajv.addSchema(defsSchema).compile(schema) +``` + +See [Options](./api.md#options) and [addSchema](./api.md#add-schema) method. + +::: tip Please note + +- `$ref` is resolved as the uri-reference using schema \$id as the base URI (see the example). +- References can be recursive (and mutually recursive) to implement the schemas for different data structures (such as linked lists, trees, graphs, etc.). +- You don't have to host your schema files at the URIs that you use as schema \$id. These URIs are only used to identify the schemas, and according to JSON Schema specification validators should not expect to be able to download the schemas from these URIs. +- The actual location of the schema file in the file system is not used. +- You can pass the identifier of the schema as the second parameter of `addSchema` method or as a property name in `schemas` option. This identifier can be used instead of (or in addition to) schema \$id. +- You cannot have the same \$id (or the schema identifier) used for more than one schema - the exception will be thrown. +- You can implement dynamic resolution of the referenced schemas using `compileAsync` method. In this way you can store schemas in any system (files, web, database, etc.) and reference them without explicitly adding to Ajv instance. See [Asynchronous schema compilation](./validation.md#asynchronous-schema-compilation). + ::: + +## Extending recursive schemas + +While statically defined `$ref` keyword allows to split schemas to multiple files, it is difficult to extend recursive schemas - the recursive reference(s) in the original schema points to the original schema, and not to the extended one. So in JSON Schema draft-07 the only available solution to extend the recursive schema was to redefine all sections of the original schema that have recursion. + +It was particularly repetitive when extending meta-schema, as it has many recursive references, but even in a schema with a single recursive reference extending it was very verbose. + +JSON Schema draft-2019-09 and the upcoming draft defined the mechanism for dynamic recursion using keywords `$recursiveRef`/`$recursiveAnchor` (draft-2019-09) or `$dynamicRef`/`$dynamicAnchor` (the next JSON Schema draft) that is somewhat similar to "open recursion" in functional programming. + +Consider this recursive schema with static recursion: + +```javascript +const treeSchema = { + $id: "https://example.com/tree", + type: "object", + required: ["data"], + properties: { + data: true, + children: { + type: "array", + items: {$ref: "#"}, + }, + }, +} +``` + +The only way to extend this schema to prohibit additional properties is by adding `additionalProperties` keyword right in the schema - this approach can be impossible if you do not control the source of the original schema. Ajv also provided the additional keywords in [ajv-merge-patch](https://github.com/ajv-validator/ajv-merge-patch) package to extend schemas by treating them as plain JSON data. While this approach may work for you, it is non-standard and therefore not portable. + +The new keywords for dynamic recursive references allow extending this schema without modifying it: + +```javascript +const treeSchema = { + $id: "https://example.com/tree", + $recursiveAnchor: true, + type: "object", + required: ["data"], + properties: { + data: true, + children: { + type: "array", + items: {$recursiveRef: "#"}, + }, + }, +} + +const strictTreeSchema = { + $id: "https://example.com/strict-tree", + $recursiveAnchor: true, + $ref: "tree", + unevaluatedProperties: false, +} + +import Ajv2019 from "ajv/dist/2019" +// const Ajv2019 = require("ajv/dist/2019").default +const ajv = new Ajv2019({ + schemas: [treeSchema, strictTreeSchema], +}) +const validate = ajv.getSchema("https://example.com/strict-tree") +``` + +See [dynamic-refs](../spec/dynamic-ref.spec.ts) test for the example using `$dynamicAnchor`/`$dynamicRef`. + +At the moment Ajv implements the spec for dynamic recursive references with these limitations: + +- `$recursiveAnchor`/`$dynamicAnchor` can only be used in the schema root. +- `$recursiveRef`/`$dynamicRef` can only be hash fragments, without URI. + +Ajv also does not support dynamic references in [asynchronous schemas](#asynchronous-validation) (Ajv extension) - it is assumed that the referenced schema is synchronous, and there is no validation-time check for it. + +## $data reference + +With `$data` option you can use values from the validated data as the values for the schema keywords. See [proposal](https://github.com/json-schema-org/json-schema-spec/issues/51) for more information about how it works. + +`$data` reference is supported in the keywords: const, enum, format, maximum/minimum, exclusiveMaximum / exclusiveMinimum, maxLength / minLength, maxItems / minItems, maxProperties / minProperties, formatMaximum / formatMinimum, formatExclusiveMaximum / formatExclusiveMinimum, multipleOf, pattern, required, uniqueItems. + +The value of "$data" should be a [JSON-pointer](https://datatracker.ietf.org/doc/rfc6901/) to the data (the root is always the top level data object, even if the $data reference is inside a referenced subschema) or a [relative JSON-pointer](http://tools.ietf.org/html/draft-luff-relative-json-pointer-00) (it is relative to the current point in data; if the \$data reference is inside a referenced subschema it cannot point to the data outside of the root level for this subschema). + +Examples. + +This schema requires that the value in property `smaller` is less or equal than the value in the property larger: + +```javascript +const ajv = new Ajv({$data: true}) + +const schema = { + properties: { + smaller: { + type: "number", + maximum: {$data: "1/larger"}, + }, + larger: {type: "number"}, + }, +} + +const validData = { + smaller: 5, + larger: 7, +} + +ajv.validate(schema, validData) // true +``` + +This schema requires that the properties have the same format as their field names: + +```javascript +const schema = { + additionalProperties: { + type: "string", + format: {$data: "0#"}, + }, +} + +const validData = { + "date-time": "1963-06-19T08:30:06.283185Z", + email: "joe.bloggs@example.com", +} +``` + +`$data` reference is resolved safely - it won't throw even if some property is undefined. If `$data` resolves to `undefined` the validation succeeds (with the exclusion of `const` keyword). If `$data` resolves to incorrect type (e.g. not "number" for maximum keyword) the validation fails. + +## $merge and $patch keywords + +With the package [ajv-merge-patch](https://github.com/ajv-validator/ajv-merge-patch) you can use the keywords `$merge` and `$patch` that allow extending JSON Schemas with patches using formats [JSON Merge Patch (RFC 7396)](https://datatracker.ietf.org/doc/rfc7396/) and [JSON Patch (RFC 6902)](https://datatracker.ietf.org/doc/rfc6902/). + +To add keywords `$merge` and `$patch` to Ajv instance use this code: + +```javascript +require("ajv-merge-patch")(ajv) +``` + +Examples. + +Using `$merge`: + +```javascript +{ + $merge: { + source: { + type: "object", + properties: {p: {type: "string"}}, + additionalProperties: false + }, + with: { + properties: {q: {type: "number"}} + } + } +} +``` + +Using `$patch`: + +```javascript +{ + $patch: { + source: { + type: "object", + properties: {p: {type: "string"}}, + additionalProperties: false + }, + with: [{op: "add", path: "/properties/q", value: {type: "number"}}] + } +} +``` + +The schemas above are equivalent to this schema: + +```javascript +{ + type: "object", + properties: { + p: {type: "string"}, + q: {type: "number"} + }, + additionalProperties: false +} +``` + +The properties `source` and `with` in the keywords `$merge` and `$patch` can use absolute or relative `$ref` to point to other schemas previously added to the Ajv instance or to the fragments of the current schema. + +See the package [ajv-merge-patch](https://github.com/ajv-validator/ajv-merge-patch) for more information. diff --git a/docs/guide/schema-language.md b/docs/guide/schema-language.md new file mode 100644 index 000000000..94d69c602 --- /dev/null +++ b/docs/guide/schema-language.md @@ -0,0 +1,54 @@ +# Choosing schema language + +[[toc]] + +Both [JSON Schema](../json-schema.md) and [JSON Type Definition](../json-type-definition.md) are cross-platform specifications with implementations in multiple programming languages that define the shape of your JSON data. + +You can see the difference between the two specifications in [Getting started](./getting-started) section examples. + +This section compares their pros/cons to help decide which specification fits your application better. + +## JSON Schema + +- Pros + - Wide specification adoption. + - Used as part of OpenAPI specification. + - Support of complex validation scenarios: + - untagged unions and boolean logic + - conditional schemas and dependencies + - restrictions on the number ranges and the size of strings, arrays and objects + - semantic validation with formats, patterns and content keywords + - distribute strict record definitions across multiple schemas (with unevaluatedProperties) + - Can be effectively used for validation of any JavaScript objects and configuration files. +- Cons + - Defines the collection of restrictions on the data, rather than the shape of the data. + - No standard support for tagged unions. + - Complex and error prone for the new users (Ajv has [strict mode](../strict-mode) enabled by default to compensate for it, but it is not cross-platform). + - Some parts of specification are difficult to implement, creating the risk of implementations divergence: + - reference resolution model + - unevaluatedProperties/unevaluatedItems + - dynamic recursive references + - Internet draft status (rather than RFC) + +See [JSON Schema](../json-schema.md) for more information and the list of defined keywords. + +## JSON Type Definition + +- Pros: + - Aligned with type systems of many languages - can be used to generate type definitions and efficient parsers and serializers to/from these types. + - Very simple, enforcing the best practices for cross-platform JSON API modelling. + - Simple to implement, ensuring consistency across implementations. + - Defines the shape of JSON data via strictly defined schema forms (rather than the collection of restrictions). + - Effective support for tagged unions. + - Designed to protect against user mistakes. + - Approved as [RFC8927](https://datatracker.ietf.org/doc/rfc8927/) +- Cons: + - Limited, compared with JSON Schema - no support for untagged unions\*, conditionals, references between different schema files\*\*, etc. + - No meta-schema in the specification\*. + - Brand new - limited industry adoption (as of January 2021). + +\* Ajv defines meta-schema for JTD schemas and non-standard keyword "union" that can be used inside "metadata" object. + +\*\* You can still combine schemas from multiple files in the application code. + +See [JSON Type Definition](../json-type-definition.md) for more information and the list of defined schema forms. diff --git a/docs/guide/typescript.md b/docs/guide/typescript.md new file mode 100644 index 000000000..a7a162dbd --- /dev/null +++ b/docs/guide/typescript.md @@ -0,0 +1,205 @@ +# Using with TypeScript + +[[toc]] + +## Additional functionality + +Ajv takes advantage of TypeScript type system to provide additional functionality that is not possible in JavaScript: + +- utility types `JSONSchemaType` and `JTDSchemaType` to convert data type into the schema type to simplify writing schemas, both for [JSON Schema](../json-schema.md) (but without union support) and for [JSON Type Definition](../json-type-definition) (with tagged unions support). +- compiled validation functions are type guards that narrow the type after successful validation. +- validation errors for JSON Schema are defined as tagged unions, for type-safe error handling. +- when utility type is used, compiled JTD serializers only accept data of correct type (as they do not validate that the data is valid) and compiled parsers return correct data type. + +## Utility types for schemas + +For the same example as in [Getting started](./getting-started): + ++```javascript +const Ajv = require("ajv/dist/jtd").default +const ajv = new Ajv() // options can be passed, e.g. {allErrors: true} + +const schema = { + properties: { + foo: {type: "int32"} + }, + optionalProperties: { + bar: {type: "string"} + } +} + +const serialize = ajv.compileSerializer(schema) +console.log(serialize(data)) + +const parse = ajv.compileParser(schema) + +const data = { + foo: 1, + bar: "abc" +} + +const json = '{"foo": 1, "bar": "abc"}' +const invalidJson = '{"unknown": "abc"}' + +console.log(parseAndLog(json)) // logs {foo: 1, bar: "abc"} +console.log(parseAndLog(invalidJson)) // logs error and position + +function parseAndLog(json) { + const data = parse(json) + if (data === undefined) { + console.log(parse.message) // error message from the last parse call + console.log(parse.position) // error position in string + } else { + console.log(data) + } +} +``` + ++ + +See [this test](https://github.com/ajv-validator/ajv/tree/master/spec/types/json-schema.spec.ts) for an advanced example. + +## Type-safe error handling + +With [JSON Schema](../json-schema), the validation error type is an open union, but it can be cast to a tagged union (using validation keyword as tag) for easier error handling. + +This is not useful with [JSON Type Definition](../json-type-definition), as it defines errors for schema forms, not for keywords. + +Continuing the example above: + ++```typescript +import Ajv, {JSONSchemaType} from "ajv" +const ajv = new Ajv() // options can be passed, e.g. {allErrors: true} + +interface MyData { + foo: number + bar?: string +} + +const schema: JSONSchemaType + += { + type: "object", + properties: { + foo: {type: "integer"}, + bar: {type: "string"} + }, + required: ["foo"], + additionalProperties: false +} + +// validate is a type guard for MyData - type is inferred from schema type +const validate = ajv.compile(schema) + +// or, if you did not use type annotation for the schema, +// type parameter can be used to make it type guard: +// const validate = ajv.compile (schema) + +const validData = { + foo: 1, + bar: "abc" +} + +if (validate(data)) { + // data is MyData here + console.log(data.foo) +} else { + console.log(validate.errors) +} +``` + +```typescript +import Ajv, {JTDSchemaType} from "ajv/dist/jtd" +const ajv = new Ajv() // options can be passed, e.g. {allErrors: true} + +interface MyData { + foo: number + bar?: string +} + +const schema: JTDSchemaType += { + properties: { + foo: {type: "int32"} + }, + optionalProperties: { + bar: {type: "string"} + } +} + + +// validate is a type guard for MyData - type is inferred from schema type +const validate = ajv.compile(schema) + +// or, if you did not use type annotation for the schema, +// type parameter can be used to make it type guard: +// const validate = ajv.compile (schema) + +const validData = { + foo: 1, + bar: "abc" +} + +if (validate(data)) { + // data is MyData here + console.log(data.foo) +} else { + console.log(validate.errors) +} +`````` + + + +## Type-safe parsers and serializers + +With typescript, your compiled parsers and serializers can be type-safe, either taking their type from schema type or from type parameter passed to compilation functions. + +This example uses the same data and schema types as above: + ++```typescript +import {DefinedError} from "ajv" + +// ... + +if (validate(data)) { + // data is MyData here + console.log(data.foo) +} else { + // The type cast is needed, as Ajv uses a wider type to allow extension + // You can extend this type to include your error types as needed. + for (const err of validate.errors as DefinedError[]) { + switch (err.keyword) { + case "type": + // err type is narrowed here to have "type" error params properties + console.log(err.params.type) + break + // ... + } + } +} +``` + ++ diff --git a/docs/json-schema.md b/docs/json-schema.md index c8689f04d..db3037223 100644 --- a/docs/json-schema.md +++ b/docs/json-schema.md @@ -409,9 +409,10 @@ The value of this keyword should be a map where keys should be regular expressio When the value in data object property matches multiple regular expressions it should be valid according to all the schemas for all matched regular expressions. ::: warning Please note + 1. `patternProperties` keyword does not require that properties matching patterns are present in the object (see examples). 2. By default, Ajv does not allow schemas where patterns in `patternProperties` match any property name in `properties` keyword - that leads to unexpected validation results. It can be allowed with option `allowMatchingProperties`. See [Strict mode](./strict-mode.md) -::: + ::: **Example** diff --git a/docs/validation.md b/docs/validation.md index f66bbf8ca..ce568b7cf 100644 --- a/docs/validation.md +++ b/docs/validation.md @@ -138,239 +138,6 @@ JSON Schema draft-07 also defines formats `iri`, `iri-reference`, `idn-hostname` You can add and replace any formats using [addFormat](./api.md#api-addformat) method. -## Modular schemas - -### Combining schemas with $ref - -You can structure your validation logic across multiple schema files and have schemas reference each other using `$ref` keyword. - -Example: - -```javascript -const schema = { - $id: "http://example.com/schemas/schema.json", - type: "object", - properties: { - foo: {$ref: "defs.json#/definitions/int"}, - bar: {$ref: "defs.json#/definitions/str"}, - }, -} - -const defsSchema = { - $id: "http://example.com/schemas/defs.json", - definitions: { - int: {type: "integer"}, - str: {type: "string"}, - }, -} -``` - -Now to compile your schema you can either pass all schemas to Ajv instance: - -```javascript -const ajv = new Ajv({schemas: [schema, defsSchema]}) -const validate = ajv.getSchema("http://example.com/schemas/schema.json") -``` - -or use `addSchema` method: - -```javascript -const ajv = new Ajv() -const validate = ajv.addSchema(defsSchema).compile(schema) -``` - -See [Options](./api.md#options) and [addSchema](./api.md#add-schema) method. - -::: tip Please note -- `$ref` is resolved as the uri-reference using schema \$id as the base URI (see the example). -- References can be recursive (and mutually recursive) to implement the schemas for different data structures (such as linked lists, trees, graphs, etc.). -- You don't have to host your schema files at the URIs that you use as schema \$id. These URIs are only used to identify the schemas, and according to JSON Schema specification validators should not expect to be able to download the schemas from these URIs. -- The actual location of the schema file in the file system is not used. -- You can pass the identifier of the schema as the second parameter of `addSchema` method or as a property name in `schemas` option. This identifier can be used instead of (or in addition to) schema \$id. -- You cannot have the same \$id (or the schema identifier) used for more than one schema - the exception will be thrown. -- You can implement dynamic resolution of the referenced schemas using `compileAsync` method. In this way you can store schemas in any system (files, web, database, etc.) and reference them without explicitly adding to Ajv instance. See [Asynchronous schema compilation](./validation.md#asynchronous-schema-compilation). -::: - -### Extending recursive schemas - -While statically defined `$ref` keyword allows to split schemas to multiple files, it is difficult to extend recursive schemas - the recursive reference(s) in the original schema points to the original schema, and not to the extended one. So in JSON Schema draft-07 the only available solution to extend the recursive schema was to redefine all sections of the original schema that have recursion. - -It was particularly repetitive when extending meta-schema, as it has many recursive references, but even in a schema with a single recursive reference extending it was very verbose. - -JSON Schema draft-2019-09 and the upcoming draft defined the mechanism for dynamic recursion using keywords `$recursiveRef`/`$recursiveAnchor` (draft-2019-09) or `$dynamicRef`/`$dynamicAnchor` (the next JSON Schema draft) that is somewhat similar to "open recursion" in functional programming. - -Consider this recursive schema with static recursion: - -```javascript -const treeSchema = { - $id: "https://example.com/tree", - type: "object", - required: ["data"], - properties: { - data: true, - children: { - type: "array", - items: {$ref: "#"}, - }, - }, -} -``` - -The only way to extend this schema to prohibit additional properties is by adding `additionalProperties` keyword right in the schema - this approach can be impossible if you do not control the source of the original schema. Ajv also provided the additional keywords in [ajv-merge-patch](https://github.com/ajv-validator/ajv-merge-patch) package to extend schemas by treating them as plain JSON data. While this approach may work for you, it is non-standard and therefore not portable. - -The new keywords for dynamic recursive references allow extending this schema without modifying it: - -```javascript -const treeSchema = { - $id: "https://example.com/tree", - $recursiveAnchor: true, - type: "object", - required: ["data"], - properties: { - data: true, - children: { - type: "array", - items: {$recursiveRef: "#"}, - }, - }, -} - -const strictTreeSchema = { - $id: "https://example.com/strict-tree", - $recursiveAnchor: true, - $ref: "tree", - unevaluatedProperties: false, -} - -import Ajv2019 from "ajv/dist/2019" -// const Ajv2019 = require("ajv/dist/2019").default -const ajv = new Ajv2019({ - schemas: [treeSchema, strictTreeSchema], -}) -const validate = ajv.getSchema("https://example.com/strict-tree") -``` - -See [dynamic-refs](../spec/dynamic-ref.spec.ts) test for the example using `$dynamicAnchor`/`$dynamicRef`. - -At the moment Ajv implements the spec for dynamic recursive references with these limitations: - -- `$recursiveAnchor`/`$dynamicAnchor` can only be used in the schema root. -- `$recursiveRef`/`$dynamicRef` can only be hash fragments, without URI. - -Ajv also does not support dynamic references in [asynchronous schemas](#asynchronous-validation) (Ajv extension) - it is assumed that the referenced schema is synchronous, and there is no validation-time check for it. - -### $data reference - -With `$data` option you can use values from the validated data as the values for the schema keywords. See [proposal](https://github.com/json-schema-org/json-schema-spec/issues/51) for more information about how it works. - -`$data` reference is supported in the keywords: const, enum, format, maximum/minimum, exclusiveMaximum / exclusiveMinimum, maxLength / minLength, maxItems / minItems, maxProperties / minProperties, formatMaximum / formatMinimum, formatExclusiveMaximum / formatExclusiveMinimum, multipleOf, pattern, required, uniqueItems. - -The value of "$data" should be a [JSON-pointer](https://datatracker.ietf.org/doc/rfc6901/) to the data (the root is always the top level data object, even if the $data reference is inside a referenced subschema) or a [relative JSON-pointer](http://tools.ietf.org/html/draft-luff-relative-json-pointer-00) (it is relative to the current point in data; if the \$data reference is inside a referenced subschema it cannot point to the data outside of the root level for this subschema). - -Examples. - -This schema requires that the value in property `smaller` is less or equal than the value in the property larger: - -```javascript -const ajv = new Ajv({$data: true}) - -const schema = { - properties: { - smaller: { - type: "number", - maximum: {$data: "1/larger"}, - }, - larger: {type: "number"}, - }, -} - -const validData = { - smaller: 5, - larger: 7, -} - -ajv.validate(schema, validData) // true -``` - -This schema requires that the properties have the same format as their field names: - -```javascript -const schema = { - additionalProperties: { - type: "string", - format: {$data: "0#"}, - }, -} - -const validData = { - "date-time": "1963-06-19T08:30:06.283185Z", - email: "joe.bloggs@example.com", -} -``` - -`$data` reference is resolved safely - it won't throw even if some property is undefined. If `$data` resolves to `undefined` the validation succeeds (with the exclusion of `const` keyword). If `$data` resolves to incorrect type (e.g. not "number" for maximum keyword) the validation fails. - -### $merge and $patch keywords - -With the package [ajv-merge-patch](https://github.com/ajv-validator/ajv-merge-patch) you can use the keywords `$merge` and `$patch` that allow extending JSON Schemas with patches using formats [JSON Merge Patch (RFC 7396)](https://datatracker.ietf.org/doc/rfc7396/) and [JSON Patch (RFC 6902)](https://datatracker.ietf.org/doc/rfc6902/). - -To add keywords `$merge` and `$patch` to Ajv instance use this code: - -```javascript -require("ajv-merge-patch")(ajv) -``` - -Examples. - -Using `$merge`: - -```javascript -{ - $merge: { - source: { - type: "object", - properties: {p: {type: "string"}}, - additionalProperties: false - }, - with: { - properties: {q: {type: "number"}} - } - } -} -``` - -Using `$patch`: - -```javascript -{ - $patch: { - source: { - type: "object", - properties: {p: {type: "string"}}, - additionalProperties: false - }, - with: [{op: "add", path: "/properties/q", value: {type: "number"}}] - } -} -``` - -The schemas above are equivalent to this schema: - -```javascript -{ - type: "object", - properties: { - p: {type: "string"}, - q: {type: "number"} - }, - additionalProperties: false -} -``` - -The properties `source` and `with` in the keywords `$merge` and `$patch` can use absolute or relative `$ref` to point to other schemas previously added to the Ajv instance or to the fragments of the current schema. - -See the package [ajv-merge-patch](https://github.com/ajv-validator/ajv-merge-patch) for more information. - ## User-defined keywords The advantages of defining keywords are: @@ -420,32 +187,6 @@ Several keywords (typeof, instanceof, range and propertyNames) are defined in [a See [User-defined keywords](./keywords.md) for more details. -## Asynchronous schema compilation - -During asynchronous compilation remote references are loaded using supplied function. See `compileAsync` [method](./api.md#api-compileAsync) and `loadSchema` [option](./api.md#options). - -Example: - -```javascript -const ajv = new Ajv({loadSchema: loadSchema}) - -ajv.compileAsync(schema).then(function (validate) { - const valid = validate(data) - // ... -}) - -function loadSchema(uri) { - return request.json(uri).then(function (res) { - if (res.statusCode >= 400) throw new Error("Loading error: " + res.statusCode) - return res.body - }) -} -``` - -::: warning Please note -[Option](./api.md#options) `missingRefs` should NOT be set to `"ignore"` or `"fail"` for asynchronous compilation to work. -::: - ## Asynchronous validation Example in Node.js REPL: https://runkit.com/esp/ajv-asynchronous-validation diff --git a/scripts/publish-site b/scripts/publish-site new file mode 100755 index 000000000..51d9a2736 --- /dev/null +++ b/scripts/publish-site @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -ex + +echo "About to publish $GITHUB_REF to gh-pages..." + +cp CODE_OF_CONDUCT.md docs +cp CONTRIBUTING.md docs +cp LICENSE docs From a638a47624d05b8c56d1038867b85595e79ec7ab Mon Sep 17 00:00:00 2001 From: Erik Brinkman+```typescript +import Ajv, {JTDSchemaType} from "ajv/dist/jtd" +const ajv = new Ajv() // options can be passed, e.g. {allErrors: true} + +interface MyData { + foo: number + bar?: string +} + +const schema: JTDSchemaType += { + properties: { + foo: {type: "int32"} + }, + optionalProperties: { + bar: {type: "string"} + } +} + +// serialize will only accept data compatible with MyData +const serialize = ajv.compileSerializer(schema) + +// parse will return MyData or undefined +const parse = ajv.compileParser(schema) + +// types of parse and serialize are inferred from schema, +// they can also be defined explicitly: +// const parse = ajv.compileParser (schema) + +const data = { + foo: 1, + bar: "abc" +} + +const invalidData = { + unknown: "abc" +} + +console.log(serialize(data)) +console.log(serialize(invalidData)) // type error + +const json = '{"foo": 1, "bar": "abc"}' +const invalidJson = '{"unknown": "abc"}' + +console.log(parseAndLogFoo(json)) // logs property +console.log(parseAndLogFoo(invalidJson)) // logs error and position + +function parseAndLogFoo(json: string): void { + const data = parse(json) // MyData | undefined + if (data === undefined) { + console.log(parse.message) // error message from the last parse call + console.log(parse.position) // error position in string + } else { + // data is MyData here + console.log(data.foo) + } +} +``` + Date: Sun, 21 Feb 2021 19:23:38 -0500 Subject: [PATCH 06/25] fix null bug --- lib/types/jtd-schema.ts | 18 ++++++++---------- spec/types/jtd-schema.spec.ts | 30 ++++++++++++++---------------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/lib/types/jtd-schema.ts b/lib/types/jtd-schema.ts index aecf5b3b0..14dd5a83a 100644 --- a/lib/types/jtd-schema.ts +++ b/lib/types/jtd-schema.ts @@ -46,14 +46,20 @@ type StringType = "string" | "timestamp" /** actual schema */ export type JTDSchemaType = Record > = ( | // refs - where null wasn't specified, must match exactly - ({[K in keyof D]: [T] extends [D[K]] ? {ref: K} : never}[keyof D] & {nullable?: false}) + (null extends EnumString + ? never + : {[K in keyof D]: [T] extends [D[K]] ? {ref: K} : never}[keyof D] & {nullable?: false}) // nulled refs - if ref is nullable and nullable is specified, then it can // match either null or non-null definitions - | (null extends T + | (null extends EnumString + ? never + : null extends T ? { [K in keyof D]: [Exclude ] extends [Exclude ] ? {ref: K} : never }[keyof D] & {nullable: true} : never) + // empty - empty schemas also treat nullable differently in that it's now optional + | (unknown extends T ? {nullable?: true} : never) // all other types | (( | // numbers - only accepts the type number @@ -129,14 +135,6 @@ export type JTDSchemaType = Record ] : never) - // empty schema - // NOTE there should only be one type that extends Record so unions - // shouldn't be a worry - | (T extends Record ? unknown : never) - // null - // NOTE we have to check this too because null as an exclusive type also - // qualifies for the empty schema - | (true extends TypeEquality ? unknown : never) ) & (null extends T ? { diff --git a/spec/types/jtd-schema.spec.ts b/spec/types/jtd-schema.spec.ts index a6f3c3dc3..daa82bba5 100644 --- a/spec/types/jtd-schema.spec.ts +++ b/spec/types/jtd-schema.spec.ts @@ -52,8 +52,10 @@ describe("JTDSchemaType", () => { // @ts-expect-error const nums: JTDSchemaType<1 | 2 | 3> = {type: "int32"} const numNull: JTDSchemaType = {type: "int32", nullable: true} + // @ts-expect-error + const numNotNull: JTDSchemaType = {type: "float32"} - void [numf, numi, numl, nums, numNull] + void [numf, numi, numl, nums, numNull, numNotNull] }) it("should typecheck boolean schemas", () => { @@ -258,14 +260,18 @@ describe("JTDSchemaType", () => { }) it("should typecheck empty schemas", () => { - const empty: JTDSchemaType