From 136306aaf4441b8a4556500be85d228afa1bbc93 Mon Sep 17 00:00:00 2001 From: David Koontz Date: Thu, 12 Feb 2026 23:18:02 -0700 Subject: [PATCH] Adds WebSocket support to Node Adds the WebSocket module to the Node platform allowing Gren programs to create WebSocket servers and handle client connections. --- gren.json | 4 +- integration-tests/websocket/.gitignore | 6 + integration-tests/websocket/Makefile | 15 + integration-tests/websocket/gren.json | 17 + .../gren_lang_core__7_0_0.pkg.gz | Bin 0 -> 103200 bytes .../gren_packages/gren_lang_url__6_0_0.pkg.gz | Bin 0 -> 9240 bytes integration-tests/websocket/package-lock.json | 875 ++++++++++++++++++ integration-tests/websocket/package.json | 9 + integration-tests/websocket/src/Main.gren | 150 +++ integration-tests/websocket/test/fixtures.mjs | 31 + integration-tests/websocket/test/requests.mjs | 269 ++++++ src/Gren/Kernel/WebSocketServer.js | 206 +++++ src/WebSocketServer.gren | 296 ++++++ src/WebSocketServer/Connection.gren | 153 +++ 14 files changed, 2030 insertions(+), 1 deletion(-) create mode 100644 integration-tests/websocket/.gitignore create mode 100644 integration-tests/websocket/Makefile create mode 100644 integration-tests/websocket/gren.json create mode 100644 integration-tests/websocket/gren_packages/gren_lang_core__7_0_0.pkg.gz create mode 100644 integration-tests/websocket/gren_packages/gren_lang_url__6_0_0.pkg.gz create mode 100644 integration-tests/websocket/package-lock.json create mode 100644 integration-tests/websocket/package.json create mode 100644 integration-tests/websocket/src/Main.gren create mode 100644 integration-tests/websocket/test/fixtures.mjs create mode 100644 integration-tests/websocket/test/requests.mjs create mode 100644 src/Gren/Kernel/WebSocketServer.js create mode 100644 src/WebSocketServer.gren create mode 100644 src/WebSocketServer/Connection.gren diff --git a/gren.json b/gren.json index 27d9ff9..71fd1fb 100644 --- a/gren.json +++ b/gren.json @@ -15,7 +15,9 @@ "FileSystem.Path", "HttpClient", "HttpServer", - "HttpServer.Response" + "HttpServer.Response", + "WebSocketServer", + "WebSocketServer.Connection" ], "gren-version": "0.6.0 <= v < 0.7.0", "dependencies": { diff --git a/integration-tests/websocket/.gitignore b/integration-tests/websocket/.gitignore new file mode 100644 index 0000000..066f3f8 --- /dev/null +++ b/integration-tests/websocket/.gitignore @@ -0,0 +1,6 @@ +.gren/ +app +node_modules/ +/test-results/ +/playwright-report/ +/playwright/.cache/ diff --git a/integration-tests/websocket/Makefile b/integration-tests/websocket/Makefile new file mode 100644 index 0000000..2d45a2b --- /dev/null +++ b/integration-tests/websocket/Makefile @@ -0,0 +1,15 @@ +app: Makefile gren.json src/Main.gren + gren make --optimize Main --output=app + +.PHONY: test +test: app node_modules + npm test + +node_modules: package.json package-lock.json + npm ci + +.PHONY: clean +clean: + rm -rf .gren + rm -rf node_modules + rm -f app diff --git a/integration-tests/websocket/gren.json b/integration-tests/websocket/gren.json new file mode 100644 index 0000000..f2edaf1 --- /dev/null +++ b/integration-tests/websocket/gren.json @@ -0,0 +1,17 @@ +{ + "type": "application", + "platform": "node", + "source-directories": [ + "src" + ], + "gren-version": "0.6.3", + "dependencies": { + "direct": { + "gren-lang/core": "7.0.0", + "gren-lang/node": "local:../.." + }, + "indirect": { + "gren-lang/url": "6.0.0" + } + } +} diff --git a/integration-tests/websocket/gren_packages/gren_lang_core__7_0_0.pkg.gz b/integration-tests/websocket/gren_packages/gren_lang_core__7_0_0.pkg.gz new file mode 100644 index 0000000000000000000000000000000000000000..e3bb45b0c6c1e8df3bb88feb64e7c077e0602b6b GIT binary patch literal 103200 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

A6Y>viU&w26_0>DHs?P*&$->^a<3dQjg#c#Q zsJ>*Gn80D=3=#m!67~Qy4uV`{CbeH4SWKT)uCq!H2|!MZy<8Ne6>Ngvy~_?#xTXud zJm9;fz;5Xc)-1^dnfyUeE~~12Y3Oh6ETO#-DMTgcda}8FQL21FTveD~mx>6)U~xl2 zd-pEe73bJd^4&}NV;(#@V|f;(u#ZnlCxbmm=Vr}ubRkA1por3d$2=KQEMAGM2Xx|7 zb|;8zl|!)^uM^8}y1|lP>^U&`)mc2T{l%-nJX$;t*BHkAR1#4TUf*fi`c~?uEN5R1b4`_EbSKm!s1^EsD#ya-e{sf# z>~T9JIOXZhX`i}Buv!6(h=(NaSxGv}z;Bnt&Feb=Y$o|!Qf#^k{T?LKzGQTl^5$U1M>r#VJ05Lcwu2ODB~Wq(zZVHOFL%-f4OU&A=}td(K91@=F7OQMF`%|sSOWlQr4#WsMU6PxtQ(y_(yQ%42_o=g}gT!2DREN9VU>Ge6psMkD z9^Q=Hgk60G%J#os99fVo`1_gg-;PLKRT&A={11R=qzuhs;mWJ%g*tWlPI(87$5*rn zu4&_2!eHC+K?}Re2&^!!tG(6BDBlKU*@v1TFO?MGuJ*F8hft%Px~1B$9zCh~iaLHwLOvqj5cr z+?j70$w{exc5_`E1jN)u5tgc0qZeGzv8&8kP3EZZyix0!3C~Ss+^=P-Vk8&Pq*viQ zlCQ5Sa8i4;T0z%-8$yU_wk`8lw(M`2m6e837KX)D-7zI@cnDQ?(kRqeT6nH~ZD_Dy zOO&byM-|NnRWjPpNWTdKz0Sx|%xqwhFSf?7Zi#<3R`_BI94NN5*?ldOyQ#Uoq~mL` z1e5_*Yb=}XRpY!Q8dn|Z5dR!&fjG5d0vg(C@s_E+oYiWFN!7KGSuIp};1_5n+l6oA zm{~|>@k`5vA0J4~Rpr|c+uQ*g)ZYn%PZs|YdiZPkq2st^XO;yY^3P}S%fKBR0k3_0 z5c1jo<;CQ>%(NW9_9yTtaGgpm>*|Ux*lDRV7@TFw0$pO(GM}|F`KWs^pONeZ6-h{s zEK(yCdqj(Mk0eU1VE(O7{?hscycMtUFs^lSDfPm!C`Kuti6LYJXZk)p6}RNwlt+hq zOFo2_Jd0x;%V{s;TaaMQ%96)$nS+I=lTQy{w#vtTsBF6&*QR3OTvFu@027y^PM(_5 zRY7mkw-ds`iV4HRi8*^iy?opH2YqQ%wivUK3fa&hb zy*89wBt47Z>LE!{o#Z7-@rW}y@MRX8wE`}*w`fQ`VU+$^N z5uqwpbXg}BxNf%%AgAk;`T&E8ah74~_#G?O+so2IUyL`aYn-0R0OK3h?|aE$o67Y~IrSB9DvtZSO7Ov6M6(vGh!2ft`>Ng(>uf zP6X0sK&lR>>B~r1WsV2lSgyJSu@85T1GFw_EGs99>AU_Xj%xa|3LY;J@e=Ztl+cb$UgVLx^?%`W$QJ8~9a#HWX zDeRI_=^i?2U^z%=QT`%LN6zTSab-F#=iGAc>`2$fIXSqO3)UCbZnlkSIbN)VwU>>@ zT_nq}*Leb2C}GWwsn>E^E5THyGiI4N-AV^lK~7o+Uo@xaMG0_`5|FEqmgHRmfFhy& zC~PGW=k=%khy%JBlRuWnU%PauqBeLW*7)1B-W6pnzkHtZyqrGK2(DE9SxT zd6ZjEt)S(ZJe}WGnWqW_T`+5nvr&YZ1f@{uoM2ZPUhi3v&5g3RUw7CT4w(htF{k0c zNOW%JzO54U!d8I-C2qFfvTIBb(fiU&=*F=Uc<0-88(o zDHpYxhM9HOr8ecPrep#<4VX?Yez3$BR}h;A2b;fH_nSisA&|mUdwfdPLx<&bYm9B# z8mm@Y4_LQVk>}^0l}OBfzNS)yVaoG2kVJWn>6sQmy1NcE)t}0DB_-q@G(8oUR9;o= z&gS{aOL@9nzixx=;f`~UF5%TwN39vtY$Q}rRD|n|T$4psy)I#Nsg6#nrpY9m2odT` zzg<8o{J)A=evtsdjxZ-{UdSt{V-=`BL|+SP#*(+?w{)(YgAhnBL_guf;e0xec>$sJ zW0lz1IrT2vD(entKIc3V><~1_n)|p_mlfxDoXmKoS#+ z7a2bTrvxRaRD&fvC?N4`6yt<2pJDoLIPgFKRcdLSMj5j&vL3zlv@|D|0L=a7o1Cn` z{~0JfSgOi`(g7z%#hZbCLoiA|shhx}!$QoECHMvDj^E%Ng2hMpElMIt(M*YFgY!pT zh}mHQuFMR>zkZeT2nRv!sIHaf5tJ(_=IM+MsbiHZ>oR#E(oGPor>%vb9)S*BuYjd#8(0NiiQH-ieYsOHg4Bcu z6{Au5w6#nbh=Hk!SIkm%nMtS;&Ra^$I4Ig$=7Z5cuH8lLX^iY8HdN9|R12)ZS?bjg zF*MCE)3u!h8{1pyt*n0658rX~x`4|PjjLujoz-OL-Trrny*#SU&`W8ArK&@Z&v&cv z{~6ENG2(Kp?m{!}=9VPvdmxd#djlVJsqB+}^5KaGMUp%EFEHr&{gwfiNBtUH#h3ZSh@083ls@DBDNr#{>-_dz<2?5-5qrH2T02zCvf zYLbwQ641AV#(#AcvXsuJAZ`b69Cw=%#`_>uj{*oo9(-}H0_h?>P?^Q6q_Rj45yZ%^ zQ$Zjsyaw$(sB5oCH%FtZG%KstwZ40st#`ZK8U#q!dBlcq9dLQLEr!OQqY0dG2+qU} zwz;mLLBTiCTZ9`Lh`fpH?odw?TkHxAh$)|8k+9TD0=&91SSl=4C@FhSb83kS=*^0u7-3+?atKkS#a(@V zP2jiE;g}cAEDb2xYKJThc(XhkWvv>r<)BSVVzFA$;e09+QD%F z!;9`Pg*#?i^g0MN6Xd*TL{OL>u=uEgtU9lohvf`Yfn8mTK|w*O8k8b=6TN++Hz>!{ zL`#-w>xyB^=RG?<{u%|Y2MXr+_-hoqo^Im7n-`LnDJbZ=La^>-+!{6_ssK}1%M4Uv=G46V+)iM zB8s!m0RFU>Tn$Y5?7nZOna+3Ea7hBZ(LL>$jsh6@^=%it@~!rpdvCAb{rjcvLrYhn z0^rnC#T??Uf<-Rgfh}}@se^0W?yzyv8nkQY zFF*&POm7q+fDe~}l1&PbG*L7pe7~{5j*rO&aJ>C*_7l6udgV7SkJwM_?i%Z{wNh7x zF>Kk(&O8Now#Wz#xQhqxNlmjBMV{i_N4TQ*)E>ipH{Ijouc;txis6?h?ps|Qq(^=C z$~Yc4y+rh=jyo(0TU!!`e8z`S;ZYr232ql#_v9{G$MQ4i%0a<{L3EUwMjdubqi8^H zEhV73Bj4yAYK@>NW|h#yQ=S9sou-?yv2BV;VZbcV_bA!u?R(^drNvLNi- z+1zdU4V8JT+;D*gxpv{TY>Uv*Hvt~W#_zxd%aU0nbu|`MC5PlrL778|INy{MRP%k7 zP8ANSe2x08Hgptd>bjcS8}?pTRLC{FTjkBHRGcDt#F>@7h*VCl-dxnN$_5ApxM?r z88-NRQ8jacnl{)SHcOV4Ro0LAwXwl&#xJE~rh;?-WQ3%AXSCphDjibwelB#(~@(Mr6* zgcGiOd&&O%Q$D$?9zC(=Fb!^(gVzuLDukcz?Y-RI+&v89o1{f+fWbU=<6preYQPb! z^71mooo~JgvnMTU&_L}!{|wi@Xjc8JjSW-ZA4E}c9_7LtMbrqrYTQNBh6kWwCoAhW z?B@Tq0a#p>Q0Q7>Se>=FTlN3tuHfhj^Q3r5w@CjVk);p{grYx;|9gMEjAnikjSD!) zS2mp0E)4f^{^19?LsQXpgo(!jmjL?Xk4=I8pYc*)eYn0XT^1tu_t@}ZcRQgg!gD6S znXR&kkc+Ag-AdgKZqFSKPXt_8n^`pnQ3AZ_uzWro3NZ@TEN$$aWXtc{fzJ5tX#3F6 z61sIPbK2uH|BM&>*GQbVw5j`aJ|2S++%AbER;G!tHGm&3t?^M8{(q@A{=AkaqWC$@ zi|u3-@g#vO&r43O4jTv;?(wlE^1fiiypAc4Y_Nsb(e zi-N>0Yqj-KU=Ao^SRcLW(qDx1#53JAPpMLOfE;Zre)xD5sl>|WDYQ8fK3>o0Nvppy zFUEHtLoC{RHs%G7duTJU56T!WZxuSOC<(ete`U*+yN30o)cMDs+Sx=;bWu%S#o8R9) zQ3a{QojcVIOlM?BtKJ(KtE+5#cWdw2_JMC>k=~>sCr6+qP1AV+5pXs|EA{H}F%9lx zJ%ea~asX`JH-&I+>8L^9aa!uId(P0e-&s96+%TkHjl#O8!y!` zF3%daTyE;ogJqj~^q{V($4)0hRu?rqkUF9yCCY%cCt+8`!y&E&JP@9p)nRcojVMks zVRzXBoC`MSDSW@P!5(Ox>Tu13KFNl=6T?yC`j?o<9R-oNbBZn0l>i^R@RXxk6$hX; z0iSK78umy?)`D*{N?XDu-2(e#bKR>3%CxI?#3~ID@J8WnMg`=hOUG6WQ}y1PZLawK zCx-ds&zbP%EoT9QeTVy&q@K1rFOergx^^>e_tA@1fyW3gU^#o8mil%ru-jm-VhUgH&!c~FIn9w7^nnco&;>|s^c{w;Gx&L>T& zam%c6wAVsx#%9u!&(;agmI|*%FN|`pnDqg` zIWE#ISRM>dc;?ayo^%1#{WMC7&7yTLuog!U;36O(>8Rudkvq^QS&P=|T~!*vQWXi< z?`C#6E7H1-HgQzJE>jeT-Bq;oF?G?{p+&bYPgq!m4sw2&^ZnCdzV_?d@x#Mt0>{K~ zHtn#3obTtk`)pI>TSc~=3;|MD!+9pQ(xea7#_ooK#=yeq%o{${PQRo!S>OjaD3^WY4ul-plN@lZ0ZuME)_s^)H16}7}-98 zCpuWt+Th~szCw_kgjUZq=bQ=z)JDs-PTbwFCEw}8B*}yJvpn^N7U94AE zK~&k63WC8~pWM#q02vxYPDf6KeKaJ)0x&ni`8_>vPSepmo=eTs!z3*{o}^h+oJ_mU zOEcHba9F|kNkmfkmE5$xPr6l4MGTv zheg0V2mDnTXf@M_gEX(yFpy(`68~<30ZPx-G68kr+WKRy0}VnR9VHUB-C^s~weQ!j z0OR|08^-#>N6W)dv%xjEYT9bV)a?a>QUr(O?XeUF?`UhDoe6(B-S;L_VUH10&8F*q zX?ETB=GG=aN|WvW-G}RIO9FYgzII*CimK7Nx(bf4Q9AXxoZ-{guXmmyE=%m=;?(HW zw(Pd9dDC({v3l<}wC8Iowf*=w340<6>c_8?F(VWvsC=%tusyn*U2n5mhk;54E1C46 zTg(TCki~lw;|Xi*9d{q^rKRHOGspx#ed2+f*TVES2O9!Kk5L_>YmcsmX$@3zQ(Xzy z`|EYMs&Ig1ajk)BM;SB%D>M2o$>m{SP|7#U>JTuRoSx$9Uq!jE+g3#~<1HE}LjXPjIWaZ6iYYKp>MnI0BHHw`a>;ce zt4J$k+^CN)KlZzD@X~v?h_&|n1!itD=c{KezXGdTsG>2XVcFE9Px%S7t$jFlF^({WbP)|th0d=3 z4ib6Zjd?Oz9j2Lh$6iFm$$S7;HKwd}QWUeix4JsPhuvX1U3JU1`zsD1l=_!StJ6WUxE$>;4q;+-UPHXzLCrg>g9d;@|9M$zWE&V&cpPxF; zou(3)$)gT-&9ZN&DKtN<-IHNGI!)IXiSj~+hV3?XH|$N*AA&9(iVq0-Jt8aS0OaML zo-RMnK6(8GZJ! zu)9OH+ehk;F)(kwLt!1V%y=%v{j}XSKA0a%PL4N)Lo?Gbvcix;c43_g`NYbjgjz{8 zKpy7`B)=5r#In+01(aP`8nh$8pI8WjnqKIaqqbdzZA68Bmlo({{p@Py&w3tmb%#GG zmEYF5DUdIJ&s^m}v_1T5O2=PLpK^m>q!LaEO76LI=c`Toj2P z0$(}^!v`Xk*Nz5~UR)*K#~TtjAfT1Nc&Obr=&u|cZrqOR@dv*@UcE%3!$*9)?Q>*M-9JlSQs0*+2r;hoxwzMtBe7x2qej0ZY2 z;-8Yn^g`f+88=HJbkf0DX?MI{?PTHIXAT7vigLp$p)EEvI~ z)>oE`FW%1ji*vek1plg>{@c#_?CmVbU6y9Fu+Id~)1-qQLRj^4Eqa(KOIl=NBs~D5 zROE0cJ3CLaQt1-xIe9Q(}dY+4n&0=1FgV4IN zRu4Mc(h6_lm_vS&)$Un}`a~`E6M1&2*#^!RXn0l+^96>dOkm=V{@kPhu~VS)GL^#qZLl z+Fy^yHxea`3TTd&2Ph3Bqh;-U8HxwC@;=pk2cgw}PjL-|45v1b4-7{CoJDMPmHjHR zXk3ayKf|XL#@2mb_@LLkm}lY(u)C;dC9y1Zp{azzit1{%xt+gXa%hQZ?Gp+)k};+3@UxcrN->uf-Ge%d?xRckO&fQAi~p^N|Ni0O7bx`Fntc$qWU?sB)N^B^0Si_FH89|F_=&MB<0VdVPR!-9;flhN3J zIF8fF@$vq_-ZNhm3qFx~{ahhbR~OqV+5+`#`|0Z!3t>U%bQ+6poKD~+yTke`pi|i$ z1x(+=M%9Q~VH8$Z+0ov!J@1w(1!k1CFnN~P#ry}Em_(^}DXBqpDsqS!H$#Y^3^Sge zv>ta@8B&L-I>gU%E_;K>7SBZhnIM@P;`DtOCwa?u7&W99J3Knr*?kd4l}Jx=(F!2f zU-=<|;D?0A`Hv{n6@6BKP%1)@bji&mk0i(C_09V7nF`oe4W8tYOGTI)@i)W~CG$Ae z~20j`c z#ymd}xw=grZd+_)+ut_pU5?FH_Ksc?3QA!kD(c6#{grR)5JC|KjG&-dvC3T3+7U-(FNOJ zYftdY4^A8S%(;lrGzOPmx@xaAJ^RwZ?_yZ=SD@$p zm9P6NFhSeHpXdiO%ZBXA*SgCVOe_!<_lBd`R)eMP$)F6`#GxpT!5%nnsFBNp#bTmvs! zRWIIDz4#&g0%sEYd0u4QC>e>1y>Sa=vCV#vm*O%{^^)donJ)Z<#OX==?%*%bs%=L( zv4zG%!6z_zQCkb=a<#^5Fqvw-iRh7LHW5>ZD=p7ztfJ?zGW*KEg$AaA5@t|zIjhXV z3;aBYQ3w|TDKR=mJ#zeX9>s7&bO<+V=C%=ZYxYCujO*nba@=D|>TVuKL(#ftJui4R z5mt8Q1`}%7{CM-$AHJvJGBkkU)i@IIh;2X-byIkM<+#7Xe!}!!gyveCq4M&?nsnPj z?Tt-1N*mSohNrmeW;Ie0r{eOhQE1)}0a8h{^aGGiU;UN$G)N_& z-_s~*k7E!8=N?I^ox5_T*A@wDwdZQ zWlfrVc%3H8%DqXG_pj5WzdCHv;QDnM^wm#;2Jip#It_+~eUnD7B|Js?v$%K`O`@XZ zxyS4a7LoQN(Ms44Z0#QVi9LAC?y!XQ*u#hLrENKxwhlHAe-3gGTvyV&6pchuMB_*& zsv&nT_>M7@PtFGYm4)8b)j^yNR@b`sy7yL3qNK=Iks77}q)2x?gL9 zuz&G0emKmcS;2N#BE$&Y+IR^Si=$J~1C>kw(WCr|M@ctIqN4S_Z`y$tdTf*q=Tngs zaL>hdEa2<@jEPglV$!L&X zur$+;1U0~BSvtx1wEL|y^;&dVYe1*|iUP}Lvp5=ZWT6C|vmxGE$4+=MiUoUp@G?i* zAhH&Le2YQTBTflo_aqZz-_)S`$fzjCoq;S(i=Wt${KnETyG-X9?IPrtc_F4hSv%lf zcg^)yFfad}%eZ=T*CEK=lO`nD&(c9Gre=d|9P>%eCefKlfLn+Z2!$dP9Haozh)O0Z8!&5#HT|F0c(`=%i z=Kd5B^Frjs>QkOa!~7o`8|?ff8lJF`$cI@pfYS>nmn@Q_8nha}WITZ#MP49s15p$r zgMDa7$jr{8IA(F0o}zN8R%nkLaWXqLOAU%ToDI2F8_J6f3OS0j#ptP}UNDdoc z8Z0b4;pxgNW;q+pNwo?I8Csr*0(Vsb12F=(5>9tKjxT>=J7YN71shSge1?$=e%2E) zWqHbWNDPSRKwkLrRrM%*+>~s1ixcjBm4+eKqC`lW82M=OEVPO|7;N!u!*DjYtiw(9%)@sKDXrTfB;>U`>v+gr7goOmp@uw9X@*G@f9kv6G z<__CS#NN2Wp7SWiI5ecbBJ0u@j~|G99^0WPd#4@XKlq^bPVIn{@4l1EtW}%0fa zLJNPj`^o!$(l2y0RAoUT((0;?d)eAM*k;eRxAvZGA9!&h2t=LFEydw$xEE#Z z`0(}C*7o)@eHG|5pY+%?pXhs3KLlW7%-jON&o_5oE(djloZZi&X^XyQ9()CQ|6u18 z?BdB%Jtn^x$QZ(70o_UPazVctvriR><8XY+UpZT<)UBTsU(w3NJ+u{`u$|o_&{+hL zO7_?*UYvA`Y@Q5NQ!rrht5PgKvEyTsv*W$fmR%hHMEP@A)Cg7mn{U`xa^K>A*qU`X zx(*BgT8y(731tyY-r0T)I(m^GnvfMJ2hn9u_x4^kLHBu_@@w*aBh2@u(0%@LZ?h43 zf0dTXa`@WFP)$gaYz3KBw5HeM*Cvk&Jy}eiilCXAA)vx<%t#24SVn#YXdKirwDDiB zWHt{DHh-(vGC|n8^EeLCO#|M2{qiNib@#ZdG}m*v_vGHMLQ`G11Fja{*?wu1JJ##b zW|MyC_s{6)u-xDT>thtrtlI#io!w{K|En9vdOb$fn_?8C`!C`W$Im{Gvb=(~4kEv7 z|91TA=F8XH4cZN~I_0xhJh^O*b+7Zf)?RJy8#c_7VULadc5~fu?zU_=z?0DtL;`!% zS24?t7_hnf?D**C?cMrr!wUtrr-Pywa3TextJQ=Yd%N4md(RtF0AkL;)Y;j6!S?`YknS(8X{f_S50`5UYE(A(i1MZ{Xt2XZkCtx~ zYE?G;PiXjYlZHEN^v|m`4mB$gcnk=9-xPrk8$G@{5>VSlL=C@f7prWZ>?PWaaK@fe zCGI9)kzLx+7|JvAufx6F?u=)-XvyMFoF%Jn?Pn3s3}`*o!!9=-xnQkwY>YA}wlm|% zk?SnEIJB>>JU;$fdLeq~w~uo2`4-SxG(uFrq6Mw!`VK6tE~jPb?QR09MVWSzNW4^Wt>9alXI^bEiu~Hf z#Dv)OsuhlDqsdwaA=zf4hX-1H8WC`~d__PiVwy`*>Mg7gp3|U|0k5x`2iVFpNv#t7 zsu4#+&+(Y5s3ijgpEw!Z0&83yi2UcD9tYsbS7dvdNU27Ya6E-PkyE&G=@Ue2BTPO4 z%m1B|jjkNM8K5Y+gVA5J5ti)|J>Ou1R(PeaJiyD-BB0Jvn;tnLXX_iKX8?NZJ!l6y zX({b@@P&bMrzhcbIs4EK@D@#GhqsZm!A*zo$&-&LxEPa!X#A)G(kZ6w3KGFy?(M$V zK428~$94s#$phHFiZ_7O@|6O><1hIV99X z4DvV8+mh5S!Ffp|dKo^>u4zDDT8jKN=7mLZsuZ$)q+`IL_mVPSQflEoFVJcgP3fcA zy2I7>5IGo}uHb0UO5!>Kg9pp*se@%@q4Ca^H58lCMU~*(klYXfXeB`zO6Ih z-Nh6LLyS&5=SZS@g+>tDz%6E)J7bYht09#Yi^Nq^i)F2Rps0~{s*#F-%~4}-SYnBt zeG|Q98@f)BCWm;j){CF|N6&I(zIUFdwOKB{D&#(5d##S$lSpg1bED%6a51C4|i z@#RrcH9}l3#kPV?ehF;AD!E{U$kAa(IdA{__2$c+qu)aP_+!rFsJLuhbeOtm4ut0d zO}|Uu`vRw!{j)k3-LH|dmzEbnWe9*X#&&t-cen_79NdvxtAQ?P>=Kgny{rjB9*he! z5$MYDcQYr(g>p>j>MGSkjpw1p6nBkg%?*1Zws2hNC@3FAy-l4JYk{sb`+~2wh(FMYvX$S-LwKOT}LiWTcFBL__G4Kl1YLSMMrsXgZC2Y~6A6UNv zv!ZEKHfzsdQVnYTx?A?8iv+`(0^6!d@lu?@9YujT13l;DUxqwf0f%n~*W(>#L2AoA z^-5PTRIf>JB6qUy9YgCK#=Sjk4;W!n1CER!UNLb}<*KYLN|#nQ+n{E7l7r*>WFokg zaN$sH3(N1JGsLU#KD38B8tF~_s-yDcS&^{l2YM5s(KqNY?j28K|HB5(zL*LrJwHG1PNQL# z=IOY=$WoEqo#(5W$OX@aC#&bPyKwejB*p4{27%`J>iWI4N2~YNS4Yv=G#w>rA@0T) zjNhG&#x{=AbX3L;BaP>Ihh11IRJ#T)*#9tD!WIwth|c{Ir(iq9&cC#_ro(lluRvvF zQI&syBg23GsbBx#zW$Nj({s*G3ZpyfwR+Q&ZyaiF?d={OZSGpT``G^(sBR3Q+uwO& zzYyyAw)1$(FAn(_qW;7Lsj-UzT-{fj|9gD6`F#6$XZL9P#rA<)DN4eXc6Mtj@e37Y zX0?GPn@;3)u=8SXckk8q(ZO$Sw}xr1@E@kR12>Nn^(v1N=T*V=tAe|)xCN2>Ah`vT z`#`w`m6yO4JXt64(*@Qw&t5?wYI$5?up=C^*sy{h9k4?Y#k6%yBoI;TydTGDnyI(= z!+D*h^JJu6;}4gDe`JLw;U8Jyyo^)bY@AN~S8MmImuvURifCV6%5xQE6e#KiGH3Io z!$1#q7%AAw=w%0|EFD6^3Rg_wm4>&%8$aT#0GVKNV6hh_ZyP$VPzGUMk-N1ZU} z;32OLlR?ICWHhqU_R+;g1~g0=FF7ues{*S1IOA+{o3g+3w&o`-o=p%wc8sBzJHQ!Bg`4tmRqIV-Iyhrodh!?_pn9m1vya>*P z$#(|Tfca>@(^oK>mAM{mJG(naJDV?e{?9hsR3vie`KliJ{nNeEeX@E|#y>B_Y$h_9 z*8{FVGP90$H*K!Fo(mA=D(W6v6F%Sqb451E-}G1TglB){E$cDud@)`M7Tze+ZX9zU2?h@u@q|yvzbu(co48Q{ z+a7EKzj`iNdFTnQ4b>$o3mMmpctSq{s0rNf7|1+7#4X; z;YveXf)E#CI4{K3RCi0un=S6VyNioCo05)=3##$}a z@tNJ}wg+A1C)WKC$scnu7v-KPFvD~@<)pVP@ut??q%yjDrsu8UbYwDCE^c%(P!KyJM9#%~an zrACp_oyZi)xYbpLP#BM6mI#>7d3MPN39UsOr-t0zuEN+Ef)ao;_TA#R$aK;Q$~xWS zV?gu}u(M__rAz4VpMRRUOTJU9)&Llp;9QZXlyGfo&i5kXg^-AA|Cy|0E}_uW zS5FKRLP;#`p;Pt_Xbgs#uo}k+l%(#Sx>%mGbY7s0>E7uYy%IZ) zj}44onKe*19!tyCnp-vYs!>fMoGiy_*COB(Q%I7^E+il6LPRxFf_|I%7O7`m({F!H zjMEgtK6z0JN7V)rb5frx+grT@0G&`lX@A>pfP7*NNH?%{norQNX5QkE&L`ck(F1UN zyncMR{qlKuD|z#N%MxVbtM*V)@7#T6^F!rQCt`l{jij61$WLtZLCYF54tyefZhpaZ zry8}P^a#rm6xs4m4mfoH$%AJlFt1SwEdt$jWD?Yp%^kT;QoD`otq_xmyGnv!)Jx;m zo|z8zULVP1*6xg?hmh|NQe7{d%Um+YzSU35vQt#5O^^Rx?eCUuyztNmscMYBM@CwC zhPcsj4w;Z>tEHTE{In7`qkpP!GbW&-?bS%@S(R6&?dmFfy7}U;LWjqKk6S0{v@#F7 z8xrTvN}7rO_#x(ML)82oQNw9m3R*N4ZfcQtDz+5}C2JmYyUen5G#^4L6l4G-GZ7M_*7N^82Jf$~Bb+v2sjCck$`J)>6ioZ1VDD&I z_YS3a`DWMK8r_{SI~VMCpbjP#V52CXK^<@uK~Zn@LwPme1*TXU2xtrRffymYffz%w zop7DCF56AnBuz(Ov2C8kkx7M70rwPjJi zBJG5oaborR$M>3)p3UQU_4~(b3(HPtQ7keAeeIwBd}J-`q|D*b7QM-V*yUWseNYDC_Y=N=qch0L;fWZYqR_}7Y)Ya-nEa#> zo1{F(Ttz7xi2{~A{S{a*o(nddXPHQf_|g#wDMUZ@S4eOW5{Hxz=R&7+8}KN`wQ4LV zK}niSQmB1?0_n_PLRNXzF!Mv+)zsQe&mnJ#!4>mutudMTt;_xc2QtqE8wq-ixmTiu z$Erw2ijAs|!vV5Rz_uaf^Ewx}D9#Ib#fL?7Cc@q9l7_&vXKaYM^d_Q^fPbG)XUI3$ zsLBMSFM{DZJiUbDQDTy2mq2yc{Z}0ft#^3Y!QJO$k>fm`byd`y3h1<;pK1r>d3%ju!y4D>0WL!3yI z%J8Tg=57NM$do2|A-QdqFX8%SUsr%t($fmA0fIfg0De0ySf;)KrUXrKYK-wxnqBx) z@eF@R#+Bk777I$1G2o-HKv*!Imx|&ILfWxvAWnF3A~FRJ*koW?RVhHCU0YdR-y%gQ zT-))=fO&*jqI@@uzgiH#C2^4*ir<9E4* zVD4tBW`Nt{D2G)z+Bt4_$r@4}Qt^2MJ}F1&k?H7!U?W{N0Bn~mNhfY!`nl{eP^5&B zLI~|ng+xaJ@?xgT6!M%$BGd4wY^Pku4`laar^qgKItR9??i~DB7wYNEy$Ge<>@ z2U%2g8lc=2J1%ZLD-x#vYE(>o@Sz1eV0jIE3pe?!(QCz+K^cF z@`#GGE;O|@gW!TSQ0i3T3I2YfpNi?MOXypCx-f63TI<3Ka#o?BwpW4tvFJ$x?E5Md zqslRhLaF^xeHoy!u(;!&GVAFnxFl^_RNqZiF>n%bzU_`3NfM!FGWD%iKdG76CPGUY zJJM~lK`sEgSU~M1hft6e+9rU-ynrT`f&>-(Eft|%ipo#3zjts{wf;tvcVDJ6ZNbqj zD`L8mbx`iDHDTDhnfE(*PS){Hxxn;x@Whhi(6ySGYILIdf%bvc%zE>Y|jFxSM`C20d%LR9t4~Y z9$Hlnh`L}zKW48QQCpm(W7<0Zh@Nakg1Y@4q;}ULo!N=nI@sPk+E(rhqqWd00e(;E zbvwp5lh(cRzIjDk5&-;ZAPTlCpF%$RUNnW}oWp^w{JKU(SIxhwM$H_&y2>UQAAt8o z%y;rOe8~r$&P{UQ9^U+JdsX@0j{mV6Cy^gYhEs54~bn+{E1sY`R2Xw&mGaHK#i@U+n-EDO3BepT6AL zVw?LreiVVCTiIy}uV@vhUj=vSE#jAftgf!rnM zRd;Te4W{aFv1N#S9bM;{>;{d162NvH37|6+qn(5< zHggeE)cnaaC)`pJs+D6C2AXE=)wYrKO%l^6H*Ov)0~n+08{oC;@^Q)0vO8$`G|Cyj zSZ(TdNb_^%W19wN#(2BgFgksJ%p2e#YI3On0G#yj`5hp19ATTcc=%TQvH7i$ulkX0SK1 zAKl_aBo=a26PA&QC3V)Br>p3*tj6;4M9`;p!(Hn*$Lj5bdMdKa>nCkvBI9(&V7V}h zd87_$b#;j4K%fv&c{WN#o_q_5^Unkm$$Tm@K8S?}en!b^BHYR`Z$QRGmf6yUP)qq` zfD_>N$TH(RHy=9923IIqd2v94<=ENoIL)^C@Weg7R%(OE7E}nIkYByVKKOjWR27s0 zVLQwG95o9$i0R2~5dxGe*r;a{%V4TAmrNLVrj-f)wp=?hkAPReOPXX^a&9rr4Yk1W zLOsLnD(zB=2i5A-)-Et5?mgS?!a>>cS15Hny-Kd1Oh866Nb0c2f;d^{7(GY7%_gt+*`v ztCj+0r;L!o)viNClevMiH+ZOYm0YW*3TjKkFEH)O!7B0gU4+~J3w9KHaj?D1UhO@5 z{c_txw)_kl3y)ghW;>j{HNg~lO3~lvbQ2a_mZ zZ)2xs{E}z@SBykOj{EzHKX`Q+-U2qlnmV97v@?AH6qKif>4)CZ8m2SxrjO^n;PUp8 zV}2^KiP)w(`&l|_c{Msr0qiR;v{wQ&p)69PZc6Q>gWnBoxv!x{3eN9Q;=N}2c{=#r z*n;>Fl@TmSRNi(LI1Bp7O{ZCl8<}ewL+O7uHl-z;1~9-r3!ca z%D2HfYSLT_gonF7Iv+dM#Y7EwD(U1*$@a}gaHE#CkiDF8Mijg$-DbIa0sFE-;vdH( zM|9n3%+P^Hea=x@eAe+pi>Hp0yNLW?$|YAtv~j6}-WIYHq(SRRkyVP68JJMigUIt{%CUXItxK4|yPY3(SQBG1BD zS5|&K8Sq7|-nqkmU;Y9LL z4l}5#vctW(XC?)cMoxCwz8?f%6VZE;<@OtelE`Vx!wrC9CEk_s#)ACx6omhlthlV5#~w^fl4>zcpk2eM8}lH3v|taIlM>=`8%k5$qSDDUz^ zi4@N!csX!xr0TvaRg#$3kuBYbCtectjR~1ffhp+}kTv`hkTZi~fFkQ}L!yT?uaP|Y zy%d1wD@S$PA&7-xvVTa0L<{!+iiRrEw1V4)1vZd`;YP_AOvuaHR4JZepv@9(9OyY8 z-0j{*VcCcYWtJjZRa{M1I%E=X)_6`Jd)`}oOPy>0D=lt7S$m$17i{L~&Mw?tv*>0W zcy$`ZF*)D^2V6>r$*OMxg7thIN~LRnYH$KrJ%kv%9LiuNFVwy|R(re0+Xn}G2iLmDWA2lgB=#N*~X7nbdW*e5NMu*Z`Tf#XNp)lQLW`lmQ%0wL{G@b zTZg7zoK2uJ?(ReGdac!0{e{B(bHZBgs00q2>W;|)_{6PGp1x73zGO*<=`XS+KT?;4 z&cI5jj)UEqrzLko+2IVh&^=tPI@zQ@PyJBeVLBZ|iDQX2I&*kHKYj*2aIg+W>>$xE0LwP zAEooj4UZ>>s05>)x_D#Q{-QxzM;jWWTEos?JY4aF0PMePD5k=|&@Li;_02a}L}ysg z#-DyeHlFT_^{nMYHd;+E<26A|@f|xn+TM571?)O99!ET9Iy(V?dC$IPc-jJfbin%v z;eOE{b8#Gv=ovQAW3abL4{&hHI1u(HWqg(9IQx^K(F|vijG`R$WStKLKngqSu{YCv z@>ciiyVZbDLq0e1(x_8nUV%T{Q*I${IfF;W;CKLN{fWpGn*rS)qz1D%>_pV_oZ3m*LzYL8h_Lt4#a6e z9}h&R_TfNl+!SJFkt}vy1;8lD?6J$ zrDJi8k^R5~oZ$`;R;n`3;5SWz67%?Ep=UWO#QA~E=5iNh3CsGenJFc)f(OWI`y&^6 z79Ql9btdfKR>IQE@`>R!lAlE5qGc8_pin{>t7Usgw$#Pn+uRB2q3?lQXsLPkV^NaV9tza%M>6`21R%i*grC@M$;3j+I@(b@5UX zGond(IZd+$;4Sk_VnY*A9N;6pNT(92-7frW=^b8*7Hqv|qcl-bdF+F`9#XszE1-;r{B{ZJ>11%j+m99&b7+(dnZCpTU#zj2HaZNSu4S=ahJA zFdvVt?F#4BWg+&)<6IP#uV11fa<|T_I#9cR>;_VU=G6Sb4Z{{iG&W*NsX-gP5(C_> z8Fr{~*HTZ0^n`<`qw_)LYQ<1KmjvNJRoldW;*Y)x=+1yNL7fX@D%I)GEbA63sch+t zri=>2$VLp>ApgG;38=u>b>-9k%IoAbNzW7VPt)}crH-9vQBk@@LZ@u|%r#7K(5dm{ zY%iJNX2xX88Diy8RT@YY8V*V5YPkR4BQ5S!0yW#Er`Y^KV}ab)vF@@y{wW`<)6m1? zOp@6K85+kG=+tQiDIZI!h0l51w@c$gT8@8@kdHS)f}&VA1S$rAdcBMcFOUWjHn3kH zz`h*R!t*rw0;EDhrrt=j+d;B7jw)+HH{khSfo^;N=ej-y*}&+usP@Gf|BVMe_Oz@G zLIZl4q?aX%&Z3z(I?2-cq!cJvWIV|suoYBYCWrX7DXaiV8pA5wY6$h2_4*cAU|Jg; zsHK=j$|8r|f3q^H#z9fM1ivbytIdJbC%&S??Na_WsjZsR77%q*_v~7hmZdWOl2%p( zcg;n`<&hF5Y$z~KjJEiU4WKb4cvJTKB|nKKXQ zk{F{u(@p$F_fH%acxA_8mR0rwBWWrE9%Kx>(p_WWBEP8o5ORo+IXYk68I|rM>Tj zLbADTiG`tRhg$NXspNoE9pm4Nz(G`*hpxZ!8WRX3Z~7~}a(5gy8lZWJne?$Es7r0Z z2KP-gg8OL@B1J1Mxt%8Kh-4J#En8w8=YA?yb>YIbF2_=#Ix+GKRtOV)d?BDuNwDZ+ zhE-Q>>6F|d76|`QEr-#=8)Wg{Jo7#ulV?`-LS)k@;e}|0>ee$C?Ux{TZTv8x&{98Y zxep7yhD|zMuxAPT`wf^3*Ebq$v*9|1gR$=5lDCrmkQr}DV6k-=Sjc)Wy9z?31N(a5 z6@(!8LR=KviM-E!IUA)3MbTCBj)h$)Jqsdw6d1b`$+>cTeVr6o#ck3_0Py1jh}j!-})$g&Dm za8*GhEbIIFkKKpZ^PPEzeTIFMevGr;*-FX*o~hTt?Yzg zuE8zctmR~G?Tz+X$+(t+|=6c_h9k zD4u(PUAS0AiS@HC+2k!^Z&`idUno7x0(J7?zL9>aA1l+F1stfyRPeeI;6)>WF%(9F zC$_8jTF;8+$|fv%NTgXPE6AH2HEjx2==#3tH|N)@WJVGXgR&eCb&(sCuuR$Mbi;j7yldj>J9me`dgil}A2%88kntxfen z{&>h8=u_)W>#X)=DBQ$1)i3blz4A^fPu4f8&mY_4k9NH^e);<3;4Z8DGERe{V`Nw4 zWT2#w(M&}JUtX_}*C3r1LEJ+w(@B&dH6_!Q-Cd=K98BiM`Dm2iOwx;uL7{;d(`{oB z6^(`9MyY-EFgIa*vj7O9D$0+8WA30&<@C{{3Q?0ZW79MX0oX`v1p=*9BzbzoqI^k2 z0vy-zk%wO{jX^i@TBf6{)TtkVmQU3$;+MMkKB-?`B+*10uMP?>;WKFqep|>b*=B98 zd%CaDBQ>%1y0FrI6^${RymlW={M%?%T#5vpzOvaP518J+Io-VfDEiEs`1R%O_N*6f1{`#hPh zZ__-wDUQ$BI1xbNBf1jnYLu*(v^G-3oJbl`y&c68F z=uQ_7ta$%(nusaA)9tBqPdDp(WHkc@Q2Up74uA`aUfBg|N|{jqhD^lzS?ZQ$lJ!P1V9u5&u)<%6 ztydY$w?Q6Fee>7rvz|KqQ%}7*b4-Pjzq%sC`I}h|LE8EZ3&6Op=&APGDN=UcYHW#K z`5(>F`M1j`hO_L*he131)At9jkIw#5CKx`$^iQ3eo?7|L-R60VV_baM)~1S zOX4TC{&zZFP#Zn9DTS@PDW!o%iMCjfoqiZB(uG^Qwl+~?5cH@QgNfHV-AvV|>K%ew zsc(#ieujEcT$$FKs_}*-g_;MU)jQOK=x!=t`Va!S6-KQ1MXl^#Zf}{oESH#`j_zZg z%6Q4URYoXEj#6J}H0qh!+AQfvG0lW@351Lrg2q_6*z}gMZ9I4&@YW!nf@Vb^M7}!J z#d~CRe?M79aS zA+nXP@5jM*=ly%^NS45QzEp$61QKA>Q^6F_5BSHMDk?D9voXPcu*RS1^bc@zp^Z=) z-9O`l{+w-W@ZOg70(yLDxuEm@Uw2M;E3b2oBk)~wS-J5X)2!nX+E{rnXrI@>(`viY z2bA-#?;_1NUE8I*jz~wBM1&_2cv2DzbQ^mlKXhAE-jPD`g504u1b-29B1a?w6 z2`IlrPp#x%+pswoZe8c<%|+(gN#W*uOCx~WC*41kiX>@q=t`|KK)XTk%aY#g`N^BN z2k(wfMe%_rP{$zUiB2@U_%{9+d<;&fS+pnyS9wolo57EHkMdiD8fNfZrCb~7Efzsm zMAKzV3GoVjsU^Kyu7W&|E|Ha_NR<(Z(8#N^LDK;|oiBVzW6dF2r2{#Z)i-M6!Qr1C zY^Y7Or(h}LN3YKwY^XjoXB!V*;rFfE4m6+s`4a4NORP&ke1QgfGV2(&hBp%hf~~%> z?g8lOM14bD43JqJ`YQBotC4$esDpxBz1dr$XOfbnNxV`M4Gh0UKt4*8AT%%Q1=;YAJhjn0>RDs2E`Lq0p4F{BQiRsK)}*&@jGnN%tNu_@4t}jRNj1p( z`~B-PV5s>|UY(JmQ2sjnQEi= z=F4FXZfvM`htE&my=eUSlEO1OX&k=O(=-cvil2lpBOQmiKTrCai}J!6H_S;esLk!4Ac+$ ztB(*6t~BBSLOzNh&Z)zAK2R?-!LjRjB^DB-x}Y-W5uzJZk&Y7j0#8PkUz`K6RbN0T zxYU&XOY4~S$yIvIb>nD~!I}c3zWg3~E0y-tRZ%SRy^Rg}d!XZaKMs=1L7H7|aI)Nd z<3|w7RWXlIag_Y;Ay5eKKqdIU-~Ep60wDI~v{+_3cTIdT{YZnHp-{ceqB$i~@tUmD zmefJ6<0MChxTJ8MaTUwNCzakU6EjZl8+WA18a+y*Z-T`_{x+ZfA_|peUIxi@#l0)F z_64LnO8TGIY25|`rvQ$|xUh}xtJ2T@J~oUR3^}zpb+lqlV>maEt*|%vL zw=5Vcv@h4m2X%%wt@XZmv83J?FO}3=vrxCzz55DL1-Cw!K6$u)ZMb0b%82hwd0~!{ zqGeqW+3+iN5m#$jm6H!@XV_Zli|r9PCh7l(_Zmct%|99_jTbKZe?DwJjjpNug*&+I?}QH;^6E6 zu4&&tKl}cje>U5dGg72?7?)R&L!lejuVX7CyhV-fZ*B9e@N80Ko!Z*&-leKpOTX&Q z@J?0nH!5@0l4I8TiiYkyTH_V41>GV9s{hRX!BUk9x?a)HgroA8y?Ypb&e^_J>YOq+ zEJdRYjo4t`U9@d9lJ_h7j3mmIeb%uRFzWNI-mtfQ!K8QcL7l6uo*MSlHh-|KHaIm^ z$E@{8v+TEl6RoFS;xV^_dE}aHlgL6bCXvgnCv7U-HY2QwtxO5T=(ZWbUCB%c@5}&E z9%g6u=+Pvd@PnsMu~ps52$A9g0}b<@`sEo3@q=7ruX9`XvBk?T(Bggd^eJ6=k7jC> zE-4i=X|6x$RjxX5^g*k!ZDUL->akQEcUAB)hzu1|H6AAvETZLB*BbYhBb6b*b`E99 zk_7z#2;I>~_%X5L%3OyLoSj14F>`aRQ|Q7d86PFHbj)9gaIp#Ii&*cO`4m;i_j$<| ziT>`{&Cs>xDR*t4RZ+f-K?5fy6B@J{z8xI~Sk z#CvE>Q1jq}rjM8=L9WySemP3$9~7t*r*6n6(SE&;`}9vk5BKGlY8UtM--a%liFJF5 zsgQ1v(rNUmTQ2VP2>gcY;I}B5rDrMCu$y9*O506~0HP=l@Yl~ZwY689VLYxBYMb0& zIg8#HOA?UzuK&I~bwk@H_ZUANQk~GHhB|Ve&;4#oDRk>T@gKiSybvYoqtTbB&MQcy zWhQ|~{JGrUW&*`GPg9ko*UUYc<$Q9CYtOd2r?v)jfDqsgUt7$_)s1HQI7yl|S?w&#rUziA(_dZW($OatD%W&nrMjk9&<4-5Fo**7~!an>6%oT7=;d zSbpV})=LBb>X%(FCz1N;Du9t#!!@3?J37{!t5lugaGR!covY;nri4|x93|I5Qb1Fs z6Bh2W;IbbiVL!~$1r}-+#YR89N|zao5^*#oOt^viOS)9o=`s#!Jx2*Esf}gh8ZlDm zV;#>o(8~Ct)5Vt2)MAWRT7CrG8)M8n9Ow+b)n<|y?*wBkXP^fb?x(_jf0qJxGf*ReYX&l{G7pn7O zU{%%S?hO1r#j6!8NEc4x&n%c0yvE=hutjr@^zxGZth1CC&9W5txjoehEKgvo5h@B>P{U88x!%*f!wgVcHS?QGU0!o8H5=PWm4pLm7AI8r@)a z2m{C_UPZa!478sx8#X@uAscU_P!UU3FHq#ze^u z2U70e%J|`M^!1kcQ*8J*c^CiddUxHEZ~u7s{H#$E-Nb`|$@tSU;zPst8lf|~g$JAS z`$kT0%Ykp0*>0OFJ(LN*vI*wjD`Drqy+Pd9YDd6f=8@xIFt}@*ez$&2gCqvuw{f+m zS$a$@a~go7utg!taS)7e#`p~gMKF8e9!7iyT*p{8jKRWJJ3hCT$MZ2n#_>hxclejs ze6DrH3Nl~DMYM>ublMshc*=ZXu#pu7rsOS}A(Uy-|L7cc)Rv7g)fobj&3qaHgvCU_ z0%!&AFLVYQ5ebfFk@w7E^^CyFIxkw*`;IGAlnsSCckT6TYZsPg=;Kv#xJyGUbYGC3 z6#m$LLr@sX=@R9@iHqZv`VqV;Q2AAw6-dfT=Ak@T3GpPCEa}>#*$}z;Xe-(8?<+YH z>jPD0q*jRn!I#2iC;?Rv$Ut{vs}%oVq7`2?Bc77e_$%+tB| z!{O1p7f#9R782+EY{|Y1p$=knm`>@GB3**mvdQnj#&EBL_rtZyV_f~>l?o%kY2pk| zME&FB&>9a|zw6hnHhB+JFUKTM7D{zxQtzM`uCycjsj;0kTH;D8l?#1yE9=f>I~-~G zOGi~3PJ26RMpYY*v{D_FN4Ljm0nrP`9U)~23EWdhbV3Rvyw>Xde3=(={szKb(A!Xr z`E#Vi#63t87z|F+_w!)&9v-6scL5hMIZTt()f11DJf&_}mK8)XpGJ&U7UexPrtyr` zH8Lqe^MU?OLB-cNw4*{mAaL|vm~5lxFt-M z$@B`HwmaN!+zql`6YK4^KcS`M|GQ~_r|-F(!{EB z_P8S3%%-z{^!v>w5NgHRP{=)K$1{T9;U=CWqvVIHmFNI46sR69#iiZTxq-^QQGZ(M zoI(%XLmG0D7V0uh!%+ebPHKiL9mK^I?G<*w#(pVXenE_58r&Gu%(>)M9u-ScPUNG+ zeuN}V=qjev&mhjzT6J6*0Rjfa1@!=T_yJK7^98^L$ddPHhbMA~q=>G-G7jBJ`W9Wh9EHftE|}D~kQo z5{RIaBbj5TvqC4drr6VktoA%uj9D&_h3_#Pl3sui2(Sgl#?U#oz5fC$X{mUFQKG`; z)EbMay2Wa0`5p6Kr#88pN(VLC=wlu%w#uBexK1tOlnbIxz#S)1!Y3!Akil`)Zcp0a z_Wn0Y_}Z288q9%J#i!67e@m4OFoMtNTS0D`j4!MD!;}cXOzo?GIT3Gm_B|QX7D(>0JGqNxAlv^1Xah}>w& z&YEVPfUp=^DxKVcR42`JJ6uP&!wTVEWm^B!NtZ6WYfQMF$*^j}tq?D_RJN2=sF-io z)hhHoe8rd!?_jHKFeR=r;5x!LowStib!kzX3wSSkJ*`*{MPE?S5mfDqQVKT0n$F|a zvi53lQQud0x$SXFtOBw(KfhrZ6QB4BpvFzh6XV@#?7G-*@q(any9(4YI{cb!kEC~l zpziHnvt=WS5R<)2RDT(L^husbytsl71h2(d`TJI#L7ou-nP}dac8V)x5WDAX?CtW# zo_^`>o;Gjw?R8sy+ii8}#ZE&n!r#=Du(@A5>-x1*>(_2uzx3a)bGo^wyX$(oTkGkg zwLP8vgStCw?)#&4eScKz`{UdDe)-QBz-9Aj9hvTNsIY8GrT^o;EQMb32+fonJu+c5HRJd~G zaAJT2w@?fbZhWO$Z3~etlar(x1Ay?Qpgf5*R+^MCSt-YIhYb>i{70CiycE`g8!;!@ z#6>R`MOIL-T}gCGnA$n4EWjP&+pWD6ms|%1{zUv)d?!ltzz|Th* zaY6Xj9oUAyuxM9m*$WXG{F(Q`$i@U&H^Y!4yi}<3X%YlfBHf zcvTdsnr0eW6>;l8PFno=G0K-g9R18%559ey<}mZIo|iRL?0LlMh}?7l*%&ija^1l; zv+tppP}6|jE7=<%is3>TC8H!G1%;g`#=EBp+E-+btBb!pBYU!L0+I2HB9*`Tq&zD^ zYuOW0afk~Cz3ZA+FQs#z)LiE|i`rFJ9A3!JZpEIb$w!?PX8LHE=!?REOz4uO>jX+; zwmD3emzU5-$W3ZN5mY*NCQm|{#^R*QVzDe}e_$D)V>F7Ch1MSwYjAPR7$w++Y{6~y zj`@zlEmgChX<))HO(lsmL?yeS20X3(XVlh!-p!4ti(i2!!;|Bw{36rp{6d}cUr1ME zv{HjoT%(d~38b-j@ZdNt^xlI9(6BQPAV`Ijlr>>WQwz-0IAv2DOp-w|jdZ5IJA3n* zRi{Uy92?R4d`zPdnt3j-$n6NRZezjD(1?S?Y9;CQKs5UVQyQ;qb`gr2oo2yx%*Oyt z&De7oMn_~D(yq+)BET*r=2jAk%Y!$>YdLteKxuMIugIB&U!RM5yZ+v5ziL2j{GQh+ z*(q*{l9PDWGT@*c&&`!ELQMZ@5#Ka^W1$`9hjRltbqzyG}%M-a!Q_s7dQ zHQUq|QYjqM3^#ueFt}z83maD6^fS6Y7~NM7AF5f~pfycMd;RmTqyhYAbpNXh-$#Wy zQ)#%7z*Ohl8`cHd5`ydc9V_$weHHyW`a^EbMG&V%w2*+88V@fd&o5Z#wshsehj zwUF0_<91!%NbctstwcykW~`L#xIZiTSz(o=!s4PhDk3-{wy*rdhpKbU8Zu@K>$cw= z`d9Doy|SZR+t2$iXcEWfkL-%#R+qA)klIs*vbMEuQ=iUhf!hbyOc3^S^-Ar7YNTlM zL&)4^OG4AnKX%<}#P#=IN0|HSG5fRT3FgMCi5WWfIc=x$ZiNfn5@;<~q z=lo83$vZRN+voSrw0Q@b9NmA*HydYoV4VSVe$PzQI+D1ZDjPl``iHxaBQLvjg_~P{ zwv^`Z;X^u7KvVu5Q`0|chrTbAWvSxfLzdgEgm@yhVlfyrOQ2jRDhr^d)lM^+BV)vt ze7-AN!F0Dt=;uC3iMey>;;pzuS~cGq&i*{&n&99jqzijB7e4c*P0J$h*H1qAFI6=l;+fOZ-%--k zVHC=A$@7Mc9f4wGZy|8Q>>}22!5cc~RX)|+k#pPRMdx?&YgaL95L8TwQKYyqcBmK| z-IwKE+YI5Qr+?skr8QNOnt|`GP{}Pa{V__HxiQ@gl!Vp7gNE*;wASLoqC;|E>9LZ5 zOh;j=+7t?+LEtFG<|sj*tsBF%e2ZP@%iPQh{6KApM*e5y(pqs6Er0Pl-zCTUI#Gjp zUOh;58rixR=D1Tm6t-`xv0bgve00!CfBj3Fkbai2H?{SK#}`X1i8Ke70qgjj6&K9M;$(T&=c^;XzOF-Z%x}MhIO4 zJ(K?>u@K^4nVv2)fEPJMjd=Em&p!u;7jUHfi|;)K3@mcOA}*9KnSW%K7cCQ{i6^YA zC|=pKz|#gl=a$RH^{UR%H>PJgU!+N{5kUCP{N7+vM~_V7du6S^)bBqtU$56NJ#b%Z z?BKrnt+|K$?)Qcs(kwI#Vc+YcC}*WxR{osYff<@8jbvJ%RO>ENDrkPJr;HiS{T8nQ zQ_hWx+=CZ>m)Qfd1KL2m?0d4k$pG*(VnBmyt*(MltRUq@10QE;`hDfq$(4aLvB>B)U7B2RY1= zk%MED-1f$aHcIR!&(%4Ge?h(GUp<_{sJL2A2GexD0ZlMK&Z1an8)pJq+$hf1>0yy;cxuVigBMl`{B|lvF8xs(JMZb zIkM0*tM%B^a>8cA!h?wnXbr_SXQM?cp3Ih4I=9l8$aE3w8^*%09;-~JEEXWtT?aAu z2cEClK%H2RR9doB4l}X_Mg;|d1!VhUxN068Ld`~rgqjt( z;qzuwEFR)M?c@89E%QzYKddG8kak!$7u|779hS`vG|&1wfLFmmI;{hM@@o(hO0Rht-&clG#WohqeE zJ>y2XGnCFj1|S#u)<$<~jhf#S-M5_ErhKCO+PZDp=bx?RmOo%`r`FZP>dm-78zxosQf_GyKTPyWW)8>F+qC()(R|=rGJ5BmAwa6HKOD&DOz_TdRq- zs_UwGh=NwTWX$94h9kS+7BtiAELc=$Q$o{AbIGw*UpAKpt*BviycJ|jIKo@X^dC!^ z{^Qm%n_ZdGO*bDLK;RSJdZ@~#X)sWfzj+>57^D$6l3hno3^>_ZdTa1#>q*y^ zra#vd?_;K2YwB>hUZQgZ^F9~~YZ zs>i!KxaK8ckcAiLk=FWRlw4m$h^BxdGYip$P*x?Lr#Nun9Psvg6N+f6<2gX2$EKSS zRysZoj>iLaer7IEIHxYoVGOuXVX9f-6D8Al8EWy-V;P40jjpws2Lu}ek`~vE+>bcq ze!E+gP6pS}hiC!#Jc5$OUmFM8#Nsr z$KStx?VTE;B9@W+SDi20*_8CIdH5{kmp6rXCy+6$y$Wz!$5zUvkq~xH!vQFeVE&4d z2z)0o${U)<&He(5?7jYIw1}aIYTfSbTnHE8Ign?KtMAZ4)j5HJ5+z&+DU{JeTdT`4 zcRsowkQ_7_-M=t&N8l3v3kNOWV~%#5Lu?fwNmh$X!nqcHo)jmtp8l-DtOOocSJQ^! zVS;?5Xu8`_fB@tK5$$u=e7sww0>HxSqL~OtO3+2aI8P~wg(orS;#ZCFR2ToC5qfA% zt(%x-`v0_-hDtp@@Vw!Y;OKt+9ig-j#9i6MM7KfjiNCK$Fj8@`7KN{ULsWFjqtPej zk|(OqSXq3miKf__68+@D>Ey?%s=ArdB&7){68+)}Mh7&1)^?a+=azO@Gvzg_0Paef z3!N@FPTE7Nv;p1n)~J?e=vCK2)JbsBZ>CZ=s1m8(@7;eB6jx49h`(wI!Yo}TAzez4 zGiR!!n6`J3Es@H~sto68_$|RWA#j3+yB?HcNu;x#{rzrFbvE#y2l&s|Eg6B{$h?bg67x>E1SkOn2hXZR z2jnO&SvW06rBzZ}gH3Rft?JWZshR%JOcg&b!%~~4 z4NM19F`&0@LSBLbZP-?iKZV&2(`$DnBRZ{3gn2q_e7f8C^hv{0I}yX$^WB!`m5Jcz z)rsJzZ4*InJ)w^GVAE7A7j`0Kr`D#zJs*}<+CATGdH$sFd7U=AH@mHGp13!d7W1Y$ zG3I&O)W~~J1`)nwcaAqZMS2}%VNSTU&cnh~s7{pkV%YX#x9!D~<`?zJ@?Y(?zp6}^ zeN~+=`=V{a+!}q z(^coHzuQy&ou2A%_f&t_Q~j-;Qky-6WFg2F0-O}J(^F~}@}+V{1pnupi&d*N{>@_Q z_c>e*y#pHVhTfkhMVo^gD{yZl^He%h)iL%)Nt&_i#ET$E)N396sFT0__5aS*`HSdd zL=I+HQX-Efix$t*`Cb`k2&C)!b z73^z~^p|j{$aRoSuQsk1eZj%oST5o;2=k5M=GLQ)&EZBEeVnIZk`}rj>jFLoi*RQ4 z60gsmOO)Vt6TfQPO7zz4qgT2RBb`jF3T7B^C{yAgLRzFa0|3dWaUUaMQxn7vF0%$w z^49F{NKl5XOy#wW)yRss63}?@%m3P>h<^*1&guZo~zWzY^_x_`?l9T>$ z0w+WfW&6+MH-2tiGozX0tS*Ne_N_9t97gDleB;-h|Fp#J%F?uGoDy6})J{o?p+`?? zl%+>Jb33JHLz<*rGbVagh5C}mZEK5{o*);*aFi+p4m7>wbJXv<@zdgvlZkO4N8@s_ zQd^s9Yjd+SD)VZnhPyP7C#KWpO%p_!48Rn@$4oEW3ei&#RdtzaV~x|xtxdbqO)J=G zsv|YJYNO>ndeC*c{^M7#ePg%r#*m92`HdM<7kjhORZ(@MK-XQNq{bpLM8CEBbogj@ zd-uuH$4|HI9+zVEl{atb3rER2`P2I8`A_Q|mMiNQblB9`da}8;$EIAp$L5Xn?S=d) zJ?>N6pY-m8Y!&R;gHd(D^)w zdpI# zc_Lrwms{=k)!DnFS0~3OZw}Ai{l(MgM3->p=4A3fZQrSm6m2g_dXBV-UEyh~h9k{e=j@I% zmPd)3v28PBJ5S3$JEh{@lx+>WcdAmJv`2TVBxTnWC0Q#V=8C|Zxt(366EgcXyvY z89seNBiVK%c~l!oGa(1-C^;yRGC6MnZJLjrS;PowLc$%_Yf`eo-Jx^STXg}pnPy>> zkjZAUECgu&+T?OWf|eiyxR8nIKp1FxnFaGXxh_9=@RurP|0p>hlSzcaSMf{N#3S8% z@L*(OflbU|5q!|O8m~5P#yB+*q+z_EO)>(TtAP3?c;DAo1pLeX(N=?yN0*7oF$b$I zqbRL7QuoFjTIw|AanHCJ59`v*;38dC(U7;f*_6y{#@q z?cvwH{k>oL1HWHu6+$5he0g|_u;5E`dzo5L2lz5gqipKsd<|$v6QdQXdi-Q}`{|Rd zN88&^AMflu*(RmU&MuRc0%7g=gjH^&&#m;Eo7vuyW*$FiZ>I0H^Ba28cVqhU-q<`D z0qKY{30sOFjuqD$86FqW#!xc(_UU&4k>&JKMM0z->pW+y{~34y+MxI$ULNd$WTkSL z;?h|e-;QW9xC~(ao0{xX>qy>i@@B>xtmC{viS_;+Hu->}b+UjUvyo`Yq!t>&R$$MO-u%I_bvUL4I#87P!q=vlCgi_#yjgP&KF0)YNAr2lUB zKn-tgCc13af z6dzd1r)18E+~9Ok`V9s)PeRJa4WnLT5~h*0?bC-w#{U1OrOqPXVPO5tjf~fTWU6rW ze${Mx^LVF#Dl^YET*-uw5qHFG6pC-Tlm3lZ&0mMGgmo4Lt&C;-Ce)W${Ucl2);>o`U`<6b$tqJ(Jc(5xA0-*O66OFu>-ffulE zj$I?OE!3{0)8Z=-T##Ff@D;cOg6Neo)8H4uY}g_a3n~+XU*Xg46|g#(iJf;c%t`_; z&oz;{Ia$ATDhp-%V(4khl}Xi|P(k z0&8L{(c-1_)=}aE2f;ViKpoAjYaJrpAvluT5GnR;wyJF-!UOJ8$QZ!Nbr0B=B$zc<@Tj_xlLa9aX=GrhF_*e*Z2#2eVc zhBExN>iah+7cg^wXNveJL6POPz4L~Z?>RC!rEDd+I`?{Y(fOVCSC`HshMDjP1^XDD zunRgKM~OQQ9g38kwt_$hL$$%+s5J$ZaRM+1I2j63c3^60LOMudA*_gR=#(&2qUVp` zO@kc~`E37_w2-|*E zF(YO2XG#`Tvw+U!C|Izymp9o{B6&X7YDK)wRY_XuS`iGnBITRF=>AiQQ*CLpUg1V6 zc%P_d;hIzzg@2uJso8H`2zu3-iEs8@R-`7m94r>`%9kHuN~V}VBi(aCLOLalSUK=! zXQ(<4yH3Io7U*X3aeLXd%fw|(5x?2CvTlWrIJpAWr}l8T?XUCJaOc)`Zq~Q++qyGo zXy&#wS#^4HzxoNXC=hBMsQIKqVt2}TUm2IBx1brgcs^+a^t=t5yWuOPuT5HO3h`r% zftqyJlQe>CISbfb>3X|`u6E8tde@!n+y_w$HQhEQWskZ~4OI|&j`9cnQ zO1{ncIB87hU{ml=3>L#K-N5ite#h7T@;|lwY2e z^i6Ec9Ls8s&Hc7=ZzXjPW)Rm`hmbr^7_rZrq!`PVhYhD}>s%B{PJTIGtA{d-+R zqgmxH+L9n)Zgh_R!{%HF4g2NJveok|%5!A$V^YRQzC=i#9;+r~z*`3S^D@?w<^lm( zoS3#G4_84qg?d^APQT7Hh-13amgaoTysPzcD1L04wcEkEIjhXunM*`|t@)OPwTu#T zJs=`CUm~K9lFQtKqeDV3N_8|!YT39+(TN;8lw^z`;g&h2NL;}31dZD~O^Pc!$5ikQ z9+l8(D#}W)6q-At*1S0_os70bc^*8dTnf01LTb>dK;1Lvz+CA^sSpjs??p6ITk7k5 zdGY(-%P(K=tF2+fHlfv?`+aEMBi`a2Ng_5kA7d{RDw-LfBc6$|OOWK)SZAV&Q%A>VhwqLLUaRj<57o)>>%ZU)y+tU(_?ob}^$m<9xr5y!#f$*0BLe##w;AQs zx>u{KO&HVpC)oDEvW>Kh(wlOUB2XES{KO^^g|7KJ>kGpZ+V=KRJ& zv3`v7H5^=XK^QC+F&^QL z%V@oVea!aNYJYG@5`aD!XqHgYGm6ZCT*C9m_P&T@M%bL9P-lE*LS8DeWel_$fbv0F z`HD5@@yZxZkiAg{lq8L~-~>Tep@v70@#>u|>2;=K6Ea~Y{1)=M%^-UWXO!mnZA1T! zFHfge8f2Q;x2E{u`8@PK@O&P2>t#-t6Cd(&Iq_j%-o86|_3q$}@3OAOLXBgS3X4V-oDQ~YhZ~H9ozqO~#DF+kQ>J`S&EnvaHl_BG z`|X?8p4e)zY0j^75W-LA!t~5$k`lS8(Ag}Q@;Vwt`z5LytF}qQfW2xki$S`-kHepq z=^rLp5GB9Q=y#i6RZbYl8s(&w%F9^^$YDe6I`!;02l3a|3Jk@3`LHqc^{_l?Tmx#W+@N3yjo_= zJxO)(ZI)i=IvZb{Px!YB3a|w0z9OM`rmrb=k(YmzHv9*;KczE|q2V z5llY>mwMwe(@7u4#D=`wK!{deY;b2cWE|E~Ld;(6F`Xl=T!cfMcR~LdAn<7`?D4-n z;;{jNAzb5ul9H6q7M9jPDmWC!7ajc5HOG5n zaHpdD@KJ6qwRDv;kcs3@Dw4h!&;qrvOnPB}lzk?KPf}jB^n+nsc=RjAAV>Q6q9ecg zBl*V;q9Ri5SH%m)LVo%3@cEf~b8vj{>d?v7<+vwZI@@`lPSF(k9jd?m^?zh^oc`^v z|IT}r{>&ZA6lYDoXi+|9@ELJ21hb&1XGDROKjjaHPi$0tlV2)`U2oj6!Y@7JuB43S zo*)$2WS}rhL%oR8bOD}1y$BWyG2oiAe5IRc_BGQXVzS9c_@mCi;K#gE{fj4|=M$F& zq(LE0$z!Cd9b0TU5jl{MgS&^FOM<=Zc%r`>vShl_>vo_mb=N7N5mzR=W$;u#G=x4* zlT)61f7mV+5~Vs}2F3}{@taKXD5+2quGt!d6l4s)c#wPPj`+x^R6*yj^+e_A^n)&} z%cT?E+^hzcfws&l6~BF2StRr>JCN1t7oL&?XD&U@=V4DJX##h97~u_jo*QpUbBVz} z&y)j$CHVzA?ZE>cdMGoIKX}k1trdF|uqce%2vOa4XJ>EG5PSr5UgWU&UuRKaG66;T z2NKOmKB3cujTu9gXh*6A=_Ns zvkOvOrzDIJTtj7T_RNvYH@(s!Jg~RA=TgR9N9trEMPCL;B7IscXERrN&WNq0E6Oc6 z`^`5Ldkj23dGqGr_(kdNV1x@gYY>Tn>#vSulf9sa_5BX`gPazdwm&D+=KS#>%X_rk zE@Old7)hiK5~5c_tUWI+h|!*!PSEUgD_BMrEo!}%fCPkgKT;g0k3BX0qks9q*)Zi4j$-o8HIXaC1;Yk z1Fy~oo84e&xMHW?)hL;nEHeQvpRqp?HkN`WQ$$>I@j`n{rm_JNJ-iMg=k)mG^lmgru7o>{fFA@~HF#UhHDbSL87D26tt0 z5D=0z6*Lg59a$$*gxWRZGV2a-kC}RTI#18OfBDiQ9_u7Vf}z>3Vlg18FEEQa7$twk zzRII{1eB~IRp%t#0h(ug(Yf>e5p`gA?v~UUv}bD(crY{dZ6>48DX}RyNfe!$V%&p} zb93c-YAzM$w&sPQ1lp+P2!_gslsZOF&KGJ|%S&<9u)(kOBv>r+4VP)@s+h;42LCjF z{esd$&V%*hEgg8i0Cu{ZGzeh6ddvu5r_0HI31I&vfRzQXQ~$A(tLn7!&y~VXzyJ31 z`MaaHXGbT;B~i>D#vSCbSR)}LN6x?*@*~%tLGX6NLQW9_Gtg=#v$$rbfN%55XpqwRv-Op(q@8loZkQL*Ck)v@+4W3xa}^ zDNR$-J<_lwdKfREZ?Go*_h{q2&`Yk2f;QAe5XD4FWtEv|7xCi#DqX%G7%?{R?w{%s z`f)&y_PCUZS~9}sJVUp%0W!hU^<%toj)k_<*H};ONx0K z26`-4cQw&hCK@!K>O?co7SZ)|Ir&d1x|Y}HKZg9OW)TI^-$8yYDPH&S=NHMZRaWK+ zv9{$e5@IX6$-2)OWtMB4^Nj9Up&O0Mm#jHdiFOF4@Ze#4$rlpSS%-TG*t(0j~M-6VO$T4BBW&Ir`u%8mr_G*%+=zR^PvD< zqey3Dzh`Siocqb%O(?OxDYAgU2(kz%dV_pr4*csZO)iO!j%ag#;KL__X2=-y4OVlI zsKX58r1D}FYj%T@nV+tV{+Z4VSVMUA$WD6=2E`}#x6o7r8K^z6G~s37si-@qhVe5H zr`U)xB6-bK)Y{UvCX-;LSW{_7>Eokt%tK?pkn5DY{xQfRcwpq^q~!sN0zy74l~P<} z6tYA%#;3>JhI2hAF;HHGOgm00e#O$KQ8FXR%$U8>8Jm_Ve}>4Zj-CUW{^Wz8yA=q~ zqYR+{3jo3_oz^r$bNsPYM~rUN={c+#)Mv8exQ<_Y7U+^(M0=5^P&{xbGJisOmH?HJ z@#*-3R{0fX4Z;KGzrJVp*pKXy6=uP#;Aydu^HUPff%lKyV-ZkBFfU^qSQi7EqvABV z>=!zlE5XdgmiP9Fb%3+}GAUml2+|~v(`zufTpiK=B#Nf1CnglPzbw*;_+K26ww3xS zIaVgiIF9lwoF<(5T5h}vmg~oyJi4tL-aJY|>@wb3*P69sA$AfX78$C9E|KYiz+2*# zngn5KFl?g+oG^cZeV2}?3R@cG3?wowR(90+asgHb_@H#ACYpSi=q@~;z+r3&j0xPd zqZzmxVH^MuH4!6OP!Ke+aY!KAF!x(5F{#h2q|)w0B$*-B`4X9(@I|gf_JJ@L^F1=8 z``5}C2TcE3(>Y_vm<_l~LRE5Sn@g6CtvDw3HKx4X3=U&)K0L^0a(FWbWt4cY?6&27 z=c_UhM;gcMKN4%_qegx$I?v6{52$^*=>;E{)Kc02Jvehy8w+;s1#cx+-m&<$vRIm5 zM-UH<5uc7dmE($E5odppr1OBY#?_ex0E0jD-XqEPa?j!mryTSwr!D z5x|zL<0zk(>zkRgH6gLts;r+Sa7(8fu1EsGC^^UDets2@j+fO$x8eDpc~uCtz)6n) zDKtdE$3I_o*@lY~-VBGoWixAMwQ6Lm8#YpK7q9%A8I4lUsnvbFbM&pN@ zFEPK&#TT-yK|Lr%8i5}SjK6nhG=c}SS9sVh?*oQApvxeOg)u>}(Uk(Z+n^gPd1;a3 zi|4gMOdj-->8S?yg)69aB2{m|f@iV55#=A9UO4gl#tX)s_UxQM$_R4w#Jvd!^05wb zZLi%WA?wDIuvQB6z^@zb6qdCPsEDJSZ=s{ItcX2!v=J382Q@AxtgaR8Y^W+DoRH81 zkrj$^s(P@qKP{t=K`apYg@^{{<6IZmkm<4*U!1!?E`(RaniV?m2#=AS0;&(N(wHZO zeGR>O?xq%E*TGCQn5RESam*}0C;jhFH*jCs_(4xL4&EMZ{17E!dW{CMe8V+mrx|w{ zAMVO`w4irQ_cqyKRKW9EF$hAWelX`uVCI&24zG5or?{a;g(wQiIsQ7x;dzn*Ddr=+ zB)P-zQr;?@Ivt#z#XiW@ej1R7HEmPvK0JbkXA(p)79A(PrcFpZgh3hj3+8yTm#~VB zn-C*r#A`@fPv4@;Q!v4i%|3nj#bC1Wb! zQTV|coH2`z)l+fs^GeD`3FXw92RBEi(M#@@=D+d+*XoF!W6@3g5$Xl5YDr=GU5*^U ztNXFN-cwZ*b;AqyMfBye7T=}eu!)!CffrJkqBWwsRUuARF4;(Rf^ zyci{um3lK!hXZx;)v=UP>Qr3S^^i z@K(&t5~&*A8i&E$l)dvpQvDaW7MvPwj%@@hE6Xee-|#ievzat6c-_px6)Hqf%^hc2 zysA&o$u{oe-c?l zc1G8fvyg7Y)%>5#hODqcLg9<(lKiLn-M|vEi$CxRBQhd)+YMiOWWpyX^=cHx)KBZmo5v0%}8Vt z&#yW^A9I_OkB5Hi_JBOaZPQcwt$RVoX}+AzB1)p8n2{u+`y>=w;}#Oic@)u*%UNwI zBS#U$%bU~5ymhnC8I5wS!_8W3S*$Cus_af5r#*EQsn7mQ`ag6b{8~*~La_aY)V>O+ zv3)hECJQz{&C%JFRwmL);6MQVA6|=={$53@k5(S4t#W~=p{0Ib&4L_p^}rK@7B{_S ziE>yk9L-cmIKzO9dfbmbAb9Cl#`9e>QJ-vzTiZ~670n7#NB_6kw=3kfZTm!Lp*1PF z!p&~Cs~*zI;ZL=%5`9zftEPExpYukVFh967=2ur~9JbB1H;tY$e-921Q5vd$!SS)a zX8SbwnVfhQ+UKm#)8B0(7V{HK37z_7nthA=>>iy`9Qt9hO4CV4Gb(ReXt<#{UWfH# zT|3$4=jOIn%#Wppb%QoPD0h6#n6SA zAgP1+GUd!yoQDw>O%@)+mtm?b__Pr&1|*F$Mzx3^O($beaMhkc^jnd=;BSBZ9~9-E z%enWrzy7;eT44*|!%}=7ydLFhJlxuOyz^xH(az(sOS^Jp4o3SIk+LiM|C^Ds@X$E& zz8%h!vC9SH51s#_p?ifJ(ob@_`vt~NOtD2>`HKla2f z)+CpuE~EQJ23gKpny>{GV32ZoB4u$FO_uENbQ-L1-M1(df&r$+i}3&&<|*4d62X{{ zW}{@hKpMQk4&m)UrwwN*&%~OF3zyp2{1+ntN-Vnwj}y)4q~tL`LW2cEYKJIAGDmV# zAkPDK%{aHJbKu?cuib%q=i$nSIv|S=$BSR#6_o8$@{hE^vlM`2S9=WIJ_5n+*`&%rJr`eDJ{1DtqD?YtvuIj775jZk#`tJQ zO-O+}^F-%zNi??AbgDUVsCqain@#1NWK}EQ*uE{lRW1KXCErComAp=yC2FK#+*Hrd z?m96s`3~Z5vdJy&qj;kv+=eI&Eq1V-Pnr zh`3rppO^>O4n`xxrtjgu4Ik5JhyS*_U9a2eTt)cU_RetY@#Eo>!6wV*NaXi)bMx`m z)2GAToyR+yPoEC!9fE7>G>AF!6pB#nUSihea;;Y$B>3!1R9^)(={HBijZ!J5;eoXI z(7>Uq2I0dAHQZSJ!m;JcG2Mo<$aMt=JYqbEbSp6h2>&R(F z&&VL2lHnO39?_IcK@>$AD0ag{h64l#-H6ckgz(E%euJve51W-@oqCI&}7(x6z!%h z@Lqm&xI;A#?O%)b4TiTTysFPWI>w8GvjcT{_U`-VXWzd&Y|>i=bSOGjZdAU?W-_-J zFmNg+J0ZJHI^}90IfF>3nd%HsG-~()R<)_i_0vd8{L!~*)T~na#7?s2c*96xNV3P> z=Xxu3TB8AWN+M8yxVfo@o15M0bKr(=ZEn&WvNjT@G4fyXcOREur&$NPONhGeEcxY`9lA4&$2tM+l;#A_I%RD$t9B}C+e_GI?M#?7 zrA9`Xu^Aju4vBj3fYUR z*XN|$&UEEWLp9)0sq+g(mr$#X5w`0}7&q;}QyF*anysLs*J4NDS}|#>rUY}}%k;+s z8I|c54@Yu~`(mxH5#d^;Lqvp8C&GF+fwsdZ(bwE3Lf_VhKA%5-uZTT{QVo)WXhpKuPV9$cJrlt%ievLuczjUX-O;xgrUJng7Zce z=IhTeiF1HSM!krFIk@XD>Uq4JD8fw13iXRRT?C=U#iMUDY-cp$m9X>Fp9QYmh%6R?U^weK1mznU=`UU~D>6B6V;3jSUvn z!P}#6AgB=@69k4hDvDUMM|VWXN#eFI@>{HfP(pN6aQy-P(2D#6NU!h%dy5il1@06L z!uIC;V*CxkdqD_v>IZU$BNY#_ByxJ+Z#a-jTv^Cr+=~ciRge}*mdE@cpw8vR1Cm6| z9!OD+RG#FyMfU{`#xlK@>$;mVnylt|bHzTov_D*wXu?>iRVN6;6Vr{Z*-Ho-@Lw2P zE%7IQE}#D-O*sL}vL#|JUL*WvTVY>q^~#(;eZFM}2I{@(`1|&ahzOl%zKn_QkCO8^ z4d8DcM*21nz8BF{|L!#ve;-Cu-Q@+08FV5|)ZhO5|4GNr-~Rjm#jdaEtRPoCal2)2 zNZ@zfWyik+&euNWbpJ)2U8ndj!QcO)=+cACs~_>#X>?=7-IUx~>KeRYs=X4`jc=yS z@l|i(wl>|Wp)=m4@4T%Xy6ctewU(m+-K8WAxvk*PFNjHLE4bqoJdJMra>Un8y-cYA zsQ{-}0U!a=8DtI$@f@E92*b1yds6%XcW+a^>cB^Kk7&r3+*%1A^o6fN22S-MOp;0B+oLrUNb=n zl8m%a_NFrfi$Km1xf;u)ZYXYHppH%YSn~;q+LTZij&ndjA@Q>#_R5^a(Sqh_E+HS3 zFxY`^Syr^So|!micWbc{Bed=F4TiR;h_zNI;S5#L&)R2C@{6bHgFc3z+I@_faRppE z{IQ3Iq0Z7cj3Eq4qxo`9UZcyodg#^Sw_UX<-6WZuWgf8P&ZRECUH!mK_`~U{j=9iK zkAzkG#s7Y@Yt_R+`3s*aGgsm6pUu>hnG3PMaui)rh$)G1>EfloNCtklj;LG0LMCKh zcSGgtX^-4%)jqd%!VP}*nuumzPrlyw7Pq3km2dGMWu>e?e7P0-pX;~}^u6|d{mS$2 zPtQ)?G z9gXPbhfBlIQ<31pw`Fg`WIjS0zTJoy5&ApuXK?~H*S;-QOmA)u)Jx0AC{kg{i3BAQ zN~$6DPeL;iv8|AwsY7TXcK$7O!Q!@!fj>PKFqdJ2jp#2H;FKIt4Q>dI8324H`Dxg)3iRn3bIm}brXxi1Mu?aX{c2vQ4iJA$K7t_W^URIjd8XL} zFx*v9GL4rxbL%7C8H%ph<5C|en>X@ZsB<1TWbypsJjka}biqdv)iBqF>M`h01LhK! zLOOc`72Pu;w6XJXDDL^VSqCdxRSIRKI3uG%;rLtb)oqBUG?3rD&7y?Bl;2OU0%({B zNcz_+aO;`J+BJBRfeZ-aWi`V=o1Vd$-x@F6LFtmdg|m(Icb>O_yJEX~402wr>?tk$ zgfa+%v}A?=1(TVUv*)r>S4GLkAOw@Vq=bS_|LJipo#^>WHix(z zQd9eGXjeLjDNloE`-Uu;rQ()cdvjBNw5mR`MmX+z!#<~u_+=XQx#yqO8I5k#s^afw zd++6?to46iRo>jFeRWfQv{L(Ok$C>)#>?{WTr#bf>-65|D%#SB$&l zETB18dLmOI=5<8X(l~nrxn)MilB^{K7AMo>p27$7%0;k%@`T#6V5%kMAwC!}G7BSf zJBniG=0hpat~;d_?KW1bu5Dd)+Nd~9SUM~zPVr33mnbflJS;o~XN80NqwJM*q@LY{ z?iDW5MkeE4nKz#%5)N(!g|fvyV4k2b!^$R$@B}| zUlJ?``3>eYfqrYtzBj#vpO*ie`Wg6Y{I&ex+kYU#^~+C6<%g#Hx5go1J%g@SZ)4GP zE3Y$Y-#Q_|n>j^ms60*qCJQtR{mosO?&%^*e4Z&1HI4>HWQWvYwKbOA3 z?*M9er2&-#^jl}F+Nm6E+%Zyp5p0Yi zSQfn5$A3-F_vPQu9;eJpV`=|edc&h@>akZ(O0{WvWoFKKW_p;Ip3Dm?MBF~--RiSU zgK9+dLDb-hk7n!g)y(W%Mp>ktyI{|`Ib%i{h9P!%FDnT7O*wBWx4WB?5Qbm2#mj$& zelo(_cYOK5^Z!6Bz%M^7mEShypL6xwSw_8q7N&u)Q+HN)%!Bl5lD-RuZjs=tf*pX@ zr`nykK(fn_Tf$nrDO!G%yra=b?@EIq)_&Z(v6zg*;j|i z)NTI!MZMO+%)S0a9pk=b7@4bYm{{qFFfjkwj9p&bBgfEwGS3?Jd*Dd}9{IUux|KF! z9n4;7kC&Ak@$%&aRr9ja{3X1FcguS1(?6d}ol;fN4Y?p?UjHMMyAUDK zN?sIz&e8E>LpOriA|OEE^ZDR#jz+~*l!c=tC439uz;Q$> z!&mSCfRLnZSzoE+3ac@XkbRPK4wHA#kDPv39nkG#Z8JP{y1_uBL5Vlqtebwr!U%p@ zT%|~LxSXJi)(L_DlK}zojru{yF)8)PlL`p|+?y5s`@7vJi*V?p{($3aJKQJQB$ja_jR4&cB*0} z38>b)rX)Uc@;#bi=Isfmq3LtH7t|E~MYLkccl&je2*+_UsB-~`ZnI{Tir&$uuw18l z!}@zkJm(j!;?7WY_V#LWN0SR8MbtfV{3Lg(8u|3Oft^&{BKv)H^ycmB z!#9V=XXLKO{6!t!EaD(3jqdI9S1vZtFaWpu6dV}DEN@V~PPm**bg16_AE*5{{nPKn z@5eg5ZOb*<#4M%D0?LiaicoT}KHEe4l2SL(+VSy@li#ckH`76YMVDahbrvyHn+KJ} zxt~bKov=d@MTb*`bV9E2rbCN-sU6k|^}u*|Sf4cRkC}#7;>1E`Qi}7SuR-1q^3|S! zGhVTMnL}n~!IZ#I2>)i8 z93%lLo*Bxc!S-OsY_0P}u+Z6s9iaT(X$RJ!0)$&^J?&56CS~k_<(cT&=f?7Rh*zlNqy^g3y6shE6G^y&=DA(N{Qs<8RO)B}# zCoA>1r;fgwQ}=Wzb7l@zhn7wxSosP&Koa(KN)F&bq8>9^7n|&R>PR*nO+DqP(pZ7{ zsxnx8Go=kYe4;v>Ogg*79KA{KHycTd(nOp(@^g4`h3jijLh&~DjvTaY z#TT@1KL|E06Y7p)8c)A(?Wy#YFq*_GF~fQ1v&#}Gd6wilQ9Z0NhyA`ivstddNiBza z5QaJt^lARx=0p#&i0Q%f)EgN!`tJBu&bx&)hIx@@l0;on*ia`mfq53SOgxFc<`WSZ zxM0;-T$%`n-@;2w4?CqL-sgWe{Z-zl-w^<_Zx`z9sv7u-e;YW{lc89x&8b;r`-KPFp3K8ip5V=msuM7-k zVp^G4C|KQ17lqE!v+`_Jri7`BNKqtS@R(dg=(o>bt&sreJhyFO#xPlG>j{-uOfTP) zTn*-TWi)G>gQq*HlcY(19wGKI?yBb%JxtFw$wvdZz!6Gx#97{ml+tB3wfnZbtb^K( zLxJhkLZt1o-GC9&?6j?9r|re?3D?B3?sVmMM5PZOKYp^g{doIv^+K}_49V|M>lcSK zm@fa<9RUYEt^(%BjXx0vx`E{)Ar_gI{fbIn38Z!JDYhtZ4mGQ?hqO37{_Vq!iwL! z^l`C%?+axWVbQ74SWt6XCj+NUqGwQAS4=G?%q9{QSs;K@9|9j3xZE@a4KUf=7E4@2 z@jI(q?vb>eiivIeg+K;ao6IQ5gVmk=K`GRK`-R$9pPXZ-=g`?xKl>N~#zyi%n~F_% zIrbMRRIdeJ&YyjkD00daM@)QkU;XU9x3J%m&0Sw&Y1l5bU+{Ce74}tWb_sBHf|~|+ z6={?N*d}k!Dp;9oZPnn+a;Mg|np*q0!PVtXtyza!|3r4})>f$Q5mP&UK@1wI2LP+ptJExBDKapy9iqeHX!$`iMDq#gT8VP z?l;YQhBvIyWpgis3^!vFPL?mpI6I)2yGkl5DX&SYD9HigGR@4HJ2kP)VE(@Ka{{?JN#1$#V=j7sWgdwBS_DI17RBthE%*<{e9CmiaKGP`k(qTo_roYPk?26TGLA9p&!si=2` z`%7!FCYT=lR~99-#G&P86zW+3rJlP*)^E4Sypy!tab1Ir_vx**_i6LfcptA^#kV%M zwx4e942RnycbLjlepqd?{d&axQV-RQ^|5zV+cg8j#*z3pC%5;@)*jK*o0wZ)os&{l zSd;XIwfUSg$#5;f8uLsQzS9zLlCBc~vi-EwUPc?M3ZrCTOR(=MUS zmyQUtrOG900<&+g{~}!NOaG!;{^7v8&6}6-XYL_r(xK0 zsv6K5U@`~w_Q>PUwZIf$1H#qIDB;WvIJ?PrU}|$|E_2<`V87o0wTllNRhD0LrE1!? zbi_)&{-vtsqkqf1&l!!7V*o>qQQ#$IbeUZX?;24AiD zX|rO5aouj|gjK8Z;|lbj480PM)aP*};PxG-vz1iObs^@0pOG>n)cooR5@vyl z+osv{9qY9VzEUgP6^V%V%Su{T`_9?dyFsB#TzongCu85=l&c5s*NH7y3b@h!Rm^uF zl0KHbQ00pt1(TADGCz8PGdEIA%SIbdaz737a=^M&2M%xt` z7|gALzn&(Ck^_FvrH6T!E^#7bzPLvgLZf46vV_G}>%40=5mX5aSZM*3d^p~+>xe-Uk+N@DW<+~MM$c|kt zTr1DggT=zsVsB)#zNb2#;k>wIeeEx6*3>lhUcL9$v0*`wVvJlgT+(g+yOco;ImGxe zr$kZ|?s666f9827zYF=DR`uiq&=TU452oyzbYZ20VQF%RtiR?tv;Z&ox>@?;b@215 zr;3yu2YSTu<=1(z@LKQ^gO7OtV`In@&lHN+fX~y?I%5ay!;KWielqP#2s5Ju{!Apw zCO~)K9K|L(FEL!sQT0xIlt-M>>Q76`Z*AV*ZR$pqL|k`B#l=~+uGp>0)u_*}6#Wi~ zl(B8;aqepTmYKgTdu}vudRt0I{7drPhI0_=&!Nt0eHBHS`hnW$|SERqxsk}20D|92m_}R$WD^uuO}a&Q+^`&trMHG_*Q8$OwsrAp7&!`y(QR8Jue`XP z{^~`d`hoi56?&)PlF9SU0t77)uLT@e5;Z0ULC#%aP%Hm>@-? z%Mi>fV>7CXI*oa(n3YUo5dMC3k?0s)vqX4ut~NP#q`96{;PUivvxh&;Dsj>38u8%$ zZO}Y+Q@^xyB@tERgNnY#BAgy1;lFCVAX?K|c9sfNxP>;7X*82s9GPeuepbr|XAc<* zI#I@pJNW<&Q)3T`^5^k?UZ?g{X?7e9>ntl3yfas*Z+34FRmM{*YUb=V=A9A4aL&|% z&RZ=@QM#Z^o=vG`(i<6*hlJmrHRTo0MTY>-!(98>o=U{&$x=|)d8hK$aC?1OX6H#N ztbEfDv0tq5gbm7NLOuIU=j-iEJJd7J&x1f1a&_+Y;iB_9@2{>&aaEpZQp}n&m0V1% zhlcooSf7vJV;Y4b;~9ys(v~>fLz(UHOB<%}btw=jXgCamdU@$)_79#{`D;}8XN?qEU%R|`{MuW?p>DKNVWvQU-4v`l>i9&Jl>ap=5YHb$ z>KY=$PNo`Lh0uE#jL3?BvAptDmdy1w=FriVl`>C!IvUwd7WhjPtY4nfYxW`bGvOe8 zs#M0eE0!o4t0CBi5uiz<1q)-{I~)cF=@JEpTnXwu$cIUw-fI(Sc_- zb}E~<%{^0KgG^#56!vECa|7XOX(sx7@#@ljKws2bk^Mg#9tbb$^{l z;dN~Hdo^&^S`->KAk?10sBE|jsbI#pwTN=-;!`c|QGY|Pzt#OV_V0XW*x1VAx*E1u zqcMdm`L1~UP_-T~O42_M+sPieSEkr$v;ots5ya(WemnK%A&F;;-r?oKmepky5VOvmzrslYom~KvtBt zxqm@^mT8)R`F%2~n0u5wN%r;$Efj{+|N9`0Mz680F_Ei$s-`l1&RHAqK*e`IKUcfz z@BWUTp21bGCOzlrHC1reqMCq{P9?0Et0`Q9hq^K!d(;)F%wz@J{Hj^7;38)(1btKF zkBRm&i9`(0`C9NraYD%Bhlm=8vAP9p5?Y8i?O>U1cm93g~J8rv; zK!=DOzzrXQ0YUeWj~7guS!0C61L82u*e{dALzC}_lm3|n{+vTss0Sr&5mV;S*vZWE z0-_CoUID3&z6EFeNZemFc;V+{?uRDW3JtQ+8rM~Dl=N2%N@2_q7AXI4GKx`Py2M~! zVAN}v;|7{_Tc)x>4b5FeuU6cK>*-W8jcTG#4sCMIS#S)s-aHpPOgC|z$R8}N0&z|R zmI~mGJJD=o>1vvc$jC^A*u9jFa!kfElb|48l4X(4QLafqrJmidu+I?Hy|a=Pcc_7; zi7Vn=yIm1~PN8l1y;lFUVcqMb zf##c@M9`+MD6f^5sshd%+)*S`WgmJD0gDnvPm*oh^l8gFn9@MfwcEE6ejJtz?8g)!!m4gt1T@C#bxy z`djM3168ElH!oZBLfS6(iO%y1`g_%_2Sge1$6Hi-%I(~8b zPMy3tKYsV-;FWrRcC1d{y!tbgVCj1Z?!CZpX8IQRryN<-RrEJwE^EBEI$LMjvCBHsRimsk(KU9Ih7hwI1*NerAVo#43oxXcrZ*5^ zIK8@xwN#GsW2!#YK%YC5hKenaIv6Ds9$5QKKVRX?AX%b)lJqoBFdYW&9SGLYL61Z! zEmF?Q3W#!GwVG@YhAGMDpR9v{%udEw2W`~@*;9pD`)KLT4JM0=+%qg&JM#|D01OJvj z!BNTGRe+{{1yeBh-hk2a;DY+`v=ygUDrtYTnV!TR3s>4cZn)b0TI;j#vHD_aMuFLF z-%a?y^$!?YQoRmAxT;~^`o7`Yosp(wj@D#Owu!acQ4|&%nXXxL6}hybfT<2$&r&sL z!R7|GjRRx;BHy_mnW#`q93V7N@$o9t$wzRSEuuuVU?x5IOBL#q z>y#!w8wueUe1W^A=Y8xskVhtil@DP5nj+bkyT(zQBn)D@hOV}P&uZV@O=#>|IB(pV z*SdKWG)=#*H(Q*Gu^u8W&rBGGxq2M6Bhv+br&SrvC<=@Ay0LQWPhY6Ii%PJ!&1g*w z9;@RuN^{^m$Kb2b0gD@oY?O0egpt6SXvJ4dkQ;ve9GwnIb38}7<*Z1<3_-ZfwnIqw z*o1R5!Z=TynazNsT9Za#B6-nQ?+yuWxB(=PO~A=t@d86}l{2bx1C@TCeO7f)+=P2- z?5l)zc0rDGp&@i6+E38NjrjBtFGxB}gf6(u5*4h041QnguKc*Wp6)iKQ*49fqj7Qd z4HgZD=2UMQos;hoGFVYasmd@ZM56;HLZcv?`xlq*3GM6>N zT@lP?I)DJZWMhTZ#ATm*`O5j*_TL)@w&&Gwka$0#)m&K`mt(q_5u+)1-umF-Y>OPK z)q=^^cc?Uc=_|p6O}{sUu+w_r7*lp)P!iGx26VH`)67I=gXGAha+^d~6T5Ty8tNWO z^dhijn{3%@z0Av`#G2WYnYg`X@O@pP1&-z%cw4a5+=-2W`f9M-`gOhpnEoB$Ll-y4 z$QzFsi0^=wCr+ks;b7oCuhMKJp+B3zNZ;^mmPlnd^eEv_YNBO~xWiH$%ng8S64?`F zq$mkMAE7sR$AK}uj{QZs(`L+BY8j5MbB=!3)S80sP1kDeM~xP z&pM_^c#%q{wxHB4$+ZX|-JjJhczU#D431%i)4}udcy>E`#?giQtE`{Lr>rI~+(nfC zmZqbxy!zW|8McP|t78~hP$783(9VQ>VnYt?OkJjev(^%4~$) zciP{dRFTaMNwQ@fH3kh-wLp2F+KdjA0yf|m%?C;W8u-MspIAjEc&qcO%Uo(i-B=)M zC;o6vH7cQcUr75_WIC8S3OIbWhlVKOAdU@DNd87y*F}MQ?eAZgh0YIhmkd$C`=p5C z!#K^gsUHSOsAK;V-LCqOMTPs#^78^h5ql5xwSyc#?v=cX5?y+)lfTaO+;kAakjkH+ zk@;`CT?!;%;tEJ*eCq~}Kt1(&>cRlx!dsmWRWmP~+y@<#@9Zw6AEIJ%iso2mmdQMh z>$^BaJ3PPa0J$f34q;8LRb@3p$V#}gC;@pE&Gb2WNXV;T5k$p7GJ0Dr!rG~UCd~p4 z6CF&=CkuUB9HYTs=Dy;{*7EA=_h@#QPDN!duSa?eVf~s*-UW-ALg)=X(9~*G*}Gs- zQ*5-rAJEQ#Z&8@B5v=WkB0ytJOs4?+?o?0HY}uh$aaq9m7Ll+fMFhd4CiCRlw54Ez zs3c%BJyIg6S)?KB$_G;WQ%qZI!TOkS1X*N&2nWifI6X|8ZP^2xI77N*kTit zmUy^mrcL-1C8jY2A{^?gC^4Z;M(*~Ig(Kqw+WHdnCKy8Ov(U!5@hqG%KMtTSZU7T{ zR24wPqJrar!UE$A0b#+f7cA|IG^H$NoR>5tGh_j-4N?tfdv4+^qj}XZ$hDWdm)uVv zEdnRA!#V65S}AWVwX|<=BX~qt%6e>8AV(=5=Z-CclsG3i5$KvzyK-JvPip*{9FNtn zNeLgqb*FUod^RU@=}fMk#`yK}SuEei_mwl)x4IzURfg@CHyySc9Kvrc%U=jAsVz`zxvLjfyW z&px+1=ttF2tp^XD16E9el zOE_dOBa9_U*oMiAt3xV(DzGHS9Z_ZE{*X)?Z%8gRVV$=jR~n@lX&@MO1Si@6A-e2{nTdvg2)=Fr=KcPOOCPt3#x8aj7wcRv)4^1eNlX zzH*l4Ii@a?M1x?|OCo^L6mLsO{; zYx-L}$1PXHV`oZvVd|P$L?QcUs_FJZaH(m_0NXMkt8PzY&2}MWm#rV=XIqd!(jZ4S zl*&?iLO-%&kRdA&pQ1q(wBgm5Bq~T+4ue?`LO$*1 z%E^a9r>axt#U?7nsp9C$pMP!$tl8@KqY(>N$}8dOuBtM;R=-bH1i7>>gO2>ndF^fvaY-K}4 zmU5Xx&jZd@jCodQesf@3O-sJ9!AFf{V1RjiymP9AZhTN+RS9cfIaqrn`Em!Gs{wKa zgVKm8DP&QA_h`D_^aRQ_lNw!Dq0|k#P9Wt*9O&Yifn+w^1P_J#<_p|2(U#~Cp1?Z z2+mbgUIOdCGHJ{F-aOC3Y*scL;%xcb`PeZ2lC(s*gWE`9 z_={4HzuZJ(POqUR)L2CqR?dyvTNu~%0B%tYT%G&pUw3O)SHX*2Eq_}5ex1Lva@gQT zcsDS_?jMh!)ABVzwQ5>k=?JHe?scW98lD%Sl9xCQ%1}hMA|L_}3UPK{zpYl+cmHi$|-( za3xX3zJ;AHl_=kL{L;3xZw4vrL`#Nwc^!HJ!^vzaCZ2dk2^wf3wmxgyF2bY%6^WOHMXnm|7bg`-8Bc4TTb( z5pio66N8}!iTzMJVOet*LXH^wo8ja3wK;U2ea6ZU9;p6Szn?(pf-&i*19*M?1!{`vSMxM&GgS7=kMk{^(za!oI>2Gg&TIP zpGX5rJmZZNNwQE>NJ^c8(-k-kFwGq40?d^oj~Y{uJU%%Y9o%lgfCTkgq}&8ru)yfh z5a_6nTA8Av@&$)WtfK5IGVd56QKuvJJVT&D-F5U zVm%czOOaW8Jt34Hnn|It?=Zv=SdEqEIv&CanLJ5SSiS|I-5EzZEif!Dx8Z=ShH|dvHa3A?MSFSE)93WPaw!`#HYsSQe)A_|g$V8^v585eQ zrHIp%d@LyIjBO_HbDv<6_M5XH;y{qQLCON_23?a9-^B4mnSDuhh^&*xuXMW#^P;H} z2up&p03sLQ^aD$U%5tjPyet=f(PisL|3#bS(VWzgBG*9bMH47VrnP3-Y#Mgo;3LR6 z+n+T8fXFGFTTwJu#yq=efdYxG9)Q_PcL9P{zgIpy_^6oS?bWd=V50)``Kgt8OCqmZ z)hZR>?BTC=W7|!o>FQHiPQKfziq+MZ8eXlw@5|PY^#yGTYwuW{QU&N=VR#p^g$f)3HlD|E$0d0b^EZbS+jN_NrG&*L3r zQo&WgCQ5~`atu89?yUce<%Tfj4e2aNG3fF>IvF7h4ruahD75K~s|El*V;WJW=6Gr5 zWL8fCJ76F;o?4X4)QE7oT?yxipz|4LN2RcStL&FI3+Kr6w+pArG-ut{Y~SV$TK?LX z6-Qq^RZr)|Yz`4>;l!ep%vMee zc&Wu1s(hfEYP^zFs~xpZgV}6NYTA3QOAPU5+gg^Yks}m}?cUEu0o-BX1$jf+nr`2Y z$YerEJupq0jk~#a89H8ET8i!(QW7T7d=s=Jagbcolw-1?tP!4PU4RVD4F-%_Bx{E?arOmh2t5_ zE8!3YJ{E`pB$R_7P#DU|XJ;3}$P3SH&|9u2jr(+8wT!bOrtW|rYgdN|6+;Ky04=WT@7 zA2JZPW@?-?9%`l+;4*f6)}vBd$Ahf@dXe&y@(ktp6)doyVaGlX1E^M6%jJ?@$ehcV zRaH@aQX%G)R{brfO`SnNhd=n-bXZ$QV@C3e%eq3NcR|wy5ER#Z=S#_4%zJ{9!yFiP zB<8#D&)?Ij{bk*J@f7;jiP{hTZ6-{I8-%9eX|0(Q)?YoVe9e^X$NolBqC) zR{pjLZG?OV=LnyuG!W}b$#I)zo& z{5wryVI`<5r(mR|tS(%vm!2*vVrRNv0LwH4Q+(~R#Apk>T3;GXr+O3xg|Y7@sU10{ zk+IK4tYE}4`Zx`*J*L|7;$)r8(h6kGsecqh;JxLP^CjnbXCtiIXmq4MCe@iZFKeHxw})tk52-bUHAlVMdGkVg-j)?s%qhKW{pdVd#_JAq&(qilyMum0N-YT_RrN>_ zc_q(q6=U-?@TvPE+M$8z4!J}y*vf`8CCPdBXVPfw$8dM(dD~snuqH-EY}T*Ej2c>5_UxojaI^r?^g3%$ojyz9=UB+&&7CJTT2vj z)-p4h8@JN|cz+F45MQNPR7~W$R`MrXH%NhZx!H>Q3fD>@tphX@SBQ1vWhb=ikgFbV zn~u7s<%R?2mR~>i4oaWE5-eupfQb%9l-1Xnl<%^4$9a_+-)(B$ELgbHvz~u6l+k5v7TilUg;w=eM$h%pe)xAv^T>fFHq08 zx7i^tz;hgQEGEVTr}0v=c{YRFaynLVdUeItl=nDYPK?>S!WhCbf>HtVg`(XF=j$>~ zcpNQ3aS}908z5yS1q~|#*da3R^pjCa0SU4tX*7RP=kgN)$o8)U(_hevgY3Hkj%t8s z=r43Dp!_r()WKP{v9-cVAo2k)Oo5wp3;}Wj{+AIribf!d)QOFxLDA{hFb}_onK^z` zrzU5D^|>CBTYS!5`7tLW=<=Po$*0-6FmLR=EWX0u=7el+6@bJJ=Mq@O8%)VCrU%JV z&C&vNjV8wsm@gR&g_#bmQ6)60T2%U`mAA+y4#j#BxK9MUgl==pWvO6|-+^yGFN$G| zvtMrErZKB;;Q=V{_|0#EQ7-OYjX<)7RSuML&)LfblLx=5u_>!NzhXK-2LJE>;knvC zW7dOJOme|2Y7+optGiAg8`VtS2TKc!9J^XMEuzFfDcl_+r-TI5Fj+}B8%YLwryOPO zGacXyH0zQhWCriSLX_;0J1U>WQPELAzK(QF=bEFKgpL>R%&btPn1(M+S(TrzhjAi&81Z;FY!PT2fx zn9hrRj#vP}YG5W9ipHdM!TTgI2Ecn5TQ}pCOGg=K%^}JXFkKzWwn<KM1iGYy{I zyHC2qsPOViLe_zUc3C9gmIpxU74R<`Dy-W=0}qKOx1J{h3dDCfp0=!3sy z!EBgpm8gu=u$1>U#Yibdo;=MqYqDo!Obb_qD(cKAe z-^p*)G?2zrS1<4iC(C`9=2Klnp_?NtpNA6=LeGNH$fl9KEwaExa=2||(G)E-78CGU zngt;R*OD=O2l9_YehERbv+gN|$$lZ*rSGTeT6a3wRbblRPfS;I>)_~dc)ufp38sr^ zE;+7jL6d=O^TXyl<_3Em;F3tuI8MQxAkAV3ZORT8K-ZX!DC~rBDyZF2&S|) zkWyP52fJN$cK+_<&2ML}9im}RF}awDHV9J7o-K?cNR%1V;iVI{B8(C>=x>eEIdGMK zou_d3-?Hg3`&%z{9H&3nFfRSAm+1ogSf=w|-1qvhTZH z6RnxhKM#YMdr(PpAlSf%_KSWWo&x;0`2*1NijPhqx3Vz8MDnN?`0qT6AdRRuZID&K zoY=yMEV=qy)qb|<`pfk&dJ=}arkRbTJ2()S?azDe$I(LAiB}XjToR@f$n{_6ngbPH zR8RKHi}|Is%AboqLn_iRh#IOvzd!%9v$M1Fg(U6pxV!V>#fum8!hYCYkRe7giZW-f z+7F^gffFz)_rnfJ8%3E83v{M&v9--Lyt{DJhIUV z7tyGg5G}cwU+$|(QOxrF?QNa(7SVMy107Q@&91iLY5P5m7P-Uw=)7_NzWQTu6P#h( zB)|fWI+>Rd1Xh$88GeI?n3$<87~*V&>jS#z>BtAbP1@a?Kxxu8Kn~P#sy*WKUrjS=4IE)}SGRKt2#RfH2k>}9TbLFLe zZZYcCNiW7;7MvRl5FWyxyGc}S8JaGqSv;ajoI|yeqRiZJ{TyX}QeAlx(-EsXfDBxP zm+i~vxec=~1Ei!~{jDfT=2K09^l^Z@yX7kMen$pv(uJ8JJNd%=@tWKTA7Nln75;eu z+2MDL+|`;_ARk&CDl@36zXi8?jJdi+Fc`s8R^&E~x72lqXCviM8MxZ=040SAMx1d( zLKNMJ8+aup9*I+FYK(ZxlmD1dn$+Bx0oHh^`dgDg20sblKX>R@i5v+$v4@=i)~J`0 z1H`C;H!|Sl@#cV0R?vf&f|sJRyD-b{NU=qKtJmLxJOcRlAM-Tnk#QYhH z6jgicU_aaVchDJj!p^9prH^2HxE*efwpnRmY1WBDIHz#43`RGg5gkd-7^4a5s{Xcuixn5N@UA75on+afLVthq>geRn zZ|dO9kve<(>g4?V4j)YW0snpv@PkC<3AMfEnL3yKE8D`Qw8Jpkz)V zc@ywAHtjfF3srw>IEP~p*M1x?W!32|z7SYRlU1;?wVL+M6Mx~Lct0gO83AEZJjbPEmxOaR~q$XERioStt{>rLy10OV}@}-%k|e4*A7m*2|QaA;=gUbOnW9Qq^D< zb=)**DQ})Dmam>HUhPVgb#8prH9nPZ&Hbc0HRn!UJ0a?*c|ug)JRwKONP>j-zgA=k z=inF?Sk{MkNkFneyqIXr%`nopJ@rOk1t>PhXXqS7^T9R7D1&@3Zd{I!IvIVG>m5=> zOZGsrseV#VfsIXqYt5Dcvp7if4{{Ww&K{!{JL<{G7F1Gg)%|IMW>`{g+kK+CyDJ;x zlihmM%?_qPT5$ka z>uMgcrA6YraBY9#;CGlABav2ns=xJmjz*#kj@NSwLR;?-HTCGp++a=J6+FMKl$5x@ zXsi>b6GC+XO|y$EU~m8;h(P-AA_e_=>t-d+IoSvZd^qa>`NSM3j#YVzd5 znX?YB1T)t8PEhV9s%7Q9!kAY8e*~_KjR-9tFq#j92}m_w0W164Jelf5W?gzIlecYG z%-&YoaQb#0O#ry#{OhpHoxZ_{!{{o=K&+Jg+^U&a^h6t2py5jl_iKvG|fE+7ak*F^Zg!H!CJO&6ZW+lv8zroV8!o zh^R_+&tD!l*Coji*R5xENwQ@sH6H|0Wdop)LeJjBs&KI?zYQScRi+v%=Ihj6HDjmR zio?%T+F;h8x?DA9QEfKN*jr$yZry|hIurnGw0^dxQuAD;ik+#+BadvB93GrInmz#2 z^)o`J2_&T&Q(h{xo3Nr;(%&}BA0txlRO{BwAL~AJuZgyPP^rz~sRdxJVz~imq%A`U zs#m)idi?zw;2{zHP7rjo;4sr+Mw5jfA$WOYfdikP>RSziY*^o_1ShxIS}?hlS6l^b zEek0yJ3v|JSda~r`>brRH-gBsICX^=d<%eUGEVGul;`mOY!=ByXSbssb<|HCwYy^z zu-bNc-qdifqaN3_0O@B;NIVIac@c!y+{E0QWFo7C3sn0zb9bvH_Kols6-PTd*hsF{Z=*WaJKJAZ%hO8xox)vJ>? zzgcdO>;eFA*HMyKX8P<;CvV;yzjL@eu30Dfm$K$*bB*;gghbnSoS`!rv~5uRt$*Kr zaq{Z;OeS;Ng=}iMI~l53KN%`pH5shoNQy8YgAskQ7a3~9ya-n!G9n({N^*mddaUlL zd#b;+D`Qq_7un#tI&P)irb>IZ*6yCvqNK4S%cG$cLcJI`7^wvL#S*XP#s8lKu^|)k zhXmI>Ho#G>M0J&`zG|y&wI|!q)8Mq&Y681$tCpDK;4s=?Xv#yuq%PD1?JepXG_Mfd zq{hXj>jSnh0NS3nM!jCIx9fr5ujf|WT~%?W3P99` zNm=M@(kvKqGh+clCHwX4ITfyAPKcw%H%K_T(^;6);}DO5@+PIy>NaItbE&Xut2$=s{-tPuB^&Hk`o`M*O4X`6 zwzk^C-czdfu=n&|Z(|F1dFcM;UI*IR?S1g>-NB#hw|DJzR4gw%Yd`0h;GqZ^ z?gjTc>fR9k3h!BIgRNUKK&D3U5}H5(|NEcrS+%XSt}1SU)wJbx#(`vMCmsyy;vf-DjBSxb- zF*S$FYk^*WQwZv+Qc;S5(@Fq#^q>Z|VFk#72{wXbP$)`DhoT|7@QFwEoVIzU@ppd< zD}AYHUMqy2Sw(62#FP&Z%=rtZn%i~-z`4p@0FDCve@%EJ60HDt=z<$A3Ldz*brZB1 zjjOThug02TequTp8WRm_+9t(-O=$o_2T`T!}BulEJ-FbMl^PkN+k|YK;N4N zGb>GO_50PFgV9)pFynjM_e$zw`E###uU~rY>yG75XkR%o3)o`X4eUtNyf^HHmQ+CI z-hFc161h^lo?bxBapD@r?F|eZOC;#;O4-tllW3=!beMOz@NCYT(52Ccuf~4S?fS za)9H{+pEQH{Iyj zWgL&4SPlodIp~8-cNBO?Vf2-F*#YBKIN@tL=}<5M@~%xO2@5(gSyu>-*5X)oReZpl zvS!S%mE67Vgx2+krqH#Dt~98e#=r~%W+w(>AVX#k%rvKCHB9Hp2zZ$Tm3TJ1AlJ!V zcHOZ`3?v^L8}>?qIGPLlY^&W0M|3Z^=UzNb#T~b%VSuU?yER>g_XO#rR*l^naZGf} zmTJwA=Nfl97SM^3pl>n-auCr_qoTWljpzp7Pw*yWy=zUDRyxrOH_bP>d4jGAau-ti z+oj{U3xz`ccKJQ#5SDazy1fw>0I>xBUnzPKf@^5gU{VxXRYy&vU z>1)p3yTdu#1kMsN!9;c<2fT88Cwl8ed1@(k)svV;!UFowWUB`6oU9WaN?=4kt;HgS zb0R+->?ERWEnbkQ4uoU~Cr;&q;&qxkP26X0XkWPrni6&J_5>|Z7HM`ZR$dAn5;^C} zGAV*vx&|`gyBNIFSI4)r!R5tY@=I{57-m7XB!skn;^YANm)!9;AL$z%!->$FroTsV zOit%I*@gfY+aL6B+cw|+Z61yE_B(wwkAv*v@vQ`Ubb@gp_+3z!!EQiqz>T3f-ouGq z3GPB+X8?W%Y6jF4&Ll{}NN3PIHz8*%{9V{r;9d#c_iZ6LYGPzSjrQhY{M_AOJWfSq z`0n`N{Mf-0zzA3d=uhHpUIaO3ii#*ohFv>EjKAsn8y9}smCuzk1y`RX63zkn==R_k zMJ~bbr5m zP29q4E3tC ziWk^Vb#AmAJ~4yb96_#695X?_esr8zF8oS;scJ$>n}KLOt}{em0y-JQDkXy1gWydd z?1Ly5ol@qdeloB_8xRQL0EhFOLfVaRKqt{*3R0kSBPt` z%ntCP7&;cjQIMOjVJFpz?DPE)!xWmi*tcKSkZj6zjnqL7bn5^tc@VL9UuH*-4U;lw zs@y%#oI`_@su0yrb0iePgUB4ZbZk`g9h)Ez(ug`TNd@1DMDBE-UV0&usFLwP8SzCw z7>35Of$sZj>)$Sa_v8m92N;_5TK|{olGHfAc#iwT)s0h0M9e#!bDv^Wa7a$4(}2SH z6lsj1%KC|KdZr_?aF0eNA`!N_6Ai!?b<0Qv-)9-s{f@d{vy3!b`ci+ZNT;2rPdiU{ zI)8W!ws9mCzrX?v4)F9T{j-DrK&8vI9jd2KJL>69M^T^O+@UJ;^ih zLH))!2UYxc`0<+3D1p?dg(F);?-wD44mG+H8pmlU8Dnesw$Mwi{jD@9jV@u7E#y}o zv>mHqxNhXee1O`To1!T=jxgvvn4lm;;^12MybS~zpB||OmD@MD z>YL^q5<>d05i_KVd_EM|yIowM46DCIeOz|T3+m-kxp06^ECM;+=dhe zl=c~8v7w-Zy`bVWxl-9Y`8sq*z^}k|UOBAR#8a5-=0>Ovgny(92s}>#fH6L8RzxB4 z2?#z$`}=Buj|260(@~I5{x@<5o12T14MJChlO8ug0jb}_b0;H&Vdzx@ykH+Br?#DN z@VhZ>(_|@o)v}}@7R6|enEf-A{0o?a97Y-kLmg`qXWV?jh-m}zepF|v6qA7F~%tUKp zWA~X;zO@LcoX-FH@bfoj6@+)&{;Q!{QZQ|GwjjUF*8Y|rk9Al`W(Q1qvp})_ClV!KV&>P& z!|6zUsbsBz>K!%H85lHn6fl0%SpkV)bvTD?-~8inI-(}XmqE6qj>2TC9mR26$z7;p zVs)dMlY+?lxmKpZY!Cl_h<&{l3(1b+tV&pjzo@`uQGvk%kA1BHZ*-8O(r2XeFpGwe z+;*9SlPpcrdG011K>mFiEO~%*CwzH+{#Io$*1W)s1CYc>TqV#YXBe<(Lv(+4f>qyS z1&CY~=2roa`+6PB%9#o3V9B)+fKL4y;C^G)fY!5TkbDeNkiCHev?1VsIMINb*V9`rYq%E+xQG~M$8$?=Q3!%OtbX|mz^zvOFlq6Wc$JfHeW z3PvUMm2L-;?dJ^g>e|uta5Mw>yzE42)lUWpFk_rWb}c7I7)ln+;Ph>j7nleML_BH$ z3(-r`#elUNm|aihX+JrURDwfIF`&(L1N3w-K+p+O9ujioqQqp-3x4GXb;6lBz$k-L z8S*Z5qA2m+RBunuPX0Y!!;w_HhEHDU1oBFNmKgDhuEZaSPNBxA|0h*X#ul7&FI7n)D>U|9)Rxq-=gB>e5j&4pTi~X43ZTtNqUGDk~9Jgx?Jcu zR#V+DgZ`F3D51bz2uvsL@q78a_yk5fju1*S)zszxE<~d7VU^YyYak%ue z?WL1|(}sALX)qgzj0yzoWw9|JCXEhW0#Hwq*HIitxuqs1Wtvy)1RW9Vb}xUm$qEe# zF{uZUMD>9D-JYl1b3w!p8D_4wuL6hq1h$~gbV|s#iRE4kecFQcphu)8F&FnSU<*>p ze1Rz)L=Yy6f%Y8asL2wx*FP>R6umf-W&gAq{5VH;4T1J~{sVB9IS5`Oj*(FdhM zcQxP}NUz+kkb8!g`|8#QCB9t=N2uZ8^y`DSZ@q9y*1?46M~@Foqt!9)#<1Fh%L_oH zOL#kuuI3p~Xb3ls_IxOpEKF)xOpvP8B%Nm!)UD0)xdyjS6a5m5Eevua{^5C9SFH?I zPznU2(LsWx7VlZaXiD~-ei7QGuh^P6;qqRZ;zl@6seSDpfo2cXlO1)R{@UHy*`YHS zfxB;J@3_hY)*jK&F#CJRjxzHk#8o^nStQ`Wj_~u(%GuJ`gW1j4IB=Plx8&ppXZ1WT zP^1RI_=)yPMDLO@M>K|G7chs;nQMfxdj?F?1dQhvnvFf{u`9^0zY%G!bnzNKRYMF- zQ4Nml(J~e()xZb5;kX!MjhfDaB~BP>j8Yhw&+M};Y!Au#-aw*4V(c1I$@1Ue<&VB} zV^M;Weq%~Bk1{YXCMl)_MoV4J^?NeL&Bk3ag$y|Q9ucqzq*<^xqog~IIb|8@QTUN4 ztZd(4UZjvD8)FXu@eFkg!YDM@igf_es`;$90&n*$;=*5qMvW-!{HB`R$4c@BH7vsN zcOUxS4e$2xuzny9-Q5L$|B8PfnixmCkDyk0!@Uk>^*d1ter+l^bPmATj9t)H?nh>9 zmolK9`W0}EDq-f#dEy})DL*X2ArcV&ILKyRnqBGh0<8yawO5n8nCH^+YCmj?Fto%{ zJpP8x@ht3br4vXvnPEa*?;GWE_|&=O3y4djWFZ3~25(5=3)(L$D6%Ovx}J)ScR09! z0KPy%ILJ*wDM%Z@21IzMLNx-U3)BNOR1Z|B9;nf#u-x6W;+tbM06j){ZWYcW%nD*;7bj^7=G%cQsQggP4 z5(VLp(4q-L?aLk0A1dt>2n|jond3WfdN9kLQuy#37&MBskIF2Xtx#9kg}@+TF32>| z-Qlte1f2IG>RW3zVm~qI>9C~dqm`6hloq7pKkrez2=5(+2iy+xeDS;2cxBP?906DsjgbOch#dvX;RDOYitu? z3us#sUj%#p!vycWY9MGaP$|eg+h0CmkZN zY>1&58%@a%ok0Pq0dXGDPIX|)2uGCKC!1fe*39Qa zLKMkDJx>5o5}U!N)oEfI&Pb-1Qc~T+x9?S$W~fksFFL4>-(2xUF!jjND=>Pfo(__C zo8;eYV^cf09?K(V7OPVi}6c%Zf@qV-1K2=Fw1J0!P5{0+Ns~Uf zw^YQRB9-NH4-FJ%!-WgQ9k>YnBuN+h_C6}Q1==OG7uWA0>)`%-B>rzzqzr4sd>@U- zgLDu5gbrYK)`YGVcFnQLdBXSa^IbE!e;*bIgufsF^(8}5?lN!qNx7*BleR*_Ax&0d zB3E)P6UqFG6uDe{*%H|+(i1;X9$u0pSsrC-WgLR!r#76uwCK3`wT+dZICNpABGMqT z6*DORG%P)yZ#R%#7qrBrM`h&+<7|}N5MrTvAx^KNuxzXJx5g(z5Tr~kAGe-;F3Ygs z6`_!iXRhd^6|}K42YWaN≻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 UtWfK<_2tX|4{18$Of|L!0IW6At^fc4 literal 0 HcmV?d00001 diff --git a/integration-tests/websocket/gren_packages/gren_lang_url__6_0_0.pkg.gz b/integration-tests/websocket/gren_packages/gren_lang_url__6_0_0.pkg.gz new file mode 100644 index 0000000000000000000000000000000000000000..8f624b4a3b34b245a05ac7f3e4f9fc82718fdc51 GIT binary patch literal 9240 zcmV+zBwTVzjP;e42Kar#6D^ruN=_J-ogN+RiA5jG^ynZ{@`!l!P~bu_O~$Ne#W|bgYFUy9qN?obWZwg(A}6t+|~J*a5HqRk^v-H7tPre-t$w|H-&!BawHh*F) z;fk&lq0l%fgm|Y&k-uX^&vUg6eN844ppa{v{YDpM!A7ZAY(KgGaBD0snH=jmlk6!7 zhMgIclIaaD6jC-x-oAZ1CiX}0-#BePCg=TAS!|kz0kLea1;K?v34l&W z&b1)yl5xWGlu43NO-C@qgBcF+igFE=QX_F!VkwEZ;>f>4lFnKBj)7OzE-3eIvjfrPQt(xO-mNv=sI zm?B(gLerF$2F1xzF`4t3m=MKqPLWEAF=&x3XkH|oW|?G4G0W%^Ef#+YxOy@&D-u*8 zp+!zn0+1bGtoNj}a)4}mkzhp1TqsSpNhElL)NjeQ75)dnVE7tXForao=pbWZW_Yuu z@M9nglqJ_sL@{~EG6Jg-XwK3l@UY;V87&|r5bbGzqr-#jke6DQ<7t`I5g= z$SK&97G#!R03u?k6Mi+PSV_&r6%m}MR5HeKSrA;#PArq8*Cw_eK75e$4D9A60mbGY zqAA{Sgz$xSns-Rj3mOa>tEPPi8z#_KR0xv~^~beZh6Q$W&{J_RfVT zUH6pKWN^}}x8ejl$#O-DLR_(|yS0rJ`6{pvnqz_~YtdBRTK4J%l=s;rGTK1cMK1<~ z@)b5eO89{$e}!pKWsz$T%sIbMWLb{Z>3^E)C8##?mNS;AwVCnTLyNmI?Fx?_|< zKuM>a$mS1L?VuqVKqxJZO3Y^#cBC|wTKxhxk)MgAhmd;CEDNP`u_$+Enn|yAQ0eq- zBbgNCi(D#A9z)H6VLes=X5BrhTXdyb`g&69%QWEE8vNhVT4e$Nomwf@Qp?Mc782%a ze^KgH4Uu5YSA#1`O23`lOpw{MDU{@~tNIz3}EA=wKj zN&FyP1*N5v0>ns}{N|0|cDIG>{hZt*-wbe&17EJ`J7&srKP{G$wGQG52<@Zw6Vf$K z5TsvzTAto|TF{x%oD(sX`Pmw>>*nRQ^HdvlNhMvVKWMJTx1Os* z(B*!xriI#&Z#rLbQ_YDoa}c$DvHS2(z04VWmT>jr zz~+OWaq-8B!_M*;(~v+~9PsM|KtQuD<|nbLtX>-Q>!9tl?xc4U%#*x}*;|^f=ED4K zh3K4(8_dWJW`)Ev6H_L#HDJ?yV%$K{=GyGHDVZ3=y{AHWvkg2Iet`xhOXtBTnBCQY z*<@Z3#}-RjjQk9Q^Nto;Jdc5e7f!k{u-ZVCg(c__AcSVwA$U8nQ=Aj@Uy^(G@D)t) zz&*f}Sa;wLSW&WB{u|4zG6fKvoDP@`3sd>|;D0xOz&{hqHb+DUxBxKxh4v@2LJUb) zl{vaKSBkCW$c5{mzC>Kh_wHG-b?=^Atzdk^7_mx)f%BM64_^ORt&Y2QPhndYI5cB_Cds+d zjAnQ4-X$;Wmd^S6=v0Yfso7+h@QGvv)%hix3^80aNu^Ngjm$4{J|VP_jAkp633P{p zGg&OCHcrSpWD+^{Ckf9L5xiItdP(zwP76j9)TElQ-?U^4R;)<6R9Yiz8_?q=xEHCD;mCO}^Vst6agiJWBvz)1saVUfv zX7*dIv;q$T;1+rJGJ#$N!z(}vYRNDnFPUUOhJ>Tv!TJD{cR6fs0<-Xp%8cwD?;RWv z=A$c69!&v=8t@aD4ZnS8hXY?GGj0ZW_-gAuJhsDr_uW@jo8Nu+)z+g*=%F)#Z@MuK z$#kj7Ri43A8^<)uhUE0*#paW1M*dH3G+%8!>>9~`{XY-1xAoQTRu|mg|NZ}a!2PYS zo@{-!yJyDiytBYWiY^$LvN^rXg#>5MztBs1oXWh^|3{ItNi()MIY0ZWI`?xvG~NVs zRX~sy`8i`MmHE^-@fQN$Xz(pLo-J1748ld{XKL#lR~^PtVEz-%g@$&}U2)2D#K%Hk z0eo@)wqdo!f{zueXR+tB^>a%5$J|8&iKu((KiV$vBxTqum`~MML|q>m{?tmb`k@j15SS_N|{rQ z>%xfDN`P9g8tIgu3I7BEx)(*iYQnmbTUwBUQ82cU>B<_NM|C-ejzN^cC4{FnyW@qJ zSqTh0tnH!+ zf9mSb38@$ygjffBC948>6lNwYy8;%;QCFdjGqI}bDfxvJ5SyPf#jK}PkrXrvm`(78 z@(bWZfq}(@=Ul@A&>;3GnJ#%chj`NzJhmW`7Ia1C^pbg@4I7ea!4_&nUI^)C*H3N( z6*n)j+gX#DFy3o2T@igH$ejH~viu^~s-vMl|E1{-Z)@n~{-4)3w71o4Xike6;0=sx z)r&_sR5G_y9L+S5n&SA9QoPxDBq}XEy3FM|+&Y2jGB|2UNns9)ds)k6aVF@HIQw%* z0?Tt~h1`%F38AYz7xaW3I^g}?DjxaHp@SWxH+CI+Up)oCFvprP8%_~BNg zMSvVX-0B*g6xlMxP~kZ_a}lUL={xcZ4}NAaND@1136`$gOjID(b4HUNqS`m+hXy-D&3qPi_#|$) zdp8rH)bHNyW(+^n+cSo*x|T6M6F;CF+Y~6s06U+T69~BW1eK)MG7TYZ+mc!;=v(!3 zgO*!cbRMks%py>A&bTFr4Cp<>*{J*gr>fX<7^p<#FQXPKsII}SQ>td%RbInhI3be# zx@6#|f$fSa@{74HR^a2&GEcSf@|di#T(3q64{Y6`$sz>f&$U*HB2QQ6XG)ak1XBQ+ zP3}^RqY?S?*?&0Vl-H=Pe|&M4SV3)l-l3N?p) zVdm}lyXc&fcVQa`K|GsnaV6wCU}kc}XlA?+W_yR`t3}T`Ev$PX6;fIE0-$qC%k|z0>29H?N#EQi&9k^0@+r0w%|3h%t;HAYy(E&?3#-O)79$hT;%h-?2uij zGe3|cCnZ0aC1a^lWq3z|&blE`e4{Y;J4Q<8RLw1i62>#H6>S~YXDBDu8cOiXy`Sxm&E!_AribP(pKEYZb#aKEovr{kfe?!IY{( zfY98DIJCPt;xHP68zS%t^Qf`S!lj^!ZbD0rCR-~xod z?PAHfjwaS7BGhNqjr(&8?wqHDg*A&}tcp*aqMuIeECPQvIr z)2U|;r&4aZR$G_o`bO3)$?lbSloR_x|_e9|o|b%OMe*y_tcZwHyYPE5JMj7@v$Z4D1n; zVkT8}-t-^-`9I7*pDR+zd_iSitkA^)Gl9n`Ff>f{d;+iZ$a(A!WxmKkG>-fYu^H)`ckZGX!g-69eghuTKIa+c%7(X`4SI*sUo&C@AR0n6y;c*Z~0)jHg zE)0vJWAvaK?TDKY2N+1D1p)%r?62Y)6fyU%{ww-E>qd0rAcPUNwn5~R(7htm}n zY^Di$Vkkm(2zrfj!Cz7NE=&vCJvxp|Va%1powzY`o!ft$QoT8655!6H?8MLon>tWqdz@b5tJP7|M+VE^+`Zi#2ZhMn*Emz6yxf{ zxOtF>>h;x_!YvPD2!8)STnr!I1xM1xQ!&NW8v;$mGz59LHEw`Z?p1(rA^;)0yCJfd ztEqG56&(=j0dQvQemdzLpBzD!Vb31^yD1qsnH+Gvh6r^}NQ`}>w2An`t(zci3`vKv zH6snWxpM+|TwkBQdbWR*^rB?&o8g?#AO~Xs*E~Q94!Ou7Y0yw~1j#_)jw=>;z>XGI zpqSA5v&ZGXAO8UUI}`vylCud)NqB`n2JHzJu%x@N^X1-|p?p+`d_VpH)_XWPRy=f3 zSWCvL;vh5o#8JRe#d)PEq(9Qs62;o}DzfUmOabPrjzHMgkPn}D2@!|`xAx7__GXVa z%Nl1`DnYXT$O>4P?g=RXDOM2#W>fv9A!Q^!d0`u9oR)AM?~wi()I3K^%)G28<%be) z$Jvk75rl85;$}@N4cDHHuo?J`GPeaXf8)}R(Q`nb@PtW7y?W%!tgVPcN~0xBikQvd zHF>qTBp^AK&2r96Gy^uL6I;h~{+ea&iap<7zr0=g@6wzVtp-qK=%Nz+```WpbMf|$ zkI5`AOhR#NQ(}iot&y@0qB*|3j}K15i#Nn1I%sSI-V!AH4ptX{tKC zib4Vv;$m6oyo6j=>r@|oF_rh=QDl}qc+{d2J9HwJV?XO{mZs9gv7yJ~8bM)!B80qS z1yE8x66bm~#p5ec%xVTFyb4W43ptbGqGnRUs}{&sY#fa3uoVVXKkRBMbYUY=iH|8s zBZDAy@O}FV<-a*eIf!54N%BEctSH=UIG*&*m|dQ|D5%!P>0_&te$YO_nnAwciKJY? zg>Q~Wtpf87s%nR`Cl>as+k3p8A@Jkpu#EwJ_2=CHV3%hHSt}DXSi1s`*a2Qg%wT?^ zKMx1fWPV!^bnC_)Ybl3}>kfQb4$_Y%@QaoD--{_D(=!3?Ruip51t6F$Znhch_@?i0R5NV zL0~-t0VsIG$!#cAa~pW9N!?`-piCl%L4A;0*bb_-?5aZ~#>q@>xE^#A_Xj z2NlbQBN1db?2r>t=4s%KAn59Q2HNYOveG_~Q{bmMm|KHZSR7OG&6WjaRh+ z@oO6Qmw|nRi`J^edPNfd*QVHG)1d)vxn!GK4b{Yi6BNF8Ey}Re ztX&)sF5D6vUu|o%wmK`5I%a3rPYn{9t#dGTifn7*gJo!QQuC&Bipd%N3O6p8Z4!w7 z+MSgq91z`HwTZ9%{guX-0B5um+(Vi0vq{T!SEka2J4pz0JgQy4RUN1>muF%e3inG~ z$OSFD;vrFnD&`tRXOC%?p&JwT7BfaQTqCGO!Z9e13~|M#pgSvv%e7&UZe8b@q#yd= zJri8ZxYsgms}8Gr4SD*hO(GciIQ6eV#$1zeJC)fQwvE`)52L!+II8vZ*BvMH+iLZr zz1v`ozUc6(*A!9fLa71!A>9Oc9F0D*uES7OxCr|uJn1b&GaG`D*iJ9#f>?Fca!*8_H}2STNcv2Qwz#Jb*pAwdHry$m?j2Z zl-X@_sPbMM?PA!oEtkzS0_g(oc3l^VH>Q}<%h`-j^r~PY!N$a{ft2!u`#Fae;8$V1 z>u|TN7UcPxSA-SJB+`Ov%Q<#>!A9f|va~CAJ4pRIEiRbn5d8P5q3_%;HPvIvF;SpW zG*~s#Gg;IaZjF|ZBT`}(POt~@`M7z;xX$%z5b6iKy%cJ9iD`e^Y9w@%9=r!1M|g+n zAg;C%w0oOs4Smq4)~M6eAyu8GdaLWhz{)pQYx)}t`}M4DR~AJWf|O@FL;j5xFK`|F zZ&<4q7l2y3NYRZR>?Yi0sdCBvI=zE8+mupV=9!6R`OC9TWnu2mGIvRYcivpmBG2%c z8hHUX1(*%l1e=^d`^sc*T3KKZb$~n`Ht)GGcL=$eYSCsL+AT=Laal&#pG`6fQ;8I2 z6AfCoM=t^A%CSN|02{s#OYZ%apUGu{9YwpKz%_D1Q&Ni~JD6%I1h%v*4)ZjKi&&lF zWnST3wB~-Ks+;yOO`ROX1-6!WZCVJ5t8@fX`8k`>Wubj#>p!=IT4LfzlO}VH$j8XUY3vC?LRotyf!}vGjQDpCr0GJZkX8u?q8bZJl7L* zwVB|r>1BRlz`+1PlZ1gi4%T?C&3$4&Av9Q>=^QRiF;@8}e+vSy-oOR39mmf>UKzRT$l;>*;B;IPpMQ&H$hF}J>dnspZOInmlD)_|EzGUB zZCKleyz+Uwt^o&asL?3yx>2CIKcJ(%EHgNwbEvC(3yROIVQKJSB*~s zuV=L4w;Lxg*QMV^RnZS=e%7wgG%vde16_>Zb1SQe1i#`Y-T@i>W}?U>-gOERE2!xY z_&CmlGaA3jl||my*uD{J*CFnkbJLUoaD{elEIUE=mOpD9l&sSZi6Fy4Az7f6k3 zmx!ry1FH{))3Es9z(@?%-HnL=$N5m*nLy1BeW#bLxl?w|mt_Wm7!RG%EISeY$cuW( ziqgf#!^Xok@808lG%DM`zu{*)fuWx9uiC4dfg}GKz#3DVm)%T_*q>o5@ye!Hu?E<; zkg&E9U{E{B;}0FQ9qFk~B*bTXey;pN?nW)U9)jZ%K^lJmPiTA&0OxD{f}xEun=cZ! z4cLf;8=OR9e7T4Z9O2e*LCY=t_W}O<5dVFI|9*@AKE{8aB>y;gblt<3IC#`JZe%~` z;?El$aO31F@~Nb9HS}%?(?dYjNpN;h*~5bqScj4kM$@_T?|PR6Tt#7BzQ;;j7Ky6XH`o%gxFdZ`RYv2uoF!1oJF*x!Ko6l%CxDpy`&rHQ!42`^^Wf^=g z@s>>8a_Z?e2bDTgG%fY(boXSx<`9wb(8dDf=TRh8B3tC()xpWZzwHMDd5QZof5;{X z6nNKCBeAk2>e$GuY{obICfA>RrCLQQs=AIdjlbFDYP2MPv>w`dI+c{C^Br^6D~7MT zio?--ZGUItLPfCQMXzo5!-N4Bzv->gEheVjNpxQ=UV;QpF3G48?%tS|L--wJ4-Sxr zr*7cchTx$b-ON#zckn*=R)x~C<#QF$heWRVn1JJ?1ADx{v60pOVZ0nas#nsBoTE&z z%p;jv7P?CNN1sHU>kho94~sp+g=e8dUNZ}+&M#XvfNvhx0KR!#gMiylyIDv`y@Qrj z7lavxS=J9eD{DcMA-xlB`6@VP0WRrL`$Yh4)OEK2d;;0I8C9}|$0*Tq z30Guo&M17=%Y)YQFP;aMZpNm|iw46z6QUS|0G{PwC?x6OTR5MN78~d{W2l`$GzW$O z(VP>xWP>5R!2!Xm7w}_1&O_JkQ*3MpYPE?uXr&Ho5NyI?^L5~Fn)0*3ss{4@4Ip8d zjj^t26F|QXFpQT09vZ?_G)SkJ+_xUZZ_tehrUC3ea>1$e0d6Nx?4mF+f2tA=Ulgpq zLO=^8>?igXw1maApydDs5nYi9rUy5D@pgW2yc0-s!n@ym#1;-Kf(o{I3&;;PZMudasrxFtNR!Ik4{R zT1N)0@4urx1#OA|*i>LeSn<^^`!kCH90|}9PW{Du*)?(?yveO$Qq_;hc`Fsq#+eiq zGdkb^n;knOXLug&yuSf4Bn+;B_ZuhxFX69GHME}|fXtPQEUm2_B%|QDRA4yBM?nU4 z-f#EiMthYwhS`tl%76cP$%|$BZbcZ!k79t$7t8y}Q%KDN4FZ22PrMd|TVWuAo^U8p z)u(C+g#^x&*=1mIJn5>^t_WU9J0r9L^@`=qLv=Ru(7L~7d7+tnMaxLAMMwz7!(yVhMvI2~KI`+hSitb0zjs!4%=jK$qKJ;84SY1?Q{SoJVnH+$i-u~P^~ zg1Y8%E*ZPUY<`VqI8~{+7Iu>G+U$~RC%45a`~ve0*W3jk1$!eixO=O{3)n9f!40Ag zDe{3lXYS!s*yh2F*@pD3feV98dPKljyky5Gds#?iQUur-7~gwrA6F{=jP&954v$rD z7~mVF68F*E4go7-kT?OvGW>&>;nUl3ecHYP9$dEqs)zJbmjmgiwRK@Jdf0+d_UUM4 z?I=FH6^hkE_Pg;V{VYU0MEy}0>g@C2&)RA6=+kLYJ+RM57}96$tnuwy`piB*k!I}# z`}Q*tta_Mz0qT+dtc%eeUr)%3FH6mfcFI2f5|phTF1{q0N&lijZ*mZLx90i9+??Lx zguxYFL-Vl{%H8!Lw95N$>TbvgT7}0VS}xOwY|p9^l^2d2OP+>jAiS~F|Ng@uEcI2O z`(5*aI_}4t&S}ow)h7a?0Shp_%>_s3qcFBS+9!aD$D%Tb+mijwfZ*e!IPSWg*HO&m uX44dWazh@U|8a~yZ)f@^U!3Wyhxs3}0!V-EmO}62$NvVsk)#V9j{pFs7tVqJ literal 0 HcmV?d00001 diff --git a/integration-tests/websocket/package-lock.json b/integration-tests/websocket/package-lock.json new file mode 100644 index 0000000..f5f371d --- /dev/null +++ b/integration-tests/websocket/package-lock.json @@ -0,0 +1,875 @@ +{ + "name": "websocket", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "mocha": "^10.2.0", + "ws": "^8.0.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "license": "ISC" + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/integration-tests/websocket/package.json b/integration-tests/websocket/package.json new file mode 100644 index 0000000..ca4d679 --- /dev/null +++ b/integration-tests/websocket/package.json @@ -0,0 +1,9 @@ +{ + "scripts": { + "test": "mocha --require test/fixtures.mjs" + }, + "dependencies": { + "mocha": "^10.2.0", + "ws": "^8.0.0" + } +} diff --git a/integration-tests/websocket/src/Main.gren b/integration-tests/websocket/src/Main.gren new file mode 100644 index 0000000..ae2262b --- /dev/null +++ b/integration-tests/websocket/src/Main.gren @@ -0,0 +1,150 @@ +module Main exposing (main) + +import Node exposing (Environment, Program) +import Init +import Bytes exposing (Bytes) +import Dict exposing (Dict) +import Stream +import WebSocketServer +import WebSocketServer.Connection as WsConn +import Task exposing (Task) + + +main : Program Model Msg +main = + Node.defineProgram + { init = init + , update = update + , subscriptions = subscriptions + } + + +type alias Model = + { stdout : Stream.Writable Bytes + , stderr : Stream.Writable Bytes + , server : Maybe WebSocketServer.Server + , connections : Dict Int WebSocketServer.Connection + } + + +type Msg + = ServerCreated (Result WebSocketServer.ServerError WebSocketServer.Server) + | ClientConnected WebSocketServer.Connection + | MessageReceived { connection : WebSocketServer.Connection, message : WebSocketServer.Message } + | ClientDisconnected { connection : WebSocketServer.Connection, reason : WebSocketServer.CloseReason } + | ClientError { connection : WebSocketServer.Connection, error : String } + | SendResult (Result WsConn.Error {}) + + +init : Environment -> Init.Task { model : Model, command : Cmd Msg } +init env = + Init.await WebSocketServer.initialize <| \wsPermission -> + Node.startProgram + { model = + { stdout = env.stdout + , stderr = env.stderr + , server = Nothing + , connections = Dict.empty + } + , command = + WebSocketServer.createServer wsPermission { host = "127.0.0.1", port_ = 8085 } + |> Task.attempt ServerCreated + } + + +update : Msg -> Model -> { model : Model, command : Cmd Msg } +update msg model = + when msg is + ServerCreated result -> + when result is + Ok server -> + { model = { model | server = Just server } + , command = + Stream.writeLineAsBytes "WebSocket server started on port 8085" model.stdout + |> Task.map (\_ -> {}) + |> Task.onError (\_ -> Task.succeed {}) + |> Task.execute + } + + Err (WebSocketServer.ServerError { code, message }) -> + { model = model + , command = + Stream.writeLineAsBytes ("Server error: " ++ code ++ " " ++ message) model.stderr + |> Task.map (\_ -> {}) + |> Task.onError (\_ -> Task.succeed {}) + |> Task.execute + } + + ClientConnected connection -> + let + connId = + WebSocketServer.connectionIdToInt (WebSocketServer.connectionId connection) + in + { model = + { model + | connections = Dict.set connId connection model.connections + } + , command = + WsConn.send connection "welcome" + |> Task.attempt SendResult + } + + MessageReceived { connection, message } -> + when message is + WebSocketServer.TextMessage text -> + if text == "please-close" then + { model = model + , command = + WsConn.close connection 1000 "server-initiated-close" + |> Task.attempt SendResult + } + else + { model = model + , command = + WsConn.send connection ("echo:" ++ text) + |> Task.attempt SendResult + } + + WebSocketServer.BinaryMessage bytes -> + { model = model + , command = + WsConn.sendBytes connection bytes + |> Task.attempt SendResult + } + + ClientDisconnected { connection } -> + let + connId = + WebSocketServer.connectionIdToInt (WebSocketServer.connectionId connection) + in + { model = + { model + | connections = Dict.remove connId model.connections + } + , command = Cmd.none + } + + ClientError _ -> + { model = model + , command = Cmd.none + } + + SendResult _ -> + { model = model + , command = Cmd.none + } + + +subscriptions : Model -> Sub Msg +subscriptions model = + when model.server is + Just server -> + Sub.batch + [ WebSocketServer.onConnection server ClientConnected + , WebSocketServer.onMessage server (\conn msg -> MessageReceived { connection = conn, message = msg }) + , WebSocketServer.onClose server (\conn reason -> ClientDisconnected { connection = conn, reason = reason }) + , WebSocketServer.onError server (\conn err -> ClientError { connection = conn, error = err }) + ] + + Nothing -> + Sub.none diff --git a/integration-tests/websocket/test/fixtures.mjs b/integration-tests/websocket/test/fixtures.mjs new file mode 100644 index 0000000..92a0cba --- /dev/null +++ b/integration-tests/websocket/test/fixtures.mjs @@ -0,0 +1,31 @@ +import * as path from "node:path"; +import * as childProc from "node:child_process"; + +let proc; + +export function mochaGlobalSetup() { + const appPath = path.resolve(import.meta.dirname, "../app"); + proc = childProc.fork(appPath, [], { silent: true }); + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + reject(new Error("Server did not start within 5000ms")); + }, 5000); + + proc.stdout.on("data", (data) => { + if (data.toString().includes("WebSocket server started")) { + clearTimeout(timeout); + // Keep draining stdout/stderr + proc.stdout.resume(); + proc.stderr.resume(); + resolve(); + } + }); + + proc.stderr.resume(); + }); +} + +export function mochaGlobalTeardown() { + proc.kill(); +} diff --git a/integration-tests/websocket/test/requests.mjs b/integration-tests/websocket/test/requests.mjs new file mode 100644 index 0000000..230df0e --- /dev/null +++ b/integration-tests/websocket/test/requests.mjs @@ -0,0 +1,269 @@ +import WebSocket from "ws"; +import * as assert from "node:assert"; + +const url = "ws://127.0.0.1:8085"; + +// Buffer messages from the moment the WebSocket is created. +// The ws library can emit "message" in the same event-loop tick as "open" +// (when the server response and the first data frame arrive in one TCP read), +// so any listener added after `await connect()` may miss early messages. +function connect() { + return new Promise((resolve, reject) => { + const ws = new WebSocket(url); + const messageQueue = []; + const waitingForMessageQueue = []; + + ws.on("message", (data, isBinary) => { + const entry = { data, isBinary }; + if (waitingForMessageQueue.length > 0) { + waitingForMessageQueue.shift()(entry); + } else { + messageQueue.push(entry); + } + }); + + ws._takeMessage = function () { + return new Promise((resolve) => { + if (messageQueue.length > 0) { + resolve(messageQueue.shift()); + } else { + waitingForMessageQueue.push(resolve); + } + }); + }; + + ws.on("open", () => resolve(ws)); + ws.on("error", reject); + }); +} + +async function waitForMessage(ws) { + const { data } = await ws._takeMessage(); + return data.toString(); +} + +async function waitForRawMessage(ws) { + return ws._takeMessage(); +} + +function waitForClose(ws) { + return new Promise((resolve) => { + ws.once("close", (code, reason) => { + resolve({ code, reason: reason.toString() }); + }); + }); +} + +function closeConnection(ws) { + return new Promise((resolve) => { + ws.on("close", () => resolve()); + ws.close(); + }); +} + +describe("WebSocket Server", function () { + this.timeout(10000); + it("sends welcome message on connection", async () => { + const ws = await connect(); + const msg = await waitForMessage(ws); + + assert.equal(msg, "welcome"); + + await closeConnection(ws); + }); + + it("echoes text messages", async () => { + const ws = await connect(); + + // Consume the welcome message + await waitForMessage(ws); + + ws.send("hello"); + const echo = await waitForMessage(ws); + + assert.equal(echo, "echo:hello"); + + await closeConnection(ws); + }); + + it("echoes multiple messages", async () => { + const ws = await connect(); + + // Consume the welcome message + await waitForMessage(ws); + + ws.send("first"); + const echo1 = await waitForMessage(ws); + assert.equal(echo1, "echo:first"); + + ws.send("second"); + const echo2 = await waitForMessage(ws); + assert.equal(echo2, "echo:second"); + + await closeConnection(ws); + }); + + it("echoes binary messages", async () => { + const ws = await connect(); + + // Consume the welcome message + await waitForMessage(ws); + + const binaryData = Buffer.from([1, 2, 3, 4, 5]); + ws.send(binaryData); + + const echoed = await waitForRawMessage(ws); + + assert.ok(echoed.isBinary, "Expected binary message"); + assert.deepEqual(Buffer.from(echoed.data), binaryData); + + await closeConnection(ws); + }); + + it("handles unicode text messages", async () => { + const ws = await connect(); + + // Consume the welcome message + await waitForMessage(ws); + + ws.send("snow ❄ flake"); + const echo = await waitForMessage(ws); + + assert.equal(echo, "echo:snow ❄ flake"); + + await closeConnection(ws); + }); + + it("supports multiple concurrent connections", async () => { + const ws1 = await connect(); + // Consume welcome for ws1 before opening ws2 + await waitForMessage(ws1); + + const ws2 = await connect(); + // Consume welcome for ws2 + await waitForMessage(ws2); + + ws1.send("from-client-1"); + const echo1 = await waitForMessage(ws1); + assert.equal(echo1, "echo:from-client-1"); + + ws2.send("from-client-2"); + const echo2 = await waitForMessage(ws2); + assert.equal(echo2, "echo:from-client-2"); + + await closeConnection(ws1); + await closeConnection(ws2); + }); + + it("client can close connection", async () => { + // This test verifies that the server handles client disconnection gracefully + const ws = await connect(); + + // Consume the welcome message + await waitForMessage(ws); + + const closePromise = waitForClose(ws); + ws.close(1000, "client closing"); + + const { code } = await closePromise; + assert.equal(code, 1000); + }); + + it("server initiates close when receiving please-close", async () => { + const ws = await connect(); + + // Consume the welcome message + await waitForMessage(ws); + + // Set up close listener before sending the trigger message + const closePromise = waitForClose(ws); + + ws.send("please-close"); + + const { code, reason } = await closePromise; + assert.equal(code, 1000); + assert.equal(reason, "server-initiated-close"); + }); + + it("echoes a large text message", async () => { + const ws = await connect(); + + // Consume the welcome message + await waitForMessage(ws); + + // Create a 100KB message + const largeMessage = "A".repeat(100 * 1024); + ws.send(largeMessage); + const echo = await waitForMessage(ws); + + assert.equal(echo, "echo:" + largeMessage); + + await closeConnection(ws); + }); + + it("echoes rapid messages in order", async () => { + const ws = await connect(); + + // Consume the welcome message + await waitForMessage(ws); + + const messageCount = 50; + + // Send all messages rapidly without waiting for echoes + for (let i = 0; i < messageCount; i++) { + ws.send("msg-" + i); + } + + // Collect all echoes via the message queue + const received = []; + for (let i = 0; i < messageCount; i++) { + received.push(await waitForMessage(ws)); + } + + // Verify all messages were echoed in order + assert.equal(received.length, messageCount); + for (let i = 0; i < messageCount; i++) { + assert.equal(received[i], "echo:msg-" + i); + } + + await closeConnection(ws); + }); + + it("accepts a new connection after a previous one was closed", async () => { + // First connection + const ws1 = await connect(); + const welcome1 = await waitForMessage(ws1); + assert.equal(welcome1, "welcome"); + + ws1.send("first-connection"); + const echo1 = await waitForMessage(ws1); + assert.equal(echo1, "echo:first-connection"); + + await closeConnection(ws1); + + // Second connection after close + const ws2 = await connect(); + const welcome2 = await waitForMessage(ws2); + assert.equal(welcome2, "welcome"); + + ws2.send("second-connection"); + const echo2 = await waitForMessage(ws2); + assert.equal(echo2, "echo:second-connection"); + + await closeConnection(ws2); + }); + + it("echoes an empty string message", async () => { + const ws = await connect(); + + // Consume the welcome message + await waitForMessage(ws); + + ws.send(""); + const echo = await waitForMessage(ws); + + assert.equal(echo, "echo:"); + + await closeConnection(ws); + }); +}); diff --git a/src/Gren/Kernel/WebSocketServer.js b/src/Gren/Kernel/WebSocketServer.js new file mode 100644 index 0000000..8a8db5d --- /dev/null +++ b/src/Gren/Kernel/WebSocketServer.js @@ -0,0 +1,206 @@ +/* + +import Gren.Kernel.Scheduler exposing (binding, succeed, fail, rawSpawn) +import WebSocketServer exposing (ServerError, TextMessage, BinaryMessage) +import WebSocketServer.Connection as WsConn exposing (Error) +import Platform exposing (sendToApp) + +*/ + +var _WebSocketServer_createServer = F2(function (host, port) { + return __Scheduler_binding(function (callback) { + var WebSocket = require("ws"); + var wss = new WebSocket.Server({ host: host, port: port }); + + wss.on("error", function (e) { + callback( + __Scheduler_fail( + __WebSocketServer_ServerError({ + __$code: e.code || "UNKNOWN", + __$message: e.message, + }), + ), + ); + }); + + wss.on("listening", function () { + callback(__Scheduler_succeed(wss)); + }); + }); +}); + +var _WebSocketServer_nextConnectionId = 0; + +// Initialize the handler storage on a wss object and attach the wss-level +// "connection" listener exactly once. Per-ws handlers delegate to the current +// handler references stored on the wss object, so when onEffects updates the +// handlers, existing long-lived connections automatically use the new handlers. +function _WebSocketServer_ensureListenersAttached(wss) { + if (wss.__grenListenersAttached) { + return; + } + wss.__grenListenersAttached = true; + wss.__grenConnectionHandlers = []; + wss.__grenMessageHandlers = []; + wss.__grenCloseHandlers = []; + wss.__grenErrorHandlers = []; + + wss.on("connection", function (ws) { + var connId = _WebSocketServer_nextConnectionId++; + var connection = { __$id: connId, __$ws: ws }; + + // Store the Connection object on the ws instance so that message/close/error + // handlers can retrieve it without a separate lookup map. + ws.__grenConnection = connection; + + // Notify the app of the new connection, if any handlers are registered. + var connHandlers = wss.__grenConnectionHandlers; + for (var i = 0; i < connHandlers.length; i++) { + __Scheduler_rawSpawn( + A2( + __Platform_sendToApp, + connHandlers[i].router, + connHandlers[i].handler(connection), + ), + ); + } + + // Attach per-ws handlers that delegate to the current stored handlers. + // Each handler reads the current reference from wss on every event fire, + // so re-evaluating subscriptions updates behavior for existing connections. + ws.on("message", function (data, isBinary) { + var handlers = wss.__grenMessageHandlers; + if (handlers.length === 0) return; + + var msg = isBinary + ? __WebSocketServer_BinaryMessage( + new DataView(data.buffer, data.byteOffset, data.byteLength), + ) + : __WebSocketServer_TextMessage(data.toString()); + + for (var i = 0; i < handlers.length; i++) { + __Scheduler_rawSpawn( + A2( + __Platform_sendToApp, + handlers[i].router, + A2(handlers[i].handler, ws.__grenConnection, msg), + ), + ); + } + }); + + ws.on("close", function (code, reason) { + var handlers = wss.__grenCloseHandlers; + for (var i = 0; i < handlers.length; i++) { + __Scheduler_rawSpawn( + A2( + __Platform_sendToApp, + handlers[i].router, + A2(handlers[i].handler, ws.__grenConnection, { + __$code: code, + __$reason: reason.toString(), + }), + ), + ); + } + }); + + ws.on("error", function (err) { + var handlers = wss.__grenErrorHandlers; + for (var i = 0; i < handlers.length; i++) { + __Scheduler_rawSpawn( + A2( + __Platform_sendToApp, + handlers[i].router, + A2(handlers[i].handler, ws.__grenConnection, err.message), + ), + ); + } + }); + }); +} + +// Clear all stored handler references for a server. Called once per server +// at the start of each onEffects cycle, before re-adding current handlers. +var _WebSocketServer_clearHandlers = function (wss) { + _WebSocketServer_ensureListenersAttached(wss); + wss.__grenConnectionHandlers = []; + wss.__grenMessageHandlers = []; + wss.__grenCloseHandlers = []; + wss.__grenErrorHandlers = []; +}; + +var _WebSocketServer_setConnectionHandler = F3(function (wss, router, handler) { + _WebSocketServer_ensureListenersAttached(wss); + wss.__grenConnectionHandlers.push({ router: router, handler: handler }); +}); + +var _WebSocketServer_setMessageHandler = F3(function (wss, router, handler) { + _WebSocketServer_ensureListenersAttached(wss); + wss.__grenMessageHandlers.push({ router: router, handler: handler }); +}); + +var _WebSocketServer_setCloseHandler = F3(function (wss, router, handler) { + _WebSocketServer_ensureListenersAttached(wss); + wss.__grenCloseHandlers.push({ router: router, handler: handler }); +}); + +var _WebSocketServer_setErrorHandler = F3(function (wss, router, handler) { + _WebSocketServer_ensureListenersAttached(wss); + wss.__grenErrorHandlers.push({ router: router, handler: handler }); +}); + +var _WebSocketServer_getConnectionId = function (connection) { + return connection.__$id; +}; + +function _WebSocketServer_constructError(err) { + return __WsConn_Error({ + __$code: err.code || "", + __$message: err.message || "", + }); +} + +var _WebSocketServer_send = F2(function (connection, data) { + return __Scheduler_binding(function (callback) { + try { + connection.__$ws.send(data, function (err) { + if (err) { + callback(__Scheduler_fail(_WebSocketServer_constructError(err))); + } else { + callback(__Scheduler_succeed({})); + } + }); + } catch (e) { + callback(__Scheduler_fail(_WebSocketServer_constructError(e))); + } + }); +}); + +var _WebSocketServer_sendBytes = F2(function (connection, bytes) { + return __Scheduler_binding(function (callback) { + try { + var buffer = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength); + connection.__$ws.send(buffer, function (err) { + if (err) { + callback(__Scheduler_fail(_WebSocketServer_constructError(err))); + } else { + callback(__Scheduler_succeed({})); + } + }); + } catch (e) { + callback(__Scheduler_fail(_WebSocketServer_constructError(e))); + } + }); +}); + +var _WebSocketServer_close = F3(function (connection, code, reason) { + return __Scheduler_binding(function (callback) { + try { + connection.__$ws.close(code, reason); + callback(__Scheduler_succeed({})); + } catch (e) { + callback(__Scheduler_fail(_WebSocketServer_constructError(e))); + } + }); +}); diff --git a/src/WebSocketServer.gren b/src/WebSocketServer.gren new file mode 100644 index 0000000..e24dd23 --- /dev/null +++ b/src/WebSocketServer.gren @@ -0,0 +1,296 @@ +effect module WebSocketServer where { subscription = WebSocketSubscription } exposing + -- Init + ( Permission + , initialize + + -- Server + , Server + , ServerError(..) + , createServer + + -- Connections + , Connection + , ConnectionId + , connectionId + , connectionIdToInt + + -- Events + , onConnection + , onMessage + , onClose + , onError + + -- Message types + , Message(..) + , CloseReason + ) + +{-| Create a WebSocket server that can accept connections and exchange messages. + +You write your server using The Gren Architecture by subscribing to connection +and message events and responding with commands via [WebSocketServer.Connection](WebSocketServer.Connection). + +## Initialization + +@docs Permission, Server, ServerError, initialize, createServer + +## Connections + +@docs Connection, ConnectionId, connectionId, connectionIdToInt + +## Events + +@docs onConnection, onMessage, onClose, onError + +## Message Types + +@docs Message, CloseReason + +-} + +import Bytes exposing (Bytes) +import Init +import Internal.Init +import Task exposing (Task) +import Gren.Kernel.WebSocketServer + + +-- INITIALIZATION + + +{-| The permission to start a WebSocket [`Server`](WebSocketServer.Server). + +You get this from [`initialize`](WebSocketServer.initialize). +-} +type Permission + = Permission + + +{-| The WebSocket server. +-} +type Server + -- Note: Actual implementation in Kernel code + = Server + + +{-| Error code and message from node. +Most likely from a failed attempt to start the server (e.g. `EADDRINUSE`). +-} +type ServerError + = ServerError { code : String, message : String } + + +{-| Initialize the [`WebSocketServer`](WebSocketServer) module and get permission to create a server. +-} +initialize : Init.Task Permission +initialize = + Task.succeed Permission + |> Internal.Init.Task + + +{-| Task to create a WebSocket server. + + WebSocketServer.createServer permission { host = "0.0.0.0", port_ = 8080 } + |> Task.attempt ServerCreated + +-} +createServer : Permission -> { host : String, port_ : Int } -> Task ServerError Server +createServer _ options = + Gren.Kernel.WebSocketServer.createServer options.host options.port_ + + +-- CONNECTIONS + + +{-| An opaque handle representing a single WebSocket client connection. + +Use [`connectionId`](WebSocketServer.connectionId) to get a comparable identifier for this connection. +-} +type Connection + -- Note: Actual implementation in Kernel code. Backed by a JS object with __$id and __$ws fields. + = Connection + + +{-| A comparable identifier for a [`Connection`](WebSocketServer.Connection). + +Use [`connectionIdToInt`](WebSocketServer.connectionIdToInt) to convert this to an `Int` +for use as a `Dict` key. +-} +type ConnectionId + = ConnectionId Int + + +{-| Get the [`ConnectionId`](WebSocketServer.ConnectionId) for a connection. +-} +connectionId : Connection -> ConnectionId +connectionId conn = + ConnectionId (Gren.Kernel.WebSocketServer.getConnectionId conn) + + +{-| Convert a [`ConnectionId`](WebSocketServer.ConnectionId) to an `Int`. + +Useful for storing connections in a `Dict Int Connection`. +-} +connectionIdToInt : ConnectionId -> Int +connectionIdToInt (ConnectionId id) = + id + + +-- MESSAGE TYPES + + +{-| A message received from a WebSocket client. +-} +type Message + = TextMessage String + | BinaryMessage Bytes + + +{-| The reason a WebSocket connection was closed. +-} +type alias CloseReason = + { code : Int + , reason : String + } + + +-- SUBSCRIPTIONS + + +{-| Subscribe to new WebSocket client connections on a server. + + WebSocketServer.onConnection server ClientConnected + +-} +onConnection : Server -> (Connection -> msg) -> Sub msg +onConnection server handler = + subscription (OnConnectionSub { server = server, handler = handler }) + + +{-| Subscribe to messages received on any connection managed by a server. + + WebSocketServer.onMessage server MessageReceived + +-} +onMessage : Server -> (Connection -> Message -> msg) -> Sub msg +onMessage server handler = + subscription (OnMessageSub { server = server, handler = handler }) + + +{-| Subscribe to connection close events on a server. + + WebSocketServer.onClose server ClientDisconnected + +-} +onClose : Server -> (Connection -> CloseReason -> msg) -> Sub msg +onClose server handler = + subscription (OnCloseSub { server = server, handler = handler }) + + +{-| Subscribe to connection error events on a server. + + WebSocketServer.onError server ConnectionError + +-} +onError : Server -> (Connection -> String -> msg) -> Sub msg +onError server handler = + subscription (OnErrorSub { server = server, handler = handler }) + + +-- EFFECT STUFF + + +type WebSocketSubscription msg + = OnConnectionSub { server : Server, handler : Connection -> msg } + | OnMessageSub { server : Server, handler : Connection -> Message -> msg } + | OnCloseSub { server : Server, handler : Connection -> CloseReason -> msg } + | OnErrorSub { server : Server, handler : Connection -> String -> msg } + + +subMap : (a -> b) -> WebSocketSubscription a -> WebSocketSubscription b +subMap f sub = + when sub is + OnConnectionSub { server, handler } -> + OnConnectionSub { server = server, handler = (\conn -> f (handler conn)) } + + OnMessageSub { server, handler } -> + OnMessageSub { server = server, handler = (\conn msg -> f (handler conn msg)) } + + OnCloseSub { server, handler } -> + OnCloseSub { server = server, handler = (\conn reason -> f (handler conn reason)) } + + OnErrorSub { server, handler } -> + OnErrorSub { server = server, handler = (\conn err -> f (handler conn err)) } + + +type alias State msg = + Array (WebSocketSubscription msg) + + +init : Task Never (State msg) +init = + Task.succeed [] + + +onEffects + : Platform.Router msg SelfMsg + -> Array (WebSocketSubscription msg) + -> State msg + -> Task Never (State msg) +onEffects router subs state = + let + -- Clear all handler references for servers that had subscriptions in the + -- previous cycle. clearHandlers is idempotent, so calling it more than + -- once on the same server is safe (it just sets null on already-null fields). + -- This does not remove the wss-level "connection" EventEmitter listener, + -- it only nulls the mutable handler references that per-ws closures read on + -- each event fire. + _clearOldHandlers = + state + |> Array.map + (\sub -> + when sub is + OnConnectionSub { server } -> + Gren.Kernel.WebSocketServer.clearHandlers server + + OnMessageSub { server } -> + Gren.Kernel.WebSocketServer.clearHandlers server + + OnCloseSub { server } -> + Gren.Kernel.WebSocketServer.clearHandlers server + + OnErrorSub { server } -> + Gren.Kernel.WebSocketServer.clearHandlers server + ) + + -- Set the current handler references. Per-websocket event closures read these + -- on every event fire, so existing long-lived connections will use the + -- updated handlers without needing to be re-wired. + _setNewHandlers = + subs + |> Array.map + (\sub -> + when sub is + OnConnectionSub { server, handler } -> + Gren.Kernel.WebSocketServer.setConnectionHandler server router handler + + OnMessageSub { server, handler } -> + Gren.Kernel.WebSocketServer.setMessageHandler server router handler + + OnCloseSub { server, handler } -> + Gren.Kernel.WebSocketServer.setCloseHandler server router handler + + OnErrorSub { server, handler } -> + Gren.Kernel.WebSocketServer.setErrorHandler server router handler + ) + in + Task.succeed subs + + +type SelfMsg + = Never + + +onSelfMsg : Platform.Router msg SelfMsg -> SelfMsg -> State msg -> Task Never (State msg) +onSelfMsg _ _ state = + Task.succeed state diff --git a/src/WebSocketServer/Connection.gren b/src/WebSocketServer/Connection.gren new file mode 100644 index 0000000..502a96b --- /dev/null +++ b/src/WebSocketServer/Connection.gren @@ -0,0 +1,153 @@ +module WebSocketServer.Connection exposing + ( Error + , errorCode + , errorToString + , errorIsConnectionNotOpen + , errorIsConnectionReset + , errorIsBrokenPipe + , errorIsInvalidCloseCode + , errorIsCloseReasonTooLong + , send + , sendBytes + , close + ) + +{-| Send messages and close WebSocket connections. + +These operations return Tasks that can fail if the connection is no longer +open or if a network error occurs. This lets your application detect and +handle failures explicitly. + + WebSocketServer.Connection.send connection "Hello!" + |> Task.attempt MessageSent + +## Errors + +@docs Error, errorCode, errorToString, errorIsConnectionNotOpen, errorIsConnectionReset, errorIsBrokenPipe, errorIsInvalidCloseCode, errorIsCloseReasonTooLong + +## Sending + +@docs send, sendBytes + +## Closing + +@docs close + +-} + +import Bytes exposing (Bytes) +import Task exposing (Task) +import WebSocketServer exposing (Connection) +import Gren.Kernel.WebSocketServer + + +-- ERRORS + + +{-| An error from a WebSocket connection operation. + +Use the `errorIs*` helper functions to check for specific error conditions, +or [`errorToString`](#errorToString) for a human-readable description. +-} +type Error + = Error { code : String, message : String } + + +{-| Get the error code, if one is available. + +Network-level errors from the operating system will have a code like +`"EPIPE"` or `"ECONNRESET"`. Errors originating from the WebSocket library +itself (such as sending on a closed connection) do not have error codes and +will return an empty string. +-} +errorCode : Error -> String +errorCode (Error { code }) = + code + + +{-| Get a human-readable description of the error. +-} +errorToString : Error -> String +errorToString (Error { message }) = + message + + +{-| If `True`, the operation failed because the WebSocket connection is not +in the open state. This is the most common error and typically occurs when +a message or close frame races with the connection closing. +-} +errorIsConnectionNotOpen : Error -> Bool +errorIsConnectionNotOpen (Error { message }) = + String.startsWith "WebSocket is not open" message + + +{-| If `True`, the connection was reset by the remote peer. +-} +errorIsConnectionReset : Error -> Bool +errorIsConnectionReset (Error { code }) = + code == "ECONNRESET" + + +{-| If `True`, the write failed because the connection has been closed. + +This is a system-level error that can occur when the underlying TCP socket +is closed while a write is in progress. +-} +errorIsBrokenPipe : Error -> Bool +errorIsBrokenPipe (Error { code }) = + code == "EPIPE" || code == "ERR_STREAM_DESTROYED" + + +{-| If `True`, [`close`](#close) was called with an invalid status code. + +Valid WebSocket close codes are: 1000-1003, 1007-1014, and 3000-4999. +Codes 1004, 1005, and 1006 are reserved and cannot be sent. +-} +errorIsInvalidCloseCode : Error -> Bool +errorIsInvalidCloseCode (Error { message }) = + String.startsWith "First argument must be a valid error code" message + + +{-| If `True`, [`close`](#close) was called with a reason string that exceeds +the WebSocket protocol limit of 123 bytes. +-} +errorIsCloseReasonTooLong : Error -> Bool +errorIsCloseReasonTooLong (Error { message }) = + String.startsWith "The message must not be greater than 123 bytes" message + + +-- SENDING + + +{-| Send a text message to a specific client connection. + + WebSocketServer.Connection.send connection "Hello!" + |> Task.attempt MessageSent + +-} +send : Connection -> String -> Task Error {} +send connection data = + Gren.Kernel.WebSocketServer.send connection data + + +{-| Send binary data to a specific client connection. +-} +sendBytes : Connection -> Bytes -> Task Error {} +sendBytes connection data = + Gren.Kernel.WebSocketServer.sendBytes connection data + + +-- CLOSING + + +{-| Close a connection with a status code and reason string. + + WebSocketServer.Connection.close connection 1000 "Normal closure" + |> Task.attempt ConnectionClosed + +Valid close codes are 1000-1003, 1007-1014, and 3000-4999. +The reason string must not exceed 123 bytes. +-} +close : Connection -> Int -> String -> Task Error {} +close connection code reason = + Gren.Kernel.WebSocketServer.close connection code reason