# v2 git bundle
2ca1e67435bc1c116b23e1435171e2d3c5d4c02a refs/heads/bundle
2ca1e67435bc1c116b23e1435171e2d3c5d4c02a HEAD

PACK     ßš xœ•‘1oÃ …wÿŠ5² ãØ‰ªªcÇªŠÔ±ºÀ9¦±�Nä_%•:vA§wïÞÁG
DÀ„’Œ†•ÖªmsÍ…Ò�¨©ÖMÛmåAJÑÖMá1�M€‡m]c‡:7˜�5mH)ª6œëJ µ¢«x[à”z #ùÃŒá)NžÂUy9Žh†R¹ñxÓ¶›ŠËJÂ#k+²:š”è_Ó’Ý¦e”�« Ö€*gã.Á$‚._K“ÜÚD�IõEq¯Êñ+þÊoÆr8„‹§np—Ï»e—Þ¨}O&P,þ.+áÃdS“ õqÊ¸b„Þ¹ôY|Ýïß@²
Þ)º)(ëRŽYlæ0f06Ñ1à’¼ZCÄ‘ ô™Ï5tœó©ã#�7žcihõÒ]~ÂÝléœ±´W
g^Š’—Åo7®ì‘#xœ­Q±nƒ0Ýý'eÀØLTUU†~A;EŒ9*dLTþ¾N”¡C‡VêMOw÷žÞÝs•qY
Î[ÅuÛV²â˜”UEÑä™.3ÙÖ-jJfeÑ8h…`eU*Y ¹Ì¹f²ÑŒ	ÎTÎ©§
.„”D­®›,,8*ãz[P-×“ƒÇŸºÏ£²n3ë’tO�•RœV”ÁõEô4Ž½søŸšD{�Üá²„%!»œnð=èœ›—Cšž{×­uâ]¤~uî”Ý”Qé«§¾ÍÃ¤´©^ß”^®\š$Éd!Œ²"¦ULóðª¾ƒãz†—þB"ˆ"Ý¢êuœa2Ãße¡ÔÂíÒOfÆ_{\L¾3¨‚Ó=”?›õÿLh’/¿,­n˜<xœ�SÁnÛ0½ë+ˆœR,5ì$‹ãb-¶ì2kWìJËT¢E–INšË¾}”�´	°Óàƒ�Gòññ‘Žž¨ZJ¬	ß£lò›2o–Iµ¢BÕ9-ç«E%•š‹=Ù+‰Õ\I9/Vy^ªgWëuÑ”ó¥jæ©Jå•Zçû¸u0P·ED‹ð!ôùù¸iQ›LºöŠr½^-òu¹€wy™ç‚ÑVÇHÿW-”~™J}sußvà¬9ÂúÙ‡“*ƒ;‚=ù ���u>÷#�BtžÒ°Yû; ïµës0jPRqKðåë�Ç§3Çýä×BôÚn@Û‘?³úe&[-· ÃPsÑdx{ÝL�¶ŠòfT’¯Ôv(w¸!qêÓÎbKp“Èiý)m2ƒ‘h‘ßdð�'®Œ[à˜ÞS å]›ÈÅù«	Á]I¤mXŽ4®®É§©ÇìºsUšÎÐ€†·†!â‘Ó1B‘ñ·¬�û%ûp²Ï™fT†1Í„¶	~þþÈŽ;•h-íÓ4{´lz&Ä7w€ËÕ0ÎxÁðoKípÉ~ÖS'wÃ=Ì„!Ü§u�‡.²EV,ßf×6¢Œ<³H¥©¹{Û*ìˆº j®:ï¬žÁeûsjâ;¥ñµßžB™ødØxOƒ}ê‹¹¦†éÃ;øôgØ´Ž"›g¼mñbÞJÉ•'xœ•‘MOÃ Çï|Šç¨Ñuu£oÆ£1Î˜èež…§-Úê¶o/¸ÎLoöÖ??þ/Á[D4£XÁòŠÑ"mP`‰‹
ËJð<|ÍYYñ’fQy(Sº\0+æMÎËºªé"_¦é2+
¾ Y}!jä„�¾Ó˜CÓ1»cŠÁ•Úoå¦˜ì®‡k¸(Ê2Ð¬‚³´HSÔAz�ÿº�Óé6áòÄb��<½!�ažw Ðôz—ì†´‚	 äAúÕXGŒÕ=:ØhûÑôzˆ™·²mÑJÕB¦<<®W¯·oë—§ûg0£ë~â;ü1Û÷>=§!ê‘�É¤:® ð3hvTîÛzr€½ƒKÈº“˜nLyÉg4Ç-rp#çèÜÝ `vI€Âñü°6ÞÃ”NòŽ¤Pö(?†Æ
»C´€ðç¤Vç±„éÑÇÙqExH½Öæ’4r;o�ùù·Ôã`âÁï-gžµQ�²É Ö>”"xœ­QÁnƒ0½ó–z&H…$Õ4í´/ØNÕ&˜�TBBµþýÒ®Ç6iöÅzö{zzž*.ÔPI‰¤e+�-M#;�÷ƒÁAk3ž8ôdp©DÏ#X#¯ë®Ó½ÚXJ¶$ô^µ²©”Ò}‚[+ÍhÃd
O'Â•Šn	ðøú<£»­¥¥ð•Tª­yƒ+1Ë<O!Ðj&&z¤ôÎÈP•¢ŒðŽ·é=Cpë�±�)Œ[WF,^ºý-²×È|s§{ò,.¯)±sU^»,Ï7‘RÁE[p]ð&‹â»¼†ÍÓš$9äùHx
ã!Ï�>Ý² sp&¿N‹…Éû>€Hp‹�ûôxÿÃŸÆÙo˜eÉB«óšxœ��ÍNÃ0„ï~Š=R©ó['B�€8 à¶‰×�ib[¶SQ!Þ�„(g®3ßÎìnôD�æY«Ò"«‹¶LU[¶­ÈÛ¬¨E…µÈ/¥ËŒ9ôd"´Rb&k!
¢Œ(ï(Ï
)8
N˜¶<­¯TÇpŠ½õ€�\�þŠá!LŽü¯òxQIgÇ#¤!ªœ§e·üÀ9›ÕQÇHÿ›fŠ0Þô„CìwÐ§³� �ƒù ­m`¿úà)8k1öL(Ã‚ºÃ¸`®¼ÁawÆ%aq4™KÃ€òiõ^VþfÇÐÈÙˆ“7t¬9-	BÄ8$¹Á^—­¶B=ÓfFLlX7ùú]höûifQ&¾‘Œ×DËíŠû#|­ÑØóÝ¶y¯É[òþÍ~ =Å¦˜xœ�Ž[j! ÿ=Ef	ÛÇè,aÉUZ§Ý¢êöö;ÉòY5;3˜Å+¥ñšÌ¶FCÞã¢MDÆä“M‘Wã­ZÄN�ëm!±Á@"¹MsBí‚
VZ8D“h%)è˜�Ö�ïêOªŸãØ¹ÿ™¯{¡üý[¹:ï-%"¼K'¥8mÉsòÿj±µ8Þ:ÓVør…“Žò{~5\¨Îá‡ûÈ­æz/1gTBŸxœ�ŽKj1D÷:E/cAÒŒ~!ß$´[Ý™!óC£ÁøöÆ'0Em¯ jaŠ&3SÊ’·A¶’:o‰Yk–[tŽÈ©/Äø–ÜEŒ¶µ·YzñÚã�“$çLÈVœéuXàÎÛ€å�Â÷~l\žäú;ã8}Ò:_À„½M©OpÖAkÕè<ÖÊï­�…'næé0g¸¯åO¦õþ“Ç}ÃJH{6ãrà/uWÿÑBVä“;xœ�“ÁnÓ@†ï~Š9&*qì$��!ZZÑ
©H¨œB³ë±½Íz×Ú]·1'N< â	û$ŒQ¸!K–=;óÏ÷�ÇÁÁ©\¥§9’Ì‹u’®qIéJ,’-¥X LQàR¤eÔ¢#`“­–¥(W"ËÄò4E±Éäj±L6"—Iž•%Ê<É²<Â.ÔÖzjkt=„—¾kÉ"¯«•Ž¥m^Ašåùz±ÉÖ)œ$Y’DmTôÕ‘TOš äÌ‘&.™n�y,Ü“óÊe*¸W§EÑâñÙÉøÎ[³…#ÍŒtÿ•™N`Ì˜ÈÏ*2Œìp"k4i[EÀo´'	“ÖÑ0»)*Ž÷ºÐê®RÆÇCOaÑù˜7w~¾7Òƒ¡}xjø„Ê°—å+:PÂÙÍ·,5î(6j‚JëBM¿‹�>àñÛO¨:tlœÈBéÈ×pÃE>5-†šµX”¸_µÚö0)ÕžóÅà°…‡š8™gh;'itìÍv�ã#++‰®i™¢óTÊ=6ôG/@SÀ›kðJó‚é§†œìê�» é0¥ÑHÜ7z®3þŸï7@ófh;¶4y{}{õñüËíûw—7/Dy«1°ð<4íÜ»Y°V{žæÐH³“g3�‹ÎÄÚÊÝÀÑƒäæÀñáù@'ÆÂ'¿S-HõyÊÃQUÅÞG-<~ÿqÜ?Ñ)]œŒ',uÎ:Z)òF¦1_ ˜¡TZ3'��;üwW—gqô°wD2™!xœ��ÍNë0…÷~ŠÙ‚Fv“ºÉÕB,X"$ÐÝOíqc)‰-ÿÊÓ3M7¬ïn<óùœ3S(T(•ÛíÖºM;˜V÷fgP+Óµ4Zõ½DÄDK»4I:ç¶Ú*G²“CÛîí·]×j�ªu­ÑkCÌGLg\þæ)­�§ãŒ~jL˜Aíû^o{½ëà^î¥Ü�})ô¿…a_ºM4ÃwàPç…Ÿ5N-ËªF56�k¡„x	É ¸Dy„Wÿ¹°D,#ä e$x®ÎQÚ¸æÍñÛGpŒ¡)§é‰ÐŒ”Å…ü÷öÞÀW›_R#²ü'fÆk&‡ójvë-ŸÖœ€Óùá—XK†‹á…ºÉÀëçH¦øÝ=\r!œ¸ÅðuÇcÅ„K!ÊkbHVXŸ�;ø&DÏÎ¸Ø•‰)8�äO_xÛˆ[I±v‘Exœ�SÁnÓ@½ïWÌ­¶HS7ql' ŠF€T•RÔ†ÞÇëq¼ÔÞ5»kÒpâ#øB¾„±“¦•èqôìÌ›7ï={KóT–Ù4žds*&a‘Ä2�c>�ã¬H§I'i"£\´hI{H“Y>�eNÓTfxšLód6Kârg§sœÅ³y‰#��¯ŒtÔVh·¨Þ¸®%;TÞ®TõXšæNÓ,K&Ù$‰àU”F‘àj£¼§ÿ›¥zº¶6X„ØXlaýCµ «Nßƒé|ÛyP–]Yò
g`E5­-–°›r°1ö^ˆc<Hì�)á(Ž¢,±€úÖ‘óðYå@(Œìš^ Fæ:Ø]Ó-†ÍTà�©·–œSyMPªšWŸã0Óxzð# /ÇáB,;=î¸Ýj„ì;« ´5ò²/JûìÜZÜŽàÓõŠë»ÃÆ‡³DàHïöOú�;U�	™[½e@iÖZ9¦°›<YÖ&?qÞ6,Og%?¡=ÍÃ¿þz�´ÖÝÐ¯�šÖo¡éj¯8<rSl¹^ˆ=±†W%éÝsÁÆBÜ‘U¥b½¾+duM�ž?Jc7h‹•¹õÆâšúU9¹…8‹›G7w&ã\é° !ŸÁõ%7ýEu×ÊÏÎ/>¾l&�íÅ,­i‚Þ…ðiîú,û˜râBîe›€�#í”Ñ,îóm…*{*ª[l×;ÎY„«‹«÷'‡¡PˆUÅc�7ËþôÊÏkNÞóªØ§Ê²â¦�^‘p6å.%g€óšÓ.âÃ?Â,õ‘WYÅ7²$} Ä`Ï“Æt�nô¤´¬P¯©‹?Ôx¼—)xœ�‘ÁnÛ0DïüŠ=¦H$Ó”]IAQÔpQ´—$H¤·bE.-Â´(,éØþûRr{è5G.†ûff�YYÙØZ©«¶•Ø´ëšj³Z-Ue¨²zÙ¨FÉJŒÈ4$hWºnYU‘m»ŠÔºµ­2öc»ÖªAƒ­¶]ÝU�©iì‘/8 |ŠÇ‘xž|ÙÐùR‡ÃgXÖM#›Z5K¸•µ”"O.%zßoaÝùÆ:OñÃ=Dâ7‚…]Ü�ÇÎ;ýÛ™¼¶�Ï?°ód@I	11áÜ¡�`¡’
˜ŒcÒ	R€ò´ã¬Ù~}â›;S„ŽÃ)æu@Ì9ï©§,‡!Ñ`ÀRÒ}âÑ¸ oá•:ØÌ¯ÍÓqcHCó »M=ýÇÍæ2GW¦¿ô2ð.3œîÁ£ÞG±Ñšb,¶ÈÁïÃ©xd·sÃt>è½vW‹_ßŸKx§™35Ý%Mî˜ÄÈáì2o.Œ‹èÁÉ¥þš­Ï¥G¸…¬§ÄsÀT¼\Fºƒ¦•â_1á%BÄavRÂÆgs­~J¥ûã°'³@Ö½Ë7º^ –âŽÝì�)xœ�RËnÛ0¼ó+ö©ÒRDÉ(‚¤í¡½åà È•M˜">ûÖ�èöKºr¹ö:;;3œeŽˆðÀ[£GÝ©±Ça4¼îPjÓa«tk$ÇA45¶ºf³Šè3pƒCËåÖ˜^ÞÑa'pÄ~„”u�ÝØÙ´L•|TÂù¨âEyŸS™1^‘§Ã¤¬«t˜AÈ®ãRö[÷\rÎ�lÎøÛl´çU,>Û	×;ˆøc†/Åƒ¨šŠÃ†vªºä Ä…=:<D5ABo¾]¦å¡)èæ�v!!c·õU™‰lÐ¼W±†!¢:¥w�šJ<|{µùÈîöG|“¼G�mððª\]�ç™`4îr~ý¦ôs¦h.�	5!svˆ*ZL»�¾”�4oqG**ž•ÎîÎžÅO7ÖR³™¬r‰>�ZÔ4|ßï_Msð	+xvÊì‚2‰~‡d½Æk ·FÀ �/hØê¹`Ûƒ;Þ®+x±F§ÈÛSß�-ñïáëŠß�–*è ÿòTì/úØÑ™xœ��ÏNä0Æï}
Ah:MÓÖíhw»sbû žÔiÃtÒ*I�òôdîœlþ~þäà˜A¡¤ºmé¦©µ*¯2]ÈªuUaÍEŽ
µj“‰Û ˆ²Èt%‰1zr¡±FÌ÷ûL´˜Âº,”lÊ"¡9ô£ò<õä²¿ü<±{W.»#™!Uãñˆx¢ÁJ
	fYÕ£	�F'*æòæ©sÔ2¸Ùsd#ü�-ˆ´H£iÿ§–Ã½y=Ð�S»ñ‰Ý@ËÉ<{þàì´q¦å«œ?ÿæÿ]Ã™ç0O«ýlÏOàd,Ä~oF»ùˆü"&Rê8}ô£ýîâ‡»õvTvÚ¼Ž¸âáÝYßS^V›Ïú0.Ù“ÆåfÝ¿¤Ü™ÝcÙYß_—;lõžî¶øÒ=›e{ûü;yˆ!›¤›kxœ�TËrã6¼ó+æ)+J²(ëåT*›�«ÖUïÖÚ—äCk` Ð²|ÊGäó%Û e¯’C¹ICLOwÏ#zfRËµÜl•X,6ÅV-Äü²ÚˆªP›ªÅŸ¶Rñ&k…giYp±*×«Åvy¹š«RÕjU®¤š¯ç²\¬«u¹Úl8]¬�'¸­…?
+è‡ÐµìûÈOûFh3•®ù‘.Ö›õf1_n—ô óÑFÇÈÿ/;‹âŽø©VQgu¤!é‘¾gŠŽ„1tW+ü•€WTé'
Qî_‡,{«ršÖsÍ6èGþ†(A{n…öçIk¯^IVøÏ™3ŠF@É2:?¦àå¬‹Ú„Ù÷dÄÑu‘µ–5YGÆÙ=”ó“à.Ër
Eî‚¯š `q9ð¦Qè*PŸ�4¢iµÝO¨¦r¾a5¡gö.ú™'‘¶�Âè”ÆÎF´t §”"bÂx¨éÊ/,c¢gÑì êàud{¡-ì„4Ä„,:ef¡ Æ©Îð	¥P²éLÔ9F'’óŠ}Ï¯¯(@‹E€ÒgÐ¹+fï>~¾£š^"Pq”uÎÞ;¨Ö»µ£vvàW³Á<„¼oâÈòa¼;õ+€tj	œGíc'L^;Ð.;ùÀ1Õ‰€1À*p„`à'3urææÓ,ú.ÄOÞ='ýK­³�_XTÒ¬ÿƒ&MWÇŸ�:¾¡ž�wÅgþ£Ã� \Xgµ#ü19*`‚™q
?gÁùÔ€Ëº³¬ðÆ¥Ùý�6¼^ÞÓ8À†ÑA„Ôù¦¡Ñ@¥:›ÀUÓßþ5”E‡ŒßN^½øÛ›”æ*7«JûNx•Zæü¿îÏºÝ�­Õ@‚Þ¤Ñ€ÿp,z£‚03Õ¡”Ð–L?÷}N�Káßu{m£G¸iã1·¢ÁÄb‹K!†(‡Æz,Ù}÷ÇóÊI,†½Òû‰+ñÍìC�Aêõ§"]ˆl_†#ÐÍíýõíýÍÇÛ·>üFå±Mþ¦TêSÉôNó2
Ã¿{w�O¯VÒ¯¿\&÷VÄÎ3àPâ$.ÝI×*9¬NiÔZ¨E�¦_‚³»ÞÇÄ‹Ó_¦Jð÷^C(Ò%ÄRéuÛß4Ü=Ð:¿d½ŽNùÎ6*O84JÈØ¹t¾w»+:ØðtC3ñèpL¤w!):@­3¦KÍOSW‚z˜¦:ST™tD¯×¯Ã+RÍt2˜½c,JQ]rrÖqñ¤éªVÞ5/z¦ÙWvÓZšKxœ�“AsÓ0…ïþ{l¦±ëÄIÓ¡Ct†)�RÎÌÚZÅ¢¶d$9!øí¬l§M®L.Ž¬Ý}ïÛgo‰ çr9_	J—y±¼b±X”ržÎ.Ó¢ÈVE–ËJ‘åQ‹–´‡¥˜Ë,Ëf‚ø–LótVàŠr’RHL‹7üÊŠ;_è¨­ÐîQ#¼s]K¶?y¿iPÕIiš+˜å«|5Ë³ùÎ¹añi£¼§ÿ«Žø–\ëÅº¦Zý!ø–�)~RéÃ©I¾¬àJ,+‚'ªic±©j¥¥‰¢¯­WM¨ôU_ýéö‰K}’-•U§ŸI\4]íUßphî µ K¢+_»F×w`M§Eì­jÝ:Šâñ~ì¼%lÖ£ž#…J£ËÎâõÎŠPObÊêbcƒ‘¨c¥'ð‘ó„ŒGV!Vz÷üïWÇõ|òjñÃÍý0�\�äZ£A�žt¹Íh³ƒ¿Á¶«ÍŽÜ1²)¿ô=×5a >zÍ=Ù[a|ÜS'ò™è]�¨áJ@|5<öH•î[Òoå|PÎjŒz†2g˜jK¬RŒË¸ø|{}ôb°¡=÷¬ÚþðÅrà_²Pîv†ÆŒÖ[ÃÑ°P17Å´ÍN�£v[ÎÏÛÃâ{…‚%³óI°7.Ÿwg,nˆSä²£–kVêL½%xIÇÅX2�
øþøÅsÇ;Þ*„kå(	XOÖzXd½N¾ÎYîPy¶ øÓIH‹³	Ä‰%sÔDÀŒyjÚ^«èSïàÖ°MYw®‹%%Qt-„;Mk|ø®ÏáHú¨n9wÃâŽÃ9…bïy4ê¹i¾Ç×x=;¦ÏmIÃ®¢!'±a²†Î}ÿ$ú·ã¡ª‘xœ­�Mƒ F÷ž‚}£hš¦W`üITâÂÛ×eÐíË——ïÕÂ,ÈJdíœÔØ;o�µx°D½w"�—ˆM¦Â{£
 pÔ(Q�A…Á±´’”=Æ{ Ñ˜†Î:§"èà<S¹h'ñÒÖèÞóø¡Ÿóàrt{*œ×«›–:Ÿ¾i{‰œ‚A< š›nK­ügmî§ügŽTYDÎ¼Gò©¶wDj7.w×¶~6Pa#–xœ­ŽMnƒ0F÷œÂûHÈ6xÆTQÕ«ž/%R Ë˜·�³ëºý~ž^-€™Bpi*ƒe¯Èˆ8ªçÈœhviìX»,[5êÛ’¨µÑ‚]¤�ˆ“gòß¡A\D'g]öbä@^¤\²‰¹�‘Æ�ˆnÒŸó@9úm/È¯«ÿ}Öåœû´¯ßÆ},ìäí`n–­íZº>kÅ?c»ÔLñeÎ¬Ra›Ê¼×þZ_oíÿ\º’xœ­ÏAnÃ @Ñ½O1û¨ÖÈ0DUÕ«0ÄQcca¬(·¯—=@·ñ¤ß›*„HW«²±l-z±†�›Œ„ˆÞ¦’3%Ž›4];”¨4!KåBj0fLWt9ž8©Iˆƒ}®d×m–ö–UàÓ19KD—?õûØµíãZ›nÏ÷xôùˆcªËLž=‘ólá‚þ4Ïº<z×f‡¢Òo 9CÖM×,±v8êÇ¢í®ðªí§<ëë-b$–xœ­ÌMnÃ @á=§˜e£JüŽªªW™�!¦q –âÛ7Ýõ ]¾·øze†Ñ‘åÕûÑÎzAâ5h¯íh4*tlÐYÔ*
VNV"T~t‹A«y�§l0óDfê×qÙ€�¾å
Ø¸lXOLâëh\›L¹rÙOy‹};Hºüøm¬Y–I
Þ•QJ¼î#öÎÿ£‰ŸWhgr@G’{vwè
º;ÞX~·œàÍsáä‘r‡—R°GŠ{ìçEü èfÈ–Kxœ}S[ÏªF}çWLÒ—6ä|"—hN›Š"*wDiú Ã€ø�8¨øë�=ißÚ®§••µvvvöb! �e$	sU�*¥)…ŒQFD©„BžB‘†óq4È•(KXÉŠ	DQ‚ŠQ®
A3MCV-ÿÇ¯Ìç""Æ¥X`	
„HÍóLT•y®!Œ‘¢hÊÛŸ�ìÜ �Ø™à¦¦7’wl ÷š<þx³?ÁWI€‚¤ŠŠÄÿ§éÛx#ÃíãÚ„6ÓGU³ó˜à®ýÌ‘Š ßyÀopoµ­#X×ÌsðõïØ·ÿ�U´ºÕøòs½q€·ö@¸Y;zæ�xÜVØÐuc¡ë¾áÛýåH•E°˜g>G%>7º®;sÁ×7±8Vöá„ÎÞa»\Ç[ú°,‰k‹¶][U/]¼®ëõ —HO—.}¢aÞ&ÓlWåÚÙKíõÙÞˆ½ÃÈË‘½o9 º«¾—Õä‘ðPæÖA]X¸…{Sã­Oµ¥ŽØ^ùì“æÚþþ•RàxY<;¨
˜NAÐ	�#~él¹zR-\ÁãÂð«—Ø\/¦r\}Šî§Ø×Üì—½Æœ.°+)9@ù€-!m¯pýÐµMùØ=ýZv_4¤›SŽÊ¡²Ruk¡èç†ÍH#cã~�ž�“)í»ìzi6†¥J�M•_…f´7Á[ÏšZ¸ÔÚËý¸•.J5ÛÆ}¦oùPk7Ò�ŠÑ{‡ç}ïA8ŠR[ºírV»78?Ñ^Õôhx-tXæÖöê“MBIZGÛ.¬c²ù<_Sž[2ëÌC°Jñ¾³äÚÙw'_KûÅ"wYD;V9/¯fÎúTˆ¢t3%¬^Eñë•ÁjWràÔ˜—IØ$BxõtãµOÎ÷açâ”¤qôë¼á§de¼¹{ÀüÃM„Þg«„µ±OžIyOèÜ¶`øèÛ�9íÕ®Êg�¿{f�Ï‡Ÿ3j±PgKöjÖãÜæ±4õ¤“gòê1ÆÊcÆ� ÚiÙ§¢Ë¬|·\Êb÷ÙnúcHW#_´�faÂ·…OèÊ¦'{ç{a»Éäèr²}ÕXÆ‡¶ö&ò²Õê¤9Õeårà·ÅŒ¼öÇï›ÎòßÁé#ë¾´d¨ðð“ÈqøÝxòsAèíKAî¿ü
ò±¥€M”ÜðPSÊ¡kü>$À:€ÞDüV&zÏ™Kxœ}“I¯«F…÷üŠ–²I„®Ý4££—èãceÁÐmFÓf2ö¯Î ¬’TmJGçHU%}}‹1P^IäˆD1)äIFiœ „$0JRŒEA$q¢04jñ­Q$Š
A)–¡’&/ð²LbN„HÄb¢¤</ÄÑ?~Bd,ò"d! 8"¼„Q,&bŒ°È%ˆWPÄ£”c¢¡ÏšÔÏ>ÃI•ÓÇMßâ1Ç�ßÞÓïà%ø¶‹<ûŸ¦Ï¡Ãm7»5-¦ÕsvÍûlˆgISÿ
8Y‘%QR8°ð]Ì[­ó¾Ç-0ó~5ÄàÛß±Ïÿ�]éµË¯àã�ÒsmÛ´ÁqmZªç»ÆŸ:ðè–‰¦ªš®ªŽælîÅ™Nº«s‘#ƒèg•ªª×{è¨ê.y+ß9c?~¡µ£µÐ²”—sÖ¶ïnÛFÊ*­ß“×I�{ôÎ/)	Œ€=êMµ²x„‘Â®S¥Uê†È–FH=7ôÁO~°Ì«ÝîÖ#b%ß>¬¤»ˆWz`¯RXnYg«��žš4ÕÑ4*ò®Í}Ø7ë÷§V,8­ì”ý¦rÇõá†åˆîwGC^®$³H–{þ|·¬¤ÝªKqd›ó�¢ÙDª¬äqb€T²¶³èêé\Síô4ÇsÚzÚ´Ä<oŒ@]Ô•ü�È_ ÈÛêÆ-€®èaÈlÄ ZMO¿püè|}»µ&J3g“´(:¶‚ç¢k„íúìÂ=�raì,Û4ï½aãÛ\²
å(—WjnõÑ¾Ü• #oÓ~Ývn/–t7ŸŒ…^Ê½}»,Ü¿¹‡�<z…ÓÆìi²´�«$c.¿h¹bùçm©oŠ½FIpzÕw‚½m	•�^º‹/F¤tþ1oÍ‰f¨?Håã™9°çü(;-ÀVí¹‰÷ˆ÷oÇûþbluâŒÅ{;gmW/ábÙ°nç‰@qˆO[\ÎÛQÌ�¡=œóZÕûív%AÔ¹âz#&­PWÈ’å7A‘G/Ã[d‡’xAžÎ¾°+ˆ§.¸‡£lÔ:$á:ÙX„'*‚ó›Ôæ�ÚúQëp	Ó—€Yå`TÒË»»YÈÞ°V0HK×|)Wj�÷ý¸ù*;þÁ€_®}ÁüÅ„a}ý;Œ:ôÍG�Û+¶~à&y�L1í>R<þô3ˆ‡š‚ÏþIq7¿5)¤mj€ÄÙb&‚¾Hš¡üƒ7}Þ˜xœ­ÌAn„0@Ñ=§ð~$d qBUU½Šœ©“DÁ,æöeÙtûõô­«53."H~•)ÍÉ{Ô¸0ÏL¨“¬Œ‡Æ]‹�÷’Ñ%!n-˜$¨�:Ò…ˆç�œ8MyàËöÚ�Om;÷7†OÉÝŠê÷uj?ÇR»¶Ÿ÷ø<l¿dLõõSˆ�<Ñ´Ââp××a¦ÿ¼²²}ÀÕ66…M›–�¥¤ZòñüTt^/á€2xœ[Á´‚i‚©†EbRŠ±±‰¹‰™™©�Eª™AŠ©�0KJ3626³404±0°41O�ëÍlli<quˆšÌÄ¨ @‚áŸJxœ}TÉ®«FÜó-e“½KMÑKôÀf°}�Ø`eÁÐL6ƒ¡±�¿>~vIjuTª:‹’ªè@ˆe¤�<†b’@,©	Ÿ
©$A¢ˆq,Ä>Qc(A¦�ÒR ä„ˆ
æsˆ³ñ"Æ²*ó²š §ß©f"VÿÑKˆ—ÓŒ`¤¦Ò[‚äDLó\�sIM“©b*C„òœ‰'ZvhfZ’ôZõ#I::�{E¿½¯ßÁWb(*‚$²ÿ)ú6�d?Ún ýuþ(*ZNÉGÚ5¿^Vd,aQ‘ ß`ÞlSQJ`UÔžðõoÛ·ÿµ}1Vøòºa­°·öÀ_YŽv8zÆŸ<ðÍT×4}¡i®î®oõ©ÖÞ‚�]yš¤cyÕ4­Ô]WÛDÔŸÏUJ×m¹º¤MyY7%®ÂYÖÝUYîµB:.º8éõ+�!`dlnN²ì-þÒs¾ƒî´²^ku7¤Z/Zð2b�ó·á­�Zš©ûÎ´†ëg6@]Ø¶¨ÑêÞdr†¾;MÛ½Òï¶á|bo�¯'
‹øºgÀÓ¸µu‘ø„?œ¶�C^‰g_ßL
ªê%šç‰äÞy�£Hï!Gw�c>ÝÏ)·bÀú.×f¦ôžG¼tî/åé4D�µ«ƒNêu½Xæá8ßD%p?³x'dá.Äð$Ç-z«wØ­?;²0d»Í-ÛïÚËKu½v­RÉf-ØoO«ÕòìGùl˜'Û�¸Ñ:d�mW[®|| Èßeõ(ÕÅ�³š4é™ÿ<pÜSÞ•µ”
Õ-è¬”pîâÖo§w’iv7ls¨n:ÌÙcm.»`F"Ü¾xlöÎ.æ�\º{gn²—uªVYú›õ‘çz6ðì-În“9Šà8erÞ¼c°ã <÷öDŠ<lÌ\$ö½>_çÕù5lÖvä'O�ŸÛ«Ü…ÎíèáÅ²@Â¶47Mpô6¤Uceô·ÕîZ]<¿¿+/é ³,j×-ç«Âd§�È3 ?úpS²ió¿Û
w±–¢­ÇßÙÅ–/SáÙ1DSh.1Ž �ÜËË5ÞãPOIÁ€¥µ6¸a,£gBE)Öm=ðK²ËvÌ_�0œå¿7‚Ñ&Ú}iÈP°÷À<Ã¤ïÆ“3Ò�?ý’©éA·]•�|è }ð<´ø~ð Æ¸y“‘fxœ�TÙŽ›H}ç+JyJ„l³/Vf66Æ˜Õîv”–c³
þú¡Ó=“žI4)©êÔ¹ç–néTC6æY‚ˆ‰˜ç	�Ú�Ž
D:ä*$y*¤J°Ê¯a� CH\ä3$Íq¼È“¼0ÂØ'D†ˆhNÄü�ËD°‚Eä%ú:îoà3#r¢HÓ4þŸ›/mëfZ”5¬²aš¤èÜÓ°Ìÿ$/ðËQ<	pb\Øˆæ)B°jŠÖm >¿•}ùß²¤Jš4“—µXª˜ª	œ�jH®g/¿ãÀ@×¬Â…$-dI²–v»<�MÙ–IßâÛ–õÎ™$I]¾´$-%4B¿GË»wb¡J‘r†PÅ€˜Ù¦æ0²Ôo+÷®ÉzWWò°®ŸÈÁíåFëÓi*òéË…�/õ•›Q™­¥¨ÇûÌÉKÙí6)ï3™Ð]îÉ‚BsëÅcù´[’4�|©7Yv¸?çl¿mÜCÛ<3é�WÛC¤Â1µ��eìqŽ¯ïRòìÅU°£l�=ª¡yyé=‚A¶Ñâ–o•´N¡¯ËOHÜêMUÙXÐæVY¸g»Ý}Ù¦‡CWÛõÖt½…wˆìV¾ô¶Ñk‘w”õÁí¢½æp=ca Î‚ÄV†m{‘�l¼Äû¶“c•Nã†ul^l¿¥ñƒ§aK(ã	ZmŽ©L¨†a`Ö1Ug‡TY7„®h<úC±'UÔ™ÃÃi(f+æv9#k;d›89KÆ	$^¿˜«ãjTèWm =pÎŒš}Qp�:z„¯®r}¯6fLî•XA*»-x¾´*é6CUyÄ÷Î¦H
.Bç¹¤Í«¬]FÐ#·†Ëàéº¯\£èÄöš¾5CBÖW\Ñ	ˆ¾ˆÕJEƒK•b?ô wÙÕVwü¾³’îŽ ½1$
ÚsV$%)^iµwgYÜÃ“r­tãY;òçË66¸Ë‰V,ôxCÜõ‚uó,¾Þ’+Qôá á•ËÞ«,õE<Ýã9Á³“¶«�Lti½½êàr`ð@ï²Ë¶4fyðÙìñÓ{õÄÒP~í,­?ŽŽm&¼šƒ Í+ðlfEA×e(v*NY€J@qSjJ`Øbä5àë;æ·�g„ªf>›ýðêL�qZ¤fƒ;£ŸÎhŒªÙz))³BŸ~ÝsŠMÀWfÐo (J›ßêX¿*4Ÿ^ääïyó{B¯YÕüôz{™m[E>‚Ñä5a¦°™�-ÿ9“ÂÏá|x7ÂxO¸�¹™–Åüï¡ÿëò¥j¢´†!š�³²ÊÇ Y¯½ßo"“WpÞÀ|D&¹)kl:�b˜“&ÅøÐ2Ž'Á0ÿ9×›¶ªÊ½Oà¿ x\¨‘bxœ•TÙ®£F|ï¯èÇ¹²löÍšDãÝÆx�;š–f¹fïÆ6þúpen’Q”´„uªN•8E„ /
C‡¡ï�,1²ðrÈ{>
¾
ap²ø¨¼²B=ÂD´z<Ã‰¢¤HŒ¤ø<+(òh…§CNT€×’¤l`ˆ*T„ž_’ïýó~åQQ8Žü­ò­Å¨Á£¢lP•u£8%Ië�‚2ÿ2’,‰‚ÈŠÐý=š§„ š)™µ>üú!ûö¯²¸ŠqÃáëÑs¾�;sí¹¹QÇ½ñ†àOMU5]U-ÍZÔ/N2Ó÷:ãYRÛ
Ç$SU©7K]éIÝ¸-¾fÒƒVºäáèÊŒ5TZ1È»2šéµ¨$žOÝOÝl®…Ëî‘U§{Ë\x‡^µ]Ã(á%)n5çd«å@òdŠ! ¸Ù®Ïñ>ÚZÓënÙŒnÍ™­ÏM7[›³°äq¶M]Ö´«4Nl-+Ä®“åž–(¢9€âÎrPt÷6È}-?îÙâJÏÞB;<kòÂÓ‚	öäºš';£¶÷w‡ž¯ýSäÎä€—–µÉöÅÄkTvótÀžwjì$Â}r½ùÚmK)Sël°éã¾GvåPµbµ´e¥TÍM/ðôVÄ]ÐyiNež‘§6­
sŸ¡Ë9›{v�‘›gKƒÞ®èƒD=ê£.o:Ä·9c¬Öª|æ«ãÍõÃkz\±¾</Üyzßn†POë¥Cû+*ñ‘]•Ë‡DçŽìA¬g_MÑ&ð¤NˆîñÆ¦Å[«ÒràµÒ1âc×íœbPàö9d]/~Ä,¶ÓN¥'½áÌLÏy²w!€û•v¨‚Y°xšÞ[®qöz‹=êú&Tll›Kí–ÁúVÒÎij_ü‡ÃZhŸEuþ|	6 õÄŸÐ©<[‘]ºGÓæb¶œ&ñ”K¼�5Ñ8L÷ÁU^ðºnñ‹M«ò«ïÑ†Ž^vÆôÿ$»+¥ZE[rz^ûœ{|ÉÔºÝGîmK³˜Ù,x—åwß–ÖšÍ—t1Ÿ<èÒœ-Ù5uw4 œ@gêùˆÒÝ‚ÄSyuQ0€¿¹»8ï;al&¿Þô«Œ¾ô‹‡!º>�¡ßæ$]…pÐ¤�QSæPÑ#’Jý€Ö“0üþ“öãKBH…ÇõsO©<š—¡=Ñ~#>ýªáá÷=Ê�‡,J‚ðîG5ï2üôÚC‹�ÿ¡î¯ñG]ß�F×·�ž xõ§­B� pøh¨R„Çý=~wÃÂËÑø“_ ~®^ûàKËbüáÛ_j¯š1ÓdÜ{�²²Êû îYï÷~0>zßÁ1Fy�sï¥l@?0 vý�eýnüÏ\ÆmU•ùœ  `6ýž“cxœ…TÉŽ£H½óyì²³ÙVÏ¨1¶ñÎæ½Ô‡’Õ�,‰õõC•K3=3­î”8dÄ‹—A,�”(CäC–wVGç\QdÑ�‡p %qÎ²"Kå°D!~(q>+yPàxI’G2'�a ¹ïD#�õxiDÁš„¸ÊQæA“×îû¾
#i4âyžþ�ç[]¡²êg¸Dù­í	k§ïâôOÀÉCY¥Á@4Ûª³¦!¨ZDµ¾~†}ûeX�U€Þû™Ì´åšì¥¶Sököa§ ÕÜ�(ÊDUsb®ŠøÄª¥rÐ”ëZ<„7EQTfk*J¶·RY’k‘ºSéî`µ."&ì}ˆêzU5ÆñÈzûvmã(D4k]7ü•=ª‚I¶md;cÛ<—S(šrÌ'.ÑÖÇ‚;Kæ³ëél<j¯hìøJJå:Ïó7Ý<ÜMÝœÑ‰¶ak�a•�9G41Uòå@	w–*FÈ`Ò*s]‡’¹ ½½îFºÁ«$£�É‹Ì›L’Üµ<:»»Ö|+|S]S 	OçƒÌR¹]æ§º_‹LsE¹´£q'[óã"¦s®ÄQ¾nnóYnž•ÓÀ|”¦‰g
peÃ@³RZ	Köt:Þ—ä–wKîhá<�ÖÞ¬H“ÏRºµ®o·ô'Î�UÓ¹J�Û–ÑWsÏO~¼-§É¤çÇ½±<î#šWòeà ¬âÊ¸Òz£/¶ÕM¨ñF.……nB5sŠ£Ç›ºD[Ó‹hha»†¿ÑüãñÕºÉf¯¯oûhº~‡º6/ÕÔELcÅu×\ð�½À-8ºjí–Í°Áç¡]éê:’nÃ-åpžÅ§�Œ†>Åó–‹ØÎ7‹l7ô9çò�5L&Î‡(~ìÛ‹xÛGh+$uÝ®ù«	§­Ÿ4B;óu±‰³=­‘måz$ç(°f¦Ü„ÝÂÎ*›[ÖÞ&{6¯°î5Ï‰Áôü¡-¦SäF·øQ®ÞŽÚC|:<yv÷+G‹âˆÙ4Mk„Ñ:ˆ/;
ü‘iEWæ�ÙŸí¦?ßÊíV}é6¶z§Ns�ÁGðKœ±Ïõ9	¤>Ûç(jÒ!*ðúÄ|ÿ’Wc†ùg91OïËO)úT¼víC°B ÃU¿faÊ'¶zyTC˜è†ƒß97ì0)Œ2F](;m¶Ñµ~ê=)>æw¯v·wYdžÙ÷ûý�ä_(ê½luîA‚¼ÞSçPæF¨wÜßÛ^S4þ,%~ôÜ;-Œp6þ¬è¿|¤Í»(/*‘KÆy‰½Ú%¶=Ÿü|RôžÆq…ÒÎÒKaŒKªK•¢ì(Èºü°ï÷œvü¥®ê<Ç%ùQSÿÔ' ‰•xœ­�IjÄ0 ï~E†mÖ2„!_i©[cC,	Y>ÌïÇÇ< ×*(¨Ñ™ÁÒ’£
"`àÅxÖN&ëQ\T"HAØÓÔ°sà=+Ô‘tB”èœKŽX+‡&Fe²È)
Åžc­ðà¶baAø6Þm­½ý¡?çÁý˜KíÜ~_ósëçT÷Hç�]¬ÔnÂ	1]tßÆàÎN™qÜ‰€¸q!Œu@ª%oOø(m‡k¦~]îø|„*b›—xœ�ÌËJÅ0FáyžbÏÙMsiå ‚g"Îÿìl=•¶	9)xÞÞË#8ý`­ÞT	Ãœ Å)E':ûÑYÎYÂ ê$YI£fo*šî�¢O!3¼�3À³c%b°nfÖÀÖA=G«G?—F¸h=£]±ƒN—£jû“‡�Ëz+e»§!N~²!O7™Í�nKïú¿ÚÈrGÈ™ž—/zz$YûQé³$*;½½¼ÞçYëZ®æ�…Qº�xœ•�IJÅ@ ÷}Š·TéßCÒÝè_y‡7Å4&?¡ðøN'pWÔ¢ZQf¯âé)ƒVÈ…ÑQÅÇA#Í„Ž8%s`ÑKËNÕ“ç9M81‡49ôžƒq<E™„“ìmÙ`Õc�‡Ú-¿üô¾a^ïxßá4¦8Fëb€;Zk¾í–[Óÿv†ó=ôª0ÿ<½žŸá-#/
W’+Òªð²â‡ž;ýù[¸ìj§Ê%“Êµù¥÷U
“xœ�Ë]j!†áûYÅ¹l	)::Ž†R
…Ðmõ³#™?¥t÷Mº„Þ¾<o- ¥˜ôàœFì�ö0É˜’Ð=Øú¨L„–Î u;¬•dïK¥xœÐ£ò!8•à‚ÕIx/û˜î¢ãV§­Ø'.?¼2½mGù+ï_çù%lËÉÑ£¶Vi:‰Qˆî^—\+þww!_+ût�ù†ÏæéƒÃzÊñ\·Ö}—\A'jÎé¡¦æŸ»_u´W“xœ�NInÄ ¼óŠ>&ŠdÙ`¶(Šò•Š1Ëa~’'äRRmªê•¼!cyTNI-ÜÄ ¡&½Zg¥ä7&J·³+]'îv¹F%½°B¯,<è�¥¨•�Òh/Žþ(°ÑýÀúÄá£�›êŸòudLçâKþ„M›¹m¹åð¶êueSÍ©wú_›ùô£¸T2Á™æë;AºZ'P"Ä¿ŸàÆ�Z‡6Òô_~ù¶ˆeÛ!—0N‚J­œ£§rÍðñÊ~ ÕaHœxœ�ŒQ
Â0ÿsŠý$Mlv#"žÀ;l’W-X[Òz{‹Gðw˜™VêŠ¸Ø'ÎQD]ìAbé½ºŒÁGïRˆ°0‹V<u\„.ß),GÑÍw,)E{àà%8?}µû\IW,w­}*�Ö×‚ú#—Û¤ãcŸçé¼-¥gmhgÙZ³ÑilÿÕ&�GÒR¨amtÓJæ
ºŽo*XóÇ|S´M˜�xœ�ËAŽÂ0@Ñ}NáýHÈ©“�â*®ë@%šVIXp{*ŽÀöë¿^Í@s0Tò9Žž2›�p
Sˆ4EŒ9{ÎXÐíR­tHžx<�0JÊ!N6¨?£Fb�‡Ðavòê�­‚4ÛRßR.íµ[ý–Û}•åyÒm½‚Ï1%böð‡#¢;êºôn¿i7oÚþ¡½‹Bµc,K¹Ãi�¡o±ÁRr÷ÆOœxœ�ŽAŠÃ0E÷>…ö…¢8vä”2ÌUdYn�lwÑÛ7ÌfõàÁãÿÑTa¥°N�“É«÷q–Š6øiáŒòŒšÐšƒ›–œˆÅœ	Gô#Û„Y|ðóêìÖì9“á÷xÖÜõxrûpa¸÷÷¡íÏü>vÞ^W©ûLü²Ø™.Hˆæ´û6†þ¯6rîêú§µhšõ</Ú�K‚T¥Ã¨à±ÃVrcóÐ›S¢˜xœ�ÍAjÅ €á½§˜}á1j4c)¥WãØh¢èdÑÛ¿Ð#tûÃÇ¯CŠÍ5…µÄˆž­C!ë)ç	â«!zZ–d:9b¬99	\7ë²w™$:BGB)áV¥¬¥’áKŸm OéO¿|2|Ì«Ëø+_ßï?�­Ÿ`W
1
o¸"š»»ªüO›íþÊ;\½°
ô6´Á‚ˆ°Ÿ 2uŸnÞÛóYÃRš+xœ�RÉnÛ0½ë+æV©íKÐM #=9¸í9 ¤¡DX"rhWýúŽ]Ž=|˜·Ì#É"B^åq×ÄY‚MÕ52k1‰’´¨Û:©óFÔ²¨ËX$I0‹š (Ó¶N3™–eÒ5(«,Ëâ¼¨dQÆy¥¶mRDY <Æ‚p8Â.BxçüŒvC>ö“Pã¡5ÓˆË*ÏÙ.�à.*£(`tRDøì€ÐÑÜßÃ$ÎñÓ#8¯ÁÌ*»Ó·ç—Ï§§ãË19¾�÷p’C)�3Þ¶³5Á×áO�Î\õhDç@l¨TßÉ[iÍŸ�~è5´îÀÏ¯cŠ€Ð€Á¨.ÌGÆ—‰‹<À£×oœÐÍF;Ó-pµ«Ë tÏ½PšóÐ Ü-Ô[>»¥i¼ÕØ­ê ý8Â`ÌHMh<�Ñ€´X¾)�`½>À³¹Þ!¸³šg¦6G’Â�»[9%àß~˜1[å¨ú�àÄñnýÀ®rðÐK_T¯Å¸ßzt°yPÿj“WX»c¡8šØå¯¨‰Wå8’?ÌÆú½#EK^Œã²>)ü@Žç–�xœ�ŽAjÄ0E÷>…v�¡0ØN'¥”v6í5äXnÌ8vHJ{úš)ä ÝHâÁú¼�ö�j;7ªÞ[”ºéZkhpªsÖßèº­v­®”é»ÑzßçÐ64a”Ú¨^«VkMhƒT
î<•p£eÂõ3Âó¶/´ÞÉëçŒ1]Æ2¿€²½1C�¥•RT:Gfú_Z0m|"Mç'PrŽ8ÎTv†Pû¨÷+8ª½¥¾|åTÐÃTÊMˆëž6ðpO|gGöd¶3ÜbJä�'‚âŸL¾ÃRî‹ø˜ºoß›xœ��ÁNÃ0†ïy
7�ª4]›bw$¤�pw[“’¤ë¸ñ<!OBF¹À‘“íßöçß)®¸¤…VËR•´àKQ–Z¹ÀZ¶¢ÖÔ j¹d=r	DSW$*±¬ê4r^“GÚZ6�P²­x[Š•d8¤½€‘ú=†7t·qè)|+»í±Ð¾»‡R.ëzUŠFÀ—œ³¬v6%úß6kíy6’šß �{Ä½×ÙÎVç�$£uÆ�}.z…ýÎ:}8DÆž÷ô#¨!%ï`æ�>Z}¸›öŠßÀÙ|#F0„>ß?þÜcS¯µŽÌŽNùµÉ[–NáI½�NÆhw®€õ1;3Á÷S{CÐß±”�‘ ˆƒM³Á1òL´É£Û”Ólwa_ÃO?V“¾¡×�bšì
f«˜™$xœ��MsÓ0†ïþï{š&þLìÀ04Ô0�¶I¦1ÃpÊÈÒ:øIÆÍ¿G'8r“vvßçÙ5Š1�ÒhY†ñ ª¸`å*\T™OI*ˆeUHÙ’%NÏµY@<IS–¦”,mC¹â±_F¡H£*NÉ¯‘„¥ˆ6˜s§À4õg¦.¬ex§‡žÔµòáÔ0YÏy×¼G°J“$‚ÌÇ�¿ò}ÇViýß´cH—BòÖ˜$p&&Hit-F*q·ÀÐ×7>oP3u¢ÛJÖ=HCŽs‹¯u.ÕdS´FÛ�ÐÔ
ÜÝ??l�¶íXìó-ÜýîP,îó§¼È¡èç ]<›¸¥ñJ»"Ö8DÐ²=YæþK1›¾ÍPiÏlàÆþóÓS{3<Imvåwâý‹”¬.3˜PX°^.þ(¾…þ!{�QÚ»ÆùÇ—¼8>æßì¢ÿ¸Z¡Oòu�ÍÐÎGe�À8§Þ"Ì™ðBºïZMvnê\QÏµQÄ×ƒèH·oèÕªyÎo‚»•“*xœ��MnÛ0…÷:Å,íÆ‰¤ÈÖOP5P· â`(Žl6©’TuÕCô„=IG’“î³"ððæ½�/8"eC’*ªrQ”›,ÍS,Iˆì6ÉDuÛ`Vå…,¢™ e¾–ëT–M�ÉZHÊPnÒœJ²tÝlŠŒd†¢öád §î„n@ƒðÑ÷¹Iù|lQé›Ú¶Ÿ åâM•¦IWI‘$«­
�Þw5„aq&±¼ƒ®ZÕàåµ5z€Fiþ³³gÏéÿ�o»l÷÷ç
ÎNò|ñ³WŽ e«L]CŒ�Š_ÒøÃt±ÐÊ}ýLÁÇVü :øH{6Ú¢\‚± ­9rÉ%ÉÃ8í¿?â/»‡ÝaïŸà è¤Œ|õnÙ	‹¾£VP3} X’¦@—NVm7pá,Î KNÿºélçAh[?+sdŒ£2à9…Ì¼(¯‚uþ²\½1CÃ=pÆvüó<×Ó=,æþ·âkjùe½2¬à1Q¥_ÂIII¸öÿð­•ÄÙ�èC°“!ØN ãz3ÏƒÖAYã£+Pã�xœ�ËIn!@Ñ=§¨e¬(EÓ–eå&Q…�Ü¢i¥sûGÈöéÿÞ˜!Xål’)qDŒŽ:�”fPGÅCF­B•/"êÀÆåì<ÚŒÁ»œöÊÖJ¬I9Ë,hï�µm\Ô¾h!¸n{åö'ï÷™ÊtŽë|´n�RèàUZ)Å�Î¥wþß-r9^–rœ.Ðxë­Äy¢'C_ápæÃè·©,û¿U}Þ7PæŒ©­µr‚Dí³,'ñÁ,Z�xœ�ËKj!€á½§¨eB ¨í£!dæ¥–Ó2öÛ^äö™™#d÷óÁ?:3˜âJd8Í^jžœ‰¶�u�”ÎerNMAìÔy€J‘CÔ*ûâ´Éœ²ÁÈ–ýäX+Ã”cN!	:Ç¼u ƒ÷™ú/­ŸÇ¹sÊ÷u¡ÚÞÓ¶|�òh-*Þ¤—RÜu©cðÿn‘êìglõ˜¡4º1Œ~q9#¼ô­µº^_ÅgP(Ÿxœ�ŽAN! ï¼¢�šMÐc4Ôg4Ðì’ÝÃ$îï}‚×JUR£3ƒ�„GÔQYStZú¨	"’K�I9­GÑ¨ó2 d«l¶“—¨‚³ÄZ•³tJ[¶Ž³	^c$AÛ¸Ô´r»P¿ÓBð¼n�ûy=ÏTnO±Î/ &gÌ4)tp�“”b§sƒÿW‹XN0—s§Áðþ£ÂGŸ[€·8J]VxHÜnõKù†Ã®ö¾�^ùWc‚@ñºµGñY_Yîš/xœ�’ÁnÔ0†ï~Š9ThWÇIœn³BE=•K%®�ØãÆZ'Žlg·Bâ!xBž'…UÅ‘«óÏ?ÿ÷OR 2ª!ÙJ¤n×6(•4e«…Ð5
¡µ¢¬”‘›0Ð˜ ’Út¥ºêJªTgÚ+YíJµ«•,ëQ¶×µhdU3œSï`¤©ÇðŒ#Âû8OÖ—��ZÇ•>@¹»nš+Ù–.ÅN–_›ýß43öiÁû>4BÀ¯?!PôîHÐû�xŸ&çÓt„70¯Áá³ŸSd¬ÇQ;ºÍ:pÞHÃ<ÁÃÅ7;L>$>PB®mø^œ­àÔ[Õ=Ù˜"ØF²©§À^L÷ëžMª°c3¨(}JS¡ü˜‚wŽB,¶0æHk˜n^2°ÍEž.bî€ŠDŽæÉyÔ
�+—qx P~²_Áu­&Xu[ÎØ'­ÿV°�ÝfÍf»‡ºÃ‚g‚à@Øt>Ç ½…äÁØQ³³?‡¯>â¹Ã•íüõ-Hpt$³÷rŸ+ÛpþZT¾ˆ‡Ïù¤Ùc‚Sî”#@!ävŒ‰Pƒ7€¬ËuÀÍÝ—›»ûå¼îi©]ùcþg:Ÿ‡ÿÜ.s£é�Y0²µsªg¿xfÖ™?xœ�“MoÓ@†ïþsk¢ÖÉÆvj;”Š¦„ÑphZ	¸Tk{ÖYjïšýH[~=c'­@B¸ywçã�ç;ƒq–¦q’,gQeBˆ˜åç¬Èç".²¤ÈgqY•AÇ*EÎcQVYD/Q,N‹\$3�LÌ“˜GYšpÁ¢"OîÝVà»-7O\q8³¾C3Ü¼©[.›I©Ûs˜¥Ù|~š$,‚c–2Ðm+�ÃÿË„|\€ €Ppë öÜTp³ºZ½¿¾Xß]~¸ýüénóñÛênùõfµ�³×0Ë×KÝ`ƒµá-ÔèÞÉ¡‘$"¶^ŽƒàrëÕ=gá|ŸADÀ:m°§á%½ðî¥DÉ•ÒZÝìÝYÐ;­/££%¯àx´n1<ƒ´TMC!ë£ñ	´ü^ª�Rž†îàU¥T£yÅ
uFWÔ³¦ŠÑiÈ²�Í�dT½$‡çðœ sÆÆ“ ÁšrŠj7qvAâ¾céþA¨7�¢<I€;š˜TøF�&]Æ�ÉÇà¶F?�Ü
÷�£Ö\ÕÄe˜2Üó:´µd(VSïdc§;ÞÈŠ;©Õ åoÖ_:J­¨¹r¯h/6\à`ÉFþDPúP	mJÂë¶HØ{­Æ+'[‚TöÌ-lbhIž@î½$ÖcRæÈ€=ú´xEæ÷'KóY_gªnÛ¬ýãh`½‡ˆÕÉa5Ê;:¾”ÞbCÛk§‰]øŒ¾“
,Mr°Êö³X¼‡LÞN(®_¡#úïòûa4ë¥C*Û?Nð‘·EÃåÕÅíÛÕ¤­dwéÛþgíÃþØèàÎ”K‘xœ•ŽÁjÄ @ïùŠ9¶,[ÆDc\J)ì©Ÿ1:#+$*Æ@ó÷Í/ôúà=^o" cˆ“
­Ñ	Õ¨/ÆE1£�Ø£âÙ‰Â¡R“Ü!"z§µ˜ÙF·«¬›yÑ¤ƒö>«É’�ŒýU<W:Xà3—&u=¿)÷W+5…�P¶/Pv1§Ù¸¡E.º¥ÞåÿæÒrú¤•!exþÀj+1­r÷´_©rÂÛuWîGeêû¹wÙø}øy¢Ue”xœ•ÌÍi1@á»ª˜»ÁÌ®þMŸRÆHaaYZ-Ø·‘
S‰Ý‚¯¾73IÎÛ´†Å`ÞE­4kÉ	®)£vq!¦,6Ü&XÐHŒ!……œÍ’È¯V/õk£¢lœ±(hŸ—>à\iO­Þêã‹Ú¼Œ¾•xŒýö	‹uZ¡”^Ã-¢xÕ[™“ß—"—û	Z¹CØKM�
œ¿áÿ÷J+ZoÈ•®üÜ(TNâ	6±Pð˜xœ•Ë±jÃ0€áÝOqcK‚¹“K*¥2u+ºŸ¤“#âHFÈ¥yûvëÜõçû{o¡6)š‰Ù±E¥ŒŽV“O2'VÞx›â°q“Ò!’1dúèç@vF’pÒQED—‚c²nBñvà½_jƒóÊ{x.µÉ¶Þ_¹ôK«[c¨· cO*«Ð ¿õ–{—ÿŸÃ�4" ä'ð{^#Äç·#”ü¡nw¸
|¾ÀCËeár„…¯²s[¤??s#U2“xœ•ÍAjÃ0@Ñ½N1Ë†B‘%Y’K	…nº,ºŸ‘FñÇ6²Èí›+tûáñ[e£�ÅTÈz
a|F.ã@�Šú�¨hòjÃÊKƒ�³AWt
Q›Ž:2±çà¢¥ìŠåB>‡h‘mZ+|Íxd†�e­¼Í�O\ÚT×MÒ[ZogèBì�6n°ðªƒÖêYoÒÿ_ª$ï@‡Ì²ÀïÏë…Üa¿áeç�kxàr�ýÈ8Ástç*E®¸ËIýå¡W’xœ•ËÁŠÃ €á»O1Ç.]�hLÊ²,ä´·B¡÷QÇVšjÍÛo^¡Çï‡¿Uf0
�tcçÙ ÆŽ´ë{«©´�‘�3ÒQ$±PåÜ :¯¢iÐAé@f÷N¯»QsoµEQÐÚî¥Â4Ó¾s©¼ÌÛ/åv¯eIþË—ç(;-±C	Gi¥{}¦ÖøýSÄô:�[Ó $˜þ>!§ø²lð`¸ž/Ð¨Þ¸Á¡¦|£ü!þ–O—-xœ•RMoÛ0½ëWðÖm'v,+†µA�û*�µÀN#Ñ³ G2$9‹ÿý$IsÙa7Š|�ïQdpDP Ø	µ(e¥¸¨©B,xCM¥ŠIðšÊëeU°™ ’Wõn!H(.%/¥ZÊb^ŠfY¯%ê¼œ7%Ã!´ÖÁºÃA|0ÖQß�ŸÐ„ÖÙ^ËLÚýG˜ózYæ¹%Üæ<ÏYÌîuôÿLÖèã
TÄÚa³ù|»Aw
nÏykàõyJ;’¡ûÙ¬ŸÀÆDMiM@=Ehn ”ä=„J´ÖúpãÁ�>Ð^ßõ‘�»eðdbØt±ë´ù�0ñ9uyHäàÆT–]	%‘»dþâ2.bx)ñOCei
íûÞY”íŠÍ³Ä÷×¼ÁOJ§×6V·_m_ï¿¾<‚'é(°EÏC×�\tÈÇuÛ¸kÎDVdð�ä=üÑ¡�&»¸¼‚•¼ô*µ £�32ˆÞÝÑäÛÅÖèÂÛOzr-‰±µ�ÝO‡Cjö0®ÎGð£<,áÝüÛôƒtïÿuì/X´ðæž xœ•PMOÃ0½çWøBí²vY»	0®$÷|¸4¢KªÄ-Ý¿'Ý´B8Ùz~ö{~¡©”ÐõFa©Œ(‹¢©ŠÆpcÔªD.U%‹B×B6¬—�ØˆR˜õZÕUÉQð¢PÍZ¨MÃµ’¼®¾â¢.7LÔú »NáÚù€}w¸“ŽÚà{«sí÷·°¬j±â¼ªj¸âç,¡{K„ÿßd��¶`×`´½þLwâ\¾,µÐúHÐØã!îAj�12ö“ºeX„Á-ÌÎgl9JÛIÕ!XÔ"$ŽÃ�<;’6u9¼E„ÓÖ<„,ë­¹9ª¦6Ø1I ògéùÌûËë/gó:ýëi&ã„z g§ÝÒ85u9c;ŸÝóF“=¶çìžû!‚€‹åÌq¢Ë¿òdßÉÝ¯ò™xœ•�ÍNÃ0„ï~Š=‚P+»‰óS!D)WÄ3,ëMk)YG¶Sµo�[�ÜV³šoF“#3è®ÝP]w¶êúŠÐ™f¨È¢ej­ÕÆÖØ”·š1²d¨Lï6}§Én\‹ÆêH3jWwUÝ7Uc›†Qá’�!Â~ÄÅ1<Jˆ<�—g”|Œaö´¦0=�i;[kmët«µ*êäsæÿ;ÕàÏ[@ç€åäc�éÚ×Kš™2¤Ì3|ðP`à
/\”zõx��¼¾$Ð�Kä-¤K1L”GXOEÅ�‘¯0_*ÅE¤¤ }¹ÖjçÜ•ò+,‡âuSFqð™Â"9ÁM$â”`b:¢ø4¥µRû°ÚÝ¦c·z¹l¿gxŸ—îÌÛ-—Ïùþ¯iÔ'œuž™xœ•�±N1D{ÅvA";9ŸsB„44
ø Ç^ë,]ì“½&w�Š ¡ �Ý™·³”a³µ­ÂF´ê¨œAe[Ûaç„²ÁV®•k87kÉF�04Ú8½±kìW¢Ž[Nnø±é:¡¶Ú™NHËt¡>&8ºX„»ŽÃü õ)ŽÞ¬L<ÝC5È
hx7\qÎªzòDø's~ÚAÉ®Œšzpõ‚à§%†ðlŠ3dÂ‘±·�JÈpöÕµÈÅÆœ{ozH˜‘2¼îßžn!ÇŸ¸ÌB¤J(Á®à½bqo<}£)UÂeýèƒNóŠ±C\î¿^ƒvù8ï.5_Æ’AÂ•xáD×UgŸg�3�"xœ•‘ÏNÃ0Æïy
ßØ„ÊÒµYÛ	!`\‰=Aþ¸,R›T‰Ã¶·'›8Y²ýýüÙ¦€}½âM‹KSUjÕÔ\jÙqUµ¥ŠkÞ¨ÎÔR-Wl’�P¦2Ëv¥Q	Äº.ÛÒ4¢ª±%
lëR�]Åd¢�°d2·Îœ†ã½t´~²úFûñÊ¦5çÕ²ƒkÞpÎrv´Dø%ëía)"<aÖ�ÖIBØ#áÁÙXICfïm6—ŠÂùÂH½cì‹SýNø©íC@MÐò=®Yq©1µw½#Ìœÿ1ö;«w`	ŒÇè®bš&hž	/ß#�‡E6½ø�á§à{;`\ìeh¡¬Ëš­OA#œéç¦L•ì` [ŸÛøâáô4Åãq}9íë”"˜•ÏyGx ù_çfŸp\ºZ–xœ•�±NÃ0†w?Åm€PŠã4uR!”…Ê\œ3±äØQrQÛ·Ç­ÊÂÀjÝ÷òÇ#´M.£Ñj•ë2·FçëªÊTJåu¡Ta[U�Ž‹Ö‰J6›¢VZUvS´6E©eSHUÚ¦Ör#pæ.Ž°ó8·÷!Ž4øÓ#îÆ88³2±€\WåZ&ª†[©¥éµwÌôRXwÜÂ<¼»#Dk�qèÁ…‰Ñû4xpÜA–…˜µH}B|v/”d½ÈûÓÄÔO¦�4…+†i†8ò_‰}Z4pNC¿Æ³~aí\ºép„yŸµÑÌ}JI­X¬�õø•4Ž±ñ6¥310º@ãÝîõü¯•»˜=]ºR›=Ÿ¶?�>†y‚®ó·DG¾ù«›ø¡ø¥¦›%xœ•QAŽÛ0¼ë¼%ÁÖ©ì8ŠEÓ,Pì¡í¡ý -Q±P›2,©H~¿²w{ê¡'g†ŒÔ²ÕU%wÇ£®kÛ(u´å®6
-IeU©HJ4µq"Ž +¥±‘ÊÈÚ’ªv¦ÜÏ¥¶ÍAç!ØÔU»·DSìü—“!øÄ~¢±¿AŽÝäG§·ÚŸ¡<4ûZÊ²Qð RŠü;¸éÿ™ÂºÛ	‡ˆ}?Ü-×py‚–lž mr½q|âwGðÍEB8ëè<˜sVt^	ÖÆë?4m¯sË¬õñ.8œR›8¦¢ÇH!n„ñ€}„ÿÒ";NT¼Ú ó4¦�uaÅ³¿9²YÖ'6+ð±#±„iÜ
q6�aõôgµ`ð2,à‘òšÇÙüºgpoñië¢`_¤!Ï|Þd&jZ˜¯yó33 ³+Žèòß&k_|q^ŽG¦øz?½âç˜ìa]~_Úé7ÿ:ŽxÉÉ™xœ•ÌMjÃ0@á½N1Ë†@Ñ¯m•
é¦›ÞaF3""¶eâÛ7WèöÁ÷z²%ƒ†‰)æHd|`�'Éâíˆ>dFV6Y;Prl²—ÆQ'7Xo¢(œ$D›¼Â½ßjƒëŒ;|®µÉ6_¸ö[«[Iï©.0ã\œ¢vpÖ£ÖêU—Ò»ü_ª\ž°oŒ]àú½Âoyíef8ÃãxtYø5«¼­¾kºK;©?´Rø’xœ�ÍM
Â0@á}N‘½ “IšiEDpå1¦“éÔ¦„táí­àÜ>øxµ¨Z’Å¦à†Ü·}�¢P¡çÍÆE×j‡®D0‘gå¤$MŠÉ¹aðª­ Q2¼×)ûXxOj¯k.º-ï;¯u*y›å,ùu³ŽÚÆwèºhO@ æ¨¯¹VýSxøI#ÇO/¶–y¿þi>«D§›xœ•�ÍNÃ0„ïyŠ•¸€P‚óë´Bµ\¸p¢àŸucÅ±­ØíÛãªj9sZifvöÛ¸"!ƒhi#»¶gd Œ�œ“Žµ ªÁŽ6-Û4…g+Ú\Òa ›†×X×|¤�lä(GÚ*5òVÈ^P1öµ*XŠ“[aoX’¯Ö­èÍù�Ù8­ÎkQ	·¼AMÇ¾ÝÔMßÂ3¡„Y]tŒøÿÍB!‹[`RÂ—>�2lFPá¼qLæVž´‘EQ^ýÊæä�Žì’½z V·@Öý|ðXWmuzºç�3xm-JÐÖ§²UuÔÇ'¬’‰Î‹Ñv¾wÝÎî.cû§@õ3]ºÑ•¥^|Ê=eé|ÔÎB`Vr—ßa&`q°QØ¾ì?®Ø\[¶žA01!è #$_ü*‚šk’xœ•‹K
Ã @÷žÂ}¡ÌŒ±£PJ¡'™èHIb½}½B·ïÓ›ª-ˆ LBÔAÉ8M 8{˜ƒB"ŠO³9¥éÑm&*F«Åi äØÑ1¦2jI>ÒœØÈÕ—Úìg“+«}µé¹}ßrô¥ÕsM÷T÷—EÞG‡ö`Ý×ÞõÿÓˆùÿq?“xœ•�ÍN„0F÷<Å]™ˆÀ -Æ3î]è¼@n¥ M¹ˆî|ŸÐ'±C&îÜ~¹çä´á ¦Ñ”¼8´MËÚQ¶9Ã²æ%b-yÅ$GÌ/Ž*![ÎDSÕº,ZÙê†qe”2†i¦ëŠUÈj�ˆ™:à¡³F¸]@ß¿ß‹‘ºà¼U™rÃŒ×‡2çEûœåy×ÁáÿÉÄØ·X‚ðpœÇÌØwi6Q@1ìR°#<¡ÐBö˜™à.“‰}'ìñ%Qê%ÉU<ó½P8�Š(á…z^-p2þÂæùYK]ì[B”v¿)|}|ÂâÂyºI·¢„ÁŠ,ÁÂ~{A
bÔàƒÓ³"ëFØ=:ý[�®¹¯h‚=ÎÆ`8¹>Æª3�ƒMœ|•ð£§�#xœ•�MnÛ0F÷:Åìì ˆMÉ’i9mÑ6mƒtåEz€±8”˜Š? Çhu� GìIJÅbt×CÎ÷½GŽD€•ªñˆ›ºiKYK¹#TÛVbµ)ëFÌÃVµ¥(Fr-Ö»f“�ºªÝ”mµUGARR¥�²Ü
ÂÒ¦*ðÄƒ�p7âI¼q>R§÷èxˆ>˜nÕyûJ™Ó*Q
	×B
Qä©5Ìôÿ›…6?÷�È)pÞÝ|=|¾c±§˜@ùîdgãgGðÂàÙßBÞƒO¾ûNQ›‘`ð–VÛòP7ÐÉ7�S =x7NÐ�˜’ÑÓ¹`ý¨ŸKÏ‰‹[®_÷F¯Ð1ÀŸ_¿añÒ¿( –�4RÑÂGÏðáð ‘ž¨ãô
®ó×Í*‡9ð*3¼î¡óaºÀdÊ$^_ªEŸ“—Æye‰q¥L„·ç‡sÞ· �	˜gòD‘�wÏµ¶Ë³^¶¹ú×â/ü'½Ÿ‘2xœ•‘MnÛ0…÷:Å,Ä?RlIVP­]·(�ŸÖ�]#rd±¡H�ÆÉz€±')e'‹´«î$òÍ÷ß°#YVi“Å«y–ÕXiÙÌæUV	™ÏªE™¥b6+I�ŽÊ*+›¢(ó¬(±®çu–—b!°‘ççµ˜S³H‡¹·ÖÁJc�oŒuÔë§wh¸u¶Wb"l÷²r‘Ï²*¯r8KË4Mâi§˜éÿ'“F=^€£Î>l{mQ.‘EQÂ¡oa¯Œ´ûtxOP[a�.>K<ß�Ú‡µ¦D ÖÀäT×[ÇÐ#·>IÆ°9XÈ¿<N¼SešhÅ.Ž¦Lšv»i8HÇõQ;agðáóéEÐ“‘Êìà(ñ`,hkv‘¨m4·æ˜~È¤Éƒ– •#Áú	ØÂí³G¶j1ÊgÔ8>‰�Õ:¢¢.x‚e0“sr:‰!	»“SPÆ3¡ÛD#B¦Müûv¸�È+Œ+ø²ÞÜ-onïV7×«íf³¾^}Õ<(„ÛõåúÓæýÕ?J2ôQ=ÆdÙâXéñ-{Åm+£©|Õ2 ‘ÐYq¾Åž|œß>Ëz÷¸£É›9°¼pªçx)ûk @“ájèù÷Ï_Ã®Ç½µúå0â–Ai9­�nGTPL‡Å÷è}òF¡µ—7xœ•RÁŽÓ0½ç+æXÔv×mš¦Y!ê‚àÀe{DMìÉÆÔ±#ÛYµ8ò|â~	ã¦ÝÕÂ‰K�ç½yïÙÑA­pÕTH¸¬K2Ï‹.«b•S�B¬sQVT¬f=z²¬«ªu%—9*‘‹¼Ø,„¬ÄF6¨ŠzÙ¨¦Ì›2Ã!¶ÎÃÖà ^[ç©7Ç·hcë]¯å•tÝX”›"_”Uµ„©(…È¸Úééÿ‘™§etþ©¡ç/*�NÑBËêQ›0ãšU:jgÑ@ËMä¯¦'Ÿesxˆžy^`n ÃÚ¶ƒÝïô‚É­0±\Åðj»œF’©“�ÏŸvéœyï¨s4ªúÖ¢U†>ã±¦]~çë]Ú*:p?ð1x7D
×—ÚßÞy�ÇØhCsö½3†üØ¾u6¸“J‚.�Úº®÷ÂÉ‹îâRžOFà%Ù’ÜoŸÓúø"¬øQÏÏÅR€Ñ–pîÄÄÀþŸÕ<+/„˜^:/·3`€«¿s`Éýd°CHÙ‰�»§ç™§$oÉÐ}25ôÆ¡š'ó	&“-†õ[`oÛq¿ã7�÷ô$7id–ú µw{² Ž;-Aw½ó1éÿ7Oxüõz~€ü4o?�ñ|?ñ:õrpü›ÚÎ'é;'÷!Ë¾„½îAê¯Ù¤30Ù˜&xœ•PKjAÜÏ)ÞÎöX=š¿	!Ë`ÐÂi‘•éé÷Æêd¦»éé±ÑÎ›d�`ð|ƒ—¾‰.�+¤'‰ðÎ›æuQõª^9Kˆ	fEV¥q™aÅQä%‹²¢H#ÌY•çeœÔL†[Râ”E˜&¹`T`Tˆií‡$gEÅ°Œëª®¦T+Þ»•¶ð±á=¼QÚ’iÖï¸r+«�ÇB·o!Ê‹4Ž²iRÂ!Ë<ÚJçèõÊ &îN 	{7üš †ÍíOZuÎ‹á:g‰·½ Ö@ïd#Ý:BØÜ=ý~ü–+Ôíry~:ÃæÛP\i‰þƒº8ÔÕgîØu0¢F¶RñN‚ÒH'Â®�ÓãaÕýœÎÎÞ/ç‹Ë³óùìrñébö/Ã\ùWBñüVÜ¢ðb„Ô¢o}»»û9½{ç©ž¶£ÎŠÉ›X7qžnE>ÝøÿE±÷ÙâR]…N‡
V»«|6guÓ�=‚Þ4šã> ÜPr#÷Ð¿åþ…^$õÆsé›ÞÜ…½lp˜‚?·@×˜,xœ•RÍj1ÝÏS|Ë{)íÍLæ6S‘úƒ‹"B¥ûLòÅ‰Í$C’i¹.Ä�.-H7BÁ­� ø6÷ôüÒV+îÜ�äË9œs¾äˆ=o÷gºf‚s”lÝŠ^×FcÉ™ªk¦U5Éˆ>çM·æ†tëºuß¶šk.yÇö…âæ@tmËZÔ•œó"<rrÖ÷}ˆ8¹ÍCéóÃdÕž
ã¨©ÕkÑÔ°ÃcÝŽ6güfePæ{ QÏœË3„¶ï.ÁX‡`¤Ê!n`NêÕI£œ&ë_Ñ¹éµÃXU»°½ú
ýl�~ŽçO‰¹Xþá.RT+FiýŠª°ÙbZõÝÛ{9-+ tv´^fLð–zg=Á`@F5Xòågç`6=F‡5acÑiònäìr*>>ÿñí*R.<ô.¨ÓÛŸþu(U)Aw�õ/ö1¿�ú×¨29I¹¤þ�¾H.*/ÃŸø7‹å�Âá];�ì„3é¬¾ñSØj@uZøÇ4,]Å0Bè¯h˜³uÅÌÏ/—�v6ÍŸÉ4 íÕÒ¦$µP3°>eéUD,H™âŽÅÜÂ‡›e;G–þÞöê=–ä×èÅLRª_›ó÷Øœ'xœ•RÍnÛ0¾û)x\Ñ¤‘Ý8¶†aØa·=%Ò¶`Y$y¨óôS’¶C�]v‘ ’ß?(GfP4È†‰ª–w§¡éU#v’)„–§q¬FvŽº©Oª&9ˆc_ë†PI©Eßò3
„¬Ú“n»
×<ùg‹+1|q>r°Û7tyŠ>ý¤ýòê®oŸëVH	�¢¢*ÕÅäÌÿ�¬tÑãÏ@Lk('h_Ëµ„È)Áš�5y«ª=üÀ°&HQÒT6£Ãµ—‘sÜžr‚OÌ|Ìiwãzø†cô1Œ#~¹�:ÐSâzÇÿER4—‹eÈ[`à—[£�¤'^ðF‘ádÎüQT{7˜ñ]mÅ‚‰¬3ôoZ`Ñ�iu#ÌEÁý.c…âûœr,¤nŠÏ¯aœ§ÕÍ×±û¿Eu¾Ú^ƒ53&ãÅ„_›ÓÅ”¢GÚÆòÕwIùJÈ´OÙGùÎðÓ”?£-£Û�Z�¥û£ú±ÖÀ˜$xœ•‘QnÔ0†ßsŠylÕv×IšM‚¢!*ñT‰LìIcÖk[c¶à ‘“0Yèxùóÿÿþmg&‚~$3Ìú êû¹¥^!½ŒcOõÜê…t3xhªˆL>CÛJ©ZX?¨f”©:˜e\°îôØªÓú
K^ÃG‡Å¼õ�)º—÷èóÊ!Z½Óáôê~èÚú¾«Ü¨^©JèÉæLÿï¬˜Ô9ðXŠs0MÜH
¡‡Ö«Í¤sa‚W]UÝÁgŒ%Ab½/Ùº´‡«º‡Å:J×bÞ¸™¯ÌÐÁ�xæx†^ïègH¹Ì	~ÿü%ØSt`,¸pÄd·FbûP0_’­_Sær©µ7v—\M�’ï3ZO|-ò'úÁRºæàq‚ˆG´rHÉÊU^öÓ£h?Ù3¤U>Êü½ÎV@j¥[ ÿ}·M%“Lë�…ˆék4(ùõŸi÷-s±ÎÊ+œì3Ë¾¿Xù½½çí?ÁeQý²KÀ!‘xœ•ŽMjÃ0F÷:ÅìK‹-[²&„PÈ¢ä£ù!&‰e%mo÷Ý¾ï}ðZU…lÉL¹›º�LFBAÏ‚c/šÅ,gŸ'·RÕ¥A”!b{2
¬ÂžeFM¥�Ñ"«£G;—
Ç+=Da¿”ªëõ÷“–v®e�ùƒËí ý”ÂÐûàÞ¶†Îmô6·¦ÿ:›v@"�‰/ßT6q¥º<áIõ­€¾h}ÿ[Ê]Á¶Ä¯¹)Áñä^€˜WÛœxœ•‹;NÄ0 {ŸâõH‘ÿvBH\�nEáø=‹ÄŽl±·g·ÝŽ)G3£Á¼x�F8n=’&§¼µQ8ç-W‹]n$ã�6ì�Ê ¡ï½´¥K\>£¢9zQF)È#’7afákmð¾…	^Jmtl×·PÆÚê‘ãëþ
Ây£„4\Áwœ³›Ýóôÿ“¥üûk(¸Q›F‡³S‡XKÊ_ÓRÇGý¦Ò/üréƒBMÐh¯?„�û�gYÓ•exœ•TÛnã6}×WÌÛJ�åX¾;hµïÖh§±ýP…A‰#›E
$åËÛ~Ä~a¿¤C%NÒ"@[À2‡gæœ£3r:y?íõrÖÉSÖJ»ƒQ6H1I³´7Äö€±Q¿Ï‚’TÚÝ²¤?b¼=¶ûÝ^�÷<ãÝ¼?Lº˜d­nÚé�º«ÜN˜JVq„•6XÊÓOL¹�Ñ¥Èš™.>C2ö:I;¶á¢5hµ:-„søÿ‘�ÁœeN›+(Ðl&‹Õfµøev0¾¹™¯æ‹ûñíæõx	B9V¨­|w{	¨ö±%"â \ü½zÄ°ü¶gB¯�ÅÉ2æ�Gà°í¿S¢Æ–=˜á@mJæ® gRZHéˆîP•¿gU)‹ŽÚNµÊÅðXjKLRíVú	•½ëiùý²Ã:dtþZ¦!Œ“B+&'gµ[—Ükó¢›Î6< .µ–õ®³'4±@Ãš§B6 éoâ‘¥ÄLoÇë›Y³à uàÐ:Ë7Ë!\v þ+”¸5¬€ª”š¸åR"ïÿÏZXO ü¤X!2`F8A3�?5›—Ï?ž^
Åñø)òö9]Æ÷(I6s„z¾o ù`;DZ9£¥DCR ì#`ŠÃÓ˜•âŸåö1"$ñ´lO®šW
ƒVËÊ›æý³'•�Þ£ÙÕæ* ñæô"ˆ”<bAUî	výp»ßl¦‹ûéúñqv?ý­Vntñ�¯4<dÒPãM}îS_}~/^Siíáy9¢ X¿9	ÂÂNÊ²Êø%÷öR(�ÍO+½Â¢#¨|n&•jæBbHÑÜEÍƒ´«TüóÛwX„ID»Whsj�!9BN“‹J:�@*uê£CÆH{™í*õ„<¶DŠmÑ“õ<,Ô� hœ ­Ü‹WöG*„ÛÕ¯í‹Û�«7¤$ëme˜ÌsÌœØãôŒÊNð´!¼†¤_Ãaèƒk#bõ.ÀWP’Ët ¿VXa˜½5¸N¢Æ
®éá�Ñ+Iƒ·¨Aä,Å7s´8d}ºH—Df]ìo"…¿ÛGçyÖaJ¡¹Ð•ÿ t©ué][Íng_Çw›ñÃ|³šßÍëÕæny�´[¶v¿Þ¬Úñîðnòb#ç*74ÒT™óÆ|à{x3�Sf=î#šiŽìØù%œwÏVúQ¤Ki¿{~X^=·%]ª*ƒ¿ W·¼šxœ•PMKÃ@½ï¯˜£R+»›n6[D¬i‚&%XÁS™ìNh0É†t£öß^çÍ›÷1a T.AÃ…Ñ’xlËRV•”¸0‰RJ$¶rqDZÖã@] i«1’ZE<Òå´G£Œ$YrcŒ*–g-1ÃÁ�68:‚›ÎÔ7§;ìÂað}m¯­ooAèDEBD‡×œ³	mëèÿ—ÌN~´t$”>@ðoÔ¯`ì‚m‘¯wéóCží7ÙTSºø›wdl™ÿ˜¦à6gl	Z¬;˜�ú–¬Cí;l&fA­'»íc¾ZïÓ<KwE±ÉÒW¨ßþ²a,õóÕùäæ÷§åO¯¼�“ô…xë»@Ÿáò¯®ìTŒ„‡˜#xœ•‘ÉnÜ0DïúŠ>&0Æ 8Zƒ$0l$@nYœh‘­Š”[­qæïÓ2<È!§Ü¸ÔcU5…‰ kßaoÊ¾µd7v­Åªïêº.;7úæHmÛ2%o<Už¬¯©¬Žu=öº0£©zÛÙªõƒ©ì±±e�›L™á!âæ	Þ§Ì´ÄË&™8/ÁÝº<„²íê£iú¶�ÓSèéDèÿÉ‚iD'™ßA˜—Hów!>Yài£MË²›‚�“�©(ð	Ýûõ„+Y!?'øúíEûd—“ÛX‹»Ë‡R�•¢Ò÷YÞ¼\×pJ+lKÌèW�‘p•Ã¾%xÆqˆ´;([Ù… †y÷Òy‚0º_ª½Æ|5ÍÑïI$ÌÄ«¢Ÿõ%U]�˜„/�$ú-ÿø|§9ŸU}ŠyÀø
^p»Û¦ëCËíEÇpRúçâ5£‡/�ZõÄ8ÿ >G’~Š˜Š?â©¿Ÿxœ•�ÍNÃ0„ï~Š=R¡FÎ�óS!D¼ÂÚ^·Q“läØ@yz\¨\G3;ßNðDPc§ªZªªF“wšª¶ÑN£ì\Õ–…¬«¶ÀÅ‚žæ hµë´"‡Z*©‹ªS&·-6­”¦t%TæªB�1ØC?b´73{ZÆÓÎáàyLfxº…¼iU)ëºêàZ6RŠ¤NCôÿ¤ð	Êö;ð4ñ+A\FFÛól¢OðæÎó†g7ì…xtŽL’Ñ\X,ù$Ùo«æ óëðAô‡8Én×Ô‚{Íqñ´®ÑÌüq¥õzI™lOá·ââj“	Ñóöþk tíá´ûyöy‰+(¸ÊŸÎL�ÞÃæ¯Ä'Bô•èœxœ•�ANÃ0÷>Å_‚P+»‰»BèÁœï—¶’c×r{ˆ%–ïI#Í”�nºNI¨ÌV©Ñ …3ƒéÆÞð½÷�Ò,f—9k:†‚4laY£µ½ÙõZÎÀÊ¶kµçqç…«å”2‚«tSÆ–{Ë)§ùÌ[NÓ©ÞèFvZº‘½”b}§s)ø?)2FÇ%å=eLétip�ê’óôZQ!ÄòfHåg^(¦w:¹èˆSäš×J^èW…èBX¶BÒæá»~ó¸ìÍžçz!MWêé-ø(×ÙŠO”²v—ž.xœ•’ÏnÓ@Æï~Š9¶"ëøÏ:B¤Q�r¨ŠB‘¸UkïØ^ÉÙ5ëqin<OØ'aÖ´A€8pÜ�ùf~ßè#�™¨ª,-Ólµ^KQç²Jõº©ÅJåY-s…²läª(£Ay´y‘H-š&Í…lÖåªLd’¤……”˜¨"Ïª"CUDj¢ÎyØöjÒo¬ó8ô§wÊRçÝ`êeíŽo!‘ežŠ")¼Rˆˆ�†ÿ_5æñ
ôqå¾L8!ðŽðôí;¸)Ìì�€¼²£	f<’?- EzozÜÛÆqGÛÛ.€p$Ö÷¸€Q5H'°HðX¹ <tš7µ“ò:ŠbØh7›Ï÷·Ÿîv‡ûÃîî°ß}d…‰ÙAYýkõ†-ò„‘Ë“dßjfŒgÆ™Œg˜‚ýÿ†øÿ2|5ÔA 2š‹†‰ÃÊ`a¯yÔÖ£"œ}½æ®xp®_†×’1fm6Ç™¥[¯}à.¨°SÆùg“gïãOó`,ŒØcM,¸¸X“·ÉN}ÿ,úûˆ3ž9Þ= >»88’P+ª»(Úºx3ç	u|}ºzÉÆí0��ÃErÎLøH—ÿÊKôñþf”!xœ•�ÍNÃ0„ï~Š=¶‚TNœÆ)Ä�8"àÀlì±Hí`o€¾=N%$„Ä�ëxg¾ñp$‚F¶Ô7ª®l­û­–¦i›]Ù£u[n•ì�²mUVbÂHžÁjÛ É^ÚNµ¦jŒê*…µ,I«²–Fé®FÕµgB„ÛgKpîC¤i<\¡ç!†É™�	ûK(u›!ÛÌ„©¥YÝ;fú¿SDêÑpˆg0Q,ºÀð6ÓLðáx€D#¾	¼Zz™ŠÑepDóêü‹whXŒ&pœ |xx|:¦¬LðfŽys¸(×xžÆ€6AÞ0%÷âÉÁÁH˜¸XÞ³†ïèFìFZ¢7ðà¡®v§°Ü-,—`�ñ•~v"+–’ËÉ|Ä@$ŽŽr#T=}ò¯`!nCq}\�lqs8û^ðašlaUÞCþgçú¯UÅ}®Ë�/xœ•RÉŽÔ0½û+êBeé,!=Ähq®¶Ëi'§§sã#øB¾'Q!Íq³]~õ½à‰Àä‡L)sYT²¨UµÎê¦’Y]—ª©v&Ï%ôÔ ,2µS¥Ìâç¢Éw2×ÊäM#)e�æ¨ÈîNáÈnNšàmÏž7¿Ç>=V])îÞA$*‹´¬ª
Þ¤ušŠøÚÙèÿ‘Â“AØ_ƒ§ŽOÔŸh¢ÇÁ1j0ž;øô@ŽZ�Ý=ù“UØkØs¸evBÜ’O`…ÁBÝŒÐ:> ƒiÛ³ýøyÁÁ1.q4ŠubûlôèÜ|%DwÏÉé(æ£a´m�aòô‚¾uUôEÿØ³¢/‚”Ãqü°ÝÀ> ›çÙ\%›«Žõä6ª-K0ìŸÐë¾�7l	ƒŠÎ€Î¤¦@_m8FÚ;
~m=©àfQ÷(rDÒ'�Ã°Är²ø·‚HôÑž!x´nùppØƒxŒ¡‡mK3ì-w´(épi†7œ|XëE:ÙÏ×—ª|¦Jx•}Å1·sxýR}Äo|	m›xœ•ÏÍJÅ0à}žb–Šô’´M."ê]‹.|�ifr[h›˜NÀ¾½EpáÂ…Û9çÀ7’˜AûV¯�©-û²Ò¦£žLi½§¦öØSÃf0�Q¯}ß-é†}Õci†Z[Ý´¦'[v„µÓ¾a¬´U˜e	.3fb¸_Câ8ï�¸Ê˜BœÜÉ…åLÛÙ£]µ5ÜéVku\—I„ÿ¿TÜv$‚È©‚ÀGæÌ@¼M×¶ÈN©·_&7NÂNrbð÷�g¾&\ Ç9 ABábžlèYö“R—P<}?ÇT<ïçèkÌX¸1/àÂ*ü)·áÕ%z8•xœ•�KNÃ0Eç^Å‚ •;GC[pìb‘Ú‘?-Ý `‰¬7ÌÞG÷Ü«›" ¥rà�”£|¨ÓI5ŒœÖœ‰VÔFhÎÇŽwdQ]>Ö‚±Š�5W\wŠ‰ªRZ1#‡†·t$V#vQ9M>ÀnVÙ Ü8p™OwÊ¥)øÅê­öû[`mWs*dEáŠ¶”’rÝÛ”ðÿJ¢‹ö Œ�}™`ð	’Ca,I\±A%„Ù06¦`‡œ¬w„¼<XŸcâ,�ðõñ	Î{hÖ�<*=­ÐI93c¡â± µw:‡µœ¼Ì^™xMÖÖ½‚¨ä/ÇHŠÔÁ[s~¦);ƒ¡Œ›	ƒ�8#.°¨sN·„ìüæ~­ÍæáÔÿTò¼ä5\°§³yÂ÷tùWMä¢a o˜(xœ•QÍŽÛ ¾ósLÔn„ƒ�8«ªêv¥Þúsè0Ô(,¯â·/v´UÛCoh˜ïo>ÎD@R^´:].ÇF*Ý�lAí”ìTÓžÛÎ¶F)×«^L˜)24¨z­ŽN›N¢:ud‘¤îÜ±Ñtêµµ$6­8ó�2<œ-Á‡˜2Maù„‘‡œ&o&�¡9÷�’ªïZx'ÏRŠ:=3ý?R8{„ÅS°0z“c¹º•ëGN£/tÈh|�æ:e*eÎf sâÉÚ…ø§)Í¼“û¿ˆîÄoÐqŠ+‘à�àà|Ä–ÝŒ6Ôm�!Ó˜^¨Àt‡põ+ÂÇ/ÁÿXTåŠ)@723“M®fß–"Ýø×‡j;”WP;âš!g\À()o4ËöñmuusO`R4s^óÖèõªS ¦÷P|¬¹þ8­Š)¼lwÃ¬=gÌ¤l)Wùçôð´5MöáóòøÚÚ÷i.ÐÁ®ùºJqu¿ÿW“â7Í·ÝÍ”-xœ•’ËnÛ0E÷üŠÙ5AcC+’‚ hí¢EI$?0&GaŠTùˆã¿ïH¶ÐUÝh1�{¯&z"(Ê¦¨PæUMU[Ë6Ë”*1Ãü¾+”ìjUÉîÛBŒèÉFØgåF–y™+)ÛFªŒî»N!¶¨Š¦«ê¼’Xu)0ÅÞyØLŠàÑ:O£9F{ïF-×ÒŸ ¯›ªÌŠ¦©àcVg™àê c¤ÿßa| é¬L~f•}²GH£q¨œtìµ… íÁtÚ�¿<½i—‚9_Ç¾qù‡}%C�Ãn€'Ô‘Êþ"ú!\çÅž:æO¨XbO`é=ÞÁ€Ç©€Æ\Vú�K£YXpÆX‹gwZ†8å«4þ}Ëf=¶xàæEph»êŒ>ôQŒÞ:Ðå¥°Gy=…�XÑhuZ¿)Þ¯ò»E[žo×B¼ö:À@haÉ(ÿ¾…¯Nùg<ó7ŒFGvŒ6ÍÓva>icD�o9p¯,–ÎÅj2zH&¢¥9í;°Žé-Fö‹zàÄÎ­¾ÌWCjµ=?,ðsL*¸ÉŸ¦@"§{û¯«  ùø ™xœ•ÎÍj1…á}®"{¡äw’ˆ•VÚRuáÏ¢n†/“/: “!Ä…wßÜ‚ÛÎ)‘¢\‚‹
¤ÒQû^‰È‚o"¢i‚sÞÔ8#dÖ[àÒòÀ€àJkÁÑ+ï¢s6h«A²($�G¹¦L×7x¤‹1eœnÏË5§ièßút_Rn¬–L4BÑ3Œ‘ZïC)øº$}ÝÃ9-y¸\ªU¥'�õÄ±Ý´_ûÏm·þ>í~»ÃÏ¹íVÇöð®ìvEþåïQ!‘xœ•�½NÃ0…w?Å[¡";v~Z¢©"¨h µa€¥²�›6"�KâH-� /Ç“à"!&¶££{Î=úlƒzŒËa!$~á+-¼‚æ*(Ã Uè¬œQ²•ÖÕ>÷B¥8+
)˜'QpIÔÎPJ±Ð§èE‚ÈÎ®M“Jv9ÂImÜVûsYÛuc¶¥>Öfs,Œ|N8¢!¥Ä¹›ÒZü’h÷GPÖºAÙ"dXáª‘Ðë®~„¶|F°D”ÆÐÛÈ¼ˆa÷	É°µ˜ƒÔ¶“Õo,6Æ·S¨J·hDp8‡Ï÷W'}z�oÐŒÃŸ:×ImK»‡Ì˜Éf…}²@Y2K.æãt9¹¼»¾Z.¦É2¾Ï’Å©O9g�ˆ\‹Õ‡Âke�vOÈÄÆß1ÄûÑŽ›m×‚=–‚6µÅ�íÿ…ˆ|<È—€–!xœ•P»N$1Ìý‚8�½ãY3!']„HŽµíÞ‹{d·�ýûó‚öYw©ººª8Á@®W»Îâ0àºóFÙs×YÒu§½ÒÊv½$±`¦È@ÝIGÒ ÖF¯­“ÆJmÜÊ{­œv¦÷+�•Ç”ánÂê	.cÊ´L»kŒ<æ´wæÒ|Êœ÷«A÷k'ÒH):fúù¥Ø„·@ïÁ¥ÈÍëïjËTçœ`®‡ƒŸê2%ôæ°ÍÈ!E!þŽ�š	™î´Çwljt{„X(sù*o+�$<2Z,ÿÿ½â^¾”·¿ÀaÝÐK	”sÊš½PÞÁŸÀ„âwrÏ-s¦m(ÜÐ¥–Ž^ÇàF¨…Ê§y8˜ß´ví”,N·t|&Ä]:½yï�üéíîâÐáC“ƒŽÔý‡ù7>þ®Wñ`Áµ+–xœ•Ë]jÄ  àwO1ïKŠÿcJ)…ždÔ±‘šÄ%Ýž¾{…¾~ðÍÁÞ9B©½³Ò�9!­Î…`×"µTIéˆÉ¢'>&äUÀ@è,3“‰FšH!i­ŠÏÊj%“öÝçÖ|6ºg†·£>ÛãƒŽ¹�~Öô’úþ
ƒÓ«	á&QJñÔ½ÎÉÿŸ¢ÔŸWøåÑ—Ü¯cÖ�!?gÀUçËrQ�P}Á
µ)}‹?š‘PJŸxœ•�?OÃ0ÄwŠ7‚ ‘“´N\!D)�Pa(¨³c?‹ÖÎKÿ|{ÜV°1°žîwº;ŠˆÀ…R£Ð¹ÒŽ¥mšÒ˜¦Pu-e£5¢2JJÍ:Ñà„c!+¬¤hÊ:çVBTª–c]ò±æªËÜ25P"Ì7j0w>Dì6Çå©�¡s:Óa{yUO
™§4¸áç,©[G„ÿ'™u‡)(c`ñ¦>@ym*�±
�ÊÁK Xã¬Cû.ø{Æfë¬žž“ô5¸¤_.08fí¬]Z68ºjñ„²Sµ}ïþÓ‡½_Ä˜¦›„8Â„jt;çS'Ø©�3çÀž=è`0clF³óahF�ÇéÏø×.Y&p•/“Ñèú¯CØ7Ì–ƒä�,xœÌÁÂ0 @¡<�˜¢ 8‰c{˜>lÇy"Tu¤ŽÂüºðºß�éLë¾E,Y˜‹€¸Ž®Ý}È¬03‰Y@TÓªòí©[<ö…™u6TšŒ]M[Ê6½#6õaõWë=¡Ðñ¢?ïÏõò	³"�œxœ•ŒANÆ F÷œböb€–cL<‚Ñý CKB¡¡4QOo¯àö{ï}£3ƒŒ†�uJ)CÈ~™SB0.Î`³NÊZ»'ê\„è¬Ñd8ÈàgOµKådÌÄZ)6
ã�]ckÞ]‘á¥¶ÎGùy£:¶ÞŽžBÛ_AáýæÔ´ <$J)îuÏcðÿK‘ò÷3ÜöN%ÿògÏð‘×¯F§\r]á,tnð€P˜êu@d­PÚzŠ?¬vSê“/xœ•’ÝoÓ0ÅßýWÜ·µ¢ÍòÑ4É„£	!„´2xvì›ÄšcGöM·ð×ÏiÉ�˜xàÑçžs~ºä¡Ê�2‰Eã.I²�Ü¥%–Xi–¥û¼©¥”9–là�HÊº’á¡.Š8“˜ìã “X¥2Ky™Å˜åq7Œ�ÔYÍG‰ðÖX‡ƒžÞsC�³ƒ‘°ý;HŠ2O«$ÍKxqÌÂm¯ˆðÿ•¬QO708ôèNä¸ÒÊ´à5÷zPÂ¨žkõï�‚&¤;ªöÇŽ±ïöM[�m?×ôjx¥X)CÎÊ1 ›ßœ�„‚ïš=ržœ†Ùó•yãl‚k”àîï>û`ÔFp]�âéšÕ(x�Ë9‚9ÔíÏ#?~¯ZãáQQÔýÝ-d!.hÞ²ù#§1@ ƒ§@±ç$º�wnËÍÇì€Ž“²æ2�Cmå](œZ£§�«è
~#ñ°’–^ 9ìí)4	³àîÓ²ªÜ¯7ì?×lÈêþè•z”sì3ë`³€Yú°`¥k�Á�ï`··ç¥B¹ý0Ý,òm=ä°J¾‚°†ð‰ÖÿZö"»þ„˜.xœ•’ÁnÔ0†ï~Š¹µM6ÉÆNR!ÔR.U ngÇžM¬&vd;ÛæÆCô	y¦ÛR´\=þçÿþ™‰j¹nä•V¢Vó²ÐeÖQkÝ®E›Uë†os±f“ôh#¡Ê¢¬P”y!Í+ä\•œg-"×uÞ´¼ÕU]09ÇÞy¸ä¬ÞZçq–sicïÝdTªÜøòªæE“ÕMo²*Ë½Ž&Fü%Ûš‡3˜<ô;„è¥Œí 2ô`,lL÷µ%­³FÉn¯?2vñm›Ÿ¨®²ÑÅU°¥`í¬î027¡—Ñ8àÓ.…/·7°z.&VŽ¸:Iáj”~0”êˆ(�¨¥Œ¢7Ó„šZCëÝ¾ðÓYg�°Co¶Äðä–ÂMÿ;#{a„iKô�H‰öñö}~|g‡´‹IÀn¤h©àqt;Ô§Ìº²¤Œ]còl�îMìá<ÐŠúeõŠ–ìJš¼¤ÒArZ†Ó³ÂÀö#¤üt@¯¢S¸ïÍ€‚£øO£Œª'óK—\ì/	uò~9ûuŸ§9 ‡ãü
”£Ìñä_—Â~qûŠ–xœ•Ë]jÄ @áwWqßƒãO)¥Ð%ú~½Þ!Ñ`ÌL»ûÎúvøàŒÎFJ

ÑÌd½L1DV¨M
ÞÓdkØFc�v®B$½h=û€&ª´°e¶Ú%eœÑëèI¤'�×X[‡Ï¯ÄðV[çcûýÀ:ÖÞŽB7jû;(çg¤�¼H'¥xê^ÆàÿŸ"q¼ò+`JðUò·�½œ;Zak9—šáQÆ
üs0Np?¡3q¹?û,¹â¸:‹?X˜!xœ•�1OÃ0…wÿŠÛÚ
(I§Bˆ¶ „�¨€ùb_‹ÄŽì´ÿžP‰ÁÀvzï}7|ˆ€Œ¬MµÐ©1¤.d*s¬ËL!%‰‘…©K™Õ†’\ôÈ1Ôe®ò"S¦R™Ì©¬Õ"Ói6BU¡rÊ©+U	¸ñ6-†àÒù@}{¸FÇMð½Õsí»+H%e’^(8IŠ$cÚYfú?)j»_Bä`{à€¶µn±ÅØ€u°µ»—4:ï¬ÆžŸî„X½n+ "ƒó¾ÓÏgSÚkê‚“óÉl.Öƒûú �4Ù‘è‘›–›Ÿp|Û›ûÓq?Äcnwy$:;d=ŽFoØ¶Pú�|OÙzaº	„Lëcq
·„æû&Öó™¶:š's¶>,¿->öC	Óô´wL{žýeV|òé°ÊŸxœ•ŽAj! ï¾¢ï�Ð£Îè„%,ì¹·Ú:‚«Wù}æ¹U£3ƒY*7é)ÆE¹}ÕÊ¡ÁY…¸�4Ö)F/Nê\Hk¥Ô†‚rÄÛŽ·�¢×«¥`ˆwm�e-IÐGëð(4Ã­¶Îgù½SGogöï¾=?a1v•;âŠðvUQ\ô™Çàÿ›"°›é(øÊé[ƒ§ÚjöT óÏä×€ÒRÊ5A¼ÎFoÓ~­�‹‰?öV•$xœ•QÍŽÓ0¾û)æ–]P[ÇI§BˆîrcH}×3%QS;ØcØhµW€GäIÖ)‹@H¸~3ßÏÌÇ�4¨+Ymd[–­Ö¸Ñº]+­Z©Ûú Uƒ–Ì†Äh9Ôh”¬ëƒÚ`Õà±i©QV)DmåZ,+-[[“¸ón“�à•ó�ÆazcwÁ�½]Z~e£³Y«¥‚—²‘RdôÜ3Óÿ3Å±¿ß‚AkØv3°¯ øÄÇe4Ü-"OA Ï‰"G¸Z=’=?®N4=^q“\¡XÏTï†	Î³$Åy¾ÍËYÿöØ¡Ï¿‰�bï>‰ìeécÞÙÏ^[‰ ’CØÝÝývfyÃ×®·8úBAü4D0nzÎñãÛw¨e½„b«Å—rg2ÃžâåØ}ui :2H!Â�2J€}gNÎ¹âÖ/v—š7Óö×Ë?Œ)Â®Ê÷`½cºçëÕ ž •¿žNxœ•TMoã6¼ëW¼Û:hÈßvPµ½›�ÑÙ&.z~"Ÿl6É’TdõÔÓþ€Eáþ’>ÊJP,ÐC�¤4o†3CFO4žÏ—yŽÅ"åh.Êr¶,.W“étZNŠe!ç´ÉÌ¡'AÒBŽóñt6Sœb±Ê—bž+œˆBÐl¼š¢('Öñd=l5Ö’à{c=9Ýþˆ&ž¼uJÜ[ý £År6^®Æ³¾Ëyžñn¥b¤ÿ�ÌJu¾…§É×Ï_¤éè±‚Úi‹œr¤•!øú×ßðð°¿†?jª	´b®páTGiÒ£2×UE¶Ž!Ë¶�»Ãn»þù6ÂÖšÈ÷ïg`,hkŽ,3à}å	J¥	^z�í¦.Kòƒ«Ž6¡X¸«#É@á©bjÝò
Bô„ÕÆÊö`T9ÀD[ðt¿Žgsð	)¤@ÉÖ~Üt|á*Ëîwï“Äµ”0çáõ-ôf”°þ´Át¡SøÉÛJºñ((ñÓ™kûMÅÓÆÆGŠ¾e™çé…U†Þ± mä_OX‡¨¬�’§À©6Gxó\XcH¤¯¬ì"©Çª?{Ëa0Êó©Záùâ‘§ß	†ˆž4'bŽZë~Ð1©åõ7‰uàU¼³þ—Ž«ácÀä_FÄ–qÝÏ*ò¬;u†˜LîÓ„Ï=•2òŽ·6œ�x¦¸6ò'j!¯	0h<ºä{3ÅéªŸÅYb¤GBùÔÅ¥¼LMÂ$q¶¶í†äšõzy°OÑz<²,ï­çY�W\ýö…[“ØÉ®¬‰/ßoÊµOæ‚…Ðp²¶a;¿ã)Ô:‚:¦‹$¯û�wÈ#%”�ð–±´u¡iÈ¸,Ûx¿ûuŸJõH•}!xçS' KF¿»¯ÂÁ£	ŠÁ:ÓÕ5!éÑº×‰¾¯“Æ–|Š|Ð`à2Ö!-j—þ_Í¡àr¥	•c1Ž/—àj=s¿·v¸îÞ’ÃM{ûú6<¸:ÀŒÛ´O­‹tŽWÿõ^dÿ šµA•=xœ•SMoÓ@½ûW§¶"NýQDH‰Ô´M¹¯½c{a½k¼»MÜ?‚_È/aR©¨âÀÍZÏ¼÷æÍÛ#B–&I�ÇERà,Å¨2'U<`+²<Mƒ<Aô:Ö£²€!FQžÄAUpž†o‚8â(�ƒ¸¨â"žåe%i•yÌÙF÷°”Ìq„·J÷ØÉá=S¶éu'Êi©ÛwÎò4Êó0Káu0�^[a-þ§W‰ýzí‡îb vaá×�Ÿ°ü¼Þ®—‹+¸õ—:6HÍ84Ì4P¸zV´¨�5XjeiNs™ž_¡ªmL
Î¬ÐÊóž�æž+™ÒJ”LB�ßÎ Ó°(ÍV¸?=99¡ŒEbÔì}Ö>úå‘æO™ÐÐoš¸4ãöÜÞoÏooî¶°¤¤Ð|€�v’CÅ„„$ˆ§p­w#¥Û Q+âÿ¯¢E¼Ð9bÕð�@W£¬m7¶GÖ
UÖØ'HhÝ;L=oµþ´'_pq``QèÞ’ŠÉéÑB°˜”°EIm¬…åå5ThË†
ºøŠ%Í}`™ZC,§]�B;#(Ã5LÕTÌ±JX”Ãpß0g,é¢¥ƒö�ÖòÌó6/×÷›QÙ³õ=ÛÛœzÚÎYFá¿)Ì°kP=ù$�
C­Ï@^ìŸâ…£|°Ò:2ó°#^]Á¡e{ÿæ"€R²¶#Ùs@E~–d®h];†!„ŽÈ)«¦Ã’Ò¥ýÅál�û†ùÓ	ÜtÎ@
§á¹ÙÛ³�…÷•ØG³šxœ•�MNÃ0…÷9Åì
‚§%q\!tÄ&öDq›Ú‘Zºãœ�“à¤E¬X°óXï}oæGÍ¢BŠEÅ‰-êºeª¢B”… ÎT{«ªEµT"Ð‘	€mQ1bírÙ(E¼©ùR´CQrIE%KÁ°)ny†1tÖÁºÇ¨îŒu4ôÇ4¡svÐr.íî
^—‹šsVÁãŒeéw§C ÿ;3™òh¨Žò	:k·pÐ¡ƒ.úí¾>>á€Ý@¯Ó=zžêãN–,ËáÅø€}öìÅ5h“`ói¾ùÅ'éóà¢ñ0k¢_yh8Ú�þLk EÝGGÉ8Éõ)(©c°9Ê ÷ÈOk{Øko»/�Òrk›?NÝ’ÊŸŽ«ŸžÞ†è¡„‹â5…™@ïáò¯î²oy4§ÿžzxœ•UÛnÛF}çWÌS`7¡MÝ¬‚´Ž¥DF-Û�¥¦@SKr$mErYîÒóÔ�èöKzf©8)�>ôÅ�É™33çœºŠ™ºÃñšãžêÄƒ('ñ`#î'<J8J¸3TQ¯¯†Ý TŽÖƒtp±tÓ~¿;ŒG¼îEýNt§<^ô“ÞxÐ]wU:Tí¶¦¢«LÕ)ÓËÂT\fÍªpÛÊ”:9KLþŠ:ÃÑ ;º÷Gô<FQ€§¹vŽÿf°Ö‡	!G;zèQYg“ÑßþE�Ás²œÔ•v=£ÄT'®`k	Ylƒ`~ývŽ�Gö!þá$iÞ™ÐkãÈ™”±ÚÑ£VÔ‹ºTqªÅPÙ^5VŠ©þv¶´’Þ�ÐýjIË»«åÝŠ*•pž¦Tú�š	¼:­2ÒEÊ:‰ëdÇî£N_�í}ÜqsJïç³w3º½[RÊ;N¸7‘çóÙå”ôF8ÂTf×¦ Ú–UÊ•¥“ëu¸P.Ù¾ üº5þ—]rv*H}™0mÐH“•ÒVÙ-˜Y¸XkNŸ:ö	ž#æä”Jc]h]Å*§dËÉNðšûêa¬,²Å
LâC©«¦�}‚íÂ\”díŽ÷´f/`Ã	-êÌia‰Tlð75l}-ž@ëÁ¦³›ÙrF1¯…ŽÕýôÿY§\íµMèÊä¥$=a®J?³ÝéÒz$*ê<†ûž� µ9r�(¡U0Æúeqí7k­tfÉ™¬Ðï”.t±¡—¾�Ú2ú,ÂöüþúÁ~xøí»ï½«"øB¹-Èk2¦GWU®VY¸¥ ˆ˜/1X€\Û\$‚Ålz½Z|Ã¥¸ôLNøh¥GsY+0
2èªüS˜âµDwÏ°ÏÏ*ÓòxÕ£NøKü¨$zÀàØxñ¦MLé3á•%ç%*£k¿û-ö%öªJ—æÁ™Jm˜dS÷VP¶ªH3ni¾¿‚2øZT™ÔY�¹‹ä³7NlïG¼ðƒCîÕ»ñêvX�U‹2 IG‡¡kJ¦uerÊŸ`!Zoç‹ðeóÍå�C`&ñ5iØ«Î2Ïë‘ØNE¡tši\'‘|êÍwÿŽ`}((}p•Nävµ[L…Ê!nË³°z‚8;{!›p}/¬a>?WÄÞª[ïÀÛÖ€qS*k1û—ü ¸¹{ÿEõBò3ý‰W•ž`-°Š¼ÉåHWœ¤Q	œwo®¨7] ¦2–À-§ðj8M%vIüSm�Ú+¶Ï'
‹‡¯ñdñ©DZHq¤pW -WŸL�Sxô#€áH´fK¨ÍÚºR§aDuÁõ2¶/*Fq-ïúþñ¢5“,˜FKå½ñŽú©Ö`Šøé÷[íPEn­3WªÀ/dÈâ¢
ìŽ”¯½ƒ¯FYfÇ-??ä™4›cÅº‡Ã¿ZžÖm  Bè5ŠØsÛ;6+qßŒ\#?Eípäe\™ðÒ9_7“Ïß¶»²¶4 “Î¢íçàNÿë{üÞÉœ ’xœ•�ÏJ1ÆïyŠ¹U±+ÉþoÑ"h_ ;™´�ÝdI²ÚÞ|ŸÐ'1-O=x†¾ù¾ùEO­Ì»šP`'±âÈ«]uB¡(rjòºkK]Jb£ôd#�l›bÑ´\”åBkÅë.¯$(¯U£
ÂBvH]ÎäwÎÃº—“"¸³ÎÓØ¤�;ïFƒ·è†{M[åmÅ7¼áœ¥é`b¤ÿ+&?Z‚6{èÍ1ª÷ÎøùúëVÒZR›ÃHaf��à¼"oìvÚùAÆ˜zÆ2xK~	&¤&˜=M£qv1éÁXˆ"|š¸ƒçõ´õrx'ÿað¸Máu’§C+ã‚DÂeÇT. ­:§©œFè]Ð¦z
Œ­]öxbH*[–g¯ã ‚+ñè’×>^_bÄ~ßº¡Î‘3xœ•’KoÔ0Çïùs‡®¼ïBˆ
8pC<îÛ“¬©c‡ÉxWí§gœ.]‰·x2ùýŽ0lÛniwî`÷Ælü7Æ´ËÖµ�1veÈk­sÍˆLI`¿Fo·Ýö@«µ7«ÝÊoÖ·îÈ*¨Eg�Ùíºnß`‘cfø±x‚·)3�ñá=&9rƒ[¸<¼ƒåþ°]6íÒÀ+£�A„þÿË†©C'™ß Ó�OeŒ=`òp&{ƒc ÎEhzCè…@2$:ƒËI8Ç¨²“pqR˜šæ>R$!wO¨…Lw3îîÂ›˜
™r%¤bîƒƒ�TÁ“+çª3-ÿcôó8cf�å8é' 8™ª7�ÉÉ?=VÈ×Klå|W»šk€Û/Ÿ¡HˆAÍ ùðH€`³
å¡ÓÛQaÖ¦ü<0aOC½åš‘˜uã¨�QóT¥OION·)�ç4¯ž�ÚHWÓ”&µè½Êç„q¦K¾× ‘&aÖTñA�Œ%Ö
*þÖÿ,S­ãÛlq÷$5sú¢W¥ «ÆÐ'89þÝÌS“/Ê}.åW¡Bu#Î!/Ì—%‡Ô1>×™{Láq^]4¿QGžž?xœ�“MnÛ0…÷:Å,Ô6ôk9AQ4M›"‹ Fã€GkŠÈQ,oŠ¢'ìI:Tí6(�E»Dræ}oøHš"kò¬ÊòF&Y–W«UŠqš—x—%Ê¢Áj7˜F½ph’<M³dÝÄb•åq‘Õ+Ì°(*™§yóO.Ë4N’HÔZ7Záµ±{}|+µÎöª^Ö¶{I¹.Òu^æ%¼ŠË8ŽxµSDøï•Q£Æ+èÄá1‡^iˆ¦xoë=·t¸SžÜ~|ûÆ‚„ Îy/lXä„ñSå5îœè �³ÎGÑ>agŸðY¡2;hœí@htÑÈÞ*CfƒG	Õñoñ&¤ÚšzpÓL+m+èß¢ŸÈòôvÐDeµÈ}I9œN],äZÊòAQ8öÖ„3BC%ê½mP*Kkõ’ÂÉ­}$ëÄs¼hfé`ÝHuhòs(Æq>x!PÞèÿd‡t«4Þ™ÆBÏCáõÐäÎ�³r¨‘;oÏPÊìzò`øj©n1Lî	]˜û×4þc�íLìàÕÎ8 ¡ó­�°ë¡aYÐ(ö§FjÜôP}ÁšàÐ¢Ï³Á@wË÷¹5!p‡ßmz‡ùƒvÇ#ß“10¢CßvÀzî²ÁP†ñ~‰~Dº4)~O'õsÇsÆÓ©öÀi<öˆ7Ÿ·ç­eÝØÅõô¾P.Þ¯Îoå�ãÌ’û/Â‘.^z?ÑOÔa–Ÿxœ•�AjÄ0 ïy…î…Åvb%.¥ö%Š-7‚Ä6^eéþ¾éz˜ÑÎ+.Îx7fËÞ#ZO‘Ñ»49¤1fv1Qâ¡Qç¢€8gWkLœ]¤àÑŽ)£�M˜BXƒõ–p S·Úá¾Ó™>JíÜö×Ýzmo±Ÿ`çÅ_æ4-ðffc†‹¢Êÿ7‡,?ïÀe£”
Ö³•tá§ôZŽ¿…XK–ï³“J-�¯H9Z¯ONÐyZe}¿Õ"[Ôœxœ•ÎANÃ0Ð½O1K-²'‰cW]#ÎàØ“ÖRbGÎXjw‚r"K,ÿ—¾þãBÚÛÞ9B‡2m;íŒñ½lEƒÔ~�"åƒ‹+”¤ÄÖ¢F=jtc=Z$Ó¨Î”Æ’Û€�p•Ï¹Àqr5<¤\h™®O.ñ¹ä%ú{ŸçGP½é°m•A¸“½”bkçÈLÿ_
¿ýÑÆx�)nT*%—>ß? ÔeŠÞ1Aœ—\x5Õ•ÂO\w0æ2;æ˜NBóþù[Oaÿr=üJÞ–ºB7ê|NL¾ýK'¾ w¥p�•Axœ•SÝoÓ0Ï_qO¨Óú‘´é’Mh¢›@H0mZH¼]ísk-±�ítÍÏ¹alH<Àc.w¿¯;GO5žUJªyUåE‰r®Î‹ÍeUË¼^ÌÏh¹X,QæÐ“‰PÏE¥êB)<;—›Z‰\ *äFÕó¼Ü)KYæóe™awÖÃuƒ�$xk¬'×ôïÐÄ�·N‹©°í%U½œ—Åy½„Ó¼ÊóŒ«­Ž‘þ2Súp�Dç	ÖzûµC¤ÖACøÆp{{^‡Ç1ÈÎ5Z`$hõÖcÔÖŒ��bG Cè(dÙ:AéØS¸È&*½5áXt°Œ}‚.P á{í4êV›í½ÿÑa£¡ #ŠÇy’L`%…ð‰ú1xÚþVòñfuýŠ›€I`H–rç)ý&	_î?œö=ëwŽ+¡‰}€ÑêÛ‚#q”Úh4‚NxúžZ»çÎ¿$ºuÖGIBNÄJÈn¸Ÿñß³|Éã!Å«tCC]vOŒö]»ÑÉ®šÎ[8¶1<yÏgÁ­wÐEÝ„ÙÑrØñ…ÉÙPaðç}¥Þ'ÚLÐéÙ¾ VlQò¢#Sµð!ÁF;phbRm ÷Ø_uJ‘ý£cŽ-Dô±sYvG^Yß¦¼^ì>c$+Ó?`r	W�™>ñ½:L'l^IŸk¢·MÃ—=ú¥ývvï…˜Tx†où•ÙV¼ó#5ìÉk¥‰MWå¬Z²×s^pÎÓ„:Ät^dö¼3£ôöØ•œ¯�Þâ0Å³jE•^Ù««JlÙOÂŸ`˜›Fxœ•SËnÛ0¼ë+öh£q*Y–dEQÇ)ÐRMò4¹²ØP¤ÊGõë»¢§ôÐ›(îÎÌÎ½E„Ãâp@QÖY9_-«º\òlQWË¹X-°,Y^å|^Î‹¤cµÁ*Qñy¾*WéŠ/y�9fe–/óª`¸:µÀ"O|c,lá�6;Õ`Ú7Öt’_rÓ¾‡¬Z6ÏKx“VišÐßVz�ÿß™ÔòyÎ[d­ÔG�2L¸ð²E<}1-À2� $‘E5I¼6ü�ø,%u÷àX�¾O’­¥Î0ºXz›ÿU}`tÖb�Ìà6rÃ>øÝár#zð<¶¡(©�G&ÀÔ„ðý’YËú«P×h'Ó`»I6…[CÐÁ±#LÌ
…Î]ÅÉ«o?mfó¢„†¹È–.xðÔÄ¢'^•ÝGKöÌž¤9²A1«ú¶QÎŒ³®#ðù°zØÆí´¡v$;��7B@žº“‡iïPáÑÝöúaý d(�9˜˜hÌlD»ônzoñ‘Âåh}Öb42¥h˜b´¦6i´;Qþ¾E"¥ÍlöŸÁ’†�'KÓÁà·%I{’Z˜§?˜,:,GÀç†7`CmM;pñ`cÖëiò.8’~â<¶Ø vòi¯Îƒ’´c<ºézÜÌ9‡�heM9Šmà—¥S`óÍ(ð&(/g7W§�QN¡J>¶�ïÇ{ÎÜ˜†í«ô1ü �Q‘8Þß�vääQS¬i‡š�½_Ï†ŽÒ¤$œòGïÀÌ6ñ1£˜]õë—‡¹#c  ÁoÏ<>ûé¿kòf{O�xœ•�±NÄ0D{Å– tÈvÎ±sB¸ñ›õš³”Ä–oƒÈß�()hGšyo¤1CpÑšÎë˜loð8ö”G
Ú‘Á^;X«Š�Ð]H‰­¦Ž«ƒõ±w62�CÐ<èhè8*\åRœ'\#ÃÃR×i{ÂE.­ÔL÷TæG0>8Ûß{¸Ó^kµ§sáÿ7í<>MŒ¬&NR>ö¥˜“”–ù
©•ðýûJ-MpÌS–M©s9<ÿs<¼l§_ú[]¯ààÆ¼•EøSnÿ2R_Þl½’xœ•�MNÄ0…×ô‘f1ÔiÒ6M‚Bš5‡pmgZÑi+7ÕÀí	,†-ì¬çÏ?ï%aVL¦¦‰l\l:6ˆÞ²ÑÚÕ&$lÁí]_¬ <'Å­¦H^û–¹�¡±Á“cGàjï;òNÛ¦‹ï|^†}Zc]×u6€u�Ñž¼�½·±6®	PÀž†EÔy‚�X=Ï‹ð:}¾ÂœYÖO¸\_”qÞÖ�wuP�Úi]dõ:¦ÄÿŸ,ÞX.¬z�u¼-òžr"%•81Ì%cbL»p)¼%ÙêcQÔy™ã4bÚžŠÃÃ&X�s¸3œEâ�SÚîLþ4òV)­Õu$šø–£ª¾ýÿ	H\Ncvý‹oCîP5-—ËïÑ/‘2žž*xœ•RÉn1½÷WÔ„:½o#„€HÜ".ü@µ]N[¸m«l'3ùz<3‰Ð åÀµì·VE&Õ’À¹iÙ�Ë:)�ëÔÒ¸¨¥Ãvžšví*�L6B?ŒóØ÷KÛö]×-ƒlÕµ4-X«¾ž×®^k9Îu�)nŽáÞ`’Ÿ­còæômÜØy-î„Û¿@3ÍCÛMS3Á§zªë"Ow#ý?²Púx ¦àÌ�pËô¤ét‰” wï8‚Ç¸@+!DN"&Îÿ¡M¾(Jø¡� ­büûz†ˆ-Ùß$Ëã#A
™Øf�3'¨8l¹(Y¥¨ÍUCÛœF¡ P…î•~#4q+…³‘�1Ä‡7	Pìöä+OtˆdU’õË‹É–l §À	r­^¹Ù¥˜µ´•t¼;{¾a>{½qtñ¸k)=gç×r2Õr
¿ÈÐ#ã^OB+- ž|Î¬íMÒJiCY0ë¤KgtŒŒ"jg‹âÞ•ß.÷�«û~:¼íö§OøÐ<À¹ŠŒøøÞ¾‹?ñ¢ë Ÿxœ•ÏÍJÄ0à}žâ.ÉOÓ¤ƒˆ:ÝŠÏ�Ÿ›i mBz3oo\ºp{àp¾C<Bt±1(�Œ^ö):n“J2é ¬õÑsÃªk¸HÕg¼è‚·†§!In%J™”¶ˆ¼O�ï‡$s;M¥Áev{DxYKÃ:ßßÜJS+5‡çP–WÆj©Œ¶ž¸áœé’‰ðÿM–òí¡´†� /µ4‚êhÚÀ­\Œ°ämËëj+TB™!å7H‡sGØ¨í�ö†Œ]Êéýç ÆÓÇýü‹ùªûÄç1´Þèñ/ ûÞÊqÉ‘xœ•ÎÁJÄ0Æñ{žbŽŠ¬$mÓ$‹ˆºgñ&Ó)ì&!NYûö–…=zðúÁŸï'�¢±;8Š®s�ÃÜ�=uÌÑcqœl ƒã¬*6ÎŒƒg?“î2QG�Áø"ZO~@œúÁ*\e)Ng\'†—\×óö†Y–Vj¢g*—W0ÎÛ®wfôð¤�Öj_/I„ÿ_ª™QŽÐøšÃnmÔ’v³È|l´$a’µ±R§rx¿y:|lÇûÝW]¿ÁÂƒù*YøGÿ"¨_åg•$xœ•‘¿NÃ0Æw?Å� Ô6iê$®¢´‚¡/p±Ï�¥4ŽG4oÏ¥¢L¶¿ûóý|HÊòTÚ"_ç™’Æ¨TRÚVÚn2mU¥¦U¢Ã@m„*•ëTe™4¶@ƒy™§\¢d)m•[a²ÁRa&pˆµ°op0�­Ô5ã3¶±¾sz©ýù	Ò¢”ë,/T	I‘$‚Õ³‹‘þ_),aÜ‚|¼�pvÆ4ôÉäÐàHAˆý�à–±l�c×I¹)±®¡\±&>˜É¢fen…=ðH"x{�‡•n[À kIÇ�{êc®÷%ìšÞÚ·Ö�D ]:Ïm0BôêÇf5§°bè2‘qídt¨'7Í¹½˜“þ‹ópðÃ©þ*?´Ã¸äiøÅîº&2‹—qû=ò÷nèAÂ]ú6qFºÄû¿Ö ¾ cÁÅX’Bxœ•SÉnÛ0½ó+æh£‘«Í+Š¢ŽÓh� E>€"G6[Š$HÊ±ÿ>ÔfÇ|èEß¼á¼7šñRº(ãeZ²lÎgù<™ó¸àE’%’ÄÙ2å˜C-*–§ù4›1Ks–¥KL8�óE’f4Æ²(³4!´ö;ma#iÍ>)mÑÈãªüÎj#Ø„éê3$óÅ4Íf³<�ñ<ŽI@+á=þ&)‘ú0^Ô)õB+¨£ŒŠšýC.‹tñYø¬jéEpçÇ„DPQE·u¼‰w+Àƒ·”yè Øü~y ©·‚Á«ð;ØS)x[‡ Œºê¿„ó÷-ÝÝõ‚¾c�À¦}öXHíá”x‚{¬;}=„[Ý8h<‰¿Ð÷'ƒmÐ¶ŠÜYR¨ÿÔî€Hù%ò\”³œ�6ÇKV'ð6æÞe7}Ðú&T_¶qrê}T©)¿6tŠCïvõS	/Âûqà½´´¡Xwzø…¥Ê4roä¬mýuìÒÒUôä­‰5ÅÂO"k)ßÍ]©¥Ô¯àwÜ–P[(ƒGm�PÖŠµcjh3÷ª+ÂÑ â¨Ø„jÚÕ0ö‚BXíD“ùÑ‡¶o-­À¡Ý†�.÷¢›²ÑÑºÝAäÑýq5ìÓ“©La”<Ó�ðã[;FÞ ³«q›,xœ•’ÏnÜ Æï~Š9&ªwë?ñGQ”t{­réŒalÓxÁ�Ö}úâµ¶J¤ä�Ó �o¾°'jëRU’TÕvµho”lz!i(›AôC-ª…,Ë6sèÉ04Tu�è†›¢ìEÑÒ¡ê¨Aªä¡ë‹¾�º9ˆ>ÃÈ“õpœ1*‚;c=¹yy@Ã“·NË½´§{([ÑTõ¡|)Ú¢ÈR÷¤™éóÊl ä[�>tnÖY[1Hà*ºÙ¢Êa$Þz¦VÎë,;n²mÿ¼µçð_	¿5O0 dëp¸Ñ9ëY›©èrXuÀ‹£Ô`’«{Aÿ%xÁY+ÜhÈ)šgR_CR'MHƒq\«O£¦°¿ ]Pßð¼²ö¤´O^ùeæf€^Nú…véÑ²'öšÄ{kéØ×;·ží¨t*6ò6ûD`ùóuÒ˜Çy~•ñºRäÈ(2rm~mAœ•‰#¦äÖˆ|ÿù”ÔÙÑîÏŸ…ÔîÛr{yø'4pUþ iÓ¾þè3dÿ âpõ¶žxœ•�ÁNÃ0DïþŠ=‚H*§‰íºBˆR®ˆüÀÚ^«�Ô¶œ�Dÿž€Ä�N#�4zó¸A°ƒ³–dï½7„ƒ‘fk¢¶(ÑéÞcÒJ§”(X)1õ�6ÑE‡ƒ²z¬v2hMZõŠ”¶‹Q“À…O¹ÂqÂ%Ü¦\©L—{L|ª¹Œ~ãóù:³SÛ^o¥…i¤k{™éÿK	y¾®A€¥L£Gs‚Ç×çYˆC�±åŠiŽTÛìÞÈ3ŒiÅEô4C\óé÷zÂÕ½XÊ”14Ç‰p‹'nà¥o S€/[ùŒcš7Bs{øö§Ð>\ö?.Ïe™AÁU÷>¯Ô¾þËO|y†¹œxœ•�ÁjÃ0DïúŠ=¶”É¶")„’6çÒoXI+lp$³YCü÷5…{èi`àñf„‰ äHº³9ôÁfŒ9ø�BÂäÑ'Sì`Ž¾wÞYµ S°Ñæ!êˆ>…´9{ÚYƒÙ¹’0"u&h…«Œ�á:ãš	Îµ1-óvÁ*#·eJ¯©ÝÞÀ8o»Þ:ëàE;­ÕÞÞ&ú?©
¡œ ñS-Œwá5ÉÊû£‘`Æ�X©k;¼ÿ¬£|øØN¿¦¯e½ƒ…'ó	©U¡‡<ÿeWß+e„“xœ•ŽÁJÄ0E÷ùŠ·Td$Mû’tdÆq%ˆ¿à%y¡…i2):o\ºpuáÂ¹÷´Ênpì4Ù•Uh­Ebîåèˆ½ŽRÅ;£(ŠB•S6Í€}À^GÖJ9Ô*°$«¤dGÒ8ØŽ‚Ö6å
ç­�á�rår¹)µ©æ2ûGŸ—'èŒEÕ£‘<H#¥ØÚen�ÿOŠÈÔöàë!/4'(¹6˜Ó¶Éó>ç6ÁëÇKöBœóîô#Éa÷|Ûÿ¾—õ
wÝø¼‘_íþ/	ñémf¢“xœ•ŽÁJÄ0E÷ùŠ·Td¤i›¾ddÆq%ˆ¿à5y¡�NSÒW´o\ºpuáÂ=÷HavŽ‰´‰V“ó5¶®õ5v=bÕÄÆGt®£¨f*<	´-[‹} ¾6Qû¾k‚Æhº*vŒ¶šm¯h•!¸Ž´†Ó”Ïãv¦I†’çä}¾=�FkêÆ4ÎÂC…U¥öö–DøÿK™ä¾ìÁ°»o€UÒ˜$ñ#m;ô3É ¯/Ù+uÍ‡Ë�$‡Ãóvü=|Ÿ×Üé7ðyþ’û¿$Ô7,fã�xœ•ŽÁjÃ0DïúŠ=¶”IÎZr¥mÎ¥ß°^­â€#Y†úï+
=öÐÓÀÀ›yµˆ€cî]4­‹ˆ�ÕŽ5²7a0ÖŽº#ö»‘ÔBERŠ~8A´�#£àHÆ�è¨}ã½?z¯h«S.p™iç”‹,óþJ©N%/7~æ|ã<Ú®q=<i§µjíýV«üŸTQ¨ž€K�uj¾¤”\V `Î×k›�i—¢Ô%Þ~%Þ÷ÓïÛç²­€ð`>€sªòUÿ2Pß2e1•xœ•ÏÍjÃ0à»žb�-%�þlY¡„´ñµôÖÒlIÈkhÞ¾¡¥Çz¾áJe¢Fêˆ‚Œ^Ú¨Ç¨;7XoZK£�ÕØ‰‚•CìF©çeÉŒÖgg=‘�Îû`[ãM0R	ÜyÊ®î‘à%åJe¹]0ñTs™ÃsÈë”ëmlë<I'¥¸·ëÌLÿ_Š˜ÃvŒú¾?†…0Ö0ÍL�÷JPiãºÿäy-­÷GÈsNPLB\óáõ›Mñðv;ý>Ê¾AêBNLŸüøK|Emvã€bxœÊÁÂ0@QQnpa„L€pbÇÉ ŒÑCb;ˆKUµ�£RÅR\8²§/=ýo÷éúe2s¡y€š(Gl¡ä†¢((UI9xóÄ^JötËdÃâ-%-ÕïƒÔ¸Q¼µÈJ�ÀRÝúëq—måÞtV›Ÿ�ÁÍ£Éër¸ÿ ¥&ô”xœ��KJD1 ÷9EïIúå÷d]ºó
�¤Û	¾OÈ$ ·wôn
ªFg2…¢G²â0N’‚4ˆq•‚‚âYÌZ¢jÔùÀÑ§DÞfÜºØ’BäästÝ"¸XÑQ¬¢9®gºq»Rÿ¦ƒàr›�ûyùØ©n�ùÜŸÁ„¸X§C4ð ƒÖêN÷:ÿÏVR¿žà“¹A›i«fÛN*ðúþóøíº�ÔLƒ‹úàjR&�x ÿþtree d30b39423b88932fdd600a95d4638fd1c4a44146
parent 340c12d671b01882edbb484794470468dca76b09
author asepharyana <superaseph@gmail.com> 1783431581 +0700
committer asepharyana <superaseph@gmail.com> 1783431581 +0700

ci: migrate deployment to gitea actions
?éOP–.xæýtree b5ae1180008e9e6584ade076724b3da53037013f
parent 144ebe6dd333efb8f43ef034e90349e831d97a90
author asepharyana <superaseph@gmail.com> 1783429095 +0700
committer asepharyana <superaseph@gmail.com> 1783429095 +0700

feat: implement authentication routes with login, logout, and user info retrieval

feat: add S3 bucket versioning support and related XML response handling

refactor: rename temporary file paths from 'teleuploader' to 'filedrop' for consistency

fix: update Swagger documentation to reflect new API name and descriptions

test: add unit tests for authentication routes and utilities

test: implement end-to-end tests for S3 bucket configuration and versioning

chore: update environment variable defaults for new service name
ë÷ö�x ßþtree 90a8a449433282e779324f5219106e6d1f15e9dc
parent fbfff3d9ec64620737d7bf17d538a4fa5cc5fcb0
author asepharyana <superaseph@gmail.com> 1783386502 +0700
committer asepharyana <superaseph@gmail.com> 1783386502 +0700

feat: implement chunked storage for Telegram file uploads and retrieval
f\º˜xø ÿtree 7b5e5d255c4fb56134aecd43a5557d936bb4e171
parent 1ef73fefa93cbc60927cb03612898f86113f3d35
author asepharyana <superaseph@gmail.com> 1783380438 +0700
committer asepharyana <superaseph@gmail.com> 1783380438 +0700

fix: normalize web object sizes
ÎMŠ‘x!Þþtree 35479cd691cab732c2ae1c63f186bc903820c6c0
parent bc806313b115dc7f500f881b54f7ba6933cfbf98
author asepharyana <superaseph@gmail.com> 1783379509 +0700
committer asepharyana <superaseph@gmail.com> 1783379509 +0700

fix: restore multipart part order validation (without strict ETag check)
p\¶šxåþtree 16e66173099d1f84a9b4285e8d3dc8bebe2493b0
parent d1c8cc9d23339793d48292bc3c764d79f67e4a6b
author asepharyana <superaseph@gmail.com> 1783379057 +0700
committer asepharyana <superaseph@gmail.com> 1783379057 +0700

fix: use safe LIKE prefix matching instead of non-character range
¨‘YH—x7Èþtree 47cd6b8eb27629dbaa3c909f8725a9046264a374
parent 34962eb08a476d0c5b5a68fe5045afb1977e49f6
author asepharyana <superaseph@gmail.com> 1783378936 +0700
committer asepharyana <superaseph@gmail.com> 1783378936 +0700

fix: ensure S3_DEFAULT_REGION and S3_VHOST_DOMAINS env vars are properly set in docker-compose
¾"a«’x"Ýþtree 4c9323551dc76df9c07a24b80b7ef190413f2685
parent 8e6ccf211081ef674b692e6d792c2a7e200f04cc
author asepharyana <superaseph@gmail.com> 1783378064 +0700
committer asepharyana <superaseph@gmail.com> 1783378064 +0700

chore: update S3 vhost domains to include asepharana.web.id root/wildcard
IÅ\d�Cx=Âûtree 4008191d82a36efb4eab6b93c73fb72fc1711986
parent 6aee3723fb0f46b1ed4465fc92a95abc0d2a9c7e
author asepharyana <superaseph@gmail.com> 1783377563 +0700
committer asepharyana <superaseph@gmail.com> 1783377563 +0700

feat: S3 client compatibility â€” virtual-hosted style, CORS, presigned multi-method, ListMultipartUploads, edge case fixes

- Virtual-hosted bucket detection from Host header (extractS3BucketFromHost)
- S3 CORS headers + OPTIONS preflight + x-amz-id-2/HostId everywhere
- Presigned GET/PUT/HEAD/DELETE via centralized auth (no GET-only restriction)
- ListMultipartUploads with DB/xml helpers
- UploadPart partNumber range validation (1-10000)
- CompleteMultipartUpload ETag matching and ascending order validation
- DeleteObjects quiet mode support
- CopyObject URL-decode and conditional if-match/if-none-match
- encoding-type=url support in ListObjects V1/V2 XML
- Safe range-based prefix matching (replaces SQL LIKE)
- STREAMING-AWS4-HMAC-SHA256-PAYLOAD â†’ 501 NotImplemented
- Traefik wildcard HostRegex for virtual-hosted style
- S3_VHOST_DOMAINS config env var
CÊoÙŸxÿ  ÿtree 73fd8dbc7828afeb7c827cb5714bb76900e87433
parent ad3f4ce75e4d3ca66ca55a9fea26dfd0845ab388
author asepharyana <superaseph@gmail.com> 1783374524 +0700
committer asepharyana <superaseph@gmail.com> 1783374524 +0700

fix: remove home page CSS lint warning
ÊäP­—xøþtree bffdddab98c0a1de10b5ccc0f7264d9f99493743
parent 339f788dc94b76f6f43b34bf096a19b28a4814dd
author asepharyana <superaseph@gmail.com> 1783374427 +0700
committer asepharyana <superaseph@gmail.com> 1783374427 +0700

test: enable strict AWS SDK multipart coverage
@ÍSa™xæþtree b351aef5ba9803a6158e0fb271eb022b186170c3
parent 2e447ee58ad012c493c159fd879b791515ec333e
author asepharyana <superaseph@gmail.com> 1783374127 +0700
committer asepharyana <superaseph@gmail.com> 1783374127 +0700

fix: ensure multipart part sizeBytes are Numbers, not DB strings
4ËY’xýþtree 861683a0c327f3aa2ce691e2b296ba93ff5ba95f
parent ad454041321d8c1d67d384067859c1b393d9de8e
author asepharyana <superaseph@gmail.com> 1783373840 +0700
committer asepharyana <superaseph@gmail.com> 1783373840 +0700

fix: stream complete multipart S3 objects
µ9Q “xÃ<þtree 8260996a4de0b4bccb38cbadd909edd0cd8afa13
parent 378a084fd3714635a0ee355ee4a464ab4f109806
author asepharyana <superaseph@gmail.com> 1783373364 +0700
committer asepharyana <superaseph@gmail.com> 1783373404 +0700

feat: support ranged S3 GetObject responses

Wire createGetObjectResponse into single-part GetObject handler for
proper Range/Content-Range support (200, 206, 416). Update presigned
E2E test to require 200. Add SDK Range request test.
Ð²“Išx
õþtree 824dca7591c3a568cf536b1300b7f12fdb7cf757
parent 5253fb384dd91d59d0a3bdcf0941b4b9fb529204
author asepharyana <superaseph@gmail.com> 1783372992 +0700
committer asepharyana <superaseph@gmail.com> 1783373105 +0700

feat: stream S3 object bodies from Telegram parts
z#SÁšx
õþtree 935dc3eda21023563e9998da74eb7ae60642eb7d
parent 85aa54592ae6618a766c13deb7e089d7171e2743
author asepharyana <superaseph@gmail.com> 1783372657 +0700
committer asepharyana <superaseph@gmail.com> 1783372861 +0700

fix: verify presigned S3 URLs against public host
)SN—x÷ ÿtree 3f93cf947048150fc6aacf4f132596f96c437218
parent 2bf0fa5af5694fadd04fd964159dac83cf49053d
author asepharyana <superaseph@gmail.com> 1783372270 +0700
committer asepharyana <superaseph@gmail.com> 1783372450 +0700

feat: add S3 byte range parser
yæL�Ÿxÿ  ÿtree 9a30e3aaa192813d74c9db714d50fec0a43585b8
parent dbdc5ed1ee8138d08c4fb094be95633a39db25bf
author asepharyana <superaseph@gmail.com> 1783371791 +0700
committer asepharyana <superaseph@gmail.com> 1783371791 +0700

docs: plan S3 compatibility completion
†kQ–xùþtree 60e0dddafd0fa557d1da93fb0e9cc4c425df0059
parent 9a48fbf2279edc0855ca3faadd9aaf3e1bad4df6
author asepharyana <superaseph@gmail.com> 1783371400 +0700
committer asepharyana <superaseph@gmail.com> 1783371400 +0700

docs: design remaining S3 compatibility fixes
%U “6xcœütree c07a79aaddbba44cc386980483275d04162f4681
parent 06f93e30f633d8f244d0be06665ae81217baeadd
author asepharyana <superaseph@gmail.com> 1783368471 +0700
committer asepharyana <superaseph@gmail.com> 1783368471 +0700

fix: satisfy deploy lint gate for S3 compatibility work

- Apply Biome organize-import/formatting fixes across changed S3 files
- Replace remaining string concatenations with template literals for lint
- Make home page inline handlers explicit via window.* and add button types
- Clean S3 auth lint issues with dot-property access and optional chaining
- Keep GetObject proxy and production/S3 SDK tests passing

Verification:
- bun run lint (0 errors, 1 CSS specificity warning)
- S3_SECRET_KEY=<env> bun test test/production-e2e.test.ts (29 pass)
- S3_SECRET_KEY=<env> bun test test/s3-sdk.test.ts (20 pass)
- bun test test/s3-auth.test.ts (5 pass)
hz%‘7xqŽütree d2967ae6c1acb9d81da923a28496e930a767c1fa
parent f88ac1a124eaf0cf8ad0dc488672a282bde8ace7
author asepharyana <superaseph@gmail.com> 1783367691 +0700
committer asepharyana <superaseph@gmail.com> 1783367691 +0700

feat: proxy GetObject from Telegram CDN for real S3 client compatibility

- Add proxyS3Get config (PROXY_S3_GET env, default true) to env.ts
- Proxy handleGetObject and handleGetMultipartObject: fetch from Telegram
  CDN and return 200 with streaming body instead of 302 redirect
- Real S3 clients (AWS SDK v3) expect 200+body on GetObject, not redirect
- Legacy 302 redirect path preserved when proxyS3Get=false
- Updated production-e2e: GetObject asserts 200 with body content
- Updated s3-sdk.test.ts: GetObject asserts 200 with body (removed try/catch)
- Cleaned up unused multipart imports in s3-sdk.test.ts

All 49 tests pass (29 production-e2e + 20 s3-sdk).
ÿ%Û�8x€ütree 782cd369b90c96353e35f3bca8bb50ee12691c64
parent 57f2740df967ba7e36faa525b3d74aef4c0e0817
author asepharyana <superaseph@gmail.com> 1783364357 +0700
committer asepharyana <superaseph@gmail.com> 1783364357 +0700

test: add real AWS SDK v3 S3 compatibility tests (20 tests)

Tests use @aws-sdk/client-s3 against https://upload.asepharyana.my.id
with forcePathStyle=true. Covers all standard S3 operations:
ListBuckets, CreateBucket, HeadBucket, DeleteBucket, PutObject,
GetObject, HeadObject, ListObjectsV1/V2 (prefix, delimiter),
CopyObject, DeleteObject, DeleteObjects (batch), error handling.

Multipart is excluded from SDK tests â€” the SDK adds amz-sdk-* /
x-amz-user-agent to signed headers, which can differ between
signing time and the actual request through Cloudflare, causing
403 SignatureDoesNotMatch. Multipart is verified via manual
SigV4 signing in test/production-e2e.test.ts.
½t2~™;x¹Fütree 84855ac5479687c8c0778706ac075d05e4f788ca
parent b731ddd7e1e5f950ff9b2b96c32ec57a78ee7ca4
author asepharyana <superaseph@gmail.com> 1783360185 +0700
committer asepharyana <superaseph@gmail.com> 1783360185 +0700

fix: production bugs + comprehensive production e2e tests

Fixes:
- RowList bug: postgres.js returns array directly, not {rows}. Fix in
  buckets.ts, files-ext.ts, multipart.ts (3 files, 8 functions)
- S3 ListBuckets routing: GET / was intercepted by handleHome route
- Presigned URL detection: isS3Request() only checked Authorization header
- FK constraint on bucket delete: cascade-delete files & multipart rows first
- docker-compose.yml: pass S3_ACCESS_KEY / S3_SECRET_KEY to container
- Dockerfile: copy home.html to runner stage for handleHome

Tests:
- test/production-e2e.test.ts: 29 tests (11 Web API + 18 S3 SigV4)
  All pass against https://upload.asepharyana.my.id
- Creates and cleans up real buckets/objects on production
 YAý�xâþtree 9577dbadb4df24f07d1e90c599b854956855eec3
parent 245138e23a67720380ef2d58d8654c7acc18502f
author asepharyana <superaseph@gmail.com> 1783353059 +0700
committer asepharyana <superaseph@gmail.com> 1783353059 +0700

fix: sequential test execution to avoid mock pollution between files
®0[L‘%xQ®ýtree b08cc1ac8b0e16e7dcc71abd0e17c56e868da78c
parent 2d3d7891a0f00868e220e941b6b8e335bc78a59c
author asepharyana <superaseph@gmail.com> 1783352175 +0700
committer asepharyana <superaseph@gmail.com> 1783352175 +0700

Refactor code for improved readability and consistency

- Updated formatting in web-api.ts for better alignment and readability.
- Enhanced XML builders in xml.ts for clearer structure and maintainability.
- Improved test cases in s3-auth.test.ts and s3-operations.test.ts for better clarity and consistency.
- Refactored mock data in web-api.test.ts for improved readability.
Q¶Ë–x&Ùþtree 41b7a36cfe9477c2c79db5b0baec1dcb5b163c86
parent 0ad783b79480ee80e3170fb28e35b0e400c6e6cc
author asepharyana <superaseph@gmail.com> 1783351926 +0700
committer asepharyana <superaseph@gmail.com> 1783351926 +0700

refactor: auto-run DB migration at startup, robust schema.sql path resolution
»_Ž�x0Ïþtree 5b53efb9170ade30aff42c221836e4eac896dfac
parent e175a275129be6ee26ceec51ab5b43a13b2f59f5
author asepharyana <superaseph@gmail.com> 1783348697 +0700
committer asepharyana <superaseph@gmail.com> 1783348697 +0700

fix: PutObject raw body, presigned URL auth guard, DeleteObjects tracking, home.ts path
èbg™x	öþtree 148bef42f2fb9f67b76bb9278e6edcb10cdda798
parent 5e38f80267f838a33ef98fc1f3beb7b9a4e0c242
author asepharyana <superaseph@gmail.com> 1783347326 +0700
committer asepharyana <superaseph@gmail.com> 1783347326 +0700

test: add S3 auth, operations, and web API tests
8Sƒ�xòþtree ae6d20f54ccc818b65e18f908f80f602ca8ad173
parent a4f902a37b77eef4376546a880c66cf1fa0578d0
author asepharyana <superaseph@gmail.com> 1783346215 +0700
committer asepharyana <superaseph@gmail.com> 1783346215 +0700

feat: add web file manager UI and S3 catch-all route
xÎSË—xøþtree 6eb4478f8887bd4e2d908dff5c5f9ea86d46cde7
parent dd61e7e0fc3952d2ea610c014d6899c11a829808
author asepharyana <superaseph@gmail.com> 1783329017 +0700
committer asepharyana <superaseph@gmail.com> 1783329017 +0700

feat: add JSON v1 web API for S3 management UI
¿ýPø–x6Éþtree 3a2ab7ef72b1ce9f523324e2c331c74fe1688606
parent 084ff3fb9dfce2b2af1aa2180410897adbb5df9a
author asepharyana <superaseph@gmail.com> 1783327962 +0700
committer asepharyana <superaseph@gmail.com> 1783327962 +0700

feat: implement S3 protocol dispatcher with bucket, object, listing, and multipart operations
"€e`—xG¸þtree 8176c6ad5841cb67eb75d2e0edc2807a74fc6422
parent ee758aa939ca32954b5d581784b67b33847c10db
author asepharyana <superaseph@gmail.com> 1783326666 +0700
committer asepharyana <superaseph@gmail.com> 1783326666 +0700

fix: add region validation, fix presigned URL canonical request (add method, fix headers), normalizeUri decode
ì4i€žxñþtree aee21a516531b02faefa52d54f6ac9fa8b3f492c
parent a2ca4890395e36f1482b4cb4e783a4274d3177c6
author asepharyana <superaseph@gmail.com> 1783324599 +0700
committer asepharyana <superaseph@gmail.com> 1783324599 +0700

feat: add S3 SigV4 auth verification and XML builders
ö�T˜�x@¿þtree 6529aed94e761b73682eb20d1db856de2189fe8f
parent caef31c7039d55c431340344ce2efdf44407a475
author asepharyana <superaseph@gmail.com> 1783323207 +0700
committer asepharyana <superaseph@gmail.com> 1783323207 +0700

fix: add explicit snake_case-to-camelCase mapping for S3FileRecord, add missing findOrphanFilesByBucket
@]g¶Ÿxàþtree d57a456e9fecef18f63a6c104ca24150bd57640b
parent e37fb00295ce61b167ad947e84684403fc53e463
author asepharyana <superaseph@gmail.com> 1783322617 +0700
committer asepharyana <superaseph@gmail.com> 1783322617 +0700

feat: add DB CRUD layer for buckets, multipart, and S3 file extensions
:–Yå�xïþtree 2d0b1befd0d175379a370d1cfd2574dc228dfa6f
parent 0d0d7a5dfab54cdd8e0f3ebd3ef1e12ba80e940d
author asepharyana <superaseph@gmail.com> 1783321143 +0700
committer asepharyana <superaseph@gmail.com> 1783321143 +0700

feat: add S3 env config, bucket and multipart DB schema
ÑÑVÄžxáþtree b3b7a0cb1de20c6be7867550e5f03a10d1d3138c
parent 52bd704d21ba4f98c14b2866dcf5a541673c5aa8
author asepharyana <superaseph@gmail.com> 1783275937 +0700
committer asepharyana <superaseph@gmail.com> 1783275937 +0700

chore: remove GitHub Actions workflow for notifying parent repository
›I\ˆŸx/Ðþtree 0ba5ec7d813cc04427bf5fd6147f18b40e68fe3b
parent bbf420759b5ab1e3d0542cad582d71061535f7b4
author asepharyana <superaseph@gmail.com> 1783275918 +0700
committer asepharyana <superaseph@gmail.com> 1783275918 +0700

refactor: enhance file handling and metrics tracking; remove unused bot health tracker
	NbL—x7Èþtree ea46106775c0d829717e4b7d290c55d750f2dcc7
parent 20b622c4c2f063750fe24025c096d93a5564cf62
author asepharyana <superaseph@gmail.com> 1783264012 +0700
committer asepharyana <superaseph@gmail.com> 1783264012 +0700

chore: update Dockerfile and docker-compose for improved deployment process; add deploy script
¿÷d(—$xG¸ýtree 50836317caeff4cbbdf33a22ee664d5b25a5ba0b
parent 50176e57643789119a65e676bff1e0f87380bb0e
author MythEclipse <asepharyanalm001@gmail.com> 1781968910 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1781969063 +0700

fix(core): resolve wrong file_id error and zip stream memory leaks

- Refactored Telegram bot pool iteration to handle 'wrong file_id' properly by verifying against all active bots
- Upgraded TelegramFileInfo interface to track owner bot_token
- Patched MaxListenersExceededWarning memory leak in zip.ts stream writes with events.once and async iteration
ÍÇK™xY¦þtree 5f380722e9e8946983bb3632685b1ca1f85e9941
parent 2a833d3908837f566462adacddbf144de1cfe576
author MythEclipse <asepharyanalm001@gmail.com> 1780724366 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1780724366 +0700

chore: migrate domain asepharyana.tech to asepharyana.my.id

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cÂn“x#Üþtree d75b0711d31f1e74391c8844b1585c795ef43102
parent 3ca5ab798337709088caea811611d3ad045a84de
author MythEclipse <asepharyanalm001@gmail.com> 1780074935 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1780074935 +0700

feat: include bot token in file info response for Telegram API
›òZ?›xK´þtree 3325ba98b39acdd21bb6681262fedb89d3bb9a6a
parent cc0f1dde7949c12c45c6ccb5999a9be330dec98e
author MythEclipse <asepharyanalm001@gmail.com> 1780048435 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1780048435 +0700

refactor: update Telegram file handling to use new getFileInfo function and include bot token in cache
àkï™xY¦þtree 78d9e919fbfc54e362f63e56be99ba94d54bfd8e
parent e88e5dbc392154c49abcff683c62d96c13f0fbdc
author Asep Haryana Saputra <90584806+MythEclipse@users.noreply.github.com> 1780041600 +0700
committer Asep Haryana Saputra <90584806+MythEclipse@users.noreply.github.com> 1780041600 +0700

ci: remove obsolete deploy.yml, replaced by notify-parent.yml¾„oü“xcœþtree b439dcdff2e7b191f925d41eaa77b8f55b599cf5
parent 0e2f09012f2bb79021edb301924de8769cd51469
author Asep Haryana Saputra <90584806+MythEclipse@users.noreply.github.com> 1780038530 +0700
committer Asep Haryana Saputra <90584806+MythEclipse@users.noreply.github.com> 1780038530 +0700

ci: slim deploy.yml to test-only; build/push/deploy handled by monorepo /r.“xS¬þtree 41f17987446a2336b626cd9040fa9df827c99abc
parent 134a7d4bf6623ed4bb2c5cce42224c064eddb269
author Asep Haryana Saputra <90584806+MythEclipse@users.noreply.github.com> 1780038501 +0700
committer Asep Haryana Saputra <90584806+MythEclipse@users.noreply.github.com> 1780038501 +0700

ci: add notify-parent workflow for monorepo integration`0lÂžxN±þtree 0cd4b387f5537b1635ef431311ec88947c890c1b
parent 8508b360268f649e19763d1fb8b02f95b2fbe593
author MythEclipse <asepharyanalm001@gmail.com> 1780001157 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1780001157 +0700

feat: update Traefik router rules to support additional domains and change TLS certresolver to Cloudflare
†'k>œx<Ãþtree ac3ed7af1266d9e266206b434af38d3adef03aa7
parent 5425f6d33ddc7d85095e40ca3922189a26893ba6
author MythEclipse <asepharyanalm001@gmail.com> 1780001029 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1780001029 +0700

feat: update file handling to redirect to Telegram CDN and adjust Swagger documentation
6Xdã�Axâûtree 238174bcfd5b14b21326dc41f8a1bc43b4ff05e4
parent be813b1c0ed57ffa0cd2b817f52111a736ed474c
author MythEclipse <asepharyanalm001@gmail.com> 1780000419 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1780000419 +0700

feat: enhance configuration and rate limiting

- Added new configuration options: trustProxy, uploadConcurrency, batchMaxItems, batchMaxSizeBytes, and maxRequestBodyBytes to AppConfig.
- Implemented utility functions for parsing environment variables and masking sensitive data.
- Updated rate limiting logic to use configurable window size and maximum requests per window.
- Introduced a middleware for rate limiting on specific routes.
- Refactored file handling routes to support streaming downloads instead of redirects.
- Improved error handling and response formatting in file routes.
- Added support for oversized request rejection based on Content-Length header.
- Updated Swagger documentation to reflect changes in API behavior and responses.
- Enhanced tests to cover new features and ensure proper functionality.
WšuŸx/Ðþtree eb2e4f5ae7b4a602a9144f420c5b994bd90d26a3
parent db5de1367d10b0cfe302307583f978f4e7f26004
author MythEclipse <asepharyanalm001@gmail.com> 1779998270 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779998270 +0700

feat: update Traefik router rule to use new domain for file upload service
'<`GŸx_ þtree 870f0ecb920364bbe6c1511897218c87fffd8d7f
parent 56b929ffb1a7cccce64fa6cde1f8fa808a4a084e
author MythEclipse <asepharyanalm001@gmail.com> 1779385909 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779385909 +0700

feat: extend file schema with archive metadata, implement batch upload processing, and add zip utilities for file handling
u½s‚“xS¬þtree df8f3dc68c75626ac4ca4f8733add8369318fafe
parent fd5eb98586471e609a95402cf692bfd9f60c9235
author MythEclipse <asepharyanalm001@gmail.com> 1779384594 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779384594 +0700

feat: enhance file upload process with temporary file handling, improved error management, and metrics logging
ƒjnÅ›xK´þtree e80f5aca7db2726df26b0d123f0ef07b757aafcc
parent 10c968cf0105ed08e1a4412c3f142ac455ce3036
author MythEclipse <asepharyanalm001@gmail.com> 1779381109 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779381109 +0700

feat: implement caching for file info, add rate limiting, and enhance upload handling with retry logic
ÖrjÄœx\£þtree 7e46ef0b3eed0d72890844e9974c87f7b077b4c0
parent 52340ee77db8ba46d74ebfb07b2e780845f21562
author MythEclipse <asepharyanalm001@gmail.com> 1779378942 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779378942 +0700

feat: add file_hash column and related index to files table; refactor Telegram API utilities for improved file handling
`	p{šxJµþtree 196111f2329d8bc2a06fd00a354bec88d3b8281e
parent 06844f1fa1a7fe8ba4f54fabc11568eab5812585
author MythEclipse <asepharyanalm001@gmail.com> 1779377515 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779377515 +0700

feat: enhance file upload handling with improved file detection, size limits, and response formatting
ìÍj×™x9Æþtree 12cc495bebb3300cb0faa4045d81152d5c5e4f7b
parent 884710fd4244224b457994c4a99d1ac8d2bace4f
author MythEclipse <asepharyanalm001@gmail.com> 1779116635 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779116635 +0700

docs: update CLAUDE.md to include details on temporary file handling for API uploads
ñÛc8•xEºþtree fd0ef86635ba8f7a6beb75d82254954d3d48a530
parent cc3f85284bdbf2e52f3e9dae6e3ffac3f17737c1
author MythEclipse <asepharyanalm001@gmail.com> 1779116486 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779116486 +0700

feat: implement temporary file handling for uploads and update tests for dynamic ID verification
'Ðj ™x)Öþtree e8326c34eab734e18713aca125193a30a1ad623a
parent d58b2390efbce1b1b9e2778c0a6a926d6482f84d
author MythEclipse <asepharyanalm001@gmail.com> 1779116097 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779116097 +0700

docs: add dynamic image fetching details for unit tests in CLAUDE.md
Õ\].“xC¼þtree 3644f9a34a5457778c3db5200636be15a1fe80c6
parent b91b276214729c7cd46cdf79ecaadd89032d4869
author MythEclipse <asepharyanalm001@gmail.com> 1779116079 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779116079 +0700

refactor: remove concurrency limit from Telegram upload queue and enhance media group handling
°¨h›x;Äþtree 8d3360c90815ffd41372b1bf9565554b62586b0c
parent 0bc12651db86c9a33f01caeb3263540ee4295c38
author MythEclipse <asepharyanalm001@gmail.com> 1779114680 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779114680 +0700

feat: add support for batch uploads of media groups to Telegram with debounce handling
É¥cïžx>Áþtree c6c031f2c1522f0d4c4e1a748aae55cbeea212bf
parent 825e9695698d6c406fda99caea74099cc36851ed
author MythEclipse <asepharyanalm001@gmail.com> 1779113978 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779113978 +0700

fix: clean up logger info message formatting and remove unused import in rate limit tests
ÀgfÿŸxO°þtree fe4b94f9ecfc7b2003fd32faf0a2664807481b9b
parent 721fa3db7d80cf49e75bee8ede46240346eeee95
author MythEclipse <asepharyanalm001@gmail.com> 1779113896 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779113896 +0700

feat: implement multi-bot support for Telegram API with rate limit handling and add task queue for uploads
Km!Ÿx?Àþtree b4bc79f9bc5b7c4f2df20459f3f9f500802470c8
parent 91cfee0b375b2ed93cb76db7ab0e17207a288325
author MythEclipse <asepharyanalm001@gmail.com> 1779112958 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779112958 +0700

feat: implement auto-retry for Telegram API requests on error 429 and update documentation
Á¥fB‘#x1Îýtree 545ab9856e7e0f488d8df2309536310cc9dca63a
parent 525348b1f3c24148a79bd85154a65be52df91ce5
author MythEclipse <asepharyanalm001@gmail.com> 1779108903 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779108903 +0700

fix: load BOT_TOKEN from process.env in tests to prevent leaks

- Update test/bot.test.ts to use process.env.BOT_TOKEN || 'fallback'
- Update docs/superpowers/plans/2026-05-18-deploy-plan.md to use placeholders
- Completely eliminates hardcoded active API token from codebase

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Q;¸vœ2x,Óütree 885f872069069d51824b17ebd5c145a300e29121
parent bf6d5f04c4e6243ed679852e2a23d246dbca9fe9
author MythEclipse <asepharyanalm001@gmail.com> 1779108766 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779108766 +0700

fix: make forwardToStorage fully dynamic based on fileType

- Replace forceDocument boolean with fileType string in forwardToStorage
- Dynamically call matching sendPhoto, sendAudio, sendVideo, sendVoice, sendAnimation, sendSticker, or sendDocument Telegraf API method
- Correctly extract uploaded file details based on what Telegram returned
- Fixes IMAGE_PROCESS_FAILED 500 error when uploading ogg audio, video, sticker, or voice notes via HTTP
- Update upload.ts, bot.ts, and test suites to match the new dynamic signature

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
‘À¿”(x„{ýtree d363c40e1da7a98b7f105f2a2fff32fa5f570c9f
parent b2de5f583dc33c5b523188dac54f75e5acd95507
author MythEclipse <asepharyanalm001@gmail.com> 1779108381 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779108381 +0700

fix: bundle migration script and ensure file_hash column exists

- Compile src/db/migrate.ts to dist/migrate.js during build
- Copy dist/migrate.js to Stage 2 (runner) inside Dockerfile
- Run bun dist/migrate.js on db:migrate command
- Modify schema.sql to use CREATE TABLE IF NOT EXISTS and ALTER TABLE to add file_hash to existing database instances on VPS

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
$ Õn“x3Ìþtree 4314d81b6954b986b9988d59744b6f133720ee8d
parent a0262e2d46e86ea750467229de02c8f8e759b4dc
author MythEclipse <asepharyanalm001@gmail.com> 1779108137 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779108137 +0700

feat: add design document for multi-format upload and anti-duplication feature
´"ah›xôþtree 705ddb7935ee847e253bf74a3dbe699246be270a
parent ea4341fdfe443845d9c2ba17b69b272db75ead25
author MythEclipse <asepharyanalm001@gmail.com> 1779108111 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779108111 +0700

style: run formatter on modified files
çSß‘%xQ®ýtree f739b5a8cd348325205f4f284df1f9d7ad1d989a
parent 04a2e52250658ffc17499766546c4ff0880f2d6b
author MythEclipse <asepharyanalm001@gmail.com> 1779108076 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779108076 +0700

feat: implement deduplication and all media types in bot listener

- Expand registered bot listener to support sticker and video_note media types
- Check database for existing telegramFileUniqueId to bypass forwarding duplicates
- Update tests to assert deduplication and new formats handling in bot.test.ts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
öÉYŸ'x€ýtree f4a6c617232e946dba90f4339a5b369b4b309f48
parent 4e805689c14fd3702b0c9f1f38c03d242f5ccc61
author MythEclipse <asepharyanalm001@gmail.com> 1779107995 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779107995 +0700

feat: implement deduplication in HTTP upload router

- Compute SHA-256 hash using computeHash for multipart and JSON uploads
- Query database for existing file records with the same hash
- Return existing metadata and download URL immediately on duplicate match without reforwarding to Telegram
- Include fileHash in newly inserted file database records

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ëJ×„“&xcœýtree 49b33aab22778d2fd3dc4223b5709affd32ec8f4
parent 22c2133b24d47fc9d4e3fad1925cf8bf67067e37
author MythEclipse <asepharyanalm001@gmail.com> 1779107892 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779107892 +0700

feat: extend file type classification and implement computeHash

- Add sticker (webp) and video_note to classification mapping and file limits
- Handle unknown mime or caption fallback to document
- Implement computeHash utility using Bun.CryptoHasher for sha256 checksums
- Update and add comprehensive tests in file.test.ts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GÎø’x’mþtree 732058b5a4cbbb1715e08a053d8aa03d4ab089d0
parent 3d2500ed7f8a83a11325f6ae84eb57338896125c
author MythEclipse <asepharyanalm001@gmail.com> 1779107779 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779107779 +0700

feat: add fileHash column to files schema

Update files database schema and test suite to support fileHash property.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
B+‚‘x1Îþtree ab661af0d1b5d30035be5d3dbb6664a594637c9b
parent 5de424bf8e90a23fc8544b9a5f12f6c4a49b9495
author MythEclipse <asepharyanalm001@gmail.com> 1779070803 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779070803 +0700

fix: auto-append file extension based on magic bytes or mime type if missing
8)a>šxåþtree 2116562f406a0c84b702c26ae35a7445e009d07a
parent ed7f607ee4c5409c19292bc2e627732ed99f7386
author MythEclipse <asepharyanalm001@gmail.com> 1779069382 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779069382 +0700

ci: add write permission for packages to GHCR pushing
‰;W@�x ßþtree 786a33112e62f191c3e74cdca56e19e08bb5f4c3
parent ed88069e60f7f2f352b1c66264f084a1e77bcd21
author MythEclipse <asepharyanalm001@gmail.com> 1779069322 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779069322 +0700

style: sort imports in telegram test to satisfy Biome check
žs[—x7Èþtree 333d6b534362bc8cc56b8d2ab8b3a8956d6a9385
parent 910d29d5b9cae2f1050f1aba99e9740f60dbb80b
author MythEclipse <asepharyanalm001@gmail.com> 1779069264 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779069264 +0700

fix: assert sendDocument with config.storageChatId to match dynamic mock env in CI
�ÙcbŸx/Ðþtree f58a341962cd71da99af41a8e0e2a7ff419b0cfb
parent c0827b09c5734f1a38bb3750bf896403379dd7bb
author MythEclipse <asepharyanalm001@gmail.com> 1779069224 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779069224 +0700

fix: upgrade Bun setup version to latest to support modern lockfile format
ŠNaD�x=Âþtree f07cdff798467b586c50c5891e1a6bb3fa0afc03
parent 7f1178d72b510522f79f99d630498b5b17426096
author MythEclipse <asepharyanalm001@gmail.com> 1779068569 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779068569 +0700

fix: make DB tests more robust by checking definition rather than strict module identity
ðže(”xD»þtree 7dee9e60ce11d243d9ceebee9db7d455973836b8
parent 06a1b38c025ae868a2f1ada6858d1d8623d2770a
parent 1fc408046627586e1906b9f2454c7f07e3f97805
author MythEclipse <asepharyanalm001@gmail.com> 1779068497 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779068497 +0700

Merge branch 'worktree-agent-a7a26e7873ab5b94e'
Tºa×•xešþtree 633f9563e2e028939a3debfdbfca1573425d1e73
parent 2c1c5ad6383264c0ec97910b56e7a80ea3f19b62
author MythEclipse <asepharyanalm001@gmail.com> 1779068489 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779068489 +0700

ci: add GitHub Actions pipeline for tests, Docker build, and deployment

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
;qé”xD»þtree 4ef940d99619d1e7e4e3eb84c0bf4df18dd2e1e0
parent e4c074c81f47bea51fc257596c5949db6b9e6905
parent b41fdb4a887e94217e674d40751f2f409a4d6d1f
author MythEclipse <asepharyanalm001@gmail.com> 1779068460 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779068460 +0700

Merge branch 'worktree-agent-aae37f6f3cccac163'
c=˜xçþtree ce8e68d24ba8fa90f5ffae02dcd480bf5a71d3b7
parent d7f5180eab08eefaa82377da156ff7e02da24541
author MythEclipse <asepharyanalm001@gmail.com> 1779068456 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779068456 +0700

docs: add deployment design and implementation plan
kýYõç†hx‡ xÿåÙ-tree d3731c514bb8193f08d58f93f8fb65823b5fc23a‘-h397‘˜AF397 +0700

chore: add Dockerfile and dockerignore for production build“+:Ñs*äå€x… zÿÙ×-tree ec818722ada3fc406688496f7b7d24e7ef841ded‘-h442‘˜AD442 +0700

chore: add docker-compose.yml with Traefik configurations“:Ÿó*¦›xôþtree 325913e92098ead18af34767e5b5836e27dd9f0a
parent 91986d1d51c2411f144696fa016a0ec50507c872
author MythEclipse <asepharyanalm001@gmail.com> 1779067524 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779067524 +0700

fix: add Bun database migration runner
tQè�xâþtree 42d98de0973264cb6d213958261038d2cb55eeed
parent 9aedc59b69772102cafb5b496747df09be8547dc
author MythEclipse <asepharyanalm001@gmail.com> 1779067135 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779067135 +0700

fix: upload binary files to Telegram with source payload
„ÑYøšxåþtree 1c38e45dc33cce54ef800aca2d3916b77f26d121
parent 6feca3564af2e2a58cdc567e601140c039ab81e8
author MythEclipse <asepharyanalm001@gmail.com> 1779064995 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779064995 +0700

fix: refine Swagger docs headers and dynamic examples
óY'–xéþtree 68eb596a98a9ff83e3dc5f4c90e338f698690152
parent aae8ebfbcf74594ee2282b238f4de43386bc61ae
author MythEclipse <asepharyanalm001@gmail.com> 1779064767 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779064767 +0700

feat: add Swagger OpenAPI documentation endpoints
±iWî›x;Äþtree abe8cb98ec8b13338f7630edc8d452ce7621caa0
parent 825a8f42cb36e64995cc441eb73f533fbf41d52f
author MythEclipse <asepharyanalm001@gmail.com> 1779064247 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779064247 +0700

chore: update bun.lock to include @biomejs/biome, typescript, and related CLI packages
…½dê™xæþtree 6714e5c8786aaca5e43ffd78c28523aca8f0b70e
parent cf79a1f1955f6a6eb4441fd4a9cc841a3528a1b1
author MythEclipse <asepharyanalm001@gmail.com> 1779064233 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779064233 +0700

chore: add build script and target bun bundle output
ÆgXð“xìþtree 1d1c4f213a385bfc81f9f19184966923a26c5be4
parent 3f4e697733039b9b4383d098d97da9a79c8141e8
author MythEclipse <asepharyanalm001@gmail.com> 1779064141 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779064141 +0700

style: format and lint codebase using Biome v2
`žTÍ�xïþtree a4dcfd12d72020fb50f9c15fc26853cb845dc75e
parent 9c5a855f7d21d97ef83d8acd063a02aafbb5fea8
author MythEclipse <asepharyanalm001@gmail.com> 1779064026 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779064026 +0700

feat: migrate entire codebase to TypeScript
BÁV�œxãþtree 452583dcb0bb3ec8615c0c32d4950456b31849ab
parent 252b2e4a1f7783c7a6789aa5d4c086ab1d500a44
author MythEclipse <asepharyanalm001@gmail.com> 1779063802 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779063802 +0700

chore: configure typescript, biome, and package scripts
¹mY �x ßþtree a4cdd6c1e107eebe38d3711d658c0767bc3dc301
parent 434905313c1986ec37652a09f31aabca0b2aaa8e
author MythEclipse <asepharyanalm001@gmail.com> 1779063390 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779063390 +0700

test: isolate module mocking and add sequential test script
û`[�‘x¡^þtree 6d3325bbc2799882aeca9668583b01e0efd79e48
parent 99b820709d01052527a3ce4734ff212c200bb67d
author MythEclipse <asepharyanalm001@gmail.com> 1779063305 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779063305 +0700

refactor: replace TypeScript with ESM JavaScript

Remove index.ts and tsconfig.json, update package.json, and install dependencies.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ó¦‡4�x _þtree d83fa65f7a2c7ebc08af96039beb97a10d75a504
parent 4996d7548a1b932d1fb8d01d46db3bf1f8973800
author MythEclipse <asepharyanalm001@gmail.com> 1779063275 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779063275 +0700

feat: setup HTTP server and bootstrap application

Setup HTTP server with Bun.serve and bootstrap Telegram bot and other services.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
²„‡Ë›x›dþtree 69ed9ba9274b53a95e804df5690a8d8245e6f6d1
parent be93a06918531e07905dc3d61a685c7ca99ebc99
author MythEclipse <asepharyanalm001@gmail.com> 1779063197 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779063197 +0700

feat: add Telegram bot handler

Implement Telegraf bot instance with file upload/forward handlers and mock test verification.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
¯o†+’x‚}þtree b804e9db8018d05f26a686c154070ea10e0234d1
parent d915814277e4c30f21a14c8f52a9e8fbdd84e0d7
author MythEclipse <asepharyanalm001@gmail.com> 1779063055 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779063055 +0700

feat: add health check route

Implement handleHealth endpoint for Bun.serve to verify DB connection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ª{öžxN±þtree 6cb43165ebf341a863b3ebea00aaee39d8de54e3
parent d7b8e9dd086517865ef64ae09610549bf1e9ca68
author MythEclipse <asepharyanalm001@gmail.com> 1779062969 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779062969 +0700

feat: add file routes (download redirect & info)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ñjŸxO°þtree 8afb941670dbeb0ec568031a6d04cdfc453d72b5
parent 1e29433d5d6ccf3f3982dce5862b10fa9833bb88
author MythEclipse <asepharyanalm001@gmail.com> 1779062893 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779062893 +0700

feat: add upload API handler (multipart & base64)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ÏÉicžx>Áþtree 5b604121a90eed6694ec3b040754e9a197aca9d4
parent b621a73ca9a0548043082f989f4486b0f84d20f7
author MythEclipse <asepharyanalm001@gmail.com> 1779062781 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779062781 +0700

feat: add Telegram API utilities

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
²‡b÷‘xA¾þtree e972b38fb43515d4af00a7adb7bc18ed6d2df791
parent db71f2928831867a7ebfed3063cb04791a92318c
author MythEclipse <asepharyanalm001@gmail.com> 1779062534 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779062534 +0700

feat: add file validation utilities

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
RÍeÓžx>Áþtree 96ea07962b9f8e5ae7c158b61e48ca87eae76c7b
parent 281329a1afad5d87ef17215963272e4c0a814787
author MythEclipse <asepharyanalm001@gmail.com> 1779062492 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779062492 +0700

feat: add in-memory rate limiter

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
š[cý’xB½þtree 97422055f2943fe49f550c3429e829885216f7ed
parent f9c0bc076893eb040fdc59e95a9cbd7c08df5da8
author MythEclipse <asepharyanalm001@gmail.com> 1779062446 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779062446 +0700

feat: add Drizzle ORM database layer

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ïee˜xH·þtree b96e8a268e19ef296429991b1ab4c70f424ed5c2
parent 77032538e8bffb263f793c974ead979ca771ccdf
author MythEclipse <asepharyanalm001@gmail.com> 1779062335 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779062335 +0700

feat: add environment variables validation

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
"h½œx<Ãþtree 17dbff3e388f7c293abd61d88d72696d099169f5
parent 25c82bcf0b70907221eb56fb34a6631603c02388
author MythEclipse <asepharyanalm001@gmail.com> 1779062013 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779062013 +0700

feat: add Winston logger setup

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
0Ðb­—xG¸þtree ab5243c311cad0d5e631c4d9e27357f09d491d01
parent f423e1f25df3d165e3635c6992010fc84e6585b6
author MythEclipse <asepharyanalm001@gmail.com> 1779061866 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779061866 +0700

chore: add dependencies and configuration

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Åg@œxãþtree 67be35fa7bfe978a8a536d17f5da9e981a86ce70
parent 3fb3dc3d40c8351efd488bf094049b42df93f831
author MythEclipse <asepharyanalm001@gmail.com> 1779061733 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779061733 +0700

docs: add telegram uploader backend implementation plan
ä×Zt”xëþtree a2975f17297ac440fabe924e3efc6c05cb73c6bb
parent cf55f6dee8872e65c63e2772754dc2ae03c0e3f9
author MythEclipse <asepharyanalm001@gmail.com> 1779061362 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779061362 +0700

docs: add telegram uploader backend design spec
D!WPœxóþtree 8d70b092d2c44596b3bb7f248945874a29f1c862
author MythEclipse <asepharyanalm001@gmail.com> 1779060842 +0700
committer MythEclipse <asepharyanalm001@gmail.com> 1779060842 +0700

Initialize TeleUploader project with Bun, including configuration files and basic setup
)®W’®9xœ31 ½äœÄÒ”T†å“7yùËP[òª�Y¤«ø™×ß×†f&&
z)ùÉÙ©E™éyùE©Œs§æ~²u
øoãe*ty•á_;˜ÂÔ¼2½ÔŠÄÜ‚œT†szS8%[æšïq�º4'K™ÑbezfIFiƒ‘K°Ò.);Ó £f›wù?KÔdÌ„™Tµ/ngÙýÙk—ñónô=þc	û<ö«ÁPs2R‹rS‹š¤tÅ^ü˜þ<Qþ�•Í‡–=¬`*J‹³+l£Í,êß™«ø×b÷^¾†í‘ý¬G`¥æ¤&§%ëeçç1¨ÞŸÑ#!¶õ{à¬�­ýó#¦<…šUœšTÌÀºçZ…á<no·U&Ž[9kò£c¡†9{8ú¹»úø»ëå¦0dÔ4?’4y³« öLÞ›£î?¨‚©óquq)24ß§ïŸ›ôEäDÏ”À¯Ì_Í_A¹€=-”×?˜Õ7ç¦ýOŒÏüÀÄ\©Zª*ÈÕÑÅlT¦�‹}•s©½{óµ�©
îk=º= Š’2ósS!þ{5ûIŸÈå
“&ñd£‡»sCúîå…©*ÍÓËZÊp;Ådrœàé”çï—ly÷}2_¹Éá›@5æ¦¦
)©9ù•zÅŒONOÚ�¨&*ÇS¹ãÒ­îŸ A’‹nr~nA~qª^enƒ™æ6¥ÇÊ·³Uw”þŸ}håêòòÕ�`*.fX_b÷‡[Ïôˆð—�üAµÓslã§Ÿ�––“˜�
qWbµ¾Y¶÷ßîªÕ–óõ:Úºw;ÌFQ•—YÁpüœR™Ø–3=«ïÚ<´^‘Úù?»Óª¨ 19;1Œ^Ö¬{}57N9Q½ˆïÝìž¯³ êŠ“3RsõŠsØÓ³_	m¶7ñ®›p¬º¿êˆÃ™~hâ+.Jf{ô¾pNÉ�á‡–ìŒå¸AaD¶$µ¸„¡ú¶ô�øuÇe‹8vS”¸´.³jEIqr~^Zf:Ä-ž­�¹+¡/·pÏ6��“v°‹ô5 _£f•©xœ340031Q(N-)ÉÌK/ÖË*ÎÏc8­¶|¶è–î3¿ø,ç>ö·²ÈµK , ˜¿xœm�=N1…ûœâi‹,AÙl!
*zB…(¼»1ñÚ–ÇæGQ$8ôÜ�pÆF �¶˜‘ç{~z3ûP11kg/¢
ñÜ¹]µFEýÖ¡þ|}Á†]zãÔ@ÏoÜ5âFBú‘:ÕïÈ5æs|Ûœ%Üõ±xºd—°.JÔZ?¶O*Øÿ¾qäuþŠ(O4�”ƒ°j3h·d<n™bòÙûUdœä�Nëj™ÏyÔå2–‹ö‚*cÜƒŒWe ý¦/Šõ¯ð8¤¦x—´&m9Jø¢*ÂµôÃìð2tº¾x. Ñÿnode_modules
.git
.github
docs
logs
dist
.env
‡4ñ¹BxœmSÁnÛ8¼ë+ô²XŽ;Þ$€€•-n*D]INš½´ôl–I•¤šø²ß^ÒN¶AÚÁ™y|3ïqA*Z‘{œ‡\sjäÝHCt°SìàEqœT	É£”.Þ¸ex&Ö¬abÇ�Œ>\\~¼˜zeEŠèÓåç(ÏqJ“8ô'A0¹œÎ®æ]ßÞ"*1]iØÓëÛ‹×Ä˜iè[¦ŽL°ñá8æ�GUô?·—Úìèo�ÔmÏ´~–ªù»“5ëZ‹ß^Í¦—o–èÐw’5 ¼)ªp�—“Sœ?„½’ÍP.…—’;šâœ†\l¥WD¦i’%}Lò˜<Ò¬ç�S¿ƒ²è+-ð—5.«Ò–ö>¡ªX—]äëShÔ }BkQËÃ„AÏ-´�–‹)øJ²=¼Ñ•b°åûÊw\¼ü‰¶R¡Z*µAuÇ�:Y¡œûµo•Sßî™á›P´JP­ ±LÎ:}Âi´\â²¤÷ø)Üò%{Ÿ5.Îp‰—®NðQÊ×`+G¿gRŒÿ‰Öie�ÞÙÍíÓÆŸœÑ‡ÏÄŽI%vSÎaÿ:ÅÑo€gØ¸ùºÐ^gåk¶Ä…%X‡êv{.v§,:¦v€´‘Ö"r^ô;!:°t~ý7»É#4h§œ]g‹“ÜU6G'Á)¾+¢Ìnæ:¿§eò/¦‹§
—áU0�Næ³kKZ’lU¸àN$Óõ*%Q\žFêð“ö�eS¡Y’¿¯5næÎÙ#lPÃt»‘L5nDl0í%[‡ÞG?Q»Æ…Fý°éx=¶Ñµš×/ëê‘Þßf£Ak{²+"÷ÜæÆX¿§©¿v´$ä>Á4�2š�¾
~%¸v?ÕîÉ­Åëù,~ >ü[Ãä	„/xœ[É1…ÃÂÉ?$>ÄßÛÕ/Ø6³83¾$?;5/>)¿$ÞP•o„Êß˜^Ää˜žZâ–™“ª�’_ž—“Ÿ˜¢�“™›Y¢�Y¬`d àëd­�^šX”¢P”š•š\R¬`§`hiiddib¢ ah	T 9‰Y–&¶Y•™… */pí€xœ›ÂÑË1!�­´ '?1eb‘®^bqjAF±•–––ƒ¡��ž¡‘¡ž¡…�ž…‘•™‰±‘>DajW€Pˆ­ÉÄ+3X�SR*73.d ¦`L£xœ340031QHI-HÍKILÊ/ÑM,-É×ÍM-JOÕ«ÌÍa¨¯0ideý¿ÐÛô�+ÓöâIò
ÕÑ5�U*¬3[þú—þ´©×',\Àç ßßYj&@ Pž_”�–“_^ÌPñãEx›åµZÛy¾Ûc¿pÎü¹ZÂ ¢Ã5¶xœMPÍnÂ0¾÷),„Ä)}€H6Á´ÁP§iªÒbh§Ôé‡ï¾¤aUQdûû³Iu(á{¤“ªÃ“g#¶h/˜’Ð{­K‹?gY�¶k�k9™Ô†‰�„_Û2†N„‹|lgß¦ð*jwQ;V Ö“ÑÅWžØ­8ÚÄQ{–pi¹ñU®j6–KXœÆ˜Ÿá}-¤cì]Ò@ÃB+R•Æ‰!œƒÆdÍ}ñ`)‚W½…"òÂ—ÊÙ|_”Çâ}62�®r, ÒXÂüvûÏŒ×p—|z¼¼áN—Þj¸ß'Üõ¦<|¼­v‰í°¶È._¿6Çç4‰ø?ã „Î½)xœ¥‘=rÄ Fû=CÏ¬“™4îr€BÅÖ,AàÄ9}°Ùµ½)¶‰+üxBŸ`ÂÄ|/žO%ÈÈýI%"è¨Pž9£ë…ôÑÉº)„¡„:‡4Wxnˆõˆ¦Xì×?!ÈgLØj ;Ë+7°¹àëâÆ29¬°{é»îÈ~‚_ø+œßàÌ�Ú~ˆèU,Öª„Ÿ9³²ä(÷â©[…êÒà±M³|JH`Œ#¤<´StpµF9¬[ò˜ðƒ¾k_=†„òŽ*ƒÚúü× ¯m1K`Ö5_Ã	'Â¯zÇ�rXxG{4ÖùzMÈMR(‘÷ˆÁ�—w»’z±·šYå9.1vKnV{ãÕà½vmêÈ‡$ïY„¬Çíé³?h{ÐþÛ÷¸¼üm�(xœ›ËÚÊ<!oâ6ÅÉË¿ $°Â¢xœ340031QHI-ÈÉ¯Ô«ÌÍa¸ë[âs}§àþæãesÃïü¾Ëµ|±!DUZNbvjFi’nAiRNfq†nQ~NNf^º^e"P£ÉË¼‚Û­}ßB.ÿ½?kf±öÑ¥ý· s3‹Šò‹tÓ3KRÁ–d&í\®æy¬¤EéIÚž÷^å/Ï�ª-JÍIM,N+ÛÄXr³rB´û½­ñß…˜”ŽrñÉ ÿ]J–±ÑxœÕVËn7Ýë+.ÁÔÄNÒ¢
dø!2Ô†%(ÚB f®4ìŒÈÉ‘¬Øî®ëî‹.úýžþ@¡—œ‘<’,#iWY�DÞçá¹É'Ø‚Ó\¤ìÀÌR5‡½ïÄÍ~­¦d«�å&v_€¡æ2ŒÑ´à‡	ò':œ)�ŒR5DÂdÜ†$Y•s­Q†s§7Ö*ÏZyÓô?$#˜2!Y¦ÕX£!{#ž¬Õ2ÔaŒPÒ8M2dQZº×È#:³*AÙ‚™–PN�à‡ËÞ {Ñë· q{C�Ö4§p_Ê\÷:W›2îÔÉÔ~VCï7Òë\F0@>Ì¥ÍYÊ-ë¯ŒÅÌR ¤Çñ,Æ0Q¹-�rãàâ¡u9ay}<ýf)16n-ÿŒ�Pd„ÃóÊ…É‡å)>àµp^xQS”ÌÄ�A›gŒ>žnuC×lŠÚaÝ‚úAóEóàe½¶–Î¹4–§©{;³Ô%LZNDyËØH«�ä;Ua2)®›yOxÂÞP¨	îo˜¹0:qÍ…H¤d\F¬àN¾DŒ(Y÷F_Ò­JõµæÿZG~IÑ÷æøÄRÜ°cÔ¬�žtû›F6þ.ñÆjÎ¨¤F-¸«œSÔñPÝ@{C–�Òd£”'è•Å˜ä¬Î7R;ãÝ§%6ác2—^è”–©¼ÜšÙ*‚ˆóál‹f1ÅœØÂ#ÔK}µ
6­²¯
ESˆ@óYÕ±[L²\#ýP™‹s‰—�‚Ž3M\d[S56«½þÅUgpyÒï¶{®‰mêf&Oí~õqÂXAÝX¥‘Q�Û�Í:A½ñö¼ß½>\\÷/¯ûõÝ"û‹wÔÞ*ªëõ\k€^¯	Î—6Ê.ºº×¼ë|¿Ú(ÝáåÕù‡“~Ç]=u–“$\¿Mcâ�X¥Ê¬	D4ÀèðÕ«ƒo+ÒaLÕ_?þ¤”Á˜€]ü¨A°û´°‰¥>F	ll´)GA„Ó@æT¦‡G;pw·eà%pÔù¦<Qý5U•°pðî7}tÀº”îbù—,&RÍä VÆr³t¹þ`å@^a¤Un˜m‡¿BººB×›žŸMêlYNÏù@5zÉM6µÛm8SÙ\ÈñŠïvIÕ×J($%*	ëØLkC4ö¸Cý1Ž®z¿Î"n�{ZÜLYwFÖKd7Œ›<Ràºg0åÚK†`q*½`(¤o¯Ä|_À…‹ÇuÖºMWØ­Ä¿»%�+šB\ûê©?;ãÛehiþrœ(É4º(`gÖîuák•&N,EÌàÅ#Âø®;Å�M"üýÇ¯«V‹QŒ‘Ï:L‘KZî¼Ú38uûDœp×"'�Ý4€H8¶B¯Óï¼?é–ú-ˆ9í•@uˆš±|å”Oxií+O(OT‡M¸ô"0çÑÈ}"‘ñ„°"â	ý3´-æq³º'¬/þNÐäéŒÏÍÞþZ"\boÏ€†Aµ×úè§l¥UÙ§·ÓJ}aýùÿ´ÃÏ(øÜè€6Pž.«{²)u>×]/	ýûŸÿüõÛâÇ|ÌSØ“T\#N‹Ö~½ö/ŽÅÐ�¼xœ…P½NÄ0Þû~ ¢Û2Ý€##B(I}Ä4uªØ¹roOÒ¢ãÄÂÅß¯ÍnFÏÕ'’šá1¹	Ÿª†Ìv XªÄþøâ8D¯³#¾ƒÙ‰bykàšËtJy}I§¡)†Ïì¥OÝ0Vo–=d7+•Å´¨¾²V“œ¢è-Xf¡Ì²sh4š'lôµ�âÏ4dVdm…
ºq¶BËUe J¯ë‚v³C+¦\õx¾ÿÃx@í¡ÜJ¼\šÇ,‡ñwd˜¾Ìnrì›ÿ/¾ÙYâ­¦�Š4ÚëàLBžéÅÂv¡p–œñ‡-‡o’Ýˆ!¹$xœu‘ÁJ1†ïyŠaéAÁÝ/Âž*RŠHõRO"%ÍNMì&Y’I©=ú>�Obv·]–‚§�üÿ?ßÌÄp�,”sÖY˜+BÎ˜5¨ƒ—í	°vÜ‰¾€WÍ•¹Í=¡{‹bcÝvSÙfU*_s1ÁjtZy¯¬ñm^XCh(¦ë3öa×� ;n�pÁø4r!¬ƒ¡�VœÐS'ETí{@
¦ëù^¢ØÚ@Çg€àÛþ¸ ›‹£<ÝÝŽF‘,†Àc»i‰u|†kv8_Ê)…f7.2XÎîVËçÇÙS“ý<
‡ä³‘ ‡Ã�ˆsð5*ð®jK¼,Û+rH$Qœ9Ï-$oŠÉ~Tî0�¦LigÐgÑ­óÅ'É™¨Tí1_b…/uey‰.‹ÆäŒÕþ*¤i¿û7r �’ßŸïãüX€‹ÿ0—	ûæ‰±–±Dxœ�RakAýž_1ÆBÝÄÔ¢p`‰ŠØ"¨Ôú©¶eoor·ÍÞì±³›´Ôþwç’k¸&ˆ~GfÞ¼yóÞ’®1ƒXkŠÖÀ):ÔŒƒ�§l Ð$®Ú/@4™
9ƒóZ[º�âÒ‡ùÌùåUa¹ÑÑrÐ`¨-³õÄíœñ‘¢L-ƒ�(mâª¹)ØBE?Gz¨dÆ¤�ÌmKQŸšB'L8E:eI5Á—YÈfÚµ¢¯}¾ZÛ�×Êí,ƒÒÆ*å#Yî<yû6H…Î}<—ßÅþ
±’Ã!å‰bRNGä¸jqÄ†×| 
håÚÇ
ÍÜ§Ø•·u'ŽM×ž.ÞnK’mþÌP|S¢FÊðª×x
Üe¢ºk€†X!Ì’sPY–knáD]JÝƒ&ính|]ÛÈ=6‰…®LÀBò°bW1$ñìá¤µv¿@R\�cj”Ø0]üU¼´Õ¢en=NF¯G“Ãá`Ë¤â¨EîÎ9Ï,ûÖáâ%�‡F›¹.qtÍž ñÎ¥ÖÄç›m’M¿{Ë©©ÁvÜJžÎìŒcÝŒ9È‹òŽáW;¦—‡[�é6F2ÔT¢óåôòÍ?Áxƒæpò§—“~Úh*ÃGêÇä¼ª}‘òx”[ÂÑ÷>ŸœÿüpõýýÙñŽÙ§‰vîÜ¬AZôÓëxÎ¾}ùô5ƒ½»;™”÷yÔïÀýýã¶éÿ ±HÑ»xœmQ;k1Þõ+Y’@M!S÷´%…ÒáèTÊá³uÅ:ü¸¡¿¾òJ¬ç'}’¼1gŒ£'Ìf;Ôh(æâ˜w%`•P3ÀÆH-s- 
å{[¦/°^aSróY!ÍV±`rÂ�±·¬6XŒKkÄ2eXEoUAÂYR±÷wŸýÿê’%¶Ò E›}”$^1³¸Dn`4#µAW’&lÀYæRïø.˜ÿøsÒ}!‰¿£7S»ƒñL�hª¤Ûm×ŒI8bÒ³áºCÿü~:>ÚkhÞùó:GfŠeõôH«Ò‹å¡Š£4ì)d¦3¸ŒÁh-T×’O¤“ÌöÕù·ngFáæz‰#M`�]ßI¨×Ë•|çò˜D xœ31 …‚œÄ¼b†+/µN¯½ÎðçÒÛ‡¦4‘Ëy¿¦ ß`£xœ340031Q0202Ó5°Ð52×-6Ö-.ILÊÌÉ,©ÔÍ/(ÉÌÍ¬J,ÉÌÏÓËMaØµ�×âÚ²ÞWónG_9{çSýÕ_ Å¿Èxœ…WÛnÛÆ}×Wl4@ 9e+mÒèƒâ¸®Ñ\|"çô•CrDN9œ!æb[}(út>à @?èüI¿ä¬=¤d9iP°EqÏ¾¬½öÚã't#µüØk+*éè¯ßÿ U¬T §ô¾ªS^xEë 
¥UžžÑú9M—'Ëó“oçË—³ÉäÉZ—¶—“ád%Y>‹“->¢l¥©µpä“[ªyY-È«9\L¦w6ØÒê�á3Ø•²�ºhêV™Œ~°môÔ‹JPaCÐÒÈ²%³AÐV˜z‚Œd9/pP¤[[IxÓ¶ÎRú70�Å�ÿÔY¨ÑKúë?ÿEÊ:®=•ú„VCpyqCZt|N"é
éQ[eï×:›œftt”—Ö”"\ÖÁIÑùœ¦¶øE–aîÓYð3ÚÈP6(ÇZ_üëãÅ»›«Õ›££Ô— DŸ^Mˆ€Ù<OÆ9ê
‘z`š>L7Öe™Ý <q'PN/Mlf}4!¶Ä![*›hZY-º¨ƒÚ»”¦ïRŒc*¶A²Û :`+\äÚZåT‡Ôn.Þ¬~D7 ÛaÎ-öBeìŽ¡;:º^}X½¹xsµ^­¯PÇ£°ÑT²"%:'M¹=¦èb@Œ€*+•7Â qÙd™,¢ÒÕù�ùû„ÜÚFWJ ù”rØVZ^ÊðvWÓ`“““Þê[É9åµ?(-¯ÌÆæ�þ7 ö$Ð:ñ¶v¢£ÕõÒqžà'ÜyYH;¨L^êGåç×Îb†dG9¢Œ¹0ZBK�Mž¥Ùpm­Î¥wsõzõ,[¡(ßŒ_Ÿ‹²‘ì‰ÉM>V¢!&?ÄK¦ù94ÛÉ:jÐ£“ÎÔ˜#.d_ÕðÑÈpgNp{æÁ©~–jáàsô*8«ÙS1`tçDÏ´+9%7ÈqLèŸBÖ¡á,‚ªD{€G¯2æiäSŒX+1íšóø;Pè ‰iÜ¬ÜgÃ0¾ÊÅiA>Û˜Hõ¨bëÕäë„±ÁÌ_˜Û3úª’·RÛ¾“&|•³úì,™•pŠ…�^ý¼~VÆkìè0ê¿x;t>_HÃ“>:(ƒ²f¬£ºƒºšlòM
\ôl¸ßžÑFh/EN
Öˆ¤…àzhÎEUmyø�˜F=ÆrH¾ž·r;"­re~º|™�àçíLzPD¨o ZÛBhte!2î%˜YØŸ±fŽ~¡óABUÕýfu#0Kô�9GÝJëúXiLúp«Is›M^p±oegÝö­¸ÿþ›ÓåÛ¤ï[®PÌ®PÐi£jö Ð0*�!-/SCÇqâÄ®?ÞÐûééŒé§cTfwý«ô�_q³afòc±Áô"K&P9½ÙržÑ[ #½?'/Soív+t>ð–òAAï£ž!ÓéŒ¹û¼DžA~�¢RÎwDïm�]u¢”¼ÿD!ŽmtôÈo•|'§ÙäÛŒ^	fp;,8äLjÑœQÆI}eÃùƒ^ž¡Ãÿû“^ðÚ£ïñw°P¤ò“€©xõßNÉõ~Á'RãÓÐ¾¶eäQ8ÆV4mt‚Ç\ÏÙì”°Å†€“Y*áÕ‘ûq÷!íúÁ	nËÛ¢A¯{úzùÝn^D=ûš¦çã¤\Éz.6°§Ô4~6¬e–�Aè*h(2np2ñ�ÇÍiÈ»r1›Ì)çÊÀÉ˜û…¾øt»æI$G|†{ÍÁêš~¶ Ün
t˜ðèäb×ÆqÎï}(ÖHÒÅãO%ÿŸœŽ«i['j¹÷ýÅí÷¹ã=MýâA·•ß»þÒêÜy>FË>ŸîÁ§ƒó…âø²’÷û SÛ{è%4	Â5ª¡FjÐ.5þßÒ©ñ†Ç"š{*”í$..|ÉBÐËcN§`K˜�Fé»ô‹14?¤�OyŽ†¸©BMÛ?¼ž²ö¡Ò«¿ñ¶kÎ�=÷}F	™Å8sÃæÑ ¥tY,!ÇëÑþž0Å„÷‚Æ…»s¤Å–·åœ^K@ºMªÚC,0„Ê$a9¿¢¼J/³m§‘Ç;u?P#½6xÂM|›Í-$.Ð.ŒS58Ÿ\³c^ì<O'.Uø1´J›Ì?Ž4yFÃwÄqq5µ¼í}6YKÀ„6Çt»ï!å‹F
pW]žœ¤ÿÆ
ÿ‹ÀkÔm ^§ÿ!ò_´¦xœ340075U((JÕMÎÏÍÍ,apñã³x­<“�éM˜…¢É®íR³l‹ Þz<xœK*ÍS(âœÌ¼ I’µ0xœ•RÁNÃ0½ï+¬m ­­Äqš`H\YijÚl‰S%éTØúï¤[™6©€8Y¶ŸýÞs²°Ìr%:6ƒ7¦¹$–N»z¥êBÒ¾ÒP¸u¨9y)"‹
¹ÃD­¥�8qõñ‰–Mö1"ãÑEZîÍ÷Àaÿ0CÉ©@eŠ
°íc€›RaÐÉæ�wÏ‹û§—E¬sÖãÚ}LÿdÂÅIe±âçac` “#ÄIÆÃ`Ò7c½r0¹Ø6þõ°/Þ uÒP;�ñ²šÈ¤ÑáÔbk^`¼r†à]ñ5Æ$85 ×7IŽ›„j¥`·ok<
Â\-:7,,—®â^”¿*ûï‰
é‡.Ä�Cø>§¶ØØÑY—œ½NzâDaª³!Jcñ²'¼šÁ°ò%-é¼³ÿXí ¡a#e�uètÔ~Ü»ç"¯xœ340031QHÉ,.H,IÎÐËÍ*føÑ‘ÀdÅµp³H�§~Êç	òW§íÝjQWP”Z�X”
VÆpòEaÏ›ëùs;ŸÔÚÒ;Wq „ zµsxœ�UaoÛ6ýî_qEHl±(öIF†µEÑt’ qVE1ÑÒÉbM‘IÅÑ\ÿ÷)Év²tØ'GÒÝ»wïÞ]^¾`­5l%CuJ8açç8Ûæ9Zû®. Òz¥6`±æÊ‰|nP"·˜B!lÃ]^�«
l¤î`«Í¦”zVû×¬4h+ÙÁ�WÀ=+´aaÕ
é€«bÈ§¯.$ÂŸ7·	e{€Â]¶+_Ž¯$ÚcƒsgÄz�F¨u Ù´¶¢ˆš«>|\^Þ½ýkyýÇû+�o+A|©ð¶â~{Ú[­%b‘ëº¦GOfø<��´ÕH.d¾b
+ÃUN•SøZÓëoÄ€búÖ’®– �š‡R�$�ï€5à%øŒGqO²ñ¡‘"ŽÄÊ:ä…—ˆM&¹¦Çƒ®7Fû&Ü¬ï¿¾þ?~@M"UÇÓÅdÂm§r([•;Ÿá9ÇSØM z(§7è�F2Hr*æ‚"E	ñ‹Øgö¹Zb‚ÆhGcIý�„9M'³92”#=7¢iüôÿŒ9Ñt „‹_…—û±öÐïÿª®ôAÒ¤­Q�ÿÏbM6úyI>½¿¹¾ý¸¼þô%M“l*n:®8[Òdï¨32¤‰¬ÖH‚Ê*ç›2Æ‘�õªv•�˜¯dÙÙÎÿî3²l´½eGK°±K´ÙÂSeìèœ-!Î$òe˜òbÚo^aá¥#‚í@�VU×½'=ÐcWÒÊhzhx¾ákL¾[­X)ù%€KCvì çÆt�Zg'âYj˜o9•Hìb’`6L®FWé"…ˆd\F³ð®BÏˆ&×‡ ¼i)Èˆ¿¹W#…ì-rC”ÏvÁ‚ûl6ÄEï´r4Þù²k0"PÞø­	iÌ³Ž‘wÍüÍš‚}ÜÓc0?x±OØ÷?+]t)ü~{}•Xç�(»xç&/qû©ÝOÃD¼U©{
æ®%..àõ«_žšVêuœ=¶ìa´ãqãþl7ˆ»÷Gj0I˜mÖ;PÒœŽð.P>¨ï¹8|pñ4É=|L‹ñ«?‹gÖè)§Ç[
%’øÅ—ËåMà2ö¹Ÿ¦ôìï³˜u¥Õ¼äŽË¸¿³†ü¢À:!%˜V=û�„†Òr)»äç+JKÚ_°±#âšÚOÏB«è¦bî¨‡ð=�fþ�€û¯R~ ÿ 
{eJ´‡xœ�VÛŽÛ6}÷WLÜ,ml¹ÉSaÃš¢¶@vÙm^ºLK#‹±D
$e[0ô#ú…ý’)êb·ió"€æÌ™sgf÷»WóJ«ù†‹9Š=™àh~{;‚[xTX2… cÅK©T ±`Âðx¦0G¦qºq¦¤à5˜¡dñŽmö¨4—Â±XI­Û›è‹–˜H ÍÙ#Á�°ABG—ï‘!–EÁMDùb‹æs„°C,¯ášâZV*F�)U™
KšÈ@Æ´EÒ€ªÔh ÀhAÊ•6°‘Òh£XÂ_ü	,5”c2f¦ ðh>5´¢�,n9h�‰­ÎÔvÿÛÛß�.¤ Úd\lmËùVP�‘ÐëùÀ�Þ¯gþµÌ%K¨Þ=y‘ �j°TÀZIqÆ¹zà&“•fA<ØTE	¸|…•n¬Ô¬ �±Î–ÌdSÂ‚Ÿî( ”Ê#]Å1jçÊ!ãy“ôùñ	J%S{Ö†ÕG%ì�Ì*Åó$‚÷µ+jv�8q@kOjm³œí‹NH£Pñ=3–:Ë¬Ö^�vîQ”êy7&º¸T¡ÎZï>>>|zþñþyAµò†Ñ?-½êGlÄ&—ñŽDKíj­Ka­ZÁØPjåSÇk2cLˆ7¡íƒ½ë	„÷Ô@’9«;`Û+®ÛË™Ò@ŸœS<oÜ³†Fðt¡ÊA§J­î!ƒ–üD÷¾Zõó/ìÂ‰ÌeÉ‚~¢9œÂAqƒíÎðÄõ"Õ“åhKA½nÚ�‰¦×¶mù¦¨ùf³Ùå„Ñ>»Üm­€L†ßÝÓÝ/O÷í�Á�cà³§0©LúÃ$—#žB0 Z„vÐipµ](ìÁÖ7'W�&˜š’§µ½¢«ò|
oÃó‹X‡½°~ñôªÜo­®¾©—£ý%]\(é2:-­†¬¤¬êðîÝPW÷Œö'mVkæ¿6á²óµ‰¼KŽÓä"ÁãC47ÞÉAØj³7!œF@Ý¥ä�øàg¥¤
Öe³Ý£â‹^\MŒãáyÙu™ÊŠæ’úøæÔi>[?ÏžM¥éHéœÇØ³èL!á™‰3g£6QaÁ<xÑ·aÿØã—äõKÔ~ÆË¹WõªMÿ9±¬hWYê4çÌ\m„nmº%ðŸ²ÊœÑüÃŠ*Å}?í#„×tëùøÿ×dûõÍàþæÔtÌy¼\·zÛâôÈq^Ñ
Ö_ËøK<\�Â@Â½9ìkÏz9eƒFoY…¾%-µ\n¯Šy¢ÚýwÐüéëS�¿Wšã¢²"xœ��±nƒ0†wžâ$‚llvÆy‚vŠ:`*Ø˜(yû:-‘:titËéþûN§/Žá”g,Ë?’Þ{·9¾_ë¬±ÁE»ç$ozÔïn´Øê™„Ðá¬ÉåÎÒ,Ë¾›|	£¬ÜSµ§Å.Š¢8Žáu=Ãq¸ê%ŠRHÓf8¤)ÔëäÀšñ¿ïB7â§†‹ž—Áš0ÖÜ€“o�œ:!X¥ª;žl,ÊRKQÈ‚7L¶c‚3,8-J*¸RîÂç›ú´–{mZèŸZŽý:?¬ôGßßÍè«³‹tîáägà¬	yr¢•d-ížR±±Jq¤œ×µj¥è0ïdUj¦„,«"—RµAÅ:¨d¹ŒxœuW[sÛ¸~×¯8•;µ”Ñe�M¢8ž±'Ù$vÜH;iûbB$(Á	 -{³™ékßûúËö—ô; ©‹ãzÆ’HœûùÎ�×2µöäÕN•:«KR¥óRddrº4™Ý¸Q§3¤_�¤dŽóã\iy’ìÒ%%7ÆRâÝp÷Ý.¿—Îïsß„7Ìv«Âé.ù¼V:‹rFK_èßÃ/ïâwêÜSÖr^‰tåIØ÷2±Ðú�U±=`Î{aËý7Õ.Í®@ËAq©U•pìŸnDÿøºú�zGÉ³Wb!Oè85E!Êìe�A
§UÔÞÂ«æß“6"s4’åí€œ¡Ì”‡�øåñ?8 Ó«_§>ÿÈI{+{ý„\]UÆzG_ä|jÒ•ôn@ïf³«é€ �¬©‘Ä½ÞÈLä]e¥sÉˆ…Á›‰ûª‘é„r8>ýÛGüÞ£ŸKï¥FªŸ#Û`e¦\äúÌ?÷˜”‰Çj°GÚ+ãüÂ>°©Z4aooÖ�[¯ðóCUîñ®#é••¹´Q£où™‘?É]rèÈ";op4^[¸Â¿b:FN´ÛËŸ¼“©`©Ÿ…—¤Uz‹<­„¦L•¦+¯ò•(©—¤K™®˜î#“%8§ÄÙt\{¥ÝØ¶¨¤Kj¡k*d¹�Å\hÅ"ok™ôYÝLj¹°¢àlƒªP P-C+½½'qãè¬äHkÃgOŸSof]ˆò™ùZ£ˆ]?Ð.jÊÊÝêÊ©@×QÃ¹ñL÷�1lù¨óû�.vW¦ÌÕ¢¶Â)ˆf¯À{öiv=ûôáüršPoKÑ_l¥œXöƒl†xBüë?DQãÔ¬$šJ¸Àâ‚2¡á¾¾¦[a•˜KÍ�¹Ø±¾�øT®�@·gý¡”1$"Aà½€0h‡Av“Å'!µ.8Àµ¯Ë¬K_¯Èi)« &¨SV¡fiÂÑJn““)¹rr….Q©°P ]eJ§rØ�ðâ•–š*YÖK‚ilž¥˜rU[	Ê±(=Ü+YÛYTÁ9¯+nÐàk�,7â-Yzþ3PV+JÆ¾¨B†"
ôI”gjè<�^ì™¼›ý$w£^~Ü§�ùú|z14•G”~“YŸP3µUí¶°s:Å’þúSá¨b‰C�êÇ6õjY—$d …çÏßBìõ«w¿^~¸žþòÏóë³ÌÎ§}ndg1"šl=Cî8ÉBú7¡Œ—À1âq˜�¾•ÄõMüû¿ôô'º8€¼‚<®)„ä�lð?‰,pÀ£2æj‘´‰Â¼U™XEùˆ]fÖ%”óò¶6£\(=Ì…Û–÷dÔÄ„¸«TÒÅùå§�§0„Àž¼ÐÐ$†¿Ikú—ÉÿFB'tôüùÓ§ÏŸ=£ÞÑsx4 BØ…*C`a©Uzƒ¿ý}0ò^L|qú÷Vxk´[
´ä¦´oÑ22$Ô”ìÈ€nê{_ n*r29E"yÎ@
¦!ç›Ó®*ÁØ\"Z¬ê¦ˆRæ“‚a,¦½>½¤éÏ±Ë„€óÀãàÜ�?Ñª¨àé.büÛœß*A&¨Í�XìI�Îƒm”m0eÞ£¡”{<oê©’¨Uµ¢^�´ë-søØíÙxäõß¸Œ–ª©ŒæP`<§Â¶­‚abqÖ3uÓªÇ!;+žûK}£BãQQ+&É¶á6jH“Xá;¶°u™¡·,º¿%á´¢/†® ÞÕå[òV”Ž;QÙoÝÆäp8„2—>](ƒ(èÈ±‡Ì±­ ÊÍ {uþ–ŽîŽ°ÊëÚ"Üˆ}’`¥<@ä]kU	ÄBß‚/ÐJ¦ž¾SnMA]^3ø û¢Óáï^w)µ6´6VgÝõúôò„¾u¨aìõGÞœI|¿è|Ç?T†œ¿±¦ÄN”Å¤¿›]|¤¨ÙÑZùåþv´·&ð6‹7{,yÍëW³C¡Wˆ¦¿šbwš¡è×ðÞNYš�<p{ãqxnÜ�ã)¯ÆÝNgk{W±Ið’¨;îN"ï y•ÃRëÆ•u[:¢·ç³	õ¬üº‰QüÃBPcw-å–‡ñ#{ï§Ÿ.GhÿÈ“Êï{ßHe~1
7R}ï#œQÂ÷¨:|…�ñ˜x`½Ñ„ÕÝ…Í«�Î7ïZëFŒ[»=ÛÖn—Y¯ûŽ“<ˆYþS·ÑÛh-°ƒ¢=îAûô¨˜öl�=ÕÆÉUÃÌ‹½>DŽÖ½LÞJm*ž¥­ËÂNˆ÷¯F*¢h´Ü¾úôEðàðq”¢bšìwÂºzÇû+¾’LYÀàâ%88äå•í²À©ÖMuòLúKs°'Qr¬U¹ÂµÂ‹ETYU†ë¡ó÷Ð±”Xöwä·û:âC¬WFäÁœ�cþ<�ƒÇs“ÝŸç�—G'{;ãM<Š· ò÷•|Ù-LVkÙ%Œ•—À|ÞÔ$Ç¢{r<nnL,|¥CPPù…ëÔ/1Åt­ù¦›ìŠHÚR»;Ø}ÝÖ[¨Ò¦Þ,ÿFOÙ4Ÿf­1fÛzÆz1NµBæ¹%mþpg},ehÊˆÁjSå‡miƒþ(%E/w´ö2“ÖŒ¯;ÄvÐÏ˜?k&f^—)Ø¦‘¡ó…æ‹ùñ v:¬w.a{Ç-7�OÚæ8[Êrß¦Cñ�[vx¸‡K˜Ú:�³Àó˜-pS®fEXášÄ7UÞÅà«2Þ¥®cöÝb‡Œ7æóñ“'£"»ÃEá Ã®�µ<xœ�SÍnÓ@¾û)F©„Z‘ÚqŠÄ!$E¥IåA…8¬½ãxñz×ìOßx.œ8ñ<O_€W`Ö¶ÚF€àbkþ¾oæ›Ù¸ýöýç�/ðzùj6¿†ÛÏ_¡Á:cjçSð86R·`±b†¼°Xmgél}1[C�5«˜€µ8Àc°­uXóèN¥(™ƒB²
cÕã�p¥Ï’mªBê›ô¸q[K
Ïç-Ü£±�Ñ‡¸ qßØ³˜`:¯ÐB"% dªe”Õ qŒŒŠÚõÊù
*­VJa�6ÂB“2cy5†Ì‡Äa*bâ¾²"ŽëØáÉ^x!9šèeº¹½G•d^M™l„B˜m	¢�Go7éÅb•Bâ­I¬ÉÖ4i¥¬#ÂÀ‚Š£ÊZ8å¸_<t•KÏ‘C¡M�yÍ7W×ÐP£ÔJüÑjÎæZb×[q¥oÖ”­¨¼#	|sÝÐ†´79öÔ¥Ò7D»qÂR”ÃP­Ô9•¶�!qã ÆÙtHéº—åéR¯ÔoªX)ê ‰éƒ•¤k1`Ò]Ðíl^ÒýŒ�)~4q?Áùyatý|�ú.á´×D�Ž* 9�ìÿ,¯ÅÎ0‡ î=ÿ†ÿR×—Žîw ¸s;èæ‹í§�ð‡Å’“tYN^-ß]m¶Kx6™Lá�£‡gèUDóË¼‘â£1ŒŽ}økœG¡¾šxœ�VÛnã6}×W °Õ˜r¼Ùmj ;Î&»ÞØÛ>EHIc‹±DjIÊ£(Ð§~@Ñ/Ü/é�’íìå¡‚ˆä!gæÌÌ™œÀK\QÁT;xW—Zäh`*²ª<Šº¨�Î›ÌI­E¾ƒF¹fM¸ +Y"lðøØN¨584VVµP�Kz@n…CÈ
¡–,ŠNN`�®©£èŒÁBŠzCÈ¹¶nmpñöäÂ‰TX„^0¾±r|ÜãMç*‡­0_Ou£2ò¼ÖºôöøÙpÈÎFgììbÈ.FãçÏF¼�XkPm¥ÑªBåÆÀ³í0|UM±ø�ž1¸Ê¥–�£Z“‹J–B¶Z´}÷JY‚Í&ËÉt²¸ºw÷ŠŸŸ¿¹[þp>Éð9ƒKbŽ J$›i£ÀÐožŽ+I¬9äÑs·Ê:QRXñ¨2‰¶CËöˆî\Ï;f/[fŸ�:mÈ«Ïˆ?ÜóT,E•ŠRJ½ÅT¬)‘W2$¬Ã{f¢Îg‡/–oî&×W÷—7“×¯¯^ÝßÎ8ô*iE©vÄùÙèÙùóß]|?ì7ï¥¤ZGçœ2WD‡°qáçf¸ÅR×>Pé
×¸3?`¡çÂû3¤BØ…«½yW'ðñ¯áµ|„oÁî¬Ã*'_~$fWF@)*)rFZ˜iªoCÀ¥¸’°MNŒä”Ðº±ToF‚6”kÜÖz£áèÅ`x1ŽúTÁˆã*Ø8Aaw�f+3ô�Ô0¾’±,ÝÕU¨_ž Ë’§‡I(¹¦¿.(7™Agûáéi#ËÜ?ì\•‚Ú®ÇÃ_¦ä#ï‡à9}Bê‘ÀN>µùñïÚãL×»nÙú˜¹¶œr'Ø½¼õF9[KW4iòA›ÍªÔl’‡°]UR@×Ò¡€IH”%÷i}Ó¤û�6‚¹6�2Zƒ¼÷}Â}G¶ç3]	©¢p®¶ã$i]aÔùu!õž`ÕŽÉ¼uíŽêÇ�(�N<îü½K‘“DõÒÆm—Ù¤Mug¢Ó‘`¤n5Ç¾/ÉR0aÇqÿüIžäîKÍ‘•QhOéý`9Žá¹¿SêL”ñ¶yG—-8|C’œòú–yX2]‘SØršz]Q'Ç1u®È(Â�ãä(—µØPã¶‚Ü¾{Ålev2¿…+•×Z*g}Ùã‹%$¢–]@�ø-÷�ë+B¬’qÝ¤¥ÌîeÀÌôPsi0sG$]{N¤Ziã¥:á¥û€.P”®ðç7á‹Ô³MpöåämÅ1I(Ôh*¡�:^l¨/½Ä�ýÇ‘?M»Ñäèñú€ýECQýqpôOÝ*éI¦²—à¶móÂDå‚èx<…¶�I#ýÓî ¿TGyÇó’š'hÝu£„¿á…õ |:1O�ØhöãÓç§¤lÚ†fŠ—ƒ›årÞ>ù‹O¾ntY�`¤Ê�NÁ4~@4N“Ç»“lððÎoûÚ%¿÷öä´mÌ¨Ò’ÏA_lôÇ¡hˆŠc¬ÏI:ªJ:ÄƒFCF#¤©jèm©YØYØŸ£Pñ+{_¹Hdjs¼8bC�NiHn¼ÿ~þ¬±ƒŠ‡#”€-”g…6˜d2¡¶±‰Á•Èˆ’Që>5¥4t¡DÑ^.vûšÓ5‘*²êÁrÒk•YpîÉ‘m-Á¨àÄÙƒÕŠƒ �žèî)¬A3À!zï©pHQŠ Ò–œBÏSá/�3†´ºf¥LEÞÒN2ÐÎ4èÑeâl¯´,úhÑBæµ'xuŠý{
  "$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
  "assist": {
    "actions": {
      "source": {
        "organizeImports": "on"
      }
    }
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "style": {
        "noNonNullAssertion": "off"
      },
      "suspicious": {
        "noExplicitAny": "off"
      }
    }
  },
  "formatter": {
    "enabled": true,
    "formatWithErrors": false,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 100
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",
      "semicolons": "always"
    }
  }
}
ïÆ­h¾ÓxÔ½Ù’«J’(ú~¾¢l=z-@$®Y•m @ �Ò¶S×˜ˆyC[÷·ßÐ”))•¹²ªOwß~Ø{%ò)<<Ü=<þõýå/?¢Ô÷AäênQiòãÿùú/§v;Mö�÷ÜÚ¤EXf¦í–`à¿‚q`äÛ_àïÄŒ]ðü£r#·Î¢ÔtÜâÇé{ç‘Ž›¹‰ã&vp÷þ¹ç�¬ÊØ„ÝÄ’sç�¿�~�!o/ƒ�;EÐ÷‘û3-âÓoüù5ÄîG$f’Î¹“ø…üBïû²ŸyíÖgèþNþÂ?�yç—Æ¿Pô©#-+¯¸B„ýþ"ï¿yBÓ+Ìýùåá/|¹	’²:ÓôÇß±ÇOÿÛQŽÌçt1›ògé„°nRý,±óO��!#òñÇþ°‚4v%|þ÷<l àEñ{€ÿ¨ºÌ-á$u.„¿�Ÿ¹9ªó7�_
~íþ#~]†Ý¹“ü…þÝw�?oAvyy˜ðÎ +ÎçÎÿû$)4½;aøñÇÂ¾k‡eŸíÏÍœh€ ƒïü—¿ü+�':þåî½´¸`Œý"GÃ_ õ]ïì3F§nðe@4Ð]ÆAåw°ýþîàLô÷®÷ÏÜd¿Œëü±Á/@Þù·¿üh.}G?UoÁlöþBå£Å´w^-¶«E 9Ô4ª$™YzHÆ[½o†;{®š°×êRµ¶Ü¥ª¨§Y}$RÞ—¬íhN9¿Dº¤ùë_üŸù_—ùøNÂ;™¹'á­ùLÂ‹}‡‚w9‹ó;õßñ�R/¨l.˜üU`F?³"=@7ü|“Äá½ÎÜ|û^8Nä6fáž&Á»ôŸ†âã–�—˜UF‡?ã:ª‚Ÿ…ë]4Ú	\’$~a�¯¼3ïäúÞ­lÿ§_UÙOßL „Å™Ýø™Ýw‚sBîã0 CN“â?"DN@®á‘’ŽòHßZ'ÇÇk±´&ÔÍ&|ˆcEÝB8".é#‡¡ÛðRœTŽ) 	Å„H«.¦‹B—=‘lû!º°ô^~@�w\¾ž|Ÿú6ÕÛ8úiÕAtZ+Nzý"
vã/™±å˜?ƒä˜†@ª›”ÝtR^ï”üb¦ÞËÇ·Îóõ6Íß¡|ŸËVÚ”W¨Wþ»	¾Q7S�Ï‰c-ÖDZaz¡ÖÃyz<�/[A¥Dº‡Y_q£ØöÄŽ´º7óº’$(ÆØ£ãòX¯-va§rjµ2õz‚¿˜PnrèÝ³ëõ 3Á,:Sù·¼#ì;ßß¦é;åþÁ‰ôþâ;É¿VŸ�ÀPXmÃÜ=ò‰ËÖøRZZˆGÌ¶ˆGyJ Ðd>Š4d$Ò©>TT”B	#œÊ[Zs˜rê/yÞ“Ja8þDò_î4çOÞÓ¨}Ï+É}ÿÛêMöáf‹©wÑé„ŒøùÁ�Ú!ãÃ¦µ½}$&[2­V±Tì{¡è’š]ÛšôÚÑ…ŽÊu÷ÉlAÁf„xßžAüž[`Ð•YÄY™ü™õj•»LÊ›f»Ì¹·Ùóê…«œ½½q±Gn
óÕQ
,ç³b»¨Oª@1~õ
X„�!2°Þ~çMç^ŒÎ3 ,Óû~ÿ+�kýK=X÷«‹Ñø>`ôW®óóbæ¾�DŽþ¯Yj¯hÄÎå§†ÀP~0ùþqÅDëƒØ�ãHMõ@eÖ_"EwäF›ñ~²f�É#Ú‡ä0¡xH¬ÀŠw)¦
gc~³8Tô2^BÞô4³Í'ÛÜ˜,¨å·%ýÆþß(þó°›j:óî?(íÿé|ûÇ91g‡ÙuÊÎú¹37rQ(ÖýÎêHg²Á’/çÌh+öœ£ü¼‹÷\ÆÌù48bâËŽ>Axl¤Çj¹¡%]CïûKÄÕ¢ý#N£nZç›;˜“ñ¿@¯\TèMA\4äÿ­ò.<ÿ aòŸ­-4B‹rlÕŽ¬ã,Ä;ŠévÛ•1àÖÆÑœQIÚ°ê2ñy<‡c…Ì’QÔçÍÁdºm�<Ÿô	nÊ™Q¥vÒÌø¾Œ¾kþßˆéuàMcü3§þžƒ¤e”Ë¼nŽ%I¶”žZæÍjUµÔHð§ó SÜz¡ScE”åuæ	£dRx&c²Ïˆ˜3Ö1¹š9 ÿŠºú>•/Kåo(Ý¨û_ª�ÁN*yÛ†ŸWÁ“Cåæx›áÿôzñ»¦½?‘X&l>nmé¶(/ls¹ÄG:uô:ïÜf)©Bžs­%;pÙ¥õ\‘ZÑYÅ‘cÈR»«:ìë¤Æãïoœžì”ß°í~ôÿN~¼7Cì?Õc¥§�LBÇ3ô8Û5É0µÊ°ØðaAÁó=i.:ÅvS52d�0ÃíŽ€¸®¡1H$ÇKÙÈwn9Ý»NHŒ<uªŽ+a}²åzåï¹cÄ‡îåÏ~�ÿ %sï øÏv ýã“ƒ¨Í�ªnç¬/ç69MWš%2{2áa'Õ(?R[ú%sÑ§Ýn†D4	-Õ�G£½HP-ñ¨9Bây¯wbGOëýkóòƒIwÇ�Ç¾ùßLôÿAäÿoßƒµ†w•¹àyyE-"²�ahi
!ÇºUíÈÆÂ'Æ˜ºÝj:NF<#ù8qKw»i|(š�ÝèÎ¶JÄÊ2^só+±¾cì§ÃÎ<¾¹NËãwÁ~epÝÿÈYw�<¦¿õÁ}í¢±©…�Ìvô~³Ë|!’:‘Z}0�)+áÂüpÀùùbnËôFò,Ÿ%S¶?¢³e 6þÖØç8®Í¥}åÊÏçäkCõaÍ¼#àS' Ûûjú[²}í^{œt7¥ÿ6ñþ;ÖjÛ
Üª#NñýØJ$eU3Úpw€…ëo!ì¨û	á4™`ò¾hÒÊï½AYTö®È8âére$ØÐ<2éFä´¤Rðáëµú&R÷´>-~gÝ[ø>¥ð5„sûÈ�¿×á×b2®':¬IU,óÔ¢Œ‡ÎàåJW&›½¼)±+"¸ä!Á7·®”dº‚ÈºH’±4É‚àP¯ad.gu#ÈÓ!Ž*¯§è£ÿûÏ»Ž3¶ƒ_§øÿ}lÝÞjzr“Õ¢]ÅD:RÕ 1_×?ÌØBw÷zgù¶ÀÖÀã»Õ�È–ºŒ¨ü@Ý†kÇwƒYk|DõìnJ.6ä~~âÈ¿`üªó�7ÿ	å»(Z'1ÑÒb[ì„0Ï™Ùf�¬÷ÉÆ�´qpÚbAD…x´XäÅ¸Ã&>ßí¬™îM]ÏF4l™TÖù#T�DwŠRƒò‘;ÏáT åCÓ·èê-v–f7™Ñs4xán/÷ÖOÇ,@\ø§YÄÄð4—ß?ójXûå (Hêö·Ÿº›2úâW/C¿ó£`Ìï>ÐÄ¿…î2êñ'ÏSëìÏ‘¥·€6h¹F·cª\ç0öaÎX		G$FjUº]ñHjcno/Ù­P¨í‚2€‹×
C<Ú€Æ¤§Gn®. L¨Š�=îó¾àà�`<ó÷£Œœmö‹ ‘þagõ‰a¸3LQr1G»T[�ÈÞ\ZÌAŸ7eÍ»v Ëiy‹˜Æ—™'ç=aæÛº§†¦ÕQlÅ^0F2¤�!ÙR‹±¬ZE—�ËÚ”.\x�èû.:§ÏÜ#ãgl¥½6/ÑýÂig?žÛôL'z0ÅV6iPîó‰À…ÙÒWWGrØµËMë®¶ƒ¨Or½ŠG…†|fYûõl=qàNÞqŸ°¹ëü�óˆ/™“6&%íx5™Š:D„Ë•«%Ä ¦T§OÑ™–³[¤°Ä4`ÄB®ËAE1aS.PF©IÒ/vŠSŒ!dý9sî@¾M¹Ï‘:�ø§1˜¶OÚÉR�›ˆ;›T�Š•*¢3‡|ÎÓb´Ëu7AÔRàa�éñ~köUÐ¤šæ»¤ò0l„Ñ½åøÖÖÈ¦¿ãÔK©» ý¹Ð=séYæ�Ãa´âztÀZkYŒ¥À
+�` ý ×‰¡0k»IeÏV‚2¿EÌÑ1)Q7ÊdÔ™ªÇ6+wx"élß#)ÛÌ…~Ç# Ä—ºõ“?Ï8Ñ‹&7ØXÛj2À¢½½
Âz, [c¼‚áÙníÛ"«Ô„I-n”’>:D�KY-ÂÆd3_Nä2%vlBçXAŸë¹Gýý$rw�Ÿ`sñõ<ªýpëD~£¬<qŠ³iX0o‰&³¶Ö2)�6¥•cb™cËµ:<D05üÒvèÚÖ8ˆ÷ÖR0÷�Â� dD¦AÍCÌä^ÉÝ¯4/±ôþ&.Ïœé'›ãq‹7M·Ch0i×ÇÈe$ˆ]8ÃR´¤ö”š¸dÁªt^çÊdå- ‘®+—;F4)«šWq¢� Î{œ7v¥E	’‘Nÿ\ôÚCÓ(ØL½0ïl%¾€(9°€Î—"a*LíAÃÉH$�Ë£j—™µ�Wóz¥×�,…žSáa¿%æ¼ˆœ×-òÀ¦è1ÕqÛ/Øy´íÓòa'0½¤Ëö¼çV@[ä××®š2ýYöÉSžáiuü;H@ûEœ$ÉML+rÏ	~§oµ§¶°¾åßœš�çt«¡¤+=©¥žeFVÏ9›ÁÎ÷…†o*NvŽó‡éÑ-&Ç÷T3è�]„«•ÎFAàui|¥‚�^XÅNjÀc~dÌ-c®rÍ¶@L5º�_t ƒ=§Ä]ŒÆ;.õÆG£w„ÃÚc×â*Ÿ”ËÉ²±CÎÇ)‹¯[ß)ù–a‚ÕOäb'9™zö†&ðp��Ô¾KV{oQÇº½à5_›Äã\pËs–ÍO�ÚwÎiûYWAtåÕë>°ëÀ.¹J€Ò/s|®ï�Øôï ·ñ¯ÁYË´.l÷glf?Ë:ËÒâ–L6Ñ'•ŠJ%I;Z©ÆÓ*)£ÁÆØ8]^§žžM°n‡9^3ß»ã¾�ûU©�ìù1n
A7!$6ñI°sh’æ¡t4Ëì„u™?²è;·Œ^SÏÆÁë> «Ä%$üæÜ¿xÚÀ¿‘ó,»’Ís«ŸUyÉ¥=‹òðÈ‰|"À¬�J|8@LD-e�÷�N�ÖVÓ]$î†ì³šœùcÚšL<ŸÆ»¶Ø¯Ôn�ä™±åcžLñ­£:v¯×F½†ž&èVØÚŸYfŸ7@~hò9Àr*Ó³Å
Þ;Íµ«¹zùÂ½…Çùqã4Ÿ ^5YÍ®fãýœ%ºt—«t
·3Lºj§·v�Ö—­—`‘B×ó1´@©“ú!ÔÊ“.]¦Ún?d :uÌ¬‰S€$ßÓ¾â¢_t|ŠÆåÝ;TN_¹GDŸC¥p\P0;Ñ¶iÛ$í›#+ÓÆI×ëGäàè^³`Luè1V+Õ37çÅb»¶³PæÖ+\Þ!ÖÚ^,ôÝ’TÅèÑlxï~¼ã–¤Ïxò
™'Ë› ¨XdG}Ã ;'´ ©vç;X_ÉˆfÅ¶ƒ°Å`VNL–›™±†ä–Rù†6^9©•»ƒÑÔÎ'ìQ÷5:§¥ñ~bÁ CòÌ°ÓêúmTžXü`YÐÀ÷®Å3«&Ý˜]m¦}Bðä:WQThöƒA X‹l[”±:AÇ¦“ÁÑÆmÓ²×µ‚	Úò;BcÉ¥L˜ãð5"Oô»irßó*ßØÛ-°>‚¢–£ƒ½ÇG¶´Vp%¦Zè.â&qRmÛÅ–¯|Þƒè%Ç'õaVîÆÚH^²J‡v†`áž)qÖÕäœÀitû%.xrÅä–|Àã™#Ü’Í¡ÐÐœ3š*Z­uK˜ËT³h�¾³YÅ`¯§ôÊ~#dFNiº½q’ýÀJÄÖx‡E13(ë¶Ç‰æ�Y¿ŸòÙë)¿/\×*ÏSþY¸º>ãÉuÐã´š)…LÚNB£ùÑE0Õ�c(×¸èÁcÜIklZº2»vËñ>Òe¶µ|†ñá`g¦nˆ¢Ûq\u‚ÂÎ°£26ü$d¦TÜ»¯™rƒùWî:¾�Ê3_V¬%ˆm3hK®o+KŸ±Sc2ã�™J)#¯²•éHÁ;eÔ°ÝÈ6„A*�ÔNöE|!ù*šøÃ‘áÇ<XFX;0¦ùv¶Ÿ¼\Ná·�éã„kþ‰ç½Û³Ž†íht®0i=Y»k¶È–ûMäû	ÈÌI÷Ø:²Ó|kä4£cQÜÏç6¦†·ã
Ž¦„Þ4wW1ó—Š°­ä—¶Ð;
Ï‚õ†ÄçZëOB5¶cdŽg˜ÉE�Ù­54×ñ–³qf DÁú8—3«‘5�ÆªãFQIÄÜ±˜qÜf0§de·¶’Ã:V¢GLé®?šåà))ì¶š\àLlðŠ§öïrãü�û5éH¡pôÄ’¤|dÇÂ~�X�¿^@Ó�t$4öÈÀÛæš¦Kib•ô^<¬e¨ í`”IÈf2­z(`…o¸°oÃÎ7‹×3ý‚E”¦‰÷š!×®ïâ’¤‰û°¾û0‹n’E`ÁG”esCgûƒ8\IG}u„J”QÒvéô£¦¶GBd´Öá t¡l†Ø8]S5Š¤¤7Lì�Åëi~Á%²’º'§ëÝJòØ÷Octj!¡Jß‘ìLbÄÒHÐ€[Çè8ñ…d!mJûgCÈÏÌ½½;Å`c7�3@Z“�3Ùr¹jGc.i-„˜)Øeõ¯&ÊÛñ‚Ê¹ã»x|´ÉØ•àH5Z)Àm�O•®ôÃt7a›ÉH�™a·¯Y8nx§w¦>ÔSb�{©eÊ jJïµv'ô1S–z¤Ðåò+ÇÍìãk»v}™"¶+F‚:ô�{"ÛP�…î±ï¼2”ú\/Ø)k°br@ñí’™O‹mOXjâYÄ3²3Ï%ž ‘xlg¼lì¼™Z8ùŠ)%F"í+ù:w|�ËWîç=È�OlÙöV‹£S¼fÃ‡Ð«û±ƒØ\aÞvcŒçdmM]n¢�À×ützô&²º
¢C”ò›dÒ‹å0÷{%œÛAÅ÷üWsåÃ‚x‘-Ðü]$žÃ:ßI+±€{v�á�g“FC“‹•ƒ|˜“˜XézPQ&2ÖD�Ü!º4nxµyk>Áá9>ÜÖ¼Ä'L^o€¥²wáS.èM'nõ‰�rßó"g)zßa�ñi)i�Æ^åCÖW¥Ñœ¤Æì·R3á|pDlI
¢Û�ÁÑ+S6ÂžÏ6¡cRêÁB™´ý¡P(a >uíºÏ•˜V»	{xÍŒ+¼¸ñÞþM,ž¹!:x¶Šz¥Ã¶ëÚC¹æ|Ô¤uZ=Ê†Ã�O=ì™N�#=ÆµÖoö‰6÷ ½2Ã	IÇˆ×.drÒoÂW2:“Œðµi’‚“¦Ÿ°ã¡ë3L®ƒ¾4÷3’XB9Iá,\ò$ŽÌ(uMš»XiÑŽ-�èa7¡"[QI<ˆ~£LÂ¾ˆ´~lRýVð w‘‡I±ž†ËIg¹*þZiÝ`þÀ“»Žï£òÌi×ëpÇ–ÀiÙ£b1fæ˜¦Æ…^„îx×©uÉE
ÖM¾²÷±Ë%yŠ•š­(�Ë8Ô{Ö8ÚRN{[ÁÓåk�u‚¬þqšto‘Á»uñC÷gHýv¾1²Ý”ùZS‹CbkK�vÇÑxa%ÍÐ	Øé‹bwÅr?ÚÅª]¦ÇÌV'ŽÏ±EbÝÈàÓÍ@n%ýØÔê1$‡Ò^Ï—²nÏŸXóÖüçwöÌ¬a5šéê·\÷œÂVj3ÝhP"Ã–"æ…Ì‚Sg0˜–Û–ÒÉEkÕa%ÇÞv™)£à“Ù¢IAd§yÍ–;oþãBr×ñ¿÷óR,uA©ÄQ¢9Z;ªÐ9Øz:J•ù.N¢C>Í*·€nÉùØkñ�H%GöËåf|Ì]Áµš…áìåN�Æ“ò5/.ð~4ƒßÛ¿‹Æ3˜ó$Ž')mÑM»„…2	Ú`Ã°t¶dõHÒ<ÕÙ­¨¬†¨±™�#®žâbãë�9 Y“øRÀ¥aµ-èx0Ú/ü`hu¯µ×ÚõÖü]ž%ÊŒæ4RÓ»l5+4¡iæ`{‘;1”„.¦”¿šðbæRÙH‚å™—æåVûIo ­„ Òºé ™~ˆ”Z²ÔižìhÃ\%�ëz�ßq­út¦Ìíû–?®‡ú?¸¿3h¹SÝ®dÖ¹ACqˆŽrÒ„¶pÞûC{ l£€0Äª·ën_°îŠÕº}l#_ÌU7/D°s“¤jx>ø•ýñ%dï%Þa»¶=B÷ò<ù=2 ½oøÜÐ¼~ê'ˆ(\ë)\Æü9t6á¢ä`D 6‚gH®CŽÇ%1Âì1Ûµl3O*áö¹Kªâå£/ïäaÎ"³Ú_«"\ zÊ�h±‚N‹¢ÂëÞ
P´ ZØ×jQàKt_WÞ
ž(ÄšgfhAV�‘¾#+Øæv^?Ç¦¢U/#Òw´E$³Ÿ¬ +]>bÿ�È7Ê\ˆý’�æ>h?W–-Š…OÍncnÆ.Uxôñ0‡�Ðczç.iênƒ%=4£EØªÂL)Gx·îkÝr§�zÖe,�ˆB¥•§³¶WÄžøðŽÕ}Çÿåç;âá5dZ=Ì0j×ÎÔL4¶‘€©{s·> 6îP[©öwÛ=•y#³“yd[¤Å—2ÇdÙ<¶ò­©SP7á°�ì³¢\<¥º]©øÍ;ï;þi*>
ý&j=•MÒHëÜ`‡ªÂL(,
8ÖcþpÀïf³æ®Ž”WD5ÂB^Ö¢0,Ž8Ò°è ãó±Cõþˆ‹Ñ.¬WÏÇú ›»:Å"ÎÉ­'¡}©LþéäÄ<¶Ã¤q™®QÈåUTŸŒ#œ‰åœ
A^8x¿›Q¾17óHÜ,ëR˜n`-œ0TÅ2¶böc"&Ã½=E£Ä[#l$�Úò†Ê‹Sg×c“wø~2è�Ó¹Êsàöw$¸K€½QíFÌ<us2Ø,aE‡9bîzXT€Ãø–°Þ›×!h\	#š-‰È?ôE„CÓô"‡†¼;=$½9±ç	U¶û9Jfž…+:Dë�õ•:/Éßéò±û�SÆó×§¨n„¿9x—£ž"tCÛðH“V‡!Þ‘#i�0+ke¶ÇŠ*ƒÅB�Çh�™–*ó`e—JzÀwøTØ‡¡î”BÑ�7ÓvIQR‹Nçf–Cäa¸Ÿ­^RäUÉ‹w‚|èjñœ¼ÿR	Éw+sè-gët¬JÈ ‰_ë+pÚ«X¢©6@ÄÒÍ&}ž£Ë‰nÃSnŽ`àlÅØ¹´Ž*6ÐvÉ ¿k’9«f§æðSæÊÆ§”÷wRÜwœ¥âëS ÿR±ô¸}äùRª´ôœ4Û€Öý±°Û[4¹¯âF'Àð³óQ"ššÖuâa©4×7n¿u™elð;?ŽÔ£8E‰C™3ñh–_Á¾ÍáwôÏ-€û dÒ—:òëäÿC£è±ÞöBf’V�ªä9j’ÙÌmÈU{Ål›{f�)MCé�Q2e.���¼À4²:ôøR–Û’ž8¨ÔàOrý"#å„Æs3H¿¹&©|&Éçl�“÷wP&ê’UQ¹mõÓwOÎ» ðHhy\Ã`!ÂŒ[È/œÍ›š,ýáX9MÚ²`_hk3Î¥‰“ÈTT¨ó^ÕJžMð¼I•Ëš‰Æuät)4)mÉ¨îÊôÛúq»•°‚ïxôØÖãsQ¬6{ÈºG™·2Éæ#šqÓB`³f;Ü(ä¬<
áí�p¦Ù\¦Ë�@Ž¤¾Ü"²h˜tÙ€ðmñ:p	\ž@Î �í“÷ùÔ­\ ûûó tÕµúËg¯'°ƒŸ7Ä~üûøüÆ#‰Elï‰í�J¦ídYãÃ°ÞíÖ*·Û/Ëxévzïln3ÃFïÒ1Œøèd é½´*¶ëù¶&ØÑtS¢C‹dVWù‰Ï5·*P/³@–`÷.wÍ@„°÷œ“»Ü bÎ”+àú£#”ŠNÚ*#¸ÈºGW¯ï#†�e›Ä}�­Ûé�êãm¬…-JÍËŠé’¼uÙKžÚòÛÕöŒçÂ]A8ÿùó)]äLÉÏA¹Êõ…#ßK‘V‚n&£F¦d ZqÕW1dA^L>gÂ€®­ÝA.Åj»sßã½€èÝlfB$ËíÁà„ï²e…@ÕEŒêq¿†û¿¸Zß9ò¿ûq¯Qü+ÿè¾õR0%R#SÝdÄ±óuÞƒ¼²b1» %B´WžQÇ�3Ù{åD—åeš¶¼Úè;$Ä¹ŸMÊ¾•\k>.»G‹íK,ß|	¿õMŸ=ÚŽlw|’SÙ"Z°6Y"T×Ê°Új²KÚ³|!óy¼@Ùr¬ÅáAØmg‚ŽØ¡Þ(-¥ñ‰ÌÔ”c­6£Òfëý˜ÄTï;øÝbæ¿eãÃÀ×X^‡ é½e:}dä_p {fŽdÏkà˜e{\MÃõhò‰sG¢doñ‘!Gni2îìÒÖ�hø0­rœ°‡3¬	G¨:N$u90²šíwÄõÿo8y7ì»H>ór-±L‹—«¬_4Ø&P\‹E$ÂoösÏ›:�ëqœÙ¦c¬ÏS}¤·!kTà°bB¼cÚŽAä€ÑÞòH®²ï¨›K¸ÐüNA~˜Žoƒ^£wî~äàC ØÛïWX«ôd©Žðv>áh%©
QS…4,¹Ûô•?€ÙQËJ€HD�Ø2’ØFjt°ån£$‰6ã!±Øz'™öÈò;ü{ƒûu|=ï†}Á§PÑÒºë!ÜasHö‡C§l�—ç€Ã2ÃGqMúÉ<´4•Ùà£e¼Æµ`A`n«¨‹¹"¬{ÛFù…‰wk1‡¤œ;¬¿�âCôþ+$o±ü—ËÆ3?„Yë)g‡d0öSª7”`«*�â@Nú¦µ¼«GÄl(iåé0üÀÙÓ¡sÂqÇCYwŒFûÝTJô·G>GÙúûX>Æõ¿Bó6ò{ìü€§:Ž””L‡Ø_º¹…ˆNÈX¼‘6<å•R×cC›�íà‰åÁdØÇH4Ø<µ÷jÞÍ·ˆ Äýì`•MÚÝa~ûÍz‘ÄK¬þ‹åñnØ÷0üýgdµÅ-¬u‘´‘kvm<œ-�5¢‹pÍ³ªÉ?NnEñèžR6þl8GŽTÒc+ "NÓõ¦•–XuØ`F·’e|Å‡\€¯8yËøç¶Ù# ÆÁÈ‡BQ¡Ùª]7gÔd.JÖQšk(ö¹ÐÑ¾ÌLúcZþb¹Žb»¤íf•9ŒihÜ¤èûÈ<¥�^KÜ÷ö.Kà+/9ßÂðcÆ 6 Ku~Wm@ÞrsèÚ³—ã²¡c’ÝHó\»°³š»…ÕìÜ-0äÃ(H—±v8¦Æ¨bgnéª§
à—d¿¿rüfi¼H*ô=9}^§[g?°Þx˜7ó¤¥�94ÃtŒ”Ç�oa#—£jTÆ)5zoµ´YõgXJ5.ŒÁ�–s™1@iÇ’ß#{Ú`Gü)½Ïøje¼÷ÅßFM¡,�¶³¡4C†¦’Éølr…™�ÝzÉhFÞál 1BVF~è°ƒ<r)úÙ#W�«TîÞ7D™Ã‘%—
¡'nôàøÞe ¼”Ð'üž98Þò+dÊI0æä(²“õL�2Þ‚3Þ–íÚv5,6`O^$ÍÉŠ�Ñ�zgìW´J™,Ñ‚-:ðÝ……²ÁnjhÏ¾e„ß¢ô¿5R¾æáuÈ£‰ódPÊjÑÑ6©·1Z“Pè±F*z¿éí.nü�·Ð–z§qûPeXy¥‡àþ�Žå¥fÊ�’æ èÎÙGjFj4b“$ì¿£jnðÿ†�wÃ¾‹ä3/;cTBK„‰„©ÈjÚïJê`*uÕDQ‘™ `�¤´Ë²Ÿ£s?š;;â Rßoµ�Œ1êÆ£ˆ1S´5jÈTÕ§„ð×jò-¬ÿÕT|ô½ßEþqÎ°¦¸C™!§,bP£É8ž0á\�6RI…´6Î�wój±Õº[•9H‘&t5%8Bî{�± ÞaÇÙFÕ évÐGÏÜkä.æßŠéÝ°×ž|)¢l¡zGÈåÆ-Å>Î¡©‚Ì¬‘5\>ÀÔÑ¦ÚÌv¶…9Ù¢ÚÎfé^9$1èHÙ�ô•¹Ëœ§ç‡šZª¦nóßÑ· úWü{ô=ôž…™,1.¤8.D-Csõ¸Ç´µÏflÉÔþ>x³f„{u—[í<ŸšB7°>É á¼h¾î—´z:Ç	óJ˜bÓ•DøgZàLÕOp°¨*Òèr îÏÏ� u:÷…wÝ=žªÊWfq
À•~p)¬òO>RòÇÑr\™ön›¨Øq±\y�u€*™áx†"„ ê�6kG˜�Ý
qÍÃpÕl¸!ZÊd“pL°,§”Ì§è��ãR\Õ<æÇ›e—Øg¦œÿ:ÐÞƒGwÞ0¿¢×LÔv-Jž)a­á!4"nm¾¯—D†à=åK£î–�‰†éVXÕ©WMÜY{Ñ;ÎÉA
T\%ChŠ¬‚PkÔ[Õç?\þ§ÁPà«¾Qò˜ªW=Ývà\Ìf&ÏEÓvÆ•øˆëy_ž¦Í{”«L‡Y>ÁÄ ¤D!Ñúu·JlIÂ=]×\DÍW‡•�KP=ÚNÙN[õ~
¥›¸©áL�ûà2<×ïÿ<~r?úg�”æþýLåY4®ÀçÂà°åGæÓ³’[z‡jƒ®iß1<«›³UC2\‹KwçT“ŽãýÚÛ4pN£�Wè­–ÖëzZL-hXnu–î)m¢¦Õ¨ïŠôÁ®{& ÿöWðÏžö7©¾ãÅj*‰Ÿ.�ù ;(óTãËÈIÀÝ&¢éc&úº;Y,§å,žÚd˜@Úl¦ô�áCa¹h#&¡ÚHKY#å°´Ô+^\iõÝé@u7×î¡õn»˜4A`Ò2Ëxd»0Heª7Õ–©átQgUÑÕ<1\«¡ÁÌê˜"xÎŠc17 ênd ¾ªÆ®¹DA<(º‹ôâ8¹{>Óê-²tÕ2€Ši4Ë4Ú¯ŽDH0�gCV!ÇZ˜å…‡iOLÁò£Ï—r#my0è¦›d:>4º¿Â¸¸õ:…Mæ�õÄ¬:ys½Ÿ(u}�¢=ÃÛTz?¯M7¯ÿ�ÿý.�ÀCöÓ4æ*\Ez¶9¤Ç6mÈqÁ§j#¤Q±uh;L�ä†´¡&›éúèÕx™nõñÒ¨@²ÖšOH\šÈ;V9„ð@Ð[¤æOp
8mÂs ÍWŸ‡�´1Èú¹œt8^^¹t•ÀÍŸ\îªô<!ÜÚU¬¥ôlnåxÈjC|'Š?%4…š¥N-„¿bÌCcaÂn0^’4ŒÌnÔ™…KknOO¨í�i†\Ù*íÂ+j7˜�²½ÂÐü×WL¹|àv¿Ëß_�µÞ›%ÃÀ’4`óÎe�<l@1vÛm"Ÿ›îäYBÚ¨î.›ãÄÒIœÏÁ¼=ßY ä˜7N³°@æ{0™: ˆPäcÊå7N0�ôûJw'ç(8xÏ,f1b¶ýt(M©^Pê\Ø¸ª¹bkØïZA™8ùj+3È!’ÑLKW³VÔ×âz.Ç�˜!¨pðyEá ®0vóÅP}Ð	O¾Auáø®/…ý¥/é:±:§9ÂÙ7ó8ÎÝE2"0£$í}1±Í.ÐJé²Mß¥Ý€Å€¿öH†Ä0<d~ïæ…×üY_¹‰šÆÁx³zÀá=[ïü×9oãK�?_Ër‚ö£,«lÓl’fyõ½	ÊvVƒÁŒ��ºÕœžR›)j‚ñW:¨ã�þÎŒu,”‡à¢�mÑ«HWœx<É‚ZQE–�?Èòí\ýåŠš?ßîÏ 1òz]Íç+â·÷ìŸîº�Í¿Œ>•80~Â¿é®‰ÿ`M>½
ŽMŸù\•çh.¨368Iäù<Å[%£Û/_/×í¿ìCù¤FòÎŽ—ëÂayˆY/¥0IP�Ú–k© r�Go=M[TïÌz!ºo:KRv™½¦òÜÝvv®Vv�feß€hŠõ°ØÞÀ¸¤¾<^.UºÑ/sÝâEE«·ºò—Ëd
§…­*óDŒ¿ýõ,:ØQZ;û\ÞŸ®�ù�oës&Öàþ$¨Fûg™GpæEAu¶NþöW@ÐË|þ\¦sêU€©zùþ™Qgà±÷gc–göô>àåeHâ¦É	6Ë,]Üættt-{ÿ0.Í~^ÒÝ“®†Á#øÝ7€.7¶œ6®§è80q‹6³S¥‡S}Š«qøÈÄ>ŠÒ6#àåºþæf�p!Ë5îŸÿ}ZYþ¨Ç:Sì	Äkï9„l¹ULÁXØé»÷½—;¬î[ÀÀ_@¼ÞÆÕYY™¥s"87ÿí¯`þ‚’g ]lpáîÆ«}Î—p [€Î¯ ½cÄù§>Âô·¿ž¿÷¶â¿ý¾ÛféM�M}e’w> ÄâLæ0¹¤Dœ ìJ7:ßp~Œ;€ÕéŒãmðí¿ýõ\Xäø3Áß‰ °=!zG¾¿ý¿ÌÐ[í6Hû‰2 <ÿ™„Ÿ˜õ©hŸ:_Éô©ýQ�?´\„÷Ôü™Àžú>ÒKç³tžZ_Êä¹ø³cóÆÐSËK!<w¼’¿÷Àƒ÷‡+ÁO�wjy²SÛ“d�¦'¡:µÜìÇÓÝx<	øç,5§/þºÊ
øëà›t€–w(oòðîÏ¾… ³K3W‹í&I	”@ÞŽë6î¸C6ß ž+ÇÉÑ´ÀR’æùM¨µ ‰d®¦â¨5(e2Š¡¼+5»x"(Ðô1³è½–ÍŸ·º6ÀZx½‡ „bAŽÁñ¹—x‡
zƒåÇÄz6IÓA‹t-c C}˜•´ùE(Èp°�Êx’uš=KáÆÇ§—ó‰Do†/c0$?˜¼ï‹€æR'æÅ!ƒÏ+ÞNÁÝgŒü8¯Š—Ún+ê[5ˆK”÷wCÎ±Ò/]<�/‡Ü'nœÆ+p®c¾øÊ- ~sŠ½üÌmÐß¹D¾@ûmÀçH_†\O·¼ä2â=ûÅ [\ò3Ò\>u‹ÿ}ñ¡÷øÙƒná§/†|A¼ûÐÆgð¾>qs;ÉÊÛ / 9y>Äö¯77ðgÝ9R¿òÏßœ•÷Ÿx4<ß§7¨¦™À·ÇÇ]¨e)“í¶¨v TpÌ³`%Ò`/tÌ‡U…Íà‚B�jSú¶wô¾*EÊ¬fæ.µüpB�áI¡¸Uj"iXnSÁHÙaÑÍö¯Ü�@ß<;!ÏîWÞ¶ pŸÁ5(ï¹& …£Ï=aÃ¬¤á`ò¶V¸ìl€25½œ¯s¹ž<…ðÒ îÁjË9‚ƒŠJ*±Ÿ¶¥3ÔÚ<êÁ,.¸÷Z'ë
Ý­åÙÛ~înŸG%(ÈÄšÊä œJÝOŽdæVi0UJu —äÑÜ[C�îêXâòØb†‚
$L´1¾'±£ëz.­„âX²"%qö`´ï]Û?Ù×þ8ÿöh ðiÝÿÝŸ‰‘•¿^œ»æ ý�BÉ�“–©¨—CÎ†%Ï[~àÃŒ²ã@zìñPîiBA¬á�–:th½8É�‰³„ÖAÄŒFÈhþIòëê °\þþÔ'ÇªI<Á=¥›gàxÙÒÑv+-#kSÑŽB
éÖ––U9!‹e*Ç}I‚L|àî´mÚAà€¾BŒ¹·hb~Pý!åÃ1§-ÔGhÊ³ü\¬¶ýõ¬¤ ×�:ùqIE¼æéÝ¯óx›2{Ú‹£½I­Ó‹7JfÍªÆ‡ÕÑhK¸m‚Õ@‰ìxœ [®ðfÐÝÆ;¦¬@VMB`!2@Æ"�7}äï÷�G_Ožõ»`så½á”Œàw@?^a
.ŸM££û3½S=µ³ÝþÒIÛ­,hÌ=£N{ï¸�}¢$�\©é4çÒ‹�JÎÖÔXL¸*.}%‘!—G¹'NåáñåQêˆ0Û-ÆºØ.ˆD‹Äš‡�÷íâ×?œÿúãzýëm«ø¶Í½�;éšÓäQÍà^‰w•¹8L
—¬/ŽÎz½…½åë�.�ÆZû"Ð¡IîÐûXGQz¹ÚHÁd»á,Ã·ShŽ†Ü~%¯CWñiæ(=À$>ØÃU‘¸=œ�«WS75MLco#î‰>®À­[—çŽ¹,TI>qê~Åe}¶ÜõT
’¿Ð4™šDö¬–ò3WÊ¡5‡ÎÖÝñËÜSr_…2 wøVÅ-ñ÷íéÑ+5çÏÒ`¬"K(,+Ž�Tn³Ñ„d)f�…â ž®ïRh„‘õ±’ÉdÃ§šaµ“+öÚ�[k†ê¢$�æòôT+ñ˜E|+šøç¥|â§†§‘“	7¢Í.6¨*upDl::–³F½ÕzÜoÚÙˆZ�ñ©×äX‰\¯$ƒÑÂ;öâ „Y$…F�Nî“ãrÙVùá±Î;¸‘ìztñOpõùo ˆî5'þ8{ÍîŠa‚ìùkñËkŠø]ZõiUæZ‘ï¦FO•à¯.†;gÖeƒb$€a§R“'¾g@°¿\]wÒ*ç!¯>ÿ1[Í¶Ôð�(#E·âhÂïAirÚ-ÐradH«)kOhþ(úSx•`Á†h’‚2Aúfãtâ-Ñ.Pt³²:n£=üXí ..•&Á¿@_±yZª¾/C³Ø±xÜ²³8ª¿Êrì�ñÄ’l@²Î4ž˜ž_CŒC—¥ƒÎÌp1šØˆ¯/âiÇæ(¨¾ªzû�¶0à•³…"éÃl»^Ñ—€owNä»Å‘˜E}m­Ä‹Í&¼Ði*r§à VŽ§­Çu â!ÙmAõ ±<¦ºÉÍs¸Ä[ƒ'`ÃPˆª© Œµ\q‰&Ã•QÍ°�ºzÌ*y»øûOpùéð?®÷ÐMo#Ï†Ðåé£ŽÂ]ÃÍ¦ƒ,@C|6A]¶	»—Á…Zydôš58¹jA€dÕ5ë¸òrËƒ•¼²Jr>öœ	’èaŠp&‰i<.çç£Pçbç5ýýñ÷ÒßøfÕx?ëâ°»EkOÛÜ—Î:pázêÜÂÀ1æÖy	üèôxù°IËê~ÃÀAµÙ�ÖºÉ%ëf"„g¿5m¼Ù·Z1›lS•kÞ6îÎÒÙt�äp**¹TÂÛ~­-Ò^¬Æ"ª4ý¬‚óÕñþ¸= ‘¾Û¿ŠI\��ÓGµNÏàŒ!ªÐÔÚÔ^Â²à Ð;Ø3;â•«æÜa9õ™-ƒCÐ
ª¶Ÿ!-Sˆ)-’¼µ¸‰µ^Û‡ï¯<kð Fßï”ÿóÇõo°(þæXÊÙ\¹”gåtâàyeÊÎTHëKé”âô¬TF‹)3jýåh¸ð²á\�.¢ï¸ltÔ@zö>w•¯-{"F4w¨ Ø6]	&é0¨¤Z “
%¼µQUÅ„Hn+˜ÉN'N\`8au}úãÍ«ÅŠjWÒ ID*ÇNC…®Æ]ÞÊ+ÉËöÖ)ÄÑ=ŒïçtÜÌhŒ†û…Í®LëbnPU2e|S‚ŠÜÞ
@ÖÃ¡0C»y,1yöðP¼?Nåe¿^2ïÒJ€WÜÅpÕÞgÝtU�`H–¦—¹Ôù•[§Ö"­R°²œÇŸüµ—Ÿ=ç€ä©ypÓ²™—™—«SORúäEüè/?�õæ¿~ÿôùó<|=kÁ+àBóàxuˆƒÑ€üŸÌÛ÷±7r|©í(YËÐH&F«$Üz`F¦+P}9(	wxŒåC¥ÉY›(ÅšfåÉ|ÎÂ¯[P±±Å2"là6a¸ƒp4Õ‡‰ûŒ×‰Kw˜žƒ½o+ùýŠ•�®k}s�Z1?ö›a®À	ƒ3¦– ©þãè­™×Î|èJå8šmçx’î–Ž¿¬ƒvžñ^ªŽË|€fŽÕ?ÖÒ8ÁñB. >wœV®×ËçXÈ�••Í£Áš$öjnÖ2ã‰‡Ä{M'A’„ÂP‰™%S“JG4ïÇnWÄóL.F)Ò±C€üE²Â#RåBpPéÞ8€I5Bq†íô÷Yó½škX%QaöX3f²Ž,4Ç‡FA´%ë¯ìJoØ¨ÐƒjrMÕñ …•gd•[oŒøÃ]+n¤n˜©à½Ÿ/Of h®“ãÍéïSlø:N¶Åg«Ì›þƒÎò&�
5È@gµÊÀKOæ= cÓ�@/SiÂ~2ai2e	2ãV¡¿tùÚ=è|ˆÁvÂtFšÇÕ¦ñÜÕ¸B±�À=w;Aý>y/�_Ÿ-ï¡¿“Á¾wŽÆ^#Žc² Ž#ÙžÑÜž‹:nŽE‡Rb»K‡bî­Ón´![ÂH&ð(€›¦°|ªq=/DáD¶ësÅž©"Z±ïµ`Ý4Ç¦ó�µ;Q{µ¬½KÄùPèYn®kP4©0ÏÆè¿ƒ¯\Ô5bóÓê@Hó¤]þýmé|{D£ÎšäÜuYln¯‘Q1óMï}``¾¢ÀùÒi�Ë¡L—[‚lPë‰w–¢qz<=,¹#¶ªTwIë&ò‘ñ”Îæ<•¾M”¶GmÇùq>�8ƒhcŽ?N‚«:=Ñéôçy
|ywz™�àÜ9tºÓéÌ3gCða¹ó57õKt²®QPÝb¹˜%;‰÷õa¸™HëU+T'Jæ
ÔB ×zLÀ¾Ë’¦qœÐËZuä;ãÚa4eT?.”ï�2 õõLp¤ý¶w¹“6–Árg‚´hO¶ÞŒ'lGeˆ¶hÙ`CËÓÔÅâ*é&øÔEŒ¢qÅ…Ü!M'óB•í·•ÊìâåR·ŽrU‰‰Àˆ‰ùà¹�ð&"ïP]š€äÝ­žw°éÙ®Xçkœ"HwÙm6NSè¡]›.ƒ5l1„DÅŒÙä8t*ò§Ë£Äì5l5µ¹=Ú×ÊV†Ëœ=®�bà�	ÕØÕ�ü½ÉÚMDï`;7}ªòÀr¹‘”;e
r=ç;M
wÕZ¥Ôˆ³EƒÔXOåqÁ�1]ƒý–Ã�G´æòZñðfWŽ;‰Ú;Gr„òk(´•µÝ£…þXÎö�n×)rÚ©åÙ9¢ù´³*k&>ˆúÒ�I¬wC|èS,$SpEŸ²#Z-˜µ‰T¯#±k’´� ¨-Žîb¨_k‡Ž�ENÌI÷ò(<^-ðÙÝ½ƒîÖ
 ü�FiAÞÇùÖ0U>Ú”äÎ7	.Ghz¬�“…mÖ‡µÅ|h'-HHÑN‡úÔ+½±2î7	éa«DU†›c$w¬N˜ «ºV·ÂÝÃ|à2ˆËvúæyj‡øúª�›#çl>�DùÞ=oÐÿ_Ç{·¸šV@çŸ¦àé~ƒŸÀ£V¸öUû�gÏ�5\&]l1ß8åTåzn:d[ÚÈSœ�àÖM5Þ*Ãw“-‚w&9[7$e¼•©æÀæ:wØ	¥�–¤f?n‘_øðþüñÜx´WÆRéjƒÕa@¬&ºL¼=(dwÐvòÈÝ·üªw6«É	
,Gt¬'ûá›81Ü†¼¯4¢/,Dm<‘!š%Þ2ÊûÕqÊ
ýyI<Û!wÏÀãŒ£
­È0-EF‚íÄ±`±GÏ\´Km^Œ,V×ô¥™ste™¥¦#Úº&Ó†Î8ã…²€X†°É6èµRÓFœÕÛcL?Þay†ÂNã¤�¼ƒum Ôi7¸^-£×4È[âìÉtš&�ÎH‹lvä˜à8:…9K(Ôl2·anHIO7ünùéqWn‚4;,-dÂb b¶­B>Y1{Pá™Ø,Qg¨Ï�‘—jÚƒìŸ±xáMúóÇË ¤Ávð†×�’¶°"ËVBNÚ(¥NO¦2¸‡`7ühïÈ%‚èÂ…@‚ÝtÃ˜Œt<,m¢Z7õ¬ã³Âc½•½IuNŠõ˜ÃÐG��£Ê=(éTêoÛ¿wV>µ�—�Wù¤q«#à_Ño±Öìa�ìœ¹Hð eµ,w.µO(óXÎ¡¾œ³;T’“RÃ�zXcõËæ&Eæj‚2ƒá}+727Ö“2ß¯¹ÈÂÛ# N}½EPí>²>xfVÇ°žƒ •×¯FÛÐÆ·ÆÜ]8C¡k¬‘#–Ý(¸ÓÃ>¢DÂMæù¡GwV>ˆð¸W-)t«ÙÈÀfža³áèÁx‡éîšÀØ7ØnÍ ÆóÍ'×pÍâº¿dñž¤õºX¿í¬ÝNØ>Kq=áÖ˜ˆÙ}É?š`ŒÇ{§`Ab˜£ÑéŠeTpéÁÖ¨4µAÝ3ÎÃ˜ ¸Š¨Ó5¨RHNw©‰á)–/½Pî+E¾Y[ »³áuŽ?½’Sà*±çòT5]„3B�1–ÛœD€{#taêÄ`;6ñzËèG #ìRo(NrÎ™\T_m²èÀ"›mF3~A;”¼å†«Í#ÕÁ1„ðgRë¯jáýÐù”ùõbúloúö(«×BœüÄç”›A+}a�ÜŒp ‚SQµ…‘1J•+-4™ºjqÂdèâD¦ßÃÌ€˜ðÜj¾Y+¨9¨)÷ÖÉU|·R=,N@e=ì£>Æzîôî‰×ÿ~Ò½Ï¼öCÕ¯ÑÍa>sÆbÉ€\÷%Ül6­™fÓ’š¬QcJc
: ÐŸSl–U(aèÆ÷�·¬a®	)�ˆ‰|€ê:ƒÁ}Ð·ò/çyv{8Eª~Wºê6ö­–Ì�¿¿ÕŽy>›r–å“SãÌ¬[êÉ+,èÿ.Îí›Öï.Òó»oA‚7Õ¹ù² ÃŸV�Ó‡®Æ“N»~d‡\Ü+oa¬:§ yë”(õ+þÎêÓz°hB.2jD¦ÔMºðç0ÒQÃ*�–³R"Fã	ÅÁÊTÑ»êEbUG#iu¹<Æ˜ÆTa0(MV&}1ê‰Çmä]• ?Ü>µêZg›Þ›;Pz¡µ¥lGò�×šaÙ§øúˆ4Ö!ƒäÞ¶b}åj\<Û˜ûà gÖ—^�£>Sr’&Áf�Öƒ WÅ� 4°€?Î3î-Òz·^-°�TÖ¹NÑ
CèÂXXÓC;d§M”6Ø‘(ŠaLƒoV$ûÆ×´½næ”Ådë�V13CG¡iì#èžKòëxÅƒÕò„9�óVaPÂ5`î,Ov…‡.�#×¢9Jü]6_í41†c#áŒÄ¢¬C¼ÀMÍDÀ13¨9	h¸1'UÆkó~å¤ðJk£‹x«Èø‘:%�•yN�sü’t.urKt§ÖÖKqÔ$Yœ'ûX,e
N(Ÿ°ˆ)‡4ô48V\cMØX#0ú“ÀÝ$œ¢r•f€ I’Ÿ€ý<x¤Ï9Ð¥lOÚúÎŒ{µþÜrBNzÌ…SÚçƒ·ñ£Sól?Íà�8ÜÏo¼O K.4H6Ž½œ<«cbÏ»Ðo©~.ÑÔ—;a²¢#`³•x”Œ–:Q·Áx93ÇÆ8^ÔäÐ¶år�fŒºmÙÕÛêÆ0gUQ÷87êÇÅâìs9¹ðä”Èx~<;´ßL�"ðyrÛû7>+Žj¼žôP;—øã‡ª%Ï¹hßMBúÝè[úÙ[bÓw_øæ÷ßRÓN¬þÝ·ß_òØ¾7ü.cí{/Üg¯}ï�·L¶ï¿ËjûÞonßþMÒ?g¾ýîã�Yp¿}Kvû¶àÜ^ø&ô	p¿æ)î{Ã yV?§“ç8/HÀyÚø�g[³&‰[²o„ ÎGÞà\öb91^HÊÚÁø
/7y‚ùsOÜ³í`TªÂn³:v=­ŒQ�¬öx5Yiª¾^<˜QOåçþüqß BYw¶àÝ²yÀF¸fëâ¤1´•¿_‰¶(m¸ þ®ÇÙÔ:ˆ~ÐÎjja.JŒ&£!›Ü=xÆf>šXs{¶ÍVÓu¨¡¬;¶²GgÌ¯€ìÁQr6-Þ4ãlS…ÁsÔ"›µóŒ.Ä„¶Ñ,Í˜'› ˜4ImŠøÁÊÔhj¤Ósp½ð™{tÏ‡Æ¢\o÷ê˜ ¢¤¸š.Çm ü)\e?,Y�kNt=•vªŸ}ÉáùØÜE¯Ã‘àÞ¤ëÇ”JÔ§…J#>WÀL.Ž*UqØÝ�Géƒ3ƒ¦¦$ÆÛvÂuF-ˆã!éÖ¶2ó�“´•Ê}´—­qØšîÃ†þ!üþç�÷ÇsràÛÞãÕ{µœNìÕ}õ³“È‚$O†ñ£«¢4§ø(©³6$Ž·ÅpèÐOš­‰‚Ö!à6ó9¸ Gi<±u%ÂOzŸ=
¯Öì\Œ5®gI%Gž6¦µÌ‰àî¿Õ#õƒ¤¬Òä¼P‚Éxúûü!/6:XÝ^žü˜ŒLí[6Îó°gìNÎ»óq�Û‘ïÂ×lÆ·ì¨·‘gŠ¾§	�N`ÕpZko™ç¡oûå'Çâ¹óä•?¿òÒçrý)€êé«ÀWs·½y½×‹fïÌËë^gð•T§iR¾ÝuzªlûÌBq·˜{J¿Tžc6¶vÆ	™ Ù=³ÓmèƒH¹=´i÷h€"@ÑMa£yÞV$j†Ø`´¥³`#R÷ÿ•öeM‹2Ëº÷ûW¬Ø·ÄÙ‚ CÄ¹8LÊ,2ˆp®dRP@eˆXÿ}W¿¾ÚŽoûõºê*›æ1«²²²2ŸŒ<�ä;£óê[À=+ó÷ðZ}ùƒ¿þi¿ã¶¯Ÿû9L†•*aÀÓ_ë¢FØÀ�PG‰ÅÑ ‡4UŒÅä &À<$ÕÇÒÚ´ö‚ê÷'SØÙl°án�Ÿ¬!Ÿ« ëÂZÙn³{âêK¨ûÿÿ÷×ß ÐoÇì×ô½±eŠìÚHB†l{C¯rÊ6Òä©Î¶·WB}ŠD ö*4A«xÄ¦Me·SºãÈñ(c¿QêY:×!ÜŠubCeÑ�Î¿=ê‡~»×äë¯äâk©Åµuîy™ýà�~¿ö¹’âúŠWÅ—¢‚÷Bß;)0«—.¾×÷<z¯?Èüð–'/õÕk.B?¼çê`þZ†¯Þqxú,ò;«þóYâÆ/}ÿ?Ýú¢ï¥®þç{‘Ÿó½ÐÕÏ|/òÃ�wï¾zÅƒ‹÷JäÎ­{% :¾6øy©¤³ÈO8K<`¹wñn­àÔy]Z÷F'ˆÊ®ˆB»eÒ[Â%¼®…¾Ï3cžôKfÃbz•HÖ–‘Í¥îäùTðè],"{ÉçÎÝ¼°rnŽMigbíé0Û“Ì}§ÒkuÚè†¡ÌÎ+Ëï¯ØÕÿ%ÂßÅ”ç5þÿ»õMnPJù?c`Áþõ¿þ$<‹�š;PpC‰²BºfWÌ²	³�!â
PŒQ ‚\”Só¨…ÚH†{¿-çŸ™®{ðìe­¸�^”¼‚p8v7Éï//.!°Ñm�\A^³Â®á¶ó
�Z@Ï0HG'IÐ—$ó…ÑÔjgÛê€5™du¦,ù±	�vÉ!Hð¢îVê$sáÔNŒÁ‘Ukc[äž¦~d¶Ýç¥pç¬€¨ÆUß÷¦ô+v6òÏ-éMWì_u<—’×·–ö'‰‹Íy+s�ÐOÏfö�Èûw\ìçõD÷ÏEæý[®ôlbŸ>ã:þí£�}óŠ;ûúFæÞ¼¾º±®o$îŒë™ÛúFâýoöxRñÃ?ß§ÿãb{ÒÞEæý—ü’xUŸö8¯ÌøÓ×<Yñ7¿�ø�ûOý^k¥
‡•i/G+²z*kÐb³œ‘c6  ÑzÝI$:Ø~¾ ~uhDP’°`ôõ$�iŒã -Î·ÅšGKçýKûÏÞÚèê]Ú™�-ü+‚âW‡âÛ
}Õí|ÿ[ðè†`ú4­·ë	2þ?qŸ'Ž’4ó,[¢e2›€«ËTZmòage6K”ÅC¶d*Œî±-áï¶€”¹�æÔhh«‚;�ø  œœ˜wvóä�[úè×*›Ù¨_½9?Þô¥þo¸Ú€URlg\ÃkK8Re[[ã
2†ñÀp«ã6-Ó-B³’Á0&!TF5Wéæ˜KPÆfò”X½¦úø\.UPîNë—oþîy®¾ >×ë¯·Ü%g\¸�t‰&9|Ø{®´¥yeÐ	^ÍmÍ²#)Ø«Éâñ3V´J³Ü±_wy‡ã8èäÓ�wœng¹Õd)/ì»{‡��>ì27HoGÞA=ËÜÏà¬~gÎd®¤ÉÁïuKŸ÷ÇÞÊ Š
_·sKÛ…Óaènø Äú»�ErÃ$×Xþž8JëÍø«�žG{›\ãïN¹ÿë“N¿‘þ Ò'œ�=“M·W¹Ôv"h©Ï|ÖçŽ»yXÏyw2Hò*eÙÀ&ÒãžjD{d€í*S
æ¢4›PÙ�‘^U—n›,N½uŸÂü1ÊË~~Ù5nTz7ôN§ßB?*µ/¹…Ðc!:TzÝm·bëêê	Ñx¶�ŸÄÃŒõv<›“íØ™
«”o+&GÙHÝ:aÔÄãí(K!HãhÆdÝGŸþ1Ü'­^Àþ Ög¨�zm6Ž¡ëÇE¢FÆxíºš-oqj†˜gæU¤;^•1åäÙäcÝgº¸U‚ƒÖQ^l‰ Yþ
0©Š9)ˆxÆšíDý;“tõ²î7šëãwúü¸×æ�1M|a§:9¸½ÜÈ²„\@kdë¬Š‘qS[]`Á�éØA[ë¦—LvÓƒ+W‡†«AŒCKâÉ8@q!‚$jÀ„sú;ctÅò¸ÅÜü˜–hìúu0…l…0ôR<äÎÉmUUê]Ïeú6BA…1Z–:•åA(PõÚÏìC–ùäš¤“n®àH½eÓ-šò©àNOáßY¢3žoOéf�þ~þ)Ì¯wÜî-:5N[âb:üÀ<"6û€_
�@×«…OkÒ-| „#9Ã!ØÌ–JÆIíTÜ8÷»q	š”Û%ÍnÂx9ëë?AùÛ©ú=ô)Ö'6ü|å� "×ªæNÑ7ºK—ÐáÒ®X\ÁŒ52*‚¾$Eju,ÁeHB«œS9Òy*emX"‡ª<Š> ~®QF:Õë6ØtÎš»9œ<�½Œý5Úp¢Eë•RÜ~²ÌWK�èŽÇéùòåDàq¿\$.-u¾«h|Ž“s½-ü¼ñ5Ð¨d`öÔ³
´…g}ôÐçS·÷ŒörÊz‚ú5ð)Îó[n§°#€FÒf`hAFk¹²Ø•8÷+cZM8\×2èUŒ§åæ¨c˜ÖŽ£žÛûVèµ!5swÉy3j!6"HÞŸ*‚$¤�ŽNÿ“)üû°øõ{èS°OSØ1»ßN¢@œ¬PpÈíäèüä]zDÆPø…æÙi’5LVé#5ÕP0[«ããdË1{v¬Å¡`NÆP2�ÑJä?Áz9ô>!ýøçù-·J…È1ŠÌÔÍh¾ÙâÒIdxÌ)Zlã ÔÓŠ@�Ád$/¹i	xÚÐ’2�­c0¨sœ$œ>­ä!¢”xªV¢ðÐ‡ñŸÍÞ'‡á<©p¾nvÒGgÁvË¤Œ²ÃV
´zÊ×»€@ÓÁPé¦M�Ê½,Ö­‰ÛË©7s*šnvCÚ™e^{vÕ9ðÂÁ†NÖ!ÐÜí!ÇÐ£~Ÿxù±Wt[¸Qåïçï4ùÖüdŽ´ÔñÎfìlß^¡
æPßçú,dŠ«9¶ée}:]2rRˆb¶,
c"’hnEíÄž»ÕÝˆÒÖ÷¶ä
ÃzÀ}[ýó1ÆûÐÇÈ›�w(¿E~Pf4‰0×¬�‹x…]ÐoÚ‘Íê"¯ã­/*P¡u§‡l¢úS…qŽp”xs‰p
:4¬Re©ÇÈˆ³#Dœ‰¨¶ÙÛ³y÷wnÑmdææõñ;�_?@ÜqþÔ˜`�°X.+Ð…”7¸™S`“�`€Ú#’Gf#Ež²ð‰ìà“SmçŽ©ˆVÀ¢“}æ‰]<WW7ÉÈè´ÜæçÜÞ—n@Þ¼ƒù%ró|ð¹µ=àh95p±q7ÅºÄN˜J§Ö´8©‰ü04ñ£7¸Vm“µU>_•ÌnÓzJ/­}	²–ÖBÛ6â¤H"ë´3ôØ«"ýïtyÆóìùý~þ)Ì'ÏÏ9&AEnTs&M$¢ÑÛ®ÆxÒ
KnnEˆzš�M$Dùt>`>à£Ž:ÑÀÇ3Käå*–V²B)ÓÎr7»>:Þg¸¼4Ïhž,ìõñ§-ìÎ
c#0.^ëQvù¨¼¯O Å7»^œ*5J÷5ƒÆiRóTe¸nAk ¹xP¯Ûy½rÒšÑÇ8Î,!rsŸÏñòòfôpó�½óusíl|s­ìšÕ 6zZjØ–Â´l@ûƒŸ/áÙ‘�æÅ@SV!Ö…š S��­sk_�ÅˆS ½U¶Ü$Õ&”ÃÃÝ¼»¹÷¸‰ÕÝÜ`Ü,¥+øõ/¡Y°l@Çg½÷[Ãf£ã¼—¶±µº=æìS+kŸ[÷’�l';�E6IËCêVhŸÈøz¡–jŒ€¢<¥®üI)"Í:ž�áLéx'pî‰;^£ÉÃ
pxüº?¼?ùÞÄ^ß!:ÿÓ{ÛpwømË-Ìôûtí¯Yˆœ•«”…4Ie[1ëv09�ÕŠ5Âq®�½År‹!+¡gµÍ®af®ê—y%ÉåHí]}¸4û}3u£¡ß˜�º7¨¾BŒ/5õ
×Ãirqµ˜›ëY�¢�Âo\Ý�FýBˆaÇ²á6–‚5Ú“š£úör{ *;o¡]ìh‘pUÈ¡ˆ„V^•­>×ÓzÈ¸þ	ÙÓÚ¿àkëqùC€à˜py»/5­\ž.ÜDHÊÌ­ÀŽ2ƒây²âBsz„Àr¼7[Çmœ”«´®K@±âô€x,G¥ŠZ`<·ãÈ¾s>^ÎÀÛHèý¼yƒê)tø¼=ÁDS€T	ò=Ë=¦Ùbt·žŸzíˆhL)"‡K°O�ÆM8ÃâÁ>Pˆ+tÐôžVš¶<Ä�Y-Y@µäD„øˆá»}ø'XOšúõ^QO�õ÷ƒ’�8…Û�HlÎÆ‡q<Z-°xŸÍ�:µ‰¥Ü�‚’žÄ»•Šét.*øA‘ts·!’#@MDezÛìµ‰“cÎ«¡û@þK@—ˆß‘ÏW«ê9ø¬ª=5‹œ‰í1µe	Œ+B°ÃÅq` ê¢Gš[P"]a�SŽ´é`Ëº›ìkÓÃµ"e-‘‰ÔGòj¼Ç™"f¬-úgU]�=éêfàÍü{Fõ¨-fU,ÖÉˆ†Á*Çùí§Aël"Ï¤	i~¾‰,­GÍšÁ(ÄD×BŽ™X…F�Gv®‡6¤ùa“1G%Ü¤7ªMiw·L/µu>n=Yõëã7xa¿^pëë±ézm€Îô›R:‰µÖiš„kÍge7 ]’¸Ýà·m×˜§T$4/#¨2R(ƒDÃÎÊ'ÉÂ×LHîçÈ²õŽ¡²ÿhíùÏ[»÷Ñƒ-7–ýûèÔ©gôp:.¬òà(b
2üí”Ç™´¯Tùh³+ª3é{.˜æKßÊÛÄ*ôÆ:¦ÙrˆIÀF�;ÐŸíÞùÓŸ}ÕßÏ?ÔÑ“«
HìM«AvÝžê¸„ú�N,Ã=Äæ°€�ùÆP–_Ä•_†ö1oX)Tï„G†"´}êM‚¸ä½±I÷¢m-îëø˜r$_ˆGE=s†ƒ¹·ƒÉònØ04wêäÕj˜2ÖîB(ÆÑpR5‘á=èõ"5r‰á>CÝaŸ€³A-Òˆv5áÉÐÁBZK÷ý%~€u‰/ÞïPg]]Æ>Ô×°q*Æ:žeé.Ì"k_¯FÔ)³-“É4’Í5bÂF#ôÊCÌR,4G·N jgkÌ�$pÝ�„-£'Û¡Ÿ.ª?†?ÑÕs”Ñà)Oµ Ãt˜£Ä…ª›º¨à¤Z4h:‘u–&‹_ªøhÜ8h¢¼iV>Ë@Í—$SøÄ“ËÞ›4>Ó\~ûgçü¬��"ŠŸàzR•	È&æmaYÖHXMWi6:1+²žO×›YyòúU°QhTÛŠeÀœF²A­:ÀÕ”)¥cf‚{ãJKLeíÍ]gØ;¢ÿ`ï=ÃúcððHÏ±C“�XÐH–çM@Þ›’Žƒ|3rA'“	(qäÛ5Ô*#kT­gŽ«2eËi4ÈŒ(§r&¹^±`ŒX ¡8P¸Vù»îÓÓž{}üájzÜo�5š>•L;1!ë*?ì)Ñ �æ$iFR8^�e´¬‰x;ñLÊ!<0UEçI†¦¯ô\s½f�gF€ÑÊHj—Ã}ë×—Vâ!#éæ`x;òÓCTðÙ1b�‘áÍ¦aå¶‰µ•ö¶MtìRÛ {Õõ`c°2Z0c…]µ¨Öx�x´>É»ßñ
:k!æ0�@A3Ì€~¯»ýÖþ³…øÏ¼7{¿ŸèQG¹�ôÛÌ=6
/Dîòí†<òó�…-ïP€ZèwÖ†÷óõøÐŽÃ�?5Õ¨ýµAD”ÍÒdvsó´„z-Ë(lmWÕ}ŠÑK]"—çß÷+eô’es7ôÔsTóYQÁÊŸœèÃ¼6ê®æ÷XxÈ
��`ÜjgŒò<ÀÄ¾tk¸²DÒ“3ßõýB�ìO5¦ù}(:A‡:ŽNv„ÿÙì]>ÿi5Ý|ŒêQ[dë©ã²lN”­£ÙÐB2s5a4¤�N&Ù!:Ò ü�²zŠ +€IQpÁ�ãÊzœÔJ�$;QÉ”EgÓ47Øýðgÿè×§?fÖÝ,«§á7øþ¸¶¦Êd‚õD1 žv›¢_p4Ûo¨	æá‰ w·Àó¦ˆ·“öÀ®ùÈ”sG®xÁÛ Nem&Å‘Ÿ¤›ŠÂN4ä€£ÈmÌýåd¼Æ—ï—Öõñ8Š:#Þ&µ><oh–—n™,¸Š.SjK¹™:£7´î† ÿ5è¨áh+t6™±}G·{ü“î�ZBìSlÃ­ƒ×»ðý{|±°nÞ ú’¸"=8è€ä§l…‹Q”wù¡cYÅÜÀE�A(ÆPV¸:{=²"�v�CÉ
ð)î“6^ ¦ÅãHÚcp>wénV XÙTÇ`ÃnvÙ�‰ó§?;è¿ŸˆèÉA¦èz3ÚÄ	áö8	@Yõ`º’, +BL?˜¼&ìÕñ4à|>*&^8fIœ.áF¶]Z’�Â7të³N¤wµgáÐÜóË¼œrç²×ÇÂy´~�øs*‹éLd¡Æ™ø�•Uò Ã¬±3GAÏÕ®&à)“¶´‚Ôœ—5á¤eyßƒº«£ßÙ¼‘ðÛ°VàŽYPQü½€þõ¯ÿ×¿ÿë¢üX´êx¤&[Ù#!/bin/bash
# â”€â”€â”€ FileDrop Deploy Script â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
# Builds the Bun app locally and deploys to the VPS via Docker.
#
# Strategy: build dist locally, ship dist + Docker context to VPS via tar pipe,
# then rebuild the Docker image and restart the container on the VPS.
#
# Prerequisites:
#   - GitLab CLI (glab) with active session, OR the env vars below
#   - SSH access to the VPS
#   - Docker + docker compose on the VPS
#
# Usage:
#   ./deploy.sh                          # build + deploy
#   ./deploy.sh --no-build               # skip build, just deploy dist
#   ./deploy.sh --help                   # show this message
#   ./deploy.sh --check                  # dry-run: show vars and exit
#
# Required env (or auto-fetched from GitLab CI vars via glab):
#   VPS_HOST              â€” VPS IP/hostname
#   VPS_USER              â€” SSH user (default: root)
#   VPS_SSH_KEY           â€” path/contents of SSH private key
#
# Optional:
#   DEPLOY_DIR            â€” deploy dir on VPS (default: /opt/filedrop)
#   ADMIN_PASSWORD        â€” verify health after deploy (optional)
# â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

set -eu

# â”€â”€ Config â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
APP_NAME="filedrop"
GITLAB_PROJECT="superaseph%2FTeleUploader"
DEPLOY_DIR="${DEPLOY_DIR:-/opt/${APP_NAME}}"
COMPOSE_FILE="docker-compose.yml"
DOCKER_IMAGE="ghcr.io/mytheclipse/${APP_NAME}"

# â”€â”€ Parse args â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
DO_BUILD=true
DO_CHECK=false

for arg in "$@"; do
  case "$arg" in
    --help|-h)
      sed -n '2,/^$/ s/^# //p' "$0"
      exit 0
      ;;
    --no-build)    DO_BUILD=false ;;
    --check)       DO_CHECK=true ;;
  esac
done

# â”€â”€ Auto-fetch credentials from GitLab CI vars â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
fetch_ci_var() {
  glab api "projects/${GITLAB_PROJECT}/variables/$1" 2>/dev/null \
    | python3 -c "import json,sys; print(json.load(sys.stdin).get('value',''))" 2>/dev/null || true
}

# â”€â”€ Default credentials â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
# Hardcoded defaults for this project. Env vars take precedence.
: "${VPS_HOST:=45.127.35.244}"
: "${VPS_USER:=root}"
: "${VPS_SSH_KEY:=${HOME}/.ssh/id_ed25519}"

# Fallback: fetch from GitLab CI vars if defaults are empty (for CI runs)
if [ -z "${VPS_HOST:-}" ]; then VPS_HOST=$(fetch_ci_var VPS_HOST); fi
if [ -z "${VPS_USER:-}" ]; then VPS_USER=$(fetch_ci_var VPS_USERNAME); fi
if [ -z "${VPS_SSH_KEY:-}" ]; then
  KEY=$(fetch_ci_var VPS_SSH_KEY)
  if [ -n "$KEY" ]; then
    VPS_SSH_KEY=$(mktemp)
    echo "$KEY" > "$VPS_SSH_KEY"
    chmod 600 "$VPS_SSH_KEY"
  fi
fi

# â”€â”€ Check mode â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
if $DO_CHECK; then
  echo "=== Config ==="
  echo "App name:     $APP_NAME"
  echo "Deploy dir:   $DEPLOY_DIR"
  echo "Image:        $DOCKER_IMAGE"
  echo ""
  echo "=== Credentials ==="
  echo "VPS_HOST:     ${VPS_HOST:-<not set>}"
  echo "VPS_USER:     ${VPS_USER:-<not set>}"
  echo "VPS_SSH_KEY:  ${VPS_SSH_KEY:+<set (${#VPS_SSH_KEY} chars)>}"
  echo ""
  echo "=== Files to deploy ==="
  for f in .env package.json bun.lock schema.sql Dockerfile docker-compose.yml dist/index.js dist/migrate.js; do
    [ -e "$f" ] && echo "  âœ“ $f" || echo "  âœ— $f (missing)"
  done
  exit 0
fi

# â”€â”€ Validate â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
# (Defaults are set above â€” this fails only if something went wrong)
: "${VPS_HOST:?VPS_HOST resolved to empty}"
: "${VPS_USER:?VPS_USER resolved to empty}"
: "${VPS_SSH_KEY:?VPS_SSH_KEY resolved to empty}"
[ -f "$VPS_SSH_KEY" ] || die "SSH key not found at $VPS_SSH_KEY"

SSH_DEST="${VPS_USER}@${VPS_HOST}"
SSH_OPTS="-i $VPS_SSH_KEY -o StrictHostKeyChecking=accept-new"

# â”€â”€ Helpers â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
vps()  { ssh $SSH_OPTS "$SSH_DEST" "$@"; }
log()  { echo "â†’ $*"; }
ok()   { echo "âœ“ $*"; }
die()  { echo "âœ— $*"; exit 1; }

# â”€â”€ 1. Test SSH connection â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
log "Testing SSH connection to ${VPS_USER}@${VPS_HOST}..."
vps "echo connected" > /dev/null 2>&1 || die "SSH connection failed"
ok "SSH connection established"

# â”€â”€ 2. Build â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
REPO_ROOT=$(cd "$(dirname "$0")" && pwd)
cd "$REPO_ROOT"

if $DO_BUILD; then
  log "Installing dependencies..."
  bun install 2>&1 | tail -1 || die "bun install failed"

  log "Formatting code..."
  bun run format 2>&1 | tail -3 || log "Format skipped (may be clean)"

  log "Linting..."
  bun run lint 2>&1 | tail -5 || die "Lint failed"

  log "Building dist..."
  bun run build 2>&1 || die "Build failed"

  # Verify dist output exists
  [ -f dist/index.js ] || die "dist/index.js not found after build"
  [ -f dist/migrate.js ] || die "dist/migrate.js not found after build"
  ok "Build complete (dist/index.js: $(wc -c < dist/index.js | numfmt --to=iec) â€” dist/migrate.js: $(wc -c < dist/migrate.js | numfmt --to=iec))"
else
  log "Skipping build (--no-build)"
fi

# â”€â”€ 3. Ensure remote deploy directory exists â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
log "Ensuring remote directory ${DEPLOY_DIR} exists..."
vps "mkdir -p '${DEPLOY_DIR}'"
ok "Remote directory ready"

# â”€â”€ 4. Deploy to VPS â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
log "Creating deploy archive..."
# Build context: everything needed for `docker compose build` on the VPS
DEPLOY_FILES=(
  .env
  package.json
  bun.lock
  schema.sql
  Dockerfile
  docker-compose.yml
  biome.json
  tsconfig.json
  src
  dist
)

log "Shipping files to VPS..."
# Atomic deploy: extract into temp dir, then rename â€” avoids partial state
vps "rm -rf '${DEPLOY_DIR}.new' && mkdir -p '${DEPLOY_DIR}.new'"
tar czf - "${DEPLOY_FILES[@]}" | vps "tar xzf - -C '${DEPLOY_DIR}.new'"
vps "rm -rf '${DEPLOY_DIR}.old' && mv '${DEPLOY_DIR}' '${DEPLOY_DIR}.old' 2>/dev/null; mv '${DEPLOY_DIR}.new' '${DEPLOY_DIR}' && rm -rf '${DEPLOY_DIR}.old'"

ok "Files shipped to ${DEPLOY_DIR}"

# â”€â”€ 5. Build Docker image & restart on VPS â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
log "Building Docker image on VPS..."
vps "cd '${DEPLOY_DIR}' && docker compose build --pull 2>&1" | tail -5 || die "Docker build failed on VPS"

log "Restarting container..."
vps "cd '${DEPLOY_DIR}' && docker compose up -d --force-recreate 2>&1" || die "Container restart failed"

# â”€â”€ 6. Verify container is running â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
log "Waiting for container to be healthy..."
sleep 5
CONTAINER_ID=$(vps "docker ps --filter 'name=${APP_NAME}' --format '{{.ID}}' 2>/dev/null" || true)

if [ -n "$CONTAINER_ID" ]; then
  HEALTH=$(vps "docker inspect --format='{{.State.Health.Status}}' '${CONTAINER_ID}'" 2>/dev/null || echo "no-healthcheck")
  STATUS=$(vps "docker inspect --format='{{.State.Status}}' '${CONTAINER_ID}'" 2>/dev/null || echo "unknown")
  log "Container status: ${STATUS} | health: ${HEALTH}"

  # Tail recent logs
  vps "docker logs --tail 10 '${CONTAINER_ID}' 2>&1" || true
else
  log "No container found with name '${APP_NAME}' â€” checking all recent..."
  vps "docker ps -a --filter 'name=${APP_NAME}' 2>/dev/null" || true
fi

# â”€â”€ 7. Health check â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
if [ -n "${ADMIN_PASSWORD:-}" ]; then
  log "Running health check via HTTP..."
  sleep 3
  HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://${VPS_HOST}/health" 2>/dev/null || echo "000")
  if [ "$HTTP_CODE" = "200" ]; then
    ok "Health check passed (HTTP ${HTTP_CODE})"
  else
    log "Health check returned HTTP ${HTTP_CODE} (may need a moment or TLS not set up)"
  fi
fi

# â”€â”€ Cleanup temp SSH key â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
if [[ "${VPS_SSH_KEY:-}" == /tmp/* ]]; then
  rm -f "$VPS_SSH_KEY"
fi

echo ""
echo "âœ“ Deploy complete â€” ${APP_NAME} is running on ${VPS_HOST}"
„&Òäè8Ì2xœmS½OA¢BˆØÐpO.1 ìÞq"&âÉ— !j¬¼¹Ýw·ÃÎÍlffï8±0&ÆÂèÚha,Œ•�¹Äð/˜XÙÛà€��•o9,L¬v'û~Ÿóöýê¯•�ìÙ\™Ë\™™ 7{oÞíï>‡•Ååâü]Ø{ô
Ö=Í#>FB5A°ƒ2jç”¢va7X…|aÚÉÏ8ùÂáðyÀ¢Ø¯Ï4¥™¬b=2—!ÒÊ�CÃa'ä’Ck,dÖø&Œ�i‹5Fá-TÑ•|sœh$Ìß >·Êm—s¥ÃŠP“kt›5M’!J¹Á“!AJ†rÌ…_‚½'/ÛgOEÍìÐ™øJmMÏ
Ðh,Ó,
Œ‰“ù¨K£.\C2©Ý€É&£tjË‚¶†Á2«2N\+¨QR¸€«4•y³¸¶\\KQQciãüí^ÿöá~êûØ‘üî¸º  ,)Myga™[dPô,WÒ�”§Ñ¦V6Ð³èÙBYçZIJm¡Ne³²@ÓšéÌváigÉÓ®ÛÉ~Wv¬‚õõë¤ÈëÌ"„Ø„
Ø›%ë+Êc‚îºÂbaa rÊ�(\t/L¹…ÉÉd·+Ó­•²D•9õ0çä¸ýÂÔÔÄ¥–Ûý¸³õ½g®3ñz?ÿeƒ
…±TI65î’-‡ÞëÜ§ÿ�,ö5Z·úGº’¯§¿%_J­í“K�ÉÛ�±VæÌ�ã/Š:ù9Ø“l} ¹�Z9¾jHËkuÎ @&làè…£ÉPfiÇqŒ[h?œtNÅ¦óP¸rnb¶¶àA/€PU¾Ã´œ=œ©,˜8Š”NÛV²Ã?XrÇSµHRDmèzÆ¡Â„à´�eæ…iÛé¿â¥ku,Ý«aRH^�OV3sÛ­g™Á£ 7>·Çxœ¥VÛrÛ6}÷W`œÎXš˜ÔÅò%œa[Zdm�%Q•('n“a ’0"A m3—™þA_úÔ§~F¿'?Ð_è’”EÊ–Òd¢
gwìåúô×ßÿþóêZFû}úýOd†Ó%á5‡c2£K$b/�Ù¹4ãò0§èz0B½x)(ª„³9¹�DUÝ{†Lùa‚YâG}z�ž#‘IUf>^•e :§rOjw!_ÎüðNÔ¼ÌXM¿Š<Âæ˜c{^‚8¹%\�tßûDEç„/±@”Q´À,ÁÈ£áÃb‰l?ÁsLÁjF8aàæ‚
r*Ô=Aø-�¡í!„£(ý 4‰©ïiHÍÓ�ILá.ÃÑÐŒúÄãa¤€z¦À‰�˜Kaÿ'"Ã»¥<da2§DHAç¶ã:ö•Õéß½/Ÿ*”å…Ø0ÍŽÓ±ûF×Ý ÚŠkJa8rì¡qa¹íK£ß·ºnÇ«§`Écd¹ãa7õcõ·š†c”ÊËBi`½U¯××Hß6-×ê_ëÙc™J²µ¬k_¸]ëÚêê”ÍÂ5ìÇ#ÇíW7ºä1)ùç´/ÝžñÊí8V/ç&¢)ÍúÇ-ê£Î/–{~ãX›6¬)ÇÍVóì</ìS•¡õóØoÎmófÍ°] ›7Z§­³£“ÖYAâX�VC£÷¯6=Ù)KÏñâ´q\>MÛî†Öh”+[¦;tmÃLyv‰4%_‰"ÛæAžŽÛëô7]ú?MiÕ_œ”CÃ±Ün§×qÜ—�¾i¿t³{ÙkÊI}#¾%¥RD™—%šrT/çEêš1è¬“ç²‘«Ó´mûªc¹}£g¥Éð…˜Å® BÀCÝiž:•&ÑÈjÛýì
>¯ )g'­òÑGG®Ñn§WveÝ¤æåµ¦ÅÆ(Û0Æ¡åfÅzCÍ´~2Æ]Bwnåª›˜¦ÄB!XH¥±ay}iÃ³6ížÑÉÊÌcì @cOÅ‚DÌÌ°$*õËÈ™ ”32�9•‰F¥ºÈB…‘;%âôŽ;‡b¼NvN°ç†ÌO4´ÆdÍDa]ƒµ&è;¢·æ‡Aè½qzzZˆcÁk‚OkP®k~8¹îI+x¤œ·›^(êaÌW�!ÿù4 ²´†ÖÅBCMµ~PB„n^¬A`ƒ>ƒÓš·� ®o#8n4{¼ Ø—‹é‚L—ÖšNÁÝ3•Ñ¥Õí–°ýIÌ�BÐëý‘ÓEå`!e¤Õ 
Sì/B Ht-g?¨ªrAX…#ý{ÄUhi2H×uÔ¬×Ñi«…h•ÜSY©W‘¶‰4ªÕ×ûû«Í)“éq}ÕÅƒÃ4 a]²±†8‘œB„ÑÑ
Èú¨­›†Ð~Ç¹"#2
JWW©x_Äƒ‡#3ØÇâ—Töe>±¨„á‰O²ö±ÿTšFDåàŒêC²©<‹KPåí®'þ¶Š>|@;uòG_Vúœt�þ>ª¼}/âIv/¿bå]]yñfõU•7Ï?nåÿâ#Á Â“(„›:g‰øå‘¾øº‚�:…,M#f5}ê‡±óÞîM&±‚$�êû˜S&†Or™Í»hêy>¹ƒ�
&…û*Ç’d¬bàÁs¢}É$æBê�o¡Èß¹Þ¾ŠbÏ`óL²”ÍÕ ßÉo1ƒóÐKÎ(z1||ñu•vÒKÎþèeÓÿayó¶¿·WÎËÍŒÌÓ0—·ä*¹‡ÝYZ²šþGvÇ¦xœ31 …âÒ‚Ô¢‚üòÔ¢bÇ1Ÿüƒjê<ò3Š2dß&¦Z ñ�J xœ31 …‚œÄ¼b†œ\�ç[}W~�X39ÀG"ùîÅ–l&`ùâ‚Ôäb†§æwKV•®ûØpö~‡r¤u¦ .7ê© xœ340031Q0202Ó50Õ54×-IÍIM/JÌÕ--ÈÉOLI-ÒMJLÎNÍKÑ-ÈIÌÓËMaX0G@æfZv¼ ß1í•¢|³µæÍo6D3ÇB7%¨¿®i›|œ½F‡�‘hÞüuÚ…³¿bj‚Ø©›´-%5ÈËLN,ÉÌ›põÈYNÁÓ9ÛüÒùäl&owœ-<§Ås]3ÝbcÝäüÜ ®¤œT°g`þ ™qŽ?¢çÕ†vë5ÞÙmð9ôñºæºé™%©‰ºÉ™É)º¹™À°€¹`£FÜãÉfM7$¼îž+¢æõ›'û¦n$dæd–T‚y9©0CÄ×‡™6†[µ
ï~eXµ/�k6+š!FÀÐKIÑMÎIMÌÓM,JÎ º'¹¤´(U·(µ¸¤¨Ì™¥À2íÏ-ÑÆÆ]Ÿ·ÇY.m–Yo‚n–¥n(óKtKSKÁÚ&ÝÑwˆŽ^ û i’ÆÝùß{£Ž$µ ‡ñ³Âá�|xœ� ~ÿ‰‰�4ÌÑÊÏèîÄ5Ê d~Zh’³Ž‘H!)l=à�Ê¦­D¸0o%ÂF›Ç‘}rCÚ…ð…®Î6˜ŸðvPÐ3dÊK“*wzy.Û´ú‹ï\‹¾Û}:dìöÛ“A1b¹à¨Ð5ÉÞÀ©©h·Â®¬ƒ“†ƒ#o=q¼¹xœí=ÛrGvïøŠ6í20 ©‹m(´L‘�Ì5Iiy±wK«Ìs03ž‹H®UÉCž’J*µyJ%µùŒ|� ù„œsú:7¤œÝJUô`ÝçtŸ>}úÜº§ûSvêÞ,qìE”±³8ˆ×KØgzé….Û_Ä�·ðÂÌÉü(do'lµ¾f?ÿëü×þ{ðà`ôjg÷·°Ÿÿæ_Ø^t™,›ûi%~Ê¬­ÁÖÓÞàIoó‹ŽÍÞDIÆ>g~x‘8i–ä—Yž°4w�9›xI>qæCh:N"7¿L}vé‡>Vüè@§ÌõYŒøãÇƒÁ`Ì¬#ÿš=déMšyþÚu\÷¦ËÜháø!ç4ÛI½xî$7NèØ‹ÛwÇèÂÅöœÌ™@5{ï;ìÍìE”‡SwEoöæÖ¦½ùåÀþrkøôñ£-èr’_"QñJºŒÑÔ	æQšuìòæÁƒ—QÂœ0ÍŸ²«(¹ô’t,:ýúlÿx´ÇNÎ^ôN¾Û?8²3  Íc/‰£+K�ˆÙsÿ½ö\ï½D1Î ³o-àO×s;,J
ˆÞµ7Í3?œõb`WÊ²ˆùrîX3Â°œeNzÙ›Üôðÿ6;É¼8e9Ð0�{ÓËItÍ¬q�½eïÆà-Ìú5»€Ž²ÄÚ†>xð*rÎœ¨)ŠE/ñ÷†M„Ì †’ª?ðX.ÅêÊÏæÌÍ�@±…—Í#$eÒ÷�}{zú†í¼ÙïtAZÒl–x'¿>`(MÀ•.s õÄs}`DÖÃÙsaÂ¯Bl)%Úv’éÜÏ 6O<¤ñEÚ©—¼÷xËôgýò.ïv/ñÿ{ óõña—%yÂXYê/ò sB/ÊÓà†“î‡½…·ˆ’–8™Çá#Ï‰0hoê]äKçy†dE§Þt|Öz°ýÌ_Àh$æX»EZ~ðC|Øe¡F¾Ûe£“Cö+ç½s2Mü8kµz½^«õé§ì%²ù½Õ�Ç­4™ö[hv½kûÇ”é¸XA.`qä‡Y8ì€Tö|Ž\éVÇmAE±%ÞV��©—åq—Oü€xvQ(®œÄE¹œBièÐœ¾¯kî½ø.òš;Ï"ª®”�s�|ŽÌ@	8Ï“ «V3ÿ…KZw'8ü�®lÔN#h•„™T á¥°(Ž�XÀ£Jæz ¦ÅkÏ™4‘Dyæ¥œª)ñjtÊúýaœOJcâE l”öAeFÔŠPkE’Þ¼>'öûbQY(¸~ì�ºE¶<}Ü!ô¹çÙ¼„N=ò $¦¡iZ³›Ø“³ä£x¦þï…IAó9(µZÐ*I©ÉL”©f©ÉŠh€ðy‰4èê5àèA4›yI‰~±FD%ŸP‚Æ%z€}K„®[Á€e=€fù©¿ ãJ•í¿é´bX´ r€…-eÕ»vP§¶„d¤?¡ �vöG6X¡£ˆ†´%\KÂ"p=˜ 6Ë}×£%ªÖï)èc¶9d'„µçÅ¨åÃ©Ò„ºe7
/üYž»Q­ sÐ¨´zl´oæÙØ¤wlV˜´*ô8�Ôû`½Ð( )Š™­BH61ä}#tÞÆ�mà¼J%¿ÑÅP$)ÐŠ•›öÀðR×KIq‰š+!	GAYCØX›Àãe¼‘Š?ÐLC»ïfêµ×›ƒ’kžà‚½Þ��Ž¾ßŽ•ùbˆVFp'Ã…?CA¬XÄ>ÛÛ9Ýy±s2:?;>`½¦Ù·HK1B=u}\kô¢d�ÍýõÀÞúJð„qî¡ú¤ªÇöæ]uÅ…›jÙ››º†[ª xû±,ÿ&Ïkáô½pæ‡DÃF/†¼ô›'ö—ö Ü]'ˆBÏ$üý^-íßà¤}`¶ GÒŒÐZK!Ã¦àl)Á1¥Nè¢yëÅëÓóÓ×ß�Ž¶ýÔçêýœ½†ÖÉéëã�W£óÝowŽŽFçû{Û=ðÈ6·=~òô‹/¿´ä$lÏ³,N‡ý~“»×2§l;æfæP—ÐƒojÜ½¾ÆÖ›×Ç§Ûèt¶jD§uðúÕùÁèûÑÁ6éèã�ÓÑùÁþáþéùûG{¯8?<Ù~:@l£êpç7çèŽNNO¶j¸øHqQË˜à!*�]Ð9§#C;	›c¡¥wÙÙÙþ{s¼¸sü[öÝè·loôrçìàjxžÀ¬G‹óÔ�ÕÁIW†}¿sì>¶¶6;ììhhcG¯OÙÑÙÁÊ¹9ÇÎø@yèÿ”7Âæ<C˜û¯ö�NkA^šâÿë¡¨/TCµÝ,ÀÑ:'kUKX­óÉtcË�¸R>ÈšÒì¹çØÁýC˜è�Ã7j.vÏŽ�GG°
doÐ½F«ó¬%Å mô˜ükb|z®gõõ—KZVeŠr¹¦¹“)
Ý(lÆ4ø¥�²½ÑÉ. WÊcµP”ëdá$—äµ~º:Ämµd¬—pÔóK©�‰ºôtc ef ó`æ±P!fCR„c“e'‹ÞjmÚlT9¸2fd#ýÖÖ–±T€qÃŸèu%99n=²…§ :ÖO¢£<D‰Z—~Œ[�m6rýŒûcpBÂÐú�ãóA¤^š;~ë‰-yI,´7áá
ÓÆpÜzªbÚ:íóª1wj$«ÞÆìrÆ<¡A—ø¦ð�+§Îb‚	
,&è[2ÇE/ø-	àÆž;Fããªõ€Ð}á§NÞ8¬hH:Dæ1ø´…RÃöÞsîSðÈtžˆ§`xäD0: æ $­Ð>ú°£Ð¥P+Eù—=ö1ë	¹$1C�Jh€0{"ØUA°†¬‰ƒœh²,Í½}¬ÿ–þâ®;$w~�þ%z8à/ ü…Ñ8—iî"û€ËÏa}a­Œö3h<V° ·ÙdÕEèrÜÚçi`£Ï°aŒBý)¸þà¢ ;®ežåG¦þOQ‰¬×·…¸¥Íã«<t…Fš‹¹Ë€¹\Ù8?¬ƒ4‡µ€é ÌØ5Êæ	(›hƒéÎÌ`e9®[ð“‹«P›p­¤mJ�±Þ‚mLçQ+rËÎÿÔtþ7jâ†-7ÈPè€B¡¦8ó<ÌSñT³û¯@¤ïï¼w¸ûÝ½‡¹0á¢²‹$Z°¶øÕótC(Â²m	gs½ÆI´ÐÇpmQýNÁÔë”[ÅþðÖFan“IÿÙE'›â¿màã|]‹üÕR¦Z`�.bîñTê½$‰’Ôú€yz9d­ylY‹Ska$MÕú;g‚üƒ¯,ÅvÈÚf0ÔæÞu®WŠÜ=ù–šî÷Ù	„ž)ð(Õ9r³ID/„˜˜4¢„Þ•¢H7hãý†~€˜}jÄ†?Û]Éá6•µÕðîÖ à²KmBÐÄ»ÖýËþ£Ì0¡°Ä‚4ÂAP\¢Á0üf™ó,]jöÉö6kkìv‡¢!|°*¬ZwyÃ�UÖ‘�Ê¬N£ ‚ðÌ“RH)‘jñ
þwÙ�Q/[-ïš€Am�—²e.§”qÝ%7¦Y\B1ëÅÒ4*Ÿš%ÍÛ)©™ÙƒuY“:ÝòHê–‘ö4Ø÷Nâ£‹�Â_2	´JÛð<_³’áõ�FÐÈŒ]¨V5‰÷S¬u�TÐ7(*Ö,ªUŒ3^uŠÀÕzÄ’Ae©aQŠfèY„2kCÌ"–´–j`à¾¤¨¶ÙëÉ� :6&oÁ<XÆˆQ0mX³à‘ZÖÛó.æçrï]‡mÍÿdÛ°Æò�2–ž‹ŠU·Û„¾pbÀ½ôn8ü�Ë­ À•0ák6(¬PR,VûPÐ)É2ÝSèMÍ4’hšå8O¢+RF#jf,›©ÇfŸ}�Ôü.•­µ;ËqaIr¶qÃ)Dœ�SÌ5¬�q‚—ší» ç$©·fÖ-ÒÀ6¤:Ðo?K‚f‰�Î}¨,8††þQ2¨Gœ@LK |î&ˆ@±Q¥[Ñ„ûHdIaÞÖ6»*‘
JBªÃ´�¶ºì‡¢•² …Ö�ëc0Ïk4hæLT›�´H„"½V{T'4Œ¬±¢òAHšlÛ¶ù�®!¼Ä–Ïí4Ÿ¤°ÚÂ™5à?%dÛ`Ù²6&ÝºMSsu×¬›kåÞÈ·×ééÇ*w¬÷� *›œÎÍjÐ�ôet\S§¶Bjt¸ŒeÇ¦ÿjrÀ™‘e¥„e®·§@/ýl¥eU$6šðÑ~
Àå:E~v!p¹Î`âýí€)?¦•ƒÆ~<ëMÁAGSSÐ0<÷¶m6lµ©E�RrCêÈjûn»cÃP}çÝXû3á8¼-‹ôí4¹‰³Èæi:ÌäY#M·¯šRÑ´ÈÓlÐ¸_Gyp/F¦pº5^9‘Ó^�uFí6áªä^¹‰’
åÜµÚ¥t­CŒ‘Á·óÅ„üäú–yö¯®1�\§=¤ûˆÜY> •8,s†§7±T9ÄÊXÁi|�ÙCƒ4•Q\—$³'•q,÷$£	“FZm�¾D6iTŒ‰Ú:Å]
q<Š®Êâ$r“%d�±lDæñ�X2”K¥„Ä6ý]ˆ]ÑÏ@s{É	HØ4{V€>ò®š@ñxIV§�uú\*µJA¬óúu/vzäf
Y(àåOPôWëVÕ9W KåÛ*-¦½Úià£!ØVýZ�®©œ…s=;Õ%íƒ3å0u[T&vºuñæ 0K¼Sw
NXœ2NÞÐ Q¦*|VŽŒÜISîv¥�4Œ+¢fëi@uÊ'@Xg7ŸH»yŒârÀ·ƒoÏœ˜{ËÍq�	µntS×h�)†iAOþ²*“FY¼c	�zðhY>ð‰˜\ímt<X¥W9à¼æJ8w(i÷óît[íÚ­Ñ\ƒoÃ†àã“öÜIi\±AY¨„8+¹Ÿ—cRœ6X3ü…C¨‡¹¤±/[Šh<^•¸@o¡Åo±£¨Áf¾À65­(áeÔ14Cü`Hœ½Šl¹€ÿõ¶ÉEÙ…�Ž+'�ÀçX�``ÞõÔó¤‡KÃ7ÛTœ(�Äù€tfy²'H=E–‰þðá³–Ã<Ù3ò¾‹"xN˜ÇJwJ”Ä¿ˆ#ÒÓhK†D¼}G“ˆu,ð–XÅùŽ�})L½Œ²•¨­ž~VèÏŽót.$ë–’¥  e,B8dP¡{—@¸¬«ïjTµU³â¬?S§4ŸJ¥IÆùúð³ö2ùA—ÛÕ§8ZÔ¬9ÀG(Í—û£óÓß¾�ˆ€Ms¾O¶Å€Àl=.þ¯‹9Ù­W/ÐážGY„–±bspˆ ï}×‹ÖhÊÉ]áumA)o,¢Ìñj '„ØùÛÔ«ì´²(Åa*t5q).èôáÔ¡c8¥u‰ugqL	{üÂß&×j÷Û�·ƒwÏí,:À§»`TÍU+Z£:@?+àBÑ½`ò‰xÙîHÕ"~?«&†Àüw°œs^5  “gž†7öÜæ9zP	í™@Ï¡9-mî7IŽjº	™æ»Ý1FL·¡é®T]ø¬¥Õ³eô˜«��<Ãâ¡Âš®
PJB÷B/±·òö§ËmÙ³a9Tûì¯¶yS5TA<”8S[ÚHlÓY—2‹`AÜlRÙs{îa@”>·ß¶¯{HQ�â»wÚ>^‚—€¹éÓŒLgjÏmDâ(±†¦Ö„ãžÕÛäK¤DZ(n©“‚XO“_ƒ&'¶XEQbÝD
–Šðõ,C¡éQü¹Ëà•,“áôJ–•�ËŒâZ–UÐ4ËÌª¶ƒ›RM?šf^ÖK3°3‹öÇ™Ua¤š-*mUë¼®õ3<]WcT¿�FUmYãÆô™D¹Ý¨‡k›«´"&Vºed*�(6¹õÑ­HÀ/u(ÌSŸÚJã=�hd—V)VƒƒžŽF¯ŽwÏ�-ts[Ÿ5pbßV#‹’Y°?ûPjiÙWòwüèñitÂsJÐ¦“Þ„SfáÔíÎóð²«2Gtˆ}êí	ÑXò•ÕÂÂóô|Õl"Âðž³ñÿé�Ë>û [^ŽÁvÈÏŒ&ü0Î³š>ÞŒA©ìqÉ0D^Ì[ðÎ€ c¡#ŸÃtü•ãÓ¤ØÈÑÔÝ7Øˆœ“Bv¯Ë©ëˆ–
Ixòs=:Ù-P)>Ñ)¼¡ À6Îú-eÃB«H»œÚ˜�šOÕ× êüæ­¨d§l§šš¬�Ï]~RŒÀól:gí‘•â8±oöÒ�~ˆMò,<?fßÄ6BòÿÉNUÇ·Ó¨Ž+æ&p¦J|‘¯ÝZf5ŠzI€.<±5þìCyÍ.E×ãŽ)å˜¥Q¸‚•ü¬…J ”]êh«¼oH Æ	sRýeÊÏ?¨€Ì á‚F�9/lDÐ OB¢_Xd,ÁRÁ°–aÏJ]ß�O8=ã®-ÿR
\L<<Ö–¢(2.÷önføéš½ö°hõ��â,
þ›DîÍ�ýêäõ‘Í÷¶ü‹qäã·,;–üÌÃ²S‡>
5°šY“Ð+gN5Îª•Úé+iz˜CEga6±J2A%ö«°ªªk¶;Ù¼¡]¬ºïš‡¹æë�o¸ÂT~üú~Av•g]@�„“c8ÍŽNa•­ôr¾”^Î™>|÷-ÿJl•‡#>¥Rß?5;8
d…{#²j]ø#��e:ù?ÉÃ{åñÝIWd¿�”þ8áß‡®�‘Í61ùw
�ÿ,l”|”®œße™2cªÌŒð¿[÷ºå8«[‰"*�§w]7¯ ˜üs@1óÊò@pA™ÉfGŠ+6Õ ¸ŒC(9ÛÕ<¡4`»m¨ Ûˆ¥Õ'r}<åÕCµßîh%Ô
'øPÂrÊ5ÁB1ÜÀ}UqSG¨�ú(h;(½ídyj=:\í*µÑ>Ó<F¶{®äíŸÙô¹qÍÈñcâkÑYW—‰åúùVVÄ“ê êP—Æ‘¨Ú™¹ƒLáØ÷Š~ÇO¶,´*�ûàTÉ…Ž¢ð
˜ÈUTA�h£e³´â¬Ë.ó ¨XMþÄ†¿Øšâ(âUÅç�8â§Hñ¬æÖÜ¶š‹ŠÄ33¦i·�$qn^ä^Re/þ‡�*ÀRUÀÅ6T’…ƒ×AV�eeéîlÜb? £hF’êŽ\S�B_®òÝ“TDdØØ’g¦ÆõŒâ6F=4®Œ›Ýå’†7FÚ}‚tÈ*â´ô?›[m;v/¸œÕUf×Y»#$iá87)V©®âêÉÈPÌŸ%\ ¢/,§°âœœ…ÝÓ‡n«™©°ªš4 ¨0­Ozä»t&E�ü*¹pã©~Êñ FR‡Ltx¯+ëüOýÁÚÊ¼•A–:qb´Rqu�¯Ù†jÁèJã,	n˜ªr}<dHÞ:î×Y;‹öO^Ÿð3qÆüÉ«ð#|X^:ÿÂ�<.ûýÏ>Èi_ŽÞò™ô›+Ìs•àWœu?É'�úVäè»rŸ¡+÷º2ß5³êï´!×šFËis' ‚GQ,íøul:\«¾~su6Cé¤-¥“ õ®²{ò–µMïÇÛ\í¤ÜÁÜòØ²kInBa¸¦® qâŒ@ük¸¶ïj÷CJÈòà—æ”e£	†€ß`–¸2�>¾‰(u^‰¤o¬a=ËÆ$Š7Zu>Ú"b—iƒ¸–AÓ{JbX«�Ð/a‚*ÚD2d…!º£ú#t't´ÂüüÆg=Ó³ÂðÔ›�»�û˜œå_Zy£æùóèí{$}Ê‰–æœ�ñõ¤¸æ§öâ™º$ÐW…ó#ÇÔåÙJ°Üzl$ý¿“û)Ø«äZÌs;É”—”|å_”ÝÉë[–"©óæ,RÓÚhôœŠÔb¶¯62[_UìoÝ¡µµŒ
¬Ô”Žƒ2P†U.Ø«¹—xF‰­H·a¾!3®@èØÔ½µY°¬B•òœô 
îH\ñ;Ç‹(ùY;ƒ�*xª³¶�+Œ(5ÖìTÐô qoïªFÓâÜá2eÕ'aÈ÷²­þ×\ÿP˜$.{ mWnÈ"2ß•­ýüjÙç®†¶!˜Q_Ž+êïÑ`«£ÒxÚ³}q#ÕîšÄ¬­�i$¢¡��;¹>B5·Oø�jêËÛ†”½^ÌÅ]¹�_Èÿ¿–V¯¥Z›^ã&ÒZÐBQïaÙêwƒŸUÝY*{Q¶.hp¡lõ»Ö‘"£¤Î¯"]Àyu·%cìÈý9–Ëý]éÜrL‡C3Ë-ß±Á¯,�j™Í�ôdÄEäË¬áÊ¨›øš}²Ò™ùÅ|˜fw¥NS‰á®£§”ÆáW”zVûdt0Ú=e›í•~5]É E Ñå]v#Ì;MØm³ÞÛÃV$HIÿßs¹Õ|7êÜ¡Õ
åfå$n®±ËÊ¯÷lI^ÿ‹ž[!“åmE½«¸¾ëý‹íºMÖm¶×]
Ä·àÅúh´ßk€#xd>JL#~	=€8Šæ§Ùua›�¯9(µ/näÍOžýóßƒ(Ñ'ì;?ñ—òFKž(ëòƒc]~Ä½ËO°wùõ®>…ÞÁË°&tÕ¢o³1{¨û û@7ð¸~ãíXtó¥³Èá�o¾’z7ðÃKuŸ�-ÓÄÒ ›Ü€úVŸmî²»fvÙ§'©è=ˆ\òU,}uÐ�‚+RØP!—ÿ™¸D."rº×Ä¢±!äšXÄ:¯NY·/äõEìÃî¥óæ&‹Ô"¾(�ƒsú\N×¸“oYä¥>Dþ¬†÷"k\Çû†³Õ·q~Å¹íõ'âîh«Žƒß6ë¡É#â%..œkqÆ_Ë2]»Ã¥ïyå{—f!)6 Dêyõ–u[âUÛBÃ×.Æð0æPÆ¾–íkXZ'Ö¦»%�úÌ2úê,_ˆüwGËæ²Ìàu²àj59pÝ‚üÒM‚ÈZV¦±&›�ÿŒPE¶Ò5ªï¼¹ú±Û«wÞ`½çë-›¬…¬|Q¯¥ ¬´qzËÖi)øBñB?Áö$Þm#Õ¼´^Ë¡!"÷Hj’Ås8õYt)T2'£{+/©‰—Ì�Ôïó¾>aÿý§?þãïÂß…òC¼Äz[ç[ñµrNé/ÇZMa0õ¡yT\³ÂTõDºZ:xËxåÈUM¯qêBNoan�E}Dbô/¢tÉÑ’ÉòÕ©<©'—ô‹?ŠýV¹ßþùßÿ�½rfN LgÆ½¥6D ‡ÎÌG½’ùv[Ó_õ«òÔ³Ð'ê‚ì]gÏ¨ÀS^xïÑ‚`Ùóß‹`‹¯$ŽË²�‰ã‡¥A=·ëÒ=Ø¯UvùtŽ2pòp:Wº±ž(º2ÝdAÑ½®ø)}„@Giïz¾—_*ÅíÎg{ïQò8m�ãºÈ#!�µ‘¤ºY’žÏÉÜE°Ì‰W…“úF¡¦€r�ë9øókä7>2”T1›âl4�
X5`yÓ¬ŠbnÓtËÙÞJC2eUmGä6**yPuíÇøå@¶¼É$BX>×ÛüJˆò[ÉÄâÁt›õ//‹o—Ú¶‡Jkà'ÃóøÊî
$óž[íÕHa™ü+áúÕ›o›ÛØ—/i(|Î¹zŒoå«òó}ÒfàÎu’”ÒHm$–Œ©‡ªld´{]4ºÌèJ¾Är"bÑi…ÔŸ…N 4t¡ëWå÷[‡.êiQ&]¡�Ý ¢køŒ§s¸Ùà£O[5xàîÆ1"šj†cjÞˆ,&iýã5_í]û™5à·"´daúþ«ÓÑñ!�§\Ê¬Óxˆ«„¹tºèðWŠŸç€‡.•U»Êºü*ŠŠ<ìèCú¡¡|ŠT@gÁMû~7ÊI=Z¯òS­ÂS­Â'R…›G¿k­À£!;##ÎF×>Ý±L÷å-ûÃÈõ/nj"QœÌ,-f©�|^1ÇÞ"€~õˆgüLV$&›f…æêy)S¸±ìtRéÈ|£pYë§üFõ†¼ÇŽ=íA<m•žÕ`ú5šúÅ;‹=/Ñ½uê¯µª»z½á’½»Þ¸ÔÛ)KOâ]8xOí�sX&oè=¬âsTµÂ}âÅÜ4Úï½æÅf—'2Irª3Îï^W®s¡-SÜÐ^?ÅÔ¥H—{âîu³�)Æ:¸Y=ì÷ÕnC¼OS>…S|ƒµñÀYý°Á÷6†ÑåFÝ³'´DÈ+ÀçQÄ‡ßâ*q:"£�Æt*tô~Ãßpª§ÇxØéw {/ùQÄíoú¸EÞÏ¢>vÉ·ê¯³�†0ÛXMUÁ÷IôžÞÝØEö×¸Ð�?Ver�Î;‘¸P'ê6úÚÖ+WÜW¹µßD‰IF�š…Gƒ-N…<0 >žÄ…«,ÕîÞQ]O…@¡ÍSèIPˆÏœHWÈúdóñ	ƒ~M¾è ðÑÎy_jõÓÚ/¦Õ+�m±~0'Âwýðã|l«˜<¯Àb üfqrnP‹[‚ BÃdæýr’	ë²�§X…4~Ë6äÇ©ü�*Þ­£þmTç
šçsÅ	aRÍÀ¾+<ÇWÔÔÄþG›ò‹”öC¶É~ˆ»µx”²Ï>lÚö£Íå3`'[%ñ¦œ±Ï:ôZèØÆñÒO€®GnÞ=·<†€ÁdÊo}U3¤¯Ä�*ïï‡µâã#ø$B€:ÓÔˆÅJÐ†·j;öùç,<è}`oâ�uô#1–{E$¯Üµ°v³$x¸‹üÔôu \I{*}â(NyŒÜtùÍèéÂ_í¯âŒ…ÓºyÇ�÷cï½ï]b‘5¼§âUë­ e½À¡Ò~a¢ÔEùâ±}Ë½LÛø÷Ø“¾—È`ÝøXñ®}	)[©6{¨3˜ �—DŽñ%š&G«ü°›ªPïLÔøSÂû)<QAk­òþ�¸¾±ÌÝ(¾)bÃäãoâÏöð«ÅI¡Í®ôÆô3z%bÄ«yä	„Qæ•È©EBE�½9Ü¾o‰¬éñ’”¾‰»¾aÖÑÌ¯;Å·6îää€M½$ó/ÐEFR�º]ŸB�NÐ™Î¼ÙÍªËû«LFõ>+Jzd&7>âFÓ”§uêyF¯–\³“|�÷·Z§êeXõZlÊuUÉ-¤ŽÊ/ö	Þ·~þ·¿3Ÿ:­ó	¦ðnh ¡…G‘|IS´Ÿ¬ò¤P§1!ú´1Å3tÞ¸€S}C–“b\JpÇoÊâå`¸Ý°ïßœ@¡{å»Ù¼CHûoÁÖ=%‰ðòÅ…T¾ÒJÚa†/Ö`u5\—fšªw‚À¼V_¼&Ä,®�»êaI{—Z"Üº+Ñ�k‰Ä3N¼¹¦>D0E?Õ¾ü¬Åù�:ï¹£2F	ì�÷é	â¾~ü¹'·ŠzRbzBbèµbÔS(ÿ§Wà}òwŒ�OüË›tˆ½£k%_FÞ£—‘‹¯!?x ëq�Ä”˜uØ8Is&ŸS¦‰À7�ñ�Y�‰—]y¿JAÛŒ×ª·,¶ð�+ŒäÀT’¨þËãˆdåp	¥à¡ÐOüá‚âcÌøR-R¥ÇFRHÇxä«Pü0÷‘ò–œ3�?ðà ~ÏÞã¼ŸxœÅZ]rÛÈ~Ç):ôVÙÒ
 þ¼ë°VK-©,K
)yãÚÚ"A`DŽ`°3€,îO*7ÈKžò”cä<{�\!Ý œ�@zíJUø`3Ý=3ýóu÷ÀOàXwLnÁµôÙ-¿Û?	á„g§ùƒŒ‹DÁ1K#±ˆY’ÁUä'Žó-üöÏýçß‡ÍÍóÁÉaÿÝæ&üö· °»É`ÎU&$Wðlw{÷+wû¹»óbÃƒC©xÆî²\–ËÂ—Õºð%
m,«òÐŸCÈg~’ñ;?©¤½p·w7@Dlüe¨…ÊXâSßÃ² 0!ýdÆîSÕƒTÈ&ûÛÛÛ“-Eìó&9žÉ=_±tîË…Ÿø^¼ðxH$~æOqî¹�’®fG"OÜn*D“�ímogwÇÛy±í½Øí}µ¿·;�gÓœ¶ø_º‘üh.T¶á9¤¬ÍÍWB‚?Cò >‰GW=ÔÙpðç›³áàF7GîèõÙùynp]•§L¦â‘©|Zpº¡ä÷,qCvÏ"‘Öx&Y b|Yˆ‘#{`Ažñdæ¦h4™ §+834Ð8d¾ºs§—þz0ÊXª Ç=sÜMÅ<›¸ð=ü0Ù@='™ÿ ·¸P&ýàeã77O„ÑqúçyÂ$ÿ‰Á5‹ØM¡cT\®�Žòd‘ÜòY.YmúÈŸ²Hb¥(v¼Ö:ä Še�§p›GQÓkúgÝþ1öíÕ�þMõµ·÷ÜÛÝßŸ»=”ÁÝ0@7d´kR¸qeÜUê&Ú;oyÄ&ð�gsÚ9‰œæI±b’Fæøœ¦|ZµÇä}á50	.š'Šy‹8š@ÄÒIb“‰ÕJHXFŽè*<3]›xeôR­£Žt‰0�–.	$Ë´Jñ™”]©]ï”ôpÍ‚9ÚíHZ ¢KoÕ€PFh_À [ß(Êu]Çyòä	\£ÁN–êC�dyJ«½Â7òyÇ…¾d~Æz–šÍaOëŽÏ!qÆ)Üƒˆ|“ÄkÂ&i5•èÝ�H¤Ž<IXÀ”BZFáúßI4>Oº¶€ž3™Lô³“ˆ��cæÈãx3žÿÌó©ƒ,Ê‰ÄL9!‚œã±äžø»Ü­w¹Ò¯ê�°÷d�
ëwç	YÅ 
Žr…Mÿs^/ß€@tÑQ{hM×�RŒG8¡ë"“ŽóÝåðõñÙº¹’]%ƒ.J@ó¡¡SDN–˜$ÁÂv­=§yõRôÜ†÷^á8.ä!ÜÝ�×%!g	žc²–Ã‘kxsAtÀËI×½•â'„2b,NV-¯D._ÖòåÊ™Ò£åÒ\Äå.pñ‚ �ƒÏø/I<çI¶E!ûY!²PA½
`ÈÌÐ$µ¦ÑŒ¯8EiÌãßT¢OèèCÃs)BÓÅ«ˆÇ¤vI.¸Fë+„Â½/¹?%µ.ÞÂÅåñ`ŒKêbüêrx}@©­VbÃå•Wæ×ÐÅR¡õUX">¨&ÍÍuÉ½»�ø€:F­Zïg·¼Ä³^K[a‚‰}Oý‘Éê:Æà� ‘ÑTÙÛüåêr4€ê hLéšÀé¿9†ï;¨ùÎt¬mv~h‰Í½æ&•U WìŠ”-0‡hÁ
¾ËIó%f³¼Ìf½ŒØ½‰ƒûÄ4ÂÂŒò€”Nùh¡™T‘80�“RÈ'Í]ì÷Zcž™hÌ©fQå.hïX†Ì„ÀÂÆ¤´ « ´47†N0ÇÁ^“…LorÙÁð½bÛ�Ra¤ïöÙ`=¬·¤¾Ç ®!¯5Mwe‚´€ÁÆÎa…~9è)
9zðtÏ{ñÔqÈy8­ç �(ý,�ÐI{0›Òã¢/bƒˆ§Šu-ˆ|ò�‚%¨ŠžqâÇÈkÒ¹åD$‘=m}ðª3b]/àÂÑåõøúòõàâà‹Ÿëç_ëéÑõåððd0îŸ^\ÎÇgÇH÷xpÉpt8Œo†ç$®|\N^æë’h	0ÕHUsç—'ãóÁÛÁùOnE1\N-i—8Å°®—$�L›ó+aàA&sÖy<;Ï²Ô£¢�ë™š÷$¦îƒS¬ÁŸ­®%7>I"ÚI.R�)C|`S]W}’„,RŸ~dò°÷ÈÐ…D„N|ae‡™÷’­Tù¶-‰þL}¬ùQœ§±Ó#p-¬ÛqÓP¶‰´]´·¯ŠUt~<ÝŠrHÃ“¢�‚¹ö‡õÀ{L¬ñ¤‚»®h…;Lk`ž2,Ø*ð+ëÄnUœ«nX0¯€Á²J´{ÍQ×÷�¢uÝK¸ÓÆ*{³sA4[š«¹¶ë;eÌ¿FôQ‡ì8ïÅ´#¨ÓsXÍ(—04Çl›å®ƒŠÇ¥½|ŸzHÊ�Tr`ï/ä¢¤ ê1±5÷µ†»AIùò~ßi)rÊVª·Ð»Šæ]ÜÐËûÝš„lÞ«ß€
<·Î X 5i-\+fI%ÁG*X[úqQd,e Ú›û©±½�…Œ3qÇ’ŽAñÕ‘tgwoÿùW&YØ8I¡ßë.oz:®—´&À#=š$›!¬ü!W�ŽäÌ›/[î7º­)Ž2Š±W©uU4ÈäèÒÂÅÄ]z]ÂX¨zP;ÐÿÅ·ÎÅ�J�UF`}…C6ÃRò‘P�/¨äO\½ÂËû½•~'K)uMaÌ¡@©·ñÅÏ?ƒkEbYöë¯]ê+…Ñjº²·÷NÎ®OoŽ´}ãdºC¤šï
U^ÕoE}Ó~"m#²O}¬ç+�EE¢~<sŸd[�–ƒØG©ß_P!£F3ËGl÷ù,o)�°¼¢»”Ñè´é/ØJ³©Xt•¡ƒoÛ[mÞ"ÌÀºdj5°"3&îØÂ6&nf|5<{{x=¿¼³í¯ÉS\æc ¾¹7…®H3K™Y~„ÀzybFÄi˜�PaÃl�5®Ø�_J§‚"L*ëƒ›·y:¢iåá®ÊBž¬ÛžN„áÕÊƒ¶ÊÀ:;6þß|O—¯žÂ·£n6æœÙ,˜¿ºq0ŸÑDX»þ=…ùkk.,ƒ=n4ÌßGš›ô�Æ:«››ðw4&6C³I±gW7,6]Kóbþš�ŒÍÜR›?»Á±Y×5;+(ÿG�Ï'K_Û}²´fCô9>Ú­úy�’)q•S´5PßªfÊü5+s
1ÍiÅFº‘.nþôÅ_‰Ñm¸ˆ"‹lsÖDƒ%Æ×£vnj«S-¾ÇÓMF Ë•ƒMâ2,Ì‰&S;:´hðŠ¾'iX®2˜]1Ñ¯œ¨ZØyšr¨ü?>‚˜Ïªûë5¨Pv]“3Ô•s8íinÖ”½¼úDÇ]'›&7\ÛŒfkã½®Õ|Ô‚sÝ7:ç´l—õGCêˆ¶¬‹Wý9/¬ûëÖ†|¿nÈGå‡­¾ÙÅ7ZqR>ëKX˜ÌæèŸŸU·¼-m8µu€”~ŽI›>×š7$¨˜ÁÓÏreÞ .XÊ¼öLÞ`0ÐEÀžÑg9óÈ—œÝB"2sE†jÁT§YQBR1¦)ýÍhò¨öQOšwË……«¯¬ÍÒRŒy"=R|SmízXwy8–ÊÇ­ûáû£#õqu~¾ Ê`A¼ønnxæ½x)ÚT®ÓÜðž¹aóHÝ�ÔÚôš\w*ÂtÞ]ÞÇ×ƒóÁÉððÍ¸žï4[°©’àb¬›í¯_üq»ÉXMENÉDa]fÜ…È¥þ†H“Û‚ªJ‚ÕŒ“Õ{UQü’šŒn8¥Œñ'¥¢X„ì@²sN™—ÂÅðBÄ¯êãoSéTÖ/{eÏù/"¶z³Šx£(\×# Upload and Deduplication Feature Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Expand supported upload file types/formats (stickers, video notes, etc.) and add SHA-256 / File Unique ID based anti-duplication mechanisms to save Telegram channel storage and bandwidth.

**Architecture:** 
1. Database Schema: Add `fileHash` to the `files` schema in Drizzle.
2. File Type Parsing: Update `FILE_TYPES` map and `getFileType` to support sticker, video_note, and dynamic fallback.
3. Deduplication Logic: Add SHA-256 helper in `src/utils/file.ts`. Check DB before running Telegram storage forwarding in `src/routes/upload.ts`.
4. Bot Listener: Check database using `telegramFileUniqueId` before forwarding bot uploads to the storage channel.

**Tech Stack:** Bun, Drizzle ORM, PostgreSQL, Telegraf, TypeScript.

---

### Task 1: Update Database Schema

**Files:**
- Modify: `src/db/schema.ts`
- Test: `test/db.test.ts`

- [ ] **Step 1: Write the failing test**

We want to verify the new column `fileHash` exists in the schema. Modify `test/db.test.ts` to assert column `fileHash` is defined in `files` schema.

```typescript
import { test, expect } from "bun:test";
import { files } from "../src/db/schema";

test("schema has fileHash column", () => {
  expect(files.fileHash).toBeDefined();
});
```

- [ ] **Step 2: Run test to verify it fails**

Run: `bun test test/db.test.ts`
Expected: FAIL because `fileHash` is undefined in `files`.

- [ ] **Step 3: Write minimal implementation**

Update `src/db/schema.ts` to include `fileHash`:

```typescript
import type { InferInsertModel, InferSelectModel } from 'drizzle-orm';
import { bigint, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';

export const files = pgTable('files', {
  id: uuid('id').primaryKey().defaultRandom(),
  publicId: text('public_id').unique().notNull(),
  telegramFileId: text('telegram_file_id').notNull(),
  telegramFileUniqueId: text('telegram_file_unique_id').notNull(),
  storageChatId: bigint('storage_chat_id', { mode: 'number' }).notNull(),
  storageMessageId: bigint('storage_message_id', { mode: 'number' }).notNull(),
  fileName: text('file_name').notNull(),
  mimeType: text('mime_type').notNull(),
  sizeBytes: bigint('size_bytes', { mode: 'number' }).notNull(),
  fileType: text('file_type').notNull(),
  uploaderId: bigint('uploader_id', { mode: 'number' }).notNull(),
  fileHash: text('file_hash'),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().notNull(),
});

export type File = InferSelectModel<typeof files>;
export type NewFile = InferInsertModel<typeof files>;
```

- [ ] **Step 4: Run test to verify it passes**

Run: `bun test test/db.test.ts`
Expected: PASS

- [ ] **Step 5: Commit**

```bash
git add src/db/schema.ts test/db.test.ts
git commit -m "db: add fileHash column to schema"
```

---

### Task 2: Extend Media & File Types

**Files:**
- Modify: `src/utils/file.ts`
- Test: `test/file.test.ts`

- [ ] **Step 1: Write failing tests**

We need `getFileType` to recognize stickers, video_notes, and safely fall back any unknown MIME/caption to `document`.

Add tests to `test/file.test.ts`:

```typescript
import { test, expect } from "bun:test";
import { getFileType, checkFileSize } from "../src/utils/file";

test("getFileType handles sticker, video_note, and fallback", () => {
  expect(getFileType("image/webp")).toBe("sticker");
  expect(getFileType("video/mp4", "video_note")).toBe("video_note");
  expect(getFileType("application/octet-stream")).toBe("document");
  expect(getFileType("random/mime")).toBe("document");
});

test("checkFileSize allows up to 2GB for fallback documents", () => {
  expect(checkFileSize(2 * 1024 * 1024 * 1024, "document")).toBe(true);
  expect(checkFileSize(2 * 1024 * 1024 * 1024 + 1, "document")).toBe(false);
});
```

- [ ] **Step 2: Run test to verify it fails**

Run: `bun test test/file.test.ts`
Expected: FAIL because sticker and video_note return 'document' or other values, and fail the assertion.

- [ ] **Step 3: Write minimal implementation**

Update `src/utils/file.ts`:

```typescript
const FILE_TYPES: Record<string, number> = {
  document: 2 * 1024 * 1024 * 1024, // 2GB
  photo: 10 * 1024 * 1024, // 10MB
  video: 2 * 1024 * 1024 * 1024, // 2GB
  audio: 200 * 1024 * 1024, // 200MB
  voice: 200 * 1024 * 1024, // 200MB
  animation: 2 * 1024 * 1024 * 1024, // 2GB
  sticker: 50 * 1024 * 1024, // 50MB
  video_note: 200 * 1024 * 1024, // 200MB
};

export const getFileType = (mime: string | null, caption?: string): string => {
  const mimeUpper = mime?.split('/')[0]?.toLowerCase();
  const mimeFull = mime?.toLowerCase();
  const captionLower = caption?.toLowerCase();

  if (mimeFull === 'image/webp' || captionLower?.includes('sticker')) return 'sticker';
  if (captionLower?.includes('video_note')) return 'video_note';
  if (mimeUpper === 'video') return 'video';
  if (mimeUpper === 'audio') return 'audio';
  if (mimeUpper === 'image') return captionLower?.includes('gif') ? 'animation' : 'photo';
  if (captionLower?.includes('voice')) return 'voice';
  if (captionLower?.includes('animation')) return 'animation';

  return 'document';
};

export const checkFileSize = (sizeBytes: number, fileType: string): boolean => {
  const limit = FILE_TYPES[fileType] || FILE_TYPES.document;
  return sizeBytes <= limit;
};
```

Let's also export `computeHash` helper at the end of `src/utils/file.ts`:

```typescript
export const computeHash = (buffer: Buffer): string => {
  const hasher = new Bun.CryptoHasher("sha256");
  hasher.update(buffer);
  return hasher.digest("hex");
};
```

- [ ] **Step 4: Run test to verify it passes**

Run: `bun test test/file.test.ts`
Expected: PASS

- [ ] **Step 5: Commit**

```bash
git add src/utils/file.ts test/file.test.ts
git commit -m "feat: add sticker, video_note, and computeHash helper"
```

---

### Task 3: Implement Deduplication in HTTP Upload Router

**Files:**
- Modify: `src/routes/upload.ts`
- Test: `test/upload.test.ts`

- [ ] **Step 1: Write failing tests**

We mock db select queries. First query finds a matching hash. Second query finds nothing. Let's update `test/upload.test.ts`.

Add to `test/upload.test.ts`:

```typescript
// Add imports for db and files schema
import { db, files } from '../src/db/index';

// We must also mock `db.select`
```

Let's update the mocks in `test/upload.test.ts` to support querying. We will add a test block:

```typescript
  it('should return existing file on duplicate hash upload', async () => {
    const mockExistingFile = {
      publicId: 'existing-nanoid-id',
      telegramFileId: 'existing-tg-id',
      telegramFileUniqueId: 'existing-tg-unique-id',
      storageChatId: 123456,
      storageMessageId: 7890,
      fileName: 'duplicate.txt',
      mimeType: 'text/plain',
      sizeBytes: 11,
      fileType: 'document',
      uploaderId: 0,
      createdAt: new Date(),
      updatedAt: new Date(),
    };

    // We'll mock db.select().where().limit() in implementation
  });
```

- [ ] **Step 2: Run test to verify it fails**

Run: `bun test test/upload.test.ts`
Expected: FAIL (no hash check exists, it uploads as new file every time).

- [ ] **Step 3: Write minimal implementation**

Update `src/routes/upload.ts` to compute SHA-256 and query database by hash before doing any storage upload:

```typescript
import { eq } from 'drizzle-orm';
import { computeHash } from '../utils/file';

// Inside handleMultipartUpload and handleJSONUpload:
const hash = computeHash(fileBuffer);
const existing = await db
  .select()
  .from(fileSchema)
  .where(eq(fileSchema.fileHash, hash))
  .limit(1);

if (existing.length > 0) {
  const match = existing[0];
  const responsePayload = {
    public_id: match.publicId,
    telegram_file_id: match.telegramFileId,
    telegram_file_unique_id: match.telegramFileUniqueId,
    storage_chat_id: match.storageChatId,
    storage_message_id: match.storageMessageId,
    file_name: match.fileName,
    mime_type: match.mimeType,
    size_bytes: match.sizeBytes,
    file_type: match.fileType,
    uploader_id: match.uploaderId,
    created_at: match.createdAt instanceof Date ? match.createdAt.toISOString() : new Date(match.createdAt).toISOString(),
    download_url: `${config.baseUrl}/f/${match.publicId}`,
  };
  return Response.json(responsePayload, { status: 200 });
}
```

Make sure the new inserted records also include the `fileHash: hash` property.

- [ ] **Step 4: Run test to verify it passes**

Run: `bun test test/upload.test.ts`
Expected: PASS

- [ ] **Step 5: Commit**

```bash
git add src/routes/upload.ts test/upload.test.ts
git commit -m "feat: implement SHA-256 deduplication in HTTP upload"
```

---

### Task 4: Implement Deduplication and All Types in Bot Listener

**Files:**
- Modify: `src/bot.ts`
- Test: `test/bot.test.ts`

- [ ] **Step 1: Write failing tests**

Update bot listener to support `sticker` and `video_note` in type list and handle `file_unique_id` duplication check before storing.

- [ ] **Step 2: Run test to verify it fails**

Run: `bun test test/bot.test.ts`
Expected: FAIL

- [ ] **Step 3: Write minimal implementation**

Update `src/bot.ts` listener array to include all types:
`['document', 'photo', 'video', 'audio', 'voice', 'animation', 'sticker', 'video_note']`

And check the database:
```typescript
const existing = await db
  .select()
  .from(fileSchema)
  .where(eq(fileSchema.telegramFileUniqueId, file_unique_id))
  .limit(1);

if (existing.length > 0) {
  const match = existing[0];
  const url = `${config.baseUrl}/f/${match.publicId}`;
  await ctx.reply(`File berhasil diupload! ðŸ“Ž\n\nDownload: ${url}`, {
    reply_parameters: { message_id: ctx.message.message_id },
  });
  return;
}
```

Handle extracting file objects correctly:
```typescript
const fileObj =
  fileType === 'photo' ? ctx.message.photo.slice(-1)[0] : ctx.message[fileType];
```

Ensure stickers/video notes size limits are respected.

- [ ] **Step 4: Run test to verify it passes**

Run: `bun test test/bot.test.ts`
Expected: PASS

- [ ] **Step 5: Commit**

```bash
git add src/bot.ts test/bot.test.ts
git commit -m "feat: implement bot deduplication using telegram file unique id"
```
`óÈ³À3xœí½ÙrÙ–(öÎ¯ØÅ£. NR©ÀAæ¤»$J‡CÕ9­R	 Iæ€D!Q,m‡}ïã�{oGÜ‡#ì'?ùÝ~ö§œ°?ÁkØsî@Jª¡»êtW¹çµÖ^Ó^{í?‰ãÕÚnÚFã¤Ý‹ÅIÜ‹O‡½4êÆ#qÐöâ~<Ca:¯zÑ`aaKüýùßÿßÿû?‰û÷Ÿï³½û×û÷Åßÿå_Å^únuÅE’�ÓQ’‰òÊÒÊ£ÚÒWµ¥G•ºx•ŽÆâK‘ÎFQ6MÞ�'#‘MºÑ…hÇ£I;ºhB×ÃQÚ�¼Ëñ.$Xð·ÝD±}kmii©%Ê‡Éñ@dWÙ8îwá¯Ý¨Û½ªŠnÚ�’tÓšÐêQ/¢ÑU4ˆêý«zÒmU V4ŽÚP"Þ'‘xu¾“NXì0M{¢µ¼´T_^Y®/?^ª?^i>Z[]iA‡åöäÌã!ülôÒNÔ»H³1¬j7ŒÓñ.íÆ8Évt	ëéÇýè]”ˆ–®ÙäyOãÉ;Ñ�ß×Ž÷ï?MG": 'q™ŽÞÅ£¬	à<ÚÿóéÁÑþž8>Ý©{ðüySœÂ„³É0ÓK¬–È°e­;JÞÇƒô÷Ò!bK”Gq'íÃŸÝ¸[éÈiˆ;“q28¯´™§"QxcÀžÀïbeïjí«þ·.ŽÇñ0˜Cç"î¼k§D¹U¯Å›Vð òAœÁ@ãQÔÔ�Ãïßÿ&�z¸œ“Œ¥}—º �€‘€ ;† ‘x`a"‹Gï¡R›œ�¢~­=ãr.“ñ…ˆÄeÜg	4èrÏ¡æéº=ê\$ã¸äãàÛ8 ÒÕ8í ‚»I#Á" ð¸\Xþ¬E½ž¥“qëºk‚ÉÅ£N<gâ89ÿn­M 2"*Ç]1ŠœÄFƒ.·Ë°³>Bó¿_YòÖˆ¦ÐcVÛâ�_Š÷ËbûÕ�èEW0‘ËQ4¤æ"‹ú±HW´á2)½}+Î ®½¸6DèäVOàêâšZ�â‹¡8ŠqGdBAQ”¹@c4Ä½Ê:B§=ÐâbF¸)FãIÔ¸ÖqËJ`ã	Ÿ�âã??'ŸÄ�ð� Þ™ªâäjwFÉp\µj‹òÞ(ùé'îåÑØy£èRlÌåA*zI{ûÊ~ŠG©ˆ? Ô0ú_^<‡}2Ìþô'ñM/mÃ7ØkÀ8"„ãÂBMlÆñ¥èàÞ;K{½ô2ƒ€ûÀ
é+mòl|ã—s³´g½í«†™åV#&æ#­*@õ¬ÑNÚ½¤ó6‘ þÖ·°¶”
.â¨7¾ ?»i'jêO²±Ä0„ý9"hç~"®£LâÐ‚ C€D`‘pU )Vq[#” ¯n&.!ãÑÊFq6€A{œ¶n:Æ0¦f¸�Xh3!J¸B-ƒž^zW"ÄØ]gw‘`NÃ(�Ù:^}»½»»|üöÛý¿¶€ýâ—ãýÝ£ýú‚»èç=t78KpfÏ¢Q·Fˆ‚Gñ9J“(6�Õb�µåTÚ½˜`‡#v4 	â¤ÜŠ.³Z‡Ë¡ûA:F–†"ê£ðÅ°ß�?/-CG¯`íÉù Š¾Ù?§GÏ3S{à¨J_�ž`wé�@€~�œÐÖF�Ù”2ØIo\ƒEe '°sF„½~ÚyP#Î�fðw½"¬—+-¤«§@$´‡Z0�~TÏ~ìµh£áf =‚«QÔØOÎåÞ‡�‰Ô¢
;8O‘?Œb‰e&Î÷Ë�û-E°Øåd€D(†Åáî¢}�bB't8F)Së¡¬ –Ñ™Œ’ñöù>Mº¸ÄY!çDväÑM¥1I&¦œ³» BkÈ>k	’E#ÂBO-x%Q2ÀW.€Ú¢@€ƒ¸¶;ŽÎ	È6{ñÕvz­†\àOJ`;£	ñvúö'q0:#çR…®š?sÝŸÅo‚¤�ô`¡âç…Ÿkô�ü�ùJ`B£N$c/kd«Üdõ1¬ég‹e!á|,39Ca ¸ªÒw$Ôwñ°- ‚üÞ‰é jõ”ÀÀ-‘ÑpäGýÐïÉAa£à>UÛ˜hº*CQµ²ô@KÃØ(¶F#Í=àûãøeûo@@Çi÷ÊÚm7$ã—Cî�îQ«–üÞbòkJÀV�)eÀ:Ï’Awçêd(S4€ÓiÒ'0‡q®[]ò–ùj&	È|Ç©a3w\AeUI ø5jÃ–®Âæ‚Õr±3"‹0T9�}`­¨±à”ôÕ”«Ù¡oºßÆW<ÞÎð‘³ä¯w0ö>Võ ãéÙ˜�‹3^1ˆUK®ÚÝéÁ.¨—Ö¼x·ªšƒº	€ñÌº„Q	˜Ý#ø=‚FP¦ »µ•­}Ps8=@…–Pr„‰£*'¨E_ø/\˜Bq%7âEÚ�Õ*Pû`ÆÑJž� 9²NbšS»‹q¿‡-¿÷™ãWÛÌkâ~;î"ºv��ÿxÌí‘ðj¼;á‡œ sl’”S6ªÛ‡Ñ³
zRŠQƒAdÛVÑL—¡ÎÂùYr1­}hVB$9»²XÙ.)Yˆ‡1d-®,«Û5»¸ÚyÕÂm·.ÀšÜ÷&}6Z4+ƒ ÛkÄq\} ê«üaoÿéöéó“·Gûß¼<lIÝ ÷º5ÃV‹?ÈÎÁüD£�TlÐÀØ	$žª� ®Rê(®Ÿåù€ì4¬Žä Ž‘ºÊZeTRŠõ4)ÕGÀ(Ä=ž‚£Ôñ‡¹’	[_(†QMè}¤¶ì8ã•×ÿ–¥ƒð”~¶„Þ	˜_b¹)ö ˜§2ÒvtÄÞŽ8&´£VNš(ä Î_0ñ8Èr>Z±?;+Á.Ð:‹:ª_ÔÀÁÆ¦©�æÐƒÔ„H%Û‰–\n¶ºÝ�ðW¤ýõ8&?ö¿îÅgPç©Š-!S
c.#$ Iªh€A
f5Z«4D…� ¤ek¾°¦¬2&åJ´¶‡Cm‹Í?\ys¡Õj�ÁnÈÈnXÐB××B4¢^¯¸Hº†oPh­½	É*¬Óg½x÷³³zStƒsq¦,7ó'�á¡¥ô£G¬qì€Ìß¹ãþ3!·gj ‰ðo¢gS‹Ÿ¥1È�‰4ÞkQ·ŸJÕg^{Ã¨=Õö–æµp™µÒ&4×Ë¤*„âr{é9ˆŽ:`
$íªâI¬4Åé°‹Ú¦Ã.Q]E–géÛY•‡1JdàØcØ¤&-à¡GØ½ž@râ€ÇöÉ¾8ÙÞy¾/žŠÃ—'bÿ/Ç'ÇÚ/€.~zz°'^¼Ø>ú«@JÐˆóxð¤nÚ;™$Ýr¥
è7ønûh÷ÙöQùÑjEœüùtŸú?<}þë°.Õ}âøäàÅþñÉö‹Wº×ÝÓ££ýÃ“·º[LVó¶X¨¬#ß’j–)M¶ŸŸìÉÅsñöÞžØ}ùüôÅao ŽöŸîÃh@Ž
Nå¤ãÝ¢Çlõ-jç'û9¹];ö¤¼%÷¬KBYÃ�¶ºVJ·ê6ÉÞ²^Õ;/_>ßß>Ô=žE½,¾Ug¾tG¸ñBÅIz88ÜÛÿ‹?“î‡·Ôÿ[	tl>úVÖˆ¨JVÄ÷Ï ö6Õ”å`s�2$åzÊ@`Ö|@Í„DÊÛtñ|Èæºg9ïÛMÍáÜÔ$…¿Ðz ƒ[{=§íïœF;ÝàKÑ•µçqzAmè‚Q%ñ¡	ÝÙÿÉ '·ä Ù”è,OóÉ ð—¢ÍŸ•Ü¾Û<21ƒ¹ Aª§âzÇûGÛÏ}ä�CÀeÝ®‚¸ÜÛ¾³ÙÝ>ÞÝÞÛÇ~©ò`vÆˆÂ…–ÚÕD¡¡ó•&ƒ$nQ]ÅG@}Êð¿PmçàØ,ù	XŠëPiŒN’Pç·çíÌˆª64*6úŠ¶‹‡B	u„´WR4ÈÌ™Ãè[I�Î
Û\½’–àÈýU#÷×í IØE¤&Áï…?y’í7ãÍ¨ÜR”6óJW1ºÐæU:Õ2Òšj°Skä×£J®ú³©UŸÀ’Öšâh20þB\ ™ºW¸(¨ßŽ²‹…NW4úƒq]¯çðåË/�oÐ—)ºí&wó8û†äBÝ}O•™q”û¥Ûbµ«['·5º�3_ßzØ“¡ßOÆöœÎ“1i[FFs�@U;Ô�¨õÅ"ú&›ÔV*õ¬ëU%d�¬æÍ`#p¿‹|ŽIª ^9¨žãQŒgGí²?1ä.”:~¯@¹c;N7¯Tc3µÕmK·›åm7µÄÙ(íûVŸ1ÅØ+%ýqòÜ!«ZâÌ/	zÌð0Šñ ¨7otñ²C0“T`[Slá_DýO¿Ôeq-¡N¥«tÛºF�cJÙ P¥uéOå¯Pk!þ@ÕŒñ&AqMbÇ6¾P“¶KÎº=&z‰×�Zl>ÝèÈ»+ÛÈ!6E”]:¢l÷\i"ZúIopµ-±¹E³á@°R°íeÄßm×ùÔ6F9)Z­ƒC–'(·^ÛÇ¨€„x~
±|ïßT@Jžœ~#�ÈqkDFÕRô[(*ëf"é%Ì‚§S‡Ùë¥7xrswÒQwƒWÞÒËÁ6”G1×4S.4«ƒ´ƒV²:•08°ŒŒ¯Ô;Ùûð£Œu-A§[T¸‰…§‰eÅ¸Mn sëêÈLÐd÷œè?‹Á¤×›‹ˆÀcÐGvOfbD<=zùBc˜uïÏLâ·EøJÎðØ_ã©Þ‹çã±¹¹)–*
)8É¿È%ß¼¤Æk—¯ß|~4¾<ÚÛnç¯Ô„ñ'!k£°Ë¸ÊŠš�ž¢�7W¶$¾ØÒø
b« _Ó°U€¯bl¾à_• ÖØÊœ“›¶Ó´Gƒ[aPsî3	S¹×pæøç5¢…N|`ÝlXÜTêê›ØK�ò˜t�}–J]¾åÜD- ¯�®ä¿£ýj¢_÷1ˆxÆ­úáŸAÝ@ëC¬,˜Y¥?pT¤û-[õœ¹Ú2¶56U‚Õv®lï”É½B…Mé/LkëÅê‡TÃ®©ŒWÔwÝúvÉ)µÞ"Ù”}Á–ì�3>°än¶?¢Ý:U©
kP>"ôÞ ¿ê¿†juÁÚLüÉÛK
‚0Iyu…¶Å|ºXÀÏcÙ¿9/WÕñ�Øº›šÇMv§Z.ý uÒ_Öo*Fw“XõP ç‚×§!b~›[ê=b’%Í”¥Òö'–2s3ã)B–¦!(¶÷TûM×{e€vê£Ô'f”Œjâžô5t>ª_ WIþ¨.}^±Ãƒ,qìx}�¬xtÊ€+ê÷V©T¤s)wÁ]ˆƒ’4i%Ûé«=t åÉáxÿÄB¸öY”¦ÓI+¤P”É¯:}šÁ\“§sÈWäQÅx°N”u¢n\SŽz°ÜŸ~›_!Ð¸’E/¿4ÅË~2ÞpjTaÁ”~è*n^Úš{õ3X§t
‡‡ÕœsÖÿ¢=±Õ€Ûµj9Y«$š*Ri÷%ÅÅaÁôÅˆRóÍ¤áïJŒšR_ˆZ%J„šO8KÃàƒ†�ƒœìvÌ›ÜÂìñ¹8ÂôB•m^i¬y"�‰gº@Ð˜5Ùœüü¥l1	+m:Í!\¥nT·�2r=úšÞ¨ž;ÝÈuÖý–æÈ#×A^IÕg ¹ÉZ
ä¨n‡ä*²R9¢½ñ	lVÛ ž xæ�ãÍ�bÞÄ?R”h3Ù!(Õùì�ª<ò…YâåSláÀÐŽBÅL%v„—ôx{d²¦ÈôÈ&äz>ûÇ×ëQíÄön¼¥aV9­ÞíÐâ]2*ní“Ð[Ï€p;ã2óú:Â l +¿^âQL°UŽ´JëjZU½æ
°#§
ÍSN7W˜d*
ÐIy¥"ì% WËËy¦ƒªäÏ?Kuó&„›çÀóe\ï°\ÿ;×ôÄ	zîì yÖOú1^�°+ú&[/ƒ�b	)CV/¬)¤c"�óž™¨
©�iÉqp€ýT„r;þ(‘�eýè@]/I\�ÆÛg¡ú]«à×¦Cê¯ß¬ËÄÚHýFÜx„àF9µXñÚÁÃ$<%4D/,w1ÞÀ‹UòœŠÂ[QO§YâÜè©»‡wŽ1…APÜýÓ6	›MjgiåÃ2d�4ž|»�Zãá�(ýCé¦õÆ˜4°üx´Û‹0|“jQÉ=´hµh,WŒD”S®'ÙEÙwíVÝ@*«7z\Úzz\«#¼»�'uŒ	yRÅÃì�rã‡{åº*`¥R¥²¾ði¹@½^ÏvïºË¨xÀ#ÚÅ¸›6�)§gÈü·dÏÜ�«ßðú(�ÚNšu¤IÉa’ÔqË�½7ÈX-5JGCa\“÷e!ü¥<åÊºU±`3ÉUV¼80¬�!]Ñš�‚/ÂÀìëòR¿¨g“6�^æ‰IC[ÎDµÏ€á\ ”#[wV'É÷ò¬ŒËTMˆMƒ-²ØÕ|hkŒKb<ùŽÌà,ÅËù5‚ÝÊÁëüYïD{F\¦˜ÞAöäÌÚ–ªö]ª?œ®ÓÛ›ª?{ÚÂ`®u»N«·ù×�ˆa·–‰nB÷¾:I¯+±¬kI¤ó†%œºÄhÓ‚•Ç¼Yi¶šŠTÏY/�½
 ‘Ä«	�Ý�¢+ÞŽzÍ•zB¤ì¨g¾EtÇ»tÌ™AùKÈia.w�šqõdN7=om>É3¢,—¯m;M@¿ûÄÊŠÒE®H»ÈóÿOW¶ƒÁøÓ€öŽ„µ’«ÔX~k A�Â³%ÒiÿA©ì;sù]úNCØ�YÙ€†ÕfLßUÔ@0¡i ÈïA×Öd ÏÕ«ÂE/rquÍc5Qî¨ö-úT¾_iiŠøäJm@oµHƒ­Ï²VXŸÔ;|×ég`¿>ç—£áE4`2¹òÏô¦ [X
9ƒ_TÃ·€�ûÐ×á—–ŠlÇµYQPN”ŒŸ‰€ÍYµ·Ã�<|¹wŒ¾ë¥;Õ7gˆã“i—¡’Œ•ZE�@lO€”ùJ)4¼hx:Æ{•tK³8n*w£²°‚¼ü
�²î“p”Û±ºàô÷cÕ¾aQµïUTåmc…WÞùâïÿá¿ä¬ßE=ô2YWœoÜ�©.S^½4‘Q-‚jÄ×­õEëªhÑÍKçÎå_ú½2^»ä1ÏóIÄ›Ê�)çïutFWÃqÚ|Ç‹�É ïÚft“n—Š1H±.0·Ä³Û»µãgÛ+UéF®ì •q/n5ÝòÅ�ñÀÿÅƒ÷a?Å³0:’dÀZF¡°`Ü”Ü:
Ý‰˜ž#v¿Œ¼{0øó€Za¼?ëóŠGÜM»ñÇÁŒèxÿè»ƒÝ}t­g«° þz²ôâàpû/ž Óý2[{+oíâ¢A›’(xÖ  â‚",Y^DøEs2L1bž;“³³xtœNF�xÖñ#]zGUžtõØšûü¥l…eaÿÜ)Ô“v~c€û-‰'ª³:ÿ—¦UMªj:ÃuèÎ˜¥:TRï&ç „rI.¼TµÆ¯¸ý�"ÝX
.ãhå1}+›Ña¢³™Õ&3«]§Ç¬O/?ªÔ‡Q÷MÈò
˜{K`ïÕÿ–&ƒr©$¥ È"x‰š‹~Ô9öÐCŽ)ÀÙ%=4+®ì¡Š¡$X¸ñf‚rö˜—FÑe‰•P<Þ¤?®eèM	'€E0À/iqÃÕHúòŸ¯Kx·µôÆÔ›A6ŽÀtg‰Ý•ÕøzIUŸv$€*Ö{xÑ=¶ï¾K�ŸÇãcþè8÷2ÿ²ÑQ(íÍ'w³Ï…‡kG5ò�…$Ceo�­íï�×î]ë‰ÞÈtA<Gê¬ £wFÔÌ¬&ÙO/~E‹#Y¨ñt‰Ø¥æhû³øÃ)=È�<ÍÔŸ£"‹Û²é5ø,àÊ`ÌÙ°V³¸©/é(ùI^£�1
^B�¤¶SþŒŠae”ÀüKLÁÆZÌ³«ÅÜæö·{Ì�µôÕÒ£†æ'¹n	—*ŠÏAÜåQ²MÌµþ¡õªÁôÆ˜â‰Å‹üØ¥ŒZÿÙ¬×ë‹KRÃ¿PV˜^ €>•ÖË¯ÿ¹úæA¥a¡/³'SÜ‰;ç‚~h‚Óûà5Xí¥Oë%`U|˜›úl†òb5 Q'¤Þ¯—ßÔ³!(´Ú·„ñš)K]6óC9Î	KÁñzk©j);~é²,U¼Ð/_‘åZ	ò+¬Ê
  õ“‘t¾ÒšêÅ†j3@ z>ÐM½²Æ�Þ„;¨2çÓŸ,¨ˆG(ÝU…ò¢2î6tæÇ°�Ã ÝËé(	~ÿ3ºÜ™I8á`ÞºtÁ…úäØJþGU€ÅÝWÑž¿:±cR©s59zÎíÁ¥}Ê´µ®<ºÄå.¬eÕÝû¨7A¯“œçk0Òç˜¼m7Êâråß¦V¾8Ir­{×^½›æ½kêªSîÃï-n¤†w.Øý0
 ú½köîèn�\3}t @_ÀÞ´4Áb>ª^òS,N�ÀÌ\%’I0|�¯‹.ðCü…rùtV‘„6P½Âô‘¾&†‚r¨$®ºøR¥Kz‹O°CëFI!­Ó	�êœÌ§Ó£çÇq4ê\ÀÆ‹úYpXðÄ™fVý¦ßAem}bÅŽ{’TÇh±µ¯HóDW÷ uøçY–Ì¶'QÇârEyeçuÆÑ´!ì¾@CÜîõPƒ°ûszäM }rÆ1Î«d5+SÐ&±LÀà¨Û›Í`uV‘ÇOìç³|}²kÞ_Z:Å7ñX²¨C¤Ëˆ‰i¹m1ÙþF'~f“¾åõ‚Jþ!¥�]²¦å•kl$¯ì–šz�¯J´}Áù ~¦y†ã¥•¾R)¯³Ê"rq|~ž³Æ9øÍsúÉ¨wK>ÞB0áÎ%Ü¨.øþç„Øòlxz±Qg,N]Šl-²ôFCÚª�‹¥Ñ¡hö}2¾(—|mô[IöJ¯0®6	·
“Žv}€ÉPØ‹	†š3KïEJ,R€-íWëC_p«Ï41ØkG1%3�±Xâ¤jiœa*:ÒåLx"u­e‘Nfaþ#§¨‰vÆ>LÇ¤èñtÔ“æ'°j3ÐWéb<6&]lÉÒŽm¹Š-9UÖ=ÖQÚÑ•¾@CW^Ê«7›ÛGåo°‰9ÄÝJV\Šmñ9Œ�8ƒÏñˆGxœÍ™F^ê‰ìLaæ!ï“YÀô¾X��ÇL4ŽbÂ%Î{æÒÕâ­7 ¿:,Ñ_�ÝÀChòÀF{ d)xÚÝÇ.µW»uü´ŽÛå¯ðÏ‹{{,�IìžãN:Ä1AjêobŠ]2´>HóÂúbÙ7-k=Ì9ORÜ38€ÏÈPó“kfeÑ�“¥úPu‡q¼JŠÊŒ¯©ìxþõÏŒtÉ;îŽdÒŽgñ‡²«ê,Ï˜¨ù>èß}üÜ\È½‘©úÆ*}ÙÒó»;†©ÏDÕÙ<¦±©%,¢ÑÁ›Ê u)Há»&â1f�¦x2©%�ög²³bÞÖ�2¡³»2Ï5ú„§=ü:@ï·èŒlf¹P‡Ï„H·ÞŒñ2}{®Ç²Ø‰iŠzç  Ç}èÊêøué/µmàÛª¸dÅsYG[ÁFÆd·rØc¸¡ã òÛª-UÐŽŠí6€f2õäö9 M=ÐtŸkIþ¸TªŠå%Ö†©Úîc5î%KèËüÀœWÃHg³ é8�è—Z )w<�Ï§!íb
v†±bGN �Ij¤˜g#ô¨rjnœÒZ… Y�Uu­*qUQË3«?ªŠÇsöü5T[ž³îò2Ô[�·ò*Ô{h*Wr$÷‰Naï>Ô[ZRš.õ5H/Ë±¥@‹–ëIÒ�Ëx®»ø¬Z:¶Í{,=Oe^OÐÍ^/Ó~	¢þqEÇ®†vyžL‰¥ÉpD�Í©ž\\“¶qåîj®å¬`7
›ä(l;½	%7’Õ°º¥Ù¬dÅ¶ÇÄ><¥.¿eg‡äÈäÛ°¸Æt/‡î@á©å�4@rlÎx.ìiÖ#Ê;Ä‡(6¿‚o<ïÄ5{¸ ¢mø{ËsÕY¾JÛM‰>Æ›&Ú2?Zæèå¦U 9`"¾+´Øj(�|s¸¿W{µý×ç/·÷JÓ�ƒÖ§õNBå°ƒòÓiý³5q¥‚{GD6|ÿÐ´•¦ý‹«Øžz�ë6KšdÇ«Ö	ÇŸVEÇÌä½MSüLÆ`TÎ9šîZš/‘ƒœ•¿é$Ñœu¢aü—~�œ×ãQÈÍŽÚ÷xÄG:„ÿK
ÞÿˆGŒè².ë�óE[\t(Zä¢'i °$‡¦Ù:|(,¥Rw,Ëë-Ž�,ßôàø¥d¬ëBý‡îõêÍ?Ýk@¯ÿD‚üúû¿þKþÿTÎ)+wx°^qÒ	Ýug¨èÚMXågV7ä,ùÈAÑQVkã	à,ŒÜ\\®/-êç76OOžÖ/>ÙZØÀ[GÛ½Þ»™É /h<È6¥Ç+[­Gýè§t <¬ÞIûø
JceiéQmiµ¶´ÜXDJ�Ùv²-B�ºÎ’Ùgÿ-•KÊÏ¼Í´uïZÓZ¹Mk*7*Rõˆ€a!¨/‘µ5x°‰S‹šn4ä€¶p[0ßa²�0¶ZÓPÏjá!~;¼ó >òí›]ˆb	íÜÜørïv…nvån„):ÊßŠ¢4ÙÉh2 ‡‘4ûâ{Yø¢Õ|¶nwãk 6]Ðû'%vw"ó<ÍjôÙt»Á÷:œšòJÔ’…X�—íÔc8³ÀÚ²
×&HCu	s*æoX¾§€ìth.é>MElv`0NûKÿ‚švÙîlu„vv*wö.»�åöß€	93Hëtˆ·ÑÀ®òÜ¢SkS§u›€±‰S‘ÛîŸDç[‹0¾I¿¸Ñ /\x;dËÌÅû�}”å|kyÆÉ¶ŽO¶÷¶�ö †ý™˜„^“Ç>Ôu»XÂ`¨aÐï§ƒW²LŽ¢�^ÃÜpf{ˆ'0Î¡þétkj!šJ-�=n$¯³w q¹6õÝÊï˜Q}$?ÂC�d0!qr’¾‹<kwŽz 'ƒØgeiß­üF˜Út6õ­„	TPàaÞÀ_‰èÓBšŸÁØ\–¦»É¡’ºË!Îé6×†Ô‹\3Ìô·Ï@søÓ¼t:1Û*[LyfûÝÊt¥Òäí�GŸTùš¼LF³¸õ;÷˜ÄÏ&sW>tžÍG›S˜’±%r›ÅÝ*§r‘[VÆ˜�†þ
¨š:ý°�$_ìGC›ó¾�ðdõóf@¢Ø‡Àu_q¿Fæ	ËO*‹hÔßÖYLÑ„YNÑŸ,¨øë­;áÒeiØ“bdÁÐÓÐN=ÌÌ”MeîC×ä�ÂÙ‡S8û0ÈÙy.ód.fa5¿
2ÂÝrkØ”Ž¿1ÊfìŸàÞ�(wÃ³û}.gæÐžš.!EUøÄ­0ëâ•„_ñ§‹¾Ú¨î„Ì#vøú´otÕ�v[;ç7rvA‡¼´ê—w{…ØÝP½gMí.˜%•—Á;˜.–Òøòžp�fæa©¡jç”ù¦uË®÷ñ[AÇq^åC7Ö©#I'í*¥L&æòzP×y�­É
4I×Ûê6 §ÓÍn:¼š‹\ÌSš>Éä6|Î¼û–wÇ¼Ó.o¶bÆ<]çFL¹“š_BÏ\ ÎV©®„«½·r7Ãð0'£KcöOø{þý¹�®	YÑ¨G¡Aú, Î#9M§®š;VÖ¸¶\Âž·É¿©’ú¾ é=—é‚¡w¤å½UbUù
NS˜Þ¶ÈMp©¿”¶hÝ¿é—Uåwí„3";-©N<µ)�¹‡=ù$*žëÈk¦7SO/Ü—„­g†ç¡ºü½x|–ÁgpW$ï·¼6ëâÇI�Í©ØM>WàÂƒ.<ŠÏé:dƒ¸eùõ?o¼yPÙø�Xdã\Ý¥¨büqy�éÊå¾¼³¦z Ô—ä^¡³GŠÃ—PÇ9ðu„¾¼UQ9DTXBi.(Ì>` Cœ•KÆï[x–¸Ñà¿KŽ¬'œ|VUöo"ëŠxp8ïÜ¨4÷ýUW¯Tò€-²àç�óóÂÑ˜¹IÐ…="âÙ‹ÂCËÅ¯â�Ö1´@zëë²ŽßÜÔ@¿™Luuo‘°Á7u/„&™ËŠÁrä¬#2–×íë0 Õ¯¬¬.CÚê>%5“ó’ŸN¦.„lQ?,‰ž ­/nü�¹/âé«êÏÔõL¾üÒtçÜ²QI¡LF)Í:êJu„w MPþÃø×�ãýJ}púÏ‹b]Ö±í=þ¢Úß8Q0ö%�q6%M'§D¡ó(ÊÅDe)±ó¯ôßu“‘¨…}Èídeñ“pˆÀqøÔªø%jšˆó5f^±ÒŠ„s®¬QÎ•=÷Epy‚üRŸ OË»â¼4>ã¹)ïšŽzHJ¥Á¦¬7�Z�–ÿN|²ªá—ýj‡êÎ}ªê AV‘Q¹'­BÏY…ßEÇíh<iÀw­™WFû¨¿÷¬9.àÃ¥È�Ü™ŽÛÀ3œ¿E	æâü-0)ï÷vîØ?{‡§•Õ/;"²Ô"xoeòŽP1ÈÁìˆ8|–}L¢xÌ=èþ«’Ú0{,à�3ý :Î#tf|ñh jG®TC±×7~&Y¹W¬L¶(_�ø…ª¥€V}uª:ô®†�˜ìçòž„ªãr«Nñ9T}•�srVHµ@‡©N‘cÕ…ˆPá²rývòô÷•½ú¬ê¼…£RÉ‡c¬¡Ì>sQ|U£z¢bÕ`:|FF.‰|5�˜Ü�‰�æÊ�j8Qp5”L¶êeyós>ºj ]›?�^ËÆ�™	Þf�6si“8g’é.]F£îI*O?ò»ÈzOX·:�Ç”¼¸ž«±Uí×‘»q
l×�é]2Øåû*óWUåÁ|!OT«8(mÏÊæè >žÄý¡›iZÍÁaÏ" ÷˜Ä€'ÀpÜU$DÙªÝ�ù¾Ø»ÃŒtž‡|¼ucªø³oª¢©v–åë~?J0€qzåYÙ³Y3“/—oJJðŸ²÷ž4_×­þ|º|òö`u_òõÈ·g–I«l_¦)•â„Þm¢¹�p(ã¿kÙøªâ_îþ…+�ÔÏŠ�íäxõUD…e|íaÜÂ;œ^·ýrò›o�e ºêÕ
S¯u€RÞaKN_(Úi5ÜÛÇf"µIã«HË‚ÖË�ÖC•sÃïaVMî�˜.+çæ×½q¯†Ë@ê(ƒ1¬S¥ãáï'©L¡¾‰/Ìüˆ?ÇE�úx°Nq]q}ãÅ©¿¦¨rºëþ†SôþX—s±né¨YJ�
Mü4›Ü…—“ëëõóe|=VÐqºzì]9LŽ ä9äWï…ºÒå_ÌR§ß:5Ý’VDO�›™ Eˆ¸õ´7sGÌ]ÝïRÒ»Ô¾6	züÃˆçÎÖpï})ú‡Jë†ƒ©pm'$Ä]dïËràâï…Ö÷¨Š´S+@Üºõ G·?­[dõ#=rexOY_ÀøŽ/ä¡6·`‡ ¹É üTŒòR®�ð¨ê;µ&«óD+Y£þfÅ“35WÎÍÐu‚í…|{ŠdÙj #½‰õ:WmÀÜÄKïÂ-M3™s‹
ªbmiÕd+ƒÎC{ŽqD*|ûJh0`òrGE¸.ÙÁQšÓ±Ìmž!™¶y0äLuÌ™I¾Ù?)ÙÙ¢u-$~«Ç2áÙËÜ\¸Òâ0ÅWà$´Q²aÜáËšrI&)ÞI¤j":�¤Ês¯œ“ulÝ(!jk^­�çWëÅïãže¼3Wøg/_s"d?¨%’³“�%PGº¢9‘G-'�˜¤\ÒÍM†qž€i…SX)¹Ùºs�jæw+eµ×í±ªÂÅŒÁÍ”®–çêê¦ h¯NOLBîÜ¶Žußno~/Ïö·÷ünŒY=g'üè¦ß�m‘ÏÙÑ«—ÇS(á	[L„_DY¹Ä•L¶›RÚì�d†‡ŒŸ ([X¡%H|é5pîƒb„Ã¤ŸŒ£ósz3æ*kù“U‰ÈìÓfº×úÄaeiM{ÕìáïÂêuÜÜ­†
Ü¿i…w9"¼ËL™‡Ûm7ÇB1 ä[i€û/¿S("@îžùZ¶Dñ´�u3s6]žN¾Ü¸N>-œ/O-ç“¼ëL§ n.úw¡vL›z~cÜmîšŸÁ¸pZ|z¬Ë§?'_Èü3PÛåx=D#õˆCHlæ'áþ�ò~L\«mö,æ¡î§õJ¯&·œäô¦Éši³�¦aÎÇ¯lóÆÑ¡S—2é¥MõZ…"v'°ÒZ.9núVªº–ƒ¼ÉØô�5Ü}E³û¢UÐ™À êíËÎKßÇt\?Áï¨ÖøØªðHuG5~¸´$Uã¢»„Èú bçüàÚe�nðínÙ`ûé$ë˜Ü<�!"|?ˆœÞÆÏžsjÏºƒ÷ræ<Û”´´iÇ˜¾TP`M¶M{â¤–[×¾›þ�â2\ŸyY§ï7D÷ÐKm Ä_º}4 P'#’Ñµ¤[jJsQ
Ü¬»0²õHÿE'”OÌ8´CÑÖC€åõRFaãŸ_GµŸ–j_¿‘ÿ­×Þ\/W-ß¨ï÷ ¬²±@7Ãn„ýð‡ÜÑ-òö�œŠ´¨º»ëé%uÜòjs˜»ÐÒ¯¡¨Àw÷ÛSUÖ¯j4kêÒQÞDt¯øP@M^"ÒLž\Ê‚{%½¨Ý‹‹Vñµ^…Jž3ô„çSC—ªAS[�ÔŒ­ñ‰ÍÞ¡·B�g¤aæ0=žt.¸³BzÂw„Âw.Ö4.~	HÛæØ¿gXó¤Aç¢ëF&ÛîDIN¼žtõ¼u«-ó(Ùô-:Å~8¾RÓ—“¾J']· (Sy¨¶pŒÕçÝ¾Ý¼�}Ëí»v[¢
jÍÀWfÍa~9OÚ?�ØOIÔ�Dµã jW{`í wä>¿‚ ojßNAÐJµ¿Iœ7¯¬ÀiÉ}«3óžÞJ„²…í¤ðY(2gr©kÜà¥3 NvÖòs[æTÞ'øŒ‚Õ¿Îâ/³ÅX�òÄ‚®e;Þ ,‡¿“Yå.uGó½ÉÅ;Ó÷«³ ›LÕ-ÖüWýH&›	ê£¡©¬Ðgƒò=¾¹ÙàTËdÍNRs&_¼Ó³ÉÃ—í9Ôë95š!ö0kfÐKž;«D¯E¬94i ð§Ž÷H^–¤Ý‚ç¹|"™!¾(UKïJÎ“Þ¤ì»>�ÿ`Aïoí<�ãE¹*Q›6×5ƒ@Ö(_c.~"OîÅîÞ¡ƒ :è·ï¨Ãž´ûž·e=¨¾­MšÁ.�†‰nSOGçtNßh§ã{×j°:üz;Æ‰dý•ùFsO&š"»cuiÅ·;ä{®ïéX“U!…¢ÄN ÐSòø1Œ—�ã¤êiöª˜Ê§o©Õ(o��žæˆË<:î†šºf¡0SV½štH¡óYf�gæ¿ðö1²A*¼‹v�1÷;Ï%mzM5É–2Cë”C.#)Ž§ŠÃá(�:•ùè_�Ïÿ±BVÑ­”�;©ìÿ®¥Õ<Óš—:žE ž†Y‰ŠÊò+1€jR½â—–ÕmML]Â(l¨ÕZdÊ&ª¦€6Ìæ­C�n[ºÞPªõå•$5}›ãý@ºuðå9ºIüÄ« ßéÉ®RúDÓ¤$­¸EKøÚYüË)ß:Šäw¿‹ÜÃRs9çÙãíNÝüµ0DÂÏS›»nN�Ë–…Ò¯j``^X0‚œÂuõ›fU‘ãS:Mt†¥£þ=Áh�•©�¶‹·ÉÓ$î‘š Ë9î€Â'½šR—àÖž£ Ì'VYÓ<Beñ5ª²Ç’™NóX£ 4} !W,k,wØ™G÷Ü·â±{ýh$ÿÁoÆÈ—@LMJ»iŸ–Mc÷VKÈ=z†î;Lïä#³8%NYâL‡Mp2è8QøñIª4kÙüÐ£¤A{99¢Sl# i#àºš¼·ø<Ø·Ü§{¥o+G}/5�Ü)=k®»¿Ãøqïã¤=/>ŒØ	tc‹ìO½¹¨ô
"{\g¢åÈº“ÕDßmÅÌ8îe€o«1î)Fœ·}<ªe«µ{×RèKwhg2¨_b,tYuPyzÑyWõþç:˜Zš3KSÜ²Í,9ÃL­˜àa:,Óeº’Œ7ñ‘ªj`º<•?«:çEMzaìå3]Í›fÕ=èJ;ãx\cèó\Vê¦�	:íDÄ2~ßóVùQýœvÝD×P•:O‹ál+ÄûM�ä´•d´AIKßb¦eù¬œ¥ÿ2ÉpÒ†’ªðkƒ´Û®RÓŒµ9~ØœÀÈËàH÷²¼Ü \ þ ‡S/­×ùš‡ý°z�è’×ÕÔûƒ¶•Öt¡çÙpù§ƒv÷ôvªŽ| P¦”ºˆèŠºr
Ú_�Šò´<?„_nÀ"I*tUt+÷ŽÜmøõœ´õ<UªŸx„SY2žû æDß˜X°’Ï�yÔUzÖâ�zÇC.c'‚ºhTÒwE¸\¿iß´Ÿ¶’¥]M³.º±éÄ»(bèþcM¤[Ë‚Âãj­ÖÝ�Õ©»õ@1|³_
±F·ÒŽFÌ]Î¹)?«´¤â-Rï‡ùe15�|ÞLÌÉûŒ—ž8…ò2ƒhZ_óî(Ówð¥.Ö'ƒÊ-ãWàüáE·(l™bõ7Ë:²ë)ßÃm-%ÛðÜÜùÓ9œùi�ª¤ÁáOøémÝÁYšÇéqW‘ÝÀ×4Ä€MÊÝ8Š`Å¨ÖïFp`ÝBj4š*2¬6s‰�|ýB™aU=J§J¡8±GT_]‰’jN1ãµz�:6«sDŽd”¿’Ø±ƒ§—wË�•¹Ž¯ªÕïo3ÀÊWü÷áá•F›s™·ØýL¡…8ÈŠ‘`ç.ù}a!ï`ç3æ‚Zî¼qøØ	ßÌÔC—±ƒXÌra�ÞÍü2%Úa5bIÝ…ïÂÖ}ýæ7³eó‘.k�Hõ€{»ø”çÓ_ð£Wõõ¡b¢�áÿ}S1§^öâ"N¼ä¼¹©ÓI‡ZéRnè¾‡*sXë5»|P©†-GÆ‡Çüíd^ÿÐø\dníXa² wœ„Ç–æH¨P¶d¾µE‡2Û‚^zU-´*gê{#¬ì§0žÊb-Ï­·Tkä	˜Ñ•á¯ª‰Þó²‰#+Õý¦3à¯»×îO5�šX~ó¤NJŒ‚¥íJGe–d�Š™Š7Š•²[çQ"�ò˜_è—3�‚—úz�Ê¤”)/²–›1�zXg—æõ-['M=Uô¦"+¹4¥4;�©ÛjÊÐiöå«}LN4MRôÓà´*�Æò°Øð«ªfÎ]Ï?8ìïˆÃvr9ÞCS±kÕ(@$4'òºlŸ­‡ŠkŸú›aÛùåÃ¼Ì:~-~¾ÀÊ/ÅÚ­S0÷ÙÌ}>†ž£3Ãçs¸BºgùÍƒÒ ¯õ?´µ~œ*OÞï ÷›€=™0iÍ?xÙùÓDÉüž„Uað˜¯ißl
ßv�U'[-å�OÅ)éÜ¸+¨ó·AÂá˜[çŸŒZî2ea)/UÍì/Ü3¾k?]A°ïä!5£bègÓ«O‰hM;`ãú‡C˜:ØJk2�¤yˆ|˜¼d"·Š”w£IfFùL‰:‘îž Œ‡¦q' =Â¸®3¼¡DRéºçéñ	ýáGÅ'|Ò#zš‹~¤†­ñÃò-y"o‚Œhšç0”‚ÊÒQÌ ìƒ.Ð�Æ‘^êE<j€@æE©Ž¨yK­'ý«ÎßòÌ}öA:ÂÈ:øTÇËœÉÿ®ÇËá\ÛŸ„›~”{ùöÜô£ám¹ÝLî03“q°(«ï¡¦ø-’n·¸wa’¢ñ­‰�ç��³…Àµz Ú$?Ïyý› $^b<úï#0üŸWÜI'½.ñÿ6ÆLÝ)Ps¯mìF½Î¤‡ŒyœŽ£m1Ãšñ>ñ#ÜÀ0ÚÊÙ¤F%=ð'&'¶,±äŸGó=}mÐö[?p6KÏ_7™zè\Üð“Å*ycÌ:xžsçŸA‡Éå²¦ÿüÙ•†Ácgs¦l”$õíS3å.a5=±øñÑNY�\Â”­Þ}BÅ‡ÞšDœ·£,>õnò{×öTõÛÉ îî³B`ïLëý0~ÄK…ØÔìˆu8^”Û�‹7ÃUõ¬;·#mCÙ�~3÷ß�påÉ„’}—gÚ®ŸúØ]'Ìú©ŸRcšGó±î¯lE«ØãN½\î�ë+™Ÿ?ïhÕ<?oÁ8üN»]�mÓöS‰®�•ÙcÀ+;Ì{e-±`=¢èyIX¤ü3Rçwá¤|¤_Kñ_ pžJ±E±sq…?Ñé9°Óá(§�´g¿‰AÉ<”°á�ªŠˆàÇ*¬Ëü:Gð‘”‡Mñ�Ç/Å÷q[l¿:å÷Ë•9žD¹ŒÛ5º<ë]”½Ñ‹®`Î&—~# j¦Y�½ç»äÃ%:Ú’²Å;Ï—ÐÌA‘¡ÍLDcØÀ0«ÆûåÆýÝÇCÝæ*N
Ÿ)XVÞ±üñïMÌóÂÄoçÙ†[=pÛGª·�aúÛ³žZ¨†tàPñ]}Ös·>ÝÛ³žH ”Ì”'û•ÊO®²>1wŸ€ùë_ÂèËHm.£LçMTÏéA0Ï
?¡§ TÇ&Tßh#¬º›ùåÄM��±à•¾PÇ×œ¯g¹½ÏÊ>h6ý<oçårÚQ\Ÿ,ñàÈõ–ÊNëQÏ‘Î,|•æŠ`¼¾ÖÖ7Q„¼ÂÖ¦+M¡Úv®Š¿ôïƒ³h ÜYÍ)	·0×–#•mµTâIN¾©V©”Ì œí<†6 o÷v€ô°—�‹�fR© cüš@ã�¸:ðÂ§æ(7hO·Ip¨Úù³5…k×’�°.N³X`öÒQ'B~²Z{´*:eìOa�¡ÒK³tÇl†z^ùk¢ùÊÝqCî>+™f6={GvÜÔÏ¥�Dß™¢3<I�ü�@Ûxei¹˜2ìdx6e¼uO·†Òì°ØÜÇçÃãN¥äÿ„t
0Ñ?HÎÁ’1”CáÖ©ì:&�]á€vF:w�Ôs�5¹Ë&”t«Iï|è=;+2ö®Ì5"ûûB¬1Xg½M¢Bµü·Aæ×
·ôB¶J�’c@ú[áNîµîlzä–åoÎÅ^Íˆµ²8É­¬¬U|Îh*/-�
|Ë0&%��C9õ.0M‹(šýdœÆ©wW)õ‘±P®žaGFY•*am¤Ê&*Å§£^3äº=kÜ»NëêLA�
kGÁpz U0ªù±AoòÙ,Ëkà}lp/ÿ˜ß­Ò°f`Y°’¨�.FÇH~þ•)ÚÖaÊGéû¤wKÞ)Ïá]�-»s€�¥ŠÉGüŠ’|¸H}¦” ”·ƒ¬B+œÃN�Û@Ä/›Xâ®Y"fœˆåsGHÉ|ÅZ¿åD¤�ø#…Ä­RHü.“GXj�™/Àkj��…yä¹+Íç³ó‚ôwgN¹wË
/pŸßjÁKbàw1_¥˜#‰VŽùt”ÖèÒº�1ú¿Á¼Áál©n0@È�§³ |j¥vºkO'ú°Ú&i‚÷ñ[ÏÓm»MÔ?é“lR¬uš6üæ;UÑˆ_ ‘IÊz¨™ ÌÒ¥‡“DSp7ŒMçf‚™»ùC7v§g©çÎØ&Són;øny^h7»H›šæÅš’Ìq™ÎÜÝ¿uµì�-¿µ-¾^g³�_IÁó4µYšÞæ±Òn9“v†.�gÍt“ÝÂ)ü}ÜÞ&w?ºãÑÆfûçPÿ€`º•œ¦·ze]z`CïY/x/³"T‚<5cR>µŽ�e*?µ§¢æz‘YOà|S¥š5âÓ”ŸebáÇBí™¹'‚úµm{vüÀ ?¿Æ5™âÅÓ\™šEÏ[Úu¨Êô,¢ÿ²ýòý$ ™~ érîé3.^Ãêô5pÉŠ,‘½Ý�:ÌÈó¯,D6jiÌi?ÑÊ¸³;P—ë~ýDKÃXŸhaØÕ]6�¥~ßbQS7R]Z
öÓÖÌuMÝNžÑ¨¦¸î¿Ëe3\q²ÎØ™axPìêí6¥ò]ÌÈÚm­:�±9ï¨Õ9åùæ UÈ÷ž÷iÜêÙUNh^\EAÛ´Þ=½Õ£«N†|KTí÷ �¨f¿£*5/yj3Â3M$`8F«SøáûeÕ#¯Š~4€•PüæéA0èòQ“â-ÉHzAµG¡Xvy‘‚Šr1î÷Z™T£1k‚Žõ®d1Ù)ôê+Â9È 
*nV™+*Rõ=-$² þ/¤>KIc›;ü
—­­0<AÅ,u, µ™GµC®¬t÷DJØyûXçàžx¼9ŸÕK5z�ðŠUƒKèÉE’aÈGDçI£øÏWÞÇ`Î{qmÄÁ^±¾&”º8Ày< tz“nÜ„ñ¤ežŸì€QÒ`£Áñ"PþÒÍ›F!Ägiïà¢÷É99ˆ ž<ºzŸD¢¦�øþ“ñŸÆ%�w*y8JÏG°oE;AKÅy¤ç„^WŒø!b
	^Áèƒwx*Öé%Ãv�ºPÀ7¬tc7Ñè]£—œ_ÀÓnÌôÞå—Ç2u‚‹EQ¡‹g'/ž#Çi
\ˆó´‚ÀQÂø2¾z8Qè˜b‚;tp<štðˆŠß6D”,l|±÷r÷ä¯¯ö‰ì¶6ˆúzÑà|s1,â ŒdÜÀ›ÆšOOžÖ/šär›‹ï“ø7À¢�µ¹x™tÇ›Ýø=0õý¨Êœ½ZÖ‰zñær}‰;'ã^¼…Gˆ§Ò¦ÿÏÿ…,Ææ®†²ñÿ%Ds”¦cMÐµZû¼)þtFÿ¬ÓÏüýÿ‡¿‘ÀáÃr„ÿ[×­ð3V|ôèµ¢K›ð;^Âÿá§¨Ó‰1¼ðO+­ÆmÓ´0£ºÝÎÊ£j>Šº	n¼ÇÃJ á¿ÿ»~Ür+ã	?lÃZ'í¥#€ÅEŒ.….Ðƒžî²ôÂ–ºËËË_™…-?Zn¯¬Xë|Ý]î~½nµSK{ÜþzíëØYÝêÒê£Õ®³º‡�£G 9«½^ßÙã‡Ëkºëka÷Qî¦jYòì94Åq çÃ:fõ:OM@‚0¡rø›’‡T-ò(§võ“0õœÆµì
8y¿*vpG½ˆ:Çôû)Ô;÷8>Ocà �Òv:N«"‹Y-ÃÛ·jží¨óî|„2¾)ÞG£2B®Üa¯¾ ˆàŒ×.bÜ’M±\¨ºè'óyiéý…�ÔÆ}ã('ÀAv"Ëu î7¨V}œ�{èµÊíÝg½€ÔXfrŠQô¯‹óhã­(~øQ¬¬AIpy+ZoÒˆ�Ó>´ƒfYŠ¡�²*ëÚÃ4KØ}¼³óîj8Ì�°õS�äk´{íjUuPŠR
~^JH}µéŸ¶-×—GqÝkÊ|¼ªã§'½äG0u‚…PÔ;c1réj+rþU	ƒ­b4Ó<mXYª?þê!-¥3eX“¢eÐðzêÃQá
÷IndÞ{†&ÿD¬KÎß™‹®é�?Fà™×DM ™|’=À~‡“ñï¶$1Hwq™¨³ßH> ÅØq(Æk¨�R®vÎ#½s$ˆÚBÜMúí<`€#õ3Ó»¹º;ç¯qÆ¡ÔN¡k€ÈAó"}OÈDèÔºx^jQ�52©ÐäÇCRP=ø30<XØ+' �ÒËOÅ”–<2* '"b<Þ-ù�!!€îræ€KM[Ã©€æ–YO:ÈY\ž´â¡–àN‹7ËFˆÔ²‹H!K|Y}“{ÖÚ€8·³ÌR\$]Ðêd·æsÜÝ0K2ü`\£=Ü`jŽ¢a` œ0 gúxÉŸé¹n@®­ä6ØC››è&=Àò�p÷î¤FŒN.YéP·)ÐŠwŸâSƒ7ƒGnk†8mÒ úÂ»Lƒ"07©ð–«·úŒÙkòq¼ ÉJâr{h˜ü,5ÀåÐhù	‹4°´Ç(Ò.B‡ÙPm/>Ó#ÑK­Y�‚0CZ„öÓ¼tUÀ,l(ÔÑŒc˜ª~AQ[$\¤…¨­¿<ðUmk´Ö
‘ ©<,ü¦=:oGå¥*ý¯þ&în&Ù"È©ÿ†srvU“™)ÐÊè²$õr:˜¨«Š¤@k’‘Ô2ËG=Z²—Uë†®¾<ô½™Ž­8+=üq‰ªmTÌ”0$R_
pêÂ9à^ïåfPü‡ÐT­©]`‰7Z2ÐøJ¦EôQD‡/Ð >rü!=ZËg&¹G³IŽ§q±
lÛSúPq\w«¹Ú´’›DŽùkªÖþ*V‚®³È*Ö´×r8%•(t×ú*â’G¿H¦Éo	ŸÉlóGaKp.šg]3ÀAöñŠ¹ƒC*]ÀÓx
Ik£€=ÖêAH‚Û ä^/VÂ
wÈ¤Â†éÚÛh°ÿq]Däóë&ïE§eÙæ"›µ‹ì Ü@£I• #bqëÿûßþëÿ!lw"t•T}r9ˆ¤»¹È‡EÇôaQ¤ƒÎBqs1»LÆ*Qly|‘dßT‘CB'é�r3ÑçÍÅÅ­¿ÿË¿
îG�xÁ—�W“7xdùKî´—tÞÁ˜é¥@@l¿C>‡ñåFƒëOo¬ÜÇª­ —éæâñªí\F ýëöz´¡Èþ„Å-lüÙ�`€ÁœÊ%ø¿ÂØH‹‚U.È¿¹ø”|àò¦S½^GÐS›‹Ý¸ôß‰»ì./+�o4`|¢þCQaQ[Á‹j†ö'¢(èYr6²E+zÆµæúEÕ�ºWíRXÌÃ„èÝ€äbeK’C¤bœâ) 'M�»@à+ºöpk÷"M³ØTæ” 1�Jà�+&XzWñ€‚�< .žü æ»ÑÎ†’Ò“õjÌ‡ „äJ`'ýlPÈ �.Ð�ìpE:4"0¢Rž^²Š¢öÕæi°Aìè§¨W·xŸB�˜ °«ºÌŸŠºD¡ñmÕóG!ê¡gDOu•ïÐnö*!7ÕK5<Q™:ÌPm~º¸µôV¯Ó�K"Å‡³£ †�l8H§ôÇ|#~�âÊF€b—¥¿;„Õ5ÅèÉá+ šˆOÛÊßü¢V{Wƒ¨ŸtølK‰Z­x™|ËåvØ¾²nË4ªÐƒíB}.¬Þ_pJ_©ÛÍ|wÙ)3wg_¿)hû¥Q¯·£ShØÌO’>ÝTÓ7Š½µàÑúEÜÆ£Ì[ŸGÃÄ'ébˆÊŒrSQGÉ¦ú(6™<Îb|JË´Ñ
ÅCÍzj�=$Îdøz�â#ÊeyXE8`>q>êÅû¬æi|�dWÝ@±šœåçŸ½–ºÙ�ü¯<æ†ZRÁ�ÀÓPùGN÷r'È@sÙâzîTZ†^ònCãÜø�*Xî”|?Gï‚Ê¹B8eÖ19YdA~=]û«„G¹äE™`‡L±m]MpñÚ‹Ï•kÉ[‰xítŸsví\tË%[�2#@£z2Ä#:²†ýuKu©$hÒ1³¥Ûçm\~ËëïÞ5çŽ¹YÜÒŠ2þie†¹©èþ[*Ä¨äl‡iTh4 Þ�µ‹¦àô˜G½¦ RmÕÒ`•2‘8˜õ˜Lõb™(dc¼îÒìi™¡@‡Leekƒ"X)	¥J�äG]&`„X/í¼ÓcÞˆ¸—ÅÖ€…}*½
ú´¨D·@/yÅ* Q‘
5¿îDj•Ò+7*dhí(=çéeüœ>nŠ‰jÇœò„(
h Ø–Ui3È%Ú�©ÙZ­#"« ‘Ú)Z‰ä~ëšs‡ªºDã©;cÍˆT—ýÛl¦ÅFŒVFd¼O|’âNGÞàìªiÞ´Tw$Ã;  ÊxÝuvÆ¼•ÍlEzÆ³vv˜Õö5Š*·I`Æô”µ]5\ÜjÈiMYÌ½kk¨^Ž–[‘KT<}ÓÊPâÉ%ÇP¸pÝcS¤™ŒÚ.”f¹÷P:µéÍ½šåË$T§¾‹¦J&Ë-ÉëIÑ¹Ï�awÀ_¸ø–FË�´½w:i7>=:Àœò°cO”èàø'<èf°�DÅÍ—:=ÌfãK•ãfseiI£ÕÜÃ0³*ê‰¬è«¶$üÎá/ª.ÕÖIo¼êÌ„žJÎn‹Ÿ=™2i[ù$b£U 6HçTÂ@_ïsî�;Æ¡ð#Z‡öœ˜ƒ3Vp(`Å¨\FÉ€¬�9–ÐQZì`ý%Çvqì{
>ÏÀÓÓÂ ¤ØÍ¶ÎÚE%
ž¾s@ûÂâÖÝÏ20›Y¯Œó\Þ¬Àu= ä­)käõTécR7¥œÖúªÿCãO¶  Žw‹81@LØ"E‡­cÜúÜþÛÿh;Ôä™¸wm-¡ô”}ŸŒ/HšÔ>´¼¦ó‹DÍÍŒ!Ðå@ê÷Œzà: •ä‰w¾^pS9®caS’ë,dB£º�«d6Už˜ŸTêžhŒÏ”±}×2WŸ‚ T}Î÷õí$RsÌLrø¯ÿÓœäg�h?ƒÑÊ<*ó¡úÞ5þwV]F7PüwV]�u«BÀ%]à
Zï:7pI´âRkc¤úþG×S}ëñ^Òódð®`(°Mnñÿö‘ƒu­û;Ek£*Äþsh´97S˜…Sì~±{6Å¿d6	Š­6í�œz&?K?GiIìxºüd�°ÿ«´ƒùÆ¿¥¿ Cÿ>Ù)i½Ù>:µèxZïVCU¹¼@y[¦¢­M±¼´Bw•±ÁCYÙÌÐ:¡z®·.’Ö}?R‹wD}œ>Åòr‚i*�±.;]ªÜˆ{×ÔïëäÍM+äp2¼Á”diHôQ�ÈƒOWÞô%æ‚=×Q&†y,z1Ö�¹å0òïÙ@7UÑ¥ì"¥·ºx\[€1J:ðû
ôYëƒ¹å,Ã0\Fr+S³ô>u�*
¶É{ÃnáÝK‘îaTªýeAM§¡ùx?yÍ	,¿ºPÓ‰zO€ª–ÓÉv²fñXU¶�*žLóÉmË®Ðþp—±aè•¾ô>ÆZÐ·ö‚µq¼Í.“T¯§Ã˜Ô}ÌÜÕîEƒw¥ Ö/DðN�÷½kÙ±zÐ¥žŽ’ódpóÙ$Õ«tT×·g8!:‡É¢qÐ×aò¶ùp1ªØâÄ;]£~¹ÅŒY,ò‹‹OZßþt/ãA¾š§ ƒö9Ý¬lê+¨–›yª7/lVC�õÏ"àªÐ·mµ<Ï{pƒøOÊZ gµŸnöˆ“e0ÃÊWi}s%dû«p§)†“wìXª¸=ØyótƒõŠú�zé<}Ð±cQ?òtpž®dUÓ“„HÞÓ†:¥uea‘îmÉ[ 6ÐIXæp•ûk‚H4hƒÀãýÀ<°F–Už'5ÄÒ›ÎOiv¼pp"Â1r°Œ¥(å+J(xs]o� °•7/Ëeh›öð}ã®qÓZ´ÉŠ­Ÿ:9AÕ?:g4Þ+öçÖ™^ÓvV=gÛ“¸³b-A¿„s1’“ùË‹çÏÆã¡Ì-âN	jÕ¥ñ�tÜê&qo¡2ÇºÄÌ.¥øŒÚ=‡e8”JlôE4¾¨S€RÛblMW4*Sã¨W÷1þÍ™Òìb·`·Ú(žÑÍn¬_7>`Ò�|ì«\ ¬$ßfØ¢7"PÏ´>n�ò $	•½eÒáSUÙtšæt´é´	Ì0VOLÐs½•$÷eî]ªä�OZ ç\¨ÞQ÷`úiåºÎ�t)[¥7–Ô˜Å‘œ�B	”;{ÖÝ r¦âà§°PsÃéÈñ¨ÛÝG{Ým1 «r,9€Qm òáˆÌ¾½ø,šôpï™.È6ÆöØ™ÕÝåóúí{äŒû@Ç£¸]Ú}ÏÓq:´Ö AX‰RäçÖöGÆxo$:ÁàfJ-l‹qJÿoiÁÊ
é³×B–·�«µrZ±µ’ð9€ž9þä$½›2“¯[Ä6‘èÃä”n¡Š4¼„@Á…Ó!`Õáƒ»_Z`ÙH~P€NØ<Á@‡i¡ÀSá¦°Ãz\ÏºåK˜Õ^«Z…ZˆÅð¤%¢ã�òŠ¹/–h„AsøF.%óªúæë‡wž¡Å•\"‘™ îå–qÙ\¬nÉT;ê¨zÕòè˜€É¶ÎK6+h²UãÊ5òbŠÌ²¹8ëéËQ·1ÆŸ…ÃÏÖÝ ]Œ_Üz>Ç0™ŽG¤1-ç¨yv}£¾kÍŠD«,níbõ^ÐO&ê¸=ŠÕ¶CÚì_°+ú�ïÊ‰Ü3’Ïr!Hž<=
F#±TyR?ƒº Ñª–BäYâÖä¦F÷fØ¹Áù°³>x”^ý‡„ÌeÒ†cˆ³TªüÆ8æˆ©szÒäìªÌsŒV³!SVúÌ˜âFé@•ùÞîÚ‰æÖoþJ¦™ÊOoa#;Ã‰Âž›_¸1ÚÃÐû6¿'×ç%µ6>å4¾ˆáß+ãH2Ñà
³nÀöA5»]f5øŒ%@Ü ŸÝ[íô»U�;õŠµ{ìÍÞ‹Úq¯8ÈÕÎðhiiqk_¾Áƒ™7Ô:Ç'm–¨#¾
ÜS‹šA×¿2|€ø9ö¾lCâÖ“=ŠÏa¨[Ls’Õâ(×–?ë´¶)�¶ø6¾*š…æ¯r=¨æJ™Ï8³ã(l<{f\ÏšÙXúe:êÞjv)hð×œÂ!h¢?& ÇÔ6*”F¢›-NÀ“O§´ŠDe*+ÎP…{ÔÍv…™•¶»]™´7kå­²2RéÜUvÚ+ûåF/§i®¾L–or¼*½ù6Ùª]=ÉBUù	ÈlµMÆ%™`
'?N)¨�ò¡©D\åèÁ£ß—ãª ”â3Ê|Ä¹Ó€&èÜtŠB×#+>¨¨*å/¤Öëu¿Oü…¥FI¥Ïúfÿ¤i–ÿs{•îý25Qoµ†š*8s‚-”©áv´:6R³9^­aÎ-à“œI½ZÔëA…{Í`ÒZ#² ‡—¸y !ßÎ87ÖqrþÝš@ŒÉðijBæ�Ar™-|zxf JXB˜ZV\W*¹‘A9©T­jNê3Êæ÷Tfó3OŠ¯-­I%àqSa²‚ªq“’¬1Þ+
Ë}„bÔI)cPmzŠ‚ÃÄ3q–Œ0!<Pâ  ÐëÒ]i*’[÷Z¾u�™»ì×z¥ÚÎ©Ðtü¤òø"ó`&(Œ±_¾0¤s™é¾ZutLÒ–@™­F¬ŠËXL@¶ã<./Lû’áJK™©d�‚Ñ.qê Å§ãÅnÔLê€dÐÁs#Q.2ø†Ñä�iphƒwƒ dÈ€øIhI¸�»	>¼AŠÇ˜úäˆ]|°•Õ=F¸ÖßaJ§+ê4¢2„Î{$j´¤í‡�®hôãú¹NF10'Û“¿Ž+l&Š9²È¢ÜÄòZÖŽú&˜ˆcYéo“Íþ‡!Ð1*…;Ü¾`wå^ ð‘/·€‡*)ãŒdŒ2¡p²ç9s-NÒˆÉ½üy” ÎÁ�LÕøUSœà®.JÎˆ�‡o®áN§—DeÆ\ówq5�tÒTÐÉ‡QçÌŸ®O´€¿ÃÒ°š`6-bfS:g:%¥c—¾´1U'a·*’± Û 3% €8šØž0¨6å€©\”‰V÷ž¶¼'Â=V�D5I¿IÏì,ûˆ_Wö�9ã6[—½Y<uÞž¬&¤´è…•ó&§¼Q3!×|î5€@ïÌ’½Éb[„}`Âý²ž3CíøÂÐo0•&=Ì±8ÆS\|y^ÉžöU�¹xÞAF¨#‰®iLÝÌØíï�×jÏ^lïÖŽŸm¯<|dYa› óñØúÛ‰ËÈ ä’çêy'ŽFÄðÞÅƒå•U«'Ê‘?¥«@Å<Xøx@ô“shÁ£ËÞ+Î²Ê”Ë·*JøÀK³Ñ@3«w‘ÂÎBÙzSåç…<Ê*e¤¿ã_Úâ)¹Ëãá ×ßáÓÂó¯îr„™.#¶px´¢Imo}©#†[ š‚+K+�–¾ZzÔÐK!z¿ÌÖÞŽÔs0§¸ûŒÜD¸ð7‚ÝfÔî ºÕ{�¥µ¨ÿSCëð&™êÿdye	þù'•ZuýÓ GBAc¨“Žð!œO…(¿	¼]¶²fIÏr/�3P³^ ð) ñ
L�Ÿ›Æ4ÖVnµÙƒ�”‚’ÇÄH�! É+z¢ƒJèeÃ7ñ¶%GemU?Ù§º" cŽTl*ß¿É`¡QGQ•ÑµJ…ÚG„²™êÆ˜°Y3BgJr¶1çœÅÜç,(¥"­ü.Yý“Ñ4Žww’^gºU7!³‹º_7Äl“üÒGþsËÍ¢ë¹“Ãñj{©³¶¶òõã³Îrgyíëè¬}¶Öyüõ×�ÎÚ_¯¬­|ÅkËñÚ£µ¯Û_¯®u¢µ¯~ýõrû«ÇWÚ�>Ô#  ?½¹>ó>•¸*É?§lT 5¥‡¢2�/1™—I=îŒ'^¦¸ˆ~™ÒH�â÷z*¿àÎÇÿŸš3zŠI	¤i«ËÜãò2*¸òA$«•Ø‰;Ñ$Ó‹yç»c†€=§Þ+e½¡û)ôÙ­�C0JpÓ¦=Þi…ú#y ¶‡ZµRîG�‹j�‚‰£uK@ï,ÛÛ¡åé‡UåÓ,P´×Þÿw&€Š¿åñ„r©« …kP÷/Á ~ý¦ŠÑ%XÌI7³Q›Ä¶ì@çw(u®û×oäv{LyùÒ’ü%¸„¦°>`ÐnŒº®óMÊZ³s•ûº-ÞwRßýuøÓÔ-ßè¿îÒþF>7„NLXSóL0éºTwõXU{ÎÝvS+˜„‡¦‹ùdÙt 'K¢ç³r`à-Y´
gÖêý¤\Yœï·Æ�B¹}ÂÃ¾~£�lüÆ"þK¾)v}3 l?˜Ç£ì9ùÚM‰çï¿*VÂájãóZÒ-M{HL×›Ð§RÑc` Á­=T"ì§Ï‹Þ3”ÉŽ–Tâ}| ìí˜žó*~Ø<áõ–‘Ð�gôœâÛ%Ÿ�ÕôcŠP�¦ú¨¹½³[ÛÛZšBÖüœvþ¦Ub.Xã¯µ’x ƒ¾€¥ý2òz“¾ú¨¢ž6ÁSN= cËóÍ—š9çälõM®Èž5äŠ²‚=çà-ë^“™–°šU‘ìÍJä<à›º‹üØZ6R¢lî0“:þÚÅàwu,j3”p©Íí¬T$ÃQŠfT=¼¯¯¾ÝÞÝÝ?>~ûíþ_1
BéÁªÇû»Gû'vU©ŸknŸ>?y{´ÿÍÁËClat˜|ƒ�—'oO^~»O=Rtòòhû›ý·»Ï¶÷Ÿ¿=ØÃf5ØjÜô«Ç_/…Ù>Þ{zD—}m¬¹†
h¾ÍÞöÉ¶Ýnu1&ñÇž£ËI¹mž&–ú@¥È(€ÍÈÈÁ�ÅK>íµ¥UÒr&¥êÇ]íø˜jÐÿ(CD•!¼R²à%×gù¤¨*Å×¤Añ��ƒ™j%æø&j+ŸW¥Ø�o´H)õGX·Ó[.ûŒyøDw‚u°ù.ß…*—øˆs/$9½îÆ“ýž«ŸºÌë//ž³Ÿ ‘ã[Ðv¦Nö?ƒ×|°ž}™ÑSˆlØ¡mÎ0oÏæY+Ð[½gü¥ß+KÜ¿æÀ�&orp„ÁÊ�—É8«--Ãÿ•*âæ�6Í ×°ÓùgÈt(ÂÂ´jötf÷‰Ð `¿�±fÌ
çhH¶Ð:.pk^ˆKF{K°s+ö%á¥·" ã¾mYâG™? *¬çC�ëUåså¥¨�w¿ì›ªÎËšÖÃ¤üÈÏ°+.Yh{­ÿ²^ðlžÊ¿�B³-…jØßŽô
çÃ®Ã9šà­Æ-˜þFƒþš³ß­Ü«ß­Ü
¯Q©N»ñÙGâ´4¼HÇiÖ(…‘Fh©1·Ë’WæÞì>ê¾[™�¼hN²à›[rÅù{:®èE¢qü‚~£ÑX†ØÏ…ö$Üq_Ò¬SùÈ9¸†žáÙkáŽº[¦ÕFCœ¾$ó âmSÇkE‹@:¬1g1t7õúš½`=ÐÔ•ñõˆ¹–“­Ò•	ZÅaz<Qñt£H`&´£”+	=ÆäK£¸¬Ö0(dN6ÏÂ»ñ–=ÜFƒ>ÍÑ”¦[ˆÙ!¾¾%ìÇ!3R�‚€�šSkƒëomp‹­�oã«-ä³Ë¸£6øs£¡
ýJ+¡JÙeËÖÔˆ™eô„8‚žæêLu&Äú—l…@ØÿqõÊ¯Kzjr=…Ò›©0Ù%7|~Ï†NAË­�Wð7ÿûpÒoÇ£­e`&æ×ÆþIt¾µÔº¸Ñ ¿¹8Ðp%Ô¸ºß°Q4Î*M–r®U 9S@úYô>~NWÊ+�*¯—Þ|\Óx<uP5EC+þNëåÂÖ+ŽHSá8…NáUÏ)ìÇL9ÍÿŒÞØ^t�,—¨•ëVs-Ð‰RšL’.*=ó«Ñ Çƒ�¡£�†ûT¿/Ú¢æ�Rç5I(:ã]2é®�ßuÍâ²º|U1+—ž½|¡SQ~ÿlÿh_†�—th–´N‹œÌ@T¶6JÕèÅÙ+Û%½¬b®é½<ÚÛ?;½ËÔìQíËë:f3ºÌ»Ô¥ýø¹œÈžë6àrÇŠý¼Säøx?ÊÇ›wñZÊø,§.‹;ªô{péšÀÖœCÄ�?�á”U1ÄÊ3«šÍ¢tIÕg8gõ¶‡V‡5oÊNrøHïì®Í€ªFéòÇð‡À“åŸÂµNy\ä�T8ŸÃ9¹¢oq²-úÙže'XG)]˜$*�¯Ê%on²îöh]Õ“Œþ[¶“2ç‚Ï¦8Ž×¤ã˜ôÚ§Ès>!`e×Ÿ°êÕõ�,?�>DÒÖK�7¢?=á5,­ÏOýyØa¿³ÌM€÷·9žƒ_Za›2û…¨X.ª4O]•2´7„�„ÏŽHÿÄÛ�¤2æ8á�òëm³ÁËfS®CâÊáwÛÏöÞn¿Øÿ¢d©XŸpC-Í°®ÖšbÛ�ª¦»5näµºXÄ÷e5º3ƒÅ‹Øº)£¬›bCç©ÇPøõbÕ4H‡Y°~>êG·’‚<ßÌ³­afNI]˜1‡|5Ì €6Å´Á€{:*½Õýlá_6À0t}‰Cñ‚(rHöÇ«Ö7ÿêÁ£iW‚€™Œ „M6þõÂ“ÄQªVœV•â›.¥FIKp¯&ˆc  >pV;Šñ…y±‹WKp…ÿ_ÿg\!–w0)Làþý¦Ø‡?¯È·–‡s^Dª9Ñ-¡.0ÊÞ)QC«žž_�ÄßÿÃá‹Ëª`÷èt�mþÌ”®P©
ú@G„ºpU:œ¿!ÅL£¯¼4rSÛý®QSä ºì!•}¸ß¡«<¢*!§?~¥át˜Ú©2„’T’©<’*p	S7ÄÃbJ”„£s…-ŸNYdi^5Â8ÞQ
ôH�•¡†å¸~^¯Š–}Ï½…!uGŽ d`
u1š¿Å0ÙÖ^«9°ÎC/,¼êa>Réþ":Ê@æP<d¬¬‘M€Î†˜¬ CKl�5ø´ò«ÚÒ£Zf_ˆ«¡9‘»°Þï¶êxæä2•†9.•ß� ;1Ëu¤¼I� ao”  >Œd�ÉŸ»•û÷)âø€²×0"°ØãìBd²™â½ X&¢"ÊnÇãË:"VÅÞ©¢{g¼Þô`€�?0Èq”Sˆ¡0Ã²@ÿY†SŸÐå ¹”Áy�€Qmš—Y!its‹C%ßSd$ÈºQ
FÐXüÿKwÉ³Â
xœÝ<Ûrã6–Ï«¯Àª{¦íŽHÙîî$#—“¸mw¢‰Ýöú’ÔT’µ`’Ø¦HAZV:Þš§ý€Ý©Ú—ùº|Éžs �àE²û2›ÔN%‹€s¿��Ø×A&8Ûö÷öÙQ0IyÄÎ’PÌD”©Ÿ'!�:�/Ø‡<bk[[Ÿ:Ÿ;[ëv’Æ~~-&Å5Oy4aIœfìùÆÆF�ù"	ã{Ü~"23Ÿù‹q˜¸Id`úþ‚¥âF¤R°$�o,‡9Üw¹É”§qw¶p¿Çö_²›€³drç‘'RX*Y0K#!ÙæÆ†»¹µén~¾á~¾5øôù³-—íÇÞµHûç)ãàºOçuö†,î-\<ÖÓ§¯â”ñ	œ7ðØ<Na‚<}ÊNþíbxz°ÏÎ.^:gßìv)óD¤I<Ça2¿¢™ŽŸ7"r|8I'ˆ<¶–
/žÁŸ¾ð×áÐ•‰âVxyD'ôJ–Åp�v–MÉð9Ë¸¼v®þ×eg™H$ËaÞTx×Wñ-[9ìöÓh�Éì–�a¡,åÞ5À†ó=}úuÌC<ÎQ|#Ø(NƒI�Ø8�gHþC~…k�&Aæ*Dº€¢Ùàh‘M¼0H¤èŸ‹P\QD:b<òPMóÎ®‡<"5¥i÷À2I.§vÆq±\ÂfàT‚‰Û@â©Ùw'g†;žMi§»©7 ^–§wü­	©Q®œÂÒ’€ ´PØKÂ¿é"‰ø·7ã×‚òÓM ›v"‘NB—íÂÆ9â7b#w‚'è#ÅÇa<—}½ÒbŽØ<å	P†r€#R@ëL­Ü3á™ Ì_å"™ñ0õýLáß¶aÿ¾ÊƒÐ­÷òOs/“ø&fpá¥~Ã¤˜�ƒP€TÀ2ó ¬„]ö0Ç‰bG�#„�o
LÄFt½Ì£þË„Ìà¯ ž	ø—Óž–¶Ï’ØHèUi[†_#¶w8ì±³³o`‰G�Ø×a|ÅC˜‰;jf²ÓqØ¾ó<Ì�Æ¸"ò9 ^ˆ9ß1="=Ä�ÃÉ |Ì¢8#`¯c_¸o`sQ2ë±Oá�	ý}›ï±¹¸J =°ƒÛ$R*@BÚ¼’U2žND´ DYœ.Œ-gw@U]8h‹ç
à	`Íb`„#¿z¨(!ÿ¨¥’”�°N€l¨*…³vó,žqÔYZŽˆ+a(¢™}¢°ÛÛÌWøD±çaHˆuë2­ˆà˜ ù[C3b�K� VE
êc‰ÓfgÀŒ<
äl@H<³$HDÏ §Æ‚´=E{�ŠN/a˜—Y,€œ’œG!×¦HaIxA.Ï³)hµŸÁ¨Á€Â<ÅG³à‡"˜6h¯Ñb™Í�\ÝŒ¶mùFí:¦JYÒ¹Üf=%ü,â3"prz¼±w><~}yðú;qÇqH_�^ AGÈ Q �b?/¶zì0æ°S!X+¸
Â ƒ÷az„°z«UcºlÄË�!¢†GÈp×Q<g¬lflHÔhò+8&MNã˜+Ü·�Q’gúœ€’ �#z~ÃÓ€_ÎŒÚ‘¯ˆDdîœLVkäÖÓUñ«ü­¹éX e”<ô5»ÍS˜�#«z<ðZÀÙ0˜ØR)à%ê×l»?3”@¡Ò;v€ÆÁÄÅGn&Û7?æ(„‰0®ç§'%e
¼^©1»
f®œ4:^‚ŽS„ÆDLPñà8ŠfŽL„ŒKÆ½, /„ƒn@ÅM|­‚H€!~ÕH; b3‘F@@Ü¸0°Xz¹*mEB$æ¨Î”_B›«U‹ˆà€—�?¸™ÙTÛ+TqŒ�3H_ôžj£	œ†§Ì@Ì…_ŠÔ#Pàòšm  8¶—qb¼†×Êk@K‹ˆN¡Í­«èÝ.  hÁ>ÇÜ+ Áóü*õj…ãÈÑC£ÙÊ†ŠQÑK/#{#´ˆˆ­72VÆ‚¼t\´áUŽNÞ÷<� €@O·Êñ€h9pùÍñÙù Èƒ:rT>¾8;8my–áòÛƒ¿oˆ¯�eÁ×E«‘€�fìZ(Q¤™q‚¬€[Ý?89<þËåþ°»±»4|}y²{vöýñé~9ªCn28ûèC#Ù¿GM@¬>æÀ‹¨ç§WÄŒ,¥JuÙWR]1%9â–{$±àmÐéŒF#pœ‚ÅFoµtõ”¾SDë‚4ÀÝíN‡	‰ÕiVk‡ääâôp­ëºýn�)HîLdÜÍÓp}[ÏP;#f€YÀ.¢qÍž^Ð» êàÖºŠ¯È©1Ìæsxlm�í|ÁÞ!Ô2q‹kó9ªrñÍ.Á4ÖðÉº†ÙÍâ=å¬u¿žî¾¼søçƒ½ó.LX=~,2ozé—ÀŸ÷�F+vÿ(möö†-C«ÃºJâ”»&I’Xp±bÐKè�¤â¯y úIs¿áú¥¨xš¬2áóhí‡î'BvmªvµÞèþÔ£É0}î
vrQèÐçèÑ;�ð�Æ˜ëºžõô“RÈ»[Ï\²7Ÿ¹›]û½’v½‡Ê›Bà»ýl–ô£8"=‡!sÆ!×ÆÁ�£ëywøŸ;Å%êÔ?ÈÌW¤ÂCÓªÊlùSÁf ßf�.˜ìµr¼66bñæ*(ëš{ËGÁÕQô>>ø©ÂÂf3È/ÅÚ†Í/Å’%Çì&	ù{ƒî=¼?d à!=êcÕ™ñ ]Ã¾ ³jjskÀN1L¥‰ˆ#íU¸‹ªß€Z|UT�0G©@äÕŽ	ôØrMª–>0 ÀµWqÀ^íÑ«Ý«›FìJxÃGÜ“—§)š/ÛÒ�©…8HGà$d,è��ªÊ-¯­^ð7¹ÁoŽ
-AÎº�ªgv‘ø\›{å©@†g*5¤lË0²G`Ä”„à"ÐTðñHKH4WˆlÛ k ì
œêk 	OiN'[„ülQáu`0è�RÔBhÿ¥4á 0�~§¸O@}Ñ^™L¨Ä|ÚÞ°î�—÷EV.Û�‹Î#€s!ÁQ»²3 Kÿ÷HÇ»Ÿh‹Ó˜YzM�™ò:HL<ú&/¬(óAQµÀ™Š0iÝDEs…rpèpÿ-“•h™¬½ÿ�‚È�ŠÚ ]!§ÊTýQ ÁÞ¨‰*¼_ÿöwŠÏ‡'ýi,3T8ÅhÔÍÑHy†‹aZSÔ†­ÕYÃ}VÂ»ûöøü…»¹õ™ûì…»õüù=;LÁtÝ³»ÿè»RNû�)ü­/6ÿD[:ÖÞ¡ÚLéCÖ(Èžê[Óç°>¸˜}<¤ŸÆÉ:ªºš6 ­AÒCÀ–
M4ð5ãª®·è×ç _Åó´õ®â”®V�Ô£•V°d~÷ääòõîÑÁN×¤Û©ª»�.å¨)õþ‡­WvøÖí”ˆÛé>~k¹â!çñ[ÿî®ÛÙ;>:9>;¸|5<„õ”Æp´ÆÀxÀï}{pz9<ÚýFL¦^êqÑ£ÐÑ£±«°UWj9áï`Û"¿¨“F÷1²5Œ ‰÷û™¦('ÀOTz^Åx%õ+jý×¿ÿþ¡ì CÐ¨]çóPVÒþj]©g|ô:¶ÝG=L4�Œ'ë‚ÅF3 [U¼ë† ÿx³Ë¶¾ ýyÓ�0mù#yc¿°°GÏ˜ã±®Ž©0ÒíÉ…ÜF­ekøÛEÎ]ƒ‡èÔÑº;ÙÚ“æâIïÉ“õõ*ô_~aYš‹Î�"Ú9&ámK¬u�…OM�{M­¦É~À?ù�ˆu…^æÖ5]à#Gû_þ’¬_†)pd<<·'Ü„�ßîNÅ
€Ð¯É­ÝAÅo?5>ìÎã·ßƒ”Õ•¾»C€pÅ�fuÍñˆ½Ò¯J[E%—ˆÀ¼’˜%èô19<”m\ïÀ˜˜óså„Î]—ý´­ê:æáÎã5[BŠçëÛ`LëPu(ø°
>GõÓ
É ÏROÚ éÁë0B�‰ <°§Vì1 ™]c6u]…–Þ463¾€?¬‘]—N! fŸnl4_ÂÖáŸ¦þüt J™è†�]ç‘v0¯ÐA ¡ÂÀ_j'E$!*Q<¡!(!Æ¤1’XCA�/!é1”3iá@´*Þoèï^°ßA„ñÔ7f­ˆš¿þç±ÇO»Ûì®_ã‹òÍ?þÛ¼ìTæüãÔtgÙ&Ž Âî’kTâ¹¾$�XeÃ ËúYZ{*R×È$û A–pÜ}UŠÈ?9>9?Ûé:³9‹91R/ûüâoÅbr°¸;*í	ä¼ÛéÜ$Rí4{l “šµ»ð÷WÝâ çmìBE•T`Å®�S¾SãÅÿV�ƒ­íÛÊQŠkH ªè“ÄaR@ª„=èÏDF%¶9Ú‹9ŽÉzÍ6|YÄ©�qx6¢Ò»Cñe;¬k4à—6[´Í }7®é%PCà& w³®IØaoçØ‘±ªk*¯ÏÊÊ8ö Þò«igJŠ|Ìœ	†'=¶¥òØ½I©úB*»ú\íª™¨œñ(u·hî¨%ª]²yÉ6(š%ß”çÎÎV>ðÀð'&Uè‰å©ÇÔ€¯¨þ‚´Ñ{Ä×ªpÉðoz­
w˜S˜tÆ‚ËT½QEþ<Í‚1÷4ƒR=KÛÀ|‘Õ±ò'tÔ±nô�›(À²¾�ó³ïL!A›M),�Úéè+gy;�¨©¡æõ›FYäãšœ$ëgÚÜ`«Ö?EÅÛÇ#îÊë¢.>`£#@ïÐÔ¹UÅÌÖ¾„ÃÇe]ò…4ÊúA54aãÁƒP„ƒõjÎŒuÁx¤b úyªI¿F5Ba·±¡(î™îw}º•Á”ƒ¨(„R£R*DÑ˜D¹(pUÀžóˆRYh£x ©ju	ÔJ�[ê>ª&ÅTc
û^—`k¥Æ£‡”–Z?_Y|\Q´ë�Š	TËŠEyu[yPºêa*Œ�~ˆzy±VVl–õ“ZÿÄÊÊ`KEKÛQ›>6A�ÐÄ3G#y·&UŠ~¾ N�D+*‚ªÀa R›Ç’ªÝ
´®¬ãM*[Ö§SÍ-J£¢{ûOã|2­
ÙÃ*|öæ—Vø¬*?ãã¤}LÓ¬¬²]¥<#?F*ÉæÐ®WO9¾äJÐúd¡â<ûêæùC¦ÅØÌ	A%ø0yÌ}u³uÏËž7��“Ÿº�TÐ»wªé�{ØH’ÆÕCµ(¹Fˆ>eìá£�³ó 	U	]=§=yÿ âÔoR˜2§Õ
°(ýR´Ííx6Ð
ÝÒ8Æ\ØšÇ4$¬nc]Õ‘°à³°Ó*Ž�N¡F-1 @½ÁŽ-��Î›øŠž©EÕ[Ìƒ8 €å€Ò,wBŽèëÐ;‰-Ì%µ:Å`»ÍJ[Ð1	õ¥rÜ©A9ƒx#OP{Öæ¶	s114(~ÁAóÈÁnô `ï¼\h¨ÅÎŒÍÝ‘Y@ ÎJePwbßœnB}ôK”‚öá$ õñš² è±@gæ�òC˜‰|oža€…ˆ²îÿøí[VW'ìî®6Võ ÔÇR8Ö«•Æåw»‡ÍI&(«Ì«êŽê¤ê»ê¼zG�=¯VÂ±æb±À`4ëˆ¼c=‚èï	ŠyˆÓ‹×¯N/ÏŽN¶:ÜÕ¢K%—Øâ`Í¦XhÌžüAþ=©†™
/*ef±§ÚY³òý
Ø5½€±Ã£"½h'×ŽµÚ’Úê’¢Öoò>[ò¾P»ÒIl’´ö=r¸|1¥e©ßžXJm|QÌDšÆi#û´º4ámùÚZÛ§@…l�ìçÔr@ÙI@Z ÊéÖ}#U¨¨žÍè–v6Z>{‡xw…ÑyÏ¸ˆdW•“_m=(»\g`TÖ’Êø°øvë}ãÛ÷DI=âF<(ÖRî»¶~ïäš<êª·¢®Gÿ;å8)ïã´Ð¨‘\×¢X\R£µ¨õul‡t¨-ŠM'Ì]�÷P¨e9¢ð«q	£§3;ªûÞüÒY¹žÕIü æäj4Û˜Hn‚#ÝÈ‚ÝïÚ›vÏ~›°U“Já, ZÖ’°£(póY°D
Á©JrjíŸ+A´‘5Â® 8W·»ZîÃžÔ�[jú�ûÉoÀí¦7àÝ2Å�˜òUžRä	lð‹•×²Ä}—ªÈý+ÃÓ(Ø zíÁ€r*çSª¨° ¼úÃx¨ÈGn¸lGÑŸ-c™V4š$­¡`l>ò!@Ç8qô
f\	·C€oŒ?•ƒ&“ã¢,©Ì¶êPT¡õadŠXR<$ð°E ËŽ:¹J•µ±‚¼¥mæÎô’mÖÕf‰`e¼ÀªÄs¼dapb™�xfúŽÇ*S�&¡k‚Vnæ`9¶uöŒZ8x ÿ™–Óî9x�º@–VûF]F­:Ö²ç¢÷c™º×¥ƒÒ‹	é6B”R˜š;IÚà0 Ú)a«MÀÖ:còÙÿ¨KzôKÁè’¥£rðè Ù~²q�Ê¬Ð¦ `Am£†�)ýZ¬4Už°ž>DPäûW(�ÜÔí”ì^²Rºôkz­è.FóN.ÞvÞQå0#½“0FcN§®ÓèÞ.·–X¼–û÷…{—Œzc]# ;X|,šþÏ‹�eŸSÓ3ØÚ‰©)­³°ÅÎ*÷òZ÷f!¬$âËØ±ìøëVz;º
“ÂÉ.6„|(‡H‡ÁÖ’®õ|„uˆ»«œ´¬XÆ±aËÑÜó„ð¥fº%›%!@R=J’®]ò„ÚåÂ¤´½8M1ÁR&5wj) m™[Û§Ål¯%¬)9¯ð_¸?›£:!uó«¾ÑEö¤p´EÁÕl·®ìÐ®9BX·(D™)Õè¥1%M9�‚�i—‚÷ëï<^ƒ0"aOþ½ñ‰¢Ó/CÔ'²_ßï?1Ý=ØS…jwÄ| ÃÔ�UŠ–˜çÀÆê¾¢êŒWõMÄQGÕ¶{ê½¢ƒeÉP  'üŠf�è	àË¶èòz3<S-õøS–ÉPûr®Ò $Ýê7ìÒ’j+fÝ”]â­Õ1b~õ|/Á%M"/ÌQó–}Ïòæ{U©Ðµêº¹0%jÊØ@l` ‡�ñ>UpJw§{Ô‹êèæ¯2«šÝm\ãªbqYí•¾†]½Þ¦BhŠÙÀ‘´Ð~`ø†ÎvÞù¦bŒ»­×½R`[Â´²Êø¬—iÕ`>b�99ýñŠw»u¯¨³Íåoì #$öÌg+¨Fæëµ‚7jé@%´Êæz	ÞˆÍ‹²YŽ7{µU¸oO©èiŽ‘Wšu,R9í>T¡¡Õ(Iœ2°°BrHw»¥�Ð³.�fP¶x7c®ÆstfÐB L¬¤h
 Q³ØRÙ÷beí'6™“§¡¹úû®Dl‰úžêB/qqz¸rK0¬Ë³`_:jsÿò®›cª—rýýç#ó-»#�G+R©-êñÿ&—úr¨`eTueæôcdLëÒ$Ìe3EªB’¶DiÙ.U\ÛGçæ#7JU¨Û³Ç•3™BTÒÊÒ{�°þó"­;P¹«l+º—¤x¬”Þ¥Õie2Ô�=Ù·¾…ÔÇ[.²¯¾*õþC“/ð|gf¾J…±~0‰\ü„T }Bé> 0sYæKeF2úRSó‹½ÂÅ_¦*ÔÓç™ŠoPó°kœVý5�L?§;‹M¬’çÃM ¨aµ4¡é|¶j$E‹bVmÏpƒ×4»€&aå‡©S‰§a Ò^%,¨§ÄËL�ú¸F�ÍfØï•{ZRõZWT>Ú¯HÚ§BGkÔöšÅžñ—5Q”Ý5Ÿ³)ì/O2r˜¬¼~žkÂÛÎô^(€�ëå€z¯ –K0R,œ$ú~Žñ”êÕd.,ûyJ;Ç$NT½Fñ;Ûz=-Lœœ(ž©ïxT>ëSÉœ U‘¾È��£�ïFî³öôŒ¿¢]ÂÊù–9‰M¼Ó&œÜ84Ô ÑŽ|iÂ{’SÕ_Qìu‚4þT_=Œ£%~è–åöT*5Ehv˜/®¼W¦Û¯
¡|X€eOÕ_ãyß°�}ÚÌ‹b'/�/,˜VÊzWëp¡‚Ïi±¡²ê�¤qô‡·„…Â¿®†
ŒTHÜÇÞµÎØNšç&²§\åe’€ô{@Ý)ªá”�XBÇÜFD±TÜèDáïét{-Ù^*æ
áU²›À›¨ü£ÌÖWE7&²Á¿Í`Åd´ëCTg";Ê�@rœ…ŠïU²Ö”R •0¥·òû3½ZƒeÏó=ë#NúãEžO¶ØVJ
—ÍÐúRø†Ú”¾4u‚—ù¦qˆ©éñh€X>¹ß??Þ?Æ†�ÐÁMÊQR›Ým¤@›f( ñ�(:«TþT»VqRÎ ¥Ï	~šH¥P°�’¾‚áawïÃòõÜ‚Î8Ô³)”!Ñ×‹à'}Ò¯ô£o»3Ü8ª"š³¸�ÿûDÛ¿“xœí}isG’èwþŠ²Gc v7 ‚%C¢E[Ë¶†‡íYYc4€ÐK �ínˆ¤iFì�Ø_¸¿äåQg )_o^¼	�ˆî®#++3+3++ëoâdG$‹U”ÇÃxçWô4—yœ,ÅþZÈeÑã›y´ÜÚz."x-E³×íí†Ý'a·×ê‹7i2^Ÿg±Èäy”FË©X%i.v»Ý@Œåjž\‰oãËÏ²«,—‹±Ç"Ábòý* ÍñøJ¤ò½L3)Viry%ÖP'·£L®fQz-£öâª�ñò…xGb5&ëåH¦ÐU2ñ"]ÊLlw»ííÞv{ûI·ý¤×ß}¸Ók‹—Éè\¦�Ó4’“ø¼óUœË(<8s9�FWmÖ§Ÿ~™¤"šÂxã‘¸HR¨�õ?ýTþãìèøð¥89{ž|}ôúu_œ”Ùz%ÓUr�Å²õ�j†ã4~/—áF2OVˆ<ÑLå(YÀÏ±·`Ð^Ey)Gë<^NÃ 7yãPhù,Î¾y”�‡Ã«ÿ¶ÅI.W™X£™�“KÑ„â­x7h‰ì
&ìRL £<�FçÐ6ŒïÓO¿J¢9çËxg3q*çòŒ0ø*yT0�VY_dy�r˜™ÅÓ¥‹¯O1¼Ê¥ ©ƒ‰1¹H±XÏóxÁ”%óï†ÿ)¡"Ô—Ñ tSû?œˆ“—_;¥ãå{™åñ”h¬3‰/-Çâ"J—ˆ•I*¥˜ÇËœÆ°ŸŽf0u£|�JËþx&Æ8@$A?“sÀpF8@hC‚V@´Ií'%ó™\Š‹8•b�¥£Nš¬s™u²�vžà#<Ogð7¬«öÛâk)Wâ$ž~ÿP åÆ“xÄ¬Â£}*`8b-“%|˜Ç¿ðG™¦	 —FÐ#ÌÑ¦¨Ÿ [ç31‰æó!Ì[¦(Ë3DóèJ¦rÜëedAïqp+Àƒ†&@¶¯GÔÇaï�¤IžŒ€9†r½�“40S ‹ æáh#ÁyD@?•£ÐÀƒø~±^ŠíöN@?þ5˜È|4t~�C(‹h^¦ñ/¿ÌeçM’åÓiInšFqðò[bàÁTæ_Æsy´œ$Ùû"H	€øÛßÄWódÍA6-³PC¶µb÷È]0§@°ƒ¡‚Z£ßéz©~^žŠq"–INŒ³\-:W@c�þZ&cÙ†ß œ2}/5¹–§Ö4�FrEä3;È ’%LüW`'3§Ô‡™%	«Fs°€Qeb0Ës`¼N§Nö€žAšN"àè›&‘È[¼9þîÇþ|²ó3ðé^ž®åÀ´´æÐG¨Á›KÝ6 X‡·³0Ë¯à%‡8F·’ÊÿZ‡ŒŸÂô¥ùø!æã ­€kD2Ù(YFÍœ»€d9ˆ~`HD	àÀàPA Bßb)/,“&)5v3qåDe4BéI¤J–¥«	°.b`¨<J�Îà‰ùf(�ê%MŒä†!Ñ"Rïš¤Ò×Pr®ÄŠ”%(¶„€É´Èü¦DÌ«ÓÓ7bpŒeð…lFr		œ�e@Î9®ªÒZÒDVvÌÈYb  -Š?M.¸+_ñŸ1›ÀÒ3F)bç„
dÉ:a¿ØÜ—ÈÈ@Ã«rÏÀÀÉ
±´|Å˜åE ­‘ÀP�fI@ð×ñ|œ!�Ìò€Ä™­€¥yÐß$ãxrU4JD3ÖcÐ"¢‘tÖ£³ã×z²SIa	 ˆ0é©Øs„2yl$amMãi£ j’ VH5O
®èP0½ê ±�×ã9ÎßÅQ¾b‘�/\¡Q‘·œ0éDYf@P]wš«u#-²*Ò;@"Ë©¥Ë¥?$¾¹	ÿ>ŠMK s�Ý·wÅK1Eˆ´lFcw¡/g–,d{–/æzfˆ¶ÄÁÉ	èsèW8p}â™ž Âû"†Ú"[ÉŠU”�jµw{Aft…Lð`‡‹~I3AÄ1ã°å%,‚?…Œ[%Ë�°Ô<¦ûQ|ívtæ/¶Äæ2­j¦À¥šã" ‡4ß þÃÍgs@RRôUWPs�3Krd¡ìÀc�3€õ`ÀÒ1U³Þëva-IÆWm‹S‡vŽÀ¬nêÑRÓd=ŸSõ @f£4É2§‹ðâÄfãóò¼Z*=àE}à4Óö¥ þ;Kæ8·¶,§„è5úædž\ˆxâ¯ð+àE$r½üMœâz²Ý' H˜o ÓâÄ£b„Ëš†úµ…S¢†Ú ½#XÒ	I5Êj<Ñ ùÕJ2Â±Ì`DŒ·Q#}{E¤ÞLíoVâ�–~K˜3PÄã_ �åz1”)˜m5MŽxi¢¯ÍVÐ\×	Pk±…æ¸3ÕÈz™Ug¸xíU×¡5$[/`Ì ,i	“3´wp2~HAù'a2‰b’ÁD]€¯­ÍìÜßÚ 5‚Â€Vé5h=Ù(�‡@Ôò(²Àœ7b’&Ñ u±�UOm#�(¢<5ƒ5m¶Û�2y@[’fVG"5WÆ4Ñl‰½çâpçÍ3pF‚%lît¯ELÚšWE¨ñ5KDÂä°ÝmµÚyrø_ Ì5¯’Y_4°ñ†¸i=…ð_Õ7µ‘a‹( p…Âè =¨UâŽ=7P’f{½ðQ£ ;�@(Òë)ª{T†i4�`ƒº	"Ml®>Zz»h�Ã»BöXAöy-¶²õÍÀÁPøð®`ìÖƒ¡„®@ÄØÑ„u’T*„ÝÈGÝ»BÙ­‡2•¤c*A=—„¯n¸ìl@šZÎ*([wo¸8~ Û Dï®ÜÚÚn¸ó»µÖÏS=b@SYD€%þ”'E©«Õ˜ñÖàÜG¦×RÁ"€÷;ÛÝ†xÝzQªý©©‹�âÿAº–‹¬mè˜±î@j
™
W(éqý uÊ8nQ·>„è�ùrÿè5+ÑRðêÐYÀRš˜°á$Y£þ‰.Òò•rÐ.@»Ó·ÞÖ:=ÁåUð…‹R¥N ö -¿Š¢(÷_òl?ÞÊþÔ]Ù‹5,… iŒÐ#^}³ÿZì‰Î¿~ö _d\ HˆPò·¨&fœˆæGN;-­ÔÇüTþ–šlÇÙI4‘¨hM�+°·–øõWêV<Ýr;î Ëý¶	{Ù@šÅ­Í�@+Œ4~ ^k°¦JÓP{.—Ó|tŸÆ‹fK÷�õÚñr4_ƒÚÐlwîpe3èPÕËËï&ÍFØ0ó÷½=nßu8úS°´t³< ôöC[.à\ˆ«\šºýLlÇù‘mý“OÄGªî]fˆfrË|–:j))Fom“hêÞoëÞ`†Ø×4�0\E}Ä@|º9’ŠOM—^¥g·‚�ÕŠß«í7XÎ‹è’Qz`Ð¾ZŒÕ×mnõÆ ÆC�ÅÒm`9ÄQ1vÓŽ=��J?ßÌ“w™¶ZŠð�P9Ä�`38å´‘ãCõ·Ï/öçUyÆÀÝ†É»ŒŒç8^zfÀ-lé¦(—Ýeò½í;�Ä ß€ã×Tÿ&|p5o:ðunÅ^ë¤=Qm¢¸èõÞ6Z^én\éÙ´ÿ ¥~ð„jãB—4†Ò²ý¨�û¥‹8WkóÄÙÖz�ÐŸRµŠWwKuØ#-Â…øxÆlŸ›°äwúX!ÁóU€¾ƒ.Ëvk¼ñcß;ß{eŠ¾åvíoþ0ì=ÞºÞÌ~ow'ì=yìªöÞU¸9¬É//ãŒýâä“Ñîˆ’'$Ö-ð°®ÌPÏÒùÑrµÖ.Œ÷¥¯Í?÷ëêñ&6Ñò™ÁsÕy°´Cøè>!:mf2JG³7èq†a ¾Oœè_ Õúky•}Ñ'2ÆôýÜº>3
zùp×}eçíu*·‡õ{Èþj…†8ºŽ+‘o\ J@Ï¢Þ£Ý™¼DþGy¤�ñøïÚÈ<\ÈpKæÅzÙ>H¯Vyò
h�n‰5‰Y{½‚Æ$µØr„Ø~šFWmô„4gíq<E©Jý"Z5›C2!† á+„nï¶@šŽOP¬ Ñ e¿ÕþÏÄ\û±*èl�N¨Æ¹Ò;úxBÝb‘M�aÙ/÷Z  e|Ð²;<;&:œ"èfž„œAijIèÔO©œsÞZ¹Qk¦(~FðqÍ9\Ž´øZmI¿šA9—¹8‡Z]°H�šƒýN‚d%Àn-†‰jJŸk k>6²�†‡ÿkt‘=üY­P…	d$1ïŽ¹°Ô8äCE?”r¶Ð¤UF™× Ö´qGû¤ë!(§vo&ÉÐ%eW€zkïò p8ËxŠ¹œËµŠ©£1¬ËG%"4c1ÜÄ‘/C~ç¢&,Âû¿ò2B«°­œ‘ÂV¤X6:Ãõè\æ Ïv~é–ˆ¿¼D(Â(�îãîãÓí^þ÷N)œnÀÕbå–sA^)Ö(µ&kÊ�ÃýÅ/áþ|š¤`- ¤ I…¯¾Ù?O^íó¸ER9Æøšhe®öPm0ÐÜtÖY(#ÀÏ6-Fõ¼æpˆÐ�¬÷	Vñ¦AÚÙív}0NhÖ_)'á)Vþ=z³û÷�Ðð¶Ýng+à+ 40½Zï¨Ávr»Ù|Á€D¾�æïñvHOC|"rÁ‘â ÷à/¼¼´ç	´+)ò*•Mø>Tß‡ô½ÕâÖ‰Êß½71cŸae°sAá<oaÍŠï[Ð·Ä<ðI£j|ÇŠÐ÷h£è§åƒk$°üáà žMý×øžøù§åÙ·'G_}{ø2|³ÿÏ×ßí¿8äŒ‚Ô®šhûéu§YìÞ�Œ¢œ;…kw 8Mp†±�"9â˜¹Ðø¨'úU õÆm4ž¢Ä˜¹f…»’ì�e'<ØF+ð jÑ8�’2	²ÈÒ#� vO[ž­Á®]D V(5Ì‹ëtÞ·á#jŽÔD~ã\Y!	DÁÜ°�ù,=·³®D9öú`u�(ºá×ÙÎ¾æÔ¾yúÛ	�Ÿ¾)T(ó”—4üa™\ôI¬à44Fø8$Õï>ê£�jinÜ2îAFE;Î¾GCHy1ÊEù ·\G®·3œ©­`R¯":êD¦èiÛ(Úï…ú†FýEš,§žà.Hé/
Rs¯H£Ÿeå^y]ù{ïK-¯á§A0üÎvà—;>±²r¯¸|â	Ë=””ŸTˆÈ=ÄÓ'ZÝ3Rõv:êW¯h•äUµŠVPZyý©nÍ3V”üRä÷= 1¬ýÕ[/™}›ä`¯�f�»8­)ÐF4(ƒ¾\‘°Á -Ø\×õPŽ"€«°�bPºÑmÑRï¡Ù€š+™Wx¯7Fï�}æÅ³Zûm	*…oíõî[m=bWb@–±8%šíC�Çr”¤ãgZéV†	d—mM_¸™×¾¶Žo€ºÀºCÒ‚¹å™Õ�
z¿§ýéZ3r`6Œ1H;vÐ�o¹o³š�Š3ÅŒU™nHš
oµS†¬ÙyûQ£Ùúô]pÑÍXÅø;¬ú³öh!ïçÍnËW°óälÍô Ê$®%­¢£§vˆ0ât£½L<e,fÏ`VÊ(¼i£Öª«(Æ™&‹áÙ[¦ßaoßá|áJÐäoÏQ[#Ô¼£ØE«ïyîaÂ6(hyBêº½Zg³¦Û&oTÁÿù»ÖA«‹Þ“¶¿†JÁ»vÜÈk@f¾›+zÎTð\Ïó°XeX¬2´U”­”Ñ¡ÙX3Ep¥Œ:Óº­iñà÷ÇTâj 7O}©”EÈÒ'÷×>«og9ÙÁJ™sX€ìÞRò)c�²—s®Ò¦çfç_ÍŸÆ×oZø§gþœú�êÏ<èØ-
ªoœ¸ØŸUß '£4‹d‰Þö1º#fÉßÄË5+¨*%Ëñ; ˆrÐm)óìô IóUô3sÛÝVPùYõßÑ\]†Àªm�á­ï@¤¶ �Ðhµ*|Ñå™Çb}LÉöÀÈôÀÊòÀ—Ù�/«#£–ÍJtV˜›û:‹2F¨`Uæq u-G†@!S¡í~pÔüHkƒh"¹ògj­kg»FœÑ7ÔtÌî–gÖXoCmßZ.4@ÊÍ-•Ù´q·YßÔÛ�5Uµ	ïT´n�ºJäh™}"‹×�öö*¼¸ÿû‘ƒC|ôñ¢ßð@ñÉ…ŸPz�0Û6JeìÒgªújÃÛ¨‹è8!R~)—±Ô@7[EŒUí6YhÌŽ“;¿û¹¦?+8µÔ´²Ìì�ãé àÕ*í�ë¾qSÒAÐ~þGŒ˜áŽÅI=�À®â¹Šóò3×§xä«û‡¢ë¾¡@ú=§�v¶š£ÍÙ±Ûè¦ Ú¿'²{ôÇ J­4Öw¶Åc–ƒ‚¢ÔGÐ¨Ô ºi3è��¬Ç`]š¼#dqò‹íRµKÁ�ÐÛ“Ããï�ñµÓ%}:=<þæèÛýÓ£ï¾ý�¸©6°Jèòôë˜|]›ë©|Úhµ'ñàm¾H’¹Œ–U3GfÚuÜv¼P}V“£Ùé¦æxú�œJº¨Õ¯ñ%«ÕO½Ò¤DAiU%y.ÄæÌyk Óv¥¡7}]à-Õ}‡EÝºpM_oú®©+ŒqóÓR©Š7ž±á:¨ª¼ˆ®y½&ÇÚ#Èæñ/ò,�›<ôL-9Tèn»k�Ñÿß––—w­–ï±T“Än>w®oîã²´£b/û/_mö_Z’=©ôd>¸fŽÁ_ŠKð§Ã7¿Á§Yè½Ê»©@v{‘ÊMpâ,ê<dtzâ€ù	$ð‰ãútt.Wü¨Ý¢‚ëÓ,ËåÎHÔè§?T&T5‹^D¿ÕkQ�«ýòðŒL7*Î¢28¡önÖb€AùDÒXæ:@ñ`Æçf::—6«Æq¶ÂQÙÀvÀ*3ËtÅÛ¤p–¨ÉIÚÖ„Ï`Z½FPÚ¨|ã�£±öV9ÐLÖK>Åh¦­`­zµz=LC`¶?áÍ¹õ°wqÜâÓa€°yfÝCêíÑØ¾pþcuMiûNíS‚É|î[¨Çä*¥è(+Á-×ŒÐíßÇÓlGu›÷µÂ¹
Oâi»`9ü\¦�gOY¯×ñáW ¢ìvi�…Á‡©ÏÊÙÎ!2ªÆtS-DÅÊ†›i½òôŸ@Uiø!.Þñkt�:E\`�àPÊ¡é!vw³]ðƒòØÜTðó#å¡­tÌ"3kÇ¬�Úè ÝR§”éŒ;÷g¶x¤·:–ìÅ¹‚û¤Ã¿nC<A£š·Å>�ËSgìÄ"ºRgñÄ„ÝœÃ‡}º»wmR^^Q”oÕã-F:Å—}�CË_';¼mMñÌp®wäQ¥©~Úáð%hNÌÉÈWt2òNÇµÊÇk+ŽmÕœîÛ×´U}D«5 ê”Î_ñq!>~Æu�CòÍ\�Ë¥ã–*íŒDP#¨��bT¡ÞBçÕ4£º�U#ÂŠ‘ë†—UtUEÅ*[d¬Êã]êŒdí)¯êù(ŸöŠ&0¬Ãh„ž¸{üªníÁ.¦†‰tÑk<�°Q?¥”§³8kÓÑN5D•o&ü2/…ú”ã×VÏa~dê#Ã€xÐýØ]Íb�ä>åùggEüJõW¡”D<ç_ôõó<•|Õ©
åDè”šÝ¤jmµµØIÃ§ÞZ®ÏzMn4¥íÿ¬ÝhúkÎx€×NØ;äÄ�u°39Ÿ'¢–Nã"Içã†©V@¡:¤Îæ„;�–±Äo«‡Pš¾ˆ‡ ÀêÒãnó5(½în`^:�ïŒ¸pô*ô
ËµÌNX~la~Eq*f?T˜#=7[u�Z2a* H»A%d¦3…n¨*±Êõ�),0è8™E›è·5LÔÜHS¡«�yïŒ£—zÅä àm£Š�#7±€9VÈaÀ©*¶ò…ÇM�¶Ø¡Æ:W�P»vµ˜$økv”…Ž¤>å mÄfÔE³a-èñSø‚¶{;æõ<Êr2bÜ—­Ü”îvûôŸÚ”Æÿå	ä„b¥··õKÊïÐoE^‘€î¿æË™²`×:R®cÄõÌÛ¥¯9{	ûHÜÚ_ï>ýõüþùýõlïŒº†LÖ/�ù	¶4ûÐ»=ßfæPóÀ!þÙ3,¡})$©
,cN moN­1ý`}œÿ¦)È‚‰eRù¸!âQÂÛ¾þñ:SqÏãÛð?‰Æ{=�ïIüÏ¡ìZ
+¯ÙV'%vîAt»w$:µLú§qÙm| ÙîÜ‘j�hÿØ“•u*tÕ	ËÒ¹J:QY¯mÝçXeIß·Ç+kÎüW·4j =Ñ_
^)*ÛÄµEZ¶±$-;±(–jÕ�Í-á¯~{S‚§•ž7Dy-»Û×ÌäöÙçnŽ{ñ¸×1ë—�ÀqŠÉŠh¥AXè1õà’·)êq`±EÞBŸGyü^žx'[�‡ötu¢'¢LZ¯þS&ÐâstÎ©%SÝ;[`Út5c>­y	t2ê´íà?ð‹•7%@yš?¸æÖðùæc5�“.Ô¬™Ý©ÄàžÓ(ªª]†Ñâ—Pù�ÂØÖt\&ŽÕ‘ù¾R¹uÜõÔF"Äi‚±ß¶è±ˆ õ©ÜÛÙ~„Ñ‡]'
[;/iî©L‰¨·ãšµäe$uÅÁ6´H-M½}WØßçO¥2*v�S$“IFÑüÝB4-× 8X-ÏHÂW'ê`$×ZøÊ
UÛŸÑ«¶a{ãà
ºÀ^¡„5}$3´âÖ²Ôý´ˆ�ãåZr-5f_Ò–µþ1�³ìéR>ŽhúÂpóP¬|èZ”bà)U»1‘S:‰atI�¬œŸ`[sÕz^ˆhŒØSôÌž;y^ˆôphãÖ›‘®5²<œà	Èâ˜»Ér-óìLµñ†É ²!>¾ŒŸ+š£�f²*	¤¬ù–š^-g‘	Šp}!`±•“]qÂkMI%¯]¼Ü¸n`Ô(’ó%Ì¼ ¡GßæÀll'²‹¶/p+L«IKÈí òµÅ¨Ki²ÀOÚb"á!*<ÇL¯£öDøüb�‘àÍ–+o<+›šQ�Ê¹
*çO´¿Œ%V6eIF©Üx{†Tß°ó$&A¨¥TòdhëÊ(Ff¦ Fm*<Ç¼	šÍóôÊñs¸ÂŒ¡!yæ€ÙrJ;»”hÎ{§ùR�Ñlk»ö‰â-¥ˆ�{¬<¤naÎµÇÑþ^çº�kÞ$Äó"Ý8¸ý6þñ[Ti tKáóyù#Ç.
‹±¶\Â¸ÖQ�n�›­ò/§âhž¸Ûú7bDÁˆMrÑ»#r;#Æá¦"F¦îŸë®ñT©x?­›oC­÷|fõ`òªéóêz E¦oÔA¤r1ŽýsÊð(¤˜ ð«{Wgå¿ð>s•>/Ë&ñÄýÚÀ
ZÅ1­/\]nPbœ~+DÑâïžÔwƒ\µêbo˜ú gÉ‹�¡z1âÅª“ŽÂÊ}S»×¾iàzâ´	™•�ÐÀ³‚švHAC-;}U¢´$ü<Ég<­Ú‡yË|¡ç„�î 4_ÔmÅk»ÕwAší…6Y½ß/S@Ñ0ÝFuæ 50ël…¥O¼òÞ’ë:Í*÷Ó U?`&ìvA½ñÒzBÄhøÛ]Ø7^öéMiJÙnWd¨Nm	å†;Oº•Ùœ”’ýíÝÝpûñ£Ýœ+íÂÕ9ÏôF^iCÍibgkcF¯»^i×ÎIëí&ÏÍÖ+”ýçòðÏî€'sºVì¯ùL£Xsd:ù:‘H�–›sŒ¼«Füds78 r~p o»ÒÅÆƒpú�ÿýïÿÚŠ‰Q¸®çSIŸúSz64kGíP59ÂÙ÷Ç »›Ã<Éxoø²ÏQnM6¹‡»ÔZ—[µSÏ|½»C:è<i¸µ•ÎRåIÖ‡Æj‘è-¥¥˜Ý÷Ço^ÿèûüóÏCÌ½ÈÅá§ƒ<œËÅÜª~
�NËð«ÿçF2ºy�½Syg”æ¡õÃÞÎ,“)ñ¥7¦|èÀ‹¢â_«$Ütðö5‹�ªŠ¼¯ë j�Ðü !5lZH©{{¡äê¦ÙÛ¹Öˆ�cEgD3U[oZ1Ai‚<_ê�§Ý8D:ÈrÌqA¨>ó7¡=;öâìàk¼�‚�ƒ“0.QZWð2CqèÁ
ñý�:døó§N§¢ük�›gõQ™�qÄµs Øó'žš“×šv_¹]ïa¯2"¤Œ"L,Š¢ÜDÈŒÖ)�h¶Âºˆt²óóÉáÁñáéÏ_þsïAsšâíîKºï{ñ+´ÚÄxO„“XGž>T½ü>mû$\Ž†zS¸à·<Xî<ìîð‚Ìb�ÖÆŽJŒ[Ì¸OWè›0t[Ç\Ÿ§•¢Ên9ôêGvòB¦¾–Rj´	µÉPÚ/pbMjãLLÛ”›¸”—¸ZVÑ�^ªpmÔÍ“i<ÂK)J¹ë	$�&ñ’n:à:2Š6ý®qÖÔe…HRk„íÑ=®ÇÓ�?)©rz%á¿êT“ÖhyºeBEj-ÛšàEùDãtf¢ÎCŒy™ñ@µ“V¶í­­6$×jn«×RHI]ŠT;c©åaäéÃíó¾’æ¨7§Ì7á¿œç¨°gË§_åy
½¨Ã¾#
É¶Ñ· ugÑTú¹<ë^ø¯²•ë~ËóÚÍ*ÌYÓàãÂ ù«ÍáÀPøú†‚}-|Ö}¦Cjx?.æM;ÐÀÄÀ‚Q0X‹i”¿¦¸ÇÓˆV˜;›“ý:Íny»Ýnš¶Á¾½®Üv±%nÐ1{Órª»h|7QÞnµ©í¤ÂGû‡ÄL=Mø‚£¶âîDE#šUÛ�PdÑS4\*¹>9i²•D«ØÔh'é´ƒmt†IþàZwÕ†§Ÿóä\Ò™	óüL9N0Ý 9 Uð2]!v²2Ècao¾Yøn‡�n¯”ÁâuÂÖwÔMNŽeB{r¹bÃ©²b'¸…Aq[Ø…ÄßödPØ#ÞÈ­=¾úøâCœÍ>o�˜ú[Äi·,y’>à?˜N�ñgï˜Šq×xX‘BY–#Œ=££ž_8±$þÅÎt6˜ÿªP¾ÝBOY…kV¶©LÛAÝlþPXð64†7Ù0�”ÝG–!aó‹Ý›·óY±ÞÀºŒæôœ/©S´Dåí1�áê®.[ª[tÁ›3"Þ)I Ý?ÖÁsßi¶!ØÝËs¹Ù—uï y
ƒw®DCÜS*ˆÇ¸œøºØvZžHñ0Ã©òÍyQí+â&Í¸R³­vÚ±eÅäH1ð®µ¦nJú¸b†÷ÅK¾`Ñ±�¾û­<[íßÌØþÁ:P—Lê	3Ž!9>ÏgWÄ`*aÔGX¼)�sÄ
0öók’RZ¼ç©c8”ê2Á=ª�ÜH¤}iâ7—&b�ö=©wwv¦î<z|gê]œ££ý[^Î;ySç1,z�¼sÂ)Ÿ
‡ÆÃŽÑW*�4XmFÉ>¾ëH{7Ìõ”Nñ˜..ª®í;ÏœËŒ´k’ªÞÕ³j›Çj�{;XÕUuƒ—’ÆÀ²›œ}ú¦;¹Xáõ‚qJW¢¼ŠÄ"Z®RÿN$Ï›SEv®¶kµn&“*gâ›ïNª¼‰¼ï’Ò.E¾h•.&X©a /˜ ¾ªz]h~tœ�ªŽõªÃŠèø<×Ñ‰až�©�Ï›oÿõìÝg­g?uÌ«Në‹öÛíwT†~	 ÓÖ°«¦{R»­B*“µê³|X§·±ŽÁ£Wm»r&Î>d"\í±�Ñ³z }óë#Ðx‰<új�nÍUï‚¥wXz>é¬¶«ÉFîU}v¶n™/T¬Ñà™ä†¡˜>ž?C‘Æÿ2¸Ï·Ÿuœ§g‡§Ñôùƒk Çsu£âÜhÝ<ëÐw®RÑX¯º±Þ]ëÔÁì„)þÿ½Ù¹f¢j©ÛEx« Ô§�F×ÔÝyƒ£bn�ôrõ¨Èª[qÀãT�Ã9{Ï‡CTÆh)×÷NøyI‚ªN7îdé2wß‹�ž:Û�*6´tS>VJ<78.¤#L»±^¡'�±cZüBµøé�¬¼fM+aîåáëÃÓÃ;Oq­¾ '\ñ…[½È÷?Çë^0‡¼�zG þbnôwÏy(¤Ap4iÙÍmÒ´¿®‘z¯7ö®ŠwòÄyv{‚€Bs^¦ 4¢ût9/Zè·æ¸[Z€{$ pvGhn²nZôp{i Ø#â‹×£±ç‡ª:Õ„— ÁMÉàZénj”£jÃo@uª�nŽÇ|,¾Â•çzÖ¹^*Q»n6ÁÄŠ´<ø‰ÁÈŽ'EØ�Ìÿïy/{ÞõI^µ™Q›®bÉ7Ç�ßÁÛê…òo«0—:;ÑÜE/cEÕÀ+xæ¥”þ]œ´*Ù„uVúÌÖC§•<ún_8(þã�½
‡o»ïü`é»ù}ÿ+³0¥ŸbÖ]Ñ	�|3Ôçìþ$¬çqUèüM.Wçžá:ç«U4«Åð¿¡ö¡ë�Íäèüwv¼nô±R†"r¥-ö­V;Uo�p,ç©ÑjªR„˜Û²•eb)Á9VúFwoñ�:.š¿ÜKZD|•“2p<�È`= FZ¼‡»ä�ÜE)”KºèTÝ¥oË¶þ¡S ­ÎûXuÙ{²ö>ïY$«˜ ¬ÙCŸÍ’Ìq­Va�¥¾†Û$S‹a]e¸žÖ\Ju§¥Çˆc†2Íñ À•
ËÀ)p\”‰®cH¡pr‘‚Y¬¯!×…ø-.åÅ/5vu±Øþ ¸¥Léô’Û³æróÞò¢†/†ö®8§59®¸Xê^ªcx…ºçÀ,‡žË«œ{ctÏM˜¶J(¶6ã6¨HÕåŒÅŒ ÆÊ+žÔ?W‚N#OA¥+üŸxÐ.:&‡Š9AEçbÍ×?9®{ã¨„M8ª‚ƒY°ûû'tŒ€:BRrxŽÛzeôÎ‡oðfý™µC(Mä=àÄYÉcˆ7‘»“Mqv}›¦0TßƒúAÙ3PBmÇäKë	a#í›£š¢´y_¼)Q1ó­Ðmß;}¥­½u^aBt8*Wp&ÎÍR¬Ð«¨Ðó+¼³N¾œ:*wøÐ¡
ŽJ­<½WÌi%»UåY¸5 ”IØ'>Nƒ!sÑ|®åš=kÏ£P&Ã†eæ7Ò7±M
»Îä'+*ÃÂÛW¿¥UýÝÜžže¸IQ1£'Ó®;¾¯årÖmÚ¨Ö¡ât‡[Ÿ+±fšRÃÐšÁœ¨
b.†[ÜGó1Îèê”r¢ÓªˆxÅrŽZ 6õRªé£:›–¾I-Ëgà:ÐA³ÊáÅŸOö«a¬Zk€j(ÍÒ¹Qia¥N¶ŠFð·]‚™<2ì�ÎÌJ™ ã³‹-YbÊII™Ur­”“âZ!Ï U¹>&NNzŽè¯%g o–tLîuÃ�5«{G³z5U'7¦§Œ‚_ýTÆÊ%]¼~Ó$EfgÙ÷*Ásý}.ÂMÌl/pù)û/ois@ù]¤jbYî¥ÊžÈ“<·¤¶Î¶ðª;“"{Ëä¸.%‚3‰­«òY�¼lÖõ™©+òRW!L_.¢ÒTS’j'E5+…÷HÅùÇG©¸·:q°’¯¿ë`’	ï/ÄßYreÖbº˜ÅÐ ð—ìÉ±È,‘™Ï}ÆX¯”e½XçŠ+Î¥\i‚7ü‡tœŒÖ¿Â6þwhÙ­Àƒ©á«�pS�g~ôº#½ºF`)Çõfæj{É‡}´1öu6‡w�¬(qy(óxçJ’n´\ E¹~1üáÊ7¨ÈÄòÑg¥)ÖUô]a2ð©4öH:Ëåo×üC4$_‘YDéðKC™çï1ï(/Y5jº[p§8.Ž$Aûç;‚]i>†ø-‰iû¢n¢y�ru±M‹
-ÄÀ<É:Ç³¾âeÂw�aßË+æ•	è>ÃF±ÉÛD^ÅGý²ª‚E+“Ù}PB#”·$ó½%eóûc{_b/•z7ã]"­ôw‡Ó0j-¤V&X¶¨<	üýœ~Ì!üÝ?p"áMAi³d!Û³|1ïï<	·»ïzõ7ö‘�¶8ÊXs§‡Î^NR)¹ŽË�oÞA{‘Œaý£@µ‡™cB í¯†Q*Ôm<ªˆvixàNDüZðl�²lËí€øú"£R·Ýíþý)Ðà½t}ñdu)¶{«Ë§tò1Y@ð¸0ÃZ˜6Ã�ßÓ"É?Ã4Ç¸CÁßù© 'LSL…¦?§*ÔfŽ>m~G§ŸŠE”Nã%4žçÉ¢Ï hÿr–`®6XS ™Š3sV…•xKj÷¯ŒH¡¹×ë:#úW-Ž ½9U
Ùz9¢Ec\”ÊJ@òOþ;dåœï?@I
rìáç|jÈÂDÓOEŸ%¯øÎm^qCi•nï”yî§Ì…ä„WV2íÜ0Çû¾w²¢Ê®Îí¯Ÿƒ¿M8š1–™!6ó.î‰¯R	JÂ„öÎcðÙ÷!U4-ûÒá�R�Á&V§LTwøR»\á¶„‰¢øÞqÀWïŽ˜Žß?Fýß>vÚ�™×¤L³e¨cž‹¡öu‘Ôåøè ME	Ò¡£©E™^±Êë¦]§œhâÝ
¶FÊÖ4X&d2•F*Á¨Lsz…ô©2]Vdâ/×àµžËœŒÚåE¹!ík¯ÌƒVä8{K…Q­ŽòœÓ Ce5; ÁUSÖ±sÅ'+5$øæ%)6ž“ÌÐ'’'Ÿ‚Š—/%0Q›�«$ËCiïÌýfîû·g�ÿË¨ümÚ$t„Å<“%á�^+VÌ-sè€¢:¶çð2wò°ŒsÜÁ9�B¶—:£²V†•¡8i2ÓW0Ô”,nO:W8^£ùVX¥ÇªHŽg4ãŠ˜xÇ‚3çá‡Ú.Ñø§u³±\Žäó­-wíWÙuze7âsO4z¯}¢õ>1Íy¯˜¯œWîÚ.Nä|pòbkëˆÓØý-ZlÝûP
H‰à¼2$Î9»|wfYåJà�:Í8…­‘L+®™GUgÇ¶ýÈ/îÌó¦N¨ÖI�±Ò7\P&Ž¾¹½ÄÍ dªGØªäà¥7Îæ`6Š–„>Ð~§/^’OspúÝËï¨@Ÿcë;ã›Q[ ®©É‚´¥y<Šó>y«ü�èzx/íö÷S‘ ƒö"ÎdÉh+M—ÊCÆò…ŒÎ±AÔFÀvÇ0:ô²�=%¯hdå;F‚ò)|ç&úÒ™„jÂÞä"Ö9"aÑäieG;ë†c�¾Ý”î¦)'G¢ÆkÂ¡ªßñf¦€Ò9ùœš+]¹3¥Í‘³ªò¾áª>{›ûü?xùûç²šxœÍ=k“Û6’ßçWà’ªŒäÕ#ãI²‰ûjöÆ»v<™ï]m.eQ$4bL‘Iy,Û©ºO÷®îî/¹~ $‚5Î%·µë�@£ÑèàSq~~.>g‘ôbq’ù«°�~±É¤¸”y‘møï§ë4’k^&±¸ˆ¼øàà‘¸wïI’	ï>„¾¸M²×2Ëg÷î‰ËÇ?¼|zùø\\½<_ýíé³g3ñ2—"ß¤2K“[¬–oÔrdá�ùFFIŠ½ˆA&ýd2
èÃl(ßJS„ñÍ8<rQ$"Ôø‰bæËEáå¯Ç‹íÿ"®
™æb8ø+é¿^$oÅ`>?ŠŸæC‘oadoÅ:*2Ï°'÷îý%ñ"ŽAŠkÉ—i”x�ÌÄ2KÖby…ðLÒF@Ö©ƒ¨·a±™L“<,’l+¼EŽ="UGÂOâ"K¢HfS@tì{H1™z™Çß½8ËM‰¿^�'¾SÎ	ákv…x9Ž¼- ‰süÏÿüh±öÂXp¾ŠPæâO"M²"ì4�BŸ§w@d‚Þ±ÂùõüÆËÌ«¨0€éÈÃ¼�±/¡RD¹É¼5üé{@`jPÈléù dðÝõõ…1:›%›‚à¯Ã ˆä­—!œERÀ¿WÇ"Í’"ñ“hÈ£ÎWð=˜ˆexó´‰ƒjÔ×Ò_ÁüÂ¼á˜O71€¸Þ¦òÊÏÂá�gá»w‘/.Ÿã'Æuypðé§â/Q²ð"q–Ä8€s~p0ÿ�Y"rå½	“>û+/¾l‘†ImÅ:y#§™Á~È‘ˆ�…A"¹ˆ’›ÐX/"@?Ùd@§e�LÒ$l`"q±ÂIþZ|ä•M
ãÍ!ßi�	�®y‘‹×R¦"õòŠ "Ï>Ìö¶…Ê8‡Y#VŒTÒ~$
 ÓÐ� ‚<Žo¢0_Ð	°R,oQz�H:2	Üòý«œø�ÅZå`¡åœgb:'Ès5=s“âR/�)Joì#›®I±¢ bÉ€œ37Î©shlprC)Ó0´ÅVÌëõK1‚. ÜË’i}šM¨-ß„ž ˜$lš@ ß÷eJ6dÑäØ$ø)ˆ··–¨Ö�Zg%ÿŠ‡‚8$§xJü4î–y
ýHT)k¯ ž�q ¼Ø@+	s¡YD¢”ÊL"<Íµ8ƒŒQŠœ¦dxåˆ×|�ŒBÿL¿E.{4ë” ÓÐNÌ))1Û Ž4Yq,?£8|ªøñh&ž”2&ž‘AÞ?cù	¥	/�…	ejü©¸U‹�Ï2é(Ä$!ðÁÛ	JØìÄéI•fÀ=™?µªÎ±îÓr>¸ÁE–ü%æ'iÊ¸Ìƒ+†œ	ãÎM$‹$F‘EæJ!æìtŽñ9ˆ6é*´&¤ß©¶Œß`%0$À5aËé›¯>°`NÄI(™»àl)Z—ë™ÊP°ÅÔÈ�†‹¬'¿ß¬2›ëŸ×ÉkçåOæ*ú½†y¹’~&ýëÜ+¼LõË,šO*ú\*V"£
˜„Ë-p,ª€±È>ÌÄÁ|>‡¶«ƒ:ã…àœi¨qðøm*Qgââäêªs^½¢N_½2�”fæd)æv?s�¥¢T_<Y“ÉþÛä'sˆ`fV noì±ö\ùe×,2œb¯-ÀÉ	³Û›|	`6•ùTSŒ×â“%Ðo&|SÆX©²wA¬ö	õmHåýR*•²Éš“Et#³.ùT�¸�AVw5†×[š™S#läOÄáû¤ Tžnü×²hcÝë$yæe7²,<ß°/#ñkYz²QBG•´ZYüw/
ƒz£o(6£bþŒ?¸y¹…DŠ‹“ä
4TƒÍ±T\›��PÒ·GG¦Î@§T‚OÅ&�}ÛtïViàCG¹jcè¶°æë¨}ÿ°	ý×È“)P˜=j[v›0ª±±ÆJŒÁ%‡é*bµñA´F§° ˜ÆèÞÞôÙ‘ø VÒÄøKš›Ì{­‹æ÷‰‡¾¢YÇB4ç Sh¹jSYŒ²{Ü�Ýµ,²Ðï´¨ªªªÙ[Uý3ttÐkByxî(R_Å^š¯’b0ì–ˆ
}ØR5êÃ—¨¿I8F$^Ðr•9;½žàñ€ó“8OjsÝ‡‘[ÆóñžF£§©º-üõ…Í_4b€£€ÅD:B²eÛ‘x¦Ã<Ç3ˆ­;lW
Ó�U¨ï�µÞ)HÝÜ“DZþ9¸ßÞ�DFV¯k¹NqXŠ·É® “Pä
,¸
ßQxYà‰?~Œ›+RÑ[ZÆ“éf5Šž‡ë
ð,{¿ƒµ=ö7�
$®âM$NˆÂ³¦ËxqÁ4	N¨ˆ˜•Ã—jbôt… ?OS,B_áI«\¢Àºúç�¥¬©	oêâajÀ‚_�cÕ<J|þÎ%�Ú Î¦* ÛøÖôå*u ë[bo®€'âo¸NF(´°aÀæ>ê6š&#šè4ˆÖ@ŠÛ¸-dkG%;÷zÙ`ÏŽÞ5‡ÔÕÍ»®Ñ Ól%z8Í FÌÚt�é;OªóaÓg1ÑGSò ûèÉ�^Ð¶9ÔÖB¼jµx¯É�«àÚRÚÔ´_–š–—ñÓrÍ?xBÊ–ÝÜ‘À_.õžo¢"„]Ñ©y-p­Ê×®· þúÔDˆcÄ£OåµFº‡NÖZ÷{y«ÿÔ£7ŠõO&y%ºVwµ"ªÍ‘·¤´ÑKIË¥³œ(ôØ—Ðª�I�ŽÒä`%ÖK�
Ñh!Æ¡Ž<˜Nèõ�—ë¦¯U�–ïóˆBCüÊòŠ×
�ñÙiÀ½LÇÃÌÊÿæ� à`)dÎr 8?ÌüMäeUoû/šËÁ�ÚDA|¨ÝŒA½ë‰xCÇ^0Ë0Ú@>/JàÏ*Æ6kCÜgIä"2l1F—É€öñ(v0X¨¤â¹ë	·ÿ0Œs^âëOèÿx°Èóh“H/WÇc´ÈàO."ŒÎÊ(ÈÊT“½
ÐÁß0*3Œ„&àWºYÀJói­LÇÂ±Iû——qøËÆúŽcàlåø!¦ðŽñA9õoKår˜€ÖÊç¨Ïát[ ÈÕÛ7js£Ùú-º&¸³èÚâ'Úúx#¯�ãnÖ»jL³ækhÍäotWynÑÂ��M£
ëGçpòã¿É­«œÇw
¾ˆ�£B˜Ÿ©È˜-’„öˆª�ëºVsöŒŸÏ’M\8öµ7–	þà	êE¿ÔôŠÖ'Å°�ß²’9ƒC {XÂ£_%¨ÃGJ‹ôQ®•‘QêõªR¬Ï+Ikñl:­Qª¶`ƒû¡Ô,õ9®ù@« Ý$®ç={¯™·fï–e2lx…Åþ«Ï“õ{¯>m4:Ý*Ëœu(¿²]#vSÎš>¾o+£T0:œ®Ï¼ºO‹’÷iTr�Ý¨ÛWzŠc¾,[ Ïó”%È*Ô,o—Œh•kÝ}%³7¡O>ØÕ1÷å'ùVNyé—Ö	PsÎÞAed1Ô‰K2ð¸âñn�¿É2`¡9 ÖÆ‚I¡!öËt	xU$‘ŸK9+»¼¨šh�›ÔtL¢À‚Ü« ¯|ÛX4¬„©z+ Ö¤( LpºEƒ;XVw8CnX‡¹$5­ôþ£e‹å�lÏdgKí�º¼”�P˜Oâ F=°é1ªS£«
@ÍôLYÕ©^..:Nz;€ßÍ9QiJ5êe�Œpm*3ËÂò×µ÷P-].G6+N–î6~ïE²ø,479æÇŸ(LP˜À�?‰_i(y²,Ø/ØƒnÊ° œz…¿r@yMÒý`xŒLyô+ô<6`¸ÛàÔ¿ÈÒ•ç}Z"A~üé
Ã.¢C'+%ò´¦=¸¦8»|yÞmÊ»U·6i%¬œº©O/mºÞÙKYYùá{ö¥…wœ³&oí§\Õà7Vd˜úáî+´ºõ²ÀXƒIFy½îÅ·T Þÿk$ÇRT+"LªV¤Åí4ùs‰F÷0* *N.ž:‘®-g œRË‘|…+)si„«­W…µŠ¢š˜CcÂ¢ôU�»è5ƒ€XŸTy`"Á$¢’°˜…8öÐ…®mÂ]&üu¢Ö\¤PÏV›øõLÀ?qr�ÈQcUhÊ®9•$üŠŽH¢�{Z�æ6Q	‚Œa–7*©îÛëGÌØ›‰ÁP<|Tµ|d‚q(�½œè6×¤Ÿ3ÝÓ!€=�¶øå˜BN4ø˜=d%â™yVÃ=}xÆ¸�_g6‡oÅE�g njxVñÑ‹*25*Ù|$Î0oÐ\ü¹\ìm‰Áù)T‹‘
Ä�@*o0þÜ¹Rè3kMuèo»cc…Û¢ 37kô#ì5Ö€ÒXÑÕõ¢ð&ÆÓa},æÂ§]ÜDBÚÌÕ¸d ¯ºð0 FÑ»�T|Î6ñsB	~†Ð ùì
¶îCæî½aðôËš}¶…ÕfG‹åÜk
w"VUíƒYƒÄ´”)s91‚ýÐ6˜g'ÞFÝ=2@zêÛ=¦~Ï@ÆÑçÉè�[§n4S µro°ÝG•™é¬bPe´s†öÕ˜	´O,¤/¨^a’½ðêAéoŸàJ¥GÿO‚+,j¦½­©bè³2¥™†¬ÃJ@�›2ÀJ¼·^ØÅz‰P2]ƒbC©ÎJéOÄcÞW[í5…bEgÌÒ2B#žs–®ÂåÏL(!w�…€òi¯~x†;c€ûM{Ñ°-ºó^È_(é|€_¢2®£$së�ÃUÝ`1RIÐ ÿ¸b3f„ƒj
ªjÛc/;¢µ‘GõøRL§ój„«8Ñ_ÓŠ-•L•;ÃN^¾�ý=ƒOÔPpÒ=&¾ãï¡ðnq{1XLr‰Icƒá3¨(=œÜ®d&ò£p¢·™Fèî®†Ã	ÅhGÃÔ‰Jßâ^~üü'ñ¡Úùøþ7�ŠÉdb¦�15óÍ›âOÂdÈ®PÄG©D‡`5TJ]zL®â˜-çqKØ/\šË”^‚õ[ ¶+ b Ö®SK÷ÅÜ‹ùM”Jóÿ“¿RCµÓiÑ:6sè/4¼—oÚ¼—òÔ×`‘ã4I¢‘ ¥üH,0)³�¿–ÁXÅmöñc4ø);Ã»‚²�FgŸ6nþwh§¼OK‹4}<Ç>M›7bÛïnGdî4”p“@JøSmyg;'®L‚g§áº’€0„Kp ŒŒ#'M"gO@EIŒiÒa…±î‰�Éæªˆ4Èãlø•No ¶gÓDiRSUöÜ{Ú;÷ú2¥‰öÅø,·™—¦ú,ªÓtã�FÃàq*Öáj7ÕøãiÕÞÄEœÐi<çQ<ø‹r‰0 Í¾z¡ÁÂ4i´Ýs,©«-‘ùsÏv�Ç¤sùo¹ˆv1ôþÆ¨_�™ÿ£­^‰Y§•³‚¤¥¡r/Ï�>o³p>%{›,ªßû¬…<¿½~¤£o§²�g»»5ùùFÆ°˜òÅõõ3>šÝäe,mÈ¤+ô±Œ[Gó÷N\q"³1ùL²¢Þ_ÐñÓò}òÆ3ô`Wð„òžP®1_u^Ö8ƒ?Š¤‹ï¬ªJN]Þ‡]³-3Ø®×žl×Ì�ûÔò6Åª‡L¨´·8å“üóÅ¦P¿õÆ‘y¸A/¨ª’«ã´ÿn–áñ¿+Î_éôeÚ)[O[Qö.“`œ1×]ŸøÆ¹vî¤Ò˜øŽ‰UÙœöQËÓã®�=ƒ jët�^XûŠvræ>y¡®Pg®h•ú¯f±Þèÿ×¶ÉæµçÉ³Êúè$QÍß·¤áþæy±ŽÌÊ ¹�•—Ydîè¶†ÚdQqV¹u-lnê¬‘eyZ%Cì‡ÅYg5WÏ¬Æ˜sƒw•¤ÛQ•­Ðæ‹·i…Ò-BÐx^1�&y$r–\¢ÎpCÔ*Ô¿·²é´?æ�,H“¦ãrßi;ôå1@Ä�Ê@…Ž—±Äè\…;!iG°�õ¨é^û·ðªCÚ=Ä—.W»¹Ò1GQ¹mcMãó¤qOÇQwU7|D	žþkÙêìE<k5¬ô^ýZÃMã«rC®Ä®µˆ=¼Î7N]”ñØ‹“°
:óÏ]k+F½#@í U\ìg›0í,˜€v¦FXyS‡Å<ÏÞhd˜ëQÝP™;¥
rf™r3Ê€Ê%:Œçxw•Î?BrÍì‰ÐFKÓsæ"¯™_¥4k�Œ“ ­ËTÌÄÕ’¥XK5�€íÌ=ˆ!�BEñy³a@~fÎØh0)øó�x³a:Gq†J_2Ø¤|X‡:©v/†ºî}]—|´ø‚ör]áxn�ÍÇ•xé�Y…¼ÙÎÊ À›\-Ìð/Zk _àcxr	ï²h¬ë|I'£$û:ïñÐõD
s]ï«	˜n´\¸}¦heÒ÷:z»neli "K&9¢2…qK\+»ÕP¹»×Ãâ·ëu²õxuô1Õ¶¾íBÔµ .ý8ß MeïõhõÄ`ímùš¬8‰ÇZ–ðŽ±$�êR0ó¦´Û�Î¥¦D®üu˜Šp‰WòèË7@zÁ¶wæW¯ÙéO–^ÜÖÛÇ©®�kó[šÐñ.h¡NÔæÇcNÊ6½Öýý µƒn÷[WÍÊÞû7©vÉŒÅýëY)Ö#ZÐ°`•��ÉËûhˆa-aôÛ¦nåbì¥¡©Ô^Óì¥±j3fáÃÆœ‚.ž”q?ÅåPZªsq�çlá¿¸Ö]„77xØ]Ý1`E×vËÁM¹äÝ~œ±ç¯£ªIÚ.ˆÍAÞE•t* w ïËY•-Ÿ×ï¿ÓŠy$ž—WŽÄ%åa„×|Õ4ÔFpYÃ”W\r"Bw�ÙhWÒ:k¡!4wWdšŒÙèï®žáM˜»«½	³bãEãU’;U^½öÛu´;4xÞüý2¤wÔ GZñ (�pÔ·3Ž„‰‹!p#ñïÏŸ±€†çóF(çÈËMß0¼×tµ(%ç®ÏuFÆ¼�¬rc¦8.i¥°b7�;>�ú;*u²Öï™7‘Ø’wŠPË¾NuÝÝ×i\5;`>BLK�‰	e¯«¢H§ >Òh7©:íÇà¸€(S× ÕÙ|��¾h¥¼®¨^ÂW•e¼ÇÔ¶³´s¨µe0€gÐÕæûÒ-‡L@¨.kÎF]ÞlÙvyc¯]ëž¤6Ç/B l^¹ã•×´n›ï-_-äü6T[Qê=[„Zdî«ºÌk™}$Ìh¦×UQy3PèèîÍq¢>¢ù*YLï0íÑGõŸDcåRÛ:—!ú"fÇ½Ëöšƒ™¨¦ñˆkV^àEËUKã¢¾¿átO0r'éZ?ò¿ú©‡�¬åÏVxa8¸#:;1oŠ¦›xs½Å1¢û¡óÒ;©û¡órÃ§=>Ûmkf	ï\¸>8Py²¹,6iÚ&ÂšÊt½ætÔ�ªb ê¯W/¾×´¢»Ã»2j÷’U;XÕ‘2)Ï8ç8Þ:×”×§>«Ðõ¬ðWO[³COìBÝµêÙ›C­˜ö­ÑÖ¿kW.ÔÝµ¸cw…Õ\ÙažbøÕ)Š®õv„&¶æþs@D…"ïxƒ¨ô2�ÅàjÙÈqwŒ~±)ŽŽþüÍS9’ºÀÇ+è.­eâ§†l¼¢<õPúA$¤w •[?ïb;#PbôÉ·b5â•U±ŽæteyVäêªcåëer)3:…ÇW-~½«»bv¼Û_1ßâ¯ü¹î¯…Ìsyûx-Š¼�ù_•�Äç|[aJ¶f)Â N¢ò­Ù¿~|ccWÏgÉŒÖZ¶ÔÍ+žj¨ÌíD<Æµ)7\{)¿ÄRäŽAÕ}è®ËYpÏÚi«>±yj‡Á±4-_§Moà½3Ö÷#£ŒÎù·€µSeDùeµ®áÌ&iNñ„Š«ò2ùÇô€ªu‚†ªÎ!~<äß‡?‰‡´÷uö=Ô›‡3ÑÅ‹«ë™E˜U\Ngœó*äªy\Ö4)PÖ‡2£	&Ì%®vL%nÃŠÞ®õ•ê:Aâçv�«[/¼þ”†®–sÑäç<‰[ªÿ>quE{Yø¿Ú7‘í¥N”PìÖ$ÅÐT%_»"}öR!PsªååNgJCR!Ð­5§|ÿu¹y[»sß£p)ý-XÁ�ÊÄÂ³–
€´³ƒÍð­‘eê
Ô^›¹‹¡@;gñ¬öÛ;R:a5KwŠ‹ê‹K‰ûqµIÿ÷]n³/‘5”¤5çÏ“ \ng–"-?ˆï#g5Zðª.êíëæ–ßÁ*oå¬SQ+ÖE’¸±�R•ï™¦`¨©ÿyÒºìp[É0‚Cÿ†l?›I¦ªM­ûVšEKŠÅ{¡YÙ¨êfL³‘R¾Î&��™Ãüêø’WnîÖ*dÚ40;Úu»¼&8å¯^s2^þ]Ò�Žë6áÔÒËk­Û2´kóã
 vÍ�3ªç*Ž,Ìúö«Í–µKù-“p)×¥o«DD¯Œ i~^ª[Éìx6½ÂÆoÀl@,]W`[aºÕ §¬ù&èR�AÎ‹�®æsÎÆ‹×ñžöè½½_æÖwŽ†šdkQ‘Ã­,3Òvx³>ID
têóµ;·â·úéc<�W{Ž¦:r±æ÷}˜šU]·É¤ËÁ7`�¬çêY»-{’Dë¡»©;B2¯Þ½ël]>v6À`q[U$¾º-¥!ÐTiÛ>m­WÏèBLÝÒ6ÕDÄe‚;Z-…ÑŸÇ7ygý\@1NE+]ÅPÒX'é¬:ê.õ²°ƒm•Úºöª¯Ó¯ê:€ü“jÆ”é<Ã}{Ç»Rÿü¯ÿ6ç\Ù#³ÊHÝ««m.Æ¼ÊÕ²µƒaôÂ91-Ý(ÖªÕ±úÉkÐŒkòÛ€™UìÅ¾Á…Mzèˆf|o[­:±W�»ý:¨7ªÁ£ œZ›Vð7‘²Šõ¢£�Bà#µ�L6GïZÈïÚYu!pÔžº�ÚñØ¸›ÏñU]8À¼æl^ßÚobã¬œs¸Á¢c¤½¯Qr ‘¿Ü¯›ö�®Nªc»™¬S²MÑ£"åý¬`‘uô$ß¿¸ì¾ãâ~-¶²˜ˆfmà«€XR½
Êmb=–xóSR{ï“Ü�8¹50Òî>¢’mâu=yÛ9ÆÙRp�¸ŒÖ§,À; Ú*	œhÇ©”^oA&ãÍlxð	~™A•Ofâ“úÓ4M%üÉH5ò¢¨Öè³ÏD? –Ï¦ßéKT&¬Æ7ßó•¾.-þ1/ôu�¥6¶sETæë±…~QEùBý\,7°c®âxÄ‚øÞìÎ(2Ü­#\¥’lp‡øýTÃßÒ�Ü5E‚ü[[\b��Þ‚	e`ð©Æh�Ž7½¡ëá=º"ãáÓ[¥´e¼I;¬èë8[’,DÍå¾=ýÅuš¾mƒ­,ï�ÞCÖ€ôc—0¢ö÷.]¼ÕäÇ kÑ0ï0J‹ÉüU’Iä2štÜmð·®ÙäYXë›ýšïFÞŸ;¡"ãfUQâI÷J(S”B­°“ œõnöãGÉ¬ $âãâçgÖ¥T¢¯¶û9/ß*D‚6r&žáêÆ•ÌñV,Âd­=hÉVNzcÎw§f0)}5ó˜ËíÅ[A7B‹[°ddÐÔ6ã“–^2D¥”P™OŽÛs«´ˆ¸Â7'Ïð²Y<ì@ç#>pñ%gÑÍ¸˜¦§±øpðalÿ§VðR¬æ"äƒzi|ô� ÔÓkÄùAÒåü.4ÔSO+é‹O‡#}¼ÚUË�²ø��˜®AùåKüR>@¹S¤n_5ªÔÓët½¯©^µ×ìz­]×=º?>:6^TäTžŒ E†òÅÀîgõè ‘F1¬€S³êfuê©
m–#4ŽF¡~ån1 †j‚œ�zÔ ì‰Çl¼Ìîl‘—¾ÐDÂÈ^YW™à²Cµß*«ë÷.Uõûc†ú¸ö‚ªþHŸÊÇÑÍ·Î>ð^®‘‘¢x¿p®(Çw–Ù\zÿsú\{¾|þ
6G·é©w@µö0|0UQ;øÀ³ZÚAŽöèNxôMíZVÀ!ý/â‘0%´ž	xœµ\{s7’ÿŸŸ·I…CyH=l'»”¥¬,Ó)ÕZ¶¢Gå®|.q8’g¸ó�Ä•Uuâ>á}’ë€Á<(1ÙÄµ‘3@£ÑÝèþuà7âL¦ý·I.~.d!ÅÉbÉ…Œs/“XœE^ÜéŠ­­÷I*¼¼}q—¤72Í†[[â|ôóÕÉùè�¸¸zÛ¿øÇÉ‡Cq•I‘K™.“;l–êÙÒðVÆý@ÞÊ(Yâ(ÂI¥Ÿ,àc ƒž€1ìŽò^úEÆ³þøÈDžˆPó'òy˜	|.r/»éOV}ü;¹\f¢ ü¹ôo&É½pÆ}ñY|÷D¶‚™Ý‹)”§ž´�ÎÖÖO‰Ñt$Pô¥˜EÉÄ‹Äå.ÌçØêO@NÿÄG™pü$ö‹4•±¿:ØÅ·Þö�G…‹0ör)^íýMøI…3^àÒäVŠÞ÷±a.2o*óñq”úó0—~^¤ùyþéÙ‹™Ì3Âÿ“»¸Â\…™�¸ZF‰À€©^–…³XÈY>—"’^–÷ñ=<ón½0ò&‘¤AnCOŒ3Áø`No<Ÿbœ„K=±	È|á�ökHf†M
X¤2OCX>�å}^j .çFÊcîÕ'ÉòlŒ£¤rb
öõÛãr†cœî4œq3RX &+1ÂÇI¶±…úx‹ßÊN$ßK	ò¼ÈAó(ÝËÕR^øi¸Ì]%MW\Âôg©7ít¾ùFüÄ™Ì%Œó¬Óé“}�ßñX¤0X¸€^1ˆæcÈÁ¯4x*Oî„¼3´^`7€‘¯`îÎÛ0Y€€VZýÒÜÅÜËªªNZ»K”`31'{Y°2²pQD¹Ë¤È¢Ò«hO,Cÿ&£–d$À“ÌrM W<¤�sEv.—ø´¢VèŠó9']´jbš&¥Ž}H\àBN§À~²æÂ-‘±AÕÒ>:™TF+«�Œé5Ûµu|¢tµ¸�émèK°›8—éìÔÔï£ß‡ ô‹<-hI¡Rù9“Ó$§«ÎWnõUÏ½x:_ûôOý�Ðfœ¥þvOSX>ŠÜv®Ø†yõ—Iÿ_Èçé
`ê­@º¥VÜš#qi±¬@93ð¬f  Y€½m/“4ÏÌ0ýŒ'ªFyJ8F%IßÖ:¶é3”Q ¬ùò¾ÚËVz3à�–‘y3k4ðÛQ¶íÏ‹œE?Ë“^JìeÇl8ƒ>È`�µ ÙªæÞ�DrYjîœ8›ß¡¹†õ}Õ«žgh;˜Z Ú9,)Cíg¦�©Kø˜Q¸	ˆã@,’ @–í	}šl{Á¦S¢aÑ‘Zƒ}Š£¾LÔW†e9yàèÎKƒËä‚¡ñYIµëòêéñØ5‚åŸÁê¬k?.¿ò%\BØ»C˜ð4•Ùœ"F¶”¾øŽÃû”däM�"XÃbéùM:�w 0°¡ãKù
—T?•‡$¶©œI¢¡ZD1XÇY�QÒ‡v¸©‰ÙÈg0B ü (ÁóZ€„·¶:PX0ã ñ³mÅlãûl{ogïûþÎý½¿õ•±ÍÂø<XãÚ0{å07rUrÄÈŒ&`»©wr+ÎÀ}f±Z"v@¥ðò	}#?ë½Š«4ÅÿûŸÿ5–Ö F¨Æ!j{KœÜQð¦ÅqÄÄž{¸¡¯lÓß®¥;�s@	¡„Îö¶Ød@šÌ �i;Bl]! £Œxpá‚%/‘Ix=Àý((¥0p¨“î‰F‹ø
Ä“ÄR£–…`ÂÍI>A;÷n“0àÞâ…x†¤@))R_òRŠëƒý}é!eâ±/Žb@”«ØÓ"¦)¡WÖ¨´T_nE
¼ Üƒq ®ˆdJí­S«møoEo.|=NOŠ3PK˜IxÜZ_öQ;-‹¦ˆ	_á¬o‘LW´LŠÌeRÄ÷bBŒ²ƒ�Ô9îŒîaƒ÷‚«2M“4«;…—C€‰�©²š‰—Í;3@k^l4
5ö‰„è/Ä_RµØ†¥%Y"bS­¯ÃNçÌQO­*àÿ8éùÜ]Ð‹é8ò
 ªŸ–E&^g÷TûÖžx'W÷â|ž& ,Àêá_XÆG¼´|„^Ù-ÉS§ƒæà£ŸUÆ ,°´°Aä… r‚Ñl
H è§É$Œ«DU"H÷b_²QW‘0®­jâCYºÓ']Ë&ûŸhŸ¡ˆ`ÆP,à[JEL_÷<	ŠÂƒ}…O]',Â1 ýxð!‰Û¤éVçZL+ÅÔÉnÓþ’Òˆ"(¤�¤*ô!"Î½Û0I[Ý¡Æ!JF:
>¨hŠYŽ»M HZN.d².à!ÿ òr™æ#[uVéIu÷;8Æ/²EÌ"ÚŠ×UöHëFŒ§Û„©q”¤pRD@[­^}aÙ$	MD`9 LƒÕ°.5ie�<“%y˜ˆ‰N0Íl•QMËFÄÍxÅ÷ÓÎÉqºð nLøL?B>‰§‰ÛÑ”ƒmøß“ŽÇæLeÕÕÞ Ê6 Ô 0û}6÷`ñnóÎ,šÀÞ¤£àrÎ3o…¶Ž,ƒOƒÌÙøe€œãcš76=¥Õ€�2óíÔ[š6zÊ§2Ã¬CËÇp¾MjAóñ1uY$AõÂYÀ²Š‹ÅD¦V$ÁÈxˆqÈÇòN?wŒÑ­¤”ÉüBiRäú1ØnÖë™1Âì2õâ,»a„Àá(TÀ*À019Û’ ?� ÊFq—ccjkœøE¦ò#¿,xªbˆÙ,¬d¦Ãk*¹ýÌËÑÉg@ó3ÆW°0f¼ëŠ.Î!ê‹Ä�þdb0{H ë;Ì»ë2™Ññ§�ÏGï¯.Fï°…ú~1º¤o—'§£wŸ®èËëû{ú³³Ç^òŸWšÔ[ˆ`?�{ºóVøFÃÈ«ØT‰ð±j!.Kþ3p2ÇÀ6¤¡É©§~”(æ§2÷çb
´ø{,s,Z²,íF4LèèìÄ¢BØ»:Ë;r—£³“³õù‚
`�ÓÔÀ  á8K6 <äÉÌ	Ž=°®Þ Œýnæ,«/Ð¨�]�ýçõåùÑÇ‹“ÑÇËëóÑåùÉètûr_5¸}ýt~tzÌ_£@×§ØdwoçzggG7<�_¿ýty:¾:?}<þ/l#•˜ÜxD4OZÍzÁàD©
9Ä©€îS6×¡qkø”|ÞPyB(ò×”® _F†¦ ÷¾LÄ•piðc§Q}{\í—MÔ’t"ËË4¼Ex‹ª#tŒ%¬¡™Öç/¸*@izÙ`dOR€™d j)á4qýaùf€NÅAÇp!sç³ªY ÝKlæŠÁ` žÔ{Ñ[õ6ûÒ£%*¨\���,Ó,¼¥ãäL„DMvâ0'Jú®úÂÍÌ-uäJKžIõt#¥rh¤çÁ®Ç[ÍáÑtoªl‡_=òŒ;œÊˆ�¤Ir4»�K¾ÙÏ$*ÿv­µ®d­W�Ì ’ñ,ŸÛÄ9‰¸  nJÐ�ðËTVê™˜1@J?÷ÐÇÝšA%r°œ´ß„Kôée¢-§ÄRØ†Ð§Ì~¢òß0ÿ˜…˜uò&è„§ðH–7ßÐŸýJ«3Åø�€°Æa¾"»„Á6Eœ0ad%$m¨`SQáp0÷2+BlJ=ïaÌPwÀ÷uÍŠCñ D9•nª3²4Ì".—ég/¬g¨j{DÝæ�=år¢&	Õ|¿òšÕÏÙ;¶a«¦áŒ(wÆÛäÇèà¹J„›oÂiè“ %jLe®¿¤Þ2S¶Æ1¢i*œs†.AãGD³WâH–†À‹‡‡†¼¶é8Ýªw­%¾Ø½’üV‘zºõ¥óY	ŠS6ÁÃ¹¤©ª£° Ï›XBÆ|è8×XCûÖ²ataa óL¨�›“›$àâŒ+2W2Te¼oÖ­ÇE6îÎðßš†eÓZ}øÒk‚ÂÑ oÊq¨Ð¥÷ÃØwCv”V¢*`ÐÐËíRÈe¹Åf"+&°Šò5[k è®Ê¼âdªÝ—*Á¨²í­…¿o[�)µí­Áh�â³?Æ-@›¦Ë`8Ó…NÚ} "§¶nUÞ©UˆYã(¾c”žµ®yþÑ[Hm½åSÜc³ŸZF\Én-
Ÿ1´eßŠÎ®K˜Šò|è[98kÿìôJGk@ÙwÃæ;Ê·øøT =ê-˜dI¥»b‰ÝÝÑC�ý4¢¥ZziBµ"ÂæËèo¾½xQz¼Ò¯°[/N}f=ãƒÑ“þí/—Ž¸ÿ˜TUÏ•G	N&…ÌV¬)ágfC	©×Ç&G¡aÇ’=q÷;…,dÉ`¢l¹¿‰¥;Œü;Aé„E�³ÆXAìt‘nm6ò~îYiµ#^WM·ÛÛ·z4µu^ô…Ðtêåsð‹1¤Áà©¶@-;;Õ>õ™û°ŽS§Ò¦Ku²DBWdb&NÇtÌÐb<ó`Ö‹><†a#Xßù±zBÜ/ÂØv¸ 8KÐD rB.xÒí_¿layUkj<0ryš™á¼{¥ä6ê[Pdûº"Ê†6"qi$Ð"¤RzÔG‹„­?~m¿|VQu5=êõÓè
PÝKf�éÌÇŽ*[(æJ¨ƒ4ØYr¬­““8–iél'á8¸ D¤¬ŽÆ«„Øýœú�c±¾¿9hÏ
íF¶Ëi››=CôÓª:c=Þ–ÞÞ¥�19xíêÅã~+±²bC­ª%œÏ:&|AÛìâ»w‰_`F×m§·4ŒÕ+IŽ¦å¦JMV‰ä³óªi5‘[[]éÐ©‘$dç6:zx›äk¨1£aÆ�0]æ¡—é ‡Ï¥Ÿ¤Á?­âØa]ZøO§TŠÖçª.¾8*OUðæÐÍIàÚêwµ¸{Mò�õ)¯va•õ°°Õ,ö9Z®Á�Õ\šbÌízg0¬÷ã é]‡YW·©$»óUÂê[C¢ —OPRBT¦‚Tô¤t­:×»ÕÖÌ£ð=,TÕ…kÖ)µºø7«…M²ä˜Ž˜Ÿ7f ðÀÙf×ÅØÝùïàEo;l*QD�Ts"äñXG„¼bR+ýàY³4¹C–8#�‰n‘¹ ð„Ó©¤M}¨[&•aý2À˜¶ôÒLžÄy�¹Ï»_\@-–Þš%TCŸ¦Ï€¢ICÃ0ž&Î7$¾ùöÁœÇ*PÀÉ}û (>â®üƒñh®A”�-œ²¨H]TKó½b6Ïñ¬JR¶uClÓžbÖÔ¿û®QèV¶Õ¦k…u=ÿ&™N-€x¥ÂÛ[[VÔB…\×ZE „.Z\ßØ0¥ÖØÉ¤.v×¤;ÂùöÁšî±û¸ýíCëœ{ãæê©å«—�kM¹á5+ðKÿ³q�éÛÒ®‰C×¨³nØK(‚‹o™&KofXõH�5D™ •Þ�46¯A• 6d?¶¦óh{{¥tß«=HL9±V~¶[]ÊÁ:Ô½qeµ¨PÂM6|Þ÷š<T»Ä:nþòäŠŸuÂ`è�î¼–dÅ‹°l¾R¥—#Õªn¶-²³¡úJôòéBN~+#á¨z�+^ßß÷*Ü7ö×5(¾xñ\nWM²ëKgÒ9ÈºÓØ`Âœö‚é*;æ¢SÕ”mÄ¯�ÚzVw(ÍtÕ­,&3UkÙ·•Kù}i×€mÊ%½Žs66*'ðúîVw ~Â2K’{Ñš“¿µâ‰ÙéÖÖÉÏMw	 t´ì_¶8-ƒ1ô¦¸SG�ªÊj•TkÛè›¤6*Ô›Œ.-“¹ô™<.m¦põômmrª}�I)J5Ô*”ã=E¸š�|_Ñ”+º0ì5•»•´¤ÂºÆâÒÐ70ÏÐ wªsžâ:§*¢iož8÷–Ë(ôéXävâç2ïƒr¥·¨�vd	ø£>0>kCùfVÃ²jn·°ÀüF@þ‰pò§ |ô¦•	™Xbvž»wi`H%MÝˆá¹äÂL w–†xèØÚ·ïYê˜pj¹±Y|¬±›þAàsCà©Æô×ƒHÔ<7Áem˜ÌžWmÒú•öËøßu÷NYÞ°Îøp‹Óž¶C”É•ÖÕb3æeÓnÌ«žÞßÕÖ•PÁ®ÕÚ–×^�¶å¹¯6é±"×<xõ²<™ùÌyÌ�Ýmp$ó�h®áPé5+ îTP'Ëêê­ŽÔñ³rÉ¬1ÌëçN~n0Ÿõ‡?«gñh?Ò:íÇçwÛÎù™›Ex§¨ve¬zw­·ö¾Xçéûb›ÞëüæÛaÆÙÔWæÔ·ºv¦¸ál‡o”C3ëþ‡K²Cå»$kFAT}ðùÖ÷ùØ8_•©Ÿ'eÂÏœ'­ßBižCÕ÷pX@0ð+Wü°ÛÿënÏá‰»(ÍƒŸjÂµ‘Ë•‘.ž4ÞÇ�c––DÍñÒ
ùÍÙßqµpË]Â>b¿îFR6/rÚµðîŸåóœÔ9l?Ãù hkA]	/LŸ.r/Ïxã}Â/ËÃ’	ª«£â EÃ¼M¢œŸÒí;¨Ø:k”‹¼£ÖúL…mÁì<½ÑÞÑ1ðA·vé0|P�ïŸØG5ÅÂ¡ú`%O'Fê<PŽ^®goÝÒdÕÚ0ç	ª;(•�¤+{–,.}š �€4E7åBñD'�Ê�ÁR—z¯èÑ/í:‡y[±º%õÕLÚìë-f[(MNe s¥f@|õ¢Zý›D½×L–âU3Î­kÂÁÓŠà¿ñß?ð%+È�²òÎ(vêÕÇýþ÷†Áš'yÂ-T„ñì}‰V7V½Â ]]’ ŒBJŠ‡ÿÜ[¯�7ã[Xä¬j—¯».!	ËèÚêf7pù&“®Ó%nv=—ÉSw"ÔÕ±úã'î�­»}µn6<Béª›®°Ñ³<¥Š²9lÔ‡¶ø £³LˆèÙ ¿:ûðéè�]YpÅ_·^'¸j8êÕÖ¡u‘`}ÐiÀçÓ÷àøƒA‡Kôï£p6ÏÕñŒ±î
-¦8è¹‘å_ 4¬TCxkÔç ë3,)Šƒµ%Í—ƒ7éÛÍ4,œØ"tñË¯î0®íçe|‘ç ×éÁè!`ð&Ü<Œo„7ÁèA¿ ±À“$¾‡ßq½Ó/{P<0ZS-‰ò÷iC‡šÊy=M…"¿ÿM×vèÀ8ç%ÎIÝí#pmNç†qJÃðt2úÉ
Äd�öÑ²‡b÷û1OÃjñË½�;ÆíÂ‡Q¾Î÷TåëqÇ¤ˆŒ'»{(Z�ÄM!,¢ž*RÆHíáû-îÎ:˜ë*fÙ=©;l HPÛ
Ë	º:�¬]´‹ñ³æ‡Ê£C¼Á[!t0O*ào)„Ë(„p¾>uå#y:¶ê0×µ•sÊ7vªég£�ºNEš­Æm¦=}õ\ Þ„±ç‚kÓÂ­8Ì¶Ú  k'Äq•‡NÅ:0LÓÅöguû§l€6ïñ7âp„tzuÓÙ
ÎiÑ™<YoØ¼¼°·)LlÐ}ò®j*é°{±€„}¥TÝf0ý£õ¶PþOµJáY¿bÓ±å¤¥ÑüùèP©pP="3¿€ëv}Iúâá‚Êg:“Öm5›jPu¨³‰”~·…oz$sÞ Y¡0?MTaf‰bŠÕ‰Z}‚'oÒÿQ†üÿw[ôd #xœ340031Q0202Ó50Õ54×-IÍIM/JÌÕ--ÈÉOLI-ÒMJLÎNÍKÑMI-ÎLÏÓËMa˜£¬«Z_÷ðà³Íf§«{&´|^>_ÜÍ$ z 	•HÚ\f?ÎÈ³·õiÕŒæ~RöŽyQþgILm{uÁ6¦ y™É‰%™ùyHí<hµ¿ ²ñø´GG�Ì½#”<)dájƒÌuÌt‹�u“ós€š“rRÁþ‚{	aT÷u1ÿmª­­w4^X<›oa×Ùoj†n”¹nzfIj¢nrfrŠnn&0tÐÜSøW¬]ÄuÓRõ7³å.ýI¼;é6k¦!HîÉÌÉ,©órRÑÌMÚ-qvv¥x^~ßÅÅUÍ1¶nA3Ë¶))ºÉ9©‰yº‰EÉ@×%—”¥ê¥—•‚Ù ³âòkÄª>|~xèò{®ÖÀ™Ü2‹î¢›e©[ ŠãüÝÂÒÔÒT$—œP?{|�ÌŠ÷—	¨´Lž}¶÷ )TÌ×ç�xœ7 Èÿ°°�6Rj-x¯äX2Äé²Õí¼vd>âÜ‘J#dqHlÿ©Öío‹,#P‚{lZ±�¯¾<ñîFxœN ±ÿ°°°Fåœ;÷«¢˜0pK&ÂþSgÍ�“Z8§p%C‚QF×ië.{ŒÉ;¼ºt›“¦vók”5ðÐÔŠÖé H$¬Õ?³"!±çxœÕYÍrâH¾ë)*º#&€Æ€±»gšw,6²›ÀÝ3{�B* ¡R«$ÛôÄDììeO{ÚÇØç™ØWØÌ,•$l<?{ÛŽè0ReVf}™•zÉ¦"«˜oÙ¹JØm(î‹˜�so#BŸõ„–«ÐqÞ±_þù¯ÿüûï¬V¸WÝ‹k5öËßþÁzj“nEÈÖR'*–šUÚ­ö›£Öë£ã¯«v£â„}Åd¸Œ¹Nât“¤1Ó©Ï×l!âtÁ×Ø:Š•Ÿn´dJ\øÄ2_²ùç§­VkÎ*CùÀ^1½Ó‰Øúðë‚ûþ®Î|µå2dó”top-¢5�w<ä�í®!ýyDø¸Oø–Ù�äìfu®ÒÐƒ£FJl~Üj5ŽÛÇ�ãoZ�oÚ�7§'m¹H7ÀHZ¼†7uf^ÊãÁZé¤Úp§VëñDt ‘âìŒÁëIÂ“TãB7‚3Þ	ß¾ö6øö<ëÿeÀÒÉ*“ïuÖ‹å—/�`£ñu�}”! ´!•ôçåK6ºñ�÷Žsƒèy‰TáQ,¸¿c‹Ìvi˜¤›Â¾K	û¥ÖÀ¾WˆIÊƒì%ÛŠd­|0á<á{?�Þ°îM¿Zg‘ˆ5X„	C3ó• ãÄò^HðÖ<EP'´cáËXxÉBîƒ•îC’b´Ú‚¥· zÁCÿ^úÉš¥¶mÐÙº±·–	ð¦±À/Ù…ÚF*ùÚqŽà„¤Ü 1 Y$Ÿªcìˆ½‡má´cw2e E¤$²ÒÒUäV&2\±Š�¶b«â�’õo²Æâs*tÒŒ…Éà4�Z­€ÃqÚ(¿|kPkÅŒ{@h•!×l«-œ 44—*¾ç±ODš%*ÇÓBœÁi¨Ý‡$æ^Âà`Öh0ŽNí8'¨SÏú÷€ï,Ö‹^•|+Óð&ÛƒTÌ7¢¥~è‹0 KPm±cQº¤7“~�%ÙÉgÈIo¬OÍŒk¾d¨»Ô==¾Ì¢
]»2nhÉ[Àƒi0‘6š è}_yW’f´V‰jÞI_¨&O}	¿•ôD“£ÿ ÓWÑ »îx á"ÛIË/‚ñÇ»Sˆ%ÃkÄY–Æ…ìçLpjè…1‚fóÇ(Ìë�ß¥¡ ³¥l¿ÙVhts|ë¼6{®D(bR{ž£<?AïÌîûC©ùPY[¡º…U�¯M,¢ mv/áJåw.�á¦Ãæë$‰t§Ù|.R6—ÍŸr~ž?µž	h¹�ñàf4™’÷Î›<’ÙÖs#ŸP­lÓ ‘‡ªb†þùæ”lfnï!³¡y²Õ?b¡Œ%7±ô{šîL†<<#ðéAìîlý·ÏÈb‘*�iÔ™Œ†„=kˆÊ8‹ŠÕÜù¹ç�j¨Â•;e`‚NI•&o-¼�fq½Xå¤’?7!”f¡«ÎòhV-ahïqï¯rù¤h�Ï½_€�Ù|%’K€±R�—`³¡�ìžó\ô†ìv< o�Õ=žÍ:`ürÒÊ‡›I)ôgÉ š¡uIv—êYŒ€ SÊUÆêùŸü†Í*¡*AL
±<¬NÀ[nBÜ”/Ñ1±Ûqæó¹þ8c·;uÙ´{>p³°^��*}v{Ûï±›qÿº;þ‘}çþÈzîe÷v0EœÅ „ÚÎÒTú•jr…Ù‡îøâ}w\iWÙí°ÿý­Ë†£)ÞHø8
Yú_!ÊÃÒAZ§àV%HsÞ¿ê§IŠPvˆŠd…|+ŠÙÊ­˜%»èð*†€Ùb‡×ò¹�Ÿå-å¢CÌTH‰ðgRBÿªƒîõMn‹‹ÛñØNgùŠÙÐÿNõOŽuƒþ°çþ Æ àõ¬°êhh¼£’¿¶g¸ž˜8g~¼òüePröÒËç9KxåŒ¥w=wrÌàþ¦nƒˆáæÕÝL¬”œ,›Pl.§”ÖÑYÑÅ*×6gT¡N¦ý/TˆõçÑ,ßayNiB‚ØQí‚[vØ·BN{G�CpÀSÖ	< 3¡O6 E¥K"ÏMfÊä}Ò*t~û¿@¦öÂd.zÊ>ô2�Ÿ_Ô-
B¢²„ÎÏ…�¬ˆ„î¨õTDî¸_xÇí“‡Ý—bwºH¸f«¢Fä/Ír~“ˆ5‚äïQUÔÌ)ŠÛ$Ç­66SÅÎ–Õîlx
ãZÑÕLÛ'�Óvçøí_��ï³4�òwÅñÁszÇ*Æí/óºN<x¬`Ât¾:Ü“$êØ”û$Y�ëäª±¯ÇI«�{Þ@H;ÌÜ¹ao_CÅ+“� nm4Møè\<kÿ\!Õ£iè¦ýöW6(zšá å@Öt®Ä£ ñÿè¯¥X†Lí“Ó×o¾þæíïõåÃžø?X,Çz-x�¬�÷ôÇÔnyú{Ö49@IjSÚ�A¯ÿ	]y’ÄiÖã’Ž½&Ø¸5>iVüÃÀC‡²ë�¾±×¦ú
›-`D×Ýc3Œ¶©¥–LCÁÇšZkjbExwˆ^ËX…h[Ýƒ…�Þ_ –ô¤IRÖ«Z9ž‚ºÞË8¡( â«Ä´ÇC‹ÌK¨$3–X¥àCF %®ÇŸDŠú¯ÜdÊ¢Ú¾
�\¹ßù*ow�ÚøÄ#ö²·€„D%•÷ÀEjÓaíSàZ7‘|Œ8óÈd¹K¦ÜRŽ^‹ GuÛ]Ñ¾Ä‹C?Ò1?e‹Æ@D�•ò £Že@ê¼)Ò"v"împÀC>Þ@Ï|�ÜÌ²X@[7‡d¿”«46ŽC×Ê-9ÕK¬¿Måíœ� }çÏ¾Ík"ðØY¢6"|çL¦£q÷Ê�A­8ºƒY¿wömÖEÎ²îÌüÎ9ïNÜ40g¿•Åœ^J|K™ŽÔÇÌ€´8ñ«ýùÀ@±iã”s3OÏ0²9ÃQÏ�¹ÃgQ>Æs£«ÙÀýàÎÈ÷ÆP™Íýëþtö
´ÑÇÙõäìM¹KK×Ýfc:„ÉtrvÒÊ±t1f™��¯ŽX­¶oÑZÍ´ë�¸�î0ŠružbG¹Tu¸Y‹tE¬yäñíDMØë¢7myR¥¼Z:rMãÝzoÄÐ®B³É¸Þ…ÔfÂXYÝtîZ2£³Ã7DÔÁÑ@ÆÎá’ÙªÍ8]ø¥IFuÊ†².18'�×†¨ý–M•b×<ÜÙý4ÇO³)q¼á}¨Bc¨%miÐE®,¤’âæÌå+G¯u64àh&=(\x’�–@'¨iª²ä2 Œ©ˆ6ËA6±4ì¼d°½0îûÀ­­±¡Ê1cXœ:Ã$}°5pCçQ’ÈH ®›±ÁÑ4ëv×Šgï%\¾*qÙ<†Œ‡•ËEäèFfB•(ä90ô`*vtðÜ&ë4ÁJ�†¿“DE4ˆh‚Šû\_œ|ä21.		ì"ÖžÂ�“Ð„é"P�ó�E¾¡/4AI¹1	F* [ôºÁÜ�nŽ“	Ò–jlOØ$�ð“�ãtIKMþ¾?‡Ç ®; ˆ-dà'�:á/;á/�;ñžølGžŽSTÂf I*;nÝË¶†+Ý¾:‡7(@cUu��PN¾ØEiðÐÊQhñØµ²9žw"¼4–É£µ†­ì=ê¡å“5ÈÎÊ;°é£aÖûæUºÓþhXò¦¥2@ð]øÉ©FX
,Š¤øìà§Û&Ê¤*›ÓcÑŠ“ïK>Y_0Òð'¶«¥ï2@z1O0¯ÃáÉÑCrsò>/VZ©XBˆ,ü“æI ØQ"ƒ %´¹Ñ82ì‹JóÐ£ût8“2�.—Ò“Ægj5Ó*—Xéƒ“—Ù„a÷£³o0{ÛT–)†q€v�\¨�¹ÒvÞ…ÛãN¨ª_í‡µsEáb›Ì!‘8‹|fB‹Ì�0±]È ¹í·*\�äÆ[ŽßyêOæŠšmùŽ6[ÑG! Ðä'�‘@Üj‡8ÞBùÞv4ÖSè¡Ë4$kj85JÕS”*ùG‚rÉâÅNy˜ØKXnÛ-K3 FöÜq—�ºGJˆ‰ø¥-Ét¦|Ë!�ò`/À˜@bã†!yÖ’(›ŠÎ#ë�¯­�i{CÄC$�æM/ N„ÑIS¬È†*'ØÁµÅ—˜¸6b÷ø.£¼½Ë¤Åb­Ôœ“B_H»r™ñjç¿rçï,·ãxœ•WÍR#É¾÷SdÌ8v�¥[ ˜1¡!@bµ #1{R—º©FÝUíªj­Ã¿�/>ùäÇðóìøüeuKØ�ðœ$Ueåß—ùe*ŽãH‹Rö(7ÙRÚØ[!Ô2ž/Dæ•Ñ.ÎeU˜U”K—YUñY�.‚°úYÒDò"—vŸ¬©½$¿Àç|A“F×>	�““žêŠÎ¯;çô¤ü‚.•¿ªgÔoì�7Ô˜âoŸFã¨”^äÂ‹^DäW|¬¬ù,3Åð:zOÒ©9¼Ùö‡¬£”ÚÓ7­µ±ôuE _þõïÿþç´·7\öÏÚÛ£_þþO³¬!Oå¼±ÊÑN÷ û1>øžìR­}½$a�òrékÛÆNß­Ã£ï ùe,ÉZè›��«s± \-DU;Ê…U#G”×K§Ö6Oâƒîn
ÛT8¹Vè9Ý¨/0éVÎË2Ç·s‘ç+è#Ã×ò±rûTë)=>88H÷�g)”†¦´©I„“ÕBØ•Ð")W‰ÊYé�á‚• ÑüÌÔ:ƒÛ•1¥‡Éa÷09<9HNº½�ÇGÝ4‰¢èG‰tHÔ+xPC!ôÖï�þc_BƒkŽ9=þ ½¿OŽ>$Ýãã”fr¡P ëLÕ^êgÖü*©€ü=&4I ¾ÍÀ$&2Š^†‚Îjsl\zöQe2‰b@ß"sn´G~¸ˆ«ßÛëÑy�(×Ø©RÌ%5àyj¥îÌjÝCFR®ÑY­Š<Ô¶­5Š^²IUU¨,èlÞÉGi‘â‘5_Vlf(^i³Mún;²­|ÅòAýd8&/m©´@�6ZÇ€ŽÝHÿdì2¨…[Æé³QèªG±Â±–>¥ùº´(H7oaHÆ™Ñj^³ÊÐ›­�»mÎÚ2a3ƒõó‘q~nåøOÃç2Ú¹‘Fï&ôƒšÛÆ‰¨jä¿3Q+à�Ê�!a-243Æ· w7Í3®d¦Úpøî=¥ÍÕƒ*d
Ï~¨¯bçPŒF››ppH;g|¶Ë^_kH—§Ô¹Ô™’h˜ÌTh1S[¸À:qÄPž)SJ*”öx
Ç)[Èl”äh2nÑG¤9x£ƒ
–_’Ï.¥Ú1¨)*¥‘I·�êÒÎ]­ƒK÷È E™/‹+v…*SöÄ«R&(UxùÊÊ>¥N•"q.Ò†aÓJdKÁ½Ñi¤*ÃuôÌ
I›Å–ë9H$«²H£4MW¢,".¾Àñß%'ßFQ‘cF-ñ5½Ñ£ù"³‰2�r0³BUNv<±n±W/�O²uÃM›y³-Cq²�Ö÷HObåÂ™Ô�ÊÍ”Ò'Šéìv2�Ü~?¸9ýÝ_6ßÿº¹Onïú—ƒéùUÿæf0œ^_@îíáóƒ³þx0½¿²ºöëóåEÒßØþù,4º½›œr–7'7·ƒéàæÓé3=nî†·—ÓáàÓ`xªôƒ	Çm;ºç _¶n8.ÄL["ïÚy�H-f…<õ¶–ïÞÞ.¼¯’0œ­K¶3ŸØ¯®ÐÊ;¿='v¿J#p²«
äãÝé“œ9™�V¾Jƒ/Ü×‚G	f—G	™E|ZHïÐèðÅÿ–¢um¿ÔÄ3Q…AÜDÝwQ´ÔKˆ\šúþðÖÌ‹âGtÜq�ñŽ’vOyµ½â¿˜þêéQšÌÁÐõ¬ÃN<æÉuš¡ÛôqLwL¹ÜÁi=ãí%nº�¯&VÍçH]¸ÕnŒŒa,Ù¢¥ˆ‘ª$PÒØË
‘bìò$](Œ �3!&²;é­ÂÐB‹ç²]+ÏCYYñÆÀ^l·sµ`Ê	®\3^P/Ø!·ÙÅyÍ[�—lÃZÌ|-�[’–ù�Y×ÐÌIi~Ò&s3çáæ
ßw.¯Îï÷kð–æ¹‹-o_^O®îÏBa~`­g›IÏ9ã—ÍÉ‹M�¯Áñ<ÂÓÿ“AÜUû”DÙ/[[,¯fãñ¬F¡„ ÈY¡ð§L«Z6«Cùjµjbª¬zä—r•4:Æ+�u2+ù4WœP5í˜Ê¿ð3må´Š%W(m•ýêüh&šÿ1]S}cz³‚¬ä)ßÐe�iÜda=MZµÔªEjŠbí ×ÈfË(×Û…{ûˆçwÛ’û�¸†ø Ÿõš7r£¯4ëä­Toœ7“PYãà<WSX“šÝfŽˆî]»ª.¸Ú†×´ôÛ`yGN±ÎÇ”«éèîúS2˜~?ø	{X[oë$Î-:*ŸZ'R¬óØQøoœÍ±]~½qiÐõf�…ãõL
?¶‡h†ÿ1ííÝ€�BG·û‡åeSÉ'GO Øã?�ç$4„ëØ€–Ú<‘z •©é	í‡5ˆÿk ÂÿC2³H¿¨x�
põ# Design: Multi-Format Upload and Anti-Duplication Feature

This document describes the design for supporting all file types, media, documents, music, images, and other assets, alongside a robust anti-duplication feature based on SHA-256 (HTTP upload) and `telegram_file_unique_id` (Telegram bot upload).

## Requirements

1. **Anti-Duplication**:
   - HTTP Uploads: Compute SHA-256 hash of the uploaded file buffer. If a record with the same hash exists, return the existing file's download URL without uploading again.
   - Telegram Bot Uploads: Identify duplicates via `telegram_file_unique_id`. If the same file is forwarded/uploaded to the bot, return the existing download URL without forwarding again.
2. **Support All Formats & Media**:
   - Handle all media categories: `document`, `photo`, `video`, `audio`, `voice`, `animation`, `sticker`, `video_note`.
   - Fall back safely to `document` for any other unknown MIME types or formats.
   - Size limit for fallback/unknown formats must use the document limit (2GB).

## Architecture & Data Flow

### Database Schema Updates

We need to add a `file_hash` column to the `files` schema.

```typescript
// src/db/schema.ts
export const files = pgTable('files', {
  // ... existing fields ...
  telegramFileUniqueId: text('telegram_file_unique_id').unique().notNull(), // Add unique constraint if not present
  fileHash: text('file_hash'), // SHA-256 for HTTP uploads
  // ...
});
```

### HTTP Upload (Deduplication Flow)

1. Receive multipart form or JSON payload.
2. Generate SHA-256 hash from file buffer:
   ```typescript
   import { Bun } from 'bun';
   const hash = Bun.hash.sha256(fileBuffer).toString('hex'); // Or standard crypto
   ```
3. Check database: `select * from files where file_hash = hash`.
4. If record exists:
   - Return existing file details and download URL directly.
5. If record does not exist:
   - Run standard upload flow to Telegram storage.
   - Insert into DB including `fileHash`.

### Telegram Bot Upload (Deduplication Flow)

1. Receive update with message type (`document`, `photo`, `video`, `audio`, `voice`, `animation`, `sticker`, `video_note`).
2. Extract `file_unique_id`.
3. Check database: `select * from files where telegram_file_unique_id = uniqueId`.
4. If record exists:
   - Return existing download URL.
5. If record does not exist:
   - Forward to storage channel.
   - Insert into DB.

## Testing Strategy

- Unit tests for:
  - Hash generation.
  - Media type parsing (`getFileType`).
  - Size validation.
- Integration tests:
  - Uploading duplicate file via HTTP twice -> second upload returns first upload's `public_id`.
  - Bot receives duplicate `file_unique_id` -> returns existing link.
n²•±‹	x±HN·# S3-Compatible TeleUploader + Web File Manager

**Date:** 2026-07-06
**Status:** Approved

Objective: Transform TeleUploader into an S3-compatible storage server (Telegram-backed) with a web file manager UI. Existing upload/download API paths remain unchanged.

---

## 1. Architecture Overview

```
S3 Client (aws-cli, rclone, s3cmd, MinIO Client)
         â”‚
         â–¼  AWS SigV4 + XML
â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”�
â”‚          S3 Protocol Dispatcher     â”‚  src/routes/s3.ts
â”‚  - Parses path, query, auth headers â”‚
â”‚  - Routes to S3 operation handlers  â”‚
â”œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¤
â”‚   JSON API v1 (for Web UI)          â”‚  src/routes/web-api.ts
â”‚  - Bucket CRUD                      â”‚
â”‚  - Object list/upload/copy/delete   â”‚
â”œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¤
â”‚         Web File Manager            â”‚  src/routes/home.ts + home.html
â”‚  - Bucket browser, upload/download  â”‚
â”œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¤
â”‚       Database Layer                â”‚  src/db/buckets.ts
â”‚  - buckets, files (extended),       â”‚  src/db/multipart.ts
â”‚  - multipart_uploads, parts         â”‚
â”œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¤
â”‚       Telegram Storage Layer        â”‚  src/utils/telegram.ts (existing)
â”‚  - Single Telegram channel           â”‚
â”‚  - Files stored as Telegram docs    â”‚
â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜
```

### Component Responsibilities

| Component | Responsibility |
|-----------|----------------|
| **S3 Protocol Dispatcher** | S3 fallback route (`$`). Parse Auth header, detect SigV4, route by method+path+query |
| **S3 Operations** | ~20 operations (ListBuckets, GetObject, PutObject, ListObjectsV2, MultipartUpload, DeleteObjects, etc) |
| **Auth (SigV4)** | Verify AWS SigV4 signatures, reject invalid/missing with 403 XML error |
| **XML Builder** | Template functions to construct S3 XML responses. Parse incoming XML (DeleteObjects body) |
| **JSON API v1** | JSON wrapper for web UI to avoid XML parsing in browser |
| **Web File Manager** | Single-page app served at `/` with bucket browser, upload, delete, search |
| **DB Layer** | New tables: buckets, multipart_uploads, multipart_parts. Extended files table |
| **Telegram Storage** | Existing forwardToStorage/getFileInfo; unchanged |

### Routing Priority

Route matching order (existing unchanged, S3 added as catch-all):

| Priority | Route | Handler |
|----------|-------|---------|
| 1 | `/api/upload` | Existing upload handler |
| 2 | `/f/:public_id` | Existing file redirect |
| 3 | `/file/:public_id/info` | Existing file info |
| 4 | `/health` | Existing health |
| 5 | `/docs` / `/swagger.json` | Existing swagger |
| 6 | `/` | Web file manager UI (NEW) |
| 7 | `/api/v1/*` | JSON API for Web UI (NEW) |
| 8 | `$` (catch-all) | S3 dispatcher (NEW) |

The catch-all route (`$` in Bun.serve()) inspects the `Authorization` header:
- Contains `AWS4-HMAC-SHA256` â†’ handle as S3 request
- Otherwise â†’ 404

---

## 2. Database Schema

### 2.1 buckets Table (NEW)

```sql
CREATE TABLE buckets (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT UNIQUE NOT NULL,
  created_at TIMESTAMPTZ DEFAULT now(),
  updated_at TIMESTAMPTZ DEFAULT now()
);

-- Bucket name constraints: lowercase, no underscore, 3-63 chars (S3 spec)
```

### 2.2 files Table â€” Extended

New columns added alongside existing ones:

```sql
ALTER TABLE files ADD COLUMN bucket_id UUID REFERENCES buckets(id);
ALTER TABLE files ADD COLUMN s3_key TEXT;           -- e.g. "images/logo.png"
ALTER TABLE files ADD COLUMN storage_backend TEXT DEFAULT 'telegram';
ALTER TABLE files ADD COLUMN is_deleted BOOLEAN DEFAULT false;
ALTER TABLE files ADD COLUMN multipart_upload_id TEXT;

CREATE UNIQUE INDEX idx_files_bucket_key ON files(bucket_id, s3_key) WHERE is_deleted = false;
CREATE INDEX idx_files_bucket_prefix ON files(bucket_id, s3_key text_pattern_ops);
```

- `s3_key` is the full object key path
- `is_deleted` enables soft-delete for S3 DeleteObject
- `multipart_upload_id` links parts to their parent completed multipart object
- `public_id` (existing) remains primary identifier for Telegram redirect
- Existing non-S3 uploads have `bucket_id = NULL, s3_key = NULL`

### 2.3 multipart_uploads Table (NEW)

```sql
CREATE TABLE multipart_uploads (
  upload_id TEXT PRIMARY KEY,
  bucket_id UUID NOT NULL REFERENCES buckets(id),
  s3_key TEXT NOT NULL,
  initiated_at TIMESTAMPTZ DEFAULT now(),
  status TEXT DEFAULT 'in_progress' CHECK (status IN ('in_progress', 'completed', 'aborted')),
  initiated_by TEXT
);
```

### 2.4 multipart_parts Table (NEW)

```sql
CREATE TABLE multipart_parts (
  id SERIAL PRIMARY KEY,
  upload_id TEXT NOT NULL REFERENCES multipart_uploads(upload_id) ON DELETE CASCADE,
  part_number INT NOT NULL CHECK (part_number BETWEEN 1 AND 10000),
  telegram_file_id TEXT NOT NULL,
  telegram_file_unique_id TEXT NOT NULL,
  storage_message_id BIGINT NOT NULL,
  size_bytes BIGINT NOT NULL,
  etag TEXT NOT NULL,                     -- SHA256 hash as etag
  created_at TIMESTAMPTZ DEFAULT now(),
  UNIQUE(upload_id, part_number)
);

CREATE INDEX idx_multipart_parts_upload ON multipart_parts(upload_id, part_number);
```

### 2.5 Multipart Storage Strategy

Each part is stored as an independent file in the Telegram channel. The DB links them logically:

- **UploadPart**: forward part to Telegram via forwardToStorage â†’ record in multipart_parts
- **CompleteMultipartUpload**: create a single `files` row with `multipart_upload_id` pointing to the parts. No new Telegram upload needed.
- **GetObject for multipart object**: query parts ordered by part_number, stream sequentially via Telegram CDN URLs. Client sees a single continuous download.
- **AbortMultipartUpload**: delete DB records only (Telegram orphan files are accepted as GC is not feasible)

---

## 3. S3 Protocol Layer

### 3.1 Full Endpoint Coverage (~20 endpoints)

#### Bucket Operations

| Method | Path | Query Params | Handler Description |
|--------|------|-------------|-------------------|
| GET | `/` | â€” | List all buckets â†’ `<ListAllMyBucketsResult>` XML |
| PUT | `/{bucket}` | â€” | Create bucket (reject if exists) â†’ 200 |
| HEAD | `/{bucket}` | â€” | Check bucket exists â†’ 200 or 404 |
| DELETE | `/{bucket}` | â€” | Delete empty bucket â†’ 204 |

#### Object Operations

| Method | Path | Query / Special | Handler Description |
|--------|------|----------------|-------------------|
| GET | `/{bucket}/{key+}` | â€” | GetObject: redirect to Telegram CDN (same as existing `/f/:public_id`) |
| HEAD | `/{bucket}/{key+}` | â€” | Return metadata headers (Content-Length, Content-Type, ETag, Last-Modified) |
| PUT | `/{bucket}/{key+}` | `x-amz-copy-source`? | Without copy header: upload multipart form â†’ Telegram. With copy header: copy existing object in DB |
| DELETE | `/{bucket}/{key+}` | â€” | Soft-delete (is_deleted = true) â†’ 204 |
| POST | `/{bucket}/{key+}` | `?tagging` | Return 204 (no-op, tagging not implemented) |

#### Object Listing

| Method | Path | Query Params | Handler Description |
|--------|------|-------------|-------------------|
| GET | `/{bucket}` | â€” | ListObjects v1 â†’ `<ListBucketResult>` XML |
| GET | `/{bucket}` | `?list-type=2` | ListObjectsV2 â†’ `<ListBucketResultV2>` XML |

Supports: `prefix`, `delimiter`, `max-keys` (default 1000), `continuation-token` (v2), `marker` (v1), `encoding-type=url`.

#### Batch Operations

| Method | Path | Query Params | Handler Description |
|--------|------|-------------|-------------------|
| POST | `/{bucket}` | `?delete` | Parse XML body `<Delete><Object><Key>...</Key></Object></Delete>` â†’ soft-delete each â†’ `<DeleteResult>` XML |

#### Multipart Upload

| Method | Path | Query Params | Handler Description |
|--------|------|-------------|-------------------|
| POST | `/{bucket}/{key+}` | `?uploads` | Initiate: create multipart_uploads row â†’ `<InitiateMultipartUploadResult>` XML with UploadId |
| PUT | `/{bucket}/{key+}` | `?partNumber=N&uploadId=X` | Upload part: stream to Telegram â†’ record in multipart_parts â†’ return ETag header |
| POST | `/{bucket}/{key+}` | `?uploadId=X` | Complete: parse `<CompleteMultipartUpload><Part><PartNumber>N<ETag>...</Part></CompleteMultipartUpload>` â†’ create files row with multipart_upload_id â†’ `<CompleteMultipartUploadResult>` XML |
| DELETE | `/{bucket}/{key+}` | `?uploadId=X` | Abort: delete records, update status â†’ 204 |
| GET | `/{bucket}/{key+}` | `?uploadId=X` | List parts â†’ `<ListPartsResult>` XML |

### 3.2 AWS Signature V4 (SigV4)

Authentication process for each S3 request:

```
1. Extract Authorization header
2. Parse credential scope (date/region/service)
3. Reconstruct CanonicalRequest
4. Compute expected signing key
5. Compare signatures
6. Match â†’ proceed; Mismatch â†’ 403 Forbidden with AWS XML error
```

**Canonical Request construction:**
```
<HTTPMethod>\n
<CanonicalURI>\n
<CanonicalQueryString>\n
<CanonicalHeaders>\n
<SignedHeaders>\n
<HashedPayload>
```

**Implementation notes:**
- Only verify signature; do not validate timestamp freshness (simplified, acceptable for single-server setup behind reverse proxy)
- Hard-code `region = 'us-east-1'` (irrelevant for functionality; S3 clients accept any region)
- Single set of S3 credentials (`S3_ACCESS_KEY` + `S3_SECRET_KEY` in env)
- Chunked transfer encoding (`aws-chunked`) â€” not supported initially; client must use standard PUT. Return 501 if detected.

### 3.3 XML Serialization

No external XML library. Template strings for responses:

```typescript
// Example: ListBuckets response
const listBucketsXml = (buckets: Bucket[]) => `<?xml version="1.0" encoding="UTF-8"?>
<ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <Buckets>
    ${buckets.map(b => `<Bucket>
      <Name>${escapeXml(b.name)}</Name>
      <CreationDate>${b.createdAt.toISOString()}</CreationDate>
    </Bucket>`).join('')}
  </Buckets>
</ListAllMyBucketsResult>`;
```

**XML parsing** (only for DeleteObjects body): use simple string matching / regex on `<Key>...</Key>` tags. The DeleteObjects XML is simple and well-structured enough for this without a parser library.

### 3.4 Error Responses

All S3 errors return XML with HTTP status:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Error>
  <Code>NoSuchKey</Code>
  <Message>The specified key does not exist.</Message>
  <Resource>/bucket/key</Resource>
  <RequestId>...</RequestId>
</Error>
```

Common error codes: `NoSuchBucket`, `NoSuchKey`, `BucketAlreadyExists`, `BucketNotEmpty`, `InvalidPartOrder`, `NoSuchUpload`, `SignatureDoesNotMatch`, `AccessDenied`, `InternalError`.

### 3.5 Presigned URL Support

`GET /{bucket}/{key+}?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-SignedHeaders=host&X-Amz-Expires=3600&X-Amz-Signature=...`

For presigned GET URLs:
- Verify signature (much simpler â€” no body hash required, querystring-based)
- If valid â†’ return redirect to Telegram CDN (same as GetObject)
- If expired â†’ 403

Only presigned GET is essential; presigned PUT is optional for v1.

---

## 4. Web File Manager UI

### 4.1 Served At

Route `'/'` â†’ `home.html` (single HTML file with embedded CSS + JS)

### 4.2 Layout (responsive)

```
â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”�
â”‚ â˜° TeleUploader    â—‰ my-bucket â–¼ [+]     â”‚  â†� Top bar
â”œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¤
â”‚ â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ â”‚
â”‚ ðŸ—‚  images/            Jul 06   2 items  â”‚
â”‚ ðŸ—‚  documents/         Jul 05   5 items  â”‚
â”‚ ðŸ“„ logo.png    2.4 MB Jul 06  [â‹® â–¼]     â”‚  â†� Actions dropdown
â”‚ ðŸ“„ report.pdf  1.2 MB Jul 05  [â‹® â–¼]     â”‚
â”‚ ðŸ“„ photo.jpg   3.1 MB Jul 04  [â‹® â–¼]     â”‚
â”‚ â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ â”‚
â”‚                              â†‘ Load more â”‚
â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜
â”‚ Drag & drop upload area (footer)        â”‚
â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜
```

### 4.3 Features

| Feature | Implementation |
|---------|----------------|
| **Bucket selector** | Dropdown, fetches bucket list from `/api/v1/buckets` |
| **Create bucket** | Prompt for name â†’ POST `/api/v1/buckets` |
| **Delete bucket** | Confirm â†’ DELETE `/api/v1/buckets/{name}` |
| **Object listing** | GET `/api/v1/buckets/{name}/objects?prefix=X&delimiter=/` |
| **Folder navigation** | Breadcrumb from prefix, click to drill down |
| **Upload** | Drag-drop or click â†’ POST `/api/v1/buckets/{name}/upload` |
| **Upload progress** | XMLHttpRequest.upload.onprogress |
| **Download** | Direct download via GET `/api/v1/buckets/{name}/download/{key}` |
| **Copy link** | Copy full S3 URL to clipboard |
| **Delete** | Confirm â†’ soft delete |
| **Search** | Filter by key prefix (debounced) |
| **Credentials info** | Show S3_ACCESS_KEY + S3_SECRET_KEY (from env) in a modal |
| **Dark/light mode** | CSS variables, @media prefers-color-scheme |

### 4.4 Web API v1 (JSON)

These endpoints enable the UI without XML:

| Method | Path | Response |
|--------|------|----------|
| GET | `/api/v1/buckets` | `{buckets: [{id, name, createdAt, objectCount}]}` |
| POST | `/api/v1/buckets` | `{id, name}` |
| DELETE | `/api/v1/buckets/{name}` | `{success: true}` |
| GET | `/api/v1/buckets/{name}/objects?prefix=&delimiter=/&continuationToken=` | `{objects, prefixes, isTruncated}` |
| POST | `/api/v1/buckets/{name}/upload` | Multipart form â†’ `{key, size, etag}` |
| DELETE | `/api/v1/buckets/{name}/{key+}` | `{success: true}` |
| POST | `/api/v1/buckets/{name}/copy` | `{sourceKey, destKey}` |

### 4.5 Download via Web UI

For the web UI, download doesn't use S3 headers. Instead:
- `GET /api/v1/buckets/{name}/download/{key+}` â†’ redirects to Telegram CDN, similar to `/f/:public_id`
- This bypasses S3 auth (which the browser can't do with SigV4)

---

## 5. Existing Routes â€” Unchanged

All existing functionality remains intact:

- `POST /api/upload` â€” multipart + JSON file upload
- `GET /f/:public_id` â€” file redirect to Telegram CDN
- `GET /file/:public_id/info` â€” file metadata
- `GET /health` â€” database health
- `GET /docs` + `/swagger.json` â€” API docs

The existing upload flow does not interact with S3 bucket/keys. This is intentional: the JSON API remains for simple programmatic upload without S3 complexity.

---

## 6. Security Model

| Layer | Mechanism |
|-------|-----------|
| **S3 API** | SigV4 signature verification. Single credential pair from env |
| **Web UI** | No auth (internal tool). Relies on network-level security / reverse proxy |
| **JSON API** (existing) | Rate-limited only (same as now). No additional auth |
| **Rate limiting** | Applied to S3 operations per IP |

---

## 7. Implementation Order

The following build sequence minimizes blocked dependencies:

| Phase | Tasks | Depends On |
|-------|-------|------------|
| **1. DB schema** | Create `buckets`, `multipart_uploads`, `multipart_parts` tables. Migrate existing DB | Nothing |
| **2. DB layer** | CRUD functions for buckets, multipart, files extended | Phase 1 |
| **3. S3 Auth + XML** | SigV4 verification, XML builder templates | Nothing (parallel with 1) |
| **4. S3 Bucket Ops** | ListBuckets, CreateBucket, HeadBucket, DeleteBucket | Phase 1, 3 |
| **5. S3 Object Ops** | PutObject, GetObject, HeadObject, DeleteObject, CopyObject, DeleteObjects | Phase 2, 3, 4 |
| **6. S3 Listing** | ListObjects, ListObjectsV2 | Phase 2, 3, 4 |
| **7. S3 Multipart** | CreateMultipartUpload, UploadPart, Complete, Abort, ListParts | Phase 2, 3, 4 |
| **8. S3 Dispatcher** | Wire up catch-all route with auth detection | Phase 4-7 |
| **9. JSON Web API** | All `/api/v1/*` endpoints | Phase 2, 4 |
| **10. Web UI** | home.html with bucket browser, upload, delete, search | Phase 9 |
| **11. Tests** | S3 auth test, bucket operations test, object operations test, multipart test | Phase 4-8 |

---

## 8. Configuration (New Env Vars)

```env
# S3-compatible API credentials
S3_ACCESS_KEY=teleuploader-admin       # Access key for SigV4 auth
S3_SECRET_KEY=your-secret-key-here     # Secret key for SigV4 auth
S3_DEFAULT_REGION=us-east-1            # S3 region (cosmetic, affects signature scope)

# Existing vars unchanged:
# BOT_TOKEN, ADDITIONAL_BOT_TOKENS, STORAGE_CHANNEL_ID, BASE_URL, ...
```

---

## 9. Testing Strategy

| Test Focus | Scope | 
|------------|-------|
| SigV4 auth | Verify signature verification, reject invalid signatures |
| S3 Bucket operations | Create, list, head, delete buckets |
| S3 Object operations | Put, get, head, delete objects (via Telegram) |
| S3 ListObjects | Pagination, prefix, delimiter, continuation token |
| S3 Multipart | Create, upload parts, complete, abort |
| S3 DeleteObjects | Batch delete XML body parsing |
| S3 Error responses | Correct XML error format per status code |
| Web API | JSON endpoints return correct data |
| Non-S3 routes | Existing routes still work (regression) |

---

## 10. Open Questions / Future

1. **Presigned URL support** â€” GET presigned URLs in v1; PUT presigned for v2
2. **CORS headers** â€” if web UI served from different origin than S3 API calls
3. **Versioning** â€” not supported (single version per key)
4. **ACL / Bucket policies** â€” not supported (single user model)
5. **Lifecycle rules** â€” not supported
6. **Website hosting** â€” not supported
7. **Default bucket** â€” consider creating a default bucket on first start for convenience
8. **Upload via S3 API directly to web UI** â€” web UI could also call S3 API directly for maximum compatibility demo
9. **S3-compatible client list** â€” tested clients: aws-cli, rclone, s3cmd, MinIO Client, Cyberduck
 Î	.¹øxœ•XasÛ6ýÎ_�NîC;•(ÛqÒœgzsŽâ&¾ÆçŒí¤ÓO&DB"*à dÝ¯¿· HArÒ›Nf›‹Ý·oßò{/�àìN´Ú	ÆUÅæ×³ù;v#W†;©{'¬\©,û›s‡Š}vrözzòfzröÃûdtÕ¯­dV¬¹ájÅ:m;?99™°Jt�Þ±Ë§íÎ:ÑV¬’LÓ2±éì6«jÇŒØcëŒ~Ú±{x•s+ºš›W<ow¹¬&ìÝ[¶‘œu«…îU)ŽÒ“­QÂ²Ó““üôì4?}s’¿9»x}þò,gït¹fö`¸XÊõÌßv:¿f�Xñr—gÙ‹ì½æM–Ýè�`®p¦ÓV:mvpT®¤bK£[
ÔG¾`N3Î”ØÆÀ%‹)x¸^‡W—%ÅÏÆ´B9¶lôgpÇ¢æ8ÝÈµ;>ô‹qKÈA';ÑH%àå7+á’Ó.²lÏ‘Ê"+¥¸`ÅJº<D#Ç›¶ÀšÛ­¯nv®¾*ÙYA�ïö–Xñ ñÙ]zyÿG"‚ÍÙ½HŒÍÒÍ9Yönc[ë¾©˜éã½Ó-0Uò¦Ahëz[#ˆhÑr©Š��yoí¼ØÝrxP„lHºTÎï9Ù;ÙðE^êöÂö�0?ÏÝËaò¡F0i™ÒL<Ië$`[Ðëº_Ì¶Ú¬)S¶  ÂcÁÓ§•4¢¤Ø‘­"ä7·uÁxcžeÛ5‚îo=¢:*ŸÚ¡ :îê‹Œ±Óœ]Súš†ÞU	UJD¯ Þ_´AÀfÀ€›-z‰8ná!{Û+Zð2g÷µìü	Ñl©•OŽBCO¿|º;P´”SÇ�í>Ïa‡Ll�€(ZÚ*†Íu„TµÙÈRP¾¢IÚý*GÝÃ-"r‡°àz;kôÊ‡ñÅãR³6µ�(åR–@;Ûp#ù¢¬ÑzÝÓ�ŒîW5‹”F`Ü	¤¶¥Uì²CLyYgÙgøx�‚r‹Œ6c\�	³ó°IªÔÕ(íÃRÝÞ9Œãt¬êQz5p²¢C±j`7’dZÖ¾˜%°‚+Z ‰žmk‰ó[½!hYQ”¯fø*ÐìPú½x—Â•ïKÝ¡®³ôŠ€™c.PÕ3òÙ.ð°`r	ÿY¥¡Ü�ÐôpÏ3 k^ïË*‚fÏm™ÏwóH»DÈžUÂ,Æ}×6ÈuÃ×‡Ù@–6•7U¢wÁ[ì<³ø¨„+Ï(Å{ô£¦ ®¹VK¹ê±ñz”[u”©Q;€ƒÜùšFåSûÁzœä‘g¯VPMd™1ÄŒ/rÝ[J8áiÃ„ý¶wL/Ç€Un�ô‘TšgràI³ß/o>Nµ¥Q¼¢H¯/`ÚGuxTVÜ”5œ(îx°ž[:Ù
‚“�B€	ð•‰¦Ûøs�¿3mK½BMæDtð˜8
Ãö-âÄå�6„E–-a�©¡©þ3ÎÞF¤SYK8¤\ÿvG,‹¢Øñ¶É´"î£ô?cè�‹Ã_ŒMí åáœñ„?ô"žK¡åšéÞ5n�ï{T¨l‰Û„ñÂj#�VTºÛwXQ,ü2OÅËãSºQpá‘yðØ“r@ØoÀH(J‚Agä†Št-v¡¼�EAd¯& ‹€Y(¶gäÙO9»zò"j¤¯Ñá‘)‘ù7Ñ›|6V›N•žneEò'ã-+ÈÈ+ô`8[
‚+9
Áû?F{ ÜÔ²¤	-°"Œ�ûš½÷-û¡N_²�-Èû1�QÉ½
§v¬ˆ8¯ÄSÙôDõ>|ä×¨†°xÃ{ÏqDÂ„55oÛë­-Ðˆ ;”ptG¨SÈÙdÂ„j«E�ÂMÝ„j#*c‘Ò ”†V,’±ÐÓT€«ê½× !²/àøÐ´¾J_ó´²³l\`ái§ k<K�¿^ý^\Œˆ"tªpgÏqœà·­$ Oì�}>>ëÃíý"²ªµuŠ·~Ùõ§|Xñùþê+ÈbMZ1A¢"\œˆpFkW°^¢ˆ”ï1U~TÇ!¼JöMqJHûžÝÑÍ#¸EØŠ~¯ ¦–Ôí&¾Õz8·¤b· _·¢iBjÒ>z¡EÅ¤²%ð4b£ñÖ½hèÕ$}Z¢i
ÃDñþúáãåÛÇOw·ÿºš?ž•†wKáÊú±”�¸È÷?žˆ>w¥8È¢äD‘d÷ÝëÛ�~…ú-â »ý
AõL%–¼o\`ß?Ÿ¿Â<õSþòU~v~^$o)C?û<'#TþÛ‡Û›«Ynm=“Õ£¨Î^½:ý{‘yÅ(HMŠ½ä^6|5ž›pÕø¤$‚ßÿY‹¦+|?LÆGs‘ZA°“¶õ˜ªo\=òÔ3„¡Å_Sv)“Â–´ÚÐ,ƒžC‘ëmÔ.I�©þ ˆ#MHGEDÆ;pÀÈJèIhIó(²ìj(žqbØ'¬ˆe�'¡‡Ú:e�F˜™N5z,™Ì¨P§žE"	Ýår4òèDèv�ÃB~Q@‡>1†=ŒŠ¡ÆÕè"´b|ˆºiošaœþ«¥7Ej�MûÁÆ^ ¼ÝˆÞÃ™	Û#—�#¾år$·]{•ÆB„Í!¬Ä.ÎËGâ’^Qô¥£´þŠ=$òÁ‚ZUhNQ¥‡^~œ[?™x)¤)Bl%
ð ‰ü°Š‡t&u°l_äˆ)*•TêÿÙ@ÅË¨xG_ÝPPÏFçO5uP†„ÁoŠ+M±Ü„€‘¦'¢Éîãk¿�>©Ñ:oC¿wPo-ßù®éð‚E�•þŽµg›ï
Rsó†#þ6~X=ÙmçÈé�ôqªOÒÍx­B²®Œ�«`rdEÍoˆ@ZOE$
;–Ç³Â qVª^P\æB-Œ•T	Œ»�õÐCðÇ	b(—ñä"|‰p¤pjbRgÀž6DâÍÎw³n„ðqŒŠï¹�ÁÆh[DÓÖ/{Å7pŽLØ’·à�?dq�˜ÛhNéƒÙ‚|Nl$ÛÚñÓá^'!‡à7‡…‹Þ¥âpKoéît{0_''±¢yÚP©…x\ …à£[!pÛ‹naZéSm“�…DFÆêü>?u¾Pæ(à^‡E.ñc<ÈgÈØþF±É?—Þ¡[Ú�jl)M›ÞØ#žn©¹A|eÔxù�Q½ûK`¸"aØé&ý,ö×¿ÙùÑf°ûgc~„¿y.}o¢dŒ¬âó.ž}M¥lŒ_Z¢ñ�…[ÿô#D„|IÃjç—î?t{�‹¨÷HÍ?ûèéGâC%ï‚Þ]ú'”Q„ÂË„¾‡MByBX4û¦›B;~.Ük×Ñ°GCY�_Ú ëÙv$9D;F�£‡ý78?JôÿšW'­¹³xœ}Y]oÜ¶}×¯ Ð‡&éJ»þHnàôpì$5š¤Aì4½¸¸¹w—µ$ª"e{[ô¿ß3Cêc×NÑ>x%q8œ9sæó�¸<g¶j”7KS¿å_¥öÆÖâ\;³®“ä¥8SÿÕâÑáâðYºxž.Ÿˆ�­-ºkg„Ó×ªUõZ4¶õâx±XÌD¡›ÒnÅs÷ƒÛ:¯«BFXúLß4n›E±­¾Ñ­Ó¢iíÝVtX£ŠL9ÝlT»UµÊªmfŠ™8%nŒÍzi»:×-¶²¥0U[k'‹ìàð ;x¾Èžž<;>:ÌÄ¹Í¯u;¿j•^™ëù[ãµJÏ.D©×*ßfIr®¼>áDÿÂÿIòÝwâ­Ue’œ•>ù�†•2µÁÙ®k{[S¼ò�x­Uã„©Å•.õgvÎÝnLIgÒN·7´ØùÖä^\šõ¯ÇWÞµX›‰+ìàU»Ö^‡½T™æ¥ÑµßÛde[zRYv`UÚ['ÔŽ9Ï^6”‰œ“¦ë¢±¦ö3¡]£s£Êr+N¿\ŠËóŸÅÍÑLÜ¿±�*ÏuãÉ¹J•Ø ÒÒ#º	1+£–8å_ùçÈ8:—¹mt’\D*rSÕ…àï·'Ir�Œ˜’o__IñùÓ;:LÔÂ.×9�¬ö´”œ×
{®„<^QlÂ^çV»Ö¿W>ßÈ,A*å[íáÕR¸®!”9!?”NÄrëµûw–e’Ãäp¢R§�ÃŽðý(ï»Ò~:µ\sÄ< _‹Ò®M®Ê¸|&jëÅï]õÊ´ø‹²½nU%Èb–Ëþà-y)üÈ\ÞZçx© 8ª5á§Ù�¨j°J‚àa�çÍ°-8ð+s‡¿°ˆÛ˜Y±«•q1,ÞEh®j[Ó)ÍŸŠp3o�	„.KžXŽÀ?»¼%%nUË`Kº6Ÿol¥³�¯JÐ[Ù] (¿ ]Èª#° ©ø¢Õµæ¥Á¿€°œ7ÞF(ÒáO±ƒºT¼éÊ2ÄÈ×üœB×—ÅéÙ;�É²C±{�Biòí¬‡šWk¼»1­ïPYÀ©ó[ ;wF�Ï7:¿v]•rÁÀ1qúñ‚÷þS99>$ÜypØZs	N[­tKx^*¸P¡PuŠ³�59
æ3ØD‰'Ob–vŠ{&¸ŽŠ/8�o´ªhÏRmuûä	,~RÈg‹¤‚ƒA›ôÒÖ:µ«ƒ€ÒëL¡…ÄE©G ÎðÆ39 ˜Qç¥Øè²ãÿóR«™s�…öHäœµú�pªÃÖµï³`áz¼)jéœ]¼ÀºxŠÒÚë®ê+>­´W:	¯ÃYl,âè¥q™f³u\z;õåÈ.×ý¤'ˆPW*ò€ÄÁØ8¯Õ†è8‡êIpue°\¬+BÙÒ¢#=’‡‹…|LÀP¼=(t|óoBèlÛÒ‘.�æ?]]}ŒÛO^mP×Z£/ ²¿�ðÃ–r|¼êšqÈ‘¼CDGõÜvóDÆX#Á‚ZÅ¯“BB×êÚ€D½Q7þ/;S#ú–½–ï›“ù¼´ˆ2ÄœˆsF<Rˆ­„àJ¯G¡Zk†ÍÇk]ë6pÒ´#uK”â´1‘9œáƒ¾d 5Êþµ­Y›Áî¡G^SXbhCjG¦,BI¦ªêÓ�Ú`àB¬Z[±y•°QßæáØ¸Ý2¸‘„åVè»¼ì¸Þäoéiõg:t)Í�ÙJun8t­·s0XG0E—9�ˆ &1&@^A�Ú®ÔŒPf>E¾ ¨Þàh¨€æHG¸aÐ‡3„iÍ@bóÈ˜tGpÊë
Å*Õ­;þÃ)i›.Êš¾ISR†Ò	¾ß¢Üƒ×$",ÕÇ­ÔÐ˜Zú‚óGú-†‡4•Ê‰ÏWg||]8ùðþõ]c`m=ù8Bãõákt<ØÍ7\åœ³QŸÈÿ’¢<ZÎ .�þ'©g�+†ÓÄõÊ›q•öúb°AŽðÙäÇO¿üöŸ¯—G_ùŠU×T`‘ºsŠWdë=ò
9ˆ6â Ý“o‡³ö,2•$RJï U·+…³‹ñöÒvŒ¿�<†{¥xQœDÌ½ÀgþÔ¯HÔœˆº«–º¥‡dûÿŸþM;%Éå}Å3T1º`Ê›… WØÒAûØÛ,eËþÒ°Ì…ur$_ù”r84ŠDòóà•dÜ×P|&Dšž÷Ï†SÞ“m*tcîä}éø£Á‰™`	•rçž0Ï=Óœpu£L9R(÷ñuJlŠø�Ö»MD:’[—Ûàvš‹ôXŽ¿ž¦“éS ê8ÊÓ•è¢šŽò/Êay|ðLFV>˜M§’V<™ÿH'y)û,�&\FòŸfGðK@G°Z�²3êÄé†ÁîlÁG¶û=„^ã;'¸Óq" ªvïG|Úú—é�=/{Wï-y§ëµßpÅJ|(RÁËÄâ€?¦>)ä)×z°ïâ2dkP—}s'Ð
!¨µ.bá…ùãŒ$ŽÄ<Åeîy´[g�Ã@ñŠ#Ã`þìüÑÏ
o4u<Ê<w	ÀôðJiÇjd!=Aâ¸½ež.VãN½&âÏbÓ'mçG�BÔÂÁ7.BÕŒ%ns%õ‚@ÖÑØ¾öÓ$ËLÏŽ%ÿtŸ…¢q³P2¿½'ä±šôë¶µm„/ëùtqH)	…$U:
öÅ=£eƒq½ŽåÍçç	…9¥WÖòºÕMH¼œrU_£ƒQUæ]9˜ÄÒ–ª‰ÙÉ»ŠÞ™½£¹48Ç1qUCòF¯i¼�So?”�û‡P&Éi�
ã¹ý�É�„K�àG ‘3|ìõ`):Ãho˜JÂjÓ§ñVæŸ¢ò�ô}NW'òt	Jø†‚a“$?SMÆ«
–ù‚ÔîˆÏêŽê0WMxm¢Û”‡“QeõUBú
�¦ì+D¿[Vµ®!0HBR�X
gÊùBoyAUÌc6™êNò°7oj¨;¨+T4¦¤žif¾m-u`”ët® �¸ÝuMßYÍ£xª:a°äã²ælš™@nt?0Úò@Åª°€h¯.–%ß[…*Ð7°TçÔ¸>Øý;!PÍœäþ´·ðQô4}�¦Ø¥áýï_ÂðNb†æt!k‹!5'ãée|aWÒ[Í¥HÕÃŠ!«l�¼q@%‹V4NYÑÃì,3o›%ÆÊÌa¸¤à�«xÜ†ýJ]‡)§^ñhØMß5%¹�,#?)Èb…ß|1!�ã:ÆOæk¾v Ø‹·+îÙÔÝÑèsmBA	ÒìLQ÷°ÀÂ•”jø©‹5¡×é½A4HfSÌû‹ºEãoª=¦êÇ¼o^ýì©á='ƒxÝ¥ÿè#â§ƒðåÖ;‘‚=GNšÍî"×-{Ñ™Þ¿p"Ê 5P¸Ù‹v05Ü“õ»îŸ¶ÿpØ)ˆµÀÓMû¸í¹uJÏ¨}<ô�q<ÐC×¯‡ÛÂ{´y�ý%¢nù~¸*ü^üýXÆR»�)?\àÇ.×�S‘Ãîunõ¢iÎ^ù‡˜…ohè¾9ÜˆOÇÖ(,»ÕQó%†1èºÚ
M6@¿âÕƒ+\óÍ_j c»C=¨�sòTfópOŸ9h´>±aWš£\ºRq4!–~ÃÁ�öñi¼ÝßÛŽ–M7	5ÉRa¢·áÌî�Rl<nOkÄ…Œ¹OÒ�Ü{Ã™Ž~sŒÃJuI!úöAóÀ6Jš¨fRq1…è¨Ýãã¨úÈLt“¡›Šûc¿G&y|šØ™îç6ï|˜£ÐïM­«R­{ø3Š
½RØTHßvZžàtiß/Êé½× š8ÿh"Ž¾ÿ(BÄjøV‹›î7®ÓY4í(½8)OÄqøè™ÙÅÛAÑ,$ÆŸ”±Â¸PV|%8©¸¨#Åkº»�íêÚJ….ÈW¯$¹‡ûùÈ€ýiX5 ¬â){À¼˜âw·¹º�íÊ"L'‡‹‚ÿÝ�¹» xœÍ[KoÉ‘¾×¯H€°ÝMõCe�MÙÂò%‹^I¤IjvaÃ@WWew—Y]Y®Ì"Ùe{ØÓ|Øã÷¸¿H¿d¿ˆÌzuUSœ<Bê®ÌˆŒŒŒwFm‰££#ñsqK?ûY°ˆŒLžIq.µÉrûýË§ÏâRÆò]+?”™èÍ¢X†™Jûž·½}ä¹·½-ž<~òløø›á“_áá~n*£Ç‡±Ÿ‡�.ŒorM�Ž2f<okKìŒÄe¢kßD*ñ¼Ë…Aže21"P¡œúZŠ\K-|1‹}#*¢z�ãLåFêñd øgn¢¸úNÇ“¾0À-£[ 	TÈ,Ñ{ž7ç*~Æ2ã1ãG‰ˆ’8J¤8:ÍeI=à½Ï3)öÏNDàÇ1žLLs�©Z‹XÍ£ (�Ô’P€úÈ¬„Y¥X³7ËÔ;ŽÞ¿�¥8=ÓàöÖ1J —ˆýÖô[%eBËÔÏ˜#b*Í�”‰xuyyV’�ÕÓ4Ž;…·EÉ,óKåk?›KA§B&ŽYzwdôD€+;;ßüZÐvu_ø1#È¢]3ó�Å�I_c5
$ö æÊ�E¤	 «IoRÁ¼bã× ÿHêhžˆä­¸‰ÌsÚr‡±4‹’ Jcâ¼†Q2ÇÌß_©@„*È—à.oSÍ[š/@Ñ€Qâ‹p¢sšÊ…©<*S�EOåm¤}¡�éá“‘ØOÓLùÁbO¼ZM³(‡žw'öu
²ÄÈ"MÌ¾óî†îS~ÁWÌÅŽej êN\8f€?¤`�ÀŸTéÈ¨l‚U~ÀG÷ˆ¥.S1$p1$ïµãwˆkb�ä%Îå …\ç2‘€‘a!uiæÏ—@ÉH¸‚ýó¶y#R:¦Ð"]BËb¹™„2	V8Ækˆ¥å3-Wn ç+³™`•PÎ <$wbbñ�S•ÖÀh‰#,Kóœ¦|Ž']«þE¦Xõ‚‘,Æ+Ïò$°Èz‰G'Ø–òFeWý—’—�é»ýšx†Ð9•Ìu‚ÑŽ P�‚ÕM	»VòHò¡«i>…ÞU„ ±¾Ö$ª[¬¾Øs°X_C$s.áq2�#½3ˆ%Ù8žmW¹cqÜAoaŠáy“ÉÄûòù?¾|þôÓøûOPóo¢üTûè¹Œ¡÷Ùª/ê X Âœ9ÙË¬mÀP‡°Ç7d�Ö� Ç¥¹þÊ§ºØ…AQF*½‹hþíÓ�ø×7¯± ÎDÈ¡HÙ¯ý÷?š±åßÿ49\3úÄâwPÿCØÝïÚ·õÕ,Û1—Æ}óá–p\Á©_c–š’Ú¡e›¶ÈˆÜbksø2ëf9CT'”Å³azÖ¸U˜Öu¨”L 60Mr¼¶s[['z•è÷-˜qCSƒêVüÀýÈq{è‡sùÿ~4‘$žEó1<™Fð ÃuúË-|þÇ©ÑÚßßÙ’z·sžÇÂžÂe."ƒw£q2ýQ’”�áO%,b	¿B>RŽŠÀï*Q7šüí‚\-‡Jlê…ŸP,R„“6øx:/s¸–£(“ÖÍ]”SJ‘l% %”·àºÉ}¶àc¥
’¹‡x"#‚§J
=ÒB¾™V6kx�Ú¸ó­ž—ªQŽ4ÇÉ®l gK¼Ä`=½Lå-)Žà¸8´|éo@nU'ú-q`­Xý=$ÉŽµYÏÈÆmÆãd·²‡]xÞƒÖòªŒ�Ão£µ 5�Z#ºŠëKn‰¢º+&ëDe¹Ø‰¨,Œ¬dfŸ£‹öûUt-žv¢+y¸_ATÝ5¶$84j“ðZ¿¶A¾œÍ$AmÆ"S„|ˆ¹/Oõ÷V†‡`yˆÔßÎù]	N.>•.½è@Rœ
wBÉ}¸ +’˜Bmù=ìbSÉoÎgE¯Œ‚ãÓ·‚JÏžö;Ñ"¾éÄYg{Èöulã=—±wáZú	"õaç”Ü?<wt½;´qT—¡À	€Þqš›1R/iä8Péê>ŠJi‰+ck…imTõS%;UYgÖ–x°d`¸­1ŽP—Õ…†ûiÄQ-e«GLäÓÙüõáæ¤nn‹
3AQ�èQYU‰’È¸BK8 ëN¥)©ÑÖÒo­°U"5þÿrŠ¹¬³X¡„½‰e4ÏÚŒ&_ÁÄÞ,çÐ¢Ä\�¬£.�–f²å“›R³ÁtøŽF¢ŽÔ­Ë%7qop(�Î¤�ýkô¶ÁŸò5¬_q/›\K7Ú‚Ä2¬ßd·•¦JÅ�fˆ×bŠ )\¢Íž¼­[ø;NÍ€Â#åm>þ�ž‹yi)UMÜp�_Y`êÈxÖXÖ�žV¦ŽÝ>:(�6ayr%Ã¡»ae›AÍ¡dInFc•6ô&%áh«Û}m•ñ7D"®Jf-Wfó±N3zO¾%~G¥8x°ËË×GsôÄQ^Ùº²tÒ’¹…1é&y«Pî7¦îP«ù…¾:—ËåWZ¹+é:š–“…pÃM¨a$�Ô‰±á�–òAÈjÙMÞ+ˆÐ×‹©¢÷ƒpI?î ¸x ¢,ƒ«aB4Ð¦Éún,Sò4t¹pºBønätè§Ñ:Î-&�_\ÞƒªŒ)Ê2Ûý’³!Üâ¨‡½Ïõ$Ä3ôåÂÆ÷ROÞmGËÈ¬Ù–sh]ÂÚû‹{ÌýÆ¨a6ƒ×zKÙ`�×ÙQ`vììRúÊ¢!¯)8)•Vù1Ð0We²Û(ß~sS §�fÅ%PÝÄ §ïŽ–!Ù!(ëÞÊáéùE•ê¸©�hl„;´uÛQ<jÇ¨BÅîßIsÊ0�è¸»É¦²�:§Ž$Ï­KAibºŽ2“ûñp¡t;€ÿ¶6(Ã¢*o‹;™N{»ìòéÅiìRA¹Jì¦yÓÆ–Dí56UËZRð•šÕs°)•,í½(!q'.“ë5gâêpí:N–©¶Ÿx�GsÕ­G�kGÆW\´VƒÝžËì{­ö/QŸ8É¶�ŠT4DSQðýö÷ÆÂ‚³°¦Tƒk,ê0wª¯�±±Nr)€ÂŽ�xsòæx s¡aG!†2Ñëúì0u+s”Þ“ìÚDéä¬&àÔÝ�˜ÃÓM;§Á±‰–’¼óÌ�ã©oXG]À÷;5êýfº·ÄA0_XÊ±£»�ù½£šË¸[[â—#Q+åž7ˆ0A¾$ñ¼·ÍËÂ‘xW\µÚ›Åˆú‚@¦¦~Q[Ee{\ˆå(1È¢Ôxã±h\w¶s-Ï9K$­´ëƒ' #Ix°z…³ï‘ ì‘Á„µìï‰3l7Òò7,(w"ÉãøÅóâŒ¯!OÂ^ê¾<ò]!w dî¾<ÒfvûIøÏrÕ³f³‚ˆ+¹z *{ªÜ>²'ÞÊ_›Îó´š™#.Ž<x©)²*é'GÚ€K\ýmo4Õ¦}p~Kï±þéÏÏEÊópÌéŸþ,>ÍG�Î¹^Y¬Š[Í:VqØÅµ°Û¦®iÈÝÔõXDöÜÑ[iØ[Œçn°Èx÷º`šTdt2»Žðóä²ù„ÎÎb�úù+öÕg Ÿy–_¯’€®°ÒÜì¹|à„~ÔxfŸžæ�_ˆß¾`x!À�f|s‹FÏæV“F···½>w „Sa³ƒxè�Ä_VàËE¤q¤2ÕT<Ì_nÎàòµÙž ±T°0õêµÔº8ž�Äa‹1+9q]¨<	ýlEw>µÎaGÓ(æŽ½ímoHq@8Þp…_Ñ›ªp5 ˆÃ_êAõ1ù�z
ªñû¥Ê–¾) m<@«¾sS6¬yÐäbÏ&ÀÑ{i“¬Ê0O6}&æ†®…©8Í0�nvŒ\+ÏÙf%Ñ+c Ž6)%Às+\È•Ç·¾m·ˆÕlVÊ‰F:)+G6Äµ�!Úíã\¿üûÙÐ¬V•ª%’/ã»ñ›1>u¹LYÖÝ@ÀìliÍžkI³ßÂŽS£ÀR^²…‚‹�}Ï¹ÂÃ©óÌ2“gTÛöÜŠãœ�èÚ„hVT-öÖUndiåº…f;3h¨îÈ™;ibäòJŸL¸°Ãu”u¨rÝH�:×UÕquf…²ŒO{ Ú‰çîaqtKñ¸t±ßŒj5ËµùÊv�Õ[¿n"à”z‹EAÿ¥©*l¯OmŸ–U[uÈõ—ˆ‚=È¹‘)\íô˜·È½J3EÁtÕ÷ç=¡ŽÁk)&.Ð�pTÀÚe¤¯‰rw·è1hVfQÞàö½Ý‡È'ÞÓâ‰óë“òÞa/;yï—Å”VS­æ�ÙÅ¡Rwiš÷¬ ¬û’IÍÚ=²*øMµ@YšÔ‹<˜Xk�yäòaïWh˜“¶ë±uyÅ-°÷ëQ£¥ÓTÍQK’¶¢5¢þ¾�Ç˜žÛž'>+ì[ª�’ŸK¡ŠÚÛÙ¡¸‹|´À¹ƒÒ,. èØÞ¦þ´[˜>µº˜èãÒÛƒÕõùR´r;±±ÄÀÝÊ;åA'TõùÊ�qìŒ¤°.Ú5÷AÇb1Dƒ¸4vVÉ7�ýt§>µUök©ëÆ“ýÒìžª9¡N9h�Õî¸Øþ°éYü¿3ìÙFö×€?w ¯Åƒ÷õÃ>_ªV1[Ëy~Tµnœ¾ì½ÒýÃ™ûÓéyúT¶=Á:Ã”Ë‚ú“²©ÂÛgG0Yßà¤–Ë¶û})œ+:›+ìe§A;…nçÇëå,Ieðuá¥º°W>Íq¼µ'ðO¢n’*&¬2DzB±aWFj1Ââì9YœKCòw’ÌT¯ðÝü +7¾¬O cpa›5¿¹|Ñ3¾�H|�+•�/êh^”ùÏIóúyÏª³Yº¼|œØt‡\´í¾0>å7!|ï1õÜ
Ë[ºIs	i­;yàÊNÔtÌÅ;Ú¿¶˜Úu„þbÛYC~ƒëVª:aw7°õnŠ"Ê…2ˆ¸zÿ€ñK|‡ÈÁyi�iª^R°ÁSG˜KÓÿ‰S.Á¹©
Š6‘«§ö—�”V0Ú¥4¾Í1šp£"w”ÛQè©¸u�ƒtê*§=�ˆ0ÒW-èB¶ }éá‘Hè'QLhlö²T¤& ¢êží|S³Í …¼Ò&Î2YÔ§1'«Ô+^}@ºPF„4% k˜(‡9X!dª—eRqšÒCžnc|Íì¤ã+n\]ÿÇç%¶Òë7Z| Ä—JñË%ÇTþ(NfÉm %D¥�zRµ=.�E�+^9Éñ¼Ãô,›•eÊøc? �š»‹Õ	ÃM„%§«8çRWò.âÂæX±·Qž%�ë® Úþú@1·U<•õ–z^Úé6�öŸƒ€H�X~+~QÃ÷‹çâ£øØ\ŽxöV™—T‹h.Z'äÃ”unß®uB‚:r*)	üá`ûUS”ùápßúq~†%æ]U¢S/Þ^±'\/ühÒ²	³ŽrŒ+í �<.^zÉyÐ%½'v.ï»„_#’¡çÙA~‰Œ².zw&^•ù¨sº¹V«rb›WI˜»þ½QtŠL‰ÍÐ•?ÝW¯þ*Qó­"ÎèèZ{´0Ëx"îŠ¯Ì¹A Þ	P›Ý«×]h –NÚwfj¯ÍéÝñv×
öyo‰Ü4¾FºÚèYªA7|W½)¬èþj@Ù3AZNïJ±?£6ì²â@oëmo_*n×ßÙ¡Ží¿={,z7¾Û}Üçrá&G ¿¶–É«D"n-›}Ã®Q‡[1zëKl’¡Þyüøgd¶Kwºv¢-)eöÌ$<y:h¾bD¯ÐE,¿Ž\—VìAùZš1FñG™©½ hå'‚¯�Á‰”P%7æ=ƒßÒ�½xW]O ÝoÓ­?ˆó}ìÙÝKZ%ÐÅK|®I¯'å¥œÿ?j©«â°¨xœíY[oÇ~ß_q ?„TÈ)_êÈ•E–¡–ªŠ4\ ÌÑîPœpwf=³+Š¨Ú§<…ú�¾õ—ù—ô;3ËåETl ’%ˆ\Îœëw®¼GçÒv¿4%ý±’•Ü§QÊî•«’b,i(3yeEN/‹Ìˆ4ŠvvžáÈþÎíõöu{¿éî}�‡ƒR”•ãÇ‡EaÍµLé™têJGÑ½{tnÍe&ó(jÈ1ËÃó²Ì/c~Ž¤H&t‰/JC‚©Ü¨_gsêw÷(1:©¬•º¤ÊËâbz5‘šò*+U‘Éú1%“JOM B9‘äD.=U§ø¤ÐÒT.›w–ªYYVV;úz8<§{_Pkh�
=§ù¦’®tí%¢rJ_Q*31wdÆt¿×}Ô#'!Yê¨�–)ÙyL/uŠÏL2Å¿¢rbÁ:,Ž“”T�C”êZ.h•V]]áèWª”‚’L±’¥Ê!)Ì"tJ£‡½�èRZ-2H{�ãÇÖ;Š£è)	Ø_hj§<îööÚûlö´š:§Â
_[Òƒ^¯×o˜kNgêæs7w¥ÌSJ>&¯×Í4�C%°‚ÔìŽymâX8YL„�-â|+èöìKºV‚Š«KSé„õ6&#•[-õ{½¸¿×�û�{ñã½ýGîïÅµ}v‡VÈ±šîzå»G'¯ˆdµ†6Vp¹°	*x
Žv )hgç*3—°Ç¹/°7Så„FR’ùÁÙˆf‰;\¥pS*T2åëŒ	ÙBà´„*Øo”dBå@ç	|xÓj�Ú1'ÊQ. RÎÌ*™ž24ƒ—ÙUF¯ÃN1!j‰1œG3+Š®ðW Å˜KL–)§Œv±™�Éª÷×#4ŠŽQ2Ž8lÌL×Ø¢×¡•aá8(�ˆ1“	ù[üa,g ;�¬SËkÓÉ¸6ÖJ¬z,/.G¸»A-oà­k¡2��ç{^Ÿ{t¸âÁ(�FÑ‡÷?|xÿýÿÈß�÷m}Á9çö»_¸z÷õ�¿šë¿b‹ý¸&æ;6Ê±F6ü¦÷-«Pš©Ô—}¢7ŒÔƒ ØCõ ß®•Ü ±ñbøùÊ$Ó—ºTÙAo«¥ÖˆüôK›å®¿Ýe®þª¹öþo®íæŠãÙ‡sÎ§÷iA´Fäý/­Ó]ÿøïå‡')�­–›ªâwh	t•_¢}Úõ…™|öO¥¾B¡[�K*DÆœLÕýÖVx¥¸XªTLù$÷!ËR‚Rôþ#Œ��	›Í Dks%[c•IØáyx~!zÂO°€ïìÐÆ™biUúð·¿CÄè•ü§	Z ”=®è2¿Z m�ž¯Ë¾\/J[-‹v9/¤K¬*ÊHq;:‰\žýsD¡ßY íITç2X]®¾zâO8ôª	¦�ºóÓ7a 	™îÉª7vwi¥¥Ù§~t;»5ýqY4I9Ú”�†§j¬EYjˆ9FÐóÌÌö›á†¿ÛŠ˜#ž):ÄoÏÐØ…wCØ¢7/²'�þéõÅñðâäxàá‚Î¯Ds]”u¾Yæ3V}‘–×Ã{Od·õe™ú~T®_[^x®2n/1/¬‡–Ï�­McÑSV^ÆÚÌZí�S™•"�wY<Ùí×Ð,j£å²Ü¿unåô+�¤qzrözðâøøüõé€Ztzù5Wn!hå†ž<,YhšdRXú¸ÑÞ-#ûÂGvcñ[G‡0".»e—Ã�Ü+‡è_$Å�}ùsŽÝ”ŒóF+ð–)¸ÿ,*lHV$fl7vÂ7+w¿�›TÔjµéàé†*Dà˜T¥\¾GÐ¡*Íß´ÙnGÀ‡ŸþJƒ*I¤s>#†éz=á~-oöÿü!L¹·TiØq¦e6¹°Ó‹%Îƒ^>kòäµ)>­½ó�cJ¥QÔ¥~Š	ÓÛJê¿[
±Úù±½UÏízxsÓ^#¼õº×óÌènÙ�à;åÄšµ0uâ
j!{Ü…Oá°N2�ˆ[y3ÁpÉ¡±¤–	W®X3j.Œå“…°i­H¦ŒåaØ¥à«zãÒ^fÏßËy½áÁ¿$L®QÔ�1•­ÎŸ~/KîììÓWo!J‰iQ?K7;Þ%À#¦i“
3ûµAÚ×‘˜^qü=òèï��^¥¬TDÈ#�{,Ë	¥»,<´Ão³4£e–m�ê­À]#±�¬”>ßœ�ýj@ ²Ïýš§{)2®nŽäµÔ¼}ÐäLÐ!Ìà5GZHxŸ%¼hò)gx1d4‹çw[,Ë!¯²½CFƒ‹ø™«[0PÀÕ2�é�ØÒÃÀ©�³“öšE®}H•ö•ÛVœ‡ Å–‚—ë3~È©ÞP›õÖâµ�+»ÁŒÌAàc‚ÇÙ¼i?óuì¸â(/‘ÉRÞVÈ›B!™�÷Cæ}fhc‘ƒÎ ¶Çi©»!µ•nÄúZÉ+(ÜìB@5,Cü9à‚U€_¼«@ª¦	lCtçx‹T„¥d½ž8šðâËÑåœKš�·þ?½¥Ã„]Bo£·]ÿªÿáÎŒœMv•[À¬î“vËÔ»�¬Ëk0/÷[hz*¾3¼%D×„æ‚õlZ'a­˜wV*»Ã¡ã�â•r�•LDå}ËnÌÿ¾-½}aóÌh^:j)96‚©É…Ò»¼)t¹®Q²¤tá]°³C#©=�ÐBývø´Å¡±O¾ñ2WNâqXGÖF4Æ{Zv�÷
`q»uÚéºÐ‚5\!uüÎ¾âÁ¯G·ú6uCVêë-¢­¤«GÍX!ùéû´AËãÄ'PúšÛ|€+Šº È5h%Í�¢m,ÌdÚñk²Em‰éÈw*>Æ(ìGËj5Zl•cOÿ°î90ƒA&e�8_¬ž}¶úN¡X¿˜[Ô,od¤è,«”d-ØˆkåªÃêkðn¹Ë‚×B„M"n¡�W©\Æ[»æŠC‹Â®×µ%Ó77d|²*áÎ�ÚfööíÇkÞ'‹
:…:uTyŽaÓÿ,x¬4Ül›~oÑd!Ù&Ö Y´Üt)$UçÃ:ô‡Èðµ/_jUnV†ÅÏM]Ø¶&½ã.�ÝîvKÞœfä˜�æ{‹nÉ*À¹~Ø÷·S2]^k ±FÙeh¸5R[)œ¯¦¿J^Û¨Îa½È6®Çÿ*‚´Î5¯’üÃ²DXW×�ž/H¹ìPßTW¡¢.’'ÆèßD¢<ì»]xœ­SËrÚ0ÝóÛ†©eY¯ÎtA3CM	¥å‘dº“,	TŒml™W&ÿ^CC0C“iV–Îñ}èžsjõz#Š¥ÊŸêÅ¥¸ê�ÏT3·&<‚l¢$·e¤À²MfÕ|>ŸÈÇ«C\3%OãBžÙ^,�6{„.ÄWÇ"žú<›îsO¹‹p3tºí	ò§™Îú¿%_?¤DÒÅ¸³mÛßŒý…}›ôz¢ó¹QÊ¯"•îòDùÜ©Ê\ª’xG•}B/w, Ä!2p5TJäœÈt˜
<8®'"T”Cí&Q»Ø‰±Ó\¼œKœš‰‰xx:™ÿèõLÁZ©l#2ëd69ÑõUúP (º¨�Š‡°ûsÔwø�»û_ËˆAk;úw0D+ÙkßÛèuÜmUëóÝ¬ûÃªÚ~©Åô)ƒ z�PJŠ¤ò°Rž®æfX ä²wT¦¢K]j²™G™å"¬”®â!¯‘-�cû¯]<]Ûs&9àùXYñ¸Ùo2
¦ �ƒºô¢Qn7 ] ä7Ûþ:°4Úz#¼
'›ñ€loÆÅ^ÍØüË5ÁôÌ"›uóÐaÅ”¥Ò<m…]$ÇÄa˜CÁ„F˜3 fpTŽ„Ç¸KÔ{.ò›{½èˆÚSéƒ'þ~÷ÈR¥™‰£$µÇÚ°§}«²Èxœ¥VmoÛ6þž_qs‹%"©qÒ´uáI ÁZ'XÒõCW´t²XQ¤BRŽÝÖÀ>íû…ý%;J–-ù¥À:Ahòî¹»ç^È/; šPóÜr%¡�[ø.ŠE¨áÛŸÿÀ€O`Xpu^î�8—yaI~¡ ’OòtdüB§=â6)†=Ò¹º	ægõ¯�Æ²¡@rª±`)z…åbU]™å‰RgVz 
[» E|P;qÐ„„YoÍŠàCY˜ÜL�Å>@gòüäîäØ\“|„=SUª í|à,�MžåJÛÚäœ…êã2AÍ-T/'jŒZ°©ÓÿÐØ²GôÀ9ê^Ì}ÃÂ¥¤<ôií;MÄœZ«ì)õ@c¸¦@‚fžÐCÿÈ?<î¼\“1:\ ÇhÃä3Ï7@Ì““X››^TIòC•ä�ôL�s�¦ÂaM©éêÇízãÊ|¹.Yö&'Ç>YÚà¹”°îÓg¬ZõŸ¿H™‘¡Œóû“û£g÷GÏŸDã.{Ñ}xöâ¡ûdÒ�œ$ña<L¹~:šŽ?‹áäÙêÖl¿½3Ûoüü¸<«*®úA«7r¦l„‹cK‡E}Ø/ËÅ76B9¨W¯ü,=§â³²ÏV“–K–¡»	ÒŠ£•Ð®Øi8V§ÒüÖ®$cc<s½{Ywm»üJ')5ë›uÍ—LOßk–S‰´ùiüzgT¦!µBÄÁ0Õ¤¹F*JRêÊ"kiþÊ4JæêÜ*L©#–8�¹:(ëŸ»¡!x^¬Õg*7's�- –2II±rä ,Ë¹rÓ«viÄR`#H‹Z¢õ[7J†÷@Åqå­`£fŠtÅ$q¸»Ûâ'åDx}õö¢ÿøöíõùåoA¢2l	eiÄ5x9<vr›ôÏÞî.7·§oÞôK¡`%3»»-ÞË™|�PËmp)Ltúý>\Vôq9¢AŸ£¤z9R%ôû�–Êw¹¦>’J£WÝº¿ü|¸³ÅfÉ“³¸Ñˆ¦¿Ò÷
ck|sg¶D¸ä“®ƒ`Èeµ ±¡1hvQÛÉGðJåS*cÁfZ§až®· îpU‡º¯L¸ŸØLlÕ¤pƒÇ�,ˆá¯_Áê×�¨v3æ›ûÿ³£kYx 1]N¹Ìª>°”AØ[ÔÁÔÍ[M‰úi` ,ö`pEyG´ëæêZ‹à½Ò))�sšb„8…PI«]¹6ámB×¦<þRÏœ™Ëš»)l
¬ŒlÏcQä¹®4Ðù^zNÑMüO¦³Ã õ"ŒY!,Åw~qw1ø�z[EEXNæujü˜È»>½}½Ú÷’˜wÍˆç‹Q
g«é8FA¨‘Y$Š2>Òný°6Yÿc^�ûãÌÍ¶p÷L,ÕWº;CË�ÈúÛð%²²SaQCºx©€aïæ¾ýõ7Ôbû+¿à!Êrv,�oÑõÆm[8ÌÆJg¦)¾ØôË§KScÖz#TBËxæÏ³dýFlrßÄ­k³¿E¢ý 9Ç1õ0Ò$Xv»Ò@c›	‚£PyF7ðÎ~|ãä�¿4TÝ÷i¹ßòuøC/©"üdîºÝ­/ªù‚ž`³�å ‹R²þxœíWÉnÛ0½ç+r
);Nš�Cr+ÒKÑc
JËŒ%’%);òïR”·4(êR '‹³<ÎBý^²l$Y£Ël45p£ôèÈK—`¬PÒ+&ô˜N:)[¡]Ô|‡*ÃšÌûf­®ã`²‚•�¼sqO:À7Š·5t²Ä¢ø—wémŠVf„Ì•Ë¬)s!9<R4;êŒŠVÔ¼7‹3ôtÌTà®:Õ:ÖÖE£›fûî3Ã¬3méZ¹öy[²„œñü\CÞLÒÁßöèÍÖ[4v>â»o7·?oï~\iƒe(}ýB;±õ^¼¸ŒP}²ïÀ;°®7ñß�6à›�ÑÜò9Ô>¡Ü‚k5¹ô9�‡ú*á¨_zq¬Ì¿ùüS1\<G©8Ý	"#›
2V»y*
/R
•ÜáB)‡„éT »bU&†µé…õÄàõMï´�’!"Š0x•|ŸK¡Šù93D~†Éa‚* t¤_ö0¢™auõ QFåP�à
_CCT8ÅÍÓ ˆE‹ˆŽ”JÎD•ŠWÎ[¹ ŽuT†¥'ì Ñ5øY$Oœ„i‘
¾“ç0ÑJ­Ïçö~ïþGßò½	´­´M@~;Hvðc…?ºÁ^ƒöB¨ Ÿ#ùs$ÿO#ù�KQÙÿÝÌ
¡ÈÊ9”Oc‚co8S¦aû¦�ÃXáà�‡ý
ŒcÞÚÅÓÅ¯‘ói$sHˆl1¶km„mŽÆJÈ ÝŸÑs:^³˜Ž<Ü6(Çôä”÷ZÉ¤�ÌÝ¡ãH/}äWmãþ‚N7pº
²s:™l	•u•‰»Oé	½Ø�À(fAsB'§§•�øÖÉèà¶r]Þü9]¶²ÄòE^Ö$ñiï?>»Ø`_‡Z?Ø<ü“cê·_x:ls©xÔ~Arý¦fábÍ¦Ü¡Ww�éJ3¡gë\=d`Ô±c¬³Oéàõà7ˆúZxºõxœÍW]o›0}Ï¯ð[©�´UÚË´JÜ��Ž�­{B8‰U0™m¦v¿~6C	0Ø—öÒJö½çâsÏ=vlZ�uãBàÜo xïl‚Ø“s0Ÿ@b°Ý:Kpç;+Ëÿ>À/`	o­­€¦!C4ÎÒ0ÏI<_\Ê„S¾KHÊ¼O–o¿·üù«—°õœ�[X”ð¶®«Nð�¡4TÅñA9%_óÞX.2†8ŒŽH¨˜ç�ã�!)æ\ýïŽ*jQ”âÎ2)Iq(žNÝ»œ|ÇáîIHúú�{sóS’¡³¡Ï:"~¬’Õ"bÑ‘|“ =l6czOß:;|sSÁžzwÏ˜yV¼MŒÚŒFÇ! pVpX«;#/{ëûÐB³SrOÈ˜-ÞÌfv©sÇ[Âû–ÎIüX˜‡µl×^)ÿ¹Y’ £0Îz` Ú;cë–(³4£W!²/bl…¦nhcq,NC¦±¶„[õóê
l®¯¢,=!Av	»<zÀ‚WEºM‡üš¥5õ>}ÝéfÿDÉòäðQ`kƒHŸ1IÈÌrèëÃ—ÛÖr	ìµ»]y�l„>¼…²šOó¢iùuø€Ÿ@ ïƒiyÚ’vHÖ¥µ¯W<\Tš¼˜KxËDI/¸Y¯]hyq�Ž'�¥y"È	1¡…®x›|Ð"?Êr*äµ+i)†î—bØ†éá¥&>¿—mlžýmuÚQã§OïÉã@!y)?Ê0$f4ÌN|ì|ëtƒ¬¿{Úç5=Æ,éáXU�ÒW)LêŠÐÃ�5´Û[šDÝêJ’»PãÛš¡Êz†©xÔ3òÌ:%‚L4.�Èùù¸*û—˜¼á/žcïÊÊ…WeTšˆ(¯jµØ¼&‡)RŒ‹n ïXn›˜sæº¸9c}nòªÁKèBù5¶µ±­%,–*˜æé3Ð~ýµ·äðCqø±‡:t‚O¿+J—¨)ºl²±óÊiµP³®˜níôùé”žu4Ô"}V£êv¹§çö³Q)I©We*(Ñ1§¼¸ÛÝ·+Žö¸êÌ•	—~8–Ø’R°gY*ïÂº�Ó ²(KŠ‰Á í²o2w@ë…8FÈ¼RZïô—õÉù·+^aH]©‡›ò'’Ñ%‡ŒqL›¿þüÜh§OM-†ö«YK'ò›W�Ñÿ¢xœ31 …Ä‚‚œÌäÄ’Ìü<†Î§µîþá³÷?ø~Çãù6«=ÛW5Ú™€U¥äç&fæ14ÿmíybnÕdÍ&ãú‰Ôe[‡|C3…Ô¼2½’b§›zßæD¹yß÷»¢§š{Ã¾ê¹Pù¹©z%¹9½÷/,aØÂ“#˜>G~‚äÃ“ÕiþMPE™y)© ƒÚ%ß¼•­ú`ÇÓó{É¡¯3?œ4‚¸%3/­(±¸¤¨4¹¤´(•!dš¿Ÿõ†–:{ùžŸù_óñÀ–¤¥%&§3èÜÍ`¾+:m­{Ow9Ÿ�â¤BÅÄZˆ¢¢üÒ yñMCûµ/ŽÙ{{šAx¯„ØGˆ‚âŒÄ¢Ô†ü¼¯—íb”4�ÃdT¶ãýæS³;ÅD¢xœ31 …”’|†d¯wü=éM÷ù¯1V“;u•wM˜	X¶´8U79±8µ˜A°\ùáæ”ôIY«Â¶þ¾•tåŒ? !´¡xœ340031QH,-ÉÐ+)fxý:!¦4êàô6££ÆwjîN~zgãiCˆ’¤ÒäìÔ�¢gŸŠ“/Y­-ydWf2;ïÙ6�Kÿ ŠÒ2sRAJªw^ZeV·ƒG9úîyfá€CþÀÅP%ÅÆ ‰×µ
\�¸­8[~ý=¹—§jš¦]€*(-ÈÉOL)ú]Ôs¦;àÿ«)r³rûÄÏ=ã¿š Ñ
Ky·Bxœ¥S=OÃ0Ýó+N]ÒF*�¡vŠÄ		DAÌnrm­:vd;-ô¿sŽ“ZBç{w~Ž£(€RYVVJƒÝ µæPæ¥âÒ^¹†*gLÔPjµã9š¦•åµ&O)XµEI'¨¥eTc`Ð®$dJm9:š8À÷RiD‹zÅ2„§åå?€8Š 9át¶XEjÒòŒYGILàog`¬ær=Aûi’Ìòžb:;\aÑÔ<%\´Ý½©#æPÔ’ã¬Òšê¢&ö¬60jV2šx�]ãÑ¦§ëèI›ë,/ÐXV”s�•ÍÈKdõÔï 5o<kƒB“ØÜ“ølPós«N¢@»Q�åÜ£}a¡˜�à¡¿oñMIŠ%«…b¹0U–‘™U%|Nþz×žâÂVuÁÉ«ÊëÓå™�·�}Ì©Å„y·�v¾,•ÈäEŽªMÁÔÍ ã¸ÀÉ�ïó‹¿WêL)‰ÿ\‘Ç¤‹ÇéÝíõMŸñ�Xí‰™HÙ¦«ýa§¡òì}¬Á<Þb›²>xœµ’OKÃ@ÅïùsÔ`­R©Q{)ˆ‚E<ˆ‡Ífj¢ÉlØ™¥ñ»»ÚZZ«^<Îfæå÷ÞL?Ï3ÈaL�˜Ú¢’šžAá
§_Q}O?Ã·ÎX�šíTi„«Ð‹—±%)¼g ý<‡ô¤Z„½Ö±@«DW0„· o]ƒ<„Aïd ºR– 13´Z1 pT¢em,î‡ŸC‹õãgÙG–õûÄ×.HÁbg‘‘Ä»0äKq–°„b¾ì@*;ãMðW‰ý¹3Ä¸iéþ~<J<uùE³íúäÔ4žÜöNOŽŽAêYTÛ�™Â¬B©V6fŠÓ*<z”Z²©wãÚmÐ C½3¦xA-ìm­«F¥ôéÊ8’ó!P_Ïså¼SóÆ¨2žEij–^RâUŠ?†xí'¶‚¼°VÍïR*R-ŠáFü�O¿¢-¼¥Ã5ô‡[ý‡Ýþ�1ng7ã(.oãC…~	iK`§5b¹<’X²±0¦AE�ëÂC]»xœ½UÍRÛ0¾ç)vr‚`zétè‰mÝi€!áœQìM¬Ö’\iMfú
}Å>IW–:…™ö&É»ßþ}ß:�0‚K?/eY"H½0V	’FƒEWí�ä­Ææ+ kËý`	¨óÊHMg,­5ÖÕ&ï&çà
Q!˜¼9›B¼’£ª–æIðnÉ ?WÆ‡&´‘!¼fË”?_µ	| $£6Ó½ lQÌC¾9j’‰v´ÐFæ» Æ™ÉüY©—/Œ	Ë¥ÔÕj¡0š‡ë,\ï›�ÓñÐ*Ö*s=ÿÚUI…³`qß5”N~	]åÖºèžfõý´Ws´­Ç[¹,öK¼Á2âg‚piì
vð`y ÃÜd^qÁÃ=V…!3Üí¥¿-‡tr±ÿâùá3 ÎÒ‘PU¨ã¶@½ž$�736‡[á ã¶WWƒ6—™ õû`�DÖL¼RÂ®úé*$‘àcÔóÔ¢,WÀœ‚Œ'IÖgÄ8ÁßW¥ùšd„ÎÁÌ?`FPJÌ˜^Ýh2nC>™"éC¦XâÒ
Õ(¢s¯Ë"ôÀiÝ`×Â…5ªó;9=�¨y©	ýp¯å'�áv*´Ž;Á­É¬qŽãþúñ3öÍínF¸®a6ãœ‚ =S·½œy&Âf…ä:Âè^G†òY,18¸»$£sü1 n‘DµÕ8m¢=,Çó'«qúH1¾z”×	nÓÍÔñ»Å©1:FõyýQ›[úåVŽPEÜÖn³QÓ¾€ûº­eöu_´Ç¬ÙS>À·&A>´x�†¯ïj°YØëuÏT?¾L¹ò²d&nßñÆSå©-3•Le÷ÿ[Ôc?nÅÿ£ÍþßÖø_îêõD»¥ýàBîwrfTÝûë«÷Ñ¥}™y[ö·øoŽ$›Z½“xœ•VÉnÛ0½û+=97-ZÉ¡p F›±ÓŠ(id³¡H�¤»mþ½ÃMŽ—l‡Å÷8|ófÆƒ^¯=‚ár*ÆAe¿1·Àpú_×È4=JÜXÚM#¬Ù%Ü ƒóZiKŸ-ê’åŽàÂã¯ÐÔJ„¿€A¯“&ê[\@·l„€šÙÜs;#~K²&¿E»ã˜ÁmÛc5zIÆVi, äªdB7cý“*=�"Üm:§/ëg£³°‹º…˜@ƒóØŠW8¡-ëØp7ë:I²…EnéÐ½îƒlªu‚œòé¬/ðE;g§J/–An;h|:ìøôf8‡‚OÑØm3W$¹Œá¢eÓÄ ÿ( !Ñh|ÑÿòùýXº’±¬ª‘`DX©‚—œâáJ·|æW±X�è²ÉÏ¡P÷R(VÀõÕ÷ J+×Z,1�Î X«5BÍX*ílö�ìä4?ö`°òþáIs=Úµá°¡Ölán%2MU1ÍS†Â*ågÝ¢?¥;©ª"-j�%Ÿî~†(xÅ)¸§ªh™¥‹»Ó]xW*Q ¼J…;)Z/$=–'ÜÌ�’¨I{�©’€Ñ3»c\°Œâ¸™èF:¯P"2¥2™8&ê–Â²Š5ÆÕ©ó—�Odß¦¯]Hwè]#qniÿ4Ò»×£G(O¹á eG²n¬Ïœ#ËU½èG‘U�:8¨{ƒ/Gp··ódþŽÔ”)wcÕè|¥A<ê
Æ×·6…&ËÎ/}ÛìÇèºUˆ,\›8 d–Ì«NyòRŸqEÉ&á£½‰èÐüúÜ�"oa+a½POêú
)7*aSM;cÖ»—Îàéj¯ïÅ‹=«÷º†Ûôû:õ•Ë ¢ÜðšÑ]›Ú‹ó†És–°×úÒÊÐ‘†c’$[fÎuŒ£ w�QCW2©x‹=Œ6æ–ÎëÝå2ïæÂ%·œµvk_‡¤Õ1=´tËóc™Æ�Ðêä�d¥UžÃ­\Ñ<Î~¯z,»ÍD¼¬ú%ýmh¾×ç²À9]Ïs…Ù»$-œû÷õ™y2aíõ<ìÉi×N'·ë-cù¹ì!(7ñ—�gÊ5®¦å¡ó¿Œ°²bxœ�TÛnÓ@}ÏWŒò¢&)TT¨y@…‚AiEš{'ö{×ìŽÛè¿3»v.4‰JyØÝÌœ93sŽ'Ãa†03UÍ°´8'¨«Â¢‚¥.äì	Rô4aïÑ9M°( $F…Œ`ˆ)`9¯=š‚Z�£�«MZÔJ›4{`*+ëÐ5PØY[òSÚ¯$[�­Â�ÔéŠx2?�Ô¦£€Mzô é, Ln‰)Á"lûøÙ˜‡pžx[ÔLP!ç�`hn[9ö·®*˜ÿ»‘Ø3ðì„ê´š_ž�^½>…œ$:#éÑ.#ZÄˆÔ·áåýÆµÓ™-Åƒ%Á Ä¦›�ôFÆK×/¶8_$ê)ÎÕìêpS(bJ9ÌØÙrCB_Ésœ¨Gßë@7'Ù†k‘K]Ò­ <E¾ÔY>*èŽ:Ž²Ê¬j@ãl}eÓº”ý#èW¹ewZ‘íïpþòÇ çõv�4,Šñáé]¸ž�©Ë„Ü:á–
Ê–A~fëqw²rS8†ûœDgfeì}lÕ7^ö×¯ãfêí4ºŒ¸,t??¥¶¡è$pe‘HFSÉ©w•FmŽ<Öho±BÛ-üŠá›…'ßd°¢î5çÒyè «û¼’þä5ûõ{½Iëãëš£‘ƒ ;YÔ‚"•UÕI¡Ó‘ø( Æà�»qíLëï›Š÷É�™°#±váMD?Ÿ£#­Šdë¥–Õ«U§Ÿ–ÈLíùOfÓ}Kvœ“ã�Ì“dPÆº“Š£~†—:}ù�B½òßŠþ§¹g6¿½9=~	,<<cY¦Q›¯�£Ô:i=¤2à�XºË9ŸÁ…¦¬”¸&jcñõs¿~Y¸bKã±÷Œ³ø¯xœ340031QH,-ÉHÍ+ÉLN,IÕ+)fP?›Ÿ¹»MÈäë²"ÙüDöËçßB”¦§–è¦eæ€•ùI7>s
Ÿ¹u·diÀw³«gø¿€*ËMÌKLOÕM*MÎN-©òºñ{ICds~ŠŸµÃÃ©!…¯^ÃÔ–æ”d$•è–ää'¦€”O*ä¾iÐ®¶ï´àìíª§Tu8¾Ì�*/6ÖÍOÊJM;-,fÍ;?æ›„ºÂ—²Ø­8c>ªbÜ±§Þ;kì,'L0©Ûó¨ü¶YÏn VÌdX¸ØxœµW]O+7}Ï¯˜û”Á¦ÏI�¦©H¥E…ögw6qÙµ÷Ú^BD‘ú#úûK:ã�d7�ºjßg|f|æÌ±#ëFÏàd-ÕòV”xù¹¼@itC¥œæfÓ8=œdw›áy 0oÝê­•ZÓ×ŸôRª+Õ´nûíW´�Vã‚n]wå7‹æJ•z·–òfÙ¤pz"(åLŽŽà¶]Xt KMSÉ\8J¹V¥\¶fûÍ¶5°Ø€[!ð~TŽƒZ‹�‹Møä�"•CSŠýY¨ž
¸ð˜þ„œx^70¿¹§P1NAûàŸ~nÑ:ëS Þ9oäï›‚u†HžEØŸE�|®Ó©~ý 1îŽ‹~�£÷"õPÉ©�H§�ZV•´HD¶�s-žæK¼¶SPm½@3¼bÀU�*—hý	¤y‡A Æœ6›·™L["£”Âîø|³}Ö·8–~š6&>€#¸¡œÚÔ„ï½Ê�x*S´X7ÂHKàÔ±Æà#UÕÂ9‘?ØŒ€ë;Š5TX:8�;:|)�u	Šö4Ì:ÑF.W)<ÐþV¼A×eáÞ™ïAÆÎû`ªß  �Þ1Ü—¢²¡éw³¦fòþÉÀŸ¯3¨NaÄ%'q‡’Ò×ñZW(œžùÞò}[–h |Èxô<Öx¶�ó`¯ú_(’Bé,£`V¡Zº|:=ín�Ëc_@ üY9ÝÅÅ=7ê`w)ùËl+…‹R3a½Bæ­+\––´€J,*V¶àfEÿI€~¤iJ÷ÄÐ›àØf±ç�¢j¿ØãF(­N°nÜF\^§¨q·ÇÒ²æ/c¹ÔßW}¤ßÙ^HêÀ|ÓáÇ $OJœ^([•{rh„|¡ÁÄ¶>©ø]T²ð[9¦1úQT×>‚¤ŸÎŸü¬F'h« ¶É¶yN¿džÆ\TõiDgvFóç	Å6ä"c&Æ„;A¡>#”­¨™jÉP\
…´•w2c?ÞÝÝ¤m#Ì–âœõ=‹ïõ¹`ƒ
í•êÌ°èøb¿½s:­Ý¨|Ç  ƒ5>SàPò-Ø¡$Ô�.‰Pbhy´Ï-RîÛä/Ïèz#.nú–­†yŠƒªùìÓÎ}LZ¹¡™%/ù¶w-Ÿ%S3ü©§<Ÿ7’õô5NC¼MüÌª1Ê_RC;„—™�ñƒñÜ+”šyÃGº•ÑkP¸†Kc´¯Ô#4¨p8î%}¯º—î]ò±¡'ÌÁ„Ì«µØØ o,,üó×ßÁ\‚Ä_QsNsn‰ÃÛ?)ómµíiŠKú ¨(4©ê5áôuÒy­u„²å4NóØì¾ŽÏ¼5†4}Âí�Q�ãCjý`YžoQ`±9!Ý°�{MÔ�ü³1¾ z	ÒkbÌsÈX‰PÎì3Jz{î˜û€+”~+ñ‰¬)wûÍôy—ëdMþqø¿9Q¤^£÷{ª¢ï@ÛCI%×øÕ¾³N»àOzoVUGYû¯þñŸyƒZO¬íýè¤ï¶sŽˆ�¥•ãA4Ò‹ÙÎÝ.d»tNÖvuûË­¿±iäÎÏ}Â°u72ÿ¶Þ“ÅµáxœÅX[oÛ6~Ï¯à€¶_¶—apšb©ÓÖ’{lh‰¶¹H¤JRqÓÄÀ~Ä~á~ÉÎáM”,·K6 H%‘çþñœ�æe%•!æ¾bä�\ð‚‘Y)Y’Át:ƒ¹,)3&7œéÙ
¶NŽxKî¯X%57RÝ÷«@'?Qqë¾ªw¬`kEËk¦îxÆÆ$|@—b%¿¤Ýø½í¤AýÑìøøˆ“+¦ëÂ›YIE(Ñ`âµqT\Hy[WSØ=;bŸ¬W\¦V4c$˜÷zŽÍä÷3¦ü±šÈ–jÐ_‹|Šjˆ{ž£jvâeÞÖË‚g¤ücÍÏ1¹+Î”—¨ìò%i£¸X±7Š¯¹ …³#hÉ‚	x¯]�W—¯^Ú€ý¾’—ì¼v÷Ù²kþ|dyo˜öøí¾Ï‰¨Ë%SA"ÔÄ¹bs:ÌeV—Ê˜Tiä˜ÜAdð3Ùt”xÚçÁåõ›ÉÏ?ýð#É£†KAøª-+/iX~fÑÝjk‹*¤™Øô½²¯¥¹À��êž[z¯ÉÍŠšÝxÝ�Zew·m·¦Î�s�)^BåÀ,:D'W„�S ù²ƒDÖ&“��–Ë6¾_™é@ñ´‹ÍÇž�šƒpÎÐŸ%ÓŠb9W,3“%Õàœ­¦b�_vG‹éºò2Wa_ÌV©I1¶Øƒ`g´ACZwÁ¬í0÷	Næ6ŠˆÑòa·8MÞ_ýFŒŒ!Ø˜ôÀg¯',¾WEršmÀvÉ%34§†&n`^ç{M¨Uà$¡Ù¦·{™ß¨!‚±÷”P>©(dRPI¡Ù�T/œ¶§dÚ;ð-‰î�Hª²¿c�ƒÖþ¯ ræD^¢ÄSœo™úv¬ô×Ôž^‰·B2)ŒŽØwý÷Cwº±ÏâñEE.#[n6Ð7Í^Ÿ]n÷å§w…D‡{‚¿~_ì
ûõqQ�ýÅrí‚\×KÍŒu¬ª`(¹Þ‰[ñu­â›†ÆŸÃä°IX3h�èy­!ÇÐW¦½ñ}lauElàÔ‡iŠíÈða�}‰;+0C3yŒÌåV’æ¸U‡ƒ²­óî’ ®˜€Y›��¬~¬!9‡�†>ž!Aù¢óï5[À^Ð¬c®h‘!ê°©£ßÐØ}M3©r� ÉÓ¼Ë£öðìY�ÕéNjµ*¡±+ÆÌTÔl‚!Ó¦Vó=²ž,¶¶€3Ù.ÍÛ¥LA¾°C{žÏ&YÕ"³ŠÂaŒ‰·Ó/B¤QÁo�8�is«Å�ö)‰úNcS+¡íŽØÈ½¢_ ÝBs¬ÔÄv.þ€³È\LíÞ èšŸ¾Yã5Í2V9'.ÏãM9 šÖ9�*‘I¦¸Ñ}›“·<»}¶¯1˜ž�,ØœEïá°KG 
X2×ìÙcxNNŸ[Ä+çò)¡[Ê�ÍÑ4ƒ‘¿¸ëµG3£+ÎWdøîy}Ñ­‡";· ‘îñ"	EûOM8¨}^ÃrÃxírxËÑµËá5,'´Ö®Ç÷T}"^ÃrÂEÆ%5‹ðáB*LóÐŠÅm#'hs°KÏÇ…•ÆóXe“‚ß2Í/1ßHŒ]U»p)€ó9Ç¥¤¯Lö»“8h@Þ™¬6¤º…s<=†fG½1^†hgîŒ?zAxpW…Q@b b%Oý„©‚¢£ \œ	ž\[Á¡™_lkw[£î¶ÝÉ7õœf¶vÏ9ƒVÓmä6´½‘u3&+` á �:5ÌsŽ²r|ˆçññ«–ÖuA
)&ž£�	¾øY6²4ùûÏ¿b#£¨�´Yo¤»0ò¦ÁšWŒ9M‚ÿh¦€O8Ò‰×Ý¨	hËž³/²Ždíãf}6L.kIhñX¨4£%;e´Ö·§ëõð �F×Â|~æÕ]t®…¼$Tk ©Hô°7ü/Êí(œ6…}2
Pb%\�àÿ0:h‚H·üúiÏ�ýÑñÄiÑÇ%ñLÅ16ÐNkTÌf�³:s^C¥¤x²{®Uª_´êú=àŽ®ÙšÝBùÈééisoêd�›ÕØ9²ëºéy´G�óÓ^Záµ�Žb%¥	·7�S7�ºß›4vWÿ[-È;êZwœœ<>ºUÓú|Ò§Î€¤òn9]7¤cØòatr ¿íËŸËò858NoT{™Ú«Ç•o¹	l\9Zýù±ÿ]'´ÖÂŸkO}ÄNcò‹(½Ù§v6£�RS©ÖöwÒÙRšï‚Sxû`$@v7K¾âÃŒbwsÒfW{?¾„œ&>Œ›zòìÄ[1²ÁxœÍWoGýßŸbU²�ìs	§A�àj‰P¥¢°Þ[Û[Îw×Ý½8nêïÞ™Ù½_ŽIj¥(±ïffgÞ¼7;ÑË<3Ü:Wp	G…ü¤l`f²%t£h„?q¶:©Ôi§•MÙ¨{ÐÑ-ßÞùTå™Õ.3ëÝaÈ¥Œ14•ñÕp?êDÝ,Ø-Û¡:£;w:p^ß¤b©à\$:Ng)5WÚ�ÙQmcaYXS÷†î�\#¤SÆÀ:aÂÃJ»H²•2RX‰rh™¡h±žk7 �†2Kæ	Yš¬¯˜cH6¥¿ý&�Å:_¨ÔRf£ºc.GoŽšœ}8yòròátòlò+Âè·wbøçþðáûð7¾¿¼;xpwS>ÿ~TÃ01&3Ø~à'ê\% ò<ÑÒƒ¢ÈÎ‚[RD]¤ˆáPfË-¦‰¢hl…¥ÅŠ3F“çgg¯WX°Å|®,…ó5¨îªL„µhŸŽºpˆ¥É]v 0ÛöiÍ£z*šG°w’½.äÂÇÙÀžÿô$1JÄëÉ…¶Îîõ#:€ž1ò`Œ	�ÎÊƒ|ž*neO–ÛÞþÕÒb9UQå ø±xœ#?–>Ã!œ-Q®Î:jY!»¬˜“áób)Ò!� ê2ce¥Ñy€®árîÎÙÛRÆLSHA¯Yò <¹~Ð®©Ïðö.W¦ŒûüÌ-´�X=‡Ðmô¯ÛxÍårÅ�§!íÃp½Ùt6)Ÿr¹STœ(EŠ#9g”+LŠEN×Õ‚MzÊ‡44`&¦FË…
‚$[YƒQ0•g3H³t+ZeÓß•t¶ÅK�¢gBªÀÍ·é˜�KFRSC~~ê OÂu ‰5î%»N×kÀC9Yïâøw|tM5�B•£TT*qü"<ÚÔð “°[ 
Àš�èí¬ö%{ù¤1¼­Ê³°§<2ŽOß<…ÙÁƒÂ¶j§!=¾:þK(hŽo‡ãö ý�\(ù‰¾”ðP7¢ÐòpÖ,\ãí›á Á©c””cFU8ð0#
áÓ$)ƒ‚Ëæ
Ñ3u´	çûäly7Æ„‘W¸NÉ7Ú±­§®…')»N%ÌŠTòhå4Ê×‚"v”épõÁUÃ*É•ýŒngûÆªc¬¯G‰�¯´Ó+ÚŸRéõÇð
¯PmÕ[\÷þ>
CÀ_:eb‡ VB;®?ªûQ½0"¼‡Q¶Há¬Qï±T]Š¼’ò�út|ï2˜•†ƒê{K�„JRDÝÑÚ×‚F:î—!6ýð1¤�	9óhº	�Ð%uÎo,R6©FŠor^!¾2s„”*÷bi-3$ïEï�Á¶Ä•>¦E’|„î8��SˆUü9–=S�d·çeVÞ2Wø¥ÒàZpÞ&×L§ñÑúcqÀþ�û#«ç©Z•ó5´ã¿ü)Û˜½IœHKt®f‘å~1B‡©ÂŒ/N@‹ÌÇêþ]Æ*i½áªãF½ðØ-L¶Â©Þ¸±7ð–(@ÕshM¯ÄÄ’(ÂoS¸¡ÑØüS|¾	Y,Ñ3è}weŽ°3Îó£Ü_hñÀ²¹ù�Â{Õé¾ðû—D°n=dºÔ<›+©gºB“P"Ñ°[Ô°¾¿¿_~	3eÓiLCÆŽZvÝ ÝÅq_jézóªv,ÁÛuÑÖàWÇ&»Bqâ\è„6Ñv�wx�h=#n)X¿Õ+`©ªIjÓ–ªg#ÓVËÜ­‘BÙ®«ßT¬É¤R1µ‚/Y6ÿ�|{ýFp\>âf®ünI¸QáJØ H<€ àU)ÙšŽ(Ëë’iÏl$£r“P-´Ê3€Á¥1QºNùO9Ï¯¬üi–%J¤×,#ÿ@Jß•ëÅM•Ôü÷ò‹£¡�^[;÷?7ëL»¨/î3uiÍ�`ÿ¶Ãâ$sêñv‘¡´uV ­:1.Ê�“mjü‹AáÃÝöf÷3Ê�·vÑž
ÿÉeìÝÒà¶Z¿|ƒ†±Ý7ÔÑ5­ðùµ[ñ7½ŒÒí‹xœ›¤±A}ÃSuÏ¼²ÄœÌ§ÒäìÔ¿ÄÜTu�É\¼j,
&“E6Gð®eæW÷Ë.MÎ€¨ªPÔ ©0ªhT‡jÎ/qÍ-(© d ’¶Üì't„‘‘ Ÿ ì¼žxœÕZ{o¹ÿßŸ‚w8œ¤T’í¦w(”8¨ã$ˆÑäbÄv‹"`jEI<ïëHnÕÐÑOØOÒ¾÷![Nœ^sHQkw8Îó7ÃåYYE®INó‚ÏÈšÌE‘‘žùÙ{´Ã�Z•¨^W©â%ê¼LÈÇã]ø7+2Êó]–+®8“»™£nñ9~Z%—L½ee!¹*Äª›.‘»SM;ž¸ÍîOÙvÌæ@y#+Äíøù3ÞÈôŒ¥l!hvÊÄž°›*K:’†60»&I‘••b/©\6XÈ%l¶[)žš3Â²�Ý]òŸÿËü#g Š¿¿‰p„vÈrH$Ï)#¨i"Øœ	–ƒ©$²šf\)6#<'T«(eŠ�‚a*ã«‚ýV1©ÆÀow‡}Ô*å¹bbN�Ó‘]w+ŽsÐ2¹Þ!¶'û£)•À^o�WÙ”‰1² úÉ/úÁÄ¾xd×<?£ØPU"‡•WK–µ´Ò_�ÐF$6³Œ˜¢‹	‘JÀ!í¬ý±ß2	§ Å…�¢
ÞÃýÑ,ŸG:6kXäÑš�;ØH´`9µgµÄg¾sÎé›$0°B‚ãY4°1!
I$c–Ú<é¢=}HŠé¯,Qä’­,5üuóùÍææø‘+l8¹ÉN'í#k» ;4†³�±Jÿôåáè�?ýL–ì#™ñxÊ`këX§»“uœÃm²ÎI5My2Zd[å¼7¶Ž=E"˜6Æ<x\RçT¥fÐm/°AZ$àOENÎß¾òÌ¬L3k!ËÉ‘69Á¦\«µOÓÔè&É¯…~7]‘åª„�Ýú´BŠ¦Î/$ÿ'Ãhž®“v>{Š¿C¨KœVYF!OÃ1jîA®¸Zê´Ð4
©¤É)—ÚnBk_n0º’Ûä‹ó‚U¶^—°G®nÐn½­FÌŠãÓ7£?ÿ¼·OÏÀ�iVvd 	…ÊçëD‡¯Ïà�n'wšjªR-› ß ÃW°¾�dÃç… +“îF¥(`—+!Ï£ú¹?¾Ý|ÒÄ$ïÞ;=ü}ÉàÈ‚dpRg_e’Ð”§tšº\Åå™¨òU0!Ó¢HÍ“×T\�y!´þröQ�lHàI^�ËkÝæ{ÇÄ²EÚ¿²•áàÌJ>éUFÅq}>*ò9_|KZûÄÞT2í ´,!á˜¤’èÓTÂÿó™d€ZŒ\HBÖ�02VmùŒ·­ÕÎµ·ÊGžUI–U~Y�m*‡·¼‹öKF)¼àr‘C[GÈètSDyvÉ’*rüm.l0Í¨¢àAõ€Âà$G@Ž	¸ÎA ¸f�|Q`ÔøO¢#,NÍÎÝ‘ú\¤Í:Dž±’åPˆq¸èÔ³ÏzZÝ´"ò[õì¥×ºí$ ]-º­ýiQ\V¥¬•„Ð“6ØwŠxajV�›NE¨”Îs¨
Qi#Ú™L±-r»×Ü‚ÿI³ð1ìÕÐØ,£9]èòjsäàY"7yÝ'‹›‚Ig“Ðò‹ßõFð¿+*4~	N£
ç'g´]Â¤Õ7¸=7FšÔÑèÒº~5iFQ;÷œƒG¡[¼pnñÿ’‡|:Ò%
,|Xa0Z`Œor„5]è×ÿ�¦|¦éTÀ«ì#Ô$cíÄóÏÙU;p¬g8n�w`âFÇH#*ž#t�Ð›E9Ö´¦�äJ¤\å	™C�ÐÖ¢IÂJíí€Öâ„<´}P!7ÿ_€°¾9 Ž¦Ìéât�uæ"”~»¤*,Vó¢ÊëÐÔKeTÑêLyWAOéãÑ'õ<1Ð‰ÂHkOÚßAQÍÎ¿ÀÙ\úêçêÇƒ	9�––KöxSëbŠçrðDïELÞtG; ôŠr¥Í2hidöt…û÷ƒ(ƒGšŸ“þwæéÀÉn
tÄßµ>õjIal´f7óÙÐ¯'ö–™eíù}›äÒëÈ¥[Ç@lS ¸
W¾	o~%‚æFúû£ý=øo0„‚Ç’K;›�;Èý5£
H!ø“	­ Ë!Ô_º •j“¼€X—å!ò³É2’Î¥Ë�[MÞšDÍ„Ôqìémôµ‚Õjbp�§¡Xh1o
WM„-@g|6ô»mœ†^÷Ž¡ÉqÊ1±q•w�ƒÞm¹�©,]1ýÈ3ªuø·³ª5ÿŽÇykáÇ) n\Ö÷<º†Û´àà~}wËf8¼¥WÁ¾AÅÄ~ZÍç†v¥¯Öü¡•·0íhŒÃÎä1Ù'Ÿ>‘Öó'ÄHk×¢3‰âJ*oåçB¢¿ãÜ­wœÀ(? Þ¹êÃ«“HY…ékŽFÄ¥L]1ðÉ}íØzçhõŸööÜ›A×q¦F¾!Uš||<³p¶‹3rà
ñ?Æò!4Tä»ƒ«$p� •ZöŽ$ºF›�±KxÅò…Z‚bµt&�»{�;iý9Ž¾WgEñŠŠ‹Ôvñ�¢zZ€‹aØ’bÿ‡ëN×¦©@RM³91³ÝMÓâŠ5ØÜ~"ÏÓD¦í] �B“AéFB™ðÏ]Ž/¶ö›Ã­û×|¡�rÇ–ô¬85R8u}¸�.²räôäœf=Ò3^÷8DÍÚÛ›IƒZmÇ™�Ö#gq¤]*
#ªïÜ$¤¢º¤Nœ8Õ4…v4NGØÏ §š:Çõ·]kÎõ¸ðæ•ŽÆ­·Nðêü_{mó½_ºæNvt¨hó÷ºˆÌàëâû®ñ¯õ÷ HÇš¹QÑ½›Ÿï§ìKkÓ†¤õY^’R)�A¥ÀÝ¥eíÚÜÓ‡#ô`58¥y�{Ì\±�ª;+òt¥Ÿ5§u§ÕGÇ”/ÏÎNà%U•ì\m^…jc Z<bÁe±@I�©QeÆ @ø²‚†v„;Ð ùŒÉDðR™î%ZgeuËlhmiF�éâ#ÝÎáAýL.sÊ
ZßÛd�ig¬‘ÆéÕ­Ò‹(´ô¡£§Vò»Ÿö”-�²›nß­U÷Kš§ aÔ-}-¡ó‚£k&ÎBežeA `QÐÆÐ÷˜Ý£w«†B*½KOzä‹ìÌÌ¨èæT†9¾ÈÓÚ¿™Ù›Nñ‰‘ëš”Ã>ö¨9¾ˆ¸¥£­¼Æn[ôÜº…ù¬.÷ƒÒ[ æíÜÁbv ˜¤�¼{xYW¯ÁjP`ërÕécÈ¼é:lCÇï8ssóo»F!7l†‰Aµw3ZÝõ‰¥©;­´hÇ€pÇ€`KZöûå µQF°!:Q´vœ:”»O~ü1æ:–EÆúý|H¸æÅ�firòø &|Ç!ì¿|Në�Êxƒ'‹{Ì,:…s›@ñ2
CµæRZ#ãmÚŽX­öF­µóˆŽ^Ë«»ˆÈ¶öñçž4>ä›œaºÒ÷N²d	�Mz(^:Óž2›ž¶ï®Þ’"Ž'uÑá�*ƒÊ*ãð'ù8‰GgC²gYZ„ŽååÄDPÄëÝÞû(<<Ñjm(& ª™*>ŒêºËaKmk;YŠŸööºŽén®A2ó	B¿‡»©¿¼9ùÜâ�PÚén0ºsÕí®}âV¬öêÈí÷Ù·ñÜ¦Vøx,úª~o·7—EÙ`ÓÜÓ_¹UÏ~4!½(ïPáÕ’?£Y¯Ð{‡‹w·|]•«&Là9öâØTMtbuO©H–ü;kØ¦ƒä´¥¢¢^'/¡'«�o_{Åtm_†·¦`£,a2ážy%â¤"²QÃäOM§jtþäÕÈå3¦ÁxM¥W}V‡(77˜G˜c©×·¶²4n¨_&4ýÕä¹¨Ïìåz×O2<¬	Ï@ë‹898úÜtáqR«Õ:lcü¤¤ßµêBß	w “Ú–·»üöƒx:Åï÷né-îgÃv­/·¢4¹€Ö�]Ô°q?cÖÂc¿ðu`ö!²ÿ2ŒÝÄªv´ŸlÜV¼3JÜÈRë´ÉÏ®G«líY©¾Ýˆ¿‰Ùêœ¯âoPïÄŠh®�+„ŒLÛ¼Õ¤þyÕ}ÜKv}FtŸíš*@uŠÅ¯l·jÕÜ7)®š{ÀÒ
ÐORš• |s¥Œ~<w4µîÌZ=5òæ^RO…°-Ô\Á­$li®§%þC¥¸IÜðMR½%»áƒ­/¾‡m–ˆíncí“Ý×Ó•±c|kö	Ú†Šlt0¸co>44Ð>ù}“ýè›­û¼w|åúÝûËãÈÎ~eùîýï”ÍC¾e·ßÄI–e«_J4ÿ/;fÿ¥ÈÓ�Oâ–Î¾�>ß,Çþ‡Ã}ÞÃÿ6À#î˜Axœ{“ü yC‚€|Rirvj‰_bnª•BqIQf^ºŽBvj%Œ³¹G A‘‘ º#ï4xœ{�|'i‚_µBRifNŠ_j¹[fNêD;yŽ4 ­›–˜<q£èFý.FaW7ÇPŸ�x7O×ø�È ×Í;Y”%¸À Y¿F5Ppò~±É—UŒ&3«ÚM¶W5šœ¯j6y±ªÅä›ªz“YÕä@v„T¤Zaš­2!N]c²šºÚd/u©ÉsÔµ9k5uÀ¶iNÖ×VØüOý' lÑ;½¼ñxœÝ=ksÛF’ßý+&©Ô’tHÊŽoSWtŸ-É±Ê–í²¤Ü‡\*C'Àâ!™‘Yµ?báþ’ëîy ’rì<Î•­�™žžž~wc6YæYQ±[Vð4Î–ççÇ‡lÍ¦E¶d½4‹Å$*Vy•õßKôÈÙ¯I~ºJ#Ü¯‹äÒ•ò4Kb;†~ÚÕ*0êy²fÌx¼ÿ<I÷DZ%U"Ê½)iÌ{-npê[�¶Oå0®ã¤^T	¾ÙÊR�m@9~VGW¢z'ò¬Lª¬XµCÂ)åÞ%�fpœÞÖn Íî¶Â´ð†ìô‘|eE¼ú&À†„»ak¨¸è™XˆYÁ—§¢¸N"1dú"}œN³MKTjì¨”³]–Œ²e^Wâ/çC&Ò².ÄÑû
þH²tÈ¦Y±äÕA!x%â§!C”s^ˆx¯®’…aÉ{{{ìßÿú§ü��ú¥ýý—ø¶pÿþ=vŸ i
Q"%_Ì²"©æKV—"Fº°h^§Wðwvù¿"ªX	Çgb3÷îÑ±9óŸšéû¬‡º¢Ç>°´^,›Õž²2Ig ùÈ¬Ìê"´Îe�,bxÅ8#^ÉÊÃy––´2;âÑ\B >–oâ’UL>3\ ˜âPÍÐ&$Îâ=±D’V¢˜rXümåîTbs{�1ÀÖ@’ ’5Â4¡[ˆªHÄ5bVÍ“’�#|Æ*—gã	î†=¡¾fçï^´8»I�7�;/!¬7E2KR¾ šþ*X6µsaìrU‰RÁÏð÷Îcy)
âáè’ãQÓ$ùŠÝÀÂüj.Ô©+ 8æ5	¡œJZ÷A²«Qdb 1ÓÈÙÍ\ —åù"‰øåBhìhú©ÆñI¾�E	Œˆ‡ˆLÆ’)«S½ºˆð¨…?a…6¶}|oí0j!fõ‚¬'a ºSKLÌ×°c�åô‘v0Ú!�‘ìöN”Àä†Õ“2*àsžšcÙš°ž\»g¸ÎÄ,N|Ivj¥&á“	YV=á9fžŽzÓùb±�yÅPOOšÛ§­Ö*–¤J\yTde)U@ðýÕ�ËòH�w¤¯Bäcìì'u6Â�÷–ÀÛ±4"œp!À²0cY�#é;vbLë÷âz*É.ïô`G=—¤LT:ZÔJ¨FúÙ¥�EéP§(É ¯80'èŒ2z!	gà!)2fu„þi“ÁùAø„Ùo“¬­üð¡�¶Ö<©qˆ�)8¡\¼Ä;NímÝ~^§/žŽ¾ùû·l.Þ³8™‰²’šZKðÀ0g	ò4³"·îÄ,ÊòÕ®¨ÀØ;â¦,ó(JHÇ§oFÿùíƒ‡¬JàÐ*¾Ì5$DÔÁP‚Xð²:Éb´§qÛn�›àÊÝºH��À ,’²R~ì¨K)¼‚QrçGiê­#
î•X±þÕdÎ�Íã']ö�BÆ5ì¯R]®�ÛfpÛH.HÂ%�®7ãÎÄFš²%5ÚNk	éäøäHJ•>w©†=c°„UÎHÅlgK<Å–¥=uÖ¿Ì€¶?>„Q1ûñ›ÁÖã*N}Z|…ËhsQ/—<0Moµä$<qTCÆ‰X&`ô§É{TjäŠ°X,@/Uèö ‡ÕÖoÐõ®-¤ÿž U´.„æ?1ã×<Y8~MRžà‘ Î›°Ë,[žºª™NùO1œáï\l
K!ä||›Š÷p¾¼¸2¼�ONè�FÒøÛö„^kEa¬u;ùq¨¤Ý‰hèß`�C”~ð™d«Œ¨Y¯D:«æ¡œ‘¡bÀý¬¿YfŸYRÖA´w�¥ÓdöW
÷ˆØi}Y
)ªÒ'ÉŒh7ua~�d^®¤:x¤Y1‚PA^ƒuNÉƒVÄ1lÃß'Ëz)ý=Ÿ	ˆ½�S¡Bé•�J¶ü´K¥jyÄˆÓu÷AþÀßáÒsBY=AùçòeC^O’´s'—b‘Ý€BI *Ö/¯’<·±Nv" Ú¹+‡4¼bÇ‡¨°@ÇX?˜TŽÔ×Nšã &`àQ\‘0Ü31 wUÔYR€O¯4upxK IñO^dïW¨ú8:ö�äV‘Õ³¹d"ZÙ¨U~úœ8‡ÄR[±C‘‹`z�@%Å&VÁŽ“m<	`KÃ‘2gÇlJeèé"Ë®êÜìÝ¤÷&Í\Ÿ	°ð<h
Ïƒwç‡Ö–ŽÓ­`úÉ9¢Ì~`}þejfò\Ø:ƒ8iK(n6‘H°†Œ�lÆN­§L¬v
Üüß¤5Øàd•˜“†-×Á'ºvÙ¶¨²òÓ‚“F¢P¯ô´S±•¤ü¬á�W“@i55ý±€ÓûëdöŒÍÿ‘/ Ÿ(•.)¬ãh/Ivt8J–�{ÍYÉ§‚hò
€ÖÁ¡jm”‘ÓÙ˜yš®×Àñ%_
vÍµÀd¬šàªÄ/Éµh,¿w�àv
oŒ€ˆ¯µZ„�þƒíO«ô¿�9«qBPŽ%ø À€}ønñ»}ö`@P©³ð·nØQQdE¿wœ^#�Fûï�×ð?¹õ .°™�7ßäÈ�àÈ¬Èz™Ü™@ºH�°…›†'Hç‹ã¼[ðb†Q¾T¾˜¦ÜØJæ"’ªíPÕYüFý¾¬§Sy fœB †jÍßŽSÛ¤ÀÎŒ[Š‰´ªË¼rç59¨¯Ò+f}…±!ˆŸÆv9iÉW—âÀµøÈLÚ2OÀ:  ¡ã+å>ì0ÝÃ{À€·’ù5¬Ç­	Èöü#[{|k¨®ysŒ�¥»Ì¾ëBF³¬b>ƒAv`CùÓµä[© �XÈ‘à» …tÝOJñ7b¨¸h}¿ß@õS ÎU+wÍVÅ‡µ'k&¦AóÃu‚AÚ!­ÀöÈ&Æ5ÅR^þGr‘u.ÎÕ<ª jç¢%›¾5w¾±Ìð‘µ…:MÀk@ìC„™6•d…˜äîJ¤å ü9Áh.rÔ'“<R›+Q®'9«¾7ªÆ7Ò–ê…Ð:ÏzÖS²]•°-¿²[)¢½òðÙ]u‡ 7ÄË¹‰³5•$e¶'�”Œëci§ÐL®lI“FB]2z�ŽñdI`5ÎÈ‘Ã­*5ìŠŠÍ¢tçuÂ¼=ûˆL�uÜógP)kI7 ßƒ±QéfŸž–óqvØ<Š
As(k&Ë0QV¾*UE´§p(Á¨ÊŽ•TÀ¤¦4¤ÚBÓ
ãt”Ì×àE½¥ìLS˜z-áÒ+¥HÑé5²ãM^�ØµLZp�[×élˆÂ?‹w Á!‚¢g#ò @@¤^ÄÎÎš^ŸÍ
1£ZEtÉtŸëRÈÇ)‰K	Æ’—Ø!Ó§ �Ðõ*šG¨Ùx¨��¯âc½‘"'rYÞ‚s˜”â»‰ÿ^{$r³eà‡žyèU?6Q¥¡[N>ô³:PsÑzÜï•sº çLhÕ0_*P�ÈÂ«ú�5¶îÓl:Åü=�G7sŽ¾zú�–·¦ß"ñ‰”³¨†A<É1%­æ5ô¯}Ñ†¤³Ôðàö÷)À¸,¿"Œ\/�}½ÏÊÉÁ>¿nzXrœ$ñ¸Î1ò3žš³…[]…o³Lè{¶¸Ç}š¯Xs¨¨áï€åk%}¦tÇo8D'›ŽÕ¨³ìTú‹K–±‹¯n›â´SÊB¾’¤\_è½8‹ê%˜’ž�šÔËy]Îû·j¨�®'‡®—·“±ÿ¶mŽõ§ºgê1z~Ó…òç†ïÍ<«B^qA{ž�ÔíÍ�mÌ¢ßIÿÃi®Rl'ß¯5}µlìûÂÑðîíaH ÖÔ+Þ–~B¿>CO-âK>ó½~UÑF»�µ)´i¶/ó¨`Àr%EK‰Û·Õr¸4-ªPÀú“ägÝüô—g˜ž‚ª«`E[�ÞµJj€×èè'¥`½?ý|w£Ñ,ü‡6Cb×Ö"`ô9¡o?n‘ðóu°î.éÔ3Qé~“>ù¡¾X*m¬Éå	|(Þ-ÓC©¦¼øÅ¼ªòr²·ÇóÄŒgÅŒZ÷ ÆúêVã=†_¿PÄµÞsžâ¿`ñÙj*G€	‘Ò×©~<I#B…µ³Pj×žp<§ÆG”�‹Hw?^è]Š^WÊ^œ�½e¯8DSº4.;˜äBŽDm�âtúS:\°HÓÉ:?;ÐUËB ŠÅìt1{“_9…9LØ è	;Ä>¸•O•TOªý:	Àäi$€¿höz<®2@æ”fõlBN
¾§…þkE@7O|^B´„‰çº ñgIÛ.O:^<iS=©°®Tõèö09Ž)õ8:ès~•eàåÖ¹Ó!Ay7R\—+,gñB¸b™¤*­3Ô¬„�Fìþ}ÙÞvÿþÄtÛ…ý›
£ÿó_×ÊÕ£NÓ�T;Âü¨Ö2bÔºæ�]º“µâ\Úˆ�d8%,[IQJRÄ Š�
YÓ:�nŸÖiD%E"§Êž:&
ñ¬lç‹®J4Õ?!ykÇ–éÖR}”¹À,È…ì¨€}z�ÔÚX¯§lTÉÒ¬Nýö7ƒ¶TD¦ÉdE«�Ô˜x%½�+ÝÊ®ÉÕÐ4q•Û¸ã˜­°‹L6NÓ¥MŽB_Äblë‚ ºÓøÙ
×s–vœþ/T‘FS5Cû&Í‡®ëƒ
¶¬;>Mã—b¥Ö'1íÊ]gµ­zJEuF %oÒ@dJÙøXf^øb!
+hÒá¹N¸†¥„k¤aÍe…llA<´[úŒ¶Ànè˜FKmÑ�œnÐŠ9”DYËÝ­Í>NB!kªé<1Có13‚*ãJp	:NØéˆWé£IÎTÀÇ<í¨½õà¶zg}éùÊEÏÆ@‰f{>%Ù†Ú¯ónó�R·R·‘2TV›ü°6”=gŒhÕêŒIˆŽK@?­O¥$!¤œê¬Öds0ÚÊ
„›ì6¤d.á�5£&Øqôôpð'Òï³¦öþX�m›Ç>—ÊniOûªµ½€8h¯“jGµdýº•Njá0f�Ñ:½Ó‘5–å÷yý‡ßô@¿®é‚Kq6ƒ÷w–rÈ—³ÿöÜ:™/€‹‚*Ts“�íSm{@.‹w£¥ªEõË%X®�íŒ©æ¸ëûp6q-[BÚJ1Æ×Ôó›Äb™gH6†k	0Z³D60ÉúB‘M6ô¾ï6â(;7ð[q~)JÙ¾Ìâ•JHöžˆ+– uçV%Èæc[e{Çï;‰‰ªCCìªLgüÝÃ=ë>zÚ�ž;B>@Z¸™öÖ¦T|á(”°eÿÒ&TîÛ÷Òh¸›�uáŽ=V� Xç¾ 2'ˆ÷È¹Cê9T¢à¬Bo),þÍzKƒjñå¤Ž¹øò«[ÜÕúË‹Àõ°ú©¸ äq	"Zõ{{½Á8Ïò>5ãôÔ—–vR™Ì ©!sñ@%›‰0d ÃÜ·f!T{)_è\ñÐô´SÚ;ø Tg›õd›”ñ6µs[Æ»¥jNùhôÕ­"0Jô~")
‘/x$ú{ÿ³·†°÷Ko€Œ³§ 	å·Æ†I[¿©Õ¼ÝÐ†¤že/©Rð€81M¤úåCÐA¸] èY†qâGi{®3a
üb›>()ýCó«Irûn3Ý¶Qn7Ú±V‡X¿Õìa…¡�!Ûw÷L½ò§?k×_yj°%eKwZHÚ"‹ë~§™z&žX‡¸ kˆØ;úAD“×—`Pi�òì†Ú‹´}‹­Ð<SZÑ›ìHÀvVZç…E•VÞv»Õ°«ÒÂ:UŒújÍòŒ®5Jv…3´ø,ü§/
Ä÷�?IÖNÜ%ôS;ŽÑ<¹gÁ ‘j:méèiöÜP¤õ5}—³áý‰ùÜ¨	·Îí¾—Ú±²†Ë’üÑKôÐm
Ï[¥)Ü„’”‡‚R|üÜ¤tO5üCtðÝ¸FÕ¤)Õã…Wb[7U‚lÜ†=:×]P½¤	kÉó>åè¸8‚6„fkÍ ÿÝ¡Ò%†�)¿AóÍÂ·µNÒRè4 �§MýïwmÑœM•P]Ñ@üÓžm§zÔ­ùã$-EQõÍ©Û-~U›87ý,°¯§~ŒšÙMõøÖ|NgQÞ±½Ý–·«ö¾Í¨ì`RBƒòVê}†þØòý“GGÌØeAZí‡_y×Ow°;YŠ�vb³•Øh#6[ˆö¡i¶A¾¡[—iØb³`°-ŽÚ)½ey¦Wè¢
ô)õA$5ï"”÷#¾üuDŸ“Ë±ÉÂXø8q*?t¢LH¶(0¡ò	ô]GX¹3¦ á~S.?_ÃüAYç˜5Ðé"Ÿ-…¢0ø¦c +?²Òe#‰’©ï3ðk`ÐTqB3dý»³*ô¥{¡ºo‹Î
w>mû¸T«ýžÿn•$Íkà;©7©WÒÃÉ,©vXÝ$!ón«­7Ñ&šƒy/ý�åÕÍÑÕ’téXY—Ac`àáI=3’»	d28¹ýÝU£%¯° ëp?†¡$õ8IX²y,8<òÔ†´f±dz‚�ž4¾Ån[4ÍRñ	W~à:W_»-žáU9/÷øwÉ|k�ÝYnÌ{Þ™“£žß)·îB¦L•‹ÈK?Ûná·á€Cìˆk˜6òVwPüm|ÂÞë¬:Æ&´¸^`ÑÃƒÀcòï×*µXa~ÕÎ;Sÿþà¡“J óak½š‘°ÁV˜XßØ.š‹èªl�ÿyoß9ãdZJJ+~gû›;~ù¯¿ØßwÞïNº·ŽX<ç€ƒG>4®à”l•Õ¬ÌE$›„â$&:Î³…G¼ÿxøMƒxá¶Œ0uoÍÙÿÓoï÷r³–¹‹�Ý1m£ƒíÍÚÑõnÎØàwìßæ}{ƒ6¸äîºa¸iýò`AÖ½ER·øëÎŒÖ[}ù&€pL§§ßœF7Mÿ¿9Ç¼ìšsN‚#j-Û
ÌÆR¸ÞILØ/ÿ4a…¬>³šm‡*°i‰ÄvÈãÓ7º²«öKúNÞƒn½‰DFq-Þ$‡ú78E°xéjNp+�—<Ê_æ«Gô�ÀÁž4ª.Ön,z”YJÇ�›iNëv“ƒ6¥×²âU]¶Î–¯ìGa nÜùœ)Ö}¯°Uo”ÎÅŒØ‹zÉÓ®À-æÀ,Q‘äªÖ™§pµã,Çª‹ä}`jÜ-õÊö�¿'m· E_V¿A¥ÞêéIÏyMÛß§;OÚûj1Ù�¼C{@6­F1IAØ$pxôêèìèÏÔIsôè?@\‡·ÀðÒÝ@l*êUtØ® TŸ·ÛF}hö»Å]å»"HQ¹é¶ªôNY‘Kt×[™.`Ã\%ë¿}sjó"OäÐß�×¸ó}ìL6°˜~ªÁ(÷wƒ·ñ¨ªé6‰�û>e?‡Ë}å'a?ç’5‡õ£?ª/CÑð¥‡ŸùdÅûh³jê4ÂŽÒìª#šÝÌÓËìjà""?Z±ƒÖ¾Ì˜q;‹Å‚º�´¨/ú¹¦mß¹‘ïÇ‡{êæ>„w*³v¥º'ÒJÈ5WêéÛJQÇÙH6\âEC³"«sªòm9Ÿé,ÎuÂÕ%w{`5Šêé´jÜEóédBÀ‡ â×
¥s+Ž5OpüK'Ûzßï*K}c.ï™£NªL~ì†*˜,QSölj
XF$~±Cg‰ýr}üæz¥SJÈð©~mÓO¹÷!³›œÓW+Fs p„õÅx6f_î}90·' ÔéB±" p²wj~gFM­®.ý±×Cª^<ÜŒ"[\Êe.¯l~øàÁ‹þ’¿—"nï~Ð—áÒLrIÏ"Q…wõÐÙáõ<•wt¨l�Ú“Í†Þ’—kÞ|ù›•ž@>Nõ)b�HÒÀj'¼šC”›*¨ŠfCIÊà+c¥L†ö®Íu‡
Ä¶’…BSN‹3rÅÜ«&êàJ1ˆilòÐöÛ£h+@:Wu®úª³…ngò»ýXqRæ®rª¥3·„øF`Sš?×kP³xÂD…ø¼{Üÿ“ÿSã7b~2¦¸“=yB§9P &ÚŠÍ�©Tj(È&ÞÿWDØ[@>c&×l-Çgmµxxf[Ú�Û�Íÿ=C?k´“›Ø?k¤cÖ3Pó¤�·Õõ#{*wml–9U4t†½uE¶f’uÁ+ÏÀþÈÏ¨X`s?›­3Fýø0Œm6xšT˜’ßí¶Áž=ýÓ—¼M¢«ï\›6”ž¨zßw»Š¶èÓ§P½ ¡,[£ˆÎ×ã0˜ø?X}¹¢æ°1xœ›sŽqÅiÆ	¸ª’J3sRüRË'Ö«³è¦%&O>Ï(ºqûv{).(€©pËÌIÕ¨
O.p�š,â"4y–ƒÒäDuÉÉ—ÔEŠKò‹ÓS�3K<StÀÚ'ÏÊª8*Ovâ4G‘ÉŽ†“—;*L¾ã¨5ùœ“¼P—±wj¥•Bvj%TK^¨ød!g¡É±Îº,

µš“ì¥6ÏuVaâÂpÏäýÉêÊ“w9HN®R×œ\å¢=y«ºÐ>‡ÉWÜÅAvñ¥Ýí‘Xœa¥�$'sò™¬ç)8¹ÄSœd>ÄÚÍ=žs8°Û 79 HhƒÄäö u ü“w)Mæúæ}�òd�`õÉ¦@\áÈ;9=Xfr#�½ˆYœ$�ú¸'³„ÈNÖt’ªåž,Ê1Ù7D|rRˆéä©!æ“7{O¾¢4ù‚‡ÈdÁPåÉF¡j“ÃBå&…ª ;ïq¨¥  ¥�rå�pxœ5 Êÿ¨Ë�+‘q?“}¹“æ!“eÈtype {“À“_compress“|Î³¸Ÿ@L ¶–xœÍZmoÛFþî_±•ÊTÒ^ƒƒ\§çØ	j\ó‚Ø¾~(
xE­¤½P$Ë%ýRWÀýˆû…÷Knfö•KJqzWà€‘¸³³»3ÏÎ<3”ÜTeÝ°VóbQn®®ÎÏØ–-ërÃFE¹³¬¾¯šrtt ­dY‰¢+³TÓ
¾I%T(˜Õ‚7âƒà‹‹>nâI¡ìêWY]ÜYWæ×\ÎC©‚¥\xúêšûJ€Ô[qûZæâ=‡GV4M§ðNÈe1E#)Ôt	b‡Èõtœ[DU*Ù”õý°.œ(:¬�ü°ÎÏÐ·OÕ¥ÈÅªæ›QßÈLìÓÕÑC¥e{Ê®ª¼ä‹ó¢j›‰ùò®mà[¨uÑ”Ó–Æ:nêMX¶ÙGüz!…¯¢Pm-^Ý5ðA–Œ—P*¾çj=aË²Þðæ” ²8‰¥Ö¼‹iÛÈ\=˜>yÂNAG-*d<_•µlÖ¦šZ+–ËFÔ<g­&ØP[|„�
lÈW"eO¦tæST�8MÇl„P±ßXÑæ¹Yõ�hø‚7wÍ8S°X.˜6è§u¦è~ZB°�%¯\	‚âÃc¨ìÙáœãQÙÌEMÓ=yKffàÈÌ±îfh&ˆà¥5m¨YKÅÜêŒY‡£+Î3c�žª¶�¿´¢§1QŸÃ#žÕ¥Rl^NÊ�àÃñ€ú+ÒÑ_äØ,Î šÜ‚]ÁÍZX?€½¸Ý«ydäQQ÷àïj¹’øT¦X¹$=dotðü¾°EV/ãO«æ¼D‘Xë¬
Hà~5‡Á¼±^Æjö(»”C’WU.ÅbÂÀAˆ$&—`q»–XÝÙ g»ñéðýÉáWß<gk¸KÖ*¥5•6OVkg ðÊ;i«aýA¨6op¾†2Þ!n Q°ŒÉ
>;Ä�baüT_2u£ñÁÁ ¸7‚gkg¼âG°jxY~ú¹wRqÇr%TãP öÉEcÜ=.>Â8ÓÙ€�hBÇÓ€úvƒŠ9xªkH�{œ;€cÌ‹v®mŽ\ŸñÛZÊU[»o°œ~~OGÐÖ8¤ý@ÐbZMÜQ€�#	^¹SÒèLŒ	 n-†võá²4.ÓÔmÖ Cåm�s!:Zç1¿ªóÞµåwrÓGGBÅ]$À9ôT;ïK „7!Ö¢6^$(0ˆýæÒu£Ä)Lè‡ˆ×l	a°d¼iÄ¦jˆM„W‹Á?‚í;ºz Ãnçe™^8›Èb§Mæ"/oaÿUËA<VeUùk]hPºIgä(
«Å/­¬÷C†D�&|
:WJœ‚8èW?øœy¦An>ýpu†4O£V¡õ–Z,+kçí¥a3³˜Øª‰Åú-zHéÆÄˆ@«å]³!Öƒ‘¡5¤þnyM�,Œ
ˆ��x¨þ4ë1*»ÌÉÎË¬èÂ;0áÐ¬wO­SØöwžK8)8U‡/­Ìò-`á`­¦­Áì²a\!ÝàKAþ\S E¨ë¯`W{’Ø!»Ü©Ù%5šn×@yÅ7‚ÝðÈ $)XUâªdoy#zËO(¾ÀÞ.`ÄÝu NI6xñÇö;~A¸ƒU’?i¢“JÒr®ÕG
Æì·ßâ#~{ÌžŽI8q]Ãõ+Ä-{U×e�ŒÎ‹´q§èü£1ztúè‘^ðÓ‘sÔ»
©àž"‰cíbÈÇ2¾ð¢@Ú´ :’‚Ú`,ç5ÐQ”íj�Xœ‹B,ÁÐDye3äTãËšßšïóv¹Ôprf jcàðž†&EAtº ¤†ìª‡ $RJÎÁ^žàŒÍŽ�A<7B~"iÃïçâ4Ç&6gì%)š1ÛEéðáŽ¸:9  >hð[]GŸK¼ >	ÁëLoš¢úD±jÖìÛ];²¸5t{"“["ò¸ÕàÕ�FéÙÌdg}uä¸C#nëÅqo«ÿ‹ÅsÍÊ»f›òjÛ¹pØ#ÐQq)k8Þ³ç–tà1” ½�ê#Åù_ÉìeØ(`ÖX@GØ¾çpîCv2We•'Ð‹†î®„ã�0%�ö.¦O,v1@%�ù¤­p¦ÝYß;rUp˜,4¾À1\a;#±Û°T°ø^÷J¾Õ¢/,°´²5\—ƒ)¿å!°ÙâtLØ¨Öq¬�mk×éY°]˜¢¦¸Ê,yöœ$­„ñZ±lÔëÕR<A:&ìéŽÿ™¹Æ¹0”BºãuÍïqªtPêh©·ÔQ�å¥‰	¾=—×’›É¹¶jW›ËUÀÜÐ>ïë„>AmaÚÜtŠuÀj§æ ˜•+,±2ÕPpUêÂšê¿Â’Ejvqido!×¾¯!øß�.ó�Ú®B¢ƒŽ”ÖT`j›ØC§ûr¿eï¶TìÖMþ;ÓG�7ÿÁ9ÄZ·ÛËÒ™¯Çï@‘�DÈå{¹ˆ¯VµXaW)ªR  Xœ†7·ulí¼¸‹å/0‘Åîž˜¢µëÐpt˜ýþöÂJiÎ†–�¢<Š3*ân›‹™ØQ Ôšò®—m‘žR‡ø{zœŒÔšC¹>
&–÷0_×øPÁG•5=µ#¾#¦ŸúíëŽòq¯ÉË¶–«õ�0\¿á5ðˆî™·cÒGQ‚"V¢USì€ë£—wâlfÈ‰‰´Ré	Î³ïô\K2Rk4„<þ
(ô_N0‚šØŠiºÇŽ�‰ßbÞ‘E+h‡ac�}yÌžéù‘Õ¾ì§w-§–¶ÖŽ&ôSÃpÆ¦dÑ'h	Í7@ŸØ/F"þÁZw÷`n¶éÙô�>5R—å…Žývs¯–±ë/ú—s›Rw^iSn¯íŒÑ¢ÌÚÔ‰£îÖ¹iÕªuò`Dýt;9î¶vN’vG‡æøêî™VÆÎïwM»sãq7ÏG–+¡êN³Sç�¾àXì˜î6ý~;=¾÷È£7·ñý;ƒ^ÝúKFkq72jºøÇg]R©ß-ÇÐí¶lbŠ¦k¾£ÿfR?U_´=Ì fp†#ÏR¨DöQ÷ÄxÁÄ�T�ë98ZAuu§S›@ük]?aLÉë«”½Åw!9¾>3Øf~¤5oÎß¼ÒojôkØ>±ß ¡�F½ßàä+ ¯p¿\6EM‡ª™\‚Ž\nd£•üOU–ºÈµ�zˆ� tuÏþýÏ¹tšP§– rp—	Aí–NÉ«ks¡Öeu Hë7&‡óZ@Û�–ý&eïE­À€ÁÖu£‰%pþI§cdú�fÚJÔ�Òv|žBØ•9’Ê€¢Êª�ƒÉÙuøjëš�]¾‹XÞûbš|ÈâPTÀ‚‹ Õ…aU<ËDE8¸^©]û	ÆÌ641=;§I'µ^ã.ÁÎ†z:O™ûd¨‹Äõgáû½€"„ÛpÜ€±éÔá›uÐŠHÛNC0ÁÛáßÆmÜgjûƒð¡X¼¼§0@;Jíí¡UaÓm(<�1ØYáÔ>š8¡¥‰ú��}ä…6rC¯,!ûÈ±ÒI©n
³Fºì#/”Ù×œ�”{Æð‚ò"À9ÎàÐˆ¡ñ-Oú£c¿”m÷SwR!¹BwSÓõßN—Ó/zFôùp«ý²=°ˆ€E…£²UnðNÀ—âæ½¦°o|}i`Eõe‚Ì@mÐb	]LZ¼«©ü´ù~âÜKÔ%zmƒb�hs“Žp"v7`¿gpÒz#½©¥¸�÷ÿÆÄg‚UÈ{V°“ãß¥'n­îÙì¢º¾n7¶ò ušÇþJÅýÒë×.Oè@®ØvÞV'‡ëqH:vt’‚Ï	|^Þ]Û0±¯ÌK,3o!ü¸cDûf/XˆØá7JþD°�Ón‘‡e¹c(Ž²G¼rwÝ—¸‹ÔŸ¿`×	y©d7˜¥
n‘H¨�2öw‰=â<Ã²Ãï§vÈîy¿Íhâ²Q�Yhy`Öª¡ß-‡&$­~zúó‘½E'<âu¯;{ª.mß‹RS¿ÑØ*ÝÆ{Aòñ?RJœ¬)2MÃº”~”Ä*ô¯‚vä(J“~¶ñÖë}{è$'ÍœSüÞKÏÐÝ�¡ÞŒ°,pkí*	ØÎ8ÚK—ƒ¹1ÄFŸ‘ûº
LK®Æ=êkäŸ°ï¾cO»“u).eŸz9^gky#.#/a·º't1ð«“±×Î2ƒÃ¯Š¦¾ß3þÆÑŒáM„mžp|ÞfEãMc¿£aº’êë¿‰{+F_dôY_rPR€Î‘‰Ø#/"Õ™À_R `x®B×Óo@ ?†Cö,…c§e[4³~”HóNÁ¸í‡ýnüd�zC}]^%IE!¸jpQ—Ñ¥DE?2é—ìý»\}þ5îMùcnpµÿò7QKöo_¯¢w*ƒ§W|mCsö•ü¶è'AüèÝîýE^ûb?,ê&q€ð3öT& ï-¬Ì¾ÀÊìsVho®¶³ °BîÑïàîñÑ÷Pw¨¯/†ŠoO”ö5à†ÃÎ.\¯ñw»ÇÁ4ÓµY—ëkòø~gŠ7DOôñIÞC©»ÀÿAgïÑ!ã÷v?‘ôã»Ü„¸ÐôßgdxŸß»|ÚŽ?"¯?*«ïÍéû3úÞ|¾?›?6—:“÷ò¸…‘Kä»Òø'’x�Âýc—¬£ ìïLpÝz!Ø£+”Šƒ°Á¡Ôè…b;Ø_3ÔâP¬ŒŠ‡Ì`Ã±þ‘ÄÑÁö?±«\Bàšyxœ{–ô i‚ñÄ�‰Æš;J­ª’3R“³Ý2sRƒ3«Ru’ósJKR=‹3tRóŠK‹R]+J€ŒÌü<…ôÔ�Ò�Ê‚ÔÉLÊ“¿1:0rM¶g•Ú|Œi•—l.�ÿ&ÈÈ iF&Öìtxœ{�ô7j¢VÜÆu�Œ�%•©
Îù¹E©ÅÅ™ùyŽ9éùE™%¹:
¹‰•I©0)çŒÒ¼l…Z…´¢ü\u==} *ÎH,JMÑ/-ÉÌ)ÖO†*œü™qÍfQæB¦ÍÝ¬•ì›KŒDØòóò3S6oTYÏ é°/#à|xœûõ7|B-_µBRifNŠ_j¹[fNêÄ~ö´Ää’ü¢Ê�Å›ô¸  Y‘F5\XA¡ 4)'3Ù3E(6ÙRÑjr‹¨ää.EõÉK4�&ŸüMQy²¸’2Ÿ‚BnfnjHeA*X©‹’9P(h\èƒ²öä\-90gƒ’Áäm)çŒ2”á§bÌ¢ P«9YZAjó•2.,î›¼QJd²Œ¦Ød{EáÉÉ¢Ê“-T%&ÏÕš¼^Ñ}òMQÕÉ/Õx&Kh‰NæUçš¬©.:ÙCK|²¿4×äwÊÒ¼%©9©éE‰¹ê`3µ” 6OÒR›¼BYjò)e¨¥µšÖf}?F )@bî%�xœ]�ÍJ#AÇ‰«®Q–‰’¨%º‰ItF<xˆˆÈ‚„½d{gj2Mf¦‡ž?ñãäQ”�{ó	æäøßÂ³à´IÄ±é>Tuý«~ÿzÝ»3®d=sc‰£ á~;‘ßDkG0N§`sæAI×�äRßæ$<2EÄÑèbƒÏ0[¢Ù°¥*-jÔI?ÓA³¹J]Ü¡Ç(i×e<øýç7y¦5¶Û£ ­‡ø–‹lÆÁ%<ÉØ‰¢ÀeÄ
u¨€™â«¥yå³¶?e_2±žû“ùe)>"’‚}1VÆ5¨ €¼^ˆ‡G®zæö)ùj_¡(ˆÈ?¶� õ8†s-ù8¶vn2?mF´”QXr@¨ ƒPOóêÊ+ªª·½½÷‘áôIb®uzÁN¨DBïÄ3áB5Ù$ýnq´éaM^�ÏËÛ2•“•B,/Ãœ$•u9TœÊ~¦“?ò£}�„ü[œèmr¢š“Íü@ºø"?ÖùgÕÜP²~Žj¡+¢öáY¾WÒªza*®—þw¿´½Õç£xœ31 …Ô¼’Ì’ÌÔbõæïGª>«÷JfJ¼ñlöáyq1 Ö¬¤ ¿¨¤˜aÑŽ[þÊw—T¾«>;Sä ÷}Í m„£xœ340031QH*MÎN-Ñ+)fˆ[˜â©|t—›Âm“.C—xY¦%?!ŠÒ2sRuÓ“Kò‹*AJ#�<º«qLï››ŒÅU¯sÕ|“5•ù‘•$��Ìùª°k‡Í¤iÇþTéŠõ)Y¾‹@RRrdî=þw]îž©køø_=ïó…ó±P%¹¥9%™0£.:ÿu²ß¤}PgÏ2•/²/so^ØÝ ­âNI½xœ]�ÍNÃ@„ïyŠ9B”’C%åTè¥·
Ôp6nº4ûƒ×A�ïN¶�„”›Çžo4®Ë²@‰· Œ68²ìÕêá(œ²ðÈãc½2ÁERÛôŒ¤A¨c4ƒ¹°>åŒ×Û˜ÐI"BóÉfR§ Ð3/ðía�žF–LÖ_c…õÊr"Ãs~
 .KÄ:’+�û]† Ûn¦"2|™�3çÉ1o¿®àèŠç5Ì™$US‘|ÎO™à¿óÁ?Þó2¶L|g¤…¦ì„ZÇIÉÅ;rÛs»ÕvÓ° zJºb›-Kr^ÿ“¿Å'Ž€¶ªxœ…VÁRÛ0½ç+öFÂÐ˜�Cg`(-´L™¥SàŽb¯‰¦²d$¾½’,Ù²bÒS¬Ý·«Õ>½U²íí	lÃÉµ�+(žk*8”BÂ¢¡¬ ü~âães!�T/A!Wta¬–¤aZÍM"›ëB(9aÕ¨Pƒ^"(R!ðÆØC„Û…È|I0»ÚÏ”(õ‡j„’"+\N¸^Rõ"£åÄ&~ÛÛF¹ù%,„©PÖÌx FÙ Ó·½�m)3›.›ÐªÒÔ´ª^ºÃ½B)E[ó¬4Ë­ÃÉ$k{ó‹HM	ÊëF»’s‰DÛÆ(û¾¸Z/9[¹ÓJ¼o¨ÄNï›p¨Óœ…)QŠZ`q¢¶7ù=Â7´pÍÊv³’0…méøäJ§\£4A°¥Ÿ»Â^& u³`4?/@ii
<46múy'Iå�ïzn\•C¿2'wxº$Ú:xS-PFŽTÊü}¶!?Óq¢ŠVxmº=HNŸñdeHIƒ×€íf'$ÿƒ¼«þºîX€¡	.CÅJi«l· šš	R </ŽÓ}¾µ<I·hÌFÚ¬ùÔþ\�9jsGNEÃu·Mä¬�Ô"n\5ã©í)¾´R7aêDHt¾Ìži„¢fíÑüòz@ïXf¼JH«ÔCÏ<‡²}ãZ®þƒ¹ð´o**Ü‚õj^;žØ9d´6C;–Ef³RÞ*å¥ÎQQ­�ûabó}Æ'RÕí÷íí­Vöƒ<ªÝÅø�µ˜;‰ãÔ�@¿ïôÅâz‘í´ËT_•Ù>4�`{Å�D§�(qh‹Dèó¤�ï…éÙ<¬½;h4dïåÙÂ;C”.Æ'*Ý
§Ùò€^‰!co‰RZUÆÚµs¿Îf‡ž°xæ‚Û�ÑG0uñýpœt—çèL‡£²Ý,bu�Ò±Æè;t®£#6Ó¡ê››ð;Bî s»‰Øhò¶¾ˆåÍG£¸Ï"7q	ÇÇ°Œ±iV®;ïÍ°0ê‰N[ŸºG2œ%LÌ#ÝøKB:ûHÌEÂEb;YÊPj�cÂ»°acÜûÕÑmo¢áÁ•óV‹£ê«û�f î¯ÉÎØ×Ý¸ÔïØ=›�
ƒ¡G½Ýÿ¶ Ú¸Zxœu”MoÛ0†ïþD/qŠ&Å®É: KÖ!ÀÖK{^›±…Ú’&ÉËÒmÿ}¤,;Ÿ;%¶É‡/?¯//¸„ecŒ¶sÈtm,:'µQÚJ_ÖÖÚ‚óÚ’ÅZVFXïÆì:‚—‹âMš‹—	|¦ßÂF8ÆTóh«šª"Ë{}©·¹NðK¿5³�Ím'n`Àð˜6M’ë6‹éƒ\×B*@å¥ß‚EöçU€€¬lÔ+¤¬~zMoB613r{Â
+ê�Úa”¡‰ôdå5ÔMå¥é
ÊÓ¹ŽœX·HQàx?%òD»Â1É~' ¤­¬…ÝÂ+n¯@4^�¤Ê,Ö¤œ 2ŸP¶õ
í4ºÜ‘fY(vRäË †˜N™gÚæ�>?/æÃ–Á™,ˆã¼¥rtœ%þh¸@¢
ÉÄ(�¾­„£ªl¨èTæ3 ²Øô>Xëêê¬¿Ë<Ç"ÅÄŸÜ¢¹6R ùh~wV\k”$™=3u^¬(K‘Yí¬4Œ~Eå†§ÔçàzÊž•ÂÃb›©¹;Yô" b'ÙzqÒ„¯4œô1`vuŠ.4nÂ@¢ù)çÁÊB*j�“oÈ“¹'GÁjëi[}ÿÈ�Ç„e+95ÚùÑÞn[ä%/ÿÇš�;Ý’^õ“äšÚÒ…êÊ•�YÙÉÙEî¢}zE’]Îi)\Ù–¡ßÎLÓú(Œ^ÇýÖŽ<í�ð,ÝËiNj•ñ{Ìoýæôgšüí/ÇB™&žÒÂÅP¸Ùmk»Sá6<Ô’ö>lj�
-“i:±Ê¤’ŠÔGŽOÎÚ=nzæM@½ïž¯` s>mƒ0ø0Mþ™Xâ¶‘xœ•–KoÛ0ÇïùD.M‚¤Vl‡uÐ'l} �s!Ût¬Õ–<In—nûî£h9i•¤Øn–EþHQQšŽF=Á‘6™®„T€ÊI· ƒµAëjrY"XGv�Ñ-–87¢Úm½•#O‰vÅr
*t"N€PèÚI­D	7{“TWµp2!b.±Ì¬§L{ø³ÖÆÝ¡ÉEŠpêcþêLG#¸2²f¸ÃÝÝìØ» Èì#¥e(ÉýÎ°IJ™Nˆà3o”üÑ ™ù•P4[Pb\›X‘ã°Õì6[Ã-—ã+p/3Èµ¡â�>ú ®À¶8LqÁÚ§þ+dÕ!Ö	_‘m¹Œàô*;\§Þ±ë:û¨¨*OÒ^.³’¶Û5&ùo1Goìª©4â­¥I¦HWÐ6{Lp�Ôx		æëœK#çÒï5g DŠã‡4ŠS?Ÿ�Ÿ€[Ô:�êYÉ
oi&vaiXùŒ^ŒÉÂ¡©Ñ¯C?ŒsbŽ‘–ÂZ’B*¼"a€»ó]è×…vº?†~¦Ó¦"±øïG’�îWÙoJeµ©–ÔEµ‹hêR‹Œ~±w7Z¯ÖÍÙÁäÝûP[x_^|JGŠ’°c µ©¦,W9œ‘]—üæÉ­Z©“òað,ë!³é¸¶â%}xã(Np¸Ý¨æ(èÕDáªvf3ûf‹ˆ"úéRB/$›Á§‘Ê"à‰rÔE<qJM¨øg,û½Á]qWôŽ›™ç‘º#ä�×÷&ÚRô[J‚˜»I“> ÷™wâQŠ¨;\Í¢­ßV-Y'ß1u¾Mÿ'Úî}ÅÅ6nèA‰ ðt›¬Úù2ÌES:K-ú]§ì¿êT‡­Û6¸ÎÝ$#GG�§óp±ØcþE^‰Ö%
µ¾ÖŠâÊZÐ¥ÕïPÏpÖù¢ô³ñ6-ÝîØpk5/ÚÍ=ážQ|ý¤E£ˆ–á½Ý‘n”Û²ý×˜j“Aj°í€ŽTHPU·îü³r?¦�È‹š§›4uæMbÏð{åù§×›¶Œ™ª×^ Çæ‡…Â§ö�ÀŠËJÒJEãôdŽ
kŸ
0�Ùx•ãxtøêÁ¡.ð‰ÑŸ™øé”ÝŽÌv¨";K�–˜�/û½¿Vôÿ´Uxœ�TMoA½ó+,N…¢*7z*ù�P•*jàywÍâ²;³�ñ†Ò*ÿ½žÙYB jzÃ³öóóó3Óñx c¸¶Ž °5²2Â²G�#Sx¼‚º­„tmSY,À“÷lÍ‡ ±t˜o=°™4Î–ZêÏò=ìX6Ú!kó-I(œègc5‡��[cNpß×­º6¿ ÓñV†´Ô7ç"�[394–‹‹€éû¢˜�§ì?¥ò;’K[ÚƒX��6Q˜G60Z­7	¤#x¢2Øì;åa2
êt©è*ýÕÚŸ–-¹&/X7°Û�‰½Ó;¢±0J�p?ËnôG�’ôP iý†lžz±‡—0ÌmÝT¤uXCÌTW�¯Tõ–ØâUE»Ž¼ÒþeƒrDìˆñ[Žó£Y_ƒéŠMÁÏ\´XAôˆ¶ÿ»Í¢½n1ßtyìµŸÂ Ê¡zP_•,©¢Òaª´Ç’Þ3×C ë­õà¸F·K½lÅNØäŽêàŽn^u‚iëŒÜûv:õï?]ùHêi¥—¡k£�“½™NæUÿdÐ�ü5æž;±æŠž¸€µšÁ‘v¥ç ½âù®W’”~§Ùçô`mw|=æHí”U˜;«§žYQ¶düÅ9jw·çØ÷Ý¢`qs<eXnxÍƒ“sãKJ_œm"nÒó/Ò5C¶ò©NŸæ!<-¸]bU9‘‚ËS’ß(·® 5Šþ×�.9–Ä÷ãK}üi‡Â¥£xœ340031QH*MÎN-Ñ-J-È/Î,É/ªÔ+)fHà¼hÆÍTË�¯i!9;ëè‡³ÉŸÛ!êÓ2sRu‹Ðµˆ�tV–^$4cÿ¼�Ò{ËÂŽ¦‰œ•FÖ‚ª::¥ë—Ý½šÀs›[®>ó1˜¾åóOU¨êÜÒœ’L,èMúuç9Ó1_ÑÓW÷ÝÔ¹°ãÝç@¨–’ÔœÔô¢Ä\ÝâÔ¢²ÌäT�r±—³¾¹h¤…¬Úxƒ½\Ìƒc]õ n
a®ë€>xœûÌø™qÂ}‘æ¢e¾ë[žèçß0:½È\Èg^ê£½ ¾�·±Pxœ�“OoÔ0ÅïùsëvÕnÜ
Bô���BN)gÒXëØÁžt•~wÆN–„¥-½DQfüÞûÍ8ºíœgà¡#¸‡‹^m‰ájïZ8Úlr²¬YSÈËT:z�eùz�Á®©sA³óhËäkTµó{‘tr€Ž|Ð�É*ÚÈ±xò¼ìQq nFa¸¼þvNº‘µ³Tï½H˜¨Eð*¯Ê)DØp(¢XžÑ]Š?ø0º/ÂÝg )2ˆõ¥'dK»½óNs“’Üè[²`±�Ic÷»=¶éœÂWié­þÙÿÉœ
«/¯â‹¶7 œ½�ØÎï<qï'OÁQ)Bµ×ð¤œ¯Æö\žcy¥Ï@æ$ºÇgðYö¡½éÞÊPïµ­i,eb2Ú)é³4Kv`œÛBß=½EVMD�œ€ì¹°½1ìš84Ç²ûÞ.@jIu1|íçaàD¡¦�rg �™Ct¬ÈËÜÐt–ÄZIyˆ¸KÆ}èsè=àê…Ì4í0§4â³ú'Ó÷q®ÈPº7“ÊÄ…�VSM\0§tZo­…Ô×ö†µÌž¡BÆ'—‘^%íâŸ��Œâ‡tûž
Ðuï0LíÕ	5š0¶h1ÖUZÝ	î?v?µžÒ9Ch&qÙ�ÚÆ¥‹³ŸòÈO4º…—Þ>…_:êÎŒ.FÙIä™llù?ÙCö××¡Ý¶?xœ­’MkÜ0†ïþsËfÉÚ÷M)M%„°9•B´òØ+*KfFÎÆ„ü÷Ž¤8vzé¥ƒçã�÷™‘ézOÂØ#¼Â­±x¯(\Àž¦xƒ†|geY¡&äª‘ä¦—ìÙeQTëukxÀÞ³	žF0. 5J#4ž>d!µ�Ð#±á€Nc)�±÷êÀ�”á(M“:x©UÁxÇ "°Qž˜tUf\~Šji¤ö„,MIU÷›Á7 À*jó`qŒ5(MžºÁÓ[ŒJ{´Ø’ê CfÕ"'š)ºaÕänIÅÉU�/i›3ýnB_ìæµ H±sŒÒ1MÍŽ²y¡TÀÆµùXC™Û¾I‰øÊ…¸’R"5F6‡§YE6 =Õ+5¿iÑE¡mZ	ûÎù=Ï²•|M2´JÊÛå3øùë|÷òã—goê¯rùÉ¹((k—q]
ZóŒ.…/@Ì`ÜöaÌþÜÐ�>SÅÊ]-X{9Úããî&bÅJ:Þ2È`S#aHÞÈr³�™ÎŠÉïãm°Ês¶rB’-/èfä¿¯ýðþ–²ïÏc@1{mÒ~O&=&ÿÜþø~TmÌPé÷ßTÙ²½ n·Qb°çxœµWMoÛF½ûWr	eè¨Ñä"¨#ˆÑ¯ÀŽÑƒa@+r)±"wÙÝ¥eÁõï›]’"e5q
õ"HËÙ73ofÞPYQjãÈmKIOô!Ëå�~“þBÏ”]ÐÛñx"•Ë\&í$Åƒ·ç''“ÓÓ:¥™¢›³‘ÝªX&ÄÏÈÈX›dJÂƒ‘¿¸¥e%ŒPNÂÊiZ‰IJ«‘ªòœaU¼–î*!¡²g?Ë-=ˆ¼’vŒ§“ùèƒÌ `RKødðkïŠä£“*±ÁßÓ	bƒEJ··W—5Þ_š)Y ®CSë{JÖ™L-Ïwzñ§Œ­Î |¼»ÛÏ-9×²Ô6sÚl;¡§Úô˜)¥±™E±äd=©`‰ØYðçY}}{I¶ÂeZYŠ+c �oÉ–F
m-ßž[O’…¯”;;÷¤öNG ŒŸü»W�Ü6øšS|þC4A¹".Üù!Ø›�³Ñwßÿ@±’r¨²]�Ã¥ŸJ”¿ð'4¢Ï+Ùû#´„•ÂÄ+f§¹b¤«ReëB¸x�Þß�@áœk1§ÍJ*t’ÃÅJ%á*×%J.¶±‡¦:ƒ)}BKgVþèkð71Ì;ôó«2,«EžÅ#�Ä±Ø•§-áB¦™4ý\ƒ-Z:ä~ZÉæZ¯©*�˜é§Ú[Ô¸=RÆŸe.—œŽ?¬TöW%9‰È:±ÀIh;Z .§×RÙAŸWßgß·þvËH]£zW—G$¥qŠâHít†§l'}Z}©w”©o¤¯ž�³pG`åÂ;Ÿ©®¢}ñöÕìUä¼‡ 9´)¹éî€~jþAÈÌM„‰ÊéÑR*6¿Ad%‡G‡ŸƒCYÃ	T/ö>{+‡6™[ÕŠéQJ]V9[í×"¾5mÖÜ^Ž{Éým®ë`½‡L!Ó@8/YŽásË‘@?£äñè;"‘y†lu p�æí£9e–æo&oæCˆgQhU#HKQie•èQ’!K¨1vð€„‘(§V2Ëèóm§ðJ[é÷‹r¦°Q©ÈÈ�ÉÖÀhC|Øûl3¯‘°DŒÛn_mm¯tL£í‰c hDÜîõXÁ`ßº.ÛïñJð’Ä·HŽ—ã@à 3¼iS4€ÍÜ-Ä#œ[@þ*³¢*ÐÚÅh:mæ.ô•åÈÑ{Ãê„q³4ÄuíH+T`Í¸<@1/è¥ï9:…ÂJí_m:»ßæ³@iGbÐiuZ+³±~ð=·×5»†g lñé‹×�¡?7ûg-íÍq=þáiÍß´¦-î(9p§3hOM‡N{¯rw÷çmÀÝ==ïÍä�NÝÑI¯:{¢ÜQãÿ_†çÎTã›"£7´(ü.ºdHóTä&Ód6È}W6¼ôvß Ã­s)Ô)ªÜeeÍÈÍÂk-¸«þ3ë023nÐ;~RB•»�/P…Ž]å•³KØ!v.8Ü­}û5]Òá(8ÚßRØ…Îÿé¨]µ"Ùaç[Ié¦Wó³ŸÎÆáŸÍÒ}‘Ä×‚öï]~(Ò¦„�`9öáIà¿œhêÚxé9é¿IüîMìkrã	¿»Çÿ�þªi°§¸”xœ�VMoÓJÝçWÜ]ÒªIŠØ„^+„AõÐBêØs“µg¬™khÔ×ÿÎ™±;I[x,Å3÷ãœs?SVÎÉ¶bº§«ºS)/ñ9ë¯«Â)M´ò®¤ñl6g+F‡yÙÙŒ_�FóÓÓ�Ò'®\0âü–Œö+•3­œ§Ï/igOu´bL¶9Ïàý/² ^åH6|ìáà¢Ä8(¯½”"&¢›àó¹ÎzH3	71&])«Ö(s²9Ž8„MY3�hÚü0ºVEË£ÌG|—Äê9-w
(ß�ˆ’„ÌK™”0)²üóÉÜ³Æú\©’²:¿eYjšÒp}½|Kn•Äå×,­Å¾Wxùž·­„vÙwÎ…nq–±±ë6#ë}/ÓÔ—Ñw©caW†}—0z‹ŸJzÛtÓÄë¢y–ÚCÃ˜LQ‘5ÛX'X·\Ább•uFŸ4^s|çža3é(/¥Ú³†Nÿ8ÀÙž,è#úÑ~Ý¼A´‡*¢¦p�VÞ­=¤>.@†ÖA£í î«Óï*ÑZ™�J�±/•ä›^ð3BãßØº(nÈ¬È:Á(Ô@†ãø€ÆíÐõª¬ ýr»Ô“À#œ§ó?ŠI4¸Rþ½wD[av\Y,‡ñ;Ê$nç:(c{òà¨üŸÃSfíoÀ%ÇYzüŸ°–60À(
0-8í òœ;¯Ó;F¼3]4ÓO­DÑDÕâ¦ýT o¡„àq0&e�+x½ô/L^ìæ±Ñc”|ÜÌ�¾�ñ›ç9}À’%U´í	±Y5{Ns‘Îm]f}ŸÿÝT\XRÞ«mÜ)}Æn±¶¬à‹ÔÂõ|´üúí1ŠÏM{ ŸF0›`Þ,œ³t (kcÓ+¥ÁÓ„ûÄþèDÏCY°[ãR�/‹v)Á�Ä”D•Õ0Âï÷ùc‹¼Tw×-Ü)&äÎ”uÙÖ!ºuTÐê�À4É$ºÓççç'ûá 6Žœ§ ”œÅ~ÞI²q�;V&$ÑóøJ]§Öòq÷ƒßÆ„Á*;*qªß^Ô§×•JÇ`¿à—ÛË$À$žÒÑK �ör,Zšó¯Î¼Ý€ñvÐ2÷É¸î"lÍ¯ß^¥{¾øÚæq”ø›à
V¶¹±|'ïŸH-Ð„£_¤nã´‚xœuUMoÛ0½çW½,	ÜäžtÁÚµrÈP´î¹Pl:bK™>ÚfÃþûHÉ_ùèÅ™|¤Ÿ¦ãñ ÆðŒÖ—t…6ÂäRm@@!K§!Å7FT`�6bƒÊ™ðs¯�©šBd�1·û; ˜ŽÇ�n±`Ä7™s0èŒÄw.å(&#X WG?ÒÉ2ŸQUCAóKx^Éß[Ø¡ubM0"3ÚZXkGíïPÙÑ9òkH½Œ_¡µtOXÞÃ‡t[©B‡õí!Û
ñê“Ug,å«5šùàß`0�Ür-"‰n\	'µâ{{£0‡õ¡½È7t!ôöiù¿iŸl)yVþá:„êÐÆö-|ÞõVËÕ¸ÃyäÇäW²Â7þuÊË“p[ ö{-‡,‹æM˜h®?T©EPO×ÀžOÁîšÉPu"“òlè£Ö‘ÓuDS|±HÇîíšÎDˆÕÜG+>q	z�&o™VÎ¹ÇB*Œ3­B~è¿'~.o/i?a„žtCŸ:‘'*‡J(±ážycP9ð{&Æ~1ÙeSå…È”tPO–¾¬ ØV³�ÙÖ«·V÷_È�7ÄØi§A§“ q~ìE³ƒ?Æu{ åîaøŒ"qE•À�/
4	0/ÁcMÎ�~‰
k m$][”1^Ñ�óð”E×«D˜•ÂZYÈ,.È'›	\í·Úé«®r�ùŠH¼jËÇ²Ç^ sŠ!4¶‘53Qk)¦QF:SýyòŸŽ•™ÊN‘"“öœïØˆ¯;M{KÂ§£<]I‹7GN¸˜úÓ|®%~¬((µ'ÞCü½¼ï0¥D¢,ûc'�Û94Œ°ÑD¬¸ÖÆº“ëÈ8Î±éÖ#:ómR]©õŽä|:ŠUs©²Ò‡bçI:¯IZZŠ»ÒZt7ŸÚÙá†—Ÿ‚Ó§žxBöƒ"�§B€v–Ü"‘PH§q)?1ó¬¼>-Ê½¤$UèÓsb–ñtk"– É2™ñ®e³ÉqJYIgÃ5	I{“E¿áêÇÅšp�×p«@ØƒÊ ð*:Y¿ÍS;¸´¦}È9Žñ:^1rñ’oÒÅ�Ï`8‚ï‹ŽÏEŸÜ[íÂ«Uã…\xœ["0Y`+«YZfNªsFi^¶•�ÈË/ÏÓQ ‰ù%æ¦Z)—eæ¥CDB*à"›}Xo0 �jAxœ›,p”wÃa6¦Z. ìxµ�xœÝYë’ÚÈþ?OÑv¹VÒ,#ðe]	“Y¯d›˜Ëï:^/Ó Ú£+µ›ª<Dž0O’sº%Ô1³©Ê¯Ø.ê>×þúÜÄƒUâG‹‹É<ŽbØõdIcæÕÕÓ:=öÅ8?ájïW2v»î›¡Ó›´Þ^÷ßMF�¿»“žóËäòÃØ‘í>›Tp?©ßQŸ{Tð(V'<,žÓ#ÎjÕŠÂ9_�¯'„ÔOOñ‰Ïgr+¹cq‚ŸfÌ¨§¯èì–.˜ý9‰Â¹e+AxH’M8#ÓIX@CÁgg1óMYÅljØÁçÄ"§u�AW«÷Šm“$"æáâ<—ìûd	"¢[&$`ñ‚eR/ãÉxðÎí�ˆÅÅOò=qÚíÎ¸3è;Ý‰¶kN}
ªfB�íXrÍe~ü”K½bñJ�Eá,�cÎ6d2Æ`Á"¦q®:dìbzlNS_�§–­øŠlÓe$Z}“„i0e1ŠHDƒ¿ZK*:ž¾0÷\Ç¾î8 ZñÏ]§#�¹á�¾°Òew¬DSÁº<àâg€P´î%:�Ýb�~²ßS–ˆÒºˆÓD\ÅÑ0fEp˜¡RZÌ–@Ò,HÊÖ¨…ÿ»ÜVZv2.#os°œ;±µLÃÛJ³(  %‰ÜÁ¼ë•Q/)i6Ã¥V¶ÀÕãa%+ê<tV\ÂAwW¢èZQtËYŸ¬bt¬ìÉä¹3›Áò;¶)Q<±YÌÄÁã¶ÂÐ�-ö®À
½=zþ†‰’aÉó÷Ë(í( ¼ŒßíÉI½NÞ«›Í´«Ó,Ý—�­-€Î¯lzø�ÀuHDqo†tM.”’h¬ÍÂ;[òí¬}¬\¬•h*…}²çÜ‡ˆd^*³-ûsÄCÓ¨«91éŠX2V©ðh³8Žb~bô8œR¸ 1€�Cì# ‘ÇQ°P�;s:õÙžwþH4±Œ°$V—q´&àRâVþ_ËƒVç�[×ž\HóGãÁÐyãBpú}·;é´›%G®#ÏKgäN®‡ÝòÞü)îh;c§z—¾‚;¯Ãqy>©�lÏs­ƒÌ-d0ýÌf6xYbjæXÀ)?~óã¤~óSöÉ"?ª¯äââ‚¤�ç<„#ýöM{l’< + ½eE_rÜdØ>bI~$�
ðüQÜµÜ¢J(Ü<€‚'_sm2tÃÚÞX÷xôœ@àð€šSèÀŽÑó‰Ój¹£Ñä�û�ð„°/˜¤¹ð1å
b‚H…é6Â2#Ä�¿A• ‡g¹Ëª!·‘ÛºcÉ-€@O@PD¦™ÙÄ		VYcC"¿u”údÂÒúÛžÓ:½užýðÃÉã$…È¼Ç$áàjTëßÿü¡È!�9°ZÂÅ¶3XhÓÍì ˆ%#¬*4l�ïhwau�¶0é�¡P%ÀTÁPA$— Á,Ý­Òq À¡ÈLë\£TŒ+ˆ5ï+UQèwß‘G9¥Ò>n`�X˜¦bï„/ò<ñLÄ~Â?ílbžÜRø‘øFö'¿(Éž Q`hÄöµ€À—†‰z\°²e¨Ë‚ëvgæ:åpÆžýk‡¦q`¥f~Ù/xÐx¸Z4…8°æPj"ŽÅ’åandjìâ.Ô®	ëË\�ç(#Ož‚É·",ÕvÙ5/¬üF¤O’¡¼S[þî„B±Vp¨‘§©¸ �Ã|+O^ó�f*z'cq�¼Ê4wºœk±X.ªz¯%VQa€æ „®™%ÁNàž™žÕo…Œ½"ƒr¶’öl‘<RA»¬UVÚüwjiö(*K©áIf–Ú%µü™¡Ø†fÄ‡®Ç¬ÿ¶b•¼jþZÿµ^¯í,+l«ÖÇÆ§ü¡ˆºÑšÅ-¨ÍM+ËÍyŽ£ÉmqÑKúïtÎ! +¥l~¶†‘_zù<OT¹@ì6�žž nž|U»ˆWÌlÔÈKkkÛvùñÙH/:PÕvÑbÜ«/S¼v.“¾2?þÖüô½Õ„ÏŸàó'ô]³^ò´	*þ$2ÅkÊý³9‘‹”Æžj§*ºVÙ±6UéÀMUY/[&F �‰eÞ�“†…k×’yÑ,•é•†ñ!iÆàš$òï�¨~Ç)Y0ñRQ�¬—|¶„üçK^IºÂn*! L_é
cÖ³é]óñ%ô¸YŸÒ”0tˆ‚_<&tÝaÀ Â²‘•£ôÞ=gÄÇ~
¼|œ¥öJõ»¯MÃB@Æ¡˜Íeô‰ÀVØ‚ìàl²£9\_îgIÙÎyžô)tÃgR«œ]žO«û'8^-z™{µûÑã@tß;a¨�X*¡ûãýäÇŠ÷›£]<ùZ-jeV‰t4™<H(e|¸«^v:	l&7äûì&ÃÍ¹W»-™J#Ìö‘:1Ÿ6ž½ §?¬- È²5AÆ«—�{p„�!
¨ÓQ~ˆåñÆ¯Pá±F "S(’÷ÁS`RE˜QÈ�„»°€jä7˜§�zú Ãàn£Ãcûæþ&éÿà€oŠ¶¬~zzkCF3íhÅØì``f’]A €=’0\žëL‚ÈKutˆŠ‡€ŠŒE1Ž“…Î9ÜÖKf|Wž;p�1t•N}ž,ñg,øœÎ 5CE^s9äÊG8òÃ(”ó2(+™£„ÅiâFi-EF³%›ÝF)”� þe†p
1=]a‡ãá¤Pb SHÏŒ�Ø¹ÈKUÔ_ÓMÀ;ÉÊ6pÏ™¥Ü÷Ð³õ]³L½+¥f6iÄTw��TU¥"ƒ‹éÉCçÇO²�*ÎE�V*O™'2­zñZÞ<éŽ=ƒš+5Àµ&¨íñ
6 ëÞÃmÝ¶ëº‹o œ+ªÌäy,Ÿ­=ÓÚ–IÔ¶Oª ÇÜkîˆI¦°6¯­³úd·`¡_Svž­+¼¶ºÅþþ¯£A_Õ²¦nQÖ'˜O;¯¶_½"Æ×­a¡—
ž$Çø+}2¦þl‹¯¨(¶³Í»BI{–oÞ‚¥ M)²/šKòÝ'ùÿe: ÁÛ!#[ºXf½J^ž5løk¨R:sÓg—£õ¦6eWÃ}~ˆVUbj#k½Ö-Âj÷Œ õä_™ùqÞÔô[×Ã¡Ûo}€®CòÛXï”û‡K�dÓRÓçÚU¦Gµý1÷±	“Ü©&ß•*à¬I	ÅÖäE£Ñ¨é£q}kÐv'nÿ½ìa<“G+¬eëYLÎu’îàÍ¤ë¾w»’†‡óHn®©sóÐ»“n§×O~îôÛƒŸ'½Ôé §rSõ þpÃ5tÿvíŽÆÀðéŠ�>¯/�öðz4ž\¿|P3ØÈŒÚá(ÿ˜äKgÜz+…vÆ.šð,?æƒ�ÿÃ,´2“üÐhdÉ5Ï±µc¯
Ž1Ö\hnÈY?+3ÖùWµ{Þ1è[ƒÞÕç
²dpÛ“ë«îÀi�T_"Q¾½ÿeÄ1sT’Ëèú“^§_òÙ‹ÆŸ_J3öÞb”'ìHg6cV#…ê¥;�Élï:î¤ïô\I+ÒIF©s)Þ„³f�ž†Œ‘1§¶üé%\ZKž�º»¥)÷�Û°¢ðâhu&ý ôÒß·<0o«U½ˆÙ£i»¯�ëîðõ¬�tirÕ‰8{*è¯kJ�	ïÚ8¼qÇû¸Ø—£�?T•¼§Åû·Àv{Ðs:ù[Yõ«ÒÐ† ºZÒxCCj›{5ýÉšMñQÅ^µ¢Þ2¨�}Ö�a¤3·j€Md1
Dù4Kf6•mÛVTé¡¥.õØÞ=‘Cšbfb)‚RFØS˜mOFµw	
®9IiC‘¢iÞNÍÒ¢öÒ@‘– z(M[¶r“U»eI°…£€îâ?u*`Ñ¼å
xœÝ\[sÛHv~ß_ÑÃõäšIÉÒÈ¤HÇò%3»¶gÊ’s©)WÔšd�@ €’9VmR©ÊóÖlÕ>î[žòž÷ü”ü‚ý	9§»ÑèÆ…¢½v²«J&}9×ïœ>ÝÐéÏ¾}zñ�ß='ËlLqŠÿ‘€†‹I‹…-l`ÔŸþ‚�ÓË(ñ–4IY6i½½xÑ;iBºb“Ö5g7q”d-âEaÆBèxÃýl9ñÙ5÷XO|éòŒÓ —z4`“¡;�e<ØôØ³$ŠÉý'9?$ø•¼¢!]°ä´/»`ç4ÛÈO„Œ’(ÊÈ­øLH¯7[ŒÈ/çâßX|=ÀïGøƒß3ö>ƒ†!ÅŸ±…ÍØñøøXŒŠŸ%ð�ð›¨çKÐtpt|ÈfÅPä%úúÞÁñ�žPŸ¯Ó9‰ßË~[ñûoVÌç”´ã„ÍY’ö¼(ˆ�Ã’­Øˆø4¹êhFl¶4c8~U06<ÎÆ¼GþÐ46Æå¬�Ì=|Ä,î‡Ç‡¾ÅÝÑ	=Éã5ó“£áC=õÖ`ìWä–Ì¢÷½”ÿÈC S. ë¼“M<bL}_<‡Ïrà,ò7šÉ9XMoNW<ØŒH�ÆqÀzé&ÍØªKÎ^½¢Þ¹øþzv‰sÎ#o¿qºäM4‹²¨KR¦½”%\³0£ÞÕ"‰Ö¡?"×4i£ä:c"dŸ· ˆ Ö`½%ã‹%HbèåS¬xX4×KS©nÅ3šh.|žÆæöiÀa�Ñ`(c–ŒÉ‚Æ0ÓAn¤�6’ƒ‡ð¤–ðƒŽf+q–E+ÃÒ(à~ÞU<Ö½ã(§‹@iÆ½«Í˜ ÕB?öxè³÷#r4¨ãÊ¢EÚº¹Q2øj E(œ¡¨†	[�KCS0Ô”Ÿ­�ÔPËI³|¤Y�Ü.ï`F±ž;™ì#¿uêÅ¦‡ÖiÝ`dàž|u$XñÖIŠ=ãˆ•™¢YE> Æâuá Û„u|iØùÉÿ1kÒ÷
#9°ñÈÖŒ'lÐ—+I|(üæ—^¹ÖÒºgÉ�Ò˜z,AS
ÐÜ†ÕŒ&Þ²$×¿n+QZ?J‚�CÜê<Fß>Ö¾­ºÍˆ¯^²^Íª£í**ÄˆbÓò)©#¼PÙ[6( …£et-”„\÷|æE	•¸’b	dñ »UävÐ1ðŸ˜œ%ÑÍ§‚ÍAÉ<ôžË©$’%7B¦Ò¦U½ÙZN ]aÓåbŸ�š–	Õ
¹æ¶Q"½t™@4B§17¦_¦c!mó ¨$Kîû,TÓÍ,xœò_‚Œ{Â7G$Œn×,€ÃŠÒ“A™ÒãBML=¨8Î‘(Š|šŠ>~:ê¡*S˜±dVÂ‚jíP�i
S0Jz_Ž?a„ÎP2·‡…qš¦!ì+¦	h´�¯ŠIÚŒj'¿ƒnm˜uVc¬
3ói}H½¾
öuâ0çï™�üJ ;Ø<™þâlF$%wàz�‡ìm)» 0ç>ÐE£{×B5EœD‹„¥©p50»f1æéWU††„k)YÌh{Ð?î�a¸Q‹ž?¬!÷›ozjsV<Ð) d/–D4;M|ÍKCB­•®ô½c^­\þP„Pïó–£JPÕä˜évž—Ÿ4YnàÍ~š(a¬ƒôl¤½0¨P"sÀfËÉíØ9‚e°ÒƒT‡˜£’sþ?1+™?^s:¾Ûœ$ËCpórž|lDÙÍ•ðÚR„õU$!7ý¦¹šB�Ø6æ\·6BÜ½íÑÄ¦íByY}¨]D>ÚgÝS-ÂVq¶É³ÜR,)þ‰^µñÅškyPŸÜÕ¥ïj ]gË^ê%Œ…ÍÍÃ”eöFý wßÚEòQþi{X�7b¶O“Þ	Æ´‡‡G>[tê‘¼cÙŠàÍŠÊÁGÛ�]rˆi¤åè'oñÊ[ê]T¦–ÔGH€ø`f„ô†'M,‡eÕ><ÚK·bôû¢sê‡ïSe¨l~>^Ô†B›ë?%ÈÛÅÐÃ]•‚é‡2T‹OM›C«öu\
feÖFà™t0°|Á¶ŠgÌ?¿*°õ†òÌÖ4K’¨’³kÌªËcëžO‰Ûò(6�¢øfWWóiò*W�aî“©7[XY�=BÚ
/GÕZ¡ø´¯ªù§}yÜpŠUaQæ÷ù5áþ¤…R:HÙ"^@ÓT6)ôlÉƒ ÑÛ|ŠZQÏàér8ýóŸ~þw’Ÿ0ÀjCý0ž>G]ê@AuÅð·8Çàáš¹§ýX÷•.™“u�}¿Á¦É61›´b à„Ð"€Ð[FÈcÒzRLÝ"02ò¢U°F€eàÎ°§GêµL<G+±ùg²IÈoÒÊc†„Öô´ƒõDÊïò¹^F`>gY˜Ó,·¦¢ý´/¿*±æóAËâ]®,!åO°pÜ*‹;ä}EyXP4[{W,;-…ÞmO�B m7½á™·<�ÚÙ’§î5Ö¬Sˆ)Š1hÑ<iµ¦ÿý»?9‘sh9íËn9[’ õÍ�OÈnäZUù m÷®
Ò–ÑÍSp¸ŒÉ!¯0{ieÈkvS¤±Ø­Ÿî??ìBðÈ,Í§'â$lÒ:?$ÆS”ö~ß¼hP¯ôÚEŸh�Ãåj¬ÎVìÕ¢õ>2”w-�KëCÃUr°;C¬ÓÎaC`ÃÄþÓïG°g»Ú¶iØ±¬[£?�eëV<È'6 Æ–pœ‘Ïù{×uQ@b- ŸÍ �=æŸ‹	Û¹á7x¦ð(]›Õ21›jä1Î:ÏzÑÓbÕé%O3=©®s×`°HŒa–åÁTù"Í½ uÿ¥M2æL‚Ÿ.£(eE¯y­H¶d+3 ˆ�Ðì€»‚¥'œú‚·,Ú BïH^ÜÑ”Æñó?q�,¦°¹O˜XMé_ÇADýêBy�à[¹U×ë•KCëV¤hÕ`Œhv8}+( •�Ñ€+A#Šðûš®*1¡næu3¦Îƒ|ªê„æ°ïX‚i–fµHAòDF& fþÑš¾4fÝ¡R±S,‹Ùª“ÔË¸  / {“˜Ê®14å¯—x*7NöÆ,b¨›B$üì….Ä�	:ií�°ðXÅˆýDE~<È„„ë èæ­ß	H€VÇÑmßÎ~ OJ¡ñûw¥Ž,o¥A 'Ì$]ðàŒ\c¬)à©LL&d!Dm%!ïIÁ=Yö$Ï;ày{*¥Ö!“©Î*eO‘‡< “yëÊüûyÀðãÙæ¿íèÆÑ©¤å¢â•�aµJ¹“Ð¤«Yt#�‰3"ïÊ!#â ~•YZœXy±˜ØI´ãt*8XÞqr:AX(`Œdrò�“Y£ÓyìÎ¡kÚ†íôÑ SCý’ûìÓS_•U¿OžÄqÀÁ�uE.Ü×’¼ý@wFSØÞ`˜—¶ã´â­��h…î{Sj¦\5´æ6
ºæ¡¸¤Q«òäùÖg™<Ï«vO.æÂI­Uö™\¦QŸ…t+}úKèÛy–ŸµelìÀhiDÂ~Ü»hÉ#ò2îß/!f“÷Âç¤mc¦°p‘-ÉO?•qS=é@6ˆ§Ü/T–Óî”ÝãuJ¾!êânI¼%ó®FEI®yÊ³(Iñ¢Mˆ4�	³2K¢›”%¦¿ˆñèÜÀ%M7¡Wr™,Ù—Àä˜D =Å:™3Øµ�>�yÿzØGšú+V`¬Œp#¼\fÀ}–¬õY¾ Z@Ý}«L&äá`h”qb¯‘k—DI
áéR	î×æ–ÖeÒÔžyÇÚ[âQàß�Pm8#z€¥m-&¶ŠÖ„eëDÃ\2ÓÐ[AÿoÐŸì'÷wDG3&Œ­á³,¼kðK�AöPYÊ˜H
ä¾ÙÍ¾*˜DÅ}!º¡²Ì˜ßvŒš:sÂ~»æ€¤.¬¢fT¡k¬dU¨hvµ~sÕc£í |�ÞaÄu�q‰ Ç8.ûpGÀ¹S1Ž@Þ�-#Ðâ»oÏ/à–œX’Ž€wG•È{¸Ws Z÷D>×ÿ!�B‡l»ÆTX¦‘_Ÿû¬>&ø|Ó¾UâÚvŠ®Û½Ý±bŸ¦âD&8.ÛªâWe}uêÐ¦„.s™!ÈW»ã
?R™ð ²®‘ÐP7§€^>pß1±…Óº²Œ	ðjÓcªÞj+û5Ën¢äJ.Ž7S 2ƒÂ<Ì‰æ<„,×´ˆ’½) ¨780ŠÒ-VÓ©eÄmðç�ÆÃÐÚJ6b³Ä{[˜CZÕ¡‘E"¬[ÐS¼UÅ"Ý‡É«ðSõ“bŒ�…e~Œ¹ø2æÁo©à.½ÎRì�Â^ôÂózötÜ!‚æa ê{ûÁŽO ŸÄ
[í^†DÆeÒÞ¸30ø4£&uÈVaW²’†oíìp¬«º ,¿gCÊvnÈÌ¢k±ry²äë‹W/aøåÖVïÝ4º+·gÈty–{·3qimÛšê�¤�#‘«=�Öa¶íèY/;î1l/k2<¹`GPŸC¥õ¨nOhT•!)¶žÊ;t¼kÒ»èš�ºI�œìÖ@h•Š¶K—9ŽÆ±F}åµ7H�M9û•Ü>°Æf”ÓœñÝ´µÅ;všE&öW¶a°LCnÎŠû­å�¬Go§2±0Ö²4�™×$³jVUÓÕÞˆIÒbšŒ°ÖraÏ ^@`sQunŸEQÀhXœîy%ï•ðrÅ>¤×|A3v¡G¢[BÞªR¹öT¬3Af¿^­å[~2‡@Ð.ˆ&Ñ\/üÆó ¨¹w‹�¶ýË±MèM©®ÉÇ­i_‘q7 ZÅJ[É‘XI3R—'3 }ÄJ�2ò”`A>W  2Šê^]È0­'G<e %àÇrßßÑ�ðoN€9Ð,™PN|1]yçlX'¸àe)xõïÝ²Ð‹|ööÍ7O£Uvf%ÜÞö%ì§�å¢“Ú1JÂÛû>øŠƒùNú÷ñ†×Û¤“ƒÁ@Û[Ý^¡6ÒÉFF[)±Š«HS¶¦ä*zÅùw+“jE¡.íJ~ÜAãCÁeC iWÿ XWUN·Ä×Û·MzbæDy(
Ë{P\»KEQ9À]ªÞˆÇ[½’K* »µÇ�¡òbÉÓ<Â'ñ$XùHŸü˜ÒXeô«”Ã¬íÓ[DÅû™Mè)ýð³$ØNÕdðÀ3!NnÂÄ±¤­–.qÌZ’XÿAÉÖò«èÕ“]cå:^-°Æ7#ðÀí�ÿb¶zŠƒ«{·Í.fú÷<[ŠPŠ5ùa˜0 ZlëæÅc(‘ÌÖ=Ä×ô‰¯õDÝ¶/**)ËÐà‡¡eÍwé¹ k{)3)†IìôiL0�CS%`�xƒ÷ù®#¦ÁÝ@ßkØ ã†Ù-Ó"¦;U§¢|™Ÿîßuò5–ã%«¨yÅª>¿¹ )j/n,\¢û8ûš[½ýü¯»¬ˆ¥�Ù×0oÛxg‡…Ü»]®hvßÚÈ6Ÿm2ð«ÚQÒtòQÏà›ñrŸsæ×,,ko1+ýî´ê¶þÿ¦%½÷h8/ÁnfšÛ•¸{òG=?¸`nCÛÝžRß¨ùšÄ¼P†£™Ð‚¹×Oå“×V!¹•ÏÇ2o’m.O_àûç¬�];­Å˜STÔ®Þ�3gœg5x†êœ9]ç7øëþú[üuqæ¼Ë#ÔÉ ›Npž±¬ZµÓéd88xxÿ>?]«ÀÕvnÓ¾hó¶ùb€V©›E/ðºo›O�‡£AgKîÝ®¿çï¶—ã: U¡ x)öù¼ý~Ë9Ð[QyVˆ�a¡—¾v�-ç¢fÙÆûæ  ¿{»],GNºŒ’Ìéút3rBˆè	÷œî²Àâë¶œ½Ž…„ÙZñ':3�×6Tr ;8~Hæ›%ºI:V”û…E”+]ˆ)Rð%hÉÏa�cïÎ¸tn×‰wßC)³f»Y‘›ƒ+¨UU¸‹b¶?.ÿÍ×¨ï�+m/»Î?Í^9esE9D©à%.¸x<‹hâ»7	˜>VÚÚ`vúú•,t¹QÂ<Ü~6ú‹ªádz[1 3fÔÈØÜ©�$vˆKÀŽÞèã·¬-Ž“Dí¦ÑW:Õ
âì¢¼­F÷2æ<Yµ/U0mÝ»E°k=¾ìtìŒ¶·E=ñã¾KÏ·ª~ì<{þòùÅst¶º
’FP!UÖ¹•ÂÈé—Õ÷1SþË|”%z™Ð¾I®–¼Èy?™ìUÎÜ({S:?ý$ÖV¸	ùþ S·ËUw|&�»ŽÒM,§ÓÅD>¸{ ÞvÂÞq¸W_q·Jô�ïî¯®Dù›bÃ.ëLì»%€úmhÆüÔ”†’N9•ÄÑBÈØ°ð<Ÿ?¢šmÑP°Ac!î ¬Uò¯¸8ƒ/!¬Ä±5›hÔëK3Å�YÄŠ§¬�g]QpÍº	C»E×7vKŠ^‚C^@lƒ¸Dq“<÷]œªÍ£ÓÅßv;8‹Óµòâš~#;Îy¿LÄ*ÿðêå×Y¿a¿]³âˆ_þƒ>®²Ô(Ì56×ªÁL™’:ì:ÃÄ¹s«ö^“W4[ºâÒ|ûÁÌï3ˆ·:¿bT©ëYÛ//Ç%‘æÍÛm™¶H`ì¤­èÁ&yx2ÅòÇýûEÃ)Æ1%zàQÔ˜¥ÚÅQLÑ]¾ 6T'5bÅÊpG^@ÔÇ|QŠx¨ÎY?%¥2 µÄ|)ê}îë'iÛÝî¤Ê©u0ZÅÃ¼®ÜìÍEåYŽÓ¯SßŽ™4V;»Ê—‰�.­1,a‡glN×ÚŸ+ö8g1‚^ö[rŸkpTVÍ¤	[Átæ¼ûÌÅBxu�±ð,‘]àû®sHÁMØ™:Fô©í¹ûb#YW.É“N®®XpWÜÉ– ßVÀ�:ÅQ<wõ{¹¿ñ]¤sM-ä´Ê‹[ˆâ¶é¤�ûœµÑÂÌ›§f¡o"vF»‡é€WTìÜQ_�5pîþ}@.šÀä_L& _é•¢%�ÅI‚tÄÊåÌÊ;RèŸ óPÅìÔf Z/íK¼W-i!gù¹ÙáÔ¸Ó/çÁˆ×½þÕ¦';÷Dñ�Ä4ËXNZßÓÞ�ƒÞ£wê·÷îvØ=nóöÖô4®½1]azl¿†¯‚áK7�hÐ”ÁŽ¦w|(ÿr[—„‘ü;.©ApL£²¢Þ¹­©@¥;­éS
{¯ RKÐWÉÅkÁæ5kC¹b¼ø^Œ—…�ÎØºB¶ûäZëÀ¼A+­màÆâ‘Ûþ/Z–:(j$Vì+Z˜‡¹ÇYÛŽú‹ÅÖAF]qC©|A	Íqn; N¦^Çu÷ˆö¼= ©ž„jë…­RÞ¢¼hØ›Ôá‚õîÓ'ƒü[…ø½b�}°Á~áJ‚ƒvÖª#Ž÷{ãá-dgÙ’á-J 2ÜÂ|çš´éMÚƒÏ]’€²BðçôÐ[ù]Â2ÏíèŒÍ¯X˜o¯­éóÐ¯¶’·o^žöÅèáL0ÓW5ê-’_.<]¼œ'ß«/@|(ioØV¸›¨uÚc4ÍzÃÏAÄq•ü†mJ„ˆwºåcxjcÿ§§ãœ�qf�tÈÇÅ{§wÓ²õw¿­W
ø­Šä–c½Õ@^	úÞsð¨ö¼óÚœ7t;{LiÞÁ­™öœXé’)±Èl¡e2™8Ïñía§c¬fdr¯á‚´ù"lK9â4&»Æu‚n¹ÀÙ-Õ »ºä×µêf]#�ëÖçU]+ vkA¶[z_¦›_‰ÌB�Ê�Ói?£	Œ@¼µ˜(þ–ìÿ Û¯üŠ³¾xãìimport { serve } from 'bun';
import { startBot } from './bot';
import { config } from './env';
import { handleLogin, handleLogout, handleMe } from './routes/auth';
import { handleFileInfo, handleFileRedirect } from './routes/files';
import { handleHealth } from './routes/health';
import { handleHome } from './routes/home';
import { handleS3Request } from './routes/s3';
import { handleSwaggerHtml, handleSwaggerJson } from './routes/swagger';
import { handleUpload } from './routes/upload';
import { handleWebApiV1 } from './routes/web-api';
import { requireAuth } from './utils/auth';
import { fileInfoCache } from './utils/cache';
import logger from './utils/logger';
import { metricsCollector } from './utils/metrics';
import { cleanupRateLimitCache, withRateLimit } from './utils/rateLimit';
import { isS3Request } from './utils/s3/auth';
import { extractS3BucketFromHost } from './utils/s3/virtual-host';

// â”€â”€â”€ Auto-run migration at startup â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
try {
  const { runMigration } = await import('./db/migrate');
  await runMigration();
} catch {
  logger.warn('Auto-migration skipped (non-fatal)');
}

// â”€â”€â”€ Auto-run migration at startup â”€â”€â”€
try {
  await import('./db/migrate');
} catch {
  // migrate.ts calls process.exit(1) on failure â€” if it throws, log and continue
  logger.warn('Auto-migration warning (non-fatal)');
}

const getS3RouteBucket = (req: Request): string | null => {
  const host = req.headers.get('host') || '';
  return extractS3BucketFromHost(host, config.s3VhostDomains);
};

const shouldHandleS3 = (req: Request, headers: Record<string, string>): boolean => {
  const url = new URL(req.url);
  return Boolean(
    getS3RouteBucket(req) || isS3Request(headers) || url.searchParams.has('X-Amz-Signature'),
  );
};

const handleMaybeS3Root = (req: Request): Response | Promise<Response> => {
  if (req.method === 'OPTIONS') {
    return handleS3Request(req, getS3RouteBucket(req));
  }
  const headers = Object.fromEntries(req.headers);
  if (shouldHandleS3(req, headers)) {
    return handleS3Request(req, getS3RouteBucket(req));
  }
  return new Response('Not Allowed', { status: 405 });
};

const server = serve({
  port: config.port,
  routes: {
    '/api/upload': {
      POST: withRateLimit(requireAuth(handleUpload)),
    },
    '/f/:public_id': {
      GET: withRateLimit(handleFileRedirect),
    },
    '/file/:public_id/info': {
      GET: withRateLimit(handleFileInfo),
    },
    '/health': {
      GET: handleHealth,
    },
    '/docs': {
      GET: handleSwaggerHtml,
    },
    '/swagger.json': {
      GET: handleSwaggerJson,
    },
    '/': {
      GET: (req: Request) => {
        const headers = Object.fromEntries(req.headers);
        if (shouldHandleS3(req, headers)) {
          return handleS3Request(req, getS3RouteBucket(req));
        }
        return handleHome();
      },
      PUT: handleMaybeS3Root,
      HEAD: handleMaybeS3Root,
      DELETE: handleMaybeS3Root,
      POST: handleMaybeS3Root,
      OPTIONS: handleMaybeS3Root,
    },
    '/api/v1/auth/login': {
      POST: withRateLimit(handleLogin),
    },
    '/api/v1/auth/logout': {
      POST: handleLogout,
    },
    '/api/v1/auth/me': {
      GET: handleMe,
    },
    '/api/v1/*': {
      GET: requireAuth(handleWebApiV1),
      POST: requireAuth(handleWebApiV1),
      DELETE: requireAuth(handleWebApiV1),
      PUT: requireAuth(handleWebApiV1),
    },
  },
  fetch: async (req: Request) => {
    if (req.method === 'OPTIONS') {
      return handleS3Request(req, getS3RouteBucket(req));
    }
    const headers = Object.fromEntries(req.headers);
    if (shouldHandleS3(req, headers)) {
      return handleS3Request(req, getS3RouteBucket(req));
    }
    return new Response('Not Found', { status: 404 });
  },
});

const bot = await startBot();

logger.info('Server started', { port: config.port, url: config.baseUrl });

const gracefulShutdown = async (signal: string): Promise<void> => {
  logger.info('Graceful shutdown signal received', { signal });

  logger.info('Closing HTTP server');
  server.stop();

  logger.info('Stopping Telegram bot');
  bot.stop(signal);

  logger.info('Server shutdown complete');
  process.exit(0);
};

process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));

// Periodic maintenance intervals
setInterval(cleanupRateLimitCache, 60000);
setInterval(
  () => {
    const removed = fileInfoCache.cleanup();
    if (removed > 0) {
      logger.info(`Cleaned up ${removed} expired cache entries`);
    }
  },
  5 * 60 * 1000,
);
setInterval(
  () => {
    const snapshot = metricsCollector.getSnapshot();
    logger.info('Metrics snapshot', {
      uploadLatency: snapshot.uploadLatency,
      uploadThroughput: snapshot.uploadThroughput.toFixed(2),
      errorRate: snapshot.errorRate.toFixed(2),
      cacheHitRate: snapshot.cacheHitRate.toFixed(2),
    });
  },
  5 * 60 * 1000,
);

logger.info('Application running successfully');
ôò˜ç<¦qxœ}SÍŠÔ@&3ˆ¸(¬¬ˆ +¥ˆÉÀÌt`Ö]Ä¿“¢s^Ú¤’4&éÙîŠ£îã.ñêÁ›÷yñ|ŸÃNÒ£É
Þº»¾þ~ªºº¿®~¸yüðüÙ@æ‘ˆ™ÈC|]>êÝXŠ<R\“**²€	6ewgCd3©!H‘«§8„©^=#Nzs.è¾lŠ°€HÉÜ1;ÁK˜b¬xÆŠY*y8:¨àFáxûÊE‘ªˆ¨ÙI,áy˜¢*œ­rêlZå„hÆLR25Íôdôw[~ï]Ý‹™ñœ;ŠŽR‘	j§T² Ôÿah ¶{—û×Ïü”wû··àzÂ^	EOG‰Ô•TÆ1ª–ŠNŒ¥�5ç–üSÏ½`Ï3$%«ºzßà¬¾�úì8û«èôgõõÜ›Þ6üx÷r	9ÎA¡i®&<pF–·7/-ƒ{Š‹f˜‡"�¡Ôƒ€¹ 8�ÈÐDÜ 0]Õ•Y‹‚o«ñîvÇïvXDà­i÷À‡££nÖ84 €e“nlž…ôÜÚÉIîð_½š~í&¬n=o,#Én<o »{V	lƒÇfÐ¹çN…e“³¦XÇ¬›V¿ìÊŠÂÌš"®_j×JCëí{öl1„‰¿ïû¾Ýóêt>ÂYß­\Þ©l¶X@§-ÓöHj0†��ÅêÉfÙÿe<O%çƒxœ{&÷DjBïÆ÷'˜”2óÒŠ‹KŠJ“KJ‹RõR‹Š3‹KRó’SõSŠ2«ªrR7ïdŽcÛ|�»Ÿ ºž®îDxœ{"µUx‚9sj^ÙFçÆÉâLI¾™¹ùE%
Õ
é©%ÁÆAù¥%©N¥ÉÙ©%:
Åù¥9)‰y)9©ÁÆ
µ
iEù¹
êzúÅ‰E©)ú¥%™9ÅúÅÆº)©%©É%™ùy›Ë˜r'ÿdß¼…}+ �h(
ªx31 …äÄäŒT†__ê;¤‹æzš¼7x]°aÕ�™«¼>˜™˜(¤dê•3Øv¶=zÚ –{_NëÎõî¹�ç<•Öš€(H-*Î,.IÍKNeh^öínâ‰Raùé|RÕ+ª§Þ„¨*IÍIM/JÌem”{·�Ýp›Î…ùœf‰ÅU…¡ Š¦8V¤xœ340031QÈÌKI­Ð+)fPˆÌ±ùõùCœ™Î·ÄÊ/â¶[O%§ äúš½Áxœ�VKoã6¾ûWÌÞäÄI¼(ÐC¼	²H¶@�(6n/E‘Ðm‘D�¤œf³þïý†¤,Êö>qlq8üœÑ\œœŒè„>ËÆH+kgIÔ„ÿæ•TMn-)9>_”[“ �([‰�‚ä�2Â)�(UIëDÕœsª'«¦NÒœÎhŽ½îµ‘¤—}¶"äáð‹‘ª�4K‘KºåµO|ö‡ù5½�(„]Ò|†ïþ@i?ºKªÛj!Íl´�.þ�´’µ4*ê³JVøà¹®—jÕ±(kþeŒùÌé³Rmä˜lÛ4Ú8ÄÙþ4z£
iÉ*P‘Èí.,þ
YJðÒ�Ìm”¥uºÂƒ<ÅKñÉC.¼ž
é„‘TŠ/ª|%¹Q¹Ãä3R^ÒÒ/$5F‹ÜÂòRŠamÃù6JxßnJU?‡Å¶ÙR%ÝZúoàå+§Uêo°"/…µÁ†Î�Æ¨§ò{èŠjùB÷¢ù`Á¨^M†ž]gãY²Ç¹òÞönaÅ»EÀxÒŽñ	ƒSªÆ%ªsÆÂ!7�0¢â$þdîäR´¥ó¾�Kmº‹ÕI�1ô—_§ÓqHy�Ol ô6�¬OÝ�Aï©äQöÜ³ÀBä„ÞO§S¦º°z`‘ì®LÔÖòóï¬­éY¾‰âA´+l8ICHøîôÎ½H·4ÃÆKêüé*h|I­Š”˜·ô<î˜Ä•Xs“ø#)º;v^ë—lL§‰2!r;>Tã³d+6‰ K£«D’ÅkBÑ­AE=ÕmY>‘
í‚E)4RÔÚŒ²ŽtP1ô¨µ°có«¹]�Š5ªØ—žÈsiíOû`"Æù^#›0¨8c]jÎ�èØ¢ÕÐ"3§¯Ä›£þŠF~W©Uq§—šø ì��S æ“ø:«‰c×!ßùÎÏñÎïä€ Lr3{§Cþø<dõ"^€ÛµÌŸÑ#×hO¸ûÂKëM´ÃÚàæÇN“S7Þ“'TµÜ›x'WÜôRULèi)J‹hÍÀ^”M*8öìYhÍ�6
‰{Õ:CèÝÕÕN¢¡w^RÉà‡¹À^f›_ïQñxiïÙ¹g8à‚(]©Cç{&‡fß<&.m ÞNé´e×eñ*êÎÌ[cð½Ü72Uçe[€]w!P®ÒŽ�UâaÒ˜ÊO5Ž8û&©¾H¦7ú;#ÂŽ‘b?¢p{½æ¸½ßÇü-†jã­~ ^vÁhÓYÒ+PãxÔ—{ìüZÌBÄ?¾Åûjý7Œb;šH6î[7Îù�®ñ£¾AÖÓÓîÑöHÿˆAAæ~œ»—NÂ	²kÑÈ½�e©Jöv©ÓÑ¥Ÿ"ÃòïXõ“Éßþ½Ã y×#[Ú$D¦ÀGžŽºZšu‘�pëôáB»G§ŸeÝ?ìá>à7À%Ü^~‡ÒTaJŽôû³µnO0~DüË‚Ê<ÌWUÇ[,të|‹q
Ã¢n-j¤mJ-Š°cÑWh�?,Â ïÃ2• maª;ç:ó®LLñBi;ýí»Ö—äˆxœMŽ1KÃP…±µ$yjG)rÑÁ6Ø´ dQªKuqp(8Ê3¹•I^xy	�ÄŸPï(þÿ†¸¸8¸8º‹³ƒ/êà].Üó�sîýÚ³®ËÀ…ãùSçz29�®1öµìG¢ÀdyšJ¥½š'Z	Ì g©P|ªQ‡@&Sq™+~!„fk!àIÜP¿Q	Xˆ@cµ˜eubz¨ËO¹â1ÐÛ‚»lnfL)ÐJƒÓGc•ÚMŸž›û<Êq‡6·¨z{ b$[!½´Î;`F¡ÎUh^-½új[Gô`�hÓöèÓöéÖÓ«s·þÏ¤0–†¿†ŠÍoèqi›ÞÙ™3êîúÃ¡)üÛbÓ± xœÍ–ßnÚ0ÆïyŠsW†¹§ª4µhÕª²i×Nr Çöl§Œ¢I{ˆ=ážd'„œ8íÍ&q�|>ÿòåüƒx4Àf¨Qf(ÓÌåWLWR%ãÍ„$¥ê7h¡Ð`¹\	t¤áÒ:&S:VK`B€A­,wÊp:c2¦µà);-š'NâqIËÙ†0àÖ˜{b\°D 8ux®QB ©Š$XÆ´+�¶Ü­UáèIß
nQÂ,zúlKÃrÜ*³©]¿ÏUV™K
YgŠÔãŒS0x®•qàvaóÛ"Ý {¬ßb?ˆ¨r¸šLâLå”�¸ÔÛ89£—÷Ý]]7Y¸Àf.¡-IiÒþx)¬�s_ÇõeÎòZÛKü„W”ùEUáNš;ê¢c+œH{˜þü,°»±¦úsë¨O1>o´¾b¼pûªÑMî/Œ¸—Ù[¥npÁö�(÷ ”8CÕE‰)iŠ�ËïÖ…Ü`¶ [aèVZ)"[IÎ/ÖB±ì–¹´áÀÝâ ˆ’JAWq¿ý¬>pöæ‹zçØSüŸ|Èâht²ÓšÊz%BÂ¨)3HvuAáããý¤\:øý�ZtÖÁÒ»>mñn@â6ÜgÃw×-7­ñy­Ÿ¤˜˜ž§f4ä*0|oÉ“�˜¹­|ùñ�»Ð¤½Ö^ÞfLÃdÏ`@rØÜ¯wô+YÎ6”Ü¶ç|À´�¼©7CskÁ3��£ØØUKe@0S~£6€ãž‚jçØvŽRÿzU'Ÿ9@c`ÇÇ¿ËÊÓFÊÇƒv]ýýXú­6a6§SÑÁw§ßÂ»]ÙõˆÃ†Õ¦%2ôåÇíülƒ}xœkÙ*´!ŒeóD–û, +’©xœ31 …”¢ÌªªœT†�§¥m^8ÿvÊQ¾ýªl2‹ÊµO›€U¥äg–äe¦3<÷‹^”2�;ÚO±äû›Èsû_O÷ß B8Â¯xœ340031QÈÌKI­Ð+)f0ŒÏgõ{—µãxVpÊV³Ò;ïƒô!jr3Ó‹KRAª8S—ñzZ—ú=qÆç‡B“ç®†ª*NÎHÍM)º¾ùiëÔf–+rÂê÷Mûß­2> 
Ñ+ÃéoxœËÏŸ-"?ÏH¿¸ÚºSî_ëüË[ÖhÕ®ß ƒ§c½xœm�AOÃ0…ïýæ´Qu-pìÄ¡hÜ@ ç)k\j”4UâÁÔjÿ�¬M×Ën¶óåù=“n�eèAZê:…p‚Ê‹Ð¯ŒÕYk[t«·XG4~™†�ŸÚè¡"…ïÂ²K†Ò]ÄÓÌ•5jáá¨4�c(aÃðx‘]¶Ö”è\ŠÍoº)>‹§âãy÷µ}¹I � ´8æp—ø’¤Â“Fsà†™Wm°äy|FO·~_Ç°	aß¶¯ ‹½p8Y †˜„¢%ü×À5û¢‘s&#¤g‡Äc¹÷1Âù–£¨·ðüúYgAå0¿J¬ÄA�·¬£uó—Œµxxœ�UÛnÛ0}÷Wp@ÛEj£¯éÒ‡mP`Ý-6`Õ¦­¶äJrš65°�ØîKFYNì¤YW Ab™<äá!)^VR¨¤6¹B™’%øëGÿÄãÎ`‰Ï¡éL¢(v‹¡YŽæL)©.Pk–ãc{=g
Ó¸6¼ÐqÆì½™ç¨þáà^Æ\¤¸$/><ôà>×»…é§wPò\1Ã¥p:™cÉ"}SDÖrÊ2#!aEe]^tÀK¢ýç×o°ÇÚ0ƒ%
£¡Öçoáý‡K8ûv>½œ:dŠ`u ° ˜„Š™¹¶t]µtp‰IÝ§“È²"º)¤\›‘»¢Ü�ŽæÒŒ@*:V˜ÊZñÊ ™´dlÜØÃe‹I*hª®`úN$„cøH�¸Æ—ÉÓS˜œÂÊˆc8ÛáÇàJ•hXD‘	%¢ZÛwTâL1mT�˜Za\¡ÒtŒ"Á8Uüþ¾Àxƒªe­„g=›�k•<Û±tž»Xà“þk£„‰”§¤š&Ûït0;X‘ao:ÈÆÞôÂldSoiÙîwÙ²Ø¸ü‡Ã°]¤§Ávöx??›AE{ÐýˆP�KcÛØÎ&VUj·®;‹»�À~Ðøhº›ÞcÅE j¤IûceÊ¨µ§U2¶Ý¹²7�Wµˆìß 
OoqI¤¬Äì–qg¹³ ³ãîd�}zÛŽ†®Ý ®²k÷Ðxöë9°ï5 [AÚýÌú	Ì˜¬ñ`á€�KÖ"…€êBSw°ê‰G?%�??lÂY—H¥dB;Ó²2¯ejkqìÞ($eÅÉ:7WÝ’Z/êÀmçˆàÙÓøE#ZÆ%[Žáš°Ì¨»Žˆ«…]ŒµÐ´ƒžêÉ�)õ–ü7è`¿ÚeBúcê·©j’9	`K3†Z\y+öÖmœ+ oSîv®¶Lž*Ý6\Ð"LEê$o¼ÆÞ1|�ãvk�af»ý™Cå²§fÒ3Ï¶ËpOÙ« &×Í%ã]%\6ÃåmSj¼¿³Dté…'xœkåÎ7Áb¯ÎD»�;s˜&k0»O.e¶ Òê“õ™¸'¿`ßÌÍRÅ ;“rºÈxœíVKSÛ0¾çWèæÀ8p£�ú`ÊtÈt
œ=²½q4X’+É¥¡Ãï®d9"q ¥×ž,ïêÛ÷CB¶Ú8æV-°ßìR-À\*Æ]é
šY \Ce °G¶0Z²¬2âá¡�#mdöf"‚˜ßÆ
QåftÒº®èˆ¨ÁÐ±­oxÑ Q�à�üò'$XÇeK?]'ªÙdDáI[•Ú *žœNØ!»XæH2«`!”pB«cb];¼j™Ç+î8ã…îëÚFó
*¶ðH¡Ê¦«„ªÙ:[.™E¯�À€*ÁÎ¼¸SVtå8Ä,ÐNŠfLv�-7Q2s†—w(pÆ¸ª7åRüÊ–;™À/µR+ëz;ÞÅøL3OÈf>¨¢:óá˜f¢ÊŽ[#$7«¯°š£»•G5ZN|Œ»¢å%‚(°Ó,üçÛ)ñ£Ä)íæ]Ó„ë½¦8®q‘š“-¾uëåîÂ­#"ú(\rGØP=Ó¬'ç%Ò	…a`ðŒeª“˜Œ=ŽKºkñ3&LÖžòÈî9—ò~($lº ±ho°�âEúÏ©£¶|ðaåÀ&¦!)/ˆ¶¯I©&oÒ˜¦¾ºM…Hû÷¿p»|¢k‰„Ì³û‚¾­›ž™o×O½Þ™¯ß7o‰Ð‹�¤EQIò’ÛŸ±Wc×©MWÛ÷¯62¯'OÜN÷àÙi÷ãÓO™uPÃÿE{Šíyö4¿ƒU–öÁ? tŒeÈÙ07Ö�àÂ~Â”�0ÀqèØ¼
Ä·à�…Ð qöÝú[›<0òP|ƒñDû¨;åÎâjÀ!E÷J"†;¥ŽÏñÎ°¦YOÍ¹[›2×÷›ó¬k«pO}üxðt­xcŸß-å²Sw–éÅÖ°ì^¸%kÁyê3Û…äÑª(µlQ¨EÃÒÚ±/¾yË6vFîíMGX³#«#¶ùå°]vÌy’8÷Å¹™¯¾dÿo“åýëì'µP�Ì”Àxq¤<•–ÙySkƒE*c¬^Î#3ô$–c¯Ñy3þÿÐ³±íXl2Ç0=É¸¯Rfô=Ö2=EéÙF�B·ÄŽÄö(¸…ã¤CüsÖ÷î»­ì[bb§úçÕû}t@áŸÅ¨ƒ®Ÿ×<‡ûTyò þ;å~b¼Âk/xî'Çþ¼6©%;Â-ù¥lMh†xœë’<.²ÑÝ� FÈ¤xœ340031QH*MÎN-Ñ-J-È/Î,É/ªÔ+)fX>çEA|Ò¦¶¦¾L~·gQ&rÏ4„¨OËÌIÕ-H,B×R$½àÌûKVýºÀòÿ´öÁÌþ�çM�µ ª6h9Ç¿õêâDÁ~ÙŒ7o×%ý›èU�[šS’‰Å‚'=²"ï,3.–Þ1z;™ËõÓ? °…Qq¿Ïxœ½VmO9þž_1'�Ô]î+4¨PÂ:®KÚê„qÖNbáµSÛÛ�æòßoü²yYJ¯ôZ¤hµ±gž™yæ™Ix9UÚÂÌGKiUÂªùçÏ‚í)]¾8lñ`bçS†v'UqÇìÊ4ËÚáCUI¸l3i¹åÌ´‡Þî�ûyðÏÙTn•ž?Šä¼j˜=½²_#.€7½cÖm.)»G³V{gr2ÍL%,h5Æ:ÌØ=+*Ë’t�ÁN»å³{[1=ÏÑª9+”¦/�Õ\Žw¡’wRÍäÑ¡Ç<ÖšÌA�@o�›&R¸é¬p¯oBR-Ø�7dj€xJ,Ã|‚Váéâ•àò.R½„@xjçYËE)”4J2EØ¾Š=é@‚�%ŸÔÍëA²hpzà‚fœ1íñ\’’…÷¶}WhF,£Çö $›Á)~qQ³x~KìÚ>uÕ”~Ñ!ž7–éš¤Ó¨Ã!Á´)`ß+‘b¹’®‘¨¢Z:–<lŒaŒ#ÖNWÐÕÛøˆMA�ºØî\i>æ’]´é0
ÏdÖP©h%’i0Mß62R¢ì ›(\œ‰+H3�ÊÂ” N1OŒà@œ6(˜bÂJúÈî½”AŒ©Ë}0!«ºÍÆÇõÑÓ`ñŠË	ÖeOUñÐ6öhémÛø$f.‹ØÑ$4=öá þÂIá†½ G>vßëN×ÊNÈŒpóä­Àm“Áyïª›÷á¼×¿„È&ø()¼?¾x×½‚ä×…û¾L!ïößå½óÞï¨É]¯¿]X+jÖbìúiê:¥í^7FîÐ[hf+-3’„Ô¯÷o~I�Ù²õúF¸YNæ=—qƒÂõÍ×i„@VB|›WÝ‹îëþÒgùå›Ýþèæ]o�ð‘ìo&�� Ò•	&Çv�NöÓšYWÉÏ¢Zpc›$»³ä¥×7ÏÆæe~ÚÍáäoïòTF&‘£d›§§“Cq©Õc^SÊöÂ��Cq¿|ˆ¤¨Ná\ârš(AÝæb|,÷îØ<@h6bšÉb½*CÙ0dÎ<´Û}WÜ&FY 8#\TX%ÐÊiŠ�“ß…3�VÌñ°O¬‹P[L¶Vo€¡˜‡4üÛ+#²šbò‰«JgA„j›¸¡R VG»Ý`©ÉÈÙŸ�ràj9E5UtuŠºêwƒlVM¸u�z«©P„ÞâOîy’¨ÃõaÃ3\Ô¾!›†oíôµ™OÓ(Wl�á�$©û7°X¦‡ÿ·È•êÏÊÔMÁ³e÷”]´™äYÜÛ'îî#Øò¢a÷¸rMs‡Óo»ïXÊ¿=ËÖ¿Âü-Ï¸Îxœ¥V]oâ:}çWÌÃJDCŸÙÝvûAu‘Zºè½ZUUjIÌÚN)eùïwlçÃÐ¤Õî•�HfÎŒÇã9ÇañŠ[�?#ØÁLðŽBÁ^_#zÌE|ô¹ÁrH8-ž×Í@]–„ô¥„©ÍŠ"öšEô;ªCºÎ_ÜpûyLXÒ¥‰bŠQÙ�!òx…Ð7	y’]qÉ›Út:ÎÉu,ŠLÛè¶ÛpÉã• R2ž ‰æ\0µˆíR1QÁ‚%sP
6!˜ú6´»ƒqÂÏ‹è¯p4e«#øIEÙJc2£Ñž<S¬
²†g¥Ç×$�§Tt ¤3’FÊ¬ÌáÄ,†Q;À‡ƒ+4M`Òd™ðuÒêeáðõ,È"àìNZ¶„´á–¬ò¥C¢È”H
‚¯¡)²¤~ ß—t#[¶¦í·ˆ%Ëâw:Å~+ey1Y�øzÂ‹SÆ21wF4à"ü"•ÀMuòšO±èz
Ím€…½b›:Øca«ƒv}†ôi“~öYDB–ú|mØAíðmsL&E#:$¾v2æ6¿2µqŸ°ŸiM\j|oÂ%Î™ÓËQƒÃÝe>?@§Ÿm5³ÝâXá_mHlýE{¥Eå!íþT;Šä4×��×?ˆ	*†¼‡v0Çë9n¿¤¶ Š-øeY¡SEæ¶‘úi¿k� DÑð\ádÓ5\á‹]ÍÚ}¢J<V¹sfü*S­)	–4”�ˆÆ8®Di–óY>ÕJ²Óólrè“Å…¤æ� Ib
ã¿o �X BéÕµ·5g	‰àIŠ NKµ‘ž’O:WÌÃ4¢8÷RsZSOç™q�Š_Qa*“–KôÅ¨])ó­TH^±+Y©ˆšK¦€l¿±d�U«+T¡=–HjØºÀ½m’ ¬µiöÒsåûá©û—Iúå™³ðÔ¬fOM+:J÷ÚD·2?f^¦ðñèR<ÓÌúÞyÇýÑÃÉ�a¼o¢¡Y!À™ï8&‡á®ù�Ôõ¾‚¸.ä€šU®’‚{Þ‚=‡1{Ôr�•ršÅkþ9¿¹ï�÷úòi«ÛàY�Üõziº_Uæ/erWáÝ×Æ�¹VáöDï@¡r•˜\¦ê8JV©®*œîî®lïSAMÑÚµkü¡"&ÕÅ&kã­\_3¿×2!+iUr-'—åî?ôÕZG%M¤qÿ¦9wÞ° ’6õ¤ù�2µ„y�.U|x‡*E¤.®ŠÌt=º»ut%³þûWÔÏÛƒ�ý´Ý#Q†º]õGpñÃm[6(­–¾²¯ýXó½óðø¹aU©H²£ôð³©yøéd&ï7g.àiR;t{Îú©³ûúwyw?œ4Û-8Û5›þQ»ÿ §NKË¯SôÃÉã™íKÖÐ]ã?ËÉ&vå‡?xœ{!ùDr‚ÖÄÀ…•Õ7~²cÞlÂ²… h4¢¸ÃxœÍXmSÛHþî_Ñ[E‰Ù[ùFÖd!8»Ô’ÃPWW@ÙciŒç,KF3Â8Dÿýºg4z±e.äö²W•"žé—é·é~Fb¾ˆOÀ¢ ü¾ò>„&I<‡Ý _¾„|?Næ»o[Âòã6LDÈ%0©ü)Ÿ³BÌó:¹dGD,eÕjÁQÁ”iÃ9_Ò�ª˜ùÄs&¢�”P‚Ë±®£pJÒ|K¡âdÕÆ­Á³çÇIÐnmSLjŒÖý¤ÇZ�½=°	WàÇÑG6`�°%<°0å b\Fé|Ì“6|ÂÒP‰èŽö»ìuZ(%ÑÂø\ó@-x i4‹âeääâÐ;Ãd8àÝ;èºÆ„ìA_úÝ”îÂÇÓ?ú°aà³$ Êæ+žHpF¯FméÏÍÈƒš2E*¤’'û2],BÁX$|"ÁgŒQ³qY2ë#ø#rI)âÈk•þpmÌG1ãä‘< ©tÛµ?Èéa(Cæs§sýjxssÛ¹kÃîÍÍÎßv+n�±…�hÀ3É!‰—¨4b3>ôi=ã+éêPGðôk(¢Y-­é1‰]!+àŠÜˆ#th9å‘Ö?øûGxFÁØã�ÜOwÜ$\¥I$IIíS-¸'1‘ ¹’U·çlq2¾ˆ——ñà�9›ÜGsÀ,1î·mv1"US).UªÈMOtcr)Ü_¤ãPø§9Õ¬†ëLŠ‡ü.asÒkYíÞ�ŠøY‰«HÜ§[äRMÛ—xØ�uDr¶šÉs/§±
ºþ3,üo«ÈÜÐ­YpÎæÜ¦í‰pY7e.æüsd˜h5Ô×¿n¯øÂ�WŠËõƒq8&Bq`©K¸©+]„1x²á…%TÍÿ�ÉiEÛ—¥6øŠW=‰—%þT<ðË†Dæ¤áö„nê¬ÇIPÓ¶rèõzZ�æxW]¬¹º]‹[±âC-V¦!�›ô#•¬6e9mÿGá³ZIXÙ†Òh\Q(›+jå»"U¯´qêÏ¸²)6«Í[öæ¾2òÍ›^ã%<f(¡¦ÚU›ÝRÂ…¯_a×Ñ.)ò—ŠÛÖ#‡�Y“Ô8ŽCÎ"}Åh†-X¢®t…[«‹í¡©üm5I,ïã4ReLµ”O{E,É¼RŠè`""|C¬KQc?á�9R8QùNp¡Ùòý!S•è˜›4
äûëYej�ä0H‡Çå|ò9V+S8(!žØ1µH2&ZžòÂ¨¶�g¾Xã4 °ý&Âà(Ú�q'"œ÷#™ø�`¬1Šô”>#U5Â>Tš8�ƒ”àØrJxŠF8MFÌŸòlç¥ˆ4LpH›1‡Óž •2)­“uJ_åºw‚é Úõô«ˆ¦è‹:‰ýuNìŽQp¼¢n™iî]I¹Š|()ÎT7ÓY|Fä&$ÿE#DSn‡úH 3�ó¡Ý¶dB!&ÍËÆ“m_9®]tJ”Zì#RH¸Ãï+4Ïvõ6�9nÁŠ¹PÎÏXºÈ5†È-¸îÞRu“�DÍZ/Ëç4‡ÆR�*üÅ!²†´ôòCÂdqLs˜,Õi†>qÈšŒj7¢´Êc=�Ž¢ Ps8«Ž6 iåˆ*fX¹õCœ¯€:c¹ ¨‡ÞÚÝ.<pÛ[™µ;mãÕ3lÅdÅW0%¯°?ÿ·y4Cn=wfW[z`×k	ùÆLˆ_Žª)öú±*5¥Ø3Na8[¼üé…î…B*l}ú½ºîd•ö\iš×n}eƒ'›8�{Ä¼KûBÏ+\!ì8š4ËT‚ûñø_X½²þ¼¾}›[Â‹góõ-d6XF¬0	†¯@Bò>úûï/1\.>�åŸ\þñ{ÿ¢%¨ìÁÎ“Avp�¦¸wt~À×3uª·s¬©_û;O£�§òqïÜìÕ( ìÏ·cô¶¥µ‹	¾û‹øºyH �¼ó¤WYÕöC<©”ËF¦è2£´QøÓÅIÿŽÿYzvz‰Zò”Ãkø9³VåWƒ-/ìípìõ(¾#T5uâº„áòï ôsË,ˆÒù¢5Þíì–10V˜„8YA åí]³¬[Ê‹#ñÑ'àäÆKB®…ŸåÑV£nƒ(ßôBy»Æ›ðÁð7ZÒ“éØ0çuå…<ºSSw]V"üœžÒwB:¬Täéo‡Ÿ&¦ÒUSŠö [µÝj�Ä!¾ÖMŸ€^…l[&½zXim·]5	K£r¸9¾¦û§ž½ºu3 ÌžÇ‚ &TÓ˜¿3àtM«Zò´"êBˆ¼ùAŠ^0nE›Õ•ÙŒç­¸TYÔ‰Õ,Ãqr:¿•iVv®£$a+3�¯\OâëÁ)ø³Ú=\;¸8¶(¸Íƒ=ô¯ÁÇâ€Òœë[³—½p°Èx¢Ìì^+%ÅÙ˜'u˜Siúù+zËPÝÚ6¨5]}>9ºìç­tÐ¿¬·I•¤ü›l~7‰¬Íà¢yuq~zþˆ`ôòþTIŸq&¿¹Øx»ßðc¦ü�çßù¹y>Ó£ÙNÐµék&vu¨–±ì‡*­�¢…­Ï|g®7ÜxV !5Òk(‹6	WŽÚA<síy¯_ÛKP	bN{)Æ£OŠo@½*q£b·Fæ%šÏ|}’³§ËÇ|¸ùs0ÀU–ö³�˜ïL´MwÝïx}JSÉmÑn`y.ætÀõ­�ynòÿÝc§òŠ¡vó-�˜®nÖú7«i |í�#xœÛañÝl‚ñÄ|/yO·ÌœÔ Ô‚üâÌ’ü¢J…`cˆHr~QŠÂÄ»ul6ó2>aaW°WÈ+ÍÉ™¬Ç.9Y–íÖäßl
0±Ízì‘ŒpÎ,C&)½âÔœÔäM½´¢ü\�4 ™ÁÉ©¹‰š“xE'ósLNà›üŽ—ms÷B¦ÉÂâZ›£ø¶0jëeæ§• ëÐ+KÌ)M-iê¥–”åeæ¥kL^À+¾9Uà"kaijQåäz)�Í%ÂÅŒ›?‹ÌfVH*MÎN-ñL±R(.)*×QÈN­,†ñ¢c7ï–xÃÄ’˜—¢1y¿´Òä×ÒòŒš“ee¤ÜÓbÑ¿ÆxœÕXmoÛ6þî_q
Øê;Y¿9uÚdq± q’9Î†¡(lÚb"å’Ô×õß‘”¨ËF’n$¶t<Þû=G†E‹X(X�üÂnEA3ìë×�îÅ"j6XÆÂ	�Yà¸ìk‘!˜ºÅv»“Jé0ÐÇœM-yI¨Ø‚u³cø9á
ÿŠrìoG„ñåŠ)Fe'ÊØ7$Ÿ9ICºˆ%S±Xn•§7„í	·å6:¯_7à5ÈBA  ŠL‰¤ âP1RWïCÆï«þ¬Á* cð²�b:�YÌ¥‚ˆ,†ñÃ(®ì€´ ¢C:‹EðV*ÁøÜ‡„ßóø�ù¯[U½#h­p[b^Ï‚.ˆ¶}cªˆ„T
²L“Ù=U–Å>o°È7éR¯Ë7ã{º,/2Ž‘'ŠÇªœ>À)¾´DÛÑÇDå;<#O•H#Ð<mx‚:K‚¦Õðx†|ûÍ¦ßX{ybNÓB�t' ¬ƒ�Fq¢XÌ!¾Í’SWk�#„†tŽj¥Î§º£ IDáú÷sø’P�µ¦mÑôX°9ã$„‰³N0ÍË¦­äD‹Šâ 	)¦LjÃu½h1·± †/¨0–I@ë´Ä„ïé¢ôÞ‰“6¶	” <(ÒõR±C*mIÑGSú³�H™…£®þ]hd}è2A4eõžñ;t]�Æ³ZööLPŒØÚ°wð“È%Ÿ�¥ê*.V[žpW`ER©
ò¬õ+ìX&iÚGÆF ÛDY½c×Xj½ùC3�ÂbQ›>ÒY’Ùá&g×ýáÎ.F—°oh¹ÎñÁuˆ¶|(Ö§Ÿßô¯¡õj•Ù²öáÕ*óÛ¼wÍSÁËµ7±ž§ö
ªÁ�Gš¸n</·ˆ±'KTZIHFoåð�vgÝ*¢Øv+G[P‰\¡v÷ºÞÿuO"Q~
ðax9¨ÉÇŸ¿õ‡ý\ê/Ž/N³ý=h2>^ˆxŽ–Êf_Own
Ÿúq°~úlÁn…Œ§í�ò¹ºƒ^¯û^–!—ÃbTP¯åÿ´ÿù§R2WiXž ËåfÙ
Í…î©‡çR/=¢�Ü�0½Ñ¤…5qý’R�Å‰j°#¥ï*Õ¿±Ï³ÂÜUˆ7W§Ç£~MA]÷G…’ÉTÍÝ•Vl×çºK¦ÏU_ñ:jôý§n2.©=¹U}ÍW¬ázc.#¦Þ–N|>¶0Zˆ3ÞN,áæQe<5,õ(o¦g	ã™'Ñ”
”>�oYHÍr™’p†ç‹eè3™Óq„`£¿�}¥8ðáUdî¥öèŸ|T˜ÃBi^Šþ¸0vä´LûT^ä-ÒoŒMÅÕÔ´�µ¬´‚žhûr’¶³<�ž›÷�I“=YM»[xÒèÑœŸ>ÇÌÑøA‰õÓcŽÆÒBŽ+#ÌÛîv9<íáä¯¢¹/]éØI§Þ0Z-á¹Ë�Ap3\ìTI#ó¼•×¨f,˜¼)±\ºš½šƒMñue½¹Óåªf8V*ßŽ³j>7�u}ÑëÎË<çnFêÔk‘ú{S¹Ã¬ÒØÍecè®½÷ÛÉòÄêZ.[Ûu�È£=ê¡ÇÅPà‰b@Ä½ÎoéæUa[PI&¡r|tg)9	Ÿi÷»0�ã�nW8}T·¨2!)Ã@ÈpT`ãˆºkGŒ·ìylå~è«áÁþþ¾žož¼Ãg!‰sÝ5ô»ï;Ñ–/˜Ø Š4�%ùA°W¼Wt»I‚´í§_»dOˆG¸Ó9·.)qèSçG‰óülp6BA6?ÃÁzâÖ»ÿß8ý{Ñx>foÜd¥»K´eÈf´…Ål4xÍ·üÏÈÛqé�îì^lÅòýæÈêÈ8Ë­é¼®Ýƒ’êiÕÚ;ðÞµÍõD÷¢neè:ðp7…uã&ø¬âŠ(xœ{¯qBc‚·jIeAªBµ‚oiNIfAbQI ë ¸¡9ù‰)§·MtQŸ,ËX8y"£ÔæµŒË™’J“³SK<S¬ŠKŠ2óÒuŠ�½S+áÜÉ�,"›g²\f�\È¥;¹›Óxò{±Í¯¹8'¿›l)b ©~0©xœ340031QHÊ/Ñ-ÈÏÏÑ+)fhúç¶7¦vß:nµ�ó=o¦M{üÒ¢,9£4/;5E·¸$¿(1=¤:äŸXô­µ�;Šs~9·å?Ë]¨ê’Ê‚Ôb�¡M3¤Ó”.ÝmÎ(Üï}|ê¶" e×1-µ§xœµZënÛÈþï§˜nƒiÓ”’Ý ­lyáM´©ÑØqm-�öˆI¬)Rå�–U¯€þê}Â}’žsæÂ!)'Ùî6ÈyæðÌœÛw.d²\åEÉ.ÿT‰J°Y‘/Youøwü«w´—¨ÝG6©˜|Æ¶š¦Ô5Q¹Y	ö¸ÇØy±æE|%d•–,œé§—×¢¸O"�kfé‡$gÙ,öç0ìÃÿ8_ò$ë#gÙ×‡-¥bàÞ,Ê³Y2gÍ§Ev_Ó¤ù|.ŠÆ¾\ðBÄ}µÓO²X<¸<gúR¯x´.ë:ô Î´JÒøZdñ%ß¤9�QDñP<*ß¯pAÄ().“ž�ô\”‹œ(¥ýëœ¯,�QÑ¹�’Ï…Ñ§½N©$Üct K&S!VlÄ¼¥²¬ZNEáÙ%'RßçI|ÂF',k³èy…�yz/|Ü�¢œ$K‘W¥YØRú¾= ‘“‚g2Y9.Š¼À³þ²*»ËòuçMó<<C†¨}µ‰‰–%°À³Hä3¦¸|§6Â¥’“ÙuY$Ù\ñ†ã—Òœ~ÉËR™ž`—�;ª‹÷õ'Ê3gA Aì. Ë¬³JŠ¸µz0+ãWï..®Æ?¼¿¿n­]�'verv>~ýî½]xùð`^Ô?¿©~k~~Ïcö†—bÍ7fI‡	{Ÿñ{ž¤|š
³¥)Ù¤)¯Ì£;Q²Ïæ¬ZµV£4w„œ‰2Z°ð­×2Q®óâNÙ¡½ØRíéåY‹c!@„®VÖER
6¾<»ÓÒG4d!ÊªÈº–e¾w\)G,‹°ÌßækQ¼âà¥~˜dQZÅBz«æ:çÖúçùé_n&W§×gã‹ÉÍÕxru6¾ùæÈ! 3�¯œÍç³;¿¿¹:=¿oÐ¤@zsN4/7ƒî%\wÆÁ8ßçå8+‹y9!‚	:”³ÌïD6DIÀ‘�ˆByýÐB)®Î5ú’~À¾o“%h.~Ÿ•Ijy²~Ÿà*Y^Ñaª¨ö¶{{â�p(J¹”x¯KAà”Š%¨Xvð—®¼*’{àÃÐzy–n pKÀ#Ö‡�\`3}E•yáùô°�HÃð´(ø&D`ò_®Eé)`�í„¨|ŠdPÍ"‘¸Š�©çÃ%_yý”*É<u’Ön ÿPdvÇhO5šU¬|C¤•L¸GŠö(kDUQˆ,Úu
	M–¼ª·ÙÖ2êg ¶¶J¶-*«¿¿Ï.ÈfŽäLÀá!�¬Ð,û} ™CTÊäàÀÆ¾Z«&<ŒŠÂTdórá2Gº}Pp*¢’Ø['å‚þ˜‰µ »¬ ¡€ã±’Ë;	Á;ëµ\‡8"Ó³ò.Yai£ïøˆ¤Ó@/Òœ¡9„ünˆf>V÷?ñk×a?‚PiªEJfÌkËÃF£øF\¤&Gc,ÍLA‚.·‘&s©.µ˜#Ù:É’r£ÙÌ@*O¹(ªa¯`XÝÌ•'\péI¨\ÐG¿(“L¦y ÷Û~ÀNØkX
!z�Çôsê"+{YäANšµgÃ=ÑÐ»"×R°–&4ùQc[jÖ¶Êi÷\—Cª®›�DT�p[Œ0Ä¨j’<cðŸ3¹Q2K"R´@‹)ÿa.øJjÏT)¤ëX\n²
%:,~JŒ�''ÝXÖŽ BM=d^Pƒ&ê†˜êgr‚�;åÐä¤rzl	ïƒV”:Lû„:. K5Æ©¨Ž3qÑày7pþD^ÃÚÂ)±ìc]U‘|R]äÝ6t®uÈø2{öøD¶Ú.å­oo†ÿž ¬I,µþñÑï:‚®éÁÞX³h¨›pCÁguí%rÖr@�2»â3úŠ
b $&«)D„‘a•£,U½ŒŠîI…Þ!;›°SŒ”Ù$ûöÅï!g ¼±%/îà1âÏbzNŸ	O‰�è¹°ª8eP§3[h‘yñ4˜Cè�Bƒg 
m	!0#ˆçü­önåÕ3¥²I~­t¡,Žê{…Ú³5s`×/øRï­W'På»«Ž7:-ãÐŽP	¨Ýžrädm¬¹–+E#b«JÚÍ=¿Z[«�ªÇ�| ±Š’wú#):­|Å~úç@ÝhÑn¹Å9+FÉ5hã‰šŠÎ±wÙÙÍƒƒkÔ)Ð¯“W[nß"4âìoàVA¶‹¼é’„ i 2Þ½ák¹Z³§XNÈ½}6Á<$Ž�{$z
<²<;lð¦Ã¡ìXùµªáéJk!ð5£ÞÅá¦úÛ|$ózÈ·%�xXðJÖ>�äeÓ±{þ‘Ã�ÓADë½„Š×9/€š4ì€cûPOÍgÚ’Gå…× igZ“£•Ð…:±‚£ãP³€2³]•VN¿çð‡e86…è‡êNmƒ‡Ðí—Iæ…aXW)T�N	•§Ýlÿã�¶Üð)Ôx¡si9ótÏ>tjPãË†Ö:†ëHÐI`°^ÀÍ©[‡…k*µín~Ö&m‹lM¨tåqìTM6¤Lkä*§  å¨ËÕ5òP¨©ra+$Î²Î‘‹6�—“X�ù‰•ó Å“¿¢gRœ¿�G»»F—ÈE—]²¹"`ëœõÈd^X£XØègFHo0Ÿm�v2«ÇBD5jÎ‰>˜äð=²‡{¯ó¨Â–¯·›ßÊ^¬=®ò¯À^ª¶d“I9¿jZ‘Â¦[Âí^�x-–Tâ�E¯{äÖñ†ÖeL½1böÛÇA6Ù^‰(/âc�H�	ÜI[[øÏtbš×‡¦->šîV×9¯ Ì9‹×ü�Q·ße¿m‹ü”²+gvˆ´îDÑ3öl±Ð9PËÒUcéaãaã¸ïBäw“Ää]½®‘Ü‡ßg	Dß,*Úü'­Dí*ÈÅef‚ðpû±VÌlYÄq°ÕH>§Duý§’]¶L§X¡ŸÓmFö H°àõt©"Þûk|à÷“®ñ±`h±ê
BÈå/åA¬}©èiæˆrQäk,ÅrªÒ1Õ- 
Ž“ÙªŠ¬4•õ¡$ÎEcÌi+^Hq–•íË}xþ1€Ä¿ÃÓw¶Ë£fê3üUíÐå¡“a’ÍrïœýöÙ£M8ÛfM€Â={Ô·ò6Ø	ãº®hó[wk»C<¥_²1Mè"^Í Ë�Vx�¨ÚÞ»Ëän^zb˜ùõ×�)¼vÈ]¢kaÝå³™S•@½ƒª�¿`ûûNªC+Þ´¸†¨$Ù¡¶[{)Pà\Ó¶­Û,1ïÙ£#î{¾í?{Ü)óÖ¿Ýe§ÇÚŠLÇ¼‰µÀ¹µ¶í%·X²Ïî ëÖ©O˜³íXÐ×õFìªÈW|¾Û„±½'˜*†Îp`lj÷V]¤ÛÛŽîoq¶.ä¸áÕ£D£;WÕ–kp¦2³Ö¸FÅ¡.éÂ6„7¢EçEjÊÏ¶íbŽ¶‹í_Å%üþ,rkÌV²'™Æ‰išGwÐä‹âÌÅ
g’ÏD¹‹«þ†î¡F¹Mnè’}åÞìâ+¶†bmZ•ªq„}ô¡L½>Ä‚™°É�jh÷$4˜iŠÑ ¸_¢¡Ây�'£úB4ß“€¾)‡

?|ª¥P?T\©÷>0­"(Cyµì~¯�à»±»� n}¶Ót[Í6Œñ²î-X*îEJÑk(M�ÜÜIóîÎÈä—ây‡ãÁÁÏ‘ê¢?s(–¡Xk£ �0 FþÔØ¯	n«µÃ(Ÿ0I'Çº6r¡Ó6ŸŽHj¿Æ(*kX|êæÊëhd£0²×|có]yÉS&  ¢VÂ}GÔžWÙ×8cCí¼1úÒ·:�~?ùæÉ^PuÆpšùXÂkîzâíŒ·[ŸW˜&
=Ü´G¦¾ÿé_ÿV¿)Ì—|Es Ï–4*2ã•4™Ñktõ:E~øhÈÔ§ØâÌ,Oâö;ƒ"¯²œ0YaØà”|Ã®¿aoÆÖgŸ¾6üwÀ?<iŠ3ñ�M&oÙóÅð¤/0bæ­ÀÉ4Ýà%Sá;¬ó*�-3|³)+õÊBÓ^©Ñ&Jl�¶^$Ê%‹sØÀ7­ ºáëÞÇp`4tªt©?
/Ü*n ò›g�M3mo�ÚÏ`/ÞøØ$œã›SÍÎÇLÐ¶¤~¦X!Ø(F5\hoSËGnä|j2üEoÌìD¥¨‡)Ågæ(Ew„ÒŸ<92éÜH¨ƒ  m„×ƒè28ðÓŒ›Ó€wÐ/t¢$`=8ö†ÞîôNv3Eºa×*£iêg¼C{•Ð®!fš	l	�uSÒ¨ßÒÛ5jœ!.Óê†$ÏúyTŠò¢^ðe«¡¶�Ð>˜‚cGn%Ö¯¶ùÓÕCÓY¥ã¬�ÝkXC{£ÙsÔ—4ìŸ¨ ÿ/�<ÆS·®ÀòÏ~�Ò[9À£�ž*ýÜ”� O1éAö,À¤Êù¶Çw´Û¸)˜øÎ½â¶uÝâWê¿°WÔçAÃæìåŽ^ñ}b§G„hÆ¶JÑ	 m…TÿBJp~áö;†!6�ÅÿÒ;>¶¦^?§{l‰ú%]ã®ŽÑ5aË¾fËT<ŽF;¥~Eb5§ªzžÑw<Nu¦…«iGxØÍnˆØ-ß|éb>âiaªTq>E2‰„>ER¯õ‡Iø’á¿�]å·Óxœ­ZýnÛFÿßO±�ÔJr{ÁJ�/'½W·¹Ø¹þQõŠ\I[S$»KÚU]÷÷„÷$73ûÉ%e×‡	"’3³³óùÛ!å¶®TÃîX¦oÄÁó‹~nÙž­Tµe£²ÊÅb¥GÏŽ¤£]ÿ*ë‹]™ui~-ä2¦*yYÉ<ÐÐe hvµ ª¯d!×ôÿÛ²‘ÍÎsÌçÇð7¯¶\–ÇŸI¡�W@Ø“rZmk%´–UùªXWJ6›í”}#nQì{„�Õ@×“|æ$|u¥eS©
"S$h¦<ý°ÌGÈ»OÔ¥(ÄZñí…P72÷Éj,éLÚØYYU®ä:áåM‡†Bäï¢ùvù“ÈÀ º®J-¦Fs-uQµª§‰,¡V<cë§ÇÏ4…ZoSx¹F›k0’n‹Ôy]Q
‰»[ÙÖm#Þq½Iõ†+‘·�,|úéû”�nÚòšxm1î‰Ée%…š#éÕL€'WL®(:\·Ë¼®)ò)»*Û¢¸b«J±¶tT"G)ÇGâ—°gZ}(ˆÙÉ`l½ÏEÃsÞpÆ—UÛ0Î´,×�Rm]T<9ËhccŒÉ	ÓDpO–ÌÅMGoY£�È?’J »#Æ`MöÅlÉa²²Ý.…b· Èl6‚¡YQ"#‚oèùÂÒ=³"ÜÚDü#Ô4��’â´9Rù$ÈÅ-fÍY¾€M( ê	kKùs+¼Ì±nøkK¦*­Ù²SW×¢Ô“¾Ô�ÄÚ—}fçkÁÎÞÄ[D#âÝlÃ­†öŽ%G9Ý«äZ–¼ 7(1‹Â<"è�å®ÚJƒ[¯ñ2sa¼7†jÐ<,…ˆ/É:Œt¹àŒ]#*ˆÀÅá˜uK¼½äk6¾x÷jöç¿ü•m '¬Z‘	+g�´
ò:8`ï#ÜÖ à41�ÂMœEql$éßÎVà�wN.Ð¬…ÔÑ*6Âuˆe½ègÅ÷?xßDÛt»D»¢±I%¶�:bâ¬P½h®°
Ñø´Á§.Æ:+¡ö¡º|+M©…¡YíFrc-ØcÈÚÓö£¤œñÞófjÑv�%˜CC£e °r©¯]¶mk¤M÷û^‰•ü…´\‹R(h0¶´�Œ¶¹ ×ÿn®T–Ï2Ïl�\½ì>;kÊ¯õW¨][¹—ð$eùê€c¦,1ÐYÁ!MV2ãæÜXÌ×sö$¯²vQñdÊžÜ@Ÿ©žL‚ÒC‡AÇÂdu·Q«·»ê—¢‹§lÙf×¢A^íJ./4xRñìÚôˆ‹§I!0œgù§û�žFÂMgg×b÷héúé?Ä®/:÷¿x!¡×AM/s¦y	ØMZ£[ÈÉÈô’’Á<ð©ÃfìØr¡¥ò=²ãWêô/¡)µªÔD|cWÏ“EØËf£ª[ÍîÞ*U©=;³Ù	Õ¬¬¨9ó•`æä� üZPq|åê×@rê”…ö?îjîü9q?ØÉsÊF°öøO¦ßÎ%I93âöÛo©5¾<aŸOH
d*n†•â–ÑvÆ£³’v>dãÑ=¿‡ÆP‰\ð] *ßÖ÷¼(v¾Ÿ0[‡¨¹”rN‹»O_ËºÆ¨Y-ð©qÞ• «€Nt+ˆ1ãCb¤£€-,¶j6¼Œ ý\–^Ý«˜Óƒ5Øµ�RpµvBâ.6jVŽâ·özÙ®V@::g†ûn#@ ê IXP”aò!¦Dw�—rÛÆîÁÂÌ,½MÌ×mDZ.#×ˆ|bëæn6€„ÖCX³[¾[
ç1ƒ£!l7àï{M;ŸF@bp¬*/ã›ÉŽ\|O� ÔïŒ2NÖ³ÇB8Äiâ-ïRaŽâ¿åâðËC¹±±îu"	Ó*QyÛ›4¡Í¢Å0Ô1žOüQÚ$)¥jh	bµžŸôTý#JyíÊ‡¸Gø|„üQjŸó`¯ö ÂdhM”2Ôtzê@Î¯£Çp6cà'áŽÊ§¬ÁuS&x¶	ØOYLÌ‰F $_�\G�ÛÑBiñÄÖ”ðlÈÐžÈ©ÚµëF8J©U…]“ÞŸÎÃqÓi}Vb�ÓÐ>jQÂÙ0“ >¡*ô®j3àcÿý÷X…b)(aL�ãåÜ½,$dI2ØO;ºCx[‰™““ÿžID›¸uB$GàÃ¡¸ë‹á0„|yYu'3] H#›
3§qV,3¬¹‰�dd2(ˆÎ•	k’ÇŒ¹_(Æ»ýÕû3ã)žáîµrìRÎ8eLéR+yƒ(B	žWe±K6½Hý2=ÌÕÝåbÈq¸“m-zþD>HpJRï%sòp@²Äï_Úæk
vÜŽ}ä“A8èÓžÑ”„Ð:Èh-ò5—|¾-h“‡tßH‚rµäÀVò‚‚sËUÓvtµõ!ÖÇ:[Òábæö�cd™-�ý°ŸÁ“f3�Z�Y8nŸZ4�kM®ß¥¨†ÝYIhyd<Ð…`âçž†Áœ†Ü®(¹ô˜v°pXjàÔóÐI†±aÀgŸ¥´{û@¥jmô=¸ ó—ÇáçVeÓ |x’âR³Íy‚,ŸEÐlN	I¾nËù©ÚÕMõŽn�GzÃá´<ê°>dƒsÒfÎÓÉ1~ž…©“¹É7ÓF¸�Îºínœ· ³�\o¾"uÎÕõ"µÇ~bc	â·zÖØ¬A‘}Ï¬åtÇ¬ €A1 ÓÍ�1rMØÃí@Î“rlBU³�PÜþöJ)¾³63@¡‡^NNËã°A–­°zÆÓ8öÙ	ûÂÉHLùYd8JãÐy[ãÈÃ•Îö,ˆÆÁ âØJ°a?õ—6Â,í¡ûIÀ;2o#£˜-Hvðsb]†cÆyR‡ç–ô²²]2è·Œ`ìê“;£J?�÷toöÉ]0úþ*pŽDEêFnÒóºÕ›ñ�gb‚�t(ÚÙâ¼ût˜+=ó:š ¡?êìr§Ï#ÎP•ÒëŠïL-Ía|ˆt(ÈÂS3MŒÞØ�u{"û£„ŽÝàHÃ¬Îæ@.í6ãÑFü2ò»™dî˜ýènñ�!ûÌ@X%²Jå:î›�KKY:ìm[Œx¸Oí'H��ìF0ŒŠÛ²j0ÝnRè{Úø¥k¢|£„6mîi‹F¶ Ñ#È™öaŠ–ûÚp:·Œz`xáØm}V»N‘¸§ß»ˆ	­>i&QD¥MÿPýp<)0/é|‚žv{&�,y—[×W{ä=äà¸“#u¿Úúfhk­Tš^‚	�Ýh‡úûÏm§¸§
-²7x²
ûxQUÞfp]V&?GÝ¶ ¢]2;Cš7ÎãT½Bt�ÛEÿs¼_�<ŸæiÝu9PsTÜA®´ÚÚZzºá2Y�tî&¤qYöK*Éa.nÑ]»çanž»k¿fëè¡�²Ã:±œxXm(Â�~¶K¹Ž€«l#oÄeâœv$$/ëzD_yÛ<„²¡vŸž{³-CóðÔÖ�Ü5{ñ¢CG#rGD=
³»×8f/AÞÈe<Ö�ú�À—R"8.õÞ†î-1¯L�Hí‚ON«¶l�¬žQWîUÓ)`¯Ñ—„æ;"¶¼Û÷Ó'Ï™Ï¼•Ï°BÌe+â^+ãïy¥9Z?6={hfÖ÷%e”T†.Í¦Bòâ¢»Õ=âÇ}ðÉ€'"ÃŸÎ»Î½IñìÎ;æ²ÔB5cï}[¥-ÂÂÛ}Tôº•‚"÷Ö~B¨GÓç$Ú~$à§9æ…¦9R�D@äŸ­P8•sÇø™Pè ¥’ÙÆÍ4ˆ"€¢Îk¥¼º-©	}üð5@R–ñ¢@ÌâækÑ´°ªöøˆš��Gñ@ívSia_;ëMÕ‚I–82ÒUqC#÷JzU²*yum_¾uÌ†§�}ŒgRP;…QK´¾í¶æ3ó	�&'.¢o³"ü”~ïóý]e¶Ñë´I° æ¯w&'Ç6Ó;C»�Eïû"?ðñqX�A
¤'ð•õÊý‡ÀÈ}ã�âáO—V¹ô¼öˆúˆ?ªbÁ®6MSëÅñ1¯¥ç˜WjMŸ+ÿäÎmaW?Ò'-ûãè.}ÿ‚ã±øÈù`Uyd]ù¿+ËïªßÃg3kì~ñ8%ÄÍÞ]^¾gî»4|Oç†>:)#í>Çˆ³ó¢­éK9#‡>,ƒÅ¡ðëÆ”TUâW+öSŽ\ÐÓ•=—˜~nu©ÃäV…¢Šh-¶Kª&=¤Ía3ÛGUxMšoI`À/³ðzÎN
Ÿ½ÞÎNÝ�ÌTœ©ÙäÔÎŒi§ììM¯Ò›Ú
ãK	I´œ´Kœq£Ì´ÆXÝ)2Nhwfš–Û=Pæ¢÷e k-?w¿ÝŠg�n•«Óá*`ë$abøã¦ne¬ù6\:Å¸Éýð·–¾ÈØ-Íè"´|ŠÍp÷Ð»éñ�CÔøR4f„Êê´Îªd9ÍÂ ¤àº9¯rüN2_øt�0{Ê_á>Ýð2P¥ßÐû;ÿçÅ CD° ³"×xˆ²?ŽðIÅ%&÷åo£J_Ýl°YãR†x|‹ŽÚýf�
°é,šxœM�ÍkAÆÉ‡E#-"jTòfW²»D½¤´¨ñƒ DiíE=t²;ig?˜™MYC¬9y±¼Gï^ÅäÐ?Á›½xü'<(ˆ»›´f`çã™gï3ïþÓ÷�ÞÝØ{FÐw:tçãx�9³÷w)×#6nfNúþ+sLE¼¹P,ÖÀ%Q—¶|7TÊÖvè½ÀÙÇí™‚Ÿ³Í³Ì|¡` D®“M]ëì%Mœ'û„3‡(æ{Æd?7ÊNn-ðüäî©¯KÕ¤c.53 »œÙm?ž;�·Ëe|m�•¯c£tWõK¸©ñ[YÃßñWYiïÃEìÆûƒŠŽo Š@ÇQ¥µ#¬¼ö€FM`^ªzzHîP×.ÇËÐš'­�ßM·�¬Qéó>Ú§"‚€eHxB9ÝÄ…Ö�l¬=Û÷ìPê)�I‰½LñâRh<yê�¸EUòZ°	çH”¯‘
“^�bî.M ¯®‚ðCÏ¹¢jàù
:õ””*�Óöz¾„ ;„)x,|—IZ�ÙæìÙ‰IÖ]˜DFžf"X°²
ÿû=ÅõâX	.¦áŸj«
q(
oÎ8ÅÕæÎ!¾5ž·†Ë3é°‹Ör!]g„£¤is0Å@(xüG’Åà­YØ¼™¨µþ4ëŽµJnhY“±9ÊÿVÒá_°�xœ�WÍnã6¾ç)A�µÇi¯Î&mv»)rHtw{	‚5-Ñ6™TIÊY7Í¡ÏÐè+ö:3$%Y–Èäð›ÿo&g''GpŸd!V¬N])35WøM)TNæ ´—V‹¢ØÀl~)ñdn…ó¶Ê|e%b#íqj)�ŒÏ­,-þÒž_YñT+‚wÆÃÕÝŠ¸Òh|á–‚ž�JÐô‚˜¡.‘y…‚0«TáÁhð¦3'ÉÕ˜´ÂR-–Òžr-ÈÍJ(ÍPlÊàÚØ'aó_¤«
?ª¹V…¼Ñs3�žär®4»L/�ÍÎÎYi¬wg¾“´k•É±wäöÙÑÑYˆ#¡¡Ksi¥Î$<)¿TDãöJ:'$CfÐcxo´GTŽ‘ÂàKK‘ÃÈ’%1â5 Åln,TeaDŽ+™+¬�_ÉÌ·¹@ýŸ£PÛYx>@sÈ9ž~Q9Ã¢^«äZé«¥+B†$ôý0x}ÞE©´ú½’5ØÀy1ÃÇ"³ÆaÖ0ÝÞ<Jí†-¼ðfö¥fH4uÖJ­Ë+?á°¬¤_šÜÅ ®)ŒX´!@±
z2‘	�ÖnöÄ0=¸òÑ¨ÄÏÁëÖ¤þâ(fKáƒ×Q=ž€®V3iS?R—ä&«Vø15Q–…Ê8ˆü6]b¬úòºT.�70@·Ä†šÄ©?¤a£:ø†`@9ümÒùa¯>ÆØ£ìþaKÝ=5½ |sˆÅ¢ÊU?ß±6Ø’)ÄýÖ�ÄAÖhµÄ70øéæº?>µÌ!€Î«ìQÚ^ xw�‹NÐÆïñ�®¿Ðõ+`ï…Ï–§ÔÔôBo@ä¹"g�në©¹ºÈ]Ðqÿ(7©[&Øö�Ú<évßÞ‰in5.)¡>%Z�TÖÔ“mb‹!�‘û·»’š˜96©¸€gp¦²™l,a|-V2Y	/çµmW�›¦Œe#+·h%2
ÒCQ¸×ÃåmÃ2QÆòØµãºÒ<Ï°3ZÔAê°Z[?�<Á<ŠÌâŽ—Ã,º0ÀäÝÔÜ3Š<�ž´CKw10è@ËµÑÑ..áÎš•ròm/=^6ŽÞb�’ïs”™gËZD‰Dµ³DÇ)‹ÉË"[ÖdÆ†ÂÄŠÄ·[”ž˜þœkk-
äfä9üA-TzIa¥¨N1¨ã4Ú§[c Ãò÷œŒFôf‚s	MÈß†¤Žbr/)å‰5'ð†ÞÜÑ÷›Qâ°xzEß|ÊíO¥ïpJ$•Né; $¶I(é7ßF
‰wÃ/¾IS#^ý6ú™.vo[µúá+o_Òzí˜oo:œÞ½{¡Ìí’´·-e+S�6DnšÅˆÛ„+&²GJXaÌ#ÕRU6Ë	cPYäÊÊÌãŽjÚ@`f¿áqÝ*¥ˆÄFw§¼6n›ºãIèDª•q�t"Õ£1%ÃëhƒŽãCŽc8NY8Á1WÈñ0 …•Æ1ÈžØŽ ‰aZé¸›N‰øµÑ¸]
‡¤�=%+CÎÒàÅ/P@psÒ¿×$F ×maÃ÷øj«ˆ¨üÑ¶ALaòy÷6èœŸo‹sXº²a;œrrpúÝðþÛ‡Î3®áî3>ìrvù°‹H=·ƒH‡]ÄÔ‚;¨é¢ó viW<÷9ÆÍÙëßœs>[w÷)y€ÅñZÒÎÛ]þÿ¯Ê¹sxô¥¡8ˆSlÒgãaØþgN¥N‹ä�€h_W•¡<œC"‰.£3Y2O"„Bÿ@5½Îã ®ˆµñÌ6ÿ£²HYàQ�  "uºoïîmVüˆIJ}Û ýLºZHiº�Y‘ÓZflw|
r ©àm†Z®J¿	ÛS,�žV§ˆóíÝc°ÓÃµ©éd8ÙÞWBûFD¤êz'Ø³ÕLç_RåÖ�½¸¸À�§P]¾Ï{S¼[’�–é¿ÿüý|óœ´½Lƒ¾(Ù2•«ù?Øœ®!ªxœ31 …¤ü†õB¬ZŒuÕ�Îå?ë×äûúûÎ É&`ÙŒ’’†èÿ³møšw�ùuo¦ÅL}ŸCNˆt±1ƒOØ>¦w×·œ:ÃrG[äËîXÃ) ÃH"!¦xœ340031QÈHÌKÉI-Ò+)fÈüjÕjzBß.°E:ûzÙ}åIóóH»Ôxœ�YërÛ¸þï§Àf2#*¥©lÛ_òÚn}ëz6ÞìÄÎôG6cA$$aM\ ô%Žž �Îtú»³ïÖ'ØGè9¸‰¤h'žLDçœËw. xUKeÈ#TH^�5Y(Y‘‘û:ÚÛá�À<ÔŒKaØ½IÉ+ÙRÑEd0~aÃbÉ�ìîŒ—,fÙþ²¢\L˜0Üp¦' Ùb=GÆw¬–š©†E ƒãßU‘t[”W¸ºdê–çO¨ãdySª]íhÛ^È¥Xðe�›‰Û6Í‰âŸ>•ìYå¹X(ª�jrÓ(6©™Ò\&r6‰V¿ÚõHæÒü$eù¼à`Ë¨wk ßH(årÉT‡Y¯¨bÅÄí€¨‚Ý·NÜ!¤`†å-»¿¦°hPÔ-��¤¦5]Ú�%3§JIÕ]BÂKþ‰½á7¸f¢sÁ
N#ÃzH¹Æð2âegòêÕyE.¸à-Ñ'"Ä(z�ÜhV�ù1+F*”NØ- Ž¬¨(J¦2àF(Å4lhK©›¹f†ÈEúHð5–ÊË ŠýÚp`GA©H­d6p±$\ä²ÂwvåLÓxìdÇš~$M¼o}&‘+�>ÌJî¸Y4ŸPch¾ªPéÅ‘@3ôç^K6XZ€â`Ÿó‹%ðBÐçSÀ/¦D4ÕèÖmÞÍ¿[1ÅÚÌäŽjµ�/½¥¼¤ó2ÈDžÃ'„Âÿ. %DIve¢­èšÌ9Ê¿Ô,³Ëd×j¤o¤$›@ó¶6\
€‡#´› d¥IÂ²eæ–¯[ËD³RŒàÿ±ŠfXÂ)Ið¨)�,ƒíÔc­ëÂ5oËuÛr²“ýòxškö]#n„¼{;ë¤OšüfR9ÿ²ÎáŽÝC-`šÐˆÌ]m 3)’ñŽ3+Y$*¶Ä²‚zvq¯‰¤jJÃkàu[xX™<§¸õÎŠQTm ¢æäƒ:!œïYràìIîÇÐîjˆÐ_•¢Ç½woˆÑ‹BæbýEJ^Ô+iä‹q7ÚA�¦DÞÊÎœñ[ô ªÕª›¸JAuÊ„°~ø˜	°—›ûi+S£go¡kvÂˆ¸†BŽÈŽ!«º §¤ þRÒ‚¼÷&:°©q	Â�Éê”·ôñØoW¼ÀÙª.±[œu3/y~^xv÷Ò;ñ‚ojÂÖé“8F‡ñ¿ƒÞ'^é÷ªXPëó¾£Òx^ðëx‡¾:@§!¤œðÆÊš½|t}6›SÍ@þz²˜¼|¢Ö3¬ôŽrƒŽÈ¬FÉÌN�^+ªyI
î,ø†üþÛþõ³øY}§äå#œ³ž¥ö`òÅ¬Å3‚'7ëd�Ýk=îDûÒPåzC5pS1Æ>dbí1ÔÓÉl¤6f‚4rãöl¢ñ˜ø¬ªP´—”¢ìÝ6ô¼€µ;h™©=~ ÷¹:GËÚ]�-ÞG{SÈy·á.h •�þå95±½rµ1ºü×†‘ó“ÔW"PÁäë»†Y] mAV:ÃT„¬ðØl'G/
Vëv}Ä1…°�Nä˜z6-˜6h<žœyË”Ìg=Y™éM‰.I¢Y~ tCØâó~1Å‘!3rÂÊ­Ž. mJf 
ù9.{K©…$½66D›‘Ð)ƒ6nbÞ àœ �J-ÿ¢»br©
ÝU�ÚýÁ)Öé¦Ì–B[mJÚˆ|DÐ"à7ÀÕ78Xa?g‹ ·jÆÕiwÐš[^«Í—ÂñÌìw�R®¼,à,kœM p\bgÒSÖ¦r˜Q¶"6¬#?¢@ÜºÁ‡ŽÞ¿5ìuéb;mXcdÃI!¢xD×ÁxÂ»,T—V©ÎýÎ×ÕƒvÑìƒvË‡‡áÊ°Y#´ö{Pâ'A�@ÍÃÁLv^w²0îû–+h›„*.Í•¼aBxýÑñ¤Î|ýJF6œ£´ÕDÆ¡/à_¿ÔûeBf¿ÿöï�ïi)¿!?pÅ«*\X’0.@Âa!�>hSpü&yn+#\0TcrÃ<ryFfäñ8ÅvŠ²®á6d`Êç·PþBŠ“–ohÕÀCƒZ,[%7ˆ0•‰l–zÉc¦upÈdBÞlÕnÑÐ³,ÖH´£Ó³·ïN[š0èhi‹¹ª�;§5Þ‹èq¨	º7…8ù1&8Ù]ú2¸?ÊdóÇ57B±b”Frwq›ÂÅ>Fà6Ÿ¦.Ð%‡­/™æ¦Ð
¶à‚i”€·‚Øaí!ãq{í„ýË�Õ8é{ÍAÏúáÈâÏ¶[MüP†�c¬¸2˜œýQF7`êÈ",vðÁ¢Ç® ~ìJ@~�êšß0Y®…4lô1˜ô¼Û©ä“¯Ù69Û÷î¤5‘D/uùÞÎ¶áËy›=�§z´›¡ Y±kI¬Nþ„á£ñv¿¡±%åZãÚçÏäõ0Ë�0y‘ýÖé]!&‡N˜@êÏŸŸ$·a;Ì4”a–ì~;†ªó•œ6h_Ik¡ðµr-ÏÐŽÂëŒ¾s*zïÝÙu’´âÖbƒ»wƒpøÇtÅŒêÏ6Jì>g¬Ð0$‡Ã'$ùöõÿ­?Æë‹#¨i Â¬™õ¶öìÞÏû¾„‡öžÅÑÃ{;%žI+nvÔm[öíqÒ‡n&®Ø¾,\!:š÷jßIl]'q©uJ—Ô;¢yXá;¥Ñým®>[ç§=ÒÌ¡uLŽþº¿�)W:ñ�M«rúxôltj!bŠi˜c¼z#DæG¨+yéF§$Ö‡¨èsõ$Þ=÷ýëæd<€ùpóœÆWÇû=×{1(‹,ètgKÖ]~š%€q�1lvÙý y�ùü´ÓYdðu¸uV£Ë6Œ,ÉW¶oŠ3”Ø­ã :‘€\³=�ÑjÔÓRûèîEÓXµ·OÝÆd@Þyñö�õ{ªWøÆ«,»{Tå+˜%®zÑz’ðrËgO’žy?=Cr* ×~�æ":öi¥6ÎÛ¦™70˜aMõŸ~`ƒÎÌ#
¼¢˜ÆTúaãú6¦Ç‚–º�	ûF†é÷6LÃ:àþ±l„ÙÞ\wò±W´sû‚"	ù9 }º·*o‹«Sxm®Ç÷Nx)„z»UW‡“¬Ã‰óÅâÙ.�k¢M¾‚Þƒ¿eLÃ`Ùí@ÞK’Œpµ­#æv}Ë/°÷3‰;gÜÕøÙA¹¯ñö}iô¿ÿþ“ü�.i	3¯X:ûÝ‹=r,ç.ñKŽ%ÙðlÔ¶=Œâî¨:'¯îò«øð'r{ÁëÜÂ�pÚ~Û×º'ÂüÖÌÝ+Ãäu
“Çx�eáöÇ?È £»8?«n”Î(Ø_¸÷ øÒÎcíù°DG›•’wŽÖž¼³Þù?:nH
í“
xœ;mõÊ|B4{µBRifNÊÄ$-Ý´Ää’ü¢Ê�=‹ù’3R“³Ý2sRƒ3«Ru6ë0å
ªe¦)h(¢Hh¤ÁT(€X!•©š“Ÿ	+¦s) €�…b B…ÔŠäÔÔ”b•êôÔ˜9>™¹™%p#ô4ŒL´@”f­¯“BHI‚ŠÁš“?ˆÊoŽ1`š,+%&…"ö–_j9È
�É>¢“÷ŠñM>)f
Ä|“¹Ä�'[ƒ1çäRqƒÉsE¥&ï·ŸüC\y²¤„Äd}	u°ÜIE¾ZMd[7[K	0 ø^E¬xœ31 …äü¼’¢üœœÔ¢bƒ§7?H¾Žø±×ÞvG¥ã�£ùÁ&`U¹™))9©å‰E©ën‡pýÙr×Á7#òˆüÜöG57ÚAå—–¤3”gW5-Y±ðINþ¤IUZG»/K; …Í.–¬x340031QH,-ÉÐMÎÏ+)ÊÏÉI-Ò+)fÐz²’ít«Ô¶ž‹Ôzß‡9o­±1„¨NËÌIESÍWßÌ¿÷ý_·%!ÿbçWmðÜ'˜äU�‘š˜ƒaú©K«LÏ2§[ïýyïNµ†øƒu0õù¹è¦Ëßúu·¯$-ì†âîìë»—zž¹�¤Z/£$7‡¡÷þ…%[xrÓçÈO�|x²:Í¿	ª¨ØÍ¹]SêÔ�Ôó®Íú©Ú`gÓ]f¨ÚÒ‚œüÄ4õ^i)‡×2Ægìg^e‘›_»çõ±UÁPõå©Iº‰™hª¾»,ZÉô^ÑÝÐ¢P,½@ìŒ¿Y âŽË²‹xœÍWÛn7}×WL^²»�,;Eú¢Dqœ ERÔ‰‘8è«é]Jb½"’ëKý{g8Ë½*)ÜE¶É¹Ï™3\µÙëá~àï¶N*¿þ,�SFOñ,·Rxù›Y)ýÅÉ7ÂÉÞ©©üèøT6G;XZ³�d6;ä±Ý–*­VNä(æú”ÚÓ…LžOTäF/Õ
FVøüPéBÞvä)‚µÌ¯^Ka¥=7W’S(ñÿ:¥7Æ\©N¬£ã•ôƒ
(Goµ¸,e±'¥Ê«’SÀP&‡OžLà	¼“åVZðkáá²Reá@À¯Ÿ?~€OÒm�vn”_ƒÙR)D	òÖ[k)
iÝM�•W[aÅ
áÀùZNZ%Jå("¸4ÅÝ¬#ì¼ð•Cñwççgñ¿ÜÒB.EUzøéè(ëªÔ^Qçc?cíÄ¯¬ô•ÕNú	Ñåá[ã<üáŒ†zÄÈçPé+mnô4F³ ¦Ñè-äÆ/œ·J¯HŠ~¿D±û]6o¶x‰Ýh¼‘‹`Š@aÃ�IØem+>Õá
’Ò…°<;zŒ‡_L¥ÎÂv² -„pÞÀZaå¨¹ u±5J{7V>S
îCÑô¬-¹�%ÒÆ³{,S?ËP½ô¤µÆÎ!¡H—$šÀnJF;É�	‹„8“PÒŒ¢«¯•Dó‹)·1÷XÎ rái&.`©dY@†Š5Æ”ÎÍ†T‚¢Á ×Ã-æb²çM ÁlÅ]iD1cáBWeyÁU£(H›*§ô5¢¸èg²\óšd Ü�Î!Å¨B!¬Õ ròÅ=;›×ˆAžøäë%1š½¿Øü%[MÅ�Päì+£(ËÐÔÖŽ¼ÂîyÐUKH‰Í’ç-�°ã¾}ë\ÍJ©WX¬JeÀ%
�±½ú ‰¾c4xÜò`¾®#¨ï&»Ë žË^û#Hcs?aAù^ØS89{_w(°]ôZ‚Äˆíñp­‰�!6�”DÙS@Ïà¤cÅ\‰5&üËUyŽ‡1ˆß÷ÍKðÃ…ˆ#D÷Ãp¹—’l�c9ôš{(nŸ¥?`Î¯)ƒA(oÃ"a°¬Cqÿ‰qzäfõ6¡,ö²ý”y€ÍÍÎ�Óìy4Jr�¥S„•GE¦â(+#c±£á8”�­Ž®Ç«>ei¨ñâÿÀp:ÙªsÆ0Ìz§ÓFØu·ì±‘�Âèf¤t*nOVòÔUâyTØñ;N5¦ˆH �ëZö’Û7€Q½_`Ä½Õ!n68‹Tß°ÌbeŠž„d¾ï‘‘&¡HIÖÆÛŽ|Zw²f ¬×®š+êT�Ây …–/½Ç|1‹‚ˆˆ÷Ì±œiÒòYc‹X¬/”5¹ìY_šö4cÓ»=µ«õ¢G”ÿ9 òÇ\†Ë™½¡‡K8‡¦Ú¶{žég´ÓL‚ŠÈLb
´¡‡QB3sí›¹;tíiŠÓ ¶1–·NvŽÓ]á8 ãèœ¿Óˆ¼²æwM6êÉ[~aÔ;¦}ÇSsb“F«&�«ÂÂex«“¥€§¬×JÒ	®•^š¸�IÍúŸl˜&ÆðÞÁqàÇN¥»6ÅwQt*ÿ‹SwcÞýÂ«Lè¾ÿÝÓ_9±��Ã£áIÎâçAzC3Æª?’,Ëú™›|ÑQ¾ÙVOkr˜´Ä7œ—æ3m²·Ÿ°�þÁnzÐfÚÅkÒ¡ùu\
˜R,ïñq»mê]Á–9¸ÓÝ‰Ÿ‡¦ñáFbõ
”äAJè%Ü·¯ƒMØÔ4í…Ãq†®³Â¿é^Og�Ù`�Ž2¬ï›“QýB¥nGç‰|xœÛ¤¸Qqƒ5#WnfJJNjybQêf7Æ~ fš/n#xœÛ¨¸BqÂæÔ¼²�>ò /Z�°ÉxíZÛrÇ}çWt\ª d€}{�LË”LEJl‰EÒI*qŠZ`‡ÄZ‹xg!Š¦Y•§|@*_è/Ééžé™]’|‰ó–J"vgzº{ºûœn¨\®lÓÒÍ“·æÄäÅi‹_—tK�]Ò ¶…™^¸Áý�R×ÖymË"­��iA{½2�xf*sÙäËÇeežÖ6®Ï²‰ÿSØe^ÖÖÀMÚ°|ìLóªœ›$ï†.‚ˆGù|aîÊ)ë‹&wm³ž·ëÆLæ¼jRÖ…yÝ2_¬ë—æÙ&¿4#zbVÖ•xrýN©EÙ6³í±µÕ;w©Ul¯°#	©ìå¥i‚—£OÜ"oL1ñ/·Q™¼^¯ÎÌrÅ~…¶Yæí#¹½â°Ñ¥i�šÆ6_ç`ç]Ã	ë¶¬Ü„=›Tº¡ÊÎ-WGu»Í)½Íß•+ìÝ™ìííÐ½n|^Ð‰ùvm\Kí"o©¬çÕº0Ž»n­r„…ia|>ŸCÍÛ';²<ìýsÙ.Žy•£ƒ(ï·t³C„Óè¤/Æ‘yÝ6ù¼Åá³kj#rP“±\òç¹SÙï%¯gU9— ²0u[^”F×c‡¼>/lB`•õå}È¹½¿ƒ?jï‰q¶z£4Ð½4–y›óÍPN—å+Sû7O?ÑÚA–hXÖã¥Y"òØw²ÔZÊ_qf5fÅÙXÐáñS¼«*ÇïôvoúLüHb’g�éöëÚMYNãg1*ÃÛûTwû�ª5×º°=–0`×ïmò­†Øá^h±y2ß_ïÜÖˆ	D§ª&úr]8 Ü]×sÂÅ}cÔù#¾D¹’§Ez¶;¥c”¨Ò™O6e~JŸÊmûSÅ½4×8êGü9*†=¿wÓ?íö_rgG�õ¼œ«—”žêUØ.ÔŽÖFc¾§z]Uˆ¢ò‚üâb7DžÏê¬0³õåpÀ{�ð€TZ‘;¡rª©#¬øí.ëFp3»Ü?/$&ù$¯³ªÊ½ÊË–B‘b­U¿aßd‘ªû¼‰®c¢/’l¾,|“ú¢yñ6Õ¡cÐ\ë¥ÒÃuYÙ¢÷H�>F…½ª+›ôÕÉÞEH*öÙ*o”×Hv‹*c_šz#%xÕ1/Ú–²ÛkãëEpO7 øŒåñä/öpž®QeP5¸ZJ:«²w3ãñÐ7ötsbÆÖ«å�¡úWM…;ª1âÙ^QKŸ |uB¸ÃÅ/m»rÓÉ$_•™^tf›K)óA÷nTÄíäÞ�À�¯Uí4¯Ë)Å·Áï©F©–Ü‡éˆV<~dkTúvüyé=K[ÓÄÁ4#ÜòÒ¾B…�¢(ã¢Ü z1
Ìíº*ÈÔùbýüÆÌ[HØrƒÏødƒM~•´¹ëb”F‘R•»ö¯¿3�ñ’Íy"Êª‹ùõã†CY¿ÎP�«|n†“¿}ýõ{Ÿ\Žh0Øí>­¿nôqòåI¨�9ýáôù32Ë\(W(…†®€q:"\›·k'Fb{éñ{Ã/aÕ˜žœ�ãòãï�þ 1	 Ž úÌÇ½£C¯0L”ézë"/%ü¼ð)êÙrÆW+R;ŽÒ½\jõ÷ìgëáM0õ–‹YÐ‘˜ÆØän…“ËÛ)Ä #´AØ ‡ÏÖ¾t´^i ÙËÖ…âØmñVÁtbYÖ�Êð?cþQ˜
àë¨¼°Å”åŽio/pAP	Ö±ÞÎ$ð^¤÷«2!aÑØÎ8FÓÅI¾‚,¨°¼™/pÐ9Ñ\÷D&Z">Õ·±çÄQ`P8[¶³4[¢"À—œ˜0 _XÏxbc.×UÞôÎR?úúÄNÐ’"Å”kèðÃýºù¬Ààõ�c i S°	­p>Jszá1	ŒèÅ&‰ó‘—K!»7……jØ�øq7!|è™×Ò‡x [H|<†Å'‘#@¯©2ÂÄ;T@Cqƒ(¦"ë!"“Dq²hc)3Þüë¡û7ºEÑ;a2døÑþ>ªÀ—¥F'¡\ƒ Ó·ŒÊ]\Ž˜ÌS×‘] Myx}¸Í0éÑžÄoxGR!`ðUÞÔ�AÔ Á»®û Ã¦ÖAkv-¥}}¥ùP>3ãEñ0Ÿ	Á…hrb�ŠËCqpÑLKú*|¼ÿ>T8ôQ8`¯‡Da$)�hæÖ+ŽD3óÎp¬ÏVu2¹xYýÜä5ú�Ôn%¦ÜºÖfÀ,Í‡¸;_J\ŽÚ¯%šÃÌwÃ�|Çø\ò_ã
Õ�½¨ããñ¾eR:t<>™ÐQíÐ‹Ò£ç'§ä,Í{…Æ–.L;_Lþòä„ (×3¸–®lƒ:wÇJ°fÎ…Ð¶6WäáË9¥2�£.\aˆÜE¶ÈÝpp(ÝÕ˜ñº±Õø°ªìÕøyS‚Âvwi‘1×{ë*\Ô^r÷öGH]gÆA+°ÀÁ‚0Š¬áS_¶‹±r«v|Â^té_GÄÎèq1tf‹ë„'S)(QÀQ`nJ‹x½ÜŒMxRB|óyJ»Í7)š}0…÷œIh›úâ´ð†—ßïSFæ÷ƒ1ˆcªcrK¯4ì;Š�B¼þžrêÀ(Q¢�Û,ÿ³�xê	¬	ªz%æË#E)yy.´{—3þÁùá7)YAK=?³/“ûHšHÀ§ð	„_(J ‰ù‡é³Q`SU#Jî°¯Éf­á"}Š‰rOL‘DËA‹‘
æÒ6 Mœ´Ë•Ðæ¢±«qQ�ïÝø±×p÷6lK¯ÈGúVëáºÎ®š²5h°¢œè5U7¹Éß¦-ñnú“—­bRiÕàocg×½^¡yPÔ•ƒåMWákrÂ‡~O.7*3Ž½Ò—ïùf¬áÚÝ+nj§~òxpgÙÕÊÄ Æí7í”‡V\	šVÃ‘ +¨Éñ9ýN~ûGéÝy_
+e½IRÔ+��ÂæŠŠÙpWG	~™Ìg~Œsµc¿#Ù‡á/”\°­Zv<	èÃÌ³FÍ(ÈÃ]î½¼•~D�Òà4Ñ¹YtjæÍ�µý;˜úÂ¶,—†?*Ý _­0YË¹§›Xt-¸.ëA’L[!Ò^äm‹áíx_Ds·vðÞ½›íœ§)l¯ð�Û÷^l=Ã#ÄÃpPÑ!‡……X¿Ø¸·! ~¯oÁ¸n  ÖÿLêßê¯F=8c“ß	¼(NóŽâ`ø2¤Àäç[QAOìBB|–ð À ø�ŸBä§Æ˜ö6Ö¡§Á<D{3Z¢ßuôÃ?þ­�KåŽÅs¯àÜ6.ð&ÿøX|‚¨Âe22ð¿`û±.üø‹Ü\ù_½ÄMáž~})�¨C4~t6¢'G‡??>{úüÙiŒ°í{•ba¯Ð¦Dª$5dþ”7×XˆžÊskòÔâ(î­W>?#y’Ð‰‰ˆ‚6ºþ­ ®H’8ò´Ç*“»ª>«éa}Šîß+§D/nÓ´àÅ¡*ÈúD\IèÂèbð•ÚºI6(æL ÃŸÞ ‹ôC€8z@ü÷”>Þÿ k%RÚ	ßîŠÎ™ÑÅ¨¨ö‡›Çu }{§	Ð¬‘9�TæƒèÎ¯¬[±UJ8ƒ§Å€¿0ãk¹Ìï¾½°+ñ§.d¸ÆßÛ$7ÆZ(q:å~Œ'ˆü)9$.êÌÊºÂ\ç—âA”¾‰âW~g^·¨DÐ~2×Š³Ö·è#Zæ¯ÇèŽ>Üß�iK”eÙ–ÛÖìÃwOF!þ~õÎ„†’Žóe~;¯J8‰†µ!Bá[iÝhaWá6!Yx…þò‡þí³¯¾3 ÝKi,S×™¯‹ÒÆÞ“‡Aò³�Gh\d¡óŠ¤	±�-èuj~Þâ«
t¼4)èHâ«až!¯‡ƒ~ÝX5ö5f<6Ká¤S=t�×L7¿ûô‡ÆÛT�ÞU1~b½Hz¥Z±QBNpàG KÊÇ:ª O5Þá·¾×3dÆ±[ôWìãì©}Ôùn8ø'ø.yÿXÆ[w;§ÛîW ½AnÉ½kþ¼!®?ßÉ˜7~�
âÊÓsþªcKÑ7½á¼PUD—\é¯ðÅOž±^“�7Œ/•£ù¯7ÿ?ºäÿð�ýŠ£Ë·1�”¦¾|`”Ú—»å„/þœ{�°‚?s‹‘r–ôœÁq£J+RÎgŒ)aIÄ˜´FÎéHáÏ}PôƒÈâ<G›‹a÷¿zøî',8lS™SùúÝ‹ôuÊp¤Æüôê"Éý?¨,ÿP@«·µCxœ�SÛnÚ@}÷WÌ›��VÊ”(U…„ª^¢åµ,öØÞbïšÝ5— ÿ{gw$ðRÉ’9sæ\Æ¼ª¥2p½)¡…LÉ
ÂTñ··GRUá$à'H"EÆó3*Šbÿ Ø¾‡¥«[™bÚ¨&1�Â¸F¥¹6(Œ»eIqá)ež£ºæÑS˜ÆþÏë‘#ähfJIõµf9ÞêèæÃKg¼DšâÁ €Ì™HKÔ`
„YiŠQR`²i-¹0¡,ðÏ8!SfØŠi´ÉLßrs€Õp�	í90Ð¤®DX¾Ì~Ì¾-àÓ6ªƒ%ƒg¤8„&Ôç»;P¨k)ˆnÇMKêÄ0Óè1„rB»„]�ÂÉ;/æÚÒ(dIÁV%A*"»¿!³Ch“�#óž‹b !ÏÖk¦Xn`šâ"‘•u2_,žˆ•Äk½F4Ó¾sñ¨:_áûËï_—ÕÔO˜ËáƒjŸngÏRÄî]…$±®‰¹GM�éƒH gE�)2§ ?†'ª–küòÜ­{€é £î€íYLW‘/{tå—"úòêáDýÕRô>†?„­½X)ÆÝG±º~õ¯ Âm{lqd=) çrC#ÖBîD¿“æ�VÝ¡N¯O×Ou
ý¹GîU/ìrñ·™Q™˜†VB·çDÙþ§=7F×ãï]Ý_\í$ø±#fh³�xœ•VQnÛFý×)¦€‘Ž$ºERdÔ…mhœ®‹ö/\‘Cscr—Ý]Z\9DOØ“tf—U%@ÑCÖræí›7o†’u£�ƒÀ�´Îþ²Uì 0º†±Ò9Î;^Œd•K£D�ø¤¥úw`#\9„ºmƒÿS«®e…ûÈU«(f”œžŽànÅFÖmª­Wh@ÐƒÊñ5˜9m$ZpÖ¢z‚¶�u‰
*�	'Õ#”ºÆYéêjÆX—ˆ Òíc	…6àJ„Ÿ¡[Ý:ˆ2­œÑU…Æ&0=k²–ð=TøŒ•�a-]	µ0�R1`2¢ëàöâ÷�w÷Wï>þvñîgJy=p�VWÏL’n+úÒ:V‚i§{†)¬¶¾
æM…x1Rë„q—Ò¤|Ã=JÁrU’AQ$ÁÆ•–?På£)°77H¦Us†™’Ï¥$6L§¥v¾X©rÜÌœMã9¤¡K³�˜‘Ø)¥s&)êÐ"C›”Î5É¡véd~àéÓ†rVÔ‹K%Ç³ÀìÎè"b–W˜Cš“é:fŸþ‹Ù	õ1±%9$qXaÛTZäh�ñZ
7ŽÛàŸwŒ¾ŒˆÐ²\Q‰'œ)¹IÍUuW
‹qß�{t­Q¡½…4d?&,&¨¹p4ä¶TµuY ´BnF¡©Fo*©Š!¼�Wü ¿àG2¼¨¡w·®³þ–©ûsŸjQ˜¬Æ‰h¸dFd·_Êtî`k±¹dÛìÿµ(ÇB´•›ÃëÏtB\{|/ú2¸ A˜%Üø­FÊ„©¹¡Ôn×¢^€9•lHÞÉÀ}y<‚qßËsxuÖWC	=Ú‚NyDþQ‡v¶èþ}»Ü_Ò½z{$èˆî{Li¼ð"‚ŸÀx_ó8^øhª7ög´O‹cÂH‹ø®Ë~«2übÿxébXOáq(3ðÉŽþº›X‘Åh7ì¨Á³<ÌÌ“\Ør¥…É	à)ï�­,/ò|¸¾üV" ò¡áÙ’¾‚›‡ÛwÜÐ†*Â°C}¸ž¾a¨ŒfÕ¢›Áµ�´
A5ûYE~4†ºã·ºëW)ÅÞYaXô˜3'ñbÛR+ýüÑ›eæÉ€%ÏVÝ&¼zÿ�lBñdM+~8;ÛOÛÞÆÇÄyÃ	©¸×)¨ÃHä¯¸¸ôº²‰©Âò‹3"kÒ‚«¥Å·÷ôyoÏ.k0ýÑDG£ì�À>ø¦ÏéÍéJCå*\ÃùC8è×0}ð÷ç¿HHýDË¶ïœ¼Ý´ó�=y9š³ÝÁŠ°éÄß÷nëª	•ˆµ CDÜîa4&“t?â™£}y€ÞªTA/SvH(�æ×µvß��…kKäMO'/]­ã®/Sþ�1žÃ˜ÁÆXô¶[¶®˜¾€ìb?ÿ X“ô³¶xœí=kWÛH–ßùê9}Úr·m $½;&$„$œ	 3{2Ù ¬4±%¯$óè4çÌØý…óKö>ê-ÉØÒ™öôN°TUªºußuï­d4Îò2ør•â­ˆâ½þWÁqž�‚VšÅ¢\´VÕ6�Ò,‰Múi7déqr¢ôz‹ü?_LÒX\8íéã“Á'Qv‚X…ùu­ùï�Ë×ÑHt‚aR”ü¤¨~">Z<âwÖ˜Ò$•½ÞýUÊ¢OqðçÉPl\ò«õ4þE\âüˆl¸q¹›‹ãäÙqùŒæ‡Ý:uß?†7EW\”î¢#økg2,“q”—ãaÅšÙhŒÖ½"¸Ô¼Ày×<NÒBXßØ…ÿWkq•§<F¡àP¿°‘jmV^Žl £v/ŠÁ©EÕöY~åñ[QÀ�5³Q”¤‹Ø£X,Ü'y4êÂÚÎ’�°ç(+w³lX!I�ó¨(óÉ œäB�±ºcèaf''"÷û§Q.âE~Yƒ°C¥“ñ¾�	‘ð�­‹þH²´œˆr+Ï³|GEtRù…I™B_ië7O'é'3"´Æì0áa™å„‚Ûé¾\µ-jvŽ?1à¡ºØ&d¯æLäÉñåF_¾ŒŠÓŽüH_$'©ˆò¡z¶"„hu=ü•be1š”§öè{+7ß¼ÝûørkýÙÖÛ½NP¬¼6#òêÕƒœr‹*“x¡èWCƒqŠ"rïe“|0m‚5@ Ÿ³¿ ¨]ˆ·Qz"x‚�`’Q™Ç‰ˆ7³6·¤×SÏñ½»•Ì�ÞÁr 3’ôd“¸à$‡‘³ôÏ£á& ßŽ/õª¡…|ÌŒÒy”¤I™Ty†|k8§ÓÉb¨•'ïTšúLÃzEÌEþ&hnúëB,Óo™“*6+ß+D8Û›�}1BB^XüñÇ…àÇ`ÿT HŽ#d){+A.N ¾ðà+`qp~*Ò ÍÔó¤ÄÅx˜’rx)% vÜƒ¡àwQo·^l¿y¬É×½bå�ÿ–Çøí· 5Vœ¦»lÍä…Hl¯(‚P(ùŸ‰€¯ÂÿÂ�IH” BËÉrž'/´À/cïÿà)Ázp*.€Ôs@š ˜$etl»]t£Ñ¯]9fdq”Æòaw¸køÏƒ­½ý�ÛÏ`a»¯Æ[{"åx¸üsÛL}c’cœwQÂ˜À£í)çIy” çh<Î³qŽ¨HZÕÓ‡­ýá¶3èÒ¦üyçU “fY^š±°AÏê ,'ty¹¿¿«~@û°Á’·c9l3H­©Ž ŽóHñ�nðfŒÄù¹™”ÓSïC
ƒvaìQT–€I
5m0+ê)‚©–§�ý[�N†CfÜ¸®>ü‡�'´$ÕØSíÃÇY?–¯e³'ð‘ÏW�…v?0Ÿ}BÜfq1X�cÄXdW]bŽÇú––PŽ&€šï‚0ÍJxóH$œ§Q�t‚h#K9¦õðŠ‚µµ5ZKðÃôPîÓ“5Ú{ø8X©<ûnm�>%Ÿ~g/ö}Ëžuë´hÓš\˜øÍ -ÀG eâ¨’+Á·0HÅ¹Tˆëè »çuÔž÷�`
iO:ÎWÛÁ�ÉU•R9‚7»ûÀ)ö‚1hŠÃää´ôh%`…P�#D^g�”1Æ8[Øxæî?¬×Y(áœ^h¿b­Ö“Ë´<µ¶]äÎjmã¨<MAóáRfRšÃù$.Ipe)LZ/ko2&Å�ú�”½žuðöU�pëPjè]tYD-ŽIÉ>ôØˆþ8Ó<¤Uˆ”»aR´}¤XoÒ€Ðd]G‚°¹mCšDÓÞÊ.|a¬>§H`þYèÑ÷*î=UA¦@³fQ â–ak±Õî�XŠ<Ü ýÔËöª$>êÒŠô'ô³ÔVxm&Â{MßçÏ6ö^®éM�Þ/}¨Œ Üä™@&�ß¶6; L nz–äå$vO³‚ -N£³øMøry©­—�P_ãÏ=÷
 W.·ù×(‡a!NF0vÓGá››ê“úµìò×,I	l«†Ì„K±¨´Ù…Ü[!­B‘ãxŒ‡`€`ßîY4$ñ�\8# <-pœav.òATˆ¸Í‚'9y÷0(´ŠL*³dJV‘�¤ TD’zR¤y˜­¾ž[ß”3¶æ8Žf.
‘åšö3–!ˆÌð�¾Zu»Y¸ØËŸ*ˆp!÷xs‹'õ'í)Þ;ˆÒ'l+ÎÎÃc—^™½ÂåmÂòÂ6²uÂcåÜÞÙÐÜ+RSX!ˆ“bŒ¸)rvÉÏJKkG¢<Í€É#%vx˜_´; ’æF,´i%IŒê…cø;K+k	YT‡8�žQ‹§5áPGÒÅÁ|«ƒÃh;ÈÆ‚-�Çi<4kÑü•˜˜ÅdCÅX‘§¶i.urC$<ä¦=q¡†Þ,TÝâÖRÀæ^�´ ô*Þrkùå—™²$ ïŸP·7áÄAžM�q”§ðÇ	cÊÿyü†]�Z~ÍHÈ~W ¢ÈÏ ‡ÈöçQýÀ€@#ORmÓÞŠ¤.@ß¨¸L¤<Ùd‡*^8¾ÔX“
#Ðè.?0½ÇêÓy2C¤2þ±ª_Nr
”ÀJ$þ<i¯ZÒHJ×5lÚS?MƒÏI‰�"MúS'*~*©™ôD¹¢JËŽnBRÆ|¤pr„ÖÞ¯ãþF^JÁÑV£]Ñ¿}[lëÖÖRƒ_óÙ"‚ÅjWˆ(œî",ö£U‹/¢)²fX!jP$3_.°Ö§UB)�Õ†á¢¥ÚØ2Ì�x\E×ÛÌytf?ègAÃ‡ŒžVRhÇLÎžzû°õçî:X‰Ú‘Ó²–Ž#J—Üš=ŽÜáè<JÊçPh¶ Õ$š™Ý–œVÿ–`+ëƒðœ_-´­­ŸÙ÷Ä €ä6ÔÏLC6òûÒ‚×¨Ò–¸b/Eƒ"lœpeE•(óŽÿoÚ¦ÍÚ�-c¢ÌwfgzIñÅ‘Â‰´l\­Y[ØèEÙDÕ�PíuVn#§GJÄHl�––—VV­‘FÒ_¹&'1ó€zOÁ;/ºÒß°›YÛ8º$Q�ÈD“³`I&âžéÝ¯ žu×±ŽvGÉÐžŽÓZw„dN’‹¸µê’šãm
§-Ý=¼£ÏDšÀHj§$ÐÔOÅ}Ôoih.hdÚ–¸e¨ºåƒ7èûïÿ÷7ø/x›eÀE_Y§@úìïÅ™¡�}hÂfÂmÍ–}Îóbk¿Õ¶˜¶1ë›l
·Wf{ô‚ µCßY¢Îj`ïP…(Æb€>›X	°¤ œˆ¸y�€X|,OrU�S·g�²hýx¸ôÈ¡ÖíØ""ž·\^ÞõpI5;ñ}•ãžiÏo«m·ö€þB(—«n*áën‚Ù†¦ONØ3;õ{¯j\¹úƒö�Ó>ÏL]¿ûèhòÎ‰ Ë_²§Æ§mz!,´®™«t¿{0÷$‡Zži¨«†Ýß=ÀÝwß´26ÏÝ/þ0èršk�g[¯¶ö·üažY©3´ûfo”æ“½ØM(E,JÎôòÐÚ˜ n–rKô4ÙI7/Þ—ÑÉI3�­t�‘C¬éß«;\üþ3ÃåêðKxÃ˜y^?Ø©ššö7…³?üLÁœ²ð˜MhY>*ýÊÙœ«k§µó¼ªïñ³¯Éå^3óƒÆ‰óLñTkÚla’7�éÎDM.˜ë§M½Jf7›»f@µ`\¯	}¸ _8W2W �SÎ>�ŽöÔÑ˜t87ÓOuJ¾Ègšœ›f•"uÃÏ4`�pÛ�Ì9Ûé_˜&³ê¾£ÆšA{ofÉ·Á�-vÁÜW¶xrEÍä/Éžè`¥Ùyª�”#MØØ[{+–ÿ�žµ:®/¦Èq¼@ž«f Ô6Øny·øêñŸD R
™9$E¿85äÏö‚Ý¡ˆ
Av�Ë€Ç·~--5›>äÅU„åý'uôà�O^ÞÚÌ^—" JSéïû?RLÜW	ºÏŸøTÙ `ÇÎ¥2®ð”:·Ü{_vêì8�±Íù”<•ÂI:n{ß3®Dç¬øzßà‘éOòÃ
ô-¿ÏÅ½„nˆ¤=‡KTÕ(è‰ZÔ’l…8êÎö«G³þ9ªÚ5à8Á¢",Ú=ŽøÁsô`¥ÏZnÎ;å–·�IEö×’|p*Ÿ
Ú¢xÂ•PêúÚœ2Ê=6‡æ¸ñ."IcEy��Ð§1ÉE3l#Ã †Y„Ž%fF Á�å•>H¶< ¡«uË“äB¸!iF#“29Aœ!~ÀÃí]	rúu‘v»Ÿæ.Xáß-þ÷û¨ûëR÷�ä¿½î‡ÏË�Ÿ—¯ÔóïA½)Jk-íà·ß¨·yÔKÒÁp�ÿ½^K7Xüï¿Ä0ÚÊÕ_z�Ì6~Q¢vð' Ò°…ki¡×°â¡mâ§²=Z“ä‘ –’‡:ÕIÂ/SX©¢fq„L>’êýZ{ùê”Xušq™2Zv˜_naßÂ]h-i9{¨�±L³¬ø�ÓVÌT­n½¸fÓoÉ°¶n„
•ÏŽ˜•œŸ
<2®$—F°»–§Øp(3ì¶X
ÅÞù¢NÙœùÑñb¤™‹ñ
o1ž‡[â,¶ š#=æ5ù:Û›NyÌkH.Î£!Áe|8oŠ`¬þz(Æ~•Bã–Bªç@0+‡’·dåFt"°¬Í*me0ê°ÒB™9y·ˆ�v‚(‘Bn6Œ´�X÷89Nòaï7Q…×K¬æ@ÈEö’X¯Q÷z‚qFsH…‘îRå/³I€�±A/%(0ñ–$A\õxÎ 	N%T²&•>5Î|"X…äš8ÊÀöÆø¡óè²ÀÐ”`y¦le&ê$�Úµç ×»SKµ�b­ÙYN•XkÎ0îiö¦räÈƒ¤Ÿ7¶=Û‹¼ÖþÂïj¬ÉôfþpCÓ[{GÈ9¢è¨ÿLÇ”²\ª„4©Cc)¶‚ÐœSdT—üÕ
Caý:^
‡Ñ½¢¢#¢/ÊˆtzElX±ó°Í±Z¹8™£œÒt¾æØXsçÙÅ¥užÔ´²ô ºÆIŽÀŠÅX€%ÇtÄîÐJ»Ç‘Rœ?¢‚Qð$W#(j‰7$ƒí«å Ó©#t¸�ôKbL�õþ£ÚÒ¥St?ì-'é£‘WÀf¶íÔ¬]£ž†º¯Á*ÊèDêÍí[eTU&%×,±_ª:…ö»:AZnL/’‰Î‹? ö~{¶ïs!0ÁÕõÁ’5áþ³ò<Â9•óù\�{µ¿:ç«qŒ60?½LL�s–WÍËíE�ÎBŽP³daŽ¥Â€MëDJºåEbPØJc�ãD¦ž(ÂAþ¸}ÜÝ!÷ð"þù:Kÿ6V=RÒZpø‡ï?ã:1V\`¶¥˜<ž«?·Þ0*Ê�,æ%­€{l*Çë%æŠ–Q:Ùqð³xžúúäuÃw¡ûÆò&Ç<kT÷¾•w)0¼õAmŒjôÃº=f�´~lù�p‘3
ê]Œ–°|ÎÑ?z3aòè�&�
Œ?Fö lµ«;¤�šýŽ“˜¶ú4Özaª;½üàzý;9Æ}¬PŠû[�’iM`±:×ƒ¦.×CNJe|¬ŒlÎr1'åøëðÚÓ$FÉH`ÐFM3Î1h±1=a¬)’_�ŠÁ¦m[=3»#‰šÐÁÆÔ^™ìoÊ!ìNœÞ&uÑn‚ýZ¸j½_W²õ•,yÕÉÏv÷ &¿ƒtä<µÑ]¾àÖÚ«N¾tðßîåEîÉ�4­ù­ÍáÙw|€ÝKŠ×Ñë�:b„Í>ì†ÓÒ8pÔ¯‚ÇkA¥µw4:—j±i6|ò1jfœº	VÝ¯fÁ,ƒ[�>X»òhÝ MnLÒfìðz^‹~û[À�'×"HsøËN|{¼x*7vøqS,Œá›:2•2á¥æ(·ƒÑ‹ßlDð¹TWKÓ¤ånO™•Ñp°¶ˆ�ï#¨\KÉáœå$š‡JÓå¿ÉtQ3ºµÇÔ¤Wê¸¹„OªÑÍ;&)h~°’qÜó\¤“F¦Á»Nûk7åç†M	¡I�sçûMY÷¡‡E˜5m‡ÓjpØGµeÂÏÄ�:¼piUrwŽï°å¹ýy”§akÓ1_µ-r,°§ŒQÆ3Ò9ÎïgÚÎº3|xÁÔˆ7ž�?-´�U9ˆ÷ôÑÒ„¶ãÙÚ�±Ë«47rÃƒ¶cOÛÑ–›U‰ð¦]á¢æô–}må›™3d;=Î´)"KŒ k|.ßñ„UMz[J±zq@É7‡§e9.ú‹‹Ñ8Ñ}zY~BE?° 	kô8p~},3`:zýÿøˆaW‡–Ád³—çX9*_Äpfa7³�‡º¬fnF3U×LÆAGÃ`¾€½\ùaú¨O.÷ƒuöâ²Cho%”ã�a]$�wŸÐ?„¢N£ìl@;Ë£$©ñart�\!Ž;	7<  ¤QvF™µƒh‚!7§‚Ç@ÿ�>è!´ÂRn19³ø‚¤¤ÇaB‰»	wƒIž`5†¤øÔ38…dÕ¯ÖRY“àtQ^joîÃŽÓð@%ÍhÜì{¸Ê¯Mxf?X&åª›QÃÄ+•`ÂÏuˆ wõËuuTùÊ¶ª[ýl¥©‹v´_#^=p< ¹÷øï�î$¦l‹§VD9J£c“h’c_·ÁÁtñeOë¦1h7‘^S…%¹ÂÐªžlôC*÷ï>eå»…•fÃ3Ÿ¤A¿ùì5×C ªÑà”¶—Ü¥GªøŠqvã8Æå¬Üžå5Ö™ãŽ_Ùs“k��­ô'úY�öÉu‡É{£‹\ EpË>èk\ÈÆ{Œ�„îÆkl JiÙžZ¢p¬Öqì©Ž¡Û¸2Üþä»ð«�h¥T6÷;£¨Z&8Ð­ªÁL&b]-Š�Z"·’œ²øg6ºžU«,ñØ g'†ÅdÔ	ÆTtþ~RfÜØv2À2ï•ª»Rª¤£ƒT�¢ª}¼ÇJïÉ_bÕ˜`œ9æÍtmò™TxìW§Âj"½ñ¤8Õ*…¯ùÔtïxM0úvµõ	K›¢‰Ô*JŸ¢æA­�Ô×i)ØSœS%-RõÕ×\w5�©™
È0�{ä+—bº-�x_M�óÔ6‰P·¢·UãN±î~ƒøWQãÜ8N7"�49Ÿ M•iEÙ¤”—¨»e%è–O¿ýX«Á�79÷¾?Õ¾?Õ®_ä”ó5Äb"¾ûãíûãíûãí¯}¼]!¿ûsîûsîûsî™sîëÓRš†kPu>$eCízQ.Çö�Y®gNqîb½-ÜÛuÑŠ1–Þ‹éàÐRË�§´uežñíxr4L�`�ˆµ•åG+?/--©S)ËËwåû5Üb”Tå�
�–ÇG`Q“êx~Šÿ‹®æIÉg9E°÷r½ûàÑÏ8:�wž=B¯Ù©Ik=H‡É'b¹ˆ(Ï#P7��E¶;œm@u(Ñó<Áª˜oÂå6X£,ÇêÜ'QŽ†FÇ8ÍwÿI'Ñ1•f?õ‹�àY›cwP+aœat‰Y¤ÃìHÏ
ç™r-˜T>ÌÒ“ìµ 0”azQ©Ê w±‡Sæ+ÄiU„SÖ?M
2Ý6Úm*ØäÄE®	öUÎðæRåx
ºÉä%(d�bg»Ê¥U¹\™eØwŽÁÅEÕªNéNµKAxâç‡mé”°Å�4õD	�âÏ4¬�¿Ÿá¥®ýF•ÆÝ™?>HÒòß×qóŸèêã–U†¤�Æj‰Êl#›$VûÙ(~ä?²P\ÈœÊù3ºõþ�ß³ýÔ‡Qá_,Gcò…Åy6î+Ýï?K&Ñ¾²”ýó<Á´ä53%ŽªAÚ=~g'´§Bž¥	öØÌ/Çe†«€†-~mW>„%6¶†wÜt(Ô<¤OŒŽhX¯è#aÓ‘@R¡r–á‹­ýETÅeÊŸ.Æ¾{°o*$çŒÎkv±õ§OéoVZìí-M8/Ã�]îhÐ3U2’d‚ÒáŸ6Š¾·ì®¶Ö¿·ýâõúþÁÛ­�ÿµ¿µ³XþÙ§ÚX¾bDm¬å˜EÐ˜íGÄ€WKæÍ¯öÃÛcÈ.,ó‰0‹PµGc¦²�0ÕUUppú=üÇ,ÕlÞŽàÅ§Ug$ŠÝ"äÁy÷�ªCUw7Süi�›÷�_‘¼Ó�oz“1V©• °¤þc#©~eMÚÛc/œ­•˜‚%o9µÊß¸®·«^_x»)�Á«,&G$"BRäÀV5,oïÙ+®F©k§¡¨Õ òJê*ô�«%�%R½³Ê9ê¶Ðˆx³j�¦*�ËæŠK(5Éâo¼™qr‚Z§âÂxŠ5ÇÃ]U-˜o›F§{ÿëäa¡ÉŽ2eÝ5{„Ióµ×oPÃ†a«pŽõ£âªX?â\~,ˆOè‚ôq·ÿWºöÝû�3æÏa-çs‡—sœ€m:4§’€©†Ì+PÂf®SÄòßÊBdõùdM*Ÿ[ëu-ÀsDû&¹,ÕA0Å#V—0j'¡�P˜°ÈsT‹daS$Ña”ŸPÅí)R./-ílüýoÿûàÅÆOíJVœLNŽ ŠÐS®¼C×u¥÷÷,‰‚CVCé1: ŽÍZ"ú×L�ª^ð
çEëæ,4™ìÕäàEƒ<+¨ö9Ÿ"­`Yù�H™“#ØhtbÓÈ¯¡Íubu¢‘¸ß8×gHr›9 ÁsÚO+Ù_IDó¯·¹»”7ò�NÍwÓ•»æóûßRº›Sùüþ  Æ‡¾ï'Ñ# 0':"K³n6.f®y9cµKoq´\4ä‘�è @íä©ðãâ1jcw7õ-fna7ÓÍŠÜ­ŠT'.m4e;ûÅƒeÔvjìUâ»ÖJõá³½TÇ1ò�bídêlP
ë9S‹ƒ¬¾ñFuøév%m9<ìï¨9¯†EU…NœçÂj‹¡yi!5Vçw®òÕ\´Æ\k¡6ÍAí[½”×£yâ~:i9ðªioDñ3R¯\’«[rƒûŒ/¡9G·Á9]Ó"’31ã)Ç5‘CD<V_­rX}yãcðŽÞ6ÔyBB](ã£ôRÙzT“6‹¤*Ý\¿i,C¼HRÎPe¡ô(8i;-ëFèËK–7U;SŸãí�"t‡$wª÷<ÌÒÈ`ÜZ¹œ}fJqPÑ–ˆÚ¶ßõ¿ +.ŒóìýI(Ù-�žXr¦Ø\ü’x[K²œ·cvo¬A ²Žl§–…G¨"±£XúÅ°�ŒŠPAŠašoÞì(wVÛÇªr_T�	=>&aSØPë—³™Òò¹>ÐDì6`~ßY~€
Wr%î¯ˆ%Yœ-t+¸<Q9Œ¢©Þ j:„p«ÐÛ¢ Ýý,#ÍÝ€ oÊ0L+V‰6âb DÌL0MFºº©õ¸”Ûá¶QVÀ\ë«’TòKÊ´TuÈŠOÉXNÔqšË×Í5Q_Ðî˜Ò´ë°£"k¯þph�¢Ù{¬ôy‡ïs vRÔR1²—Ø±•GªõÌ·Xß^w�+`–|!¨Ôk”±¬�@•HÕ[ãëtÖ¢¬qº^Øò¡¯äFà¼—ŸXú‰Ð´MÇ?ÑåGìyt.½+xõrt4D„±Ï1&t‰IÝEÍÉ„r5Ð…ZÊü‡ÆBnKÿ<š±]uCJH¦®j€³öãî5ÄØês(!aÖÜ‘'|  �CízãWCÙ·{I™›”‡ u™�¼7–.J²‡°Õaag{g‹ïYÖ'"Ê†Uì›ZKz·f/Û»Ìbœ=î‰‰G\Žé«¶¯r¬ƒTAjÎ;š¼}ìm÷i{Oð“Il†¶¯–äÎN(¬dnË—ñš/÷B±.šgã°M
¸¼xÜ¸ÜU§>ûìžËŸ�@�“#Þ»îœ…”êÊÂ’™ :Š×kf¶à†Ì1°(ƒÁãñnæ‚šÚ.UšÅ³$:=rú p¯º$Ðz¹£�ÿ²¸xÒ	Z[m™Oy�ˆWqÛä¸ÝóÕÝš@¿ÆÛÙ­xré\žlG»k´�Øu{¤¼×^˜‰…¼ÕUÚc2Æµâl0ÁRtZ9`Y-rD?­¨mÒ_Y¡K¹>©û¬”ä»¡4£Ðu"ŒÀ½r#0¥§`M†¤ãù—<G«ŸZg:�ò˜/�êÏíŸ«ŽÞâ´¬$È·ûÙKÉÐL¨S·Çü§Èw3H/–šg—a“,ojlˆ‰‚ˆàÔÑ«Íâ£9ÙëvYÇq|Ó}h®g��AeÃóúˆ¬ÌxÔ!.rÎ×æAÚ=:„ST¡æÁøSÉ5µ¡<5éŸÐ­ëÓû©6Š_Ñöl‚•�Ý$é;O�†2R¾ú	ÿ}ÇaŽµ4ëRìôÚL­5´êˆ3Ï´P%eŸ�ÝbðECùÅ¤à­<—^EæØ¡8jŠqý«+afá·¨¥ÏxôÄI“iÝY‹¥ýà(›Nõz©ó‘â¥²+Uþa�BÚ/;H¡ü‘¾r–¿¡SÃÄ~\s³)X8IO5ýˆŒ`Ã>nÁ€™gc—VÑ©êè��·«*{O©�Žn‰ŸùEk±öGr%¢óMã‡–z$hÿÍðçx�9“NV-ŽYW´Ð€Lk´ÛÏ”*ZàW(ÍJsUFÕ´# ã|wal]Sî’£~Ú1gvˆ¹/Ri�#‰š[b�IÚAô@Æ™AœRÿ O�—ò¦Ù o=uuPŒh1c[*¶ÿi]¿z,/&÷üb.H×¶ï8¯ïºS1»­9«ŽðµNÈ¬“í\º.ƒ†gû|†<šêºÈ/¤áëCàùWÉ¬™oÍVy*ÅÓQŠÃ±ã×.…*œcÖ1SÉªk—é\èª×ŠEµŸ½‰ÉäâKQšòÕ¶[¶ºòGKË3œ¸8Œ�‰œK,Nn‰dÑÒ‰ûÇ~p@‰ý$Ç.‚ÐUQ;|¸Î®KÆe€ÊÐö0n‹3’,Û�ÌÖc÷+S“‡*2«ë¦MM¬©í\Ÿj3kB)Dzµ 	ÝBvÒ>¾Ï÷^C%Ós�fÈ)ú‡]Ý?˜eÖHÓÌ²†NSm²*»›n—UÛ7e™WM3§I³½f}±Ébó>eÆ™nºUù�k¼õ-Ë1å¤6÷;™sŒ­|ÇØ@k¡l,cûkRE:Ö¸_á2ÿb+æ°ÈŽË®¾£¥æ¨ããØ¾Í»Wn:væåkvÒ3ÄP%§Fã!æM/+Á;d­ÛJäM+d»£\ÔÈ«#95
ëÃ«]ŒáÓ:Dž­FÛq	�jXÑ�%ß¤@¨Çq§4B¬F ÷Øü”b¥ç åÏ†°Ÿ‰sVöPžDóHÍ¯©ªàÜ_}•pªÎ¿™ÌQ/�j£wúŸIc_©*KŒñÈ8tâçv–—û¸{89ØµfÉ¾+Ü¾º¼´´DƒK ÐfÊBVOèíŒêáN4ÄËE¸âÂ‘ÕA‡çƒæ\‡]î„|W)‰2HèP?
å@]«8Eß©b½`'º0³ÆDN¨}°g)Œ%qÔƒBá¡•×#C2ðšÒÉ¶yýj€>óKŽ›Í'uÅ›PÔeÇ´÷-ûdÎÐfä”òÖªO&ÐÃZ§«˜¦W� •F·ÔÙ¹Âœ÷ÄiG°?i—0ƒZp¾çoÂ\¸‚ª×õ …êß_°“©Y'bT*»½èý‡@½Oþë²Œpq'zÑ5—½’ìñ7!v–ôün™ý™~˜~ý-XÓeÅLWÜŒåIÀy‘`b/qžF¯64É?q¼	 <¬› x»õ	‘�˜{å�¢}'qõŠ`Ãé’çÚXþ{Qä‰¢±Š+pBîOÔüªÅ!Vø„F¨Ú^ú-wD…ÎÊ@�.�ž¡ãz’á'C@:Ìøa’†~xoõ3
•åô–)M#~;,åd¼…ù0¢uí„ùUÝlÈˆùjW‡Hì,I/õÎŽ„4ÚÏƒl4ÊÒ]ùÛreªo\òûÐ2�\`….aˆœíy†³úA$½àn|RìçLÙ£~¥kUAŽ`ízRŒ‡Ñ¥Ñ Uöã/uTê©¸(wä­Ò´ž¡;æ{÷§šK7Xþð´GF~ðô©öbXþÀ¶Ã·‘%%¼ÜOQa½Œ
ëéà’ÿ™ù°©”l¹B²jŒ	ŸÂfMf¾lå–éË¦�Èê:™DNí½É<§ÍU»­"‘l”S>½!>¢x4ÓÌw<6c“�Á¾»uk¸×X"*Ðk\Yüà“º¸Ü$�ÐúºT�²ÃIïÝè¸üªâøÝƒ¹òƒ{�üO%�oEöšám¼Þ§Âªu3¬bÝT‰Ö�›–i‘Ë?±,¯�é4d~/!¿Y³Ï__Þ+Þu/ñg—ø³Iù
Úá_ÙúZpóÞþN*B“…oÊ²s½àm|×ûÌ'OU¡t™`‚a"£âšnMøÒ3.½#Óâ›^€{ûšÄ¶\ûŽ[ŠyŠ:Á„~ûû+kïF™¿¸ïÌiã®Àµ®h°¥{û\ëñì­b¥åä‚UY“zDCÞïäÝ[·;ÜµñQIûT}~íSœ~Í-í"S®p$UïÿÎøÆ¼öÌ€o^2¼f›²©š-³Ê•¡…¢"ô*g:= ”‘
¯i¸å¢"_\1Ä¢¯ª2«^¶Út
aõucv‰nÊ¢ìþá%Â!Ô?A‹ÙÈëèµ±Kt—·Ñy½y¢[ÈÏúkòö�QuÆ>2JâCßùYû¦=jè^›mž{]#k�ƒåš§|8¶„:È\×“¬ç'nQ‹�E&æ�&Å4ÀÂ¯ò\€‚¼LçÈôÑÛÈO–æœæ¶<òy®ÃI(™~ÀÇõ©	cîÌ_‰âŠ¿Ô$®dþömŠ«JÁ‡›èÊ)ÍES8–B“58›K8ŽÆ·RÂÑ+‡¨J¦|½’ˆTR5Ÿ©†ä?S�C^ö¥	ŽÖk]v®>s­ZœînKÊ-¨2‹s¦uÎ4«›Õl8¬­Ù@$I%dÂï?ëé^qý’öÔ’N×©«ÓÃ!aS-`•¼Ä�=£–UÜôÀEïðX1J[¶[\:Çl’PÄ^0tø9“t½M±pÇ‰™ŠœüQ½çÄã”¬¸¢{úà�‘™"~zÞÜ%®«Ô0C?ÍÒä%’Ä�Ê—«z Žv.‚“¬uâ\dVË¿§|Í9Ó0=•*Ûu³dBÁqå�VO³;Ì’¸ª¬Ñ4‡ˆÒM™ö‹ÉÑ()KEõèãB,DÁùH²vTò�3Ú`?
ºËéG‰Û<1P;/‘ã÷ð¨‹èK›õ,¿![kŽ A»;çÌñj oÝ×³)?1‡¯§¡ÇíÚV_8�mucÅ+â=²*â�š©ëa	¡`ýõ3Â+Uk�ÂÊd¬²¾‰Úþ§SçÝD²+(dJš³"Ì.Þ�ïæOØm ¾Â.™Ü®rg„B¡³�«ù,T™XV+zì7E6a˜v‚„FJ Í¶IñÖ«áû„ŽIªÅ0§Úª¸è7¸w#‰¹ªK&u4g’úÌõ¶êh½üw�õƒlÈŒˆ>6V3c>J�BE“ÉâÊ8«\ÑÊÅÝô–ËÇóÇÀåÞoö{–ÛˆÎ‚I9´õMÇIzkEÚ6ðfd”‘Ñ˜X0Yªd‚a–}šŒ=4ß¸Ôî´âv¢ñcnÞkìrŒÇJpÚï—><a³ÁŠU-Æ�×LÏýD¯ XØôØ¬Á‘obßm¾ÀTnêšÿ	d²¦³OK*·•;Óá'þE·aX]íë¶f*¿XE„ùQ�Dª*Vlï�f�„ÓwP~Qú,¬;€mš¸ùMÀ I%©ˆU®¨5è´I¿^í4¸þMeÆ^Ø|SÌËˆEY¢P):£ý+F”«Pk–è´A®©�»p{)‡Ò@´ßÍ“w8¥ç­„ñ?r]öáôò\ò@º)çÐ»vv®Ò0&yÐlè]Tƒ©\?Û÷¬É//S¯?{Šãñ0“÷	­‡Ú_‚×;äÃ«*Ç1©’k?U9_³¹CGôëÇúÙðCÒŸ“´;Î³“¯yò­N¿ˆá­Åÿal”¾º  Ôå Ê»	Ùóö¦˜)tÏïtÁwË|€´s†ÕIÔ¹>²Pk§9¦Ý ^],Ügs¹†Dá<¿èq¯ê-'¹4U•dJL›�ÿÖSö£ÔL—ÿ•ÚH.EÁ¤|LjB¸&léªm2—„Oz.?t1ÞIÏúYi±qé´ØÐ ÛõË­[9àõB¡¾¿¬Oùö\uÑ]Æò- |Yãë¨»õc}›~+×ÃôáFâ•E5;æ.í¦ùèŽ&RKV¬‘¨p=i=b �¹pè[$µé±>ÙX–0BÉÅ™»ÐrÈÂ˜IµÑ¿{"üöˆÐõåÎâ“µ*µ³sªSôµfeªF¡ÕªÚ8¬ž:Ž­‹ÏUßñ	+(ñ	ûMÔ3ËâWÍ-ûql"Ä«z-BÊÆ<½Seãÿuü>nç¥Ö)xœ­W}lç—��‚Ð�„¤¡!oÁvpÎd)ì�“ B>ŠbÓT.w¯í+ö�{w.šeÛ
ÍÓ–	!Á6åP5‰�Ön� kÚ?¶idëÚUªºvkÿè¤JðÏ´IlÝóÞÙŽãØ°ª�’øüÞóýþžçý½µìGWÊÎ…Òz*AÉ1²SŠQÂkæç€¬KzŠL�°ªÄ‰“ã¼Ö¯¨ÄyIöRöZ¢š7Œ²N¿CŠ'UGãI)&ŽÒÃ¦­ÿK»=\Éº¢¦ lß÷Âcõ�Sùé<‹‚6…CT£	E“˜ ×„hR>DÅ ~ç#”­0[eâÉ˜.%x5OæÊªá/ö•vQÃ~r˜YŠQ^N&B4ž`A3Åþ��}{‡CvíÛ3À©¬%U:pDÇI‘ÙR„êªª¨#TÓøü¤²Ž–=¼œhºJùxHa6!ZÖ´LÇ‡vkþ]6
{Ê»à¥ò Ü)£¢ÍøMegs}až\LÒt—Ûýì!£Öõ‡²ÚEaIá8× K<×ë½Ð¡N�9Ï´­¸ªqœ;c/iuÞë¨{xa]9AIÊz 0uàËŽ[‹ÝŠ4Fu
sž¶©¦eÆƒ�?/á]¼7?Ó°C‘E„"ó1²‹ò"U5üŒ%¨
¶-ÕÐ³eéh°³]Pâ	^—ÆX*ÕŠ¬QVTÒ¹é2ªèdD¥°DEÎAÚ¼ßëDVôìòXVg;qÁñ¥µÃ:ñ±­’äˆ‰)NCØùkšt”R:Õ|DNÆÇ©Êc¼–3ê#ýXa�ÞXÖ
·¶W@ko´hÙ@Ñ:f?NŒQ£·³>súržPm{wÓ·rNÜ¸ toƒò>’žp3í	·ßáð¶µaFäx¤ãÛd�J…lÉN7mA!y¯­·ùõ@«Ñ\Þhû)ðsË—ÏKt;ÜÙ¶6Ÿk€w»vz™¼•º¶PiPd�y!ïˆRá�FxÊ_íu†ÛGx]ˆz>�*2Íûš­g{P’ÖƒmPÕS“v^Ñ´žTe�ð,ìg*’\hR˜èQJr¹1·’Æö�hX*�õ¬ÑA9‹$ºBœ¦Fœ¤fi�ZÕbÚ½Yw}$Â å5Ë›sx8JeŒd¾Öa¬¦Và‚m¸$'iþ¬yHÏ�Á±÷Ì†4ë3Ÿ9ïcÑo®.B£µÌwñZnÉ3„b½´F€Ø§[HÅwyBYXð
Â6.¥×Öärµáö“cpÙ~Šz½$»�°¥ëPGi\™ òX¸p›^L^ð×Ã‹gë@Û¾ûñÅÝé²pDÌþ1Sãríb}ÍUÁ³ ó\X!N=qgè´ŸªaUÏZØÝ÷×ÀWÈš•	Ì–µ9�Þ°Ö¨ßr¹ÔL31û÷ÕV-˜½æÈ¾ä³ÙìXÇÿtwlÍ�îñ°C,•@™+j}Z9z Ã_»=/Hì"W�-wŠ™ Þú�ÏKì+Âœq»?êxÀb°}ïlšY>ê­+r³ÃªÞ®5<{&íiÜ34¬ÄžF‚ˆc¢dGÿ(Ù;6¬1ßBRUq‚ÆRÄ…5 	Šÿd�À&ÑPIsM±¶Ÿ×4GáB^Rl"<ÛAT<ºÄv„‚HØ‘X¢„É(6éTMªÕ¸DR‹ºÌÄ9Žsñ‡yIÇÑªÄ%�rèÄ•é,@�‹ó	¯¥d�MPKÆ:Å”€wÆ«óÊw,£›¦j`kp<ŒÀ/‚­1VÀ'ÁºF(þŒ”5üq”º­�j|<[þU�—|ŸÁþý­¬t1ìøx(ÀK{˜)"á7æøýå¶ØÞg"ZšñÍ1”ãñPš©Ÿ†y”3èÚ+ÉúÖ>UåSÝþùÊ'XØÑLÒƒä“��I�Žˆ›}DW“ØÑØ¨aéˆ�8½z<aÒMQU�Šu¶;ÍÃÞŸ®†Æï×¯Æl%*l¼™øtiÑòºŒ?=Y¹r!åà±×«1H£qò®-€X�*$Uæ”¶À•Ï ]YÜ$’ã1IÀÁÍÁ:¸zª6W€2å€¿�rÁC³!0[7o¬ãÄÁÅÞ¡q²>}®¶N¶@óé–JaÆó§{*Jàêý|\dø\ÿá?Ë¿©,ú§š2Yxá"þfká£)w§Z¡	Z! NˆàçºÉ:¸ëà·°¶ÁÎ!šòOn|`Y@Óéõö	7œ�[m(/zKñYl”µEÃÖ”°ÞÏ(«1q~Wií—áöå�Æ¹{})0q~�1sñùÒCýeãöå_Ùª0žé«CÆê´­Â©ÂªR†q§)ÞZ¾x…÷ô/{I0ÊãU§/#!$L/&(B”ñ'© sŒo˜×¹`gNÛœíÀ!šÂ¶k–hñõå³þ"tØï˜˜'v#|‚Ñ1söRë¦ˆ¬§0&è}i5|6R—6o�åÊå¼�jfsXÒ£,`�Iz²Ç5‹”Ä3¡zLn8282@XÂùT« ‚V\Ãš¿Ýº}«ÔM\™:ù2T€a�ôô§©yxwS¥U;xb|�WþZêÎÞà\¨–Á°ª“ÿšþ¼Ô�ÇÒ£íÊî¢80ÒW\Æ¯<{/²ð�k£ÅŽÙÌðÂ5¾bM±÷Ì@ 5Ýn4Ú^k)«0{†^wÂÉ'¹f<M¥ˆ¬¨”ˆI“7Rvá5¹¢EnŒôëPV_Ì2ž±Ôhº±·ü^~�Ó3i[)~`T½}ª>üý…m_oÒ¼öÎúÌ¤Yß�]	¶Ù†ÌÌôÁÑÙMðÇ�]ÑùX��-c‚¹´ Ø8m…n6À®ÉUlœ½¹Ž›pgo?ÙñXt;ðˆb·cãã›oÝgÖ€|k®haM˜ç>(»Ï†þ®( øqEÕ]p÷Z�7–àDÄÆßë·ßÇÜüú­�¥Sc6ýÁÛÿ ç�ü¾Ðxœí[ér·þÏ§€]ŽwF^Î’:è„%‹:"ºL‰Å#®”Ë6‡;Ø]Xse‘jSyˆ<až$} ÌÌ.IV~D*WI‹ô×ÇÀ*É³¢WâH†QxK±“"KÄ Í"¹]V…“ÁÎš2ýÒ0ÍTÔô¢Ÿn‡óZÅÑKyñ\9Â‚`ÄÿEYªt$ÓJUJ–£	ôZŸ„ã*+æ®”q–NÔ´?^¦oZÝfuúZFÇ0<œÊ¡@qG2ÏJ…‡¢’±œar,‹7j¼D•NŠVY�«º�£H5Òãl:•Ew@9�ø#�ä¥«P"«B�Ë'YK\TF-@wìIX¼ƒ§yœ…Ñ‘,ó,-åšÇ39~�»z¬þÁ±Ó:?‘IŽÍÔ”%y]Éa9ÃŸ2-aUÏ.+ø‡ÊRjº¬
Øî•È“yNƒ¦²zVYq Ëw‘›P$÷Xµ„ºR1[ÐÝ†ÌI†j­\=­ Ëº…ØÚèÎqXÈ{ˆšÖ�û	¸¬Bq.'Y!EYŸ'ªÄÅˆ*ƒ¹Èî�¸3ZSi%@’´2xiKq¦Ã°šm£z*�î@*Žå¶•°µ{óJ–Û"­“sYpã4{õd"‹mÁï¬-Hç5qG„—*©ÆqvÚ£á©Tœ£0_€îÖ–rë¾øþøÕK½¾ ;¿KŠ‹X%ª)°Ââ`?ëR2gh,ÂFüóÞ½? ˜ì�,fàÅ"L#‡ÅTŠ<œ³TX!¸�¨f’gƒý)e�³�ÖÀÕÊŠš==üáÕã§¿þ°°òëÞßNž‹]TáŽØÜ¸{_ÿ¥-õ’öGd0$mh­¢@p½I8Uãul£fE$+ð0`ÐL~¼ÿ——�ON�žÙI7·vìæò*„uSŽÇ2Ç:Ÿ7kÒˆ‘i”g ^\ƒŠïË,eDy\Éíê:í*ÈD\Âœ—å¨(u..T5RûéÑ¾È9Q—>-‚±ôh[@0J³‹tG~¥‡³È4L¤Óý%ü|Ô`oá,lSZ¸´õâîCáh†MyŽÀ­0
¢$Ö7=¦M+Åy+9Gœi8±Ì’psôü‰¸{ïOßZ9¥ðd0%œžaÛ¶»ÊQ:óðag¾™ä;@V˜°®ëâdÖ™dÈûèÀ¿³ºêo)Mõ]!ÁÐiIŠðÂHÑ€&Uiµ&Øœƒýƒg¢‚påbšPÎÖ¥l´+<TÌlº¿�éŠ>?Å•[ˆDGÓal÷!ÁEM„÷ÊÊ*,ªòGX…7 }ø>u‚uïÈÆACGò ÌóX�C4ã(ƒ5T&Šâð€á—‘„ÕxÊÓ¼ôÃýBsz?ý²óó7¾±„|ã5òq¸Ö�z“R�:úÐ—ŸîþìêÄm›?Ãä8¤»=ï¶„EãÀÏ8ç”d²'¸dZ­ÿ Ó)@#„‘7a\ƒ_#ì9ðv UÈ¿k<)p‡�òâääÛkYV}ÐŒyN4�	ÅCÄÞYZÇñ™¸˜I‰ZU
Ê-é¥Ù?0@¥ ŸŠ\lA†Ô+Ñp�2ÛPH‘F¾É â­Àé‚x4/wÕxö2 ‰Þ@«½ÎjÈ–:á»¢Ä�!ôäqPè÷~Zy4jaÛ…î¤Êç*…<ãñh_|ýµôpWl bô¯m=�cÑ'X‹”¸ƒ°m¢¶„—hUÆXÊˆÎ¥\�I=Ñi’Ò#ø0Š2FS!±
�®ypÝªQ&ËÃcqóž0…Tct£/X½Ê2N˜]t´p€ƒHq�Ñ!•Õ)¬ÒƒQ:ôû.F
ùD§W�—q\¤‘±)V»eXw`ÖE
³8iwþbw—‚iÛ_j³@<¹Ôzì�áö¸\iÅ1£\ðdPïŠ­ÞäÜn!øþ�ª@ÈöPEáÞ/|ÐZðuðtLq½?©ãJ�™+q†1û‹ �ê7(õŠ9g¨þ"U¾á/.zµÛ*3Ç/¯ß}°%fPÝQªÐE¯É‘M1ù €%lnéBÍ í)ò†Ë˜ž†PÝ*®¾‹UúºUã.³ªš¯N�½5eçŒÀéf86µ'ŒêÖ“œû¡ÃNã'ÜY˜Ã·€ÚúCÊ“ºŽSÂPª1»ž2©± È;5x$Ë1”•ÎÚ´­CÒfÈ‰+ýªkGJ•âŠ8ÅBühÎ®Á*ræ®úÌõÖ˜(‰]ÈótløsNEÎpnÁ›�r¨Rþ¹Í¶½
¢Xå^„ªjYÔÓ	[<ãò…hÏGd·,µpã*‹láúÇBa7°™J×™ )z‡ìá:\$�bôÅRºO(ö€¹â‘
)0mc>Çœj®²›üA#Ôˆ˜°?Q¤:�+AwØÕuM3£ÆÓä%h>]|WƒÉµ÷îk´Ìàmâ5ìQ·ÛÝÐ@�~6ª’œÈkTdùúWW|’áù‹34i&á(ÈøØ«Ó€”ôŒŒ¡³‹„‚¦ð[Ó®gzêßFkóÛ!›�¨ é¦_‡:½�‡EÎ½�a—Eù<ž«I1¦"Ò£Ðá·–Ö9F°Ô‹"wæ�ÃaÜ�÷pÿX¶Âöðš¬	LÏå8/órÿµ0Ñ!7G»!ÛFÂÕÝ@gŠ& X7qÆ¥è°I”0\Ñè{×-äR€öÚV¯È#MÁh»ß  {„ú‚µ´cØ:Ž€'ú˜I«¤iˆxˆ¿bÝ‰Ä‰~y©¤[�Ð‡°+ÄÓ§ZXŒ`5Kt4CùI|@N&¤¡VÚ¯lÕ„AâÝê%Ãm+`Jï:2;i¦7¡Ã×¸¾=#�ázÁÖ¿;¥�ñcSuXn<’Å"R�—ØŽµ´iô4xuW&æ#WÚtnÕë†¬\ìjÇóú#ðûÀG¢¢#�xû–cì-=éXfJOP?n%º„¦Î}`FsX>‰_¾X*ÇÒÊ²—¯4/²7
LÒ-Å66t¾ÒÅOÏÉ�y›šðF%ž÷²¼(Á~euhUÒ„Æd�v~n
�Éè5
ï¸òä¥*1 èc –Ù>,&*�öæ =3}`B¶Ö÷ËÕìÊòPjåtbêŠ]\rðÛšÎ®ë�Ó"öÝ½¼Û7¯®tÂsÚk0B!â:‡ÁÞXÙát%»w}æÊz¦¥4ŒŸëŸÍAžžtO¢='%RoýÛnX'ç™ïÎZ¸©çÀz™Î¶g¶5ô]l®{Ž:E­pÿ#ÝºÎYßu¾º2³.ØÎnåÒ}õß67"O0ÇØB¶Yï¤‰ê-i_¶xø.±Ã~zâ
-=#Ë=wïíÊÐvÂ¬`ŒrHg€°‘*€"A]ÖóÅGA	¸”Xñln1TYÝÁâ¬º
–¶CÒÂQ§[€–äö@½[Å~´-6LóÂšýw‹®•Þ56ŒFâ¸U[ˆÿüëß‚w½U�è;–væ½TÃeP�«¶@÷:É4^ŒË›‹Æ o†<¬«'Ë·Ã20¿) —X²mŽvØÏës€Ê>¦SÞ·¾�a‚ªƒòNVà.Fy÷rÓ�¹™gè Ÿwƒü`ÑÚ³ ýuù¨ÓTAF»~¬éã`˜7ûÉ,¬p¨ÆD«µ×Yßûõ§ê~ÿßx—Öb/¿–)è80;0¸ÁÝ+¾^iÜ´œÛ;ëê3²e¾ê@í–®ÚælörÉoUÁÆyw»·¸šé±Ïómu@MÞÀ–âöÊÛ¨…žÊH]´‰íŠÄe{»ëxÐ¬c)cL…[_ @—/:§\C½�ä/PLçjŒkñH&:¬ìòK}¼b/þ×|J§"ÜŠ°y\ë$kGñbÖµ' D/CÒI‡@­“šîPÃpïŽz3?}àòÓwæ¦[7qÓoß‹›þñwâ¦Öx™–âð�ÅH¯ôYÃ#5Ù£
ØkX*¹�O,²w+½Œ¢Ñ€ÒO<Ò0Þ7�2v¶ÏYlqPãýG:Žk@ù—(þKá±3ø„sËÌqM_"ƒs·*Qóaq¯ÝKD—�à6uîl‰çµŠ|ã‰Qs
»+ðôygÞ¼fž@_þ�úb$î;,NkºDØÃ•O!Þ¾½nÔj*Êùå¶¶²ÿÚjNƒvX+ßŒìxx¼üT<—âTA€*�,ú~–¥ÓHm}*I¥�kïÿhŸ·ÐõÌ®ûfÉ³²>ˆ¾ÏnÃØ?!75ÍQxÑ¡¦.Ÿµqc§m‰Á0àdÐ�x$Q6®È±ívÞYë©w'ì–©7öºÎ{›¶Ü³íe|úSðãçK½ÏY¦ÆÓÿ/¿FC|f×ŸÙõgvý™]f××³ëÖ[ÔOG¬ð}¯å[Ì�˜âØËÿ§ªÌqÍüªE*ût«¹øÓÌÕŒ¤B3Âg	Î‹)zÿ¥Ÿ©Q¹ÀØ†z1ô.™Ø}‘å…B~ÉïU&ôJª¬s|(.éí—á´Hpì#¯# É=(ÎÓ<Ç×òxÀªEbz K›áË¤Nùañ½{'B‰fä0¹”—ôÞå˜ïË/ÍS0Ì‰":™Ëy¼ ­|ËÙ¥¡zSuÑ¸ò…!îù€Ó{ëF'kž«é=Ø]ñ�Í><ãª¨7Ð¾Vì}qj)GÛ@SÑÒ,!.ƒ~¡¸ôfÙÑj!dÌïWLÓ=‚Z9GsLàŠ¿&Hi!ó>m�ß‚ýj¢†Ðêê×0±¥í6‘­û¨ùE·öµùûD¿ÓOø8/Æóë—Äº ëx=Ç�ˆ`«	¦ÿˆxJr·¬xœÝérÛÆù¿žb3ã	A‡()ñLèÈ®%Ë±ÇÖèH§ã8,ID €à°Ä(œéCô	û$ýŽ]`q�’Ü¸MšñDäî·»ß}í2X$qš‹á¥ÒÍå‰týÓ>.ÄJLÓx!zQìËñ4ë=Þ
4läFqàWôÕðâhÌJ Ûvø�;AäËë<¾_x—2_†²ú6hþ¼¿|í.ä@„A–óHÖ>ÂŸ8ž3Ø€R©Uo&?K/Ï0Š›¿B¹¿ä©g‘ÿ�\â¢ ÷—Ç©œ×8œÅÓü9á‡Ë[]çOa&Êë¼Â Œg3™6a³¹›JßáÉ®„Ò�ŠäL.:LÈ(+Ryx�Ã‡ Žb&óÃ4�Óïe–¹3Ùæ†:¡Èƒ0#¼L!¾Ì‹èRúLí‰Ì’8Ê$›Ç)Ñy�Í³Ô]lÖA7áñVC\	™ÔLâü8ŽÃ6ŽA4MÝ,O/êœ\�äÀ‚a+`“-çáÃ-ñPœÄE.EâÂ4H Í09u½\ú¼i>—âüä@äs8[ù2‘¼ìWebQ!Y?�iÍ‹K¹,¿‰•qž”¢L¸"+<˜<-Bñ×Ó7¯…fž‚�!¤„ïæ®Š3ÀÁ†™LTÉ�„8à/m8ËÝ¼È üåÙÙ±þæ�¹	Ë—S·s±3õyIªPyÖ>ßÙ»ÊrñsG@Ÿ…HŒ"Š¯@IÔÆ{´Ù¸\)öžT»àRZ7 ö¨«~#èt‰J·Ž<ÉLàÏVÏ;Ñ~/jIù‘d:GKP“<Q±˜Èt#Ñ7
ÇU‹rGüëŸÿhÿì)Àý$"ðA�`%ë^�#Æ…¡R»Lä1ØíÜÁU�ÏQgƒTÄd|ì¥²’¡6¤š
½PmIÎŠy#¯ÉÜ˜Es7òCùªr—?l³ÜlyÂ®ƒÍ™üFsä	²‡�%.×øÂ’+7ÈM¿k‡4`…ªª¡Ô¦6PlŒÐÛØ7±ÔÙ“>ždÝÐ¼�?;ðê{æ�#øW�±«òŸå8Q~±óøèôÍ)‰ÝêkXfæòr¬°j»OTKV}ú@D1Ï…R…üXS‰ªa8ˆBí"’W
XKï0|ŸfI!Ib§7 áÍ¥w™áˆð‹$<Z0‘0 ™f ¬a`©üE™WyÀlðYdE0^Hi‡²Tô9(§Ü"¼@.:�¬­]ŠÍwÀÑ�Ø\Ö*Û�ÉMmÜÐ
	Á;hbfÍ²ƒµl«ý>lGùÇBÖü5¨ÐTXŸ‘ƒ%ÿö›øÌùé­;üu4üú�úkßÝlm¯ôøÇnçV¹`�4”€|ŒÕ;Š> <M9Úâ¸ÆW2õ\ˆ˜bwøh$ê¦Ù@D1x`b”’ìÄ—à|ÑUI¤¼{B´å43/M¢^ÓïÀPù%7 û4£“¿6¬Uá¯Ï43¯ÆyuC eýCÕ&ÊÄrp ;£íºyp¦„æ¡Ž�,™sJ¡)¡©£M¶@|_$ùR›å…�÷4Ìáýmö`Q‘I¿oÆŸ„³�a;¥0Œ¥a·›­„‡Ö‹ëT²p'ƒyn$»¦Á€ Þ›Fƒþ‰‘›ùÌ`ë®~|½šñ¶6ƒ}Ö¯Ì‰6¨ZÃnàX}Ò²/-#w[)Y‡óÕªTÇKžˆÑ†A#ðLÒ
C³ù³lè¢©®ÏJFc‰§¬Üúº Ïxß3èsÊÈ¡QeyØ„Š
Gz‡jƒ:Ÿ¸³ rQËûãõácº4´ùÓª|(Törk¢¤¿.0º4ü¥ßå™EÂ�ô¡Þ@"l1@	wíLº©7grlÈý¬O÷ú¨z½jèp°@(kW–j±c¬^¸×P½bÂöš_(Ï xË­î½ ~¥O¦¶Ú�F@éö¨fÈ¸´‚4ò,¾”ÑZÄLÈaŽ ¼mT„¡Á¸mÅ#‰E¼™b6ªn3‰,sÄ¤,ÈEÅ3þª˜À_Zè—icdgiy”åìiÌìPF3Ðì'z/C>A–„îR¡h,É u“Öh —°®Ô\ÎV•š‚†×w¢äØŠëi1ˆf,b;ÛU-	ükø×{c[Ös‹`!Ï ÞÅ9ýYÏeÁ¯r	±x¬tÃ‚�õXßÜ]ï ?ë9™»3=þÒÍæz<„ÚýûØ¦�ôÇjè¬rtðWPWEžŒ§â9•0B<5áê¹¼5&;Ã¥–ÞïÎý}¨yÃØõÏÓp,.ÜpËÉž@‚C+gê<¸‰í¤˜€ÀŽüÕE­ ¥Nò7C=x ’×PW4tj\S£§Ñ¾mHZ)×Pl¿{Ê²EÁHð„Fyqž )˜i›ÇFù›œˆgÇGâ‡í2X<ƒ¨–€f. X	Àüs‹�!õ%T]p�¼ YËÐ§˜'HŠŠÐ85ñq%Gyj«×ñéƒlç"™k3“ÅÐÈ´þ|‰Vy&Š÷9JWŸŠ±Hše:J€ô;mj	òv„	añdÑ'Ãr±¸¡<zóI|é×*žÐ/,ëê8À`�
;®ç(hà^TVTøç�õ» ó=qáä‹„Ú™~'Ã+9>¸á.´Õ_]Tk®RK÷‹ˆ—¥wéÛ<Wod¸>AÓéµÁ­>"yB3&ì< Á¢kÂÝÒe’Ç/iØêeswç«G=cÁéÑ·¯Ÿ��Ÿ¾ßÿûÙá)¬Ü~TÍfÁ²>¨…¸¿:†-§S™¾}poß!ä¶ym˜U3¿šƒ0šCÈÂºš£`,Ìsµüª`ìÇ®PÚblèÒkã&šœ!(‚÷Å&.×v¢Æ/1ñ¶±kÑ®åò
Å/öÜžÀ×Wä5³Õ.lœXUnÀc¹5¦µs¾ir¼¢½’÷B9ž½–x†f?n¬…ÙE2Ó’7MÝ%æ åÆýjYCÂ�²¹¥wé‚+y¥�:ØµÚªþ¯Ø
‹l% ysÕÕÔ7¡tñÔ¸k¨ì„�òy_q%Ë{ƒosÃpYú¥/°K&_ÅÞ¥Õ4}”,0K	ØfØxéÍåµi$ñ¤F•>Á4Pc5˜8hpËØéÆÈ”×:Y*Ó$Ò÷Æ•
'›à‘lˆÙ èôúv'gÈä.9h Êƒä8èÊ¡Ý„z°¯{¹Ì‡ìSz*	­Šˆ˜¿c]8\d»àÕŒË
¾"f©„t’MçGÇ™Dï}�|Þ–6mfOÔ5œ�›w7§eÒ§d§ê
Ž’.ÃJ7°öþ§LQµšèì«MI;q­‹£‘»¶òÕvjÚóc¯XÈ(ïé9…|zä�ÅH2çŽjm+½9f]c³Îþîa
fN¯ö«Ëe•©3ã6%:•žIôÝòVSH­ÈÐvFÛü±i„x¯ÜÔ?©wìÕÍœ­fÏâS¾½còšÃ?TÆ¼Fäu1ÕÔ]aŽÑSmÓpýÉ€¤��éÏ©7wj'¾Q´jW®|wÊ‡¨nÐÄ†ìE`µIß¦�¤ÕWãÁøj+!}­©ñÊ®Ï¶WœGd�›×iå@˜És—”TÉ»6ZTw¼í#šó•+ZkoukkhèzKë°3]šê¼Öòšv§0ßw*ðžæVO—_ÜE’Ý0Sè7Ae]Ø×úÝS+S56øV‡}ßÂ-k6­øNÖ[¬¶Te§kzæeÍGWÛUÕ‡OT³³Ù#ü¯µÂ¹ndô0&Ý³3~K7¼³FûSvÃU­=©zßºßiïÑ£Vz¢T.VœÂB?H©'•Çýuº£µå,P/5ªnÜ¤BŸž:�›Gii	Ú´"•³"tS¡SÜ©\dYA=»£�ìV âèâàùkì—þAµ¶Dˆm3a@×9Æû‡õJ¬$óÿ¢ÆÍBubûéÔ&å.;]§«–ÎÓq¡ªÉÍ€!öööDO)r¯žÏ¦n4“ôØ'çPAåÚÃ.Ï¯jÉÜ†wPWÞk»_0²õz*k\ÕÙsMãVb5“ù5ÇtÔ³‰Z‚Uðœ®.æyždcÇq“ \cÇéŒzø@êÁ�>Ô†oï©í¾rŒQüðŸC©AŒÑ±¤�š�åë—1šð@Ì©¤ƒo7ê9*bÆ5ôV­·q´ã°]„jê^‚„õÛ— "ÍÐ4Ñ¸p?Àr
rŽ†Ïª¿´ MLâÔ‡?´ÀÓOÛi—C/ÑÀÍQÔÍp'‚‰‹ÔÓ­ÃÿôyE–H/˜.)¾Ñ¾¸Yí?ô;¾Ä›{æ^ºÐ âbNT4ßÛÏ)’îß?õâd©ž·îöÆ´}Ûïq‘·ñÙ§ŒDåwÆ{=6ddâ~ãY_9Ñ_UOêS—›Rë“†Ô²õÏj�.}N%é	XÉšh…(ºi ¶<Nù_@¡î»�ö�¹Ïß<zýâ:zFg¸v6²©‚ü¸`Â¾¸CH1�¦ÀRZ[ª-»P:5m~} ©6¹S¸©glêbM³‘eéPÂ†+ØÎ|Egjzòç(’fÝ½B^³hcyÜ–Êæ¹¿¶>6@ë7ªÕ}ª²¦t®Ÿ¸® nUí³±²n¬zÙQhWÆØ,¶Moö?ªºE�¶ÛqÃŽÚ‹6�6ˆ7Ýg#ùâ¡¨nEË��bJuœ?ÆWüèL`†DŽbë<“]äb�¹’q“$�“4 kì‡*¦bZDÅo|Ç©"°QkÄ‰L)¾g÷K56ðòÅû‚pô56ÄÁ¥­�ßÀ gIðñ¯3o}ûÌŒ8¸áSýµj\ÿô#f´?:¶�dÒµ¥)=«(×mx~(eíCF-ÝÈXÄ2ƒUˆ
i\�9ŽøöðLà©x¨zÔËÚ.ŸNÕóèå·ÅçŸ3.oGïØïë•àŒ>Çaß^uµ¤T�½kÇók}éBŽžð:~súiÃ�7cV«‹‚la÷üðÕáÙa?ç†î#Ö£¹sw4ù„ÍˆÖßH"¢ƒòWcuÎö»²�_¡ß!t…»£"rIƒ:½EÊ. lÎuT›ßQóê c¾©6Ü¢¿MwÊ7s÷ »K©4áj>Ý¼ÿ²I)o§»þTãw"ëœõ*»»Yeë4âVcpFát¢6¡ƒw‚_¬±Å'·ÒµÑ�&�býæŸßtð±ü
n§oÿyíÇ·Yu7?´í½Z·à›éRpàË—«ßúœ»öG²q÷ŽlltïÀÈ®ç;íÞuýAùƒ2MÿjÑ–¼�NºèkÏÇ>.ù@—F�Ÿ,ê×	Ã®^@ÔÇ×iP§|€\KŸðUY[CøoN€@ì*ž1xœ;^ú½dB›ARirvjIPjA~qfI~Q¥ŽBrFi^vjJ0�—˜žª£�–™“Š�Ÿ\È¤¨š’©nÍ•™[�_T¢P­�’QT¬�Xf'g¤æ&‚TJ¤g—¤æ%§ê§eVUå¤êgæ¥¤VLŽcò˜œÀ8yó2¦6fQtWèå 5M6gÛ(ƒj»^r~i^‰S¥Xýæ_l“˜0õ¦-˜<�3KC&¹(5±$u3×sFÚþrgàµrr9Oˆ†Ö”Ô ÔEmÑÍ¯y^à0zó]¾MŒÂhfƒ¼¹y–À!Î¹Ë½ù h?j¤èm–•ÉaÚ|AÎ—Æ¿Ü!“ã•Ù$Ñì+ÎO+q96ur€šÌæ~eK\á´y²®¿ Šv°Œ“ÚQ>T×LþÇ(½YQ}!nÇHM~¦-‰C–S§Ó–ÍSu$'_�óÚl¯×Á ˜É	1n�\xœû^ò®dBsj^ÙÆª¿– ;8yçxœ{W²¸dBwRifNŠ_j¹[fÎä‡ŒÂ2)ù¹‰™yú©y%™%™©Åúi™9©ºi‰É%ùE•½&m¶d4’�ì&À¯›\”šX’ªÁ¥ H†¤jTƒ…
J“r2“=St€üÉ3äÌ&3ëIL~"§1™K^{²±¼Åd™âÉ—äE&o”Ñ�Ì« ÄV«	Rº¹JA›—.ém°�l§§4]dr½žâäyzª“;y&ŸÕ“œœ/Ã=yƒ çä�z"“�
OÕ—VP(-ÈÉOLI-òL±R0 ›c«¯>9I_~òÎÉúBÈ.c5Èä (¤`Îí"€xxœ[\Òœ¿a&£?—‚BrNjb^iAHjn�[fNªPÈÅÕÍ1Ô'$ÞÍÓÇ5>$2À$˜šW\Z”êZQddæç�„ÒSK\‹Šò‹|S‹‹Ó�Z'ŸaTÍÌ-È/*Q¨V(.)JMÌÉ=ù4£<w	�¡ÝüŽ1_$ÂNMQ°UH,OÌ,AÑ¢‘tŽDDCShYPjbJj‘†¦Ðð‚¢Ô´Ì
+uý’Ü}�Ò”¢üÝòÔ$]u…ÚÉR¹PÃõ&¿‘š‰à´H·r[)ÃùŒ2I(ü(iAŒ ˜\/“Æ‰PóTÆAÆ)Î¬Juª,I-…¤–$¦#	rœÇd;Ùí6•]ÇH„µûåÕ9`j&{êKOþ#ŸŠäŽDO¨­Ù©•0€\„d4†±9/±8cr¡�AiRNf²gJmXõæK
�’ í9½¬xœ340031QH,-ÉÐ+)fPž ç(«Íç9Ÿ�ùªÜûÚôéçmCˆ’¢Ä’TÝœÌÜÌ�ÂïMóÄò¯h[ÿŠXøg£ñ&ûïÙ¡Á!˜ºüxÊ5èimport { createHmac, timingSafeEqual } from 'node:crypto';
import { config } from '../env';

const ADMIN_USERNAME = 'admin';
const SIGNATURE_SEPARATOR = '.';

type Handler = (req: Request) => Response | Promise<Response>;

export interface AuthSession {
  username: string;
  expiresAt: Date | null;
  method: 'cookie' | 'bearer';
}

interface CookieOptions {
  secret?: string;
  cookieName?: string;
  maxAgeMs?: number;
}

interface SessionPayload {
  u: string;
  e: number;
}

const getSecret = (secret?: string): string => secret ?? config.adminApiToken;
const getCookieName = (cookieName?: string): string => cookieName ?? config.sessionCookieName;
const getMaxAgeMs = (maxAgeMs?: number): number => maxAgeMs ?? config.sessionMaxAgeMs;

const encodePayload = (value: string): string => Buffer.from(value, 'utf8').toString('base64url');

const decodePayload = (value: string): string | null => {
  try {
    return Buffer.from(value, 'base64url').toString('utf8');
  } catch {
    return null;
  }
};

export const isAuthEnabled = (secret = config.adminApiToken): boolean => secret.length > 0;

export const timingSafeCompare = (left: string, right: string): boolean => {
  const leftBuffer = Buffer.from(left);
  const rightBuffer = Buffer.from(right);

  if (leftBuffer.length !== rightBuffer.length) {
    return false;
  }

  return timingSafeEqual(leftBuffer, rightBuffer);
};

export const signCookiePayload = (payload: string, secret: string): string =>
  createHmac('sha256', secret).update(payload).digest('base64url');

export const verifyCookieSignature = (cookieValue: string, secret: string): string | null => {
  const separatorIndex = cookieValue.lastIndexOf(SIGNATURE_SEPARATOR);
  if (separatorIndex <= 0 || separatorIndex === cookieValue.length - 1) {
    return null;
  }

  const payload = cookieValue.slice(0, separatorIndex);
  const signature = cookieValue.slice(separatorIndex + 1);
  const expectedSignature = signCookiePayload(payload, secret);

  if (!timingSafeCompare(signature, expectedSignature)) {
    return null;
  }

  return payload;
};

const cookieAttributes = (maxAgeSeconds: number): string =>
  [`Max-Age=${maxAgeSeconds}`, 'Path=/', 'HttpOnly', 'SameSite=Lax', 'Secure'].join('; ');

export const createSessionCookie = (
  username = ADMIN_USERNAME,
  options: CookieOptions = {},
): string => {
  const secret = getSecret(options.secret);
  const cookieName = getCookieName(options.cookieName);
  const maxAgeMs = getMaxAgeMs(options.maxAgeMs);
  const expiresAt = Date.now() + maxAgeMs;
  const payload = encodePayload(
    JSON.stringify({ u: username, e: expiresAt } satisfies SessionPayload),
  );
  const signature = signCookiePayload(payload, secret);
  const maxAgeSeconds = Math.max(1, Math.floor(maxAgeMs / 1000));

  return `${cookieName}=${payload}${SIGNATURE_SEPARATOR}${signature}; ${cookieAttributes(maxAgeSeconds)}`;
};

export const clearSessionCookie = (cookieName = config.sessionCookieName): string =>
  `${cookieName}=; ${cookieAttributes(0)}`;

const findCookieValue = (cookieHeader: string | null, cookieName: string): string | null => {
  if (!cookieHeader) return null;

  for (const rawCookie of cookieHeader.split(';')) {
    const cookie = rawCookie.trim();
    const equalsIndex = cookie.indexOf('=');
    if (equalsIndex <= 0) continue;

    const name = cookie.slice(0, equalsIndex);
    if (name === cookieName) {
      return cookie.slice(equalsIndex + 1);
    }
  }

  return null;
};

export const parseSessionFromCookie = (
  cookieHeader: string | null,
  options: Pick<CookieOptions, 'secret' | 'cookieName'> = {},
): AuthSession | null => {
  const secret = getSecret(options.secret);
  const cookieName = getCookieName(options.cookieName);
  if (!isAuthEnabled(secret)) return null;

  const cookieValue = findCookieValue(cookieHeader, cookieName);
  if (!cookieValue) return null;

  const encodedPayload = verifyCookieSignature(cookieValue, secret);
  if (!encodedPayload) return null;

  const rawPayload = decodePayload(encodedPayload);
  if (!rawPayload) return null;

  try {
    const payload = JSON.parse(rawPayload) as Partial<SessionPayload>;
    if (payload.u !== ADMIN_USERNAME || typeof payload.e !== 'number') return null;
    if (!Number.isFinite(payload.e) || payload.e <= Date.now()) return null;

    return {
      username: payload.u,
      expiresAt: new Date(payload.e),
      method: 'cookie',
    };
  } catch {
    return null;
  }
};

export const checkBearerToken = (
  authorizationHeader: string | null,
  secret = config.adminApiToken,
): boolean => {
  if (!isAuthEnabled(secret) || !authorizationHeader) return false;

  const [scheme, ...rest] = authorizationHeader.split(' ');
  if (scheme !== 'Bearer' || rest.length === 0) return false;

  const token = rest.join(' ').trim();
  return token.length > 0 && timingSafeCompare(token, secret);
};

export const getAuthSession = (
  req: Request,
  options: Pick<CookieOptions, 'secret' | 'cookieName'> = {},
): AuthSession | null => {
  const secret = getSecret(options.secret);
  if (!isAuthEnabled(secret)) {
    return {
      username: ADMIN_USERNAME,
      expiresAt: null,
      method: 'bearer',
    };
  }

  const cookieSession = parseSessionFromCookie(req.headers.get('cookie'), options);
  if (cookieSession) return cookieSession;

  if (checkBearerToken(req.headers.get('authorization'), secret)) {
    return {
      username: ADMIN_USERNAME,
      expiresAt: null,
      method: 'bearer',
    };
  }

  return null;
};

export const unauthorizedResponse = (): Response =>
  Response.json({ error: 'Unauthorized' }, { status: 401 });

export const requireAuth = (
  handler: Handler,
  options: Pick<CookieOptions, 'secret' | 'cookieName'> = {},
): ((req: Request) => Promise<Response>) => {
  return async (req: Request): Promise<Response> => {
    const secret = getSecret(options.secret);
    if (!isAuthEnabled(secret)) {
      return handler(req);
    }

    const session = getAuthSession(req, options);
    if (!session) {
      return unauthorizedResponse();
    }

    return handler(req);
  };
};
˜åOãå®Xxœ¥XÍ�G×xcËlÄ!X–M ¢XûÁ|$(X‘�M¬%Š?Ä:Ž@‚tMwõL±5UMuõîN"«8 Ö ŽÜ9ï!ÿA8"ø8 þ ïÕGOwO�dµ‡žîz¯Þû½ßû¨úÓè—?þí³¯‡#ø�•LùtÄeÂÎ^üèÁÍÑÁÍ~^°Ü�•‰`š¤…ŒW’˜5ðÕZæ„’²<S2gCr0zñ‡q	ÒÛä ^gšåLX#	-ÌžyLKH‘ƒ¾œå9ª£©Á_EÃ‹´(\[ŽKN8%±RÇœ¥É„QF39„Õ£mv–)m— éÅß¯¾�<�±Ž]%�3²GÅ)]äd‡&s.w@|â9áóL°9Ø]÷­Cÿºæ"`Õ{g×Î¢4•‚ŽôÑ¶HBD$­ÌX3�¼_˜“Ó�g$QD*Cj[M]ì„‰!Ê]8-_ë½q·Ã)\8gf¦¬s	@B bt"x>s.yuèÊòÛ½oÝA%�3c­@#]èÍåÔ#<˜PTì˜°=áªÐVÅÃÞ9|8¾Or>•(—³Ø@ö–ÒB@¼ÁŒOï
.�½ú¡Åyœñ§ˆÅs,xæä¾w“ä÷¿¯P÷#QÚ|Üb¸a¿ô¾]‰:Ü>ËOz¯¿ƒj�°<e†Ãõ9 IÞeÿòÏ®]òz^ô¾:´zf4«Ù§9EI01£¡h‚"çì©Þ…m²¼¾õ×�÷g,>F@Ô€Ã­XBÀ˜„ð±y�ëÇkÀØœ'LÃ–~-j“Ö‹3?ù�KXŽ:¤’ƒ’Í3³ZïfTÓyÜ °±àwŒö5¡ˆº‚í­¢P"£9KÐ©š+}¥ôŠÈá³B§O.›¶Ëlý´ÂEÍÁ&ž*Ï‡8�ô¢Âƒœ¦à¢˜*ÍÍlŽ¦A…9tP�[CšA%K2)e( ÝòZ°Ôpú×¹)ýfÖwgÃ°¶VóéÌÙ´¶Ëï,$PL©°P4�¸ä 8ÿà•oöG�W®hê	7šêE`U0à °988:ç»7<R¸ç”Ÿ`¹9ðÁíF*)b$¦ö�·-LÆ*Á4‡½(X�f”u”ÂŽ[sN¨(ú�ë‡ë‚e�’pÌMhPÉËw>ˆ‹¿<ƒÄJ9ò+Õv-ÁžÒÍªjH €
€$S.©°r°UÈKÏ´§¡ùz–±26®ŽN˜kËäœZŽEïz-w†ïVæÜ‰†š�g€+ÿœ¬ÏC.a+ž`›°ì¨J8Š­`NKÏZ|uf>³Vºh@“-ëe,ŠÄ%@Òv”ðïå‘zÇ…ª�MØ<ñê-¢�R.ªàýûâ.¶Éšµ¡°÷«Z¿z³Ì¶®/såò9¿ôYEü{‰ytÄÌÀÕòóXóIaBFÙÎEÕ³³�"žõXÌ!$µCl�ÉK±è“#hGÜ°ÛÐ³>&v¡KÖG
YùXeŽ h…V”ŒšvH`Ïñ”ùÆ1 Á†±µ¡2`�ýÞ–5_öØÙõ<œNnÛˆî;P¿qùîEœÈòãË_+íÓî°çöh·ï~ÇÂïº{Õ;t’Ù_?þLq¹·{‹ìîßÚÞÕW3˜gp˜óaoõ]g*ÔÊ‚ãtay�¡ c3£®£ÐŸ3;ò�Í',É;«ÑûG�U”åÒ6PR®&8Lx¶šÆ°ŸÃÜ3ÏúÁ�v…l°R4¨æ‚Õtè›‰ü|z]âd•Ÿ@ÖMR´JÆœƒ¿ý±MØÅ ‹˜öR¬FçŸ]þõ…r-.…Ü4Îç ™ÃZ±ðódU›¡ì–¦æø\*Ôt‡�ÉUaÐŠr­ü<r`Õg3ï36C£nch@ù®¿hÓ°Öœ'ÞBÐ6»lƒësîö›.S¾ÿê/j¸ôUÄ-{©M­jHKµšÃ;MOIÔÜÁ"fÙÞY…�®·ÅËÊÂZ�vCI6‚)à	ókciï@xj›Gú%UÈÄ‘èóW?¯ªêªsfç	?0ãLéYÿ<@Ñ™÷õä­N'›;sŸ$Ù}+}"÷í�Öv?ËgüÜ•¾›;)êXUó9ÏsÛŽæT¸¬ê{Ú'ýª½–Um´_Øc,rUÍõ�ÂäšðÜMÖ{vl¶4GëýÿN�2ê"ïjTEÅµèª°(€[s„K®ïã£ ’z�'<!×ÖÏ¥Ž¿úò_¶ª9«Š	Ô˜{öÄùÃƒe†íÅH˜§>±ÑªÜ±3çjôvã¹›‹y®d Z×qÈÊvuî±¬ú8þx:gvz«ßlªJegyúßÏ:ˆO8k9‚¯Ïø¬qI�ç7CÎåk¿¯2ô½3˜òcãò¢yg2T,w'S%¦ÆDlœÛ['Ê9}TŒãš9t]8àA�¡8ð“{u¬üÀÛÈ›�j9‚Û·Ò¤–nø%cuW”/$|ël�-½s¶žû{ŒhÇ…o'²5C*—Ø‹FãBé©¦ÒØc�½8².ÁXìjúàµ§	2Æe.§äðéÓ'uTÛùøS±3˜«¿êN!]šn]yÞ1¼ýæ[äC²)ÌLÚßÁ9(gàQC’ÖÊ6µ‰JVÇÿÕd…Û»È‰‚ )r»ÂÃjGÙFG�>uŠo’�º-;ä¹+1ËòjLÈ“D°S<ÛQåTÓ,_Q¹º^t–7Øìý¨ãVÁ2ue »[w,p‡¬@.”ß[ÝÖù¾•DàüÅ›Rã�ö¨Øeúa38Ãà!f@G]]ƒ:{Ë®D©éF_ü=$t&°'iQ6¬óE²—½ýP2bm�ÿ_ÞŽ=¬Éj/ŒàŒ­vU¤êŽ«ÿ¼ú·Þ ¯wé�xœk�z¹Á‡m³Û-vîäüüìÌÔ°ÄœÒÔÉ�¸•&o`ã›¬«Æ±¹ˆ�—Ù+Á7±B×1=ÕV¥:7±ÈNMÎÏK)®MÐQPH,É°ÕW²<JJ
üór*AìàÄÜÔàÌ’T[ŸÄ
0?5¹´(U}ó#AUi $›)n~xœ{y'rBsj^ÙÆŒZ 4÷n¸“xœ¥XQoÛ6~÷¯¸>EN§º‡Ù‰Ñ"óC°¦-0-Ñ6‰RI*‰×ú¿ïîHÊ²ì¶$ˆC‘wÇï¾ûîUT¥qðÒR/Õ
¶°4e'Ãá9~ûÅs¥3ùt2î©¸Y>9#Rw•+©ÝuÕ9…ßv-ŒÌÎk§r{®ªÝÙ¼\­¤ùÆvÿ°q×;?=…×Ð‡Ù€ÒàÖ�²(qÁ'ÏrU(Ö•Fáô¼§´“f)R	3|ü†žNùø— Ù{[ \‚‘Ÿki�Å©T2ƒ¬6J¯ØMZƒ~áƒ)Ù4 Fµv#ÐlbN«2]ƒSÚEIaûð¸–úˆ®RFÚ`?I7Ç“;›[ëëæ–|5(DUQh÷rc!q›J¥"Ï7�ràú=úteMO˜Fëø!rË&/AËG¸Õ…utíA±IÒs,7âIuB$ä(ù÷ˆWð¹$‹of€|P©S¥†{•Þ[ÌV+€›×~¼�¿›M?NßÎg×Ó[Œá—øåSÝƒS˜á¥Ð"^.`µóÂœ!L�'éð«J�q"Îgp€—�ô �
•çÊJ*C03¹uŽ4@üî~CÓC4�ôïúC¶i¤«�¶0GÇ;bLn-0·/ŸæRh™Ñ¡x_Æbêo1.!¡ø.aç©³—¦i.]4‡	 oö/$ÁÀÅßÌã½¼Chh—�¨¥÷9ñg†ëâèšÈ$† ôÓ‡!žçÏýÂ¶G?La‚'>Fþîr9Õ¶6Œ‘,Ælˆ-9FóåU®ôý!'¶°ØÀRëÈ†§¿C†YÕ�™Ò×ù(óLÚ†øtm\%#Þ¹²ø3H0©¨DªÜæÿpg/×|Y†ï*˜¹ŽÙÅ4?”*‹I¦¬t`·ê	‡�ôÊL„#¤"/œ¨ÇµÊåq³“Ëcv}þý<|¿KŠ»c�T'é5Ê>þzy-Çµž5û°0RÜûGÇ)µÛ;fµèrµ–¤¨œ˜6Ã]¡*kÒ¼¶ä‰�èõa-,R#•2“¡S¸«|DªPÀ2“üÎk2³+Ê?*³[cbE”hÚ:Ž…/äÐ–ÐYdE�ñ)Û8rkSÖ«õà¶^X2«Ý®»(�F
ZŠ¨‹HƒÛgA[î–"·òrƒMÐR4Âhdv»•øs\|e–©G×T…Wê§	¿3Ö/ú„*—Ò�ÈájØFD–a½ÚŽðÝ9Scl¾œÔðú8Ze”Í=Ô’,4_;½ŽúˆÕLÃ€'‡ÓôªŒs¾+aÕ,Ê’Ô%Ž?ÔÑqóÄçî€Ã+é‚ …â{æ7~ý
ßÇ#…ÝÔÛË­w2h„5L?¢Ð¶:>z}²7lì|`ÞX`Ýx•¥LŒ£òÒ|Ìì„*¼k;÷,Ð0^Ç�XCbWr2k*¤)¢Œø
!ô¶‹N8œí&žÖFß%ö‚nú�Ê²\b2´O#*b|¤Ö«€*ŒøÕâÉüBX_çe(‰.w#õ?4Eãw´ubO$ôá8èyÕ¤U^A ¼üùW˜—%NFz³Ýi+d wwOw®&Ô/ùžËqŠ1^—žàÐXuŸœD¸&ãâË8Â²:®bYW¦t2uûÕûšá­0úf'aŽK95É=x�Ô&aÑ.Í‹yÒÆ{OLu°=ÂÞ#?�`Þ§b}�ó¶Ê‹YÀe2èa5'ßÝÒ�UÈ#ìF§�ÕÑá�¸?V¿ª0ÊÎë	Äâ@:Z‡fžâlÃZ°¯A‰ªú­áÈ‡]?ÙR'ødL‰7>ZD°¥2Â¡ÁÕvÄt‰¥Ã¥ÒØ¸Q€¾!îŠÐ5·<îùÿ}(NEJ×\)¡�drQ¯ +VÒ—VÓS<ûiðnì“iáºj@ºb/¨Ö�Á¦µ›ç×cËN…ã¾	¼èHG¼§Qþ^!ªpz@áEýrt É4ýlÇ�Ø{3%VºÄÉ0å|)ú„€-ÊÚí½ÀÑCÉ/?¾‹ó%¤©<åâd@Û¿3�¡V1èƒ#ÊB±öZ'vôF˜<…ÐJçýëH²°ÛÍv8Ç/\\	�àkãQ¼¨ùø�nñ¯Ñw›T±ë1‡[(ì�·<¤ƒ%ª1¹&°þ‡lOjì(�ªFåôßpn}ß ³ù!•;Ðð!â.Òè_aŽ´{íŒxœÛ¡tIp‚ÜDiÉ‰:um&ž—SKÎ/Í+±RÈ+ÍMJ-²æRP(J-N-	ÉÌMEÖrM~Âh=9�I‰59?¯¸dr"ŸÈä|æ§›�X7—q|cœ,ÄsmòožY“eù®N>Åjò+? g(­ntxœ»$xRp‚"sj^ÙFÝ¥ )±F¤xœ340031QÈÌKI­Ð+)fØj¶O¤}ÆÙh¯/O–|
ŸŸ÷Î èó¼’xœÝXÍrÛ6¾û)på‘¨¦vuâL•X©Ýqb�%'½Å	‰H(‚ÈÈŠ­™>DŸ°OÒoR"õc;Î¥µÇ	Øý°Øýö’“TéŒÝ°@%#9fs6ÒjÂ¾ßqÿn½#“P\7^ìÈR>âI‹þ”�ÇBg“¸U_úÝ¨dM«<¦cœLO\gšYïu|Ù[è+“­Cä™Œ�°×ù*u–ó¸A¬Š$MïBü™‹5m(ñ<‹Ö¯qªÆ2i-¿ÀÊòÛ;Q�72­âXhc¡ÚË…uÔ·2'ÉHµ*ß/D(µ²m #ÈÜ	z,xœEÛÔ#»{7€šl½R„½;•7z¶Š`öîÔ¿LcÅÃmÊ¹Ý½à£vSùáÙ6ˆ©¶y*·`h÷wóº'2ô”k±Æ�©Ì¢ž‰S9‘Ù�ývLÐÜéìîî°]Ös|6,‹ëï±¡¥5K8¼oAh]Î$
“’1œ2áY	Ã8«R\„m“ÍbÁB5á2ñ!O*¿¦\ó	!±6 S&�šÈdÌŽƒóòßŠj‘å:1V¬jŽ©<	[Li–äqLâ�ø¶�’ò‚òÖe&;dPXÁ„æ3™¦o­.;|ÅnvsÚd94 àƒ›!BäÐkØ¤m²Û[Ö€Ós¦m«‰·Šå›½ôýÈúÁ4_ìÌ—n?™Ð¸>Ü7�|¬­£N)=ÎÓTp�è(6Œ'¢îùI¹OPCnDÈPÅì
ë[Œ¢´üÆ3I{å2<M=c6d…yLˆœh�
ñ.¶¥aÇˆ�É¢i
ÖC«sÏ*»L¤ò8¤{ºÂÇYIK·¢«wâÇE¾¯†»UžK+dÙKýVÁ‚WàÃP©XÀ§5&äÔ`‰˜²Ë‹S‚ô±Ò¬ÄþµÓò°ÂÖXG
–.•êî–Øu€ù!¢sr¡ñ#n¼Æíîä[»/Ç	Ç¢Ñl½ÎwQÃ•´ëJ¯YjX/*•Áõ¨ðZ(MJÙIÁ+ö—N,2¸êú2•!=�!òêY]db‹dˆ[JX•ja`?á7fÊ«4-åœëÈÎözÎÞ�ò*dïar7ŽÕ”LI0•FüHÅè"Ø&E+Åkyq²Ä�¯©*Ÿ>�”Ï†Ô?È�ë%dËÎq Ì}Y.½*yßZÞLÜ%Yãì|prö¾�RrcySpi¥[‘Zk3§,çËzUdÞ!;~FŸöéº=ô)ŒW©bV�ªg‹;¨”ùq£
MÊšÒ^£ÝF=Êd`ò‘0_)‡_©›ØWÓM!ñ¬V§Ü[ÑÇ˜ícøV²¥¨‹�cP77®z)ØJôK¤ýíC³a¬†,Ík²IŽC:„²l‘Ðÿç¯¿YÌõX�Ïƒ\k‘dX˜ÐM ¨Ý¶bEh†Ä4«ˆ„5â2ö-oW�bIHðE‹*÷óëë–µbŸ>E,ýùBqð!uy:Šðˆ5ÿþ<Yúq½¯Ú½õ$(cä¦¿õØÊúÇ�§ÖG¶"~E~ÛÜFŒ.ŽFËÀ6³òâ=DNõ+¥9„*OQ¿¨ìITG”~wÏ�<R‚Qžå™¦­_4>±å¨ÄLhnêT£ÈÚÒGAp¦8³À4q¨%B„"´ÎD¥™1áCXÖF×Ç‰ÉøÂÝÔuå®zÅTj1!sõ:OÐ&ôWá5¯ÊûZ.ëóHQn1H6Œu™hfIÆ¯H¼ÍúH8Ø¦€´»*¦î+·y^öù¦,dF�ƒ4ÂÕŸdXH~”qp]ÂäÚùú¬³{e™#®íêT„íÐr¢a%]²4Š*s~ÖÔV¯:uÛÞ7oYõª5 t¿ÍúÕ‡Kë <Gêahô,ª"9Öu«ï�Šh¨³I°ú]J/Mÿ3ž£whÑkµ¢U—|pªÒß#Úý=´•lh'®¤ØÔ/Ñæ;$éáç"öž Ìå÷^ï!=øñ-¯jûÑ‘…k|T>£†|O_®{þ¸×=:X9ÙíõN{ƒÞæ=—â›vŠlÚ´'v:ìmêTûÝDoÊÅÐëunÜ<:ïÜ|³9ó}¿éôÏ’x†ˆÊˆ¢[äW¦×nî4l8`ß·yµû‰µ-°÷ð¿?Ìï[z&¯w-¸�K«ÿÉåsŸêíÊì{ª÷säÉÞ®;xsüt¯w_‘o”$UâNL?j?`:´?~7·ƒ Q¯ Ô~'ß¬7›¦­wb]|¥'T~¢õê?ù6«¾W¬Läûñ.ï‡ƒÅxRýx2NàŒSxœ[c´ØhBçÄËº;Õø‹�õË2‹JJst3ò‹K6~±�  á	ië-xœMÐMJ1Àqü¨›"B)ºyA…q¢öŠƒ…¬íhéJ2“WM“1Jj¯ ñ=€HOá\¹rë%LíFÈîÁïÿ^&‡[/û½Æ5ÚN­­œÅc—ß¢�À”¬N%Ø©Á#ôµÂv“ù3ª‘g¹0ÄÔöZÌ-Wòý¦Ÿ%½èŸÖ+þ¼ù¯rÃ×Ë=ŸVþ­´1¥kåågB �”JV(.­��Ó$Ý… Bá2Ásø¿•#%r*Apc!û[Ï•Ý„œ	s¶
0¦¤Pìs�&†®æÿë­³NJN’f’&¤uÑ)�wŽ‡eC.�:; £æÚ=7Ü*mfa©,¸bfG€Œ‡¿ÉU1Š@i`(ÂÙ±V7ýUui¥‹ÙQÁ/ü¤:þ<Jw’¦x340031QH,-ÉÐ+)f(â¶»-\&Î×§gî·Î;Ç.Cˆ’ŒÔÄ”Ô¢b�ª½Á»ŽÎ7‹±ÈbXŸØXôRs9ÿi¨ªü¤¬ÔäÝâ’¢ÔÄ\�Ú‰š9;ùþWÌJy»r¯°üVQÑŒoPµE‰yé© 5Vòê^?Ù$ç÷±^6¼4=mfž¿<TMYfQIibŽnF~q	Hé
‡÷ƒö=xzÔò|çÕ‡þ¾\/O6C•Väæ€TœU�*®xÒ©Ód¸HXe¥nÂ�Ë{ ˆØVœ²äxœ½;[{Û6²ïþÈ~Ù%™J”ì\š#×iÛ©};>–œ^\o‰�Åš"µdÇuõßÏ.$@RNÒ®›‡XƒÁ`îÅóE–rGD<�ÓË�²ýÿ,iBVdšgsâ¥YÄz“üv!2o{c£óäÉyB†ºÍœp‘Ãg2ÉæšÇ<K‰˜QA9»f©à3¡BÐÉa9b8ãŒ“‘BV6‘›Y<™‘|™r§€:å‚¦Q1’³KšG	ãœdSDu3c9ƒM)œDñtÊò�œ2±ÌÇ”&œ‘i–“yÌçTLf,j',½3À¿X
ŽxDFèuG$aô
I. `%¬Šáh×1%ŒæÉ-��¸Íq¾ƒ³Ó9¬˜
Ò&C eç\æ jÅZÐy|93àœÁ)£uð¹>È0_2OÉ8ºÌiŠ0äÂv6$·¬ûÜUˆÈñ‘¼ž^×RÛ›¯A�fpò”ì¼"wDq]žèõÙ	Ô‡%Câ
¶8‰¬PÎ $€å~‰0Ôü}´³c/×Ã�$‚h>«+ÄíVˆHVäÆÂÝ²1Âæ+ØŸ}”²§‚åS:ad_¾vÊø2r³˜¿§Iœ�§ËYbÓ¤§	¢“	ÈÞìÖ°n[GT0wDÆã,¿Ž'àŠüAÒe’àg–çY¾*÷m	±j ü=ËãéíIÎx|™²è,OQŒ%…Ë<±ÑÏ™˜e‘=2c4b9ï�zL²<úÆÈƒúû
AøÓ~Ó)ùÓ†ˆÊpý iv'Ø–Hú•ˆöOßîîƒxxü)5:Ü?=:<îßã½áÏ>ä ÎŒËàÑ�ñ|9—Ì_:N™$ÙäŠð+vCÆLÜ0†"‰á®EEFƒ(¢Î«ó¶Ç”³qÉk't	.v";$ý„&IvÃÉæs°éR0nkÔQÿ§»oßíþðaðÃþ�Ž@.@>!/ºðßf·ÛEr;„ñ.ªÅÉl§ì£hÃ#ß‰Ûã“<^ˆ¶ê¤Yî6‰'±hÓôVï4^NQ[A¢¨a*ÈH?Ïé­Ö°?È,)‡@sË/FyQÓp=ÈšÎ	Ë¦ö£@²½ÜÚ¯€O�Ù%¹$Ø.õ§‡pÞýt2œûAÈä'ˆZ¨ŽÈgtëù‹öJùm:©×:“sv8î	˜•˜3-¸¯\ƒ5£|VØ!zCcA´—áË±HXÅ— [¾78è·�¯…üV)p†ki1l|šµŠeÕ*œ(wl†‹Ã9]øþ8@ZÇ¡È’tóE.h44þV‹x]/Âß²8õ=ÏaØlN'É´’cW¨†%-ÐuÎé%³lºaU	Ua—â(ônÅ2@€y_Ú//§7^K~„Ý~©‘;’Ò9ìîõw�¯xzøfMV
LšrõñÜCæ]à7Û™(›ÜL®ðÍùê5*Ù,¼j‡É—L€qH�uš!ŠÏhùªv)F“W7_Øƒ®1lmÜ		’‘æ¨å5ûxžX¥g�ïŠÝW£ Un+·fõ²DÑòI0m˜m^5Z¶º&œŽ.�d6*qE ªt˜k¶¶TS_¬°/Ñ;µ Iù ~â¬#Ëãß¥¿8�>­4úõ­8\UÃLq„‘'ªXÊ`Ôïì0;þù¿[_K”“W+øz$l
Üb÷ã� özí UOòÇäQmf¸Üªô2Îi`Í	\Š*[œo^„ü#˜ëŽOUYfÇ‘Ïë[éïµà­‚å¼{Ñ²¹êì¦ž5ê]�ßÒóE€Wxª „ƒàB
Qè™Ábsµ×  =£{•ëÕ+[ŽÇË8‰viš¥ö$§*ÊB!®Å‰¸tb Ïò¸qüÿ–,¿UeÏW‰/&>xJp,:¡·IF#ÇˆêÀÀU-C‰ÞŽâl®4ZÐ¶g›„Y¡¥øO¡»¦Éó$MçùÆÛì†å»:úÁJ¶çmë5Z®F�ï*p«Þã;‰*’çðý×t¤­j6fÃÆ£® ÀßÙ¼w,¦»úÌrÐá‚q»)©ÐÁõ1¦·Iü;dä”œ�’˜6Œ‡U¤ŒÑxœ³¹ÌæÉ’“Ó7»äéÿ¼|ÑÓÙïfHöz¸rpm÷Ed2ƒLw"ä]<![˜’Ï³kF¢L´9»T8ýQ8’Aû(G�ÜÝì€ŽW&ÞÏÃ­ð™›l/óX'Ï”(º)˜È�Ss Jö ÅTT¬°ƒübøŽŠ±,E¿&ƒÒ&" øgì\ª¹P°Xò~!%¶ ›=Ã$¿Æ× éˆ)ÆíèO@$&ðY
PHMààÜê52ô>_``ÍFQÅÐÚŽØœüüÀÏ/$	˜Yù.IƒAk²wä‘™–ü	=d—3ä…â¹®â1Áæq[ÒêËšT”-1ÿã	Š4J�´1­ikv­ÍÔÙÂE¶ðƒmÜúûŒ,XaÙ5KîAl–.ùÌlÅ	À„8“/'B	¥ŸfDä4NP~$å„}ÄT–äYf]¿%ž;fíÞ^‘.ù–Œ:�ïô¸²"p]«é)q+c]¼ãÁZü¿gM…9[$tÂüÎ¯�¯wZ¤ö@.®29ÔiÑštËiMT%xPì ¹Ö9äùÁ“‹Î¥ò]þDYáÑ?ÁŒÍB´Xûè¿¸±šÈÎ‹Â¾Žtº`ê"MÎÍ2“ÚÁqFóÉìM¸ ³Ó·k 1Â…$ËˆAp ó&ÊÐT¦0âk]Ñ‚ÆèÙdúÍ¹ëÙ.^iµq´æ\†²’OJuJr o†•Œû�­H�,
C0è€ÜZÉ¢�S¤”Çš1çälø¦ý’Œokƒ/f’,4à T"°ª¹-rünH’xjJ‰€SíÉ�û¾N¯Z„^_´Èù>�¯/,ßª%	 ®(Ÿ^«gà:Xi/©¯³ºd\]2.— w(ù†ŒëÛÞ´g^Y3›¦:'¿u%—ìLBM	;¥¤Úû#÷]Š”ÐC’¦uó_^5½<°Ýp™/�³èÖª}`ìº>E’¢€+Ð’â_c!PÓ»�›Â…o�u³ˆ'p¢‚Lz "H-é}lÓùïm‘G:\òYx’ÑVwëE÷ëî×ÃÍ­.üûü8R7�UÀJ™pà¨öÞˆÍœ=†@ègøwt´·7<8˜Ï9ÿeDTý½îÌe^ÉZ4ù†ìÁ^€ZCbì \cY×vó*¡STœ‰‰ÌãÔ7ËÂIÕe¸º>7é“Zbr§û¿FwÏVþÙ*þÝ¯úÏ/�;E:óhÞ�#™½Î[ää¼à†f˜üƒDÎ²%ŽÈeK·Ð5KD•ŠR¨Ì¯¤õl¸ëkÇv¼œ�!ó“¼8“­öÙì­5 š€€›Û\%)¼‹"þ¾mô¹îQG¶@í,ù•µ|82Wý¨Ñ,ãb¤äAZÑ…Mv�d°l¢~’Ä\y-%áH2šÜÐ[®q`xû„[D°4"EYœêÆ˜ÞˆøMh"½å-(VSˆw*ZãõEÛhO²$K!øC(©œGÉss©<PÅ�*[I®5�ºªJc‚Wo i!t ­d¼Çœ™D¬’B© ‰óÜæ�¦L¶?ŠZ…S©«ç±VCä3³ÐsÜjîŠ´š»"Ÿ(Z�§Já«¬È”yhHíÊSa5JÐoCŽ…,þc,f¾‡%Ã6–GÛƒƒ>Ö]ƒJ+í®lt©R¬Óä’‡-QXªCï±4f‘‡çZ¦]Ùº"™U!+MžZõ@„ÉjP_í% –}¡•I…0îeŒgBÖ`>Ÿ(%,EÅö¡‰��Ð˜GS±’ù–Uœ’�÷bÂ'ÙœýÑV Ï¢	7«±øf|–èYBØä¿ñˆhH”å¯Gÿí»;8mŽõ¨Ñê§µÿ½:Ë�~@Âù? ðfB,z�ŽÉ�‰¬UÄTýÂ.gøÆSRlÕ4-t§µI•…ÍN\»ºáœ¾èZ™²š§‚H=ÝV1¨'«k&ö‘U^{¹kâÃÓý>Ò÷í¿lÜ@tç‹DVaÜk Ú==îößöð¹ŠŒšH7ž\ÅÃ:¨(ø©0é€€ŒÙ„.%.*<Nn0˜�àªsmÂtít"ð!Žô±zS¸¤÷™�äM{Â× ƒ™8N:�CŸÃè\º5–ò¥­»ÌcqKn€‰ÙR(lã\¿v‘N)Ü°ûžv¦âÞ&Ü•¯RˆjR£2	GêçÆB´ŠK•ÿVQ�-Í•KÕL“†ª™™óÅ¦ÕÔÜ37È«ÙRõHEUîh³GÞPHÆtr…ÑžL"L¨7%eþ„ö‚(½¿Î‹jº‚”†å�WÔž÷J£gDn
‘Ý,ÅçR¾õd¢6y&T jL ¦"p2x�M“WÉŸŠ%eÎmÁ•2{šÝ 
™�ÀG?Øvf%u;ä³tÌ}„nÛÛ‡ qÃBÇ X)ë|¸îUý•F¹ûŸ¶ú’÷?.â¼°e1påî¢=kø…‘|£%äi�ì£NêÄ·ÉÍª'jÜ’–û®Fš=MVÊðË~·E^ªêæ?L^ý�²0b¯Dÿ\¶–­*éÝ?×XV¢ÈóËÊOAËJ–<1.ÂOnÜa�X±–…ÊJ·4Ã)BÌÕø»#šª¡âcuOª†ÁÝÆy8`LcùœÀ·Ò«‡ï4é&Hu@!Yää7EÍO¹WË9^a††ûG'doÿõÙ÷=’d—ek°x I"&hœðõ‘i�Œ–mcåHà
bÄÆËËÃtšÁF|lsŽ©XÜk°êÒ®S{5_QJñ³nYÐ×qÚ[c±œ¾Vóµdº�ÚÖ«Š_1kq¶ç|+©zjQe1¬W¿ÄV¡O_k¨*CÂË<³–µ(*K¥�s‡Ró .:üþý³G‡ƒ£þp÷ ç‘¯ÈÿÞ‡ŠvüâŠŒñ|¨ðÛéº¸E¾Ôg¬¿«üŒS›æ¼eVK¶ªlÛÍ§ÌMš¦¼k<4+­6yC•Á~dYôCËÖ†-áV ae–•AYÀOÊu:�¥UoÝëÎÏ* ¬IìW V¸^¸¨jo6š\BF/fsì®Û=
0o¾÷SÂ€vßÀ8¹HéÖÖ¯,_¾x•—*vÉiÝj'I«"0¶ó¾ÅÆs�/¶Gðã=KU(àìXÆnëá´W>§‘ÂW2Ýo½vñ�„³ÞÞC.“ìQur3bÉŒij1þ}È$VoL©Vp-¢d·z÷}ñ‰0Sz-�5æoâ4Ì`0,6{³CºÖS¥¾xˆ—]¶§„ƒå'	øS¡Êªªâ ŠÈ1!øKø‹¿è>{Ùíêâ=/;ÀÀžœîƒ)?Þßû°ÿÓÉáéÏû»ïŽ÷ðÅ±Z¶mËØ�2&†ØœÓüªà†z©läÀŒ¾ºwÛ‡–\[ ¶æáWÓ“¯‡¸OÝ~±ª…VÌVb<Õ…;ãRìÚ…>�$Ù¹²†pkMY²ÐØúŠ::”4kšksrêžÂÜ_¿íÿRm{å5ýÉî¯Óý�:{«·±,¬muLã8î¿V’¦²[ó6Ä+O ­ÉÁc¬ÜÕ_¯%Ø>)JäöTÜ4Û¼uÓ«Švù¶h;È›)j¹!È‘^Qƒ�k×¾Š“³_ønmÁÈ}÷©²¦Z_º´t«xÛq^.‚àóŸØ�+ÛÙ>éÿüö]oäþ„àÏ–•þLŠ¬u?Zz½²(zØœøóY}y²¬ß¾Z§(KyŸŸÎÖóØ1xMhe"ä&A¤âjzõsn†¬3–˜žZ¯z?Ñé\÷“?ì×­ë>–ÊnÞ‘Àì'Ú�nW^¯u]TêÜú§"EçGM5÷âÒeUù(!�ÊKpj‰©šþùpþåèl0$c,Ì'	¸“þP¹ÊØa’¥d
—{«ëç †M1%è-ÄDñ…¬þ©æ‚r®Ëí£JCzD|õVPè4S šª<¢’ÛêJ¼düRþL6A¥í�€ºê®zþ†I3ö‘¨3áó±*+5¿ìß›ÇªÐT¹U\¸óR Ÿ*I7?É 0%¤Ú“¸²É•ä‹ùå\C>mzúU^yº/êÑƒìÚ¿Þlz¥3I(¿‘ÞëÍ§m•N�nþWµë@¾PUMõãActìAOÐÁVQy¨Ïíq™.ÙÊ„ÅÖFÌ•<ý‹ví5�ö¤€Õm™¢Fÿ‹\Mà$¤xœ;TóÂoó¦mŒ›Y×rMæ›üHTÒ(ÈS7%59?%5E¡ ±$£X!3O¡$#U!91/?/391G¡(µ°4µ¸„KÈ*)-Ê›<KPj³°ÈEÆÉbý’1…D½œ| âTçüÜ‚Ä¢T�$ÍÉLâ­@MIù)•V
Å%E™yé
5
y¥99:@áäü¼’Ô¼’àŒD#S3ùÉ³ÄåTP”hBmGÕh=y£xòæ•2L@×§¦è%&'§{§V*(ÚÚ*;Âø“ïÛÚOö”W•€*,JMÏÌÏ«‚0¡
'ÛÈñl^ XÁ8YHEb²�Š4¿ª¥“;Íä'—«\›ì¢/0y†ª$³ººõd/u¥ÉÍM6oÒPbÔÊLSÐH­(HM.IM	ÎLÏKº<lÔòb˜ ÜÒH�m›?J<fÜÌ¯kÍÂ2`òCc°´¬©ÜäV£O.8½¦PSQ`˜¥¦ ]™™˜„î9˜’ÉŽ
b“?7o65]Ä,�Ý�èŽ“ÛœbÉÊ –nÈÑìÌ‚xœ�XMlY–a‰�•]È²ÙÉ¢­XKf&xÚcçÏØ80±ÇØÄ?afœlvÒoºßÌtÒÓ=ô{c{vcšwÄ !$ !Á­X|áŠ¸ï…+\¤…#âDÕû™é;ÑæàL÷«ú^U½ª¯êõ?w~w˜�nœHø dÐ	¢V�5yåû=Â4“¸¹(öù’—ô»2Î-OOÏ]½:W¡®¤‹ÅAÈƒwº,	D�l3	Ý„ïóH
ƒLJæ=ªÂžàÜT#;»»pÐ¼6$½H@!v$$‹$aqHx‹%~È…€¸IXmžpÜÕÚ"ÀšMž8På²— F“…‚C3N ˆ“^›ûÅ�GiK¶qƒnO
’1°ý8ð!äì)�"ZUQ-@çöœ%aí pëÐ·Ð{ÖA�¦„"ÔÑ˜f�iÃƒÐ:BÜÉH§IÐj[yÁÑOÿy
‰q¥žô8MhÄh˜õ¥€SäHvnZÅ+s¤«V Oö-½Y³¿}.,!jŒ¾G°r>˜xåÓ�Eô‡²C�–‡r
ìTAµ‚’(Š¦ç‡€iâ˜_^YÉê›×e˜Pëc¤ýŽÉ¼œÈ�x2›EÄÝ�Ð ~¨>ˆ$Ošì£W~;•ê”Þf‡A§×æy¼+Y#äà…±÷ÄS~ .8ÇLLj`tN<ÙGG)©Úœù<)6˜à>aÕ‚ÖýëÀz˜’‘<•8”Ô€…a| `þ&bÔ“ëavtbÛåw¯ní®Þ}\»[yðx»†aDÑ«p³„æK¥Òñ¦>šNMîPR†ÁûXIöª›ÐE{h½‚$¼£ª°öT×WáÚ×o.™œ�w`“Îç¤é¡\‘Gôèƒ×Æüô0Fª.¨’:ñ>?–E‘ò–Í»Ž«bá:Ž[PÛÛ-(›U½Üpœëã5ÒK“ò	;Ð†3LÌøÈz4‘ó(:\òáe«¢²}Ð8û`ðÇ³ëéÜìFX—Y£Ðc+å¢œð_h( L¥Ó•ý‘j^Q¡;7ç¶LXR…ºsè3Á"Õa2¤„ü÷þ”ˆ¨Oï5º,‘§=‘Ýdý®­Ý´Ð;|ê8Ã"ò¹>�óu¨|ãMÉcä2õ64PÉ¦¤æˆnÈ|n.—­K.zá°Êß{„òï=RÅHÉ›#S-®­>*Y»¼‚ušsr‚–˜Âª•H:!CBZŒN7îæËæÝ8ÀÑtV®'Ú­0¬wæwbì:Üû˜õ\�“áÙu³»>-d1Ÿi®@uy{X|ýo˜½ž ¤Š<{øÿñ›/ÿeê»¸×‰B°W_/.B£/y1N°â)vT<!™8;»u@ö`¡åÞÁ¿^ùÉ2’Á7 Q°Fç‡ñÃ•Û™³`žJƒŸ}áG_jÄ~ß#<ƒ¨†ƒ¿ž¹8øù]Ë	÷XB�)|d¿{Xd�÷‹>“Ü…}bÉs§å€»PZ¸YºUºUŸ_@r)½‹	�®`„5žhmˆAoMéš5å‘ûÿmo¯­Õ76:!ÞuA7Í“¥Ü%}µÅ,UùA ²Ð:PßGS_ÕøñŸ¿ü�)ëâ}z�ºBn;ÒÕ^Ø£Ú	"=`yá“fga ¤uiœäi`,<`}a0ˆÿž¤H*¸�Zœ�âN€æJb/3ò˜ÑŠ'†ZæP­²>?	pŽ‘Õ›l;í4Ð	¼8Œ£¢à$%yêOz¤Ãú|0òDÕK$³£Á¾	amlcÆ,™˜Îe½ÛàÑÌÅ+"îð|[5.µoÅ<YÅV˜/h& Crƒ_^ºx|öÜW>3ùÄ\’×	áPëâ.ïÏ‚¸V¶…Á‡Ü"Èó5q$¨ìôÿJƒÚ�ÍÕ«�kòå	¢èaŽfË„ûÔ¥qæ^ÜÅÒØ^(˜9Å [mšQj•êýÍÕ
‘…YÍâ‘D½RÝÞÜ)¿TßÜÝ!C¼¬!�ª“g™ßÚ}0GËvóË§ÒÐ¦ìËÂàãWºpæøk¯ý`*Õ\¹ZÝ¬o®–·–hÖVõ i€x—Z½h³…7m²jZ0…ã±(Žˆ/5’Éy‚<ÖSXLæŽÝX0J\E¶TRJ_èAŒm&p†g¦  ã�¾þÝ¿ƒ2(‚ùÂšMÊ:Î:j@å‘èŠ‘¼•H÷�lÇ=©Ñ‰™ÔU1ë.z…2œöäþ=ÖcFÍÔx]ÓNãéå:iª0øäüh{~	Ö±A@o)TÌŠêl%7aD¡t|x‹¸ëào¯Ï¦æ µF=$·lºÕÆ�¥ÑÉÛ 6±Û]^ò™ùrÈÝ$–š5lÚµÍÔ¶t…†)�õ*)ÊZpOzC�Qs)ðQ[ÖXQ|€ŠðñçXƒ¦¾Jæ­àxŒãkˆ<I3û/b'á/„XïCU51�âí“3­Ý~ðã·–¿ZÕ8•Ã.6X?Gáh8MÎ¾1c’zûÚT('L:­€õýNdŽìE‘4/k’uºè§ytDˆŸ/ÍÂba™6¾6cûÛÌ¨agU©ô}û¤· ÿ¾ðæç‡ŽüþíÇ�7®LÝ:…Êðf‚Ï}*w†ìŠ½1SëêMáøÃ¯OÿæÒòK©�P®�@=lnØ‡4×k^Ù«n	Êã[hö±üÍÒõÅRÉ\2Ea8ÒùÜ«Vj›ßÞ©¬=®¼so³úðq­²º»³F×­¶l"¨\ÇpZƒOÞræ1ÝÕ®ŽO˜Ó~EDóÔ¿wŠÿÏa»Ç)£“¯Nðÿ¯/ÿªýéh—&àA‹_LÁÏáÞ»3¿øléÓìèD±C<:ö®|YÛŽ7š Í@"'¸´¶Q.ƒÙ$§u÷4bw	Ëð—šˆI2Æ1öªÐÖ¯�Cêí °½W«#ÉÊŒò:¶2¥fYñÜÄq­oHÅ¨§j¶�%(F—Qó)£Ë�æ4§»šë‡1p!¯¿ñ`¯‰bäë”šq?a©}ß«tí©OI�,LP$h¨]OO6Lm~~Ð"â5w°l0MÄÔ •¨öØ60»<>s•#àI‚™¤ï€“€ýÎtbÀÅSCÒ§Èp¡G2ýIâŠ©ÇñžH3&ãÈ¿Á­sE2Õïªz;}ÿfã…)Ù'ŒÁÿ^›I—5n6Ç:6ýÙÛÑÕZ¼W~¸µ[^Ãì|tMŸÖß.l9d@©ÓeÕ¤7	•J�1¦,�Ï_%cõ¥NÃfaˆE3Î~üê7ß¾Ãü5u‚¦;�®p	ßþñÝÄ¶Oxœ�SkoÚ0ýÎ¯ð‡JIº$êÆVMéÖ	•hE‚R*MªªÖK.à.±™mZÊßÍ´Z>$ñõ¹�s|Ì’¹�šl§\°ˆ¤d"EBŒbi\´Z°Ì¡àJ“ ýx5�×~§ë��Œ 2ú¦´d|j“â{I¾“M‹ƒ†!(å`®–"vh‹WGH6eÜðˆqjØo¢Ð3©öÓÛäö_Y[›tý¾?ö1406¼÷†7Á;¥f@#�Y©{Ä ª³ÀÚ’­©f‚ç‰¼ÂàÚ¯æÐŒº_ªÐˆòi½ß›8ªÃÙÎúFp8DÄ&"'`<ÜÍ¾ãÉÑ­_N'Y;§ÕréÐzùà>ÆMÃ&†uŒsv^
Ž�FØ\;ùüªI°|ªgÍè×cúøc:­þûTéšèþà,r>íG$ü] âYô_œŠi8
šáëùç³3LJ›ÖTíë‚2šÏÄe‹^ä•®Ì
ÃRKZâÞ3oj·¬·÷/‰™ÙÛuÝÆ}ÈZ(�/ qÒNB×‚íœ!bÍz¢\‰ù)žCU¼íüv–Ëé‘§“M�I?œlŠ+k~<·Ò§*'Í¿²Ê)v@­¦Œt>�WÁ®–³J®*ëPZ«ÞÌÄÉxÅ$¨E¬±‡×
QÕÃÎ„L„$f�½ÿ+›¼ÐxDLÈð÷3„ÚEËIÊ¬�w+£eY¥†EW�6·Uòiî½�¼]dÎù_~´ˆxœ­ÛnÛ8öÝ_Ác©••tŠ-N�¢Í¤3&iá8X,Š¢¡-ÚæV¢<•ÄIìGìî—ì9‡¤Dù2»3Ø<´yîwY¬JmØ#[ÔêA®®ÖjÆ6l®Ë‚õU™‰áC.§ý“žôp|µÊ×W/~<ºj`Ó£¥=	ag¥2B™1W‘0³^	F¿?r]‰±¨êÜ4^zïèéS6‘…(kÃ¢¢ŠÙ¼ÔLªLÞÊ¬æ9›ˆ\,4/ØÙO—l¶¬ÕW6f¶UÊžõ€keØäü×óŸÇo.¾¼;ŸœýòeòþâüÃõäËÅ±Ç_Ž���“¸'I%ˆ©ç|&Ø‡é?ÄÌ€|æª¬5<ö3Žß;™‹÷Ù�UFKµ8	n®uWòA¼]Q™ª‹©Ðx¸¢—ôžV¦Ô"»ò¯Ã»YY¬´¨*Yª7ù¢ÔÒ, è/ÀQ}ö óü¤·9¨Xx¶ïÕ
,‰šhñ[Wç¡	¸&<†/ÂïœWæ¢Ìä\
@ÿ‰AÚ—†ç(ú¶š ÷¶!?}ÆKrñp'H‰Vú�9WJdˆLR#É]ŠDOäÜÈ[qe¤•Â_œ«¬=&66¦¼>€G,’hŸá>£%Þ@¿
µ0KO*²ý”D´t—M%î<H„÷Œõ¥¦AÈˆiØ?é‚åÄ ¯ÈQG�Ø[7Ý|ÿäÑRÃïÍ÷7Ž:mP8¯5CW¦¦¼žœ9Žfÿ~À‹‡DJ- _¶˜<ˆÏfbeäÏ
 úSŒÞ¾×�C2Pb]æx»ª§¹œ%¬à@}!F/žÿåÅKHABØÄÖc¦ÖÊ›œuâ½µ‚p@·“¯†WáAÂDëzðWSŸ>w½¶²W;0ÌÆl.+çó
þ±c<Ár9\ŒÑrn£>fÖÕíÅ% Yü“­[ÎæŽ=££´)lÀž[0Ú‚°·rÎ"P•½
~ûf�ÁN=Ÿ˜YªZX,§sºª«¥P›g‰û½•YÜ,Sð^Dt“–Wr6ÛÈ”}U*”2iåÙA³a°iCÁÉ†T$u†µÄÔÖÙk(}£5_ƒ³ÇD|šƒðþª½;ÏÌNŸç–@œNËlý]ÃˆúÉß–eŽeÊxf¼Âöí©úa •¬Ä· Î\>äB»ñf
Uû¬9�â“Ö@ûC0p¼ë„Q#¹–HÊÒöp«#‚F¯;q©­*w\:5C}èÚ•\(š¬ì‘s‘�¹ï€NZ~�™YêòŽô9×ºÔÑMÓž‰:›sh›OâÇÔÕæÆQr®FìÖn‘Á9~¿­çs4�6— KÞ(•®½�Èl.Ž6]g’ÁÇ­÷å}àÇÃA´å×;®LÕÄ
u¬“Gl4‚Â~øaçšJ\6y¹§ô¬á; ûF"d‡„nE²³FÌmt½Ë!Ž8,`SG¦�£€~×½[ikS.­ê)¹7Úk«d¯�ž±ç.z�ý©	ÙfòÜñÑ“Ç½T7ƒÝ »¹iS�,òaEí¦ëZRéu�!”ÀWô‘‚‡“‘jcC àºþt#Q¨Ä&ùÓZ5vÓü�[“Ve2íÿ+»‘ÎVn@cjÁ(N<~û0Á+_†·ÃìTˆß:˜CÄŸˆ¸�}—ôØShh÷²¨7[`÷ï<Hh$pï¦k…ŠÌj­axƒ¢u·{!^U¢€©H-·�‘0K;SÐ€ƒR—J±>†Ä¹0šI#aÏà²†Ê(rèŠe­28œ®Áƒ²²Æ_€CKÆoK™!µJhÉsù€B\ÂŒ|0$@¡WÂÜ•ú+ÓHf nU±R)ÑÑ5Á:Fjšfy+Öœ9 ™€¢yEœ[œ´fà�¨1ð«ÒÓrê[ïkQp‰v#ÎÐˆ~êµÏ½�oÆ—g.Ï®ÇãóË³¿Cà¼l7®H§\ý/à%‹œoá”�F8v”yë5ž›­Vhª-}¥5LÅáôà�Ø±™�Ã4ÌÐœp3Ë¹,ì­÷†©ÒÖÂ�Ý‘#3qŸ 1(¶Y•—Þª¼KN÷ÜZB	OZ ›Ð" ß[§‡oy¯&	ŸF˜›FP~&07ãˆ+ÙNÎ8áª!<‹ @+P3KcókzäiÒ[æ§Ï[½ÑÙF2š¨iê¡&RûÈ‰ý˜M&¡!»m®V¹¦iÜ0ŠvÌfgº¾íy"2»'æ÷æÙ³wƒÕ	ÏOG¬#‹ó³s
|ÐÏm=UV~:M�¼ëwXá7;Òcý"­S\uD�,w¯Éfª~ž´c29#é
cO°Äˆ“Ã"…œ�§° :ÑÃùšÛg¥¡}RµãOµõúŸgéC0îma�Go‡¨+[7µ3*þ±wÔ\äæÚTìÝJêk\ìÊæÉ�ÀÍ�«P!=,Ì¶aerÝª
”xLlˆîÊ€üXÇ‰
Tvø	6m–J¡ðaçÍg]Ñ&[ÇœÉ�º”tgÒ˜B¦Už+¬œ®Dºšòïþ«[Ášë½5Ñý¯TWsÝ;ÕÉoÓÕMQ#]3¦£(>	€÷¦ZHç‘Ýò¼	Ë°@m3Yú)þ×¥hó¡c6…ë¯»—D1_%BAÃª…»	16½Ý_â,/áá×`l$„F$pÄ	5
™Ñøc!D›ô~‰á÷p.×@#~¦»WjKÙáµSçbÏ›BGuËîcpdLi«J¿T`™5CÿöèÖ?$D�€i;Q°Ãh{Î®Ü2c?wËÆãðÚ¢íÚÄR´ôø4Á¯ š�£ßŽt68~IÔ,Œœ¡V6ÝêèíÎZp‘ÛéÈéúš>î&Á®*íº¯»�‚ý¢ak�'áJ-nžv¿gïUÇ4­„‰še¡uXÃ…ö«Q«o²m¾à¼À›•ÈN›è5�ÞŠbÇøás&Œø`·��“PŸ4Ðœþ’W—¼oxüùexport type RangeParseResult =
  | { type: 'none' }
  | { type: 'valid'; start: number; end: number }
  | { type: 'invalid' };

const DECIMAL = /^\d+$/;

export const parseRangeHeader = (rangeHeader: string | null, size: number): RangeParseResult => {
  if (!rangeHeader) return { type: 'none' };
  if (!Number.isSafeInteger(size) || size < 0) return { type: 'invalid' };
  if (!rangeHeader.startsWith('bytes=')) return { type: 'invalid' };

  const spec = rangeHeader.slice('bytes='.length).trim();
  if (spec.includes(',')) return { type: 'invalid' };

  const dash = spec.indexOf('-');
  if (dash === -1) return { type: 'invalid' };

  const startText = spec.slice(0, dash).trim();
  const endText = spec.slice(dash + 1).trim();
  if (!startText && !endText) return { type: 'invalid' };
  if (size === 0) return { type: 'invalid' };

  if (!startText) {
    if (!DECIMAL.test(endText)) return { type: 'invalid' };
    const suffixLength = Number.parseInt(endText, 10);
    if (suffixLength <= 0) return { type: 'invalid' };
    return { type: 'valid', start: Math.max(size - suffixLength, 0), end: size - 1 };
  }

  if (!DECIMAL.test(startText)) return { type: 'invalid' };
  const start = Number.parseInt(startText, 10);
  if (start >= size) return { type: 'invalid' };

  if (!endText) return { type: 'valid', start, end: size - 1 };
  if (!DECIMAL.test(endText)) return { type: 'invalid' };

  const requestedEnd = Number.parseInt(endText, 10);
  if (requestedEnd < start) return { type: 'invalid' };
  return { type: 'valid', start, end: Math.min(requestedEnd, size - 1) };
};

export const contentRange = (start: number, end: number, size: number): string =>
  `bytes ${start}-${end}/${size}`;

export const unsatisfiedContentRange = (size: number): string => `bytes */${size}`;
ó=Dü³>xœu’akÛ0†¿ûW\!Ô6‹e‡Aé\ÜA7F…ë×£Š}iÄ)Hòšä¿Ï’â&qÖO¶NwÏéÞ{k)´m[>He €h.µÉ]H¼Äý×° Òn©h8Â÷‡?9”y>©òËì2ƒäÚŸº,6ó¢UF?23�Â2ŒcÇ ¨]×šK�7ŠÖ¿Ñ6vL4¸ºŸEaÆW.W¡i•ð·š³£l|\ú&11òNþEõ…jŒ\å6Ô.93Q˜‡q™UÇéDá’ÓŽœ>‘Q:†ÐöÞ^�'Ó?)gÍMk»ÝÑ)r+ÓÔ„šJÉ‘ŠN)«Ó¯’&¯Yò©Ú}IR­'ã‹É¶��RbP›'†óó®ìÌŸ:jÞ6¨£��ðý»äý»„Ø!\-íZý ¸2�dæÇG?Ê7%·�4Ã¥�¡‘Ê„îeµ÷ÁDËyoÏR-:…^±ÙáÞüä¸n3© òéžrÖ÷9¶ÅžöÕ'ò|ÅÎÖfg'é›ÍÉƒŠâ„Ûn†‰ÿË²uEã½ûLFë!`û2ð»€bðŒ½{“h#Å‹™;+ûþÔ|oŽùÜwÊÝN¦÷¡ÎÅÿ È0Co‚Jxœ{Ìþ˜}‚‹‰±‘áF¯™Ì ,¦¸ÿxœíZ{oÛFÿ_Ÿb!!•È¤EkK2;EŒÆNêGPÔv/4µ–ÙP\†�Úª# â>á}’ÎÎ>¸|Æ�\ïŠ; ˆÈÙ™ÙÙyüfwÍ`³$#·$]M½)MR²$—	›Ëq¯ÅÚìõ|¥¡©ïÅô§yHÆÄN³dƒÀA4¨2žô†ÿ	q‡žOm÷‰;ë‰7�7­Ael$ÆÂ¬>4C³†¡¾ú”³†AKN³µùAÊv½Œrã§„?š†“©“±½£·GH°…º3çlz»¾üyÅ­?
iä³)ý�.¸Êß¼0§JßPŒÁÓñ"ÖTò™DyÈ½Ç*^3Èx<&Vž„Ù’³œîí0ˆVD£LÌ5 ED$	Ls]ò¯þQÿG^æþGšÓÄËX@3_¯Go0)ÄÃ Í„d*¶^Â$NäÍõú6‰ŸPpët;þ%ËÓó!ðÿ#¡Ÿršf{Sí ^É÷F[7 ý7H7°lÜî¬õµGÆý“ãïW¿íoMz£7`Îvî/¤M‡4ÍÃŒ€p”ŽûWYo¸nºîxsïwy×©ã³¹;e¾ûbmí›ÕµõÕµçnŸû{$5L0yVnåšð’iîÅ¶|&Ä¾+…ÌDŒ`ñ“•Û"
÷Ç`9rqHñíp·ÀÂ¸O€_&"pkq‘ŠŽ\9á‡¡T5PöýÊ‚È¶¬Á²W°ÁZFn³‡&6+�ë}/<^ÞaÑe0ËEn`¨A³ý°(µh½o˜\´º5¡ß^üJ}‘¡ÜÀûe³pK5¥Œlæ.g8&úGº(ò<~§/…‘(Ÿ_Ðd“ÐÌ›¡—fûà—Ë€J¬Ù$ó`NKp «#NèepÃu	º éq’G>O�rÁXH½ˆÓç^ò‘&H7 EÚ¢B³¹ )ƒy�5*ˆèM¶ß¦½©„ÉPî1…nFêA%^¯Oe³FGïÐOœS!º-\WÆq.#Y¹”ðUIJ‡|þL,«.+„,„eØpXÐøø®ŠSI½Ž^Û…X�cZ©§l�ó_„’cªŠJŒ ÓX"Í^‘•dúdÌ1œ^VO¯
¨6“p
è�A?“<‚%––Ë(ºú"9›xcT™�«Ì1Ë�‹”…ì«co6éƒ‘¯ÜeÖÌ)bðê{ÂÇt¡ƒ$ÊñŒ%ÞŒîÀ<éäèxû`wûp8L2³^¡Dp¹U1Ñ{åV@ÝQ±vÔ|Î¢w’OÑ”µ�	ëVÔtÙT€&Î�~-ÍWpÕ'6DT‰îd–u½/ýþÅß¤‰Åàhj9¶Ícö‘F-x½s>pÊË£Ì´æ?çï_üWz7(ÿ 	Ê§Žë¦€h^6_€ñ. /@÷ß€ã+·µC½µt*Ù]“©Û_Wðÿæñ¿Ù<j™ ûHw’5Ê6w—öd«7š÷/ºûÀÄ´¡»œ#‚(ÈÈ-u‡Ì›~©Sí‰¿æ(ôøCñ^³5�>w mqV…§a‘F¡�Nä"�G­F5BÕi~óápÕî#îímhÊ\ß8ð‡ìŽm›…†ÝDËm4�wBoÑoÛ6
_÷f§ý{„]´[4Xô[|WPïÝ10˜í0Èµ*ðÓÑ­±SŸC^1ÖÙ'âòåMG“ˆ;šDÜØ$„­+ÐÍvs‘Têé‹õ"âTßGWgSÃŸN}ƒôrQÝ:CpO”ê/WÇH¢{]D<¼bª>úëŠG�°bŒ‹U!z¸BÐýU‹é¾Z(2m(ñªÎY°Û³}'Q‘òEÔ¤¹wUÊ¬ª×e.ëRh.ïâ´õ¹SÆ•
²8-ØÂ�	ÉÀ‡{»�F²âMJºnñ9€o´¤qè-j�V!S@÷2¦ÀãíuDÿZÄŒ÷ÚiÞ*!œ1¡À¸‚	1J®¥š¬XPVqH»£2[6™�»ÃÇBÅN³u�@‹7Ò²Rˆ•¹ØPÃWnÌª+•{nïÛ—Ø½w†C+’Dxã.ûç)JT¯wuZ¾b¡IÂ’zSâÇ…âmNÓ2ºÜyê]Ã´‡D¯ ø2êˆÇ#!l“´�^%b®âî:r	7Õç¤rÆW|¼e>ZÙp/çq¸Ÿñ\?UÇ×}áëŠ¼æ–h»˜·#LwwgÙ‹wJ.Åß†ª	Vƒ‡ÚàÃ¢<çƒ0¡¹4Û· Ý<he£ºý‹‘º“ƒÓuä-üj#îøÊåÈI ,Oü
í+íätr«¼­dmcÎ¶dì¡4´Ä«¬çÌšApË% »^2):çzÍX$±§k¤Ú¥›a¾Þéã|�f^–—6à5ïÃ–…�{“%žüdƒRŸ%Ó‘ú°AüN€ùv‰ñ*ì›àöýZSì"QlnöP;Ô&3Cr‹ø Lˆq¥¬Ð¨ØZ@ñbùâ¦l5[ÄÔ‚­Ça ú§)d©?Ÿ;Žc.N�—þ»ìüxBÀ”(¦”ü´ÿ†ß¤ônõ‚¬%/Ù?¹€_ã+ìqÆŸ6É§< ™>‘%/¾j0ôõwäÜ±hu–P
ç^æ_‘Œ‘+/š†µáŸ¼€ÿ1žŒˆå%žŸÑ3‰‡<c²�$ÞÂáŸþ QªÚCÛÅvaŸž¥gGçO·£3lîâe#v|:}~^Ö	®¯8ÚÆžOá„˜SJF?ò%M¸c!#ÏðÁ"î2IšÇÜ{ÚDtØèŽÎ®Ÿnl¡xšœ¥O³$§ðF•‡ÜÀ�y3\š”Ð,O"éá¡T¹Üì-»[Nãžèîñ -“K¾4¥ŠŸÚ1‚7N•Æ5%RÍ¨–d2g<=W‰£ñû©*�”+ñÒ½AÏã…„Jp7¾»³2û>æß˜dk-~dáúAâ�]ˆñFÂ.YØ‚œ£ôJf©Ú5ïqì³é³�´K’\¼RÃ=Û¦G4Þ-ûô—þè,=6èó%"U)	.‰­-yò¤P7Ð(„Nuâ<½²©fñë`÷¢Lëƒ²’çkƒ¡–i ç †âs´_úü;4Ëø@­¿"(J~)mæ;Ãe¯H~4SþOÒ�~a¦xœ340031Q(.OLOO-Ò+)f`Ð’³{qôÑþËúgžžj~ÄÌ¢ ÷±²½xÒ#-Üimport { config } from '../env';

const errorSchema = (example: string) => ({
  type: 'object',
  properties: {
    error: { type: 'string', example },
  },
});

const jsonContent = (schema: object) => ({
  'application/json': { schema },
});

export const handleSwaggerJson = async (): Promise<Response> => {
  const spec = {
    openapi: '3.0.0',
    info: {
      title: 'TeleUploader API',
      version: '1.0.0',
      description: 'Telegram-backed file uploader API with redirect-based downloads.',
    },
    servers: [
      {
        url: '/',
        description: 'Current environment',
      },
    ],
    paths: {
      '/health': {
        get: {
          summary: 'Health Check',
          description: 'Checks database connectivity status.',
          responses: {
            '200': {
              description: 'Database is healthy',
              content: jsonContent({
                type: 'object',
                properties: {
                  status: { type: 'string', example: 'ok' },
                },
              }),
            },
            '500': {
              description: 'Database or server is unhealthy',
              content: jsonContent({
                type: 'object',
                properties: {
                  status: { type: 'string', example: 'error' },
                  error: { type: 'string', example: 'DB Connection Failed' },
                },
              }),
            },
          },
        },
      },
      '/api/upload': {
        post: {
          summary: 'Upload File',
          description: 'Uploads a file to Telegram storage via multipart/form-data or JSON base64.',
          requestBody: {
            required: true,
            content: {
              'multipart/form-data': {
                schema: {
                  type: 'object',
                  required: ['file'],
                  properties: {
                    file: {
                      type: 'string',
                      format: 'binary',
                      description: 'File binary payload.',
                    },
                    fileName: {
                      type: 'string',
                      description: 'Optional file name override.',
                    },
                  },
                },
              },
              'application/json': {
                schema: {
                  type: 'object',
                  required: ['file'],
                  properties: {
                    file: {
                      type: 'string',
                      description: 'Base64 encoded file content.',
                    },
                    fileName: {
                      type: 'string',
                      default: 'file',
                      description: 'Optional file name.',
                    },
                  },
                },
              },
            },
          },
          responses: {
            '200': {
              description: 'Successful upload metadata.',
              content: jsonContent({
                type: 'object',
                properties: {
                  public_id: { type: 'string', example: 'xYz123' },
                  telegram_file_id: { type: 'string', example: 'BQACAgQAAxkBA...' },
                  telegram_file_unique_id: { type: 'string', example: 'AgAD8w...' },
                  storage_chat_id: { type: 'integer', example: -1001234567890 },
                  storage_message_id: { type: 'integer', example: 42 },
                  file_name: { type: 'string', example: 'document.pdf' },
                  mime_type: { type: 'string', example: 'application/pdf' },
                  size_bytes: { type: 'integer', example: 1048576 },
                  file_type: { type: 'string', example: 'document' },
                  uploader_id: { type: 'integer', example: 0 },
                  created_at: {
                    type: 'string',
                    format: 'date-time',
                    example: '2026-05-18T10:00:00.000Z',
                  },
                  download_url: {
                    type: 'string',
                    example: `${config.baseUrl}/f/xYz123`,
                  },
                },
              }),
            },
            '400': {
              description: 'Bad request.',
              content: jsonContent(errorSchema('No file provided')),
            },
            '500': {
              description: 'Internal server error.',
              content: jsonContent(errorSchema('Upload failed')),
            },
          },
        },
      },
      '/f/{public_id}': {
        get: {
          summary: 'Redirect to Telegram File URL',
          description:
            'Gets a fresh Telegram download URL and redirects with 302. Rate-limited by IP.',
          parameters: [
            {
              name: 'public_id',
              in: 'path',
              required: true,
              description: 'Permanent public file ID.',
              schema: { type: 'string' },
            },
          ],
          responses: {
            '302': {
              description: 'Redirect to Telegram CDN URL.',
              headers: {
                Location: {
                  schema: {
                    type: 'string',
                    example: 'https://api.telegram.org/file/botTOKEN/documents/file_0.pdf',
                  },
                },
              },
            },
            '404': {
              description: 'File not found.',
              content: jsonContent(errorSchema('File not found')),
            },
            '429': {
              description: 'Rate limit exceeded.',
              content: jsonContent(errorSchema('Rate limit exceeded')),
            },
            '500': {
              description: 'Internal server error.',
              content: jsonContent(errorSchema('Server error')),
            },
          },
        },
      },
      '/file/{public_id}/info': {
        get: {
          summary: 'Get File Info',
          description: 'Gets saved file metadata by public ID.',
          parameters: [
            {
              name: 'public_id',
              in: 'path',
              required: true,
              description: 'Permanent public file ID.',
              schema: { type: 'string' },
            },
          ],
          responses: {
            '200': {
              description: 'File metadata.',
              content: jsonContent({
                type: 'object',
                properties: {
                  public_id: { type: 'string', example: 'xYz123' },
                  file_name: { type: 'string', example: 'document.pdf' },
                  mime_type: { type: 'string', example: 'application/pdf' },
                  size_bytes: { type: 'integer', example: 1048576 },
                  file_type: { type: 'string', example: 'document' },
                  uploader_id: { type: 'integer', example: 0 },
                  created_at: {
                    type: 'string',
                    format: 'date-time',
                    example: '2026-05-18T10:00:00.000Z',
                  },
                },
              }),
            },
            '400': {
              description: 'Missing public ID.',
              content: jsonContent(errorSchema('Missing file id')),
            },
            '404': {
              description: 'File not found.',
              content: jsonContent(errorSchema('File not found')),
            },
            '500': {
              description: 'Internal server error.',
              content: jsonContent(errorSchema('Server error')),
            },
          },
        },
      },
    },
  };

  return Response.json(spec, {
    status: 200,
    headers: {
      'access-control-allow-origin': '*',
    },
  });
};

export const handleSwaggerHtml = async (): Promise<Response> => {
  const html = `<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>TeleUploader API Documentation</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.17.14/swagger-ui.css">
    <style>
      html { box-sizing: border-box; overflow-y: scroll; }
      *, *::before, *::after { box-sizing: inherit; }
      body { margin: 0; background: #fafafa; }
    </style>
  </head>
  <body>
    <div id="swagger-ui"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.17.14/swagger-ui-bundle.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.17.14/swagger-ui-standalone-preset.js"></script>
    <script>
      window.onload = function() {
        window.ui = SwaggerUIBundle({
          url: '/swagger.json',
          dom_id: '#swagger-ui',
          deepLinking: true,
          presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
          plugins: [SwaggerUIBundle.plugins.DownloadUrl],
          layout: 'BaseLayout'
        });
      };
    </script>
  </body>
</html>`;

  return new Response(html, {
    status: 200,
    headers: {
      'content-type': 'text/html; charset=utf-8',
      'access-control-allow-origin': '*',
      'x-content-type-options': 'nosniff',
    },
  });
};
S{ZRëÆ`xË4þÒGä>�ôQconst publicIdParameter = {
  name: 'public_id',
  in: 'path',
  required: true,
“5.“o!};

const fileInfoProperties = {
“'6“°:“=“P5“Œ6“Ò0““4“a“�';},
};

const uploadProperties = {
  ...fileInfoProperties,
“kG“ÂE“A“h8download_url: {
“0“)/},
};

const objectSchema = (properties: object‘F&±íê
objectSchema(uploadProperties“¡³�/publicIdParameter³¡ publicIdParameter“¡'“ËXobjectSchema(fileInfoProperties“¡“�'³)©YžÆí‚Zxòþä>Ö;°È“ø£³ž0FileDrop“Ú3File upload API with stream³<;
           “w
L“i!³Ð
/413“Ñ"equest body too large“1'“_Request body too large“"³Ï“lDown“U
$-Redirects to Telegram CDN for direct download“h·“ï“0•³ÎÐ status: 200“ñ«FileDrop³¨^“04"|\L£xœ31 …œüôôÔ"†E×˜/¿]ñ·�oÖa“y¬fægMÀ
rSKŠ2“‹^]QÝ.ëð¦¯æØÅ}¾$ÊÅQQZ’™SÌð;Jî»÷—Kþç8äÍ’æ]]Êã¿÷# Ô²)Â¤xœ340031QÈÌKI­Ð+)f(çáÛmsžS0æÓÒ/ËØûÞIÈíü Óžê³Exœ•”MoÛ0†ïþÜINà:wÁ0lÙ°!KÚÃP´ŽD'ZeÑ�äd[›ÿ>Ivì~­@s,ñå#‘|mY7d¤¶Ž4T†j`ýŠ�'Él:M`
šFI^:Iúì ‚¢íX©·
C&']ÉmkPx˜ÛÁeÇÈ}v \éÐCæLáU@Xp7áa#¹¼É TC4ävNõFj'�@­kZ!n‡á
–‚Ô°!·©"œDM�ÚAÊI‘‘QdþÞuã•™ºt“k‰–‡
CbúíÇz5ÄQï¥!06T4KÂYîÔ…ù©97X:\Æíô.ˆ¥ÎÑÚÜƒòåúËõrq±XÂý=0©+b™Wvgª[ç}Ù©WÀÓ˜“5ZWÖM:É^ŠÇžÚô¼ˆßàL‹p|YûË’î0ñO`U¶Ê}GWàhö’c¬’
…¡†Á1èœ)µ²üŒÜÙ¬ö8E©¹j…wJ7þaðS4†ëŒÀü³�ûË™.ëpøc§°ìÔ^÷ØPÛÛ€�52|{Cˆ0$Äo±¯Âk–_a/­ÜH%ÝŸ×NïiÑá×¼_Á#�¬ÖŸ×‹Õ¼›Ï��Ödƒàýÿ¬òl»³½ŸîÓŒî5H'“Øg�}s®²ä8ñü›Ò{¥Îÿ�br†àƒxœ»Ìqžm‚ÂfFif®ÔŠ‚ü¢…j…œüôôÔ"…Zk. ­±
H¤xœ340031QÈÌKI­Ð+)føã)`ç¯fÏûp‹HÛ·Ì·îÆ×K Ó3·Çxwˆóimport logger from './logger';

interface Metric {
  name: string;
  value: number;
  timestamp: number;
  tags?: Record<string, string>;
}

interface MetricsSnapshot {
  uploadLatency: { p50: number; p95: number; p99: number };
  uploadThroughput: number;
  queueSize: number;
  errorRate: number;
  cacheHitRate: number;
  botUtilization: number;
  timestamp: number;
}

class MetricsCollector {
  private metrics: Metric[] = [];
  private uploadTimes: number[] = [];
  private errorCount = 0;
  private totalRequests = 0;
  private cacheHits = 0;
  private cacheMisses = 0;
  private maxMetricsSize = 10000;

  recordUploadTime(durationMs: number): void {
    this.uploadTimes.push(durationMs);
    this.totalRequests++;

    // Keep only last 1000 measurements
    if (this.uploadTimes.length > 1000) {
      this.uploadTimes.shift();
    }
  }

  recordError(): void {
    this.errorCount++;
  }

  recordCacheHit(): void {
    this.cacheHits++;
  }

  recordCacheMiss(): void {
    this.cacheMisses++;
  }

  recordMetric(name: string, value: number, tags?: Record<string, string>): void {
    this.metrics.push({
      name,
      value,
      timestamp: Date.now(),
      tags,
    });

    // Keep metrics bounded
    if (this.metrics.length > this.maxMetricsSize) {
      this.metrics = this.metrics.slice(-this.maxMetricsSize);
    }
  }

  private calculatePercentile(arr: number[], percentile: number): number {
    if (arr.length === 0) return 0;
    const sorted = [...arr].sort((a, b) => a - b);
    const index = Math.ceil((percentile / 100) * sorted.length) - 1;
    return sorted[Math.max(0, index)];
  }

  getSnapshot(): MetricsSnapshot {
    const errorRate = this.totalRequests > 0 ? (this.errorCount / this.totalRequests) * 100 : 0;
    const cacheHitRate =
      this.cacheHits + this.cacheMisses > 0
        ? (this.cacheHits / (this.cacheHits + this.cacheMisses)) * 100
        : 0;

    return {
      uploadLatency: {
        p50: this.calculatePercentile(this.uploadTimes, 50),
        p95: this.calculatePercentile(this.uploadTimes, 95),
        p99: this.calculatePercentile(this.uploadTimes, 99),
      },
      uploadThroughput: this.totalRequests > 0 ? this.totalRequests / 60 : 0,
      queueSize: 0, // Will be updated by queue
      errorRate,
      cacheHitRate,
      botUtilization: 0, // Will be updated by bot tracker
      timestamp: Date.now(),
    };
  }

  reset(): void {
    this.uploadTimes = [];
    this.errorCount = 0;
    this.totalRequests = 0;
    this.cacheHits = 0;
    this.cacheMisses = 0;
    this.metrics = [];
  }

  getMetrics(name?: string): Metric[] {
    if (!name) return this.metrics;
    return this.metrics.filter((m) => m.name === name);
  }
}

export const metricsCollector = new MetricsCollector();

// Log metrics every 5 minutes
setInterval(
  () => {
    const snapshot = metricsCollector.getSnapshot();
    logger.info('Metrics snapshot', {
      uploadLatency: snapshot.uploadLatency,
      uploadThroughput: snapshot.uploadThroughput.toFixed(2),
      errorRate: snapshot.errorRate.toFixed(2),
      cacheHitRate: snapshot.cacheHitRate.toFixed(2),
    });
  },
  5 * 60 * 1000,
);

export { MetricsCollector };
6#öyí˜x] ¢ÿ÷¥P// No imports needed â€” logger used only by setInterval which moved to index.ts±¸
“Zd"ƒ«x340031QHÎÏ-(J-.Ö+)fèöo-ì‰f›d¶àýÊù*Qó¦Ÿ4„(KËÌI)a÷äà¾êq§éŽˆÔ©ýë–;o#`U’Y R°UÆ?³ïˆûßÄ.%†y^|Š(ßUPl¬›’Z’š\’™ŸR*±üPèŠ»þ~ÍZ­ŠG¼õwÎ_ôû TiIjn�nqIQjb.H¥JÅú¾£K$¹ÅeÏ(bæÜù¼ª²,1'3%fd÷Ç•ëRÃýQ—îÏ>¾|é-¨Â*ˆûÞòhìw{ul®Ÿ…˜nÜ¡ß›Ÿ:Ö ÝüiL±axœ­TËnÛ0¼û+¶¹Ä29‡
9»— IS4.r%­d6|¨$…Dy ýˆ~a¿¤KQ²•4¹0l“Ü™�åÎr6™ÀJËÊ µ\+`¢Ô†»�„BÈ6µºÅ
.¬Ó†•Ãd6ÂûJ®©p?Ý¢°_>ðjž@ÕBÌG£Ùd2‚	\VŽ™dXHw‹)Ò•qö–WÉáEâO`½A¸éin ¬ôqã]H`÷’£·aj$ö®®ø.‡öfˆìB()ýÔÂyÁLÙ“8
¢’KN%õª?UÌ0Ùe<†Ýuë´.
4ñ0®¿†¸Þ È^Ö�Š¥ó·@¯´-¹¬ûô–NÀi`Î¡¬ÜèºÚ(ÛªWšNÒAk0� m‰™ÊÛbw©m�´uC¦•u Y“bß¸U+bãA	,Û˜ú�.(�Tk�L7_–��dêqQ�ASÏ5‚¶LÞ6æ3,Nà‘ñÆ¶wÿôôÅžùUIN<~OLÔ „»ÛÉi¦ï¨ñc Ïs>�Ú:ýey³{G/¨ûÕU£²qè³ð�+÷ñÔÖ·ß¡à“hÞÐá‡ªOÿTò?ôÎf°úv¶>[�ž'/ÄBï •`\4OáËåšö—½ç=Ë–†‘xþÉØ¢Ê?ë¬–¨\Z‘fºTÜ’óz¶:�Yg�É@dum2ïLÞ§ºšåþüúý†h8ŠÜûü 4 iš9“l©Î›Öä�>I~`ælx•öŽ? ýD*y'xxþ¬Ñº½®ÌkÃ*ÔÕ^-ÇqG${û¬öŠu±ëz[Ž…ñÅ×£i £«ž‚Ã{7t‘°ÌÕíz§Í­ÏøºµÃ´�I¢÷:Ý½ÔÔkúüV?©º°xœåÛnÛFöÝ_1Åv+ª d'›f³r]¯ÛIŠ¸1çaÖˆJ¬)’%‡¶ÕDÀ~Ä~á~ÉžsæÂáÍJ�¾mÀâÌ™3ç~™™x�g…dŸY•&qzÃ¶,*²5¥Y(fQ¹ŸÃW\Šrt¸+Ð$[.E¡Á¦Ó}õ½§¡¸¨½ýï¿ßcß³÷<É†‰{Yð@–Œ§LEV°µ(K¾
ŒVéMšÝ¥ì–'•˜ÂR\ýÏœ|­WLØÕJèßÅ¼€WË•ì�Èeœ¥>+Ä¯"ÀŸð‹—8"d0O	[!dU¤¥ƒÇPQÊ"N—µ¿'î‰Á KKÉ–Bž!ä…<b­œ‚Ç3½˜ýÄ>ï1¦6ÑøcÀÁÓ@d#4ìXMLÍÆ3öžV+¬ãÃ½m-»“r“«"K³ªb�Ý
!“uÀ‹‹âD‹0ŒËŸ´‚´pvÇ‹~!$Bð�ÐÝ!ÀÝJ¤ð%X–‹‚“À"'eKò¸Ó%—+þÉ¢Ì’J
–ã·Ìhu“’ù‰ài•_Ü9ÒzÄ8rÅ<ƒÙˆäx©ŒìÇÛ,2â”€ÿ2Æïx,µ�Úå 1Öp¬H5c­ìqŠbðFçÀš‘hMN4�|°zƒÍgZ¹-µ+Ä[µ™Q»’‡\rŠ2(â…|)³ö"Í"ÈŠpŠR‰S)Šˆ‚¡ìR$q]V‹$& €Xª4þì;E*ã(>c9A½�Ðõê+‘ˆ%ê‹ví,“zwÞµ¸³5óÀ„0Ãƒ"+K¬¸,Ç=˜?ÐÊ.þ×§ÊòD½â@,�+ªK-:�?@ô/ q¦Õz!
ƒÓ¸#à¾‹å*V¶¬—ú&ßEô¶ˆÁax¢hHùZè…øý|¶¹¹x}qÆä&†)2|µf¯ÅLµ×�å—ñïÀdÊ)JCŒ=Çï6YFR“8�ÀöŒ=Ñ¾^˜ÕÔã³|•ÉÌg· ­ÌDºšü>R¬
ªT[«¦Ê“Œ‡ÖbÌgW`WÀ#x�!ÊÊ!â€q@Ô•V…úëDÎØ)ü`_L¨üb‘n•+�ŠˆW‰t¸úBµEš±2c ¦H^µŽS½]#ìœž�Ÿ|xsu}þúÍÙõÕ¿.Ï ðŒŒÜtzb—¢˜à†ÂJúIâuYª¡&…Ñbz?cïˆá3¾æ"y³ÙeÆC}tðøIó�Ïö÷Ùã—ÏÑ�Q}3í�xtp� ¤Ú¯@Å«0F¸ƒ>\0ª�eq vñ4^SBøŠ]K7¢ø
®ÓLŠ��äwj´[6CGm!ªrp’§�ã¨ 8ý2ùìŠì33˜•¶Â5&ÀŸ¥Ö™oÄ†´?¯Õ=w15}Žäî+Éúµì|#ßaÜJ®.tUS3¡ióÖ|ƒFžVI¢«µHóëÞ¶X…`|/�­¸‚¢d\ÖÑwë `ºî/‡ÎudÁR9˜¹N�$¾!çØÉìÍ
I¡Âµr¨> þ>ž–yKo´?<øt<•Ù›ìN/x)<Ê¼º¢Pèi–šÝÚà GŠÂÖ;:‚` ŠZŠý;±ÈGìË—ÖãiœIYÝi5ŽÆcSØÙ¡C½ÅÐÒZñîjgôÐ¡QK)#ˆQkÅ0ÙŸ¬¾€m ¬á�˜ÈÐÿûŸÃv‰FÎ¼Ÿ/Ï^‚{$›ÃÚ<žg’�\¾Ö•v	á9�\åm)Ò�ÖŽ5¦~BHø#Sª=$Äe¹Ò³Fô~•ŠÍÅ’T¼néÈ…¼\oÒ+«í.ícDh(žvMÍà ×u‡Ñ¶„Ü'P`Ðf4¿gê�0ûb%‚›-Ä‰Â)ÕÄ¥®VJ·ºâIÔ†*G’¶1Uš@á„([Õ@�ê)}HS¥`4«ƒ0ÖË4.{ƒ…èf›Ë¢s«�u-òY•†¥ÏæOJ Í�Ù;Ø¡¯cA‰ Ùï¹N‘æwª+q‹,ÃÖ¢ã”¨Žœâá£Yú	M²ŸõÖ
®åøã‘BÕPÝYZVõƒ¶ve+ŽßÐ­£Y@:i‰™aÁQ˜ð5¤Á¸4J»àKh<H5Ì»<=÷Ùå//}†.í³—¯ÏÇŒªØ
$Ö¡˜uQÎ‹*Šð  .JyÂG\Pª­�I­¾n1u¢²Hêt=%Ô~PÆ5ÍÈTà:Ef�J]%É„7t¬`¸m¤KM%Ãô!xi2"�Í"q§ŒM�ð:Â1(Õ*]Û´[3aÎ0�„¢fÚü†�ž¤,[`Ð$³$2òw‹Áµ6¨)-cË!–4vŸcdgVÑ`®=½Ô-Z3öœþâˆË×q
¶ü¹Ûýtzhº;éü‚ç�âXý5Å1kÄ¥ý<ŒFšð�¯fU,ÎÓ%�ÃŸÆ8År˜ÀXÝ˜À4ãøG�c1´Ÿ'<NqBÞK3áîÿ{œã,þÁÙ-…×Dà‰’lÕ5À þ1ñ[‰sZV^|ãøìÉ’‹>ÍÑ™å¯àO6Íauv¤˜U‡b‘ƒŠZupÿìì»ï´Æ>>2£?8£�ÍèáŒþÍŽþ½½+ˆrÇ®QÔ·kø¬o×(jáG�ôâß%"ˆ0ÏÚ"B5¶�¹ÆÚ„Ö†÷Ñ…hfy
µFy[£âƒÉ¹‰1GÖäëJ‚©ëYÀÉÖS ¤cgtÃÓ<Ë½ñ7ÓD¤KðõŸŽØcc:ß4ðÂràÃ°Ô&jþígƒ|;ýö3@nç–‹Úñt=Ó„å˜½¥83Å\éi�§€<ô¼›1:®‘Ú�Ò(‘ðå­nF†z´á9ÌË	xˆàëÌÚËXà–1n°i°ï×¬mmâc§UpC{h‚©íx°³{uuu9IâH†�0TOqú ÛïÄo•(¥JîxF||ÑªÀÐî@dôR'’ô‘áPƒoõòþ ØËA£¼QD+¦jšQopâÈžž›ôà£=ñ['x@×:¸:<¹ê]fdí,{ˆ`s~ÝÑäOˆ¹*…q)y°ÂªÆQ‚>Šsx2¥lm¸=4§#ÎØÇO‡æ £I¥ukÌÖÌ�ñmçfì1ÄšI�¾XÐG…2W1 Ï*ÇŒú_@‰¨0“Ÿ<�ÆÆh®Ì¬üXT¥öÜs9%$}`Ö>k×»a¥Ò, ²°%Ô\ë\yrã–*T3P±ƒ“fI•J„ÂIœ–vd|+¨€Ÿ‰ÞxÞs=âŠÃg
´ˆð´�*mh`�QêZÉœ’>Å=V"š¡Y×ïÌQAÃ±œ„k÷jÖšwÀ­QOõP[Õ·í�,¦Ã:b‘”e„aWb°Û€� 3w©BŽæu"-ÇÔUi Z5³ŠyÂXÚÚZCxf7(ÔûÅø#bøÔ,õÍõ™mpH©ÚL»NY ÍÌïÕé&‚Î�Ä›"¡y2¦ô¢µ.ù”Ä Þ@„"Ó¥sXGÝî!V¹dÊäNX¸3PLíÝx�Ï‘©jÒÀ‚suÁ×s¥§¤äÐÊtGÔÔj»…>Oª#l	û*j…ÄÉÁ°8sÃ–ÏvYöÀ‘—âÁAmÝÅøŠÏFŽÖFcczÍ•ÖÞšÃ;£d”ÛÆòxj*�ÒqVÙi	yZx“Gêä­PÅÝÞ)~ûWÙ(Ü™VJ�ŸãaSo·X˜:æ/²½ÿ3Sw²:™ú`Í5‡�ƒxì¿ºÎ4ÏÃNpQ—–¶8¨û�Àê³ífeË	Ìð.'°5å.'èÔNÐ7¥� wUííékê¦c¼ÈÖy%õuÈûW'“Ç?<ÞïY/Qè�™¸îÓZv«Ï@&ú¼°0¼{îZÕÓ'“`ÅÉ°Ð
ï'àYFf·ãåªïÈL‘ö
fÑTšGCf ÀT&¤â Óé‹b“Ëì{£rÅaC¥{:­r ZhìcçœLÏ+9x# |T‹®vO¬Í(1hWó 3-…´÷¸Øä;óc÷V±®â:í
T‹›Â\Ý€\ÂãèuÜ¹ï_£
jºwïÞÃy æUzén¡.ð{vêÅö×vÜ¹tßÙ@\Ëf1tÇ>¼qÚì\¶=:l=¨ý[‹0æ�Îã5¸yM`ôh¾}8çàÎëM
	»–•ÓÏôX‹ÛÚôL«.‡î|†×›&h`ÚôCC«�Öh Dßq=°…½Æ‚Ùö§ô`o¹cÁ®#é,¯thôl¯a9¢«�¢Ý‹Jœ9‡H¢U[%xØë%¼À¸0f °­Dct0…nf®†2¾Ž�N¦w¸ÐÀ]R™§ï�ëG�×�˜L'é=	…zi”ë£åáÒõé<YÛ£D4ëµz¯ûZ„N�û¤C8fcË*õBê¾Ìæä�¬Š]Ï€AuQÚ«W©| QlïZúFl Ð÷	`˜ªÖs„F6P•6wck8zqtZ € ª$xðPà%@^%Ü¹ ¹²vJÁë…¶d0ôH½I@D_õ,¡~H£Øë¹ÑC?Øí®ÙöXþàKÛxW0d¬�b‚.€�oàfÛ€åênº{Ëk H,ƒwï¦¼o7…àÐµo£ºÖ6€ZWÃ/,ÁêyÉÐƒ‰8–ýNkMR°¹�×ÕÚ^õF�’�]Æ·"í†à?õb©~–Õ¸Aî—‚W¶oô�«×jc“£Áv@*èNÖ2(
ÏçöqÛ\Bq‘a3úúýÛÉ³§�ƒ§×¼>RÆ¥ôÎ¼ïÄjÜ¸uq«Â{&íS;OÞ~@dIÐÎY/P=Ük‹HP�îy¹i8[Vˆ^ØAÀ;Ÿ÷½�®évÞGŠãzn*3 Ò^Í¨Q@ zßq¦®ùÕkZM„z§©¯ÈuôT�õ#–2öD‘ø� ßi˜Ö[Ý�t{
ðßš^»g¡�Š»]cÿ¡¦×‹‡ž˜:>ìPÓ·‰µR ½83×|ðyÍe{­–Y˜Ý¥$üïÞè5fèº*’vá�fû¼Š)Q¦…ÙóÌÀî“ëµ~YÝ�êB®i^i»3”œŠDC—¢¸…¨[Ò0R®§âkrt�¢
Ô›Ë¾Po]ýÛ“ÿ&ùe«ÃêÏïßþ‚›Å<‰Ë!?\ 8Z†©ƒÝ¬ñ´Ü7\8ñ¯½®á•êÚË±^zÙlžœûú~ÒZ*ÍšC=5ë˜'ÍšÓ5ëš"MÛ÷-ngµ‰ÊjÖ5­V,"æ§uTPšv5ÿö³Çv?ÚW·ª–·í\ßþÃÿÿx•§*¶0xœm‘_kÛ0Åßý)Îžl‡Zi÷2hH;ëÓB	¬0ö Ú×‰F,»WJ“RòÝw%ÿY`Áz°¤{~÷Ü#Ót-{¼£lmm68¡æ¶AªÔ\Ù×t‘$óÙ,Á_�žuéü–PîY�‡tU19×5éeOÎ£}þC¥W"ÚŸ[²Qçˆ_‰ažikl%
Ï{ç©BÇíñYïDÅÓU<’bÙQ~%ã®ÞÛÒ›ÖB:w4zz*¾µ|Ð\Qþ ÿT<’ÞâsKº"v
?¤–ÆŒ0½;è7'(¿gëpóñ“º–ïfôþ¹Ó¬¹A�µô1¶lc7ø¾^¯ÂyXÅÒ²¾‘–çY´¡|žÐ1Æ/#KbÔçû%Ê:,‘	ûvL4¿¤XÞá=L�ìÃiåÃ H§AÂbhR�	…€–Á¼ƒÙ�ÏÒc1U„¿4_�Î…yì?!»Þïy�rÝÎð*Í]ÿ¾‡¦É"màõªÉî°§vY/¢/w…éÒüÐ½h"÷Û˜Á¥hN‹ä/jLù¾µNxœ�TÁnÚ@½û+æf�9p%”(©¨T©hÕëbx[{—î®IHˆ”�èöK:³¶©K[UªÄÅ³oÞ¼yó„,÷Ú8x‚T«­ÜÁ3l�.!L’!ýPÂq [Œ´«Ñ¿VhÝP*‡f+R´C;ŠÊåÝ>|pF¤n5º©Ò/èÞPã\ÿƒã �«D1È	H\Á°× w5•—#¬F°ñŒ D‰5×M£‘{¹InA:(…Ks´  Ë�ÙÀºc��éRH•ž[^ï…%3Á ÖÄ)UªK©v0_¯í„ÄCºÊ(ëa]=4v«+•õAPUQ0|àƒ·…'‰;$[–ºrX{ˆˆü
Ÿã+°ÎðÜ“§€É5<ÐtóÔAIŽ"Cc"ŒBoZ§„ä@­ðowˆÞo�ØÑGþ¾õvØx<ÿtÿéDä¹xŸ#Ym¼ßgoZãÅ~�ÂÐ‘4l„âKÍoÛw¦Ú‹hUïÐ¨ïGGù(œä·¶LR§9‚?«°ÿs©Ú°|FÏ©6¤cËÌ¬èœ�é=K|ùT’Bù¨ñHº1s£ø"¦ò	èæÑæº*2¶$*+ˆgslƒ\WÌ"RwÍý;/Òo•N¹ÄË¼ªÓo‚sMÚh] �á—ð´º eæÍ¦“óÎÓ)¼ß|ÆÔ%¼ç�"´Q'eñøÌRÊ$(¼‡ËwC•¸º›zvDø-îÜÀ9õ�Ðýƒ‰.äuP4 ±”¯4_ð=m’…Ÿ³òq°’;%h.†qŸàu| ý…¥¥²ðxœ•WÿnÛ6þ?OqŠZÎü£íš`Hê¤iÚ®Ú­h\CQÌ´DÅD$R ©8®a`±'Ü“ìŽ?$ÙñV,@`‹ºûx÷ÝÝGz|xx ‡pµ`šgP[Q»‚\i0VsV
y³„´
X^VJ3�6¢à $dÂÜÀr�O”ª²BôÖÀÕ›‹á“£cX0³€„ÉTe…’¬(VðþåQh�ßYÍRçdÅˆkÉl­9$¹ÐÆÂãc˜¯,7}ZÉ®E:¤È¸å)AŽÐ�\/•4ª37Y]"ÅÇ¬MihÕ�2�ŠYËµDÔa©VÆJYVT˜à›éô&%­VEÁµ�Äü4lŸ€èŠeÝ%ò_òù�U¢³Ü�Ü¿àÐ¼6lŽ[äµl2"ØÂjYyÈµ*¡'UÆOr3®ðInz§­©dR‰¬5u�hp0><„ßåÆ1G„¬Ÿ;\OÆTM‘‡MÅÀï.ÌuÎRWÛ·>  øévG £¨4ÏÅ$Ï2y³ÞØ–Õ˜,2­ªaoÖw;A0=?¡x�™Ó€úyÁ%X]#Q¬0*ôw	`ã„Žjðs4â2Ø¾ÏŽv®TÁ™Œ¸ïÙ�(ëAµÄ–p]uŠ°Z-ˆÜmà©Á¾L9Ï¨‹„Á²}ã¿dwWøô‚\qY—s®O6žî�Ü`H rªu�¦Ü˜¼.ö³žbßg< FÂ/æØàÄ…£{ŠB^j��,Ý|ºB„XéùÚíòÛL&¿Ã	¾æÆELH\Z¡#… ß ã»(T‰93|xü4¢üýç_¨
8ÞXXƒX(Ö¬­É–Ì¸ÒF:³#Â¾×SeYØ¥à\©b¢Á×Ä:´eðÎ¯·d#æ
›T,Ë°øK�~ã8ôTy³Àà 6ÐAƒ^ÔyÎõ	øÏXg§šÎx�t£Ç`ö‘³ŒæÚ¿|é;”¹žíWQ'$ õ¤þÆ¸ïË#l" —¬�• æ.Î¨‡Stiš°‘SÒú
XmUÉ¬H�£–s­Uãù¼bÉÒ.tÂEüªæ–	‚ØŸòlÔñVA-†A†°¢s¾`·BÕò‚]o­9†�vô7Ã6¿¥¬]™lÈcèò(¹eÄºwÓ»~Eáoàm³îŒÎ(m?Ì<ó*æå¹ìÎ$L�Ñ•L!ÁðùžÀ”ôÙ',éÏZ³Õ*Ì›y�fú'ðÁç÷ìþ„ŸÁäÌM¹+ˆè¤Á…•ósØ•ÓÓÆ«´®gg½�Z¶.]®ºNÝu<K¢y”4�=Xû˜6ÖþÐIú›Y‹LÃŠ3ÁÙ‘#Š6‰Îý‘—ô[k³`$I�|é<.õª²ŠÄ{þu¯ãPº$»¹ý‹+öú@Q�L®ÞþòëÅôÓÇW¼ø}úê
±wÂ‰Cu¹¨å�‰ðå+Ú}ùJvïXEúµo¾uIŒqà©¾Æ=!ì‡¥¸í2%ñØ»eEÍñHÇ†\2aC3ŽèÃ“F([	™÷aŽ/nÜ.-TJ�;ö�"Ð¸&¶ñocüqâÍG$+ï¸¼¶‹�vÙj’&¼žàéëdàáÃÎÙV;µÉÅi¥L¦¼hS øºÊ¹Nz¯‰‚lŽ`Ë‚B”Âö×MÐ·Æ¨®èÚ—¸<#¬ýùþ7¾}#6¯:)ï”öÙn¿t“ó„k^¢2’fMî5×p§UNw|ñíe¨—/„©çŒt%y4h�;´íôç¨ªÍ"‰(ûìšBG£­ZGJ;Ä¶ýÚ�ÅeÓ}t}±é¢±�á³Ÿ÷’­ð »åxË¢¢¯ü¹‚�B—Œ¿ÓinGîÓÝ6riÜÕnÏMhÒìådž;ßþV’ôðžÓëv»¾]Ø{Ö£lJÌ1Ùaz°CiÀÝ„æqÓ’µÝá3…{F�ßšÀèútü4öt`ÀŸ†Á‘ÞD®Çd„þßõ	ÜYF[<
DA¿³²š®^¾P÷
ÒÝÇ‹‘ÿmÒªù÷v¿eÂn[ØnøÝ¾>QÔw¹Ÿb£€xƒ1ü�Jo¾Ÿj0†­w¶§¬Ù?˜¡Ùµ^xœ…TÑnÚ@|ç+¦Q¤`šB‚òBh¢„ˆ¶¨%ª’´jû’x�Sí;zw†PÂ¿wïlCAiúdëì�™��ÛF­VA}ñ(Ó,…H=§ÃI¦~6¦Â8Xù› G#mpG	��Ha�6bLuFð ëóŽv¸üÜÃÃ˜Ü;™Ð†BA«dCV'3Âˆ�-²)œFóýNB-ÜDª±‡J„“ÁHÈÄb.Ý{ã†~edÝi(‡´\­1�ã½:®¼bVžM-b´‘‡"1œ t",ÄFd¬‡YJŠ�UÌ|ŽéJqÒa&ÅFþ!¬†ð`'Í¬ƒ¢Y¨ðèn"™JWÇq‹›Aõ¸Õj6[''¹m3òüVŒÈ-<VÊ=²¯žÞw2!ÌD’2Ëmð‡©Ñq6tR+Tä†ÇÒóöÈ4HÍ"o|£B�SÍª†Z±¬»î§îû›ËþýÕ‡/×ïo{?º÷ýËo÷�ïwÝ[œyy55OŠG»Riäø*D†ñç“Ï5y‘†\f”õîˆ²Öéˆ'UFà‚boCý-—wBhÞàŽ»‹ÉJSfk;VõP[øŸ­HKn9ò”ÒSNµ•NÎh‹l>§r@]�,/É/z±Ê9ÝÄè¹Å²kŒ6+ôFaK´Pç¢Ýµ€’›‰éqHÛœÚW¯#¶Éª!ÉùÜŸž°·LrUšÇãªn;y
•¥2Qù‚³s,+ðU_]‡£º(½\à@„§§Ýé¼=ÃQP¼|¶ƒ³=G0¤zÐS3Ÿ‹MOk¢6×¬
öÐó—Ýÿ_8ž¡Ãþr›cU:LOw6Øfw=àuËÀûË—SQ¬¹êþCƒ¯øæE+¾ôQ}‹ê`ÝC‘¿}”b=Wþ;°Xu¼CT¾	Ã¶±Y±µxýé,‰9ÞÈTY*	Õ×z
ùõÙ™n»²jWþ ìX¿˜xœµénÛFú¿Ÿb(**–dYN×W7q$‹¤êÅ6I«9²¸¡†ZÛuìCìî“ìwÌ�ÃC>º]‰$ÎÌwßœx¹J³\\‹T‡JÜˆy–.EO§‘ÚS_•ÎMo#v{ÂLÉ\ý¨dt–Ã×åÀ>ù)‹sÅ�ê æµãéJé�0¹Ì›»¶Vð+6ª¶}&�ÒrÙ j%ó…¿«ÐI¬¿Üà<Ö±Y¨¨¾ÙÝ�´Ôiìm§Ÿ°acëÑ#ñB™0‹gÊ)L¬Ïà¿<±“"Rð	kZ]ˆŸ_¿2ñW5�¶6Ô%aÈ¯VJü¯^ëU‘¿ÄÃ‡âzCÿlfÒ¤È•@†h¾°R-¢Ø|!@Bäj¹z[ö@°�±oÏŸfñy¬eÂgHŽAa€y ©(!ˆH¨9»¢-}Ïü ¿+ 7–ë·*—‘Ì¥˜§P¢ìaâ”�!º‹Û:QqÚA©4À¶±ÔN}	M×RÈÀÎ¤ŽóOV\‰‹8_ …È¯O—à=]`Ž<îLD¸PáS,E:§Ó…Óå*SDŠÁÂ	³pg²'t±œ©¬$%þM¹ƒLÌÅBiáALžfði`§}µŒçoƒ¹ŽÿyŒçW9Â˜•;PI:ù/À»U&:ÅEÛ^Ñ†S:_�vöñ£2E’‹LåE¦�.9ÏYˆŒä6ã8f}ßåhõi&A¦ïnñ>Í�Sã‰ÑƒÏ@2ÆÀ-()ÓRÁ«gÃÉ“§ ¨K@x®Lî)9Î*€aªsxäì+iZÕ
B.°TºT·ÁÆH“ó£�Ÿ+Ù¿ËÔU_ ZNÒôK±¹œ�ˆ‚×g§bggü�Ø¯ONNÄîx2Ú«4¹ÒýdÂF„S `gòëûgÏßœ€60Š}ˆu¾3y–eò* 	ôGK¹
‚_@j¤.ûâðˆ”–€]}•I�ÑŒV�Uä.À•YœÃóñ>}9»ôes³OGEy�?¿Ûâ{1¾TÑlwwg2¿ˆ€WŽŽŽÄv_ì	ï'â¹�l|ÞÊdÔg!mˆGâÃ
Nq;+´F³lº;’>YêWŠY1Ÿ«l Æ_W2ƒlŽ/†â¸È2ÐŒÃˆ0éü%¹Â”—å€/ÓñåKû7í�|@‹²ØP<',ˆ‘Qs6I3´ö\ñ	æÏˆ÷ˆ—¨†w9-òú1Æ'�j ô:{0Î=‹²ïžw©Ž•Jd¸3"£©<Ït>Z}ýB'ú Ññå|þ¹®ÈÝþ­ª«4wœjð¥ø›ü*Ï ¯rñ8)ÙÐðöløâô¢ÚÊcˆþ@õDOÉoFAµBži¨€BPÈôÀ‡Œ^‘šKsÆ…£Ð âë×•ôL‹töælTF!t 7Å½S!u$¦Š~0œÇ*‰Œ¯À(5Èß{äH¤°;âã z»&Ë8ET”Ú¼qúdpWJ‚†Å[�à¾—ot®ò—E’üÖ‚þ@l·;&�Xm°rjyàUZd&è‹ƒ±Žø{µò6Ö�ìÚ\"lsEY¹éL=nÚ“þ€00A@4™„ñ�¯àC\]ÀÁM …[d¡ ¼›šåP‰Šv³ýtˆ‘§Ð&>Çv£ÎAN“8Ï5T:Š¥îöx6M¶w¬àá‹ ÕàîœQ/`aû)j CZ@ûkÚ†˜É‘*LNíïÃk�ˆCë+N§}ç³‹ùûQ]ÏŽòêâP~ÐFÎU0!åZœ¢7'�»è¤óù@Ôì€Ot‹ugòÿ+>É ¤ÁH––8ß)Nƒª8wŠsgRŠ“âØdÉ¹ ‚+Õ IôS
7¶ñº�1T’ÑÞ¢LBÁ@-ÂŠ¹"!¹aimé��˜ƒ£‹`&Ã/Cª0ÍD!è¢Îû¥3™ÕJƒ€¿�Ä´ÕN�E¬;Ò›…d‰qêméé˜6‚¦¯t(²Õ%
ï=ÔŠX0bþi¢?Bß¯e¶Áhø·w_¡ƒ+uR
þÂ°Yq§3—Ï$J™:ã€wD�Ý³	ËÓÞKn.M[Yã’!mDL%IjÔCÄgÀ|Yq;û*¥õQ­“‹	àˆQ–ƒkŸ-Š~Íx]ß…üW}òq�£@½Õt¼)ñÊ@%�AŽLa+Xh,un`I«D†ÊpíosgšÎ¥…Æ´Å‘2¦I"WH
H…EŽex”"$Ô�Òhí>âJƒñyÚY¢³˜bµJb!(ÛÉä]©v@ªVrµÂPFí„h¦C8<ŸÇ—Üái$hNúu=«óƒL^x²+Dßsõ„yÎÒU‰Ñ8Á¢$dèh_,å•˜)±,r¬›!’™]*)2¶1¦è7åÚ"h³ýxtrur¦ò^;¢"…¿×£j˜(‰Éâ°œõ”€ûä€#k ÁÖÇOŸ¶>on¢Þ¯½æâ§Ñ§¯�Ü`[²ñZg�ˆ�A‹Y‹õÛoË¯9<Ä³íGðìûòÑžè!y½
*ØÖkìp ªC0J¤á‡§ó é©v#“V€åAÈ€¡<l’‚dá6`‹ãV+Hê:KCË{(N�(Çº>wˆm)éC§µ´ÐQ ¡Â`°@JýŽÒ%€2Vú §ß\;^o†ß\[ˆ7ß\—tßL÷ù/mnr<ÅéE‰GF‘‡ÇË�åÃzo “°H¨±Coî˜àÈÚ  =™c+Ú%Dg!Ô³#ˆ,-Î¶–ÃzÈáX&ç)Ä¿Å²áÜnà€nºnt×nßj#FI]•P¶¤²Õ�ùCÇ*NÄ\ogãsðáÅx.wŽüû×C[íá_ebÏ�[CßCßµ„œlcX/ 4Bäu£ØŠÚ·Mh£ÄMÐ*Vôõ»šB"ÐÔgKn¶6ÄŠ| tZÖÀúú<ÐÅÂ”§71ŽjÉpÂæjŒ³µôN¨T§û÷Þü¨0Öò Û×‘Ìxj›OÊWí–òYÆ`X*“ ç+0È3Ãt>´†U–„Ï4‹FâD†70"¶#ÅÚp0Xª|‘FP�ŽˆäÆ 
ªÂr¨%1Ã#œ¨ x�b€ÏŽLF		f 1ÑD|E	º>÷Y5(§_|v†(©¡¶£dïP#3ñZÓ'ªöjõ�Ÿ=û¯Æ‹�ê¾ô]][ùrµ…�¢,]Aðâ¡Ð¿ý¯¦•\¸º«UVÕ„w“\3›Ÿztœ]� ^Ñã g¤ïçŠŽAœÆiŸÛqkÂuÁÜŽyqà¶Q'�PG•ðšÃ ÎÂPXFFìÈ¶\Þ÷Šåª€/Kf“¥gó�ŸŽ°]{£ôy¾Øç–G·`ËŒÌ›7qäfC3.¨øÂr¥IWÅB–1r¥…W«Xª¬@ž7:=ŒA‰ q AžA™…:`þñí£«YAu„‡5§àpÆð�½­y;lg	Ö÷….	íìÐ"h–ŠmX
�“�v§ð;è`|9~<Þy<{2¶“›Ú†í§ÁdÝÂCŸÓ@­{‰ª‚Ž%N-;“5k¥ÊFôÖå�n*kñÌøüUçÜ³Ï•J</b<õ4Ìc}Šî6Àûæên:ºR5þÝ8ÒmÈ­
³*HU«P÷ÂrCé\Õ#R ÷³ñ>«®žj_ûÍ×º�-ÏrK7–/Ë••§ß.ûv:£¬8Û>-nÇ«ÛãÌh]´¹_èhk®äz�o·¼{2Þnx÷-þ}ï¥?²Ò ·†€:,Ëf(èÜS3š;67ml-qw…‡ÿ]6^AåÞÁ^Ë1jûÛ¡ƒ—û~×¡ØÎq ‚ìv­VEQ·ä{æ¨§ã'·ä¨‡>wá-¹-Îß½	ÓS‡Lî¹µ¥“nÊ�N\^ñx<0¬†x´¡ö¦§z{îày/ÃÓZ¸¬ÞjÛÂ�ßˆ½…ºì•äX©ðÏªô JÉ¡s	T–¥eâ±sGèò,½
j…%ßó©jë~íìÂ6¶í‚€¶&´'— Ê0÷îëpœæ^PCŸ2\ªejïÔßRœjz�K­Uà‡€¾cŽÞñàZØôŸý»\©v#(j¥¾¶)³¢Ÿê"I¦�>Z›'xbHÃ{¿Ë¥ÁjEª;WÄ|N]ãþ�œ&³8Ú3	ÚÖ3üöÁC0�á˜Lœœë,*- OM3„QþUìv´m«w¨šò—ÜV“ûŽ+:µq¾•ÎïÉªM6���b¹þCìŒÅÁa…ÑÑnbÁÐø\Ë_’ø»±^*_ò0Lk€øJ¡:ƒCD¯X38øe¿«©Ä[¶‡.<ôn®¤}·¿ß�ÓÑ]dâ[ÓúyW�1÷÷@?yZ;O½ÿá:r4Æ3¼QåÔMsË‰F¹T›7TûûÑlHwvÓ§²¹™�WÇ6"­	›_ôÛ^³bÖ3I÷bJy”§gd¹�‹¶h$5H`&UÑWFaÚÖ²²©¾h÷®ò³�:¨*™8Žë¹ºt»ZNckXÜ=#ÌÃLês¾8–ãšÆ«6@ÚËn|é®>•ó/iù7¿Þ Í4oú·Õ. Ã(ï>E¨™:�uyk™lÞØ²ÆY»ŠGGW¾8i·î´apcúLÅcG¸�eúÓ‚77é²³8šoÐÎAåë±PBXnO…Qø+�é‚Q_·Ä¬ˆ°ÝL�c4‚²oŠFì9Žœã¨„û€¨pÁ1»ž�h2&ÖÍµ[÷ï˜†È‚:&ãô"Ÿ�ÌWÜŸ’„çºäOÏïÎ@MËm¤"7þÓÝîµo”WJÓ@ô²Þºy]5
³pSº†ìŒ]`±Y.Ï
Õì9¯ÙÔqH@½#SÂ„Q¬ø ¯*ðW/)D-Ëqlª ´O4"T�?©ÚƒµT5®¡YŸN×cèJ«L”—v-v+™v‘·Ý<ÕJ¡]¨Ê´Ù�8;�4uØê$tkÌ,¼4+:lÁkôÐêÐÀæ0±™}O¶†Œ:22kÛk›«¼¹.C><GV­ŽË^nt	 a7­QW-_ÞV5ÜP¯3ÇëíÉUíV‰•6Ý	Êë$ÿÖ~¶lä�xœëO~›4¡ŒQgâ�‰³Õ6¾YlÀÈ c¢¿¥]x340031QH,-ÉÐ-Ê/-I-Öâ½’b®×G9føÉÙ(K+ž¼ÚéýdÉÕŠƒ†åpusœ>û±·ÊãÿîI'«Ûd?0>o�€ªKÊÏ/).)J,€+nK¾ËzÉ(~wï‚k9¸_Y¨M\W\¢[�ŸŸW{½øÏ©=2Š<ýïÉ*Fn½ÅŽPWöü¥î–†ré¯ë÷Ê¶J$s¬øÚU–œQš—�š¢[\’_”˜ž
×¢ýÄ\/zõŠïåsçmÛ|ÐðikøR¨–”$¸ª²Û®må{É*ÁHÕÞ`¢üÜÇìª0U©9ù•ºÉùyi™épÞ){v29‘5þ9ÑË{gó	ÏI¶P©yepe6ñïhÚrFBq§ã”žM9�S×@•¥eæ \yWùÍ?;»s…OìŽ™2Ö‹KîÙ¼_I"¦¦nº/zÛ%¨êáf†…mÝN
æËC¡
3RsJ2àVKz5øÜÞº2D²"˜õÓ–—µË§xyš �BFjNAjQ1ÃÓ[AŸY3¶ýØn÷zÿ*·’%M›`fåç"×æß‘©wôïœ[°]ð[ã¬éi¡êò“²R“K€�^”š˜«[�X”˜““ŠˆÖ¶~ÕÚÁV+¸ÄÙ8«àÀÁóª³ (?¥4¹$3?O7Õa—ß:­‚Ý%õé×©~¼Z°áÁ‘]0¥I9™Éº@›Ròór*ÑÓ°uTx˜Þ×ƒ¼/ïËLy#|ûðûÒP�E‰%©>™¹™À´Mð³
¹d«¶|<,Î£,¼SºÖî_�˜Tq±±.8‹¤¦ ¥¢ˆ~)³K-e2¿gvÌ4] °ô×¿e¨êáFû=½¯“³2°ëåsõ_Ì4ð
kA(M*MÎN-AOMåŸn,»hj jž´5ÄôG–öªÈÜ·=)ù@=E@�§gºnSÐûÙ§µ¥*¶/
ÞŸ)Ú±9bº/B4–uQü1±£";Gñö¥ÊG»'~ìÿqª¡%6áÖX®›óáý5‹ëv”æðÿñfÈ)GÒLJ‰ hD¤QÕ?‹]_ñ+ÖO¯çÍš%üW3`*BCQbRÀÞ
¿Ã¼âxcÑ•v½êÚû³r¿;!Ô§dÃ�ñ]èúÞO®�|ìÿ²lËlúþï¸Leybzzj\©˜Ú–o÷‹ƒ'^®>öÀï!ÓÃÞ5¨Ò’ÔœÔô¢Ä\¸Ú#w‡*þ»->1íJÍO~÷©–Gú:ájs I®¼Pú›â‚Ž,¯Ù·.âûí%ùª¼XN$¦ÀUÞü÷moMÓ´ …ÚÜ+|wÛ[Ì÷‡ª,OMÒM,È„+ý�ÆÖX)4¡óÛ[ö°[vOå†¥®ªLD�šœ_#©/f'¦=™UÈâœUý|™5­ ý¡LÄ¸’xœåWñoâ6þ½…'MJR• m×uá8-@îÊ®@UØ4étâÜ`À×SÛ)e”ÿ}Ïv�ÒöÚ»í‡MjÕ’¼÷½çïûülèdÊ¸D4 "äôŠ r7%¡<@T¢%r6AÖU{’iUööB	áCœD2ˆoQÙ×dî!!9�GèG	É>:¨ú-öšr!\ß~„ðOèþ¾jB+{ËJŠ*È· æñöÖÚV­Óë÷:‚¶u€¬òáÑñO'ž_«Á;Ë©l„v{�Kÿ}Ð¯ŸùívpÞo6TN¡\*™¼ŸO)mçÔünÐÿýò\EŽ¥œ
¯X$wx2�ˆ²ÉvxÃïùù”)rÄ‰¸‰ 2oÿW¨é–Ënù´äžz'ÇG‡E-ÂÞEç²§pŽK¥­µ;� ´ÿPï³\C²mù�V³Ý÷/škn8K$)r"’]“8ŸÐºÝf§Ý¯w:šA¿í·‚UR_€”=ßòÿì+j»A½ÓntUêÑ‰i9•� 0–¤k°êŒ]S&¬"<Ã`GªÍj[®[<,ÒX>Ä`‚¢¢½8¡ƒADf˜“"NäXg¸cÃ«s6¢ñÁú4ž}jí¨{¬àJÎ¢ˆp¡kÖ¬½õ‚¾×Ø`®�Å<Ñ›Þ[¤öÐ%S!Ž‡.`‹QAà•r¶mZ€ WeÛŽ©¨§mv¨m©ŠHÓ.€E{µ#(p±‘@4F3
1rLt2¤£„“ÒŠ"X¯Úha”Š1ŠiÀ2]®2�vVìä¸´uB1™ÁŠnp˜­· 89b!ŽÆàí"žÒâmYóO!
-ÒL„&DŽÙÀCàã.øxõ|Lð èõ@=K±Kb°ä|J,ÅÓiDC,¡÷¢bÉBËuâî¡ßº�¶k&Îí…Y¼·ÓãhédéÙJCõ×C¥€a™Ç•¬FìÃR	Br†›Lð7”Âc<YÍ-´ÔÒ;
 ¸Ipdçc,<˜PÝIe»nJ„;"@.WHÕÒHu`ƒ›»°j½æÄëÄÑÜ ,ÊWœ|$ešg°-¤!õÿá½ì×Xå¸T~Æ*„sÆŸòI`5c8áhº‘_n–i'Qd?&-L!¡`ˆ)dTVÌü§E~�¨Ïíÿ¯µ§§2ÀãMBaNgÂnH$3ÐÎa½zx˜ûFF­¯ç`Œôp­î:rít¥+49ôLi+W ÝÐbÅr>–>¥´=fŒy¹+&dÃ9éMKÞfkËÇ¤4=]¥Çñsãº¢„5„/W"Þ£öO¦åƒ½÷ô¡
»Y•4h“g:D¶21ê.ÝUè‡jY¦	Ëp6ÓÊB¶è
pÀ¯3²à�TJÄ¶7á5-ztBìtJ¼×–à=ÐÌVñnÌfðî…ž¼"pã¯8"¾¯IÔkÆé_z Àv«™¶v^þù[À†­ònÊM†´‰—ƒ¬»-óxÇÈØœêêÚ'áK	Q7ÁtRˆ'tÉòhó"QœM~RÐ]d®´žf_5 ¶‡Ï#[ê
æó�‹îòîó®èe\mµöœë²ðÝ·ŠœâaÖ6û1;	à'Òßs^z|CŠýíîI¨¾¢{0Ï4ï:W1’'ä{Ý+¯¾ ·ð]Á‘ji}V¿}™p½ƒxœíXmsÚFþî_q2#‘€¶ë¦0´•A©ÝÄà±œ¶3™>‹Ã(:rw2v	ÿ½{§“�Ø�dšéË�Gb÷ÙÝgßîL¦”	4GCÂ}\“*"÷Sâ‹*
Z £d\ÇQS.ŒÖÞžO#.@|„ãP¸Ñj#ó–<4,ˆnªè‡1I+¨ý#šï!4eÔ'œ[$º{âïÑ§OíD´µ·hiTN¾1�··tÐ4Žû—ƒËþk·gT‘ÑØ?8üî¨éwj]÷•QiD½Ëþ…ó‹;èœ8½žûfpÚ•:µ†m'zß¿üÁ.ë;ž;x{ñFJŽ…˜òf½NîñdË§“²x×¹tò*SÊÅ#ücz˜“é˜7Ÿ?þ3Ø´û«ñÒ¶^î7�öë*	%¼óþÅ¥Ä9´í×zý®;p{¿ÉßSÝ„dÓpºg§½�s~ºä†ÑX�'>#¢&è-‰ò
žëy§ýÞ Óï¿>u=çÌÍ”RÐÇäÏœ?’ZÏíô{]Oª%.ëôãXŒ!‹x†¡öU™¦aYuÎüz	ÂF2^—×'Áp’f¤.ÕJZÃ¦¡�b„�SfV6i¥ÉÁ˜�‡“ *ÆÜÊÄ|JoÒÃ"E%ƒY `A�˜n"Žp4Dw„#°‡°VDSüR<,8�bëß ø8�³d¯™¿zýž•Tz0z0ç(njš ‹±":¼èÈ@ÒÑ¢R±õ”’©, èY(¥£Ã˜…FU½ª´r¶¥ÏXÄL†%¹²ä‹Žòù<qËÔîU5U/ÒÓÁTJ*Þ‡DÍKÍ«gs­»°žÍ3K‹«Jz{LRÊ±E%ã“‘`�#D¦\fHü¿Jçý£|öâ047‘9Ááˆ²É’M5*ËL>í�al´º•º¥F£ðá¡¬ŒŠ¯ã“µ&G(ü%#[deÆO`õ!¤ƒ&xm�jîu‘$P^¢–xefµ9×…—xTÕOËy”¾™à{ç†œñ¦®Þäý¢T_‰^RÖ˜‰ß1–…µ„[´¯Š„-5:48ˆLãß×ÀVûHÍìÇeÏ±·ëOË¥ì=-é�—^ Hû¾ßBZ%ÇXÉ^H0K’·k²¤f1WKövg»µ=Ý[°]d1ï¢UÅ
íÕœ‚°Eð°Ã;g±–i?!xHÏ±ø4”!µŒÊ;û½&<u6‰G»ª‚Öž¾‚%PHW.§Îæw|¹m4ðOVÌ	‹d¢“Í¥/æ%ž1¦ÃTT“¼AžØhŽHÄO!ù¤?2åfÚ4Íq„Å­r©EÏ×.H�™öd5ôšÔ›p¹,s[r×ýXt²¼&wÉs©ÓžÍ‹Ð+‡’íŠwCil±z$Žf0�4c4ºA²œeÚì?¹	ó+ö³{Ê�9ÌÐÇ¯Í[|' «è6J}þHÞÔ¨T;þF>Ä¯.�•ü1ño�•ð¥”5¯’•¦¼X=û
“5±­ )¢ˆ2Â!ß
&åû3Õ#ài³rŽ»Xn½8å�H3¨®cpþDŒ|„SgÖÉëOòßºì/´“m‘ÒO¦ºÚÃ=¤>Çpg¯ãi�³?V•ðsÓÜÔ6h±ÎvBÕÛ–„Qü‰pÕD«•˜·¾’ù"ø¯©6|lÜ™[m¼uð…�·†O´VŽ:8éŒË¯BìÁfC†À
š�I””`ÀÑ0àø:$òJŽùCäê/$P Ä�;Ÿ�%¿L †›ifrº(Ñ|ñ¢¥!’˜E É§€A¬Bž#zÛD²û³ô/ò†2÷O2‹Š9Ù8°Ä${¦v¦0äÊó­°7á€‘}V)0Ÿ®³J©R Ž
°?¹NÉ¾m—Ž¨’	ýccÓ^\—)X‘4ËóªþÖò/ÊÕ·ÌÓ¡Ý(5¥2%åTÀj´»cBì„1Ê ²ÞFéH�¦ÉˆX“h{õòÁå:Ø�gHr~¿þŸcÈ±öt«Ñ¿ãÐ_×¿N3Ë¿¿ |~Â½·¥xœÕWÁnã6½û+x“Tx¥¤»'Ùõ"I³M�MwádÛC²4¶ØH¢BRñ¦®�~D¿°_Ò!%Y¤íØY§—v@“Ã™7�Ã™!ÍKÆ%Y�h*�ŸdYŸL`Ê8œGqÚ'	ˆ˜Ó	ô	|-!–}Bñ›³øž,É”³œ8“ª%éz=ùT¹þŸJIY!ÈdÑ#DÙx’¢Ê'À8ÁY…{pj1ãÉPHN‹YŸTÅ}ÁæÅhÐ[¶ê.Ñ˜VÉWFˆ�¬T²âÅ5ª*Ýlª]i¬ÅˆWê	m­©±ë²Ú•ÐrÌ-`#�ŒkõzXÃI³ÙW¿úz¾FÛ­Ô¿ëµ¥6ë©„µôVÐ§3ZDYï´*|¡ÆƒÞjØ ®E"Ñr †�ÇÆ®šà±pI¦)v9<(¦*<TO¹6Q"  ’ÏxäTÀ°�Ù¼ÉˆËS&WÔ©íèF³Ëç Xöîb»³ø�íÔÕ˜¾”‹’•Ê»xëÚ[lþï‚î‚°û�H^ð,ýJ	åpRÉ´U�0Ü»´æ"´˜ñÂ5¢FÚ‰H<1iUëæ�s†ºœ/E„†ðÿ€Ä!Ë>Þ9!#Ya(¼;:n½ïùçï¿ð£ãK4?þ/Ÿ^OÑèç,©2pß�Z`n™F1ˆ`ÂdÐðëôkÞˆŽÑÄNhERã_±²Mk2	r:ã‘[¯ŠK=�×+4q#P<o—zt*e`ÜHÎ2.‚JÇä›nÊF��n„rcô[mNi{,~@‘1$Ó±4oV·ús1eÝÊA8Rˆ2™îAr¡…^iIÝ—=v>2Ìˆ›®â4ÞÕÍùKx­ó,ßw(b…]óURp�a*ól¤ô=t¼CÁˆ·{ \½mòä<¿`ºþÀª"qìŒôN¥Ë�Íaò&*ét¿Áä¤¤¿[àžËžÌõÔi}Òœ&h}qÐ¶–<ºš®‰]F*I3¨dô‘æTÚJã¢¢*ÇíêöV`F§à;–S@ í²Å—*lZfr%�ÙS
KÌ]h’™S™š†×ØØI(Ñ–Ô‘«ëØªÖíÀµ™@»ê¯4batw
éÙF_KaÛWºÎ)c¶¨¬Û«.Rq]+êv“¤ëp|5:Sä¹Þ [jŠÇöU3o—0Ž|]@wcdÕ,[¨ÌÌjÕŒ�TºŽHY•%d²r\¦Øª•eFc]³ˆ¨bV1­²ì	é0ZŒÆR4�¨$T·îF˜'ðÕ©Í�¦]wWLy¾dÑ#œg^É¤õØ”lˆÛ.¬¥ëîI»©VNøL˜-§æË�qEÜÝâÇ²amk�`¼”Àå“ë(wï[vÔ½ó®=M{­¶žÂ�0¥Å†çÏÉÛ¦LhMÝ¡½µýÓ ,«	ò=TpC	žø”¦©®á‡íMXüRÆ­�7N æÑl\çvçö ë‡aVg÷x¬Ó}�©–á?Qƒb¯×“Ã«t|çØ7³ŽPýRÁ{¹M×{ÿÆ
æ[õ6\�ÏŸ®®×ž8Ë�¡›fTXgÃˆ¯ö¹ÍÓ§î,tqUkÃ yÂl�2!M›ª‚ç€¯!l�¥ÁÑï¥c-�¡Y¿®õõv¿?:²ùª!)9Ý2xJîü¡Š2ûé·‘ìÌ:°™ð®iÂ=¶É�ëŽåEäê£i¨ýé|'³%gAA26(6Lù¸ßÝO-ZT	ïæ…¦~›K|wnãÒÚ°Iê3ÚU}TßÂGw¶éxœÍWÝNãF¾ÏSœ‹Jqªà$K
»EÝ
éÕjû8qqfÌxLHQ¤>DŸ°OÒ3Î$di«ö¢H¶'3çœùÎÿ!Ÿ—\Hx‚8“(ÎâdÖ…	f\ Y§X%"Ÿ`ð±ÄDv!§gÎ“;XA&øÚ“šE+Ù>hµJÁ¬ªÙCxr9�.<»€×DÄå ’ü‘ùTÃÓÓóÑùåÅðý¸a¸±¯G—–û;™oF—×Ãwgã7?/.ÎÞ�ÏOçÞ ß¼Úÿêëo¾ýîûþËÉðælüóõ{E8“²¬¢^ãyY`˜ðùõép4ô9ªû"—H,Já0�l‘_]^�Ù~¿¯îmõz01ÙJì¾Æ!g•Œ± YJ{¤ÄU…BæœU­„^ÒŸ;Ú†BÄËÃ§ �'µÈ’e¬žOPÐvœ¦\£¬-K<”ôâ™¾ùH�—ÈÒœM}–*ÿ×¿WGýã'ûƒ|õ“F\qX $1ƒy¶ôX´ê–aš’ µ
²š%Jê-ä,'b¶ìBÆ":ðú®(xò
iû¨J1¡Á»ƒP`Å‹:¡œ!WÆ‚N‡°ÒÓRW…sžÖírO[¬Ýµâ=�j	ÉY\2ÚB¨Õjú(y)«ãˆ²ÂÓôØY
V+Rû‚´w.%õÝÁ–›´ÀÐÛ‚ãctbí<kÄõnã²þzÏø¬ÙXØÅf¼„e]Í‡«ãˆ¬AÜ¾Ù^u”°•5ªóýœŠ8ƒ?~ûê
!†¤ (UÑpËpÑ:1;·°àâ®"µIÁD~dXÒ¹Î×H„é¡
B)HGÁØN‹‹ì¶AX‘NyRÏ‘ÉcÜ�^Í¸äA7Eù6/ðEªUKeÄY¦eTÿ°Ö%ÓhŸa•VÓèÿ§iaãUÔ‰ä"ØÀæBZ%A¨OHuý=ðö­v^œ›mlª¦óL'c¼Û©½Î€9ÑxŠãœ2ÁËJÜFâd¤¦¦¸Õ«/žôý«Û®9©YNy`ÔzM «µHðê¯ã#·–þ¯`—FÜG´Þ{µ¡ØÆþéEð�ûÿts¥©"Ô'©—çs«h‰ -ñQöÊ"ÎYÛb+c9£õi?¿ÛVŸ�9þQ£ÿ¤ÂÈ�“X«­êmO³¦|k»\‹62¯K…JU*7ší.¯8/6j�ì~6r=ìí0ìU"éå,±I‡Z`Ï!ìQ?Þ+•¼Nh%X�'ð_H"Që+ˆ«%K<Øàº@¨¾o
ŒE`Ë÷V…/�MåŒÌÛ7ÇjšLÍ5ùK
_N~¡ZÞá²
v:©³ÝÊäÔöðÏùÓµ’<ƒ@N;^Ã“ÓÐ/ ÇÏ5°T6^w¬Zë·k j¬ˆq.ÿ¹Ù�Xk{­YêûœGéHõ4{dàèvëÁ8ð½Dmò‚ƒ	_�’½IV—$SLuß<©Ìb–4íìš–ê­×'››rÒ¬šñºH‰é‰ÂHëšÉ� ;‘V…På4ÍJüƒ}_�Ï‰Å,£O®wš¹dë
Rs_Ý_Á—0ðéÔàªv!ÿž9ÙoÖ,4¸½ŒŽ"x‹TžŠª†àõtF_7Ú=Ï#TÝh´kÂÄ±Gü†úéà¤&\"Tÿ·¨šCœi,ãv§úW(•�Û®ã¸ø±ú™[šJ¦bø<5:�b–3Lƒ�•¹ýƒéŽå�À˜"kDñô?c5w¹ÿ´*Xä”ôž#^ž»LE«Žò=|ÁÜ4ß«Ï%ùØwV›n”œû\Õ›M¦uqp2s¶ÂVØÊZ@=/ü£ƒë‰bxœ›&ûAfBŠ@°‚­‚zR~‰¡UI~vjžÎÆYx„Œ
n'xœû óAfƒ#£ÉfCF{^ )¶`½�xœíZmoÛ6þî_Áo’ONÒî¥JÓ!NÓ¦kw‰Ûƒ–h›�Dj"•ØKýßw¤HYoNº­º.‚ÈäñîxwÏÃ£'<•èá©$é~õÐ„LyJq0ï¡�ˆ ¥ÒCd‘�@ö…Ÿ˜—=$’å�¡š¦<FÎ$c¾$B:»šëŒølFR3íy}‘}1Ç)	ûùTŸ²�,Öä2!àÉˆDd–âø˜„!ðŒfªz2I#ÑŸÒˆ€–N¿�ŽÁ3DØM9‹	“�$å¨ð`ÌGãÑðõá	ÚCíã?"g{çÑãï¾ÿáÇ'þþà $Ó—óW?¿Žâþö—S1z÷þz±ü¬•œ�†§û/ÇGû''‡oÆ¯ž×,´(Sßnomæ¶jJûg‡ãw§oêÎÚa¥`.e"ü~?K"ŽC’@`–˜a/^z4TQÑQp9‚äp&ÉB‚Ê›Bq[¿5â»  BîCFhè#–ÅÈæJ�§$‰–>:%2KÙô?UFøTÆ³ÝÎªdö$ç³0‚Å{ÈäÂ¯yÓE{ÏÐ[0Eyš±KÆ¯(Ñ
Î$Ne}ùÍ Õ•�€3!ÑŒÈºÞ®_3õLG(Õ´ÖÇ0é©g/ÀQ$¼)Ô¯ëªgmO=œo] ½½=ä¥Íéþä�o_ ,*Úó ¾Ô"Ôõak83d%?À¦1R]—›± 09ž»jùœ±`6Õ×-ÍK‰àÑq»ÝÝÒ
»¤2\Ð6üN�Òx'ˆ°Ú#ë�Þ›ä—„ùHÈ”²™*±¨ì[î¨*wÔ·Ö‡L}Í
«`Ìª•àQš’§nÅZW; .Ì©ðô¬Ô¿•¢UgÕéèXÇ<Ì"â:ÒøíôP2W­·»ñ+{ëuVÝn‘kÊ¦ü,Y‚zM�nÎ‚=ä¨	§ˆISž¶Êé%Ø±¼ì:»*ùÈd¾pMy‘<VðŒ¤W4 
FÀó×8Gü¢¢™`#¢vKšT��’„k€�Áò£¿gäU¸™P�,o· &Ög�»Þ*�®ûƒˆàT•“�+ƒ³1iÀÒ‡ÊhN˜5'lVª3…²ZtÃªO3Èk¸Ô€ÝM1…iT«°«·@ŒÂ±:öÆÀñ½²ë¤Ø™©¯¹_†÷•üø¬%V]ûlV¥}ÛZ(o¸Vf»~ƒ�Ûø†eÀ¦…A[4·Tá”rU»F¥ëˆ9Ï¢òI%Åýƒ Å†Väœ 	—€,–,@åZËAxƒ4ƒ+P­`oøS °îS\Û„P8¾Ò)†£¹Êús>S0¹˜(–[�Eve’-•Ô+¢jAW»Ù»­Ä¼IsÁh¨l@žH	‹Î§KˆQbGøŠap˜�ðW*ç®9¾l×ça¶t_@ä$å¬ÛT6dôßÏ��™êÇ@¥“Ì¹äêáŠ†D?à,¤ù‡ëFc¬¬©/BÒàR³Z¾dÌ¸$Î…�L›�ù\ÓSÀ|ÓÕ–ðäd³I´YfyîQ_ÇÍžR÷Z^ÿVIåþŠj¿Të ÜJÉëæL÷wvëEÐØ•(Äôw…¦*ï”¶n€åÕ$K7T¦)š¼P�(¦`3×9ÂœÅÒlqdËå�·ÐÜ"À
4(œ|E‰ŸVšÓj·úÙÓ^\HÖÇŸ«+Èã�õ¡d3P–Í7¦EáŒájU9èôlq!õ½]RÀ¡á£í­�Çå©˜Æd¬:X�“$¢�æ©~N›*Ž•\påU§Wö±x0W­B‚æÇpCîS�QÊY-eáÕ[”ÛÙ¼ÛúþJl¿�†mÉyù'%ß…æêIÐµ²½e:Ç‚F(¤9l�nË2œ|€ÇòÒ¤�”(™üd¸9³Šñ©²A�éý¶4 >º—ðÑy	xe>(3‚m ½…rÁV^¨0ÃÎúFÓƒùÕCp×ÞÙ:˜‚0
_t)Tt”)ÚœéHÈ¬bhµþrñ¥stcÿ¿�k"¹ñ@6ïDÈ‚B“ á^¡@	Qg—êê…Â,çh"Ð5˜â™Å­Ç30ô ÓÛaÚ¸ëyÕž¾ŸæÂ÷8ÊÈ�ë+l’M úŠ# ûp9Öéc<	Š¢ÿÔKî†nËI^ÍC§ñŸe�ÖŽá3°Î]]CK¹þÝŽÁ^ÌUÖ¶Ôs?Â£5 LÄ7¡×Lß�`+ø×PüÅÃïÖF¿-8Žþk[¯&òµvú·ú½Rï� {¿Ð-�gý²oz×w¸$ûÂð†Õ`\z¯ú5!ÙþSCã�IªTUƒ¹~Ÿ«~þ>É1å²•xÍYën7þï§à4J¥‘¥\v«\€8Mà uÄ^ì�¢ˆ©ŽÄÍ9åPVdCû }Ä>É~‡ŽÆcÉu¶8@]9"Ïå;ß¹D¥6–]±™È´ox²°TT‰‘31`âK);`ÿ:ùÌ6,3º`½ÙRM­¨lïÙ�"#¸Ç¼Z4Ç”NÅ41ëÒêíI».^|w&r17¼8æB&¢¹Ç£Ê$£T\ªéW�l}vXùÃ[iWìõb©>‹ôÔjÃç7ÄH•^Y³LìÒˆFÐ(ñ·†•¿�£‡ØCv»*f‚Á ¼+>ÉyU±ˆ4Ü'š„tÄÇ¶êÇlYæš§oe.Þ©`½Ó¾bZåk8¾*¥_ÜóRýÞ)É¨�5`•f+Á*»œá,·Œ«”•¤šÒC]BõRW1­©l1$é£Xi­P>”2x.ð@e½$P‘º›:OÙ9»´2¯º^;'�9_ë% ²ZÈd�çY®Õ\€G’#yfñ‹wgÆ¸›4$ZáÅ‚Á§dÌõ§7ÁñâåcÑ~0²jÅMz¦ë�O6#^­UÂ¢O³5l™2€Bé•°þ~Ï1…¥FªyŸ½xD±Æ±.*é”�ÛùðÁU¸³9¸'¯Ÿû·’?/Å»úôÒý¶óR®QU€&]8ávg¹Ö&râ¦‹¨?ŽéOŸ}ÃÆþÑM¿ï?Ì…õ Éôu[*‚ò»$S>Uòöc
YˆO”‡SÖãe™Ë„[©ÕHn)à½pØ	(¡7œ“êdY…Œ|puýñ­§fÚ~²ú³P>ž<züäéôÕÑëáwoÞÖ"k»6}Æ«&úØe¤¤‰Òºü@W:©Ä…0Í×„g€¨(ñ}¯®w�[>eGË,àú 
“•x~¡eú’À@°â+.-Î¨Ø‰‹H‚¿Ùv°iÞ2Åþ‡ö‰¶fížh?òàÜl˜±WôÐæüTØ0„)äQ>zÈä\�”)Yð¥S"PsÔ»Îzñ~jé�QÁÊ\Xd�#Êi‡ÙHúÚÎo¦Ìg­m"1æp{7�#8�Rµ~'”XuŒBX à¤¹›ü„ i£^x�¸<fRYíÈ$ÑÆ€™Z3ð‹Î<åV0·Æ@c4cFhâqS!‹j€þó_ßòY¯ÿŒ�Fl<aŽBœþ&Å7{#[”(!•:~ŽQ6ÈLž‡×ÁÓ’cDµÌ-$ùµwn	^ÍxD?EùÁå ¦“É@øÆ1ÑÛ#2ù9çô7éë4=Eþy^|ò:ÑE	…@•Ï+ÑýkðÁ‰Tí‹‡ß>õ‡(:[³}J8›}ÔXÝ7DÞÜ˜´¬â\¨¹]ôc«�Dô¨–àŒpÐj”¢æÅúèx²ó¬Zð2ŠJéÒ½óÞÁ¡Owßü¼äyôãxÀ&öè§ýR~<ü)&‚tª_}ü;Ç‘Ch9¾îRp8<û*Ÿk°Ö¢ðÏ½_æ¹O™ð(¹h�_<Ï]%å
‚ðx'6D	GYC'Ÿ|%þù,ì[Ýu÷ü}„»W¬A/µuM\¦(ª“{øñN<í¼Ï�7Kdõ5ÞË ™!•fvzüj8yò”-Ðyeô�EžëAhV[ÿãnˆpïµ‰àžðŸ÷CÍ�]ö7=PÈÆ¿œþ(HÇˆMÍ,Û1	µmÁ¹^ ÅIDT¡úq*ç(1Qo!¾ôúwÇ]Ž©r}ï™–BÓBÑüR–Ã`4À>Ê0äÜ&Á�ªª†êØy ©¯ÄÕ«æO/Æ$‚¹0Bw[\}E—ë+ì€¡�[_ÓäL¿�dDºí­¼OÆ“†¦šÚ‹Éô÷Ko@-…ó�ÞPLi2ŒBsƒ ú§vµî×MhSÊ©´ÄÔ¯Ü¨zß£…8C­úÁø‚ëO6•Ñ+ŒV.chn
aÏ]Ùjàæéë�‡°ß¬žQ�°H‹Zeò‘ÿy—ƒ¦®N²¤¢´m°‰TÀP”®‡R5ŠPâDôt)Ò¾2nI“+F'OiDÝ4 µ£žÛ“\æ’ZÐV E˜K…D§i¥ìê$VëVÐü‹æÑ(Èò9X[ã¾†öì·_~e+­z-ƒ³¥˜–à}­ÁÖ%·¦äV/Ùû·4ÅÐqo^îîþòÄ¤¶3øä&¨·-a“íÝÕ³Þø¾“®ŒAë%j/…Ñu;I}ðNû‰¥N'½fº»ËüÒž`z#…þ•¶CØ$4QÙ3ÍcZGºå¼©æ4ãíg¶_Ü2ÐP¡ôÔÜG½rnÁ q¶0zù�ZìK1’]žØúÐiæN¡rb'¾$BÔe4,¸ØäðäˆU<vÍrYÈÝÝúýt5
¸[RM×ÿÔÅœ�ÕxØúºÓTm¿ø?ƒÐõ{½¬˜àX˜xß×‹�°–bõª˜]HÎÂÞŠÑÒk'¾÷¯0"¥"3“½OCçò(ôL¨œ“›í#Él•ÞÊŽ®õûûy‘Ôjš’Îì´£[ùÓ7ÞíÐµÊÎãîj—ÆÛz‘K?
,ñÕbøœ	×Ó¿J¼ô5³æ&È¢Nû˜_ˆ#!ÔkššÓ3,<«=[êJª˜6Šëí.! è-š÷w)::ôLìQb´åu7OU¢'µgÿwI"¹Hxœ�’OOÂ@Åï|Š¹µ$M¹K4‰ÑD=é˜ît“¶[g¦†?á»»¥€˜ Ýö÷Þ¼7­+kÏ
k°$†]F	Ð¢&£	8…ÌØ—eMu£$�î È˜¹‚ä@¥éHØŒ\5cåÆhÃ4ª‰Å‰RehdÙ­VÄÒâØ¬3B19•øøOßNŒû:q4EÅ…à—ÄQñnï`=€Ð0Ž$÷MaÛÒm›�«D1˜þ"a·•ØfÃTý„¦4sÙx8·›pž´ëju©º2™×¶õ+»»:=j+>1í íªW¨¿Œgâ}¨š}X¤:’3!RgÏåè˜ºÉ
gž{�JÍË6ùµüGå>›^*QÏ8§‡õ
ü•DÂ«�¢=ß°¤ËdéJz_Ö=Hq+š,õüwÿßÏ´©�–¸o©'”ü2i˜PÉÞkŸ öO´ýMÛçêÌ€t¸~xèøimport { expect, test } from 'bun:test';

const repoRoot = new URL('../', import.meta.url);
const deployScript = Bun.file(new URL('../deploy.sh', import.meta.url));

test('deploy script is provider-neutral', async () => {
  const text = await deployScript.text();

  expect(text).not.toContain('GITLAB_PROJECT');
  expect(text).not.toContain('fetch_ci_var');
  expect(text).not.toContain('glab');
  expect(text).not.toContain('GitLab CI');
  expect(text).toContain('Gitea Actions secrets');
});

test('deploy check mode does not require an SSH key file', async () => {
  const proc = Bun.spawn(['bash', 'deploy.sh', '--check'], {
    cwd: repoRoot.pathname,
    env: {
      ...Bun.env,
      VPS_HOST: '203.0.113.10',
      VPS_USER: 'deploy',
      VPS_SSH_KEY: '/tmp/nonexistent-teleuploader-key',
    },
  });

  const [stdout, stderr, exitCode] = await Promise.all([
    new Response(proc.stdout).text(),
    new Response(proc.stderr).text(),
    proc.exited,
  ]);

  expect(exitCode).toBe(0);
  expect(stdout).toContain('App name:');
  expect(stdout).toContain('VPS_HOST:');
  expect(stdout).toContain('VPS_USER:');
  expect(stdout).toContain('VPS_SSH_KEY:');
  expect(stderr).toBe('');
});

const workflowFile = Bun.file(new URL('../.gitea/workflows/deploy.yml', import.meta.url));

test('gitea workflow deploys pushes to main through deploy script', async () => {
  const text = await workflowFile.text();

  expect(text).toContain('name: Deploy FileDrop');
  expect(text).toContain('branches:\n      - main');
  expect(text).toContain('uses: actions/checkout@v4');
  expect(text).toContain('uses: oven-sh/setup-bun@v2');
  expect(text).toContain('bun install --frozen-lockfile');
  expect(text).toContain('bun run lint');
  expect(text).toContain('bun run build');
  expect(text).toContain('secrets.VPS_HOST');
  expect(text).toContain('secrets.VPS_USER');
  expect(text).toContain('secrets.VPS_SSH_KEY');
  expect(text).toContain('secrets.PRODUCTION_ENV');
  expect(text).toContain('./deploy.sh --no-build');
});
•¸�xœíXÝnÛF¾ÏSH¥6%YŽë(è¢²­¦BüXÊvw‹B‰GÒ4ä33´¬¦ú}Â}’=Ã!%J¢y,ö¢7¶Džÿs¾oÎˆÇ‰T>Aˆz¬ø ï›à`¢dÞ(mƒÚx¯ŸñBa,Å„O—"AP×j\GqWbºÏ&x>KÅ‡>ÿ�Œº—Ý7·�«áùï¯ßû½u‡W�Ïþ9èö7�éSÖSÃ#]¿c™áR�ƒgE¸¾×w\I£0ðw¦8E¨éÓRú ü|û7øô('ßË×3™F!ÌØ‹"Pø1åä&£P¯©A^ßéÖ# ½wJ&¨ÌÂ÷FÒäÚ«½ÞG^©Ø”
ÃL/ÜSgÄ4¾WÑžÒ”<{š†mÙž¢B†HußS:’ÓK¼Ã}ãPÌà%�¹ù‘‹PÎ¯ö­éRïŠÝßR3i^÷UeaÌE'áY÷m!jMãu.åŽ×,Æ§éQ��)É=Ðßµñ–UªB“*L)¶p09»7o»×}°°«ØŽ¸Îþû›¶k6¼3ô�J±*ú•d¡˜š™“£�j­3&nT÷cÊ"¿µ#�µA/R!$LiÂÓÀ…Á)*—RpsÛyÓ%vè\_w/‡½‹ê´Ì"A9�*'yJžHãªÊ¦T*dõ„ñ%ÇÔ£€jlÇ¿ÿ^ƒ¢j6j;rÎ¸/O5Ä	K#FÂq£Ñ€ùIï)NTî“ µX•×Ê÷ó’¥{cƒTà…~2±é=¯ô÷““>X—ýÙ:=—Â0.ŠÚåŽ6ý ¯HÜãb"wøÍ�ÚE’™Æfy·ˆ¡Â×Iã)UÎ½oÎÃÈŒíŒ¢D3�4_þ÷a”,ç‘�µÍ8Xjf°~–QÓC®íHŸËhæœ$‰)È	èÑPÖ± H	�‹«Þõ°ó®ç(Å¨mÒd…µ~·ßïÝ\ÏonÞöºÃëÎU7³hÒaìc¶*Í-ŸžÒàt»y[¬j©H&iD¥‰ŠÀ¢À²i±R|!V­×áGÛ…XŽ?Ðtdd„§‰Õ“C[š"3C…¥È˜�ÔÊu¾(C÷[°Ú©
¯vZÍLÉ9üû�?m!h7"[á"ÉBª‘&™R�”"f¡ó]Ïp:×‹EIçó9·<ŸÉYÎ·�2J€ÎÌ8•‚GøøÖcš1ÚÆ½
ƒ™xÖ$«s4Q‡r›œ‹2€y˜°±uEþæèQa³êÆÈò:’µ’¯„i:�‚ª)kuÆvTßâÂ÷'\`èWÏd«�c:««¥mù³¬ö×F8U,†±Ý’AÓš\ŠkÇö˜\k¹ZŸ-¨*<dK­i‡æ+¸:³–StÄ”
¾í–­Md(ÉhÆ`h*w÷lo? í~ãT›ÂÍqev¾\ãìkÅæpÔ°o§h¾·­�,Võ£:õs�Þ/*ö˜¹Kj»Å�ä…(ÔýÛ"ãõ{0š˜ÄžôEÛ‰'¬ŒèX�gÉÆìžÇi~V�Z5Çl˜üLfOOÓ~–VqÜ)î–~µQ�Bsóùg
¥ðW2¿,ÔHÞ¹qY¢bmÀçb™Äñ©�:FÂtœ!x”N7KIãg‰`](Ÿ?ß¨ÃËF«Õ<9>­eckçÕµÁÎîfÈÈv–@ý:Þ�C]_�Ý;ÂÞ/E‹|Ü¹.ÑB`™¶K×b¤ÆžènxÔ”‹=BÛšÙÝ¡=jçñ+û×ÐÜix�¾)©¹áÔx‰ý^Ü²™¨Gu¤�R$^Od,
üêí“öaóKZk/Ÿd®T:m˜"JÎNB:*í>�ñ÷N:†¼	Ù¸dƒDEdz!Æ›àè'lNWJê‹ ßÈÜ‰éNkÂ¥ûM'ß!>•�2,E¼oî˜ŸüÓ'ÛÉßPÉbýÓ*£òµ�mh©°ç–´ÃÊáÂM6‘3²!Á·p–Š@ÛP}9½ŽÃ6üd—²W–C»y<w!ûÞê‡¨ÚóŸ
…9)üòÕ''ÄhXrõP‚_
RiCá ‚Ò’z°|¾ºv·ÁkµŽ_ž´;gç‡Ýï½•ÔöÍ‘¤i'ußœ¾j”¤Ï:ýîðýí%ÉÌŒIt»NHbqa0–qIð¢3è”„	:fªPŒHƒiLfºýâÅ‹ïÈOÐ<jÍÓFpzÔ>9nÕÝoiéÝÍí€,Ø[iééÎ¡jCß(êÓ6ãÊÅmBÚmt<A¯ô”š¿þô!‡Žë¶›sº]RÇÙœq×þÀ>Å°,ç,-¥ÎáuBïÜµ"p7¼7[Zá£F÷XãŽ¬ÆºÄRyyñõvÅû¬jÅ� ÖéØaõ3�^Ù"µÕçÿ8�ïfÿCmA¡ƒâ‚òµˆ_½Éx�bë// @ts-nocheck
import { describe, expect, it } from 'bun:test';
import {
  checkFileSize,
  computeHash,
  extractFileName,
  extractMimeType,
  getFileType,
} from '../src/utils/file';

describe('File Utilities', () => {
  describe('getFileType', () => {
    it('should classify video mime types as video', () => {
      expect(getFileType('video/mp4', '')).toBe('video');
      expect(getFileType('video/quicktime', '')).toBe('video');
    });

    it('should classify audio mime types as audio', () => {
      expect(getFileType('audio/mpeg', '')).toBe('audio');
      expect(getFileType('audio/ogg', '')).toBe('audio');
    });

    it('should classify image mime types based on caption', () => {
      expect(getFileType('image/jpeg', 'my photo')).toBe('photo');
      expect(getFileType('image/png', 'cool image.png')).toBe('photo');
      expect(getFileType('image/gif', 'funny.gif')).toBe('animation');
      expect(getFileType('image/png', 'funny gif')).toBe('animation');
    });

    it('should classify voice and animation based on caption', () => {
      expect(getFileType('application/octet-stream', 'this is a voice note')).toBe('voice');
      expect(getFileType('application/octet-stream', 'cool animation')).toBe('animation');
    });

    it('should classify sticker and video_note', () => {
      expect(getFileType('image/webp', '')).toBe('sticker');
      expect(getFileType('image/webp', 'some caption')).toBe('sticker');
      expect(getFileType('video/mp4', 'this is a video_note')).toBe('video_note');
      expect(getFileType('application/octet-stream', 'video_note file')).toBe('video_note');
    });

    it('should default to mime first segment or document', () => {
      expect(getFileType('application/pdf', '')).toBe('application');
      expect(getFileType(null, '')).toBe('document');
      expect(getFileType('unknown/type', '')).toBe('document');
    });
  });

  describe('checkFileSize', () => {
    it('should allow files under the size limit', () => {
      expect(checkFileSize(5 * 1024 * 1024, 'photo')).toBe(true); // Photo limit is 10MB
      expect(checkFileSize(1 * 1024 * 1024 * 1024, 'video')).toBe(true); // Video limit is 2GB
      expect(checkFileSize(2 * 1024 * 1024 * 1024, 'document')).toBe(true); // Document limit is 2GB
    });

    it('should block files exceeding the size limit', () => {
      expect(checkFileSize(15 * 1024 * 1024, 'photo')).toBe(false); // Photo limit is 10MB
      expect(checkFileSize(3 * 1024 * 1024 * 1024, 'video')).toBe(false); // Video limit is 2GB
      expect(checkFileSize(3 * 1024 * 1024 * 1024, 'document')).toBe(false);
    });

    it('should fall back to document limit if fileType is unknown', () => {
      expect(checkFileSize(1 * 1024 * 1024 * 1024, 'unknown')).toBe(true); // Document limit is 2GB
      expect(checkFileSize(3 * 1024 * 1024 * 1024, 'unknown')).toBe(false);
    });
  });

  describe('extractFileName', () => {
    it('should extract file name from headers if present', () => {
      const req = { headers: { 'x-file-name': 'custom.txt' } };
      expect(extractFileName({}, req)).toBe('custom.txt');
    });

    it('should extract file name from various message attachment types', () => {
      expect(extractFileName({ document: { fileName: 'doc.pdf' } }, null)).toBe('doc.pdf');
      expect(
        extractFileName({ photo: [{ fileName: 'low.jpg' }, { fileName: 'high.jpg' }] }, null),
      ).toBe('high.jpg');
      expect(extractFileName({ audio: { fileName: 'song.mp3' } }, null)).toBe('song.mp3');
      expect(extractFileName({ voice: { fileName: 'voice.ogg' } }, null)).toBe('voice.ogg');
      expect(extractFileName({ animation: { fileName: 'anim.gif' } }, null)).toBe('anim.gif');
    });

    it('should return default filename if not found', () => {
      expect(extractFileName({}, null)).toBe('file');
    });
  });

  describe('extractMimeType', () => {
    it('should extract mime type from headers if present', () => {
      const req = { headers: { 'x-mime-type': 'text/plain' } };
      expect(extractMimeType({}, req)).toBe('text/plain');
    });

    it('should extract mime type from various message attachment types', () => {
      expect(extractMimeType({ document: { mimeType: 'application/pdf' } }, null)).toBe(
        'application/pdf',
      );
      expect(
        extractMimeType({ photo: [{ mimeType: 'image/jpeg' }, { mimeType: 'image/png' }] }, null),
      ).toBe('image/png');
      expect(extractMimeType({ audio: { mimeType: 'audio/mpeg' } }, null)).toBe('audio/mpeg');
      expect(extractMimeType({ voice: { mimeType: 'audio/ogg' } }, null)).toBe('audio/ogg');
      expect(extractMimeType({ animation: { mimeType: 'video/mp4' } }, null)).toBe('video/mp4');
    });

    it('should return default mime type if not found', () => {
      expect(extractMimeType({}, null)).toBe('application/octet-stream');
    });
  });

  describe('computeHash', () => {
    it('should compute SHA-256 hash of a buffer', () => {
      const buffer = Buffer.from('hello world');
      const hash = computeHash(buffer);
      // sha256 of 'hello world' is b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
      expect(hash).toBe('b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9');
    });
  });
});
ˆ¸[–ë¨+xœ›«9Cs"ÿ<¶âŒÄ¢Ô”�k&3MbWÛœÄl- �é	k¶·xœíYërÛ¸þï§À¯’šJ$ã8iVÎf'²��w›Øk§3ÝÙÑ@$h1¡ íÕz=Ó‡èöIz  x‘ì¸i»7M›ÀÁÎí;¨l]R&ÐÂ© ìež�Ñ’¤”‘¯Æ(!<fÙ’Œù±$±£~Ö4þ€nQÊèyËª˜
Â…w¸·'6%AïÈÇ
žÿ–‰Õ9fxÍÑ—fý	Ýì!Tªá¯¦ê«ežÅ‹,�.XV\ÂøíáÞ­QyÂe3šl@•\Cäs#lå^e99-RêˆZå®îä^wp�­ÉBªqyöY,7`àÕzI˜]ß�Á‚$,Nö§E}ªÆ˜ŸÛþÜS–<×«Ç¨*>ôºxa”¼¿ËZ¬eàiÏ¾·æÍ¬›×¬Êœâ„°S£F´6ù¥€ÑcøKŽ	’“Kˆ¯2­R.(Ã—d†ã¤h¦Àð¢Ês)�Y¼Ê®ÈI!ØF0$#Ï÷óÕŽõóá3XA–˜��ÌIÖÚ‘àW¿b¹Y4îyx4Êñ*V+h)Èµ”
GóþByjJS*7nÊš­e5H9Q±BŠ·Mà%üAdŠÁzÌ7EŒžÏ¡bx™ÛÔ{á3èwµ8Xqœqò|þÂ¿ÞÁÇ×8Sšƒ÷°Ú©³Ïõ®a¨‰AÇ§ I•ô¯üSEf‡Œæ9a(SàÂÕø;RRžAlÐŸQ¼‚Ô&É…Î$+ÂHmÑZåy‘Ì6NLä Gôñ0–t*DÇ[YWK`Í¯ˆ/'Fã½‘u£Ôÿ5¦"ÍŽÚ�þ¢�Övk©Ú¿1� ëiŠ¾xö—§OÆmDñ²5X¾/É¥76â%+˜*WTPª¡(x_j‰%A¡P@âÑþãƒ'O§/gG“ã“W0};jüHâ‘vëÙò=@µ‰sÇub�™z^ybiÇ$È~!­_®t|}/BÎâ0+R†Áø*†L!a’yãZ©uCjƒði+˜Ó� �9YÜSÔ:¯<¢#ñ]‘AIM»¤fàŒxY$ß’Í6I�`v6ÁO·Ápc¥üA ¥Çõ£¬VOÚ5ÉÏZ|œ*—KˆôGzò¶þÍi*Ž!·œC¨}ÀÃ¤+1Ã"îxæQm­
a,nKDZ"Ï8œ3’f?¶üDUÆ@ì¿ÿàNÉõd�)=zÆÊ.øð6°R6mù_»ºMÄÛStzWë3,«,OZó´b±<iÿ$¦géz6A­æ=û|�õ¦»@4!³8¿hºdÔÄ¬#÷Q¾+?$ÓSúÙNw1G‰ËA�¦&ŒØ¤RCVºn¿G+,¤XÔC8‡_­©†H(¥VUC&<\–pf,2Z„4DL n^7Û:îsõÖëWkR+ßp�Î–¦v·´ûA‘‹ž�B¯¬µ“–“Î¾±ÚÜ¥YÍìR•Q÷,ü±‚«ÖP‹7�ÏLÜ­Ï2®&Sœ›‚�8U¹Èdv§œÚÝKÎIèhï€-žd7¦Ýšödˆt²sJóàÒé®'	Ê
WàIÆ œïÓpŒé!h�” ¶Ýê½è8[N»¾6Ê®1KæÔÂÕ`ùn+E·šT�NFª.ßéŠPÔ Æœ¤)ø	RêˆqÅ)bÈ§Xç›››ïÉã w´½ÆEÄŒ[ŸIó oÐJMhæ¤c1E’¸Ð´&qN8àž˜b@Ýp%D6tO3‡ÿy£ ¯÷°·£ÎçÝMê' ç2Û"@:¬}¨F”ÌüÑ¡•q’fX`Gëj/P+ ..ÀU©/ÒŠäé‹0£`)HqÌÎæ‹ùÙ·'o�¿uù_­GÓ?é®Ãä_ñöÿÄ‰Ú¨~Ü@y³Ó–¸¢NT·­ÑÑ‘Y®,1¯ünl$Y—mV»º–n»ˆV^*‚øŠVyb.6ÑÊR�:Gÿþ)(’ln‰9T(ê§ÊÖd9]—9‘ýKuÀ³"&~—iš¸·o‡7O0	â3ÃœÆ8_Q.¦Q…iW\T¡ JóTÇ«QÍmôÝÀ�v�~¡#ï„�fþ£@ÐñÁE¥KýÞÂÞ	ímó¹}•¡î–]Õr] ^ÕÔª5Y§›ÃßµâU§� WuØà&�®Áièq´ÿYCe±¾ÅËä«-äZ`»*£­RN3è÷�/›<Š"(øÞ¼Ó)ö›Y‡¢É³ÚûcíŠ†¨un µî†šÀ¶m­õ:u=u–ò3—¡yxƒÎ’{ð´{2µ;¸Ú]lí¾vcÛÆÙYÛ}xÛvæv'wÛÊÞ†ù›·í?�DO&�žÍ£hªþ .÷FnhûÜî5ã¨÷~(ÄÙBkjî!àvºh!àŠÈŒæ’¢úÞ_©¾Îx£z‘õ�:<‡Óã2L0Ê.U•”´Ý©ÃáW;ò³ûT¯zEˆ6m	Q¦^é±çú8ÿ¬ð× ŸX1z­@á»ïÏðÍB2O8�lbsûË‰ýîÐƒ?ÿk�í‚°+ÂtXÜ¶¶“µHô[e,²š"/qô!ÌEºèWÅZTÌt°$þæâì­b+À€‘âá²zÓŒäÉ¶êÕ§O–j×/Ñ¯ž”(Ú”ÅNVÒžvHÉ~tðì“YÉò3ÄL{ý¬ä7ÊJ,ˆ=u	~(¬Öâà'u¹{ƒàþ't8÷›ç­8(õž|¬pÞ»©o'pÈùf}kå·¿ë—¾ù¾½_ûfe·øÝ¯Þ·gFCW�©aˆÞÒ..¹Ð\ñ_‘sFKÂÄÆ÷â,Ü8Þ¹ÊÛBÙ“Ý§�ü~ˆàÿ®TþdPþüæ³XxœÅSM�Ú0½ó+æfG¢	jµ—Tô ¥¢‡ªÕþÇ/Žlg�Eûß;NX¤Jí¡‡(É|¼yï�­êÆº '(pc®„¬¦P¢—N8<4(Ã=µ•[x…�³5°¢5y@ØçÉ$ËàGÌ•"ˆBx-Žè&Òº®ÕeæÝç	Ì¿À/ÂQS‡ÞêgäIBP1ŸÖ¶l5r–¦™w2Sfã„®•¡u˜5è¼ò�Ä¬têåE#•”x`Sè‘ùiP9Ä7�†nz>¦2¥ÌëtòÚ=Ëål�B‡
md»¦Ôè.°Mc€ª‹÷¥9„cƒvªóqÄ9 Û‰>«Bh2ò"8«	Ž]ç‡·KÒ1&1‚Ñ:¸ðG#G`,$�ßK�Âq’“½íý�^ÈÄ^¨ð�{ô1Q�¹Ñ@þ&�E}e[]‚CÚ Dh=|œÍ€ZÀna_¡�¯P~@<’ëïu÷Òîh²Á=<â®¥3ÈYäŸg™¶RèÊú�šÍfƒvå�¶‹cÂœP{¾0zŠø´§š¤Á.�á+¬Â–ÇX¬~òÖœ×0€ÄšØ¾ÚµBóÓ =f·¬shT;ZklY‹g\ š¥ ”=îŸ<}<Eç¬£{„ÒþbooÈ
é¶nbê®Í·Çë{Ýh¬ÑÐeÍOºxï®ðSäW²Šƒ9£aKkE©¾Ñ(,Y’Ü¬â?íñá/÷x�Â:�Ù�².q®ºïÄy—ñù+‰ÌÕèƒ9xœkænæÞp�‰Ñdò–�Í:ÌÅŒ`æc cu¨xœ340031Q(N-)-ÐMÍ+Ó+)f`ÚÑ÷5±}ƒàáµ~šÉÏÌ &€ºNxœ]TÛrÚ0}ç+ö-æ’K:�©	NB!v
NgÒ#ì5VcKŽ$‡¦Ó‡~D¿°_Ò•M&%O0’öœ³gÏº×n· !j(ž¸’¢@a@£©Jøûû�ý§!Á”Uyýž˜Ò0ö.ƒ…L<C!“*GàrÉL¬13:*>ž†•Vq�Ê£W�*Y€É”Ür±f€¥T/¶
PøXq…‰ú_‘s¶ÎQSl­éÐp“„ðˆ/å$…Ñ-¬º]"·’VôvSÙr‹f$¬Ö• C-¯ fy®�(YüÀ6è|×R¼tˆü™`N^7ÉÝk&–BóéîEt­�|ƒ:5Ri¸,o0Þ-�¢¥w±ðÂhæÝwÀ�ÜLýÈ½�Fa0óü ‰�C¨4Zv®@n…EÙM¡Ö»'�‰†SHòPVqÖL¡×j•JÆ¨µc_Žƒ°!XÂ¯_á`0<:>9¹ã‹îÄ»ì¼?;ï†£«ëi÷ólÞ±wïÏF7~Ð½ý²8ø°µƒ…{åE×®ï{óh:i »ƒ~¿�µhoŠÆîÒ‹îóæifL©G½þ`E™£ËâÍû‰ºû5¥Ôf£P?æTÈ4–™µÛíOÄê†gpÖwÎ†£Óã£aÏÎ÷àm° ã~ÿ­:?˜x‘çºÞÕ¶z=¸%¯mâ¬ ùO´	²'š¥ƒs¸ÓTó
áÎ¿üÉ4œ¾;Ÿß;0¦˜±ÊÈ®M¡¶h¶Na)ÁRÂSISÛÅ”òS‰N3KÐ†Q�åÅŠ(“ƒ±�‚­¬ò¤FS¼¬!SÆónÊh‡7S‰ÍÇk<jÄµBö ´‹ê¹N~½(z·xŽE³ß€]¾ÚCÂÓ”ëµ³ô6Òœ>?ÊœÇÜÐr4æ4	…7÷l½¹wµpo(*wþ,ZN¿yÑø>ô–äó’›wäaýáñîç°q~†X‚Ì“z÷+hçÉ.XÓ¢nm“˜’´n¹Éê–vÚÖšãÚ­‚zP“ÔÖ?—†›æ¼ƒxœ�UÉnÛ0½ç+æ&
Pä ½9Ë¡rHR é­è�–FIÈQl#0Ð�è¿ôÞOé—tHív·ôbKÔÌ›7oª²Ö†à	dFhÞÊ$� E›µÂpScB(‚dF—¬šjIh)8=R½/n”%{»­’Ê‡T™á‘°¬ÛS¶ÿk£ß©Ýë€Zé—™�‚RY3ÒÜBÏ,øs%K¦y¯U57¬%åSÓ\Vi�—ÚY´ºxô/—Tƒc/¬Iªb!2™ ]äDõ"Ñ]hø€}ŽÇŽpÔ‹%‚=Ü Âù<H`´¦%X2ªºcG ²¥|À+¹ÕÁ92ˆKø€‰6éYku>x,6Ÿ(,œ¢M„žkÇÉaxê=3m@´‘?,"Ç‚°¢Ï 3x¿ºçzÇüjZO&»�=ß¬)
íÃ9NÐ¢‡±ü¢«ŽpöÌç‰Í’ÆXõÈù‘ivƒÓ¬'¼Ã@«³ÙµY#5¦òÉ»ã�Wqh\!&©<»°k¼Žê‹È	’Ì)¹_þS4ÔÔ‚S3Î]»¬Ðª¹ù,-àÇ—¯P�Â�Ú@á«8«;LÊ+zï«ª7ñ½–l‚¨ÿ6rçgîáÂ8[øÇÎ®—®�O±×ybR‹‹Æ¤_áìË)ümÚn ÆÔýœLæÁM — ÅÇÏ~†9fÉó$;ÓgÌcL35Ÿ¡Ò_�vÌö}'Z¶±u­k¡rÓ³Î±št”âcçL7U
kE9o3ÊÖ²x€•;ýƒª¿ì¦ÿn�¦'îžÿ¸àÆ%:ããÒ³h\»8Ö©´ùJK“ÂåÇë+Ÿ¼89vÂù3€´î˜¦µXLö4=è»Î!”XÝð¼B»'çíY6¼žü]�ûŠm<^·²Ú+!.‘dÌí¹½´UæC1Sp¼âDØw„[m�ûˆÌœ£ÉµäksLH”$¶$©±kth‘£L¹ÿâ;d‘»•xLÛþ5ŸHN*…í0Z&+�n*ÏÙíq63,·ˆß]Ã÷opûÜ+\ËJÞñ…7k�ŸŠ«~¼‡xœÕWmoÛ6þî_qXîdÉ/Yš)s‡K‡b}Câ~YZ ´LÙ\dÒ%©d^j`?b¿p¿dw$%ËIÖ­Xl€[ä½ñî¹ç(±Z+máXa¹>aù2†/”æþ÷œ›\‹��ÿ¼æ¹�AàßJå—°…B«tg•Ì,7¶{Üµµ\sfù÷Ü¾šý„Z§Ü¬•4hÅnÖüâk¦í™ªtÎSI’�§Bb,Ë¹IÍ8UNºo,Ú\íœ8K7pÊä‚£)ÃÑIUÚ�ÛÒ$�6:éƒx g¶š¸X”jÆÊéR˜¤à6_^€QÀ¯¸ÞÀ”—|¡Ù
Ö.¼9}šÛJKfj¾�ë¥2œlÍ6˜à2WsvÉ½†¬V3®˜âB!´±19%Ë�rk=pnA24ç%Ûð9,9»å&¦`„E¿F•Wèâùã³)üþëo°ÖêJÈ…³£CŠÁXQ–à³åÌQfK&$äJkL'(=çšÊ»–ƒªl_}¿˜«ÕºäV(™ ~ÚÉÑ®E]cYY>¥@_Pý'õ`òn: ^Di±’•¸u;£˜òZª`—ÜYA1RUºÌ(b:Ë—ãpÊßWœ2<ÔÚj¡|bÕ™SˆzÇ­Ýóv«ÄýŠR:|ôvþeïm22Ývõ™�oá¥¯Ôš°ôLÚÈ­ŸßÅ0ô ƒA[×{’_ÃÄØÑc­Ù&:ê%¦?Gi
'-0P¨d%†¨À×r&�Ãu¨ž.qVœ³ó‡ïÐ™q¹¬q`	0™L`ˆG9ìâõpu�¾ÆÆ†S·ã£(
 j¥Àp;+Ž˜ˆ¢½¨15à×#iÜRpö,³•É`4Ä{p<&îÜ@Oc¹´ý’Ë…]v3åuúÿÜíô`Û6²íížBÎq1öi	[÷½íar¡’—R]Sž�¨vð¼�YÌjƒU²2yÓà<nay{ÜÁO§†á%q¡+¢Âxhe¡þØÐâþ„V/õ²»¤ˆ‰�(‘60ÐSQògó.Ä¼ÿÅÍÎìö"nI½¡^ºXZ»6Yš²µHê�DéEŠèäéLÙé«N^ºîØ3EMâÌ5ÒÃN îP2Ã)‹ª,ý:*ÈîRqsÌt]©$ïîYÈUYM°™Á1kÐ„&kÄîZìÑ>åÌª‚´¯™'È„‘Ì“ª(¸öÌÐ‚«OQ­çV¸¨»7gèÌÈv¼ÄË¾�Ý¸ÅvHŽäÌâ”ÌZpÈö!µGÖÀn‹Ì&1mÄøå`··{í ämæ�ž�v3·÷”EárIzM7Þƒï “ÔG¨;hçÜ‡éÚhçTØ¨&¬°ÒçE˜3Èm~Šàø”8ñŸ£¦án~ïa>CÉï0¼·3�óº‹¢aG½¸éªhtëyLÏïÚô¬9{XüÉ]$ºiÎúžÚª‹ßýa·¦“ÀGS]¶^—"g4S•[Þ\GjqnÙåè«Y£s¾PsQŽö	�ßa Qw4öCüLƒÌ}~ì6<fûÛ	ó ^t9©Ü%&Ûµ_tCq \Õ ‰çÞŽË'<BÞ€îþÒ¤É÷"i¡`†¤ÚlÔÞhÆ!þà¨ïf™«6—s×Ââ•kÙš}Ç«ŠüsÆ£xœÜv‡³.¸Þ�å|øU½;ºgw4®wÇí>!ÈÒ	åŒº.�Àr­ŒÑüÛ ôu»K“È!�"¯X)æ–MÛbÊdÃ¯i¾üsx�þ7ðþÛˆ>üD#T‡‡þ:…dÍD	ÈÖä|î¢ý‡Ty¿´búGsì:Í­a�éÎRk�!ôÇ½¿n–áá=@·0N‘í®ƒÁ<rfb¸ÛÃVÜ¯=Ú×Fw%»ÚãÈ«èëÞC¨Gý»ÉA¸Øºs»oJAÔÍlmKãËâš÷ù0Fªî†Þ%Œ¯@ÚßâñeÆw½µàeWªÚì’ãôùH‡n÷ðßžJŠ¯(rÆŽ£ô\k¥?½�?Ëìÿ§šëè“GÇç™G÷ ý›“3úû�ó"Œ¾Šxœå=YvÛH’ÿ>Eú=¿&`“à"ÚeÑ’ühYe«½i´T/.�’I-` %–Šõúc~ækæcæsˆ™ëÔæ
¹ ±p‘,WuOù¹Ê�{F&êÞcÙA¦ýÄ¶×Úc	�“˜ýü·ÿ`Ñ4ˆ™{îzAœ°dÄ™ï}ælÀ'~8ó q 78¦½0Q«?ðëì3ë÷GÞ³ÏM»ÊŽ6˜uä�×f|÷~wƒxï'ìõññ›Hw€°¼ áÑÐíó¸
?÷ýéÀÎ™ïFçœYMöê…Í†žÏY¹A<äQ¬�8ä�Ä˜ñà3ûìFqŸ2Vc/ºG{§'‡o™úcøÐ�úI‡�’dwêu1¾ãÆ|2r£™¸Îxæx[Á8Ú8íîîî�¾ÙûS¢3ˆÂIÍŒ½Àìq´·{¸w¬{DÃ†RäwbZ51¤¡îÜ}ùnÿý)ñôøÃ›½÷ùÎŠÂºö;‰Ýs.'�|ûêê*‘žõ¦� ÿÕ'šj¼Å|æ$±þ6‡©Z7â,œ$5/ Ž�åŸ±Ax 9™Ë°è*™B;Xt%hÍ<6,ã*9…Ã“÷§o»‡¯öN�)·›w;%Z@‰f¬¹À�GµxÂùÀñä‡€GN?×›¯^8=˜pfÈêÉxR¿ðüP=Fê×ïÝóÆ“0JØ5s‡À¿]ß¯²‡¥âôã€ÇýÈëñ*ãW`ú*Î~Î†Q8f@¹ƒ˜Už¥Pø•D>š}Ý,¼3Œ¡Õ½z�ýüƒ¿l7†Þ¹üíÿÁß{ýu�–ØmKº v@ªýøÇYe•è¡0`”À,¨¬<#¼¬›½Ûâ� t—îÉñk)·Ù>y©–½d·ã#hþÒM¸„—–í$áQ�ò³6žØÏîù<aýˆÃëÁ‹iÿ‚'q‡Åôþã'è÷ñ“àCN'n2Š%l!Hßî¿Ý;=è¿†•'kü�Æb*k	H–3ß† ¶oÌÃ)hTZB‡‹åŠe–ÍÌôäàí‡îËÓãýw{Np�Ÿ4§�Fãƒé4Øa
šéóòÃÞçz5[i¯–îd�DÀ÷'`¶H]^†ÑEŠ…½ µué�ýÜ�f“$|„æÑÝÓèÞ½á4–>¹­ÇOFüÊ¸‰«x‰ýÈN@Ï=íF‘;³õÓë{Œ‰Ù�`¢¿D„W«" V€U9Ó	€å›žDL@À®ƒ
Í9ïÔžeƒ
…?ÎØ�XVÏfÛ;¬—2ó‰íLÜÁQâF‰Õª²J£¢:ü%ô«‚#Î�‰�Ænÿˆp±.ø¬cÌ§ÊÆñ¹š“m¾¹É«Àfg	`ÍI¦3ËbvÎ`� ÃgVÌAœ…Øˆ8ý9âçÐaª¬Ã6³t?"â~Ì¯’½ #=Âé'+&Q�\@/ƒJðÞ:ëþá¨ýàZà4?³jŸk{¡p[ð²oT2¤È¾u/ãö)z2hîòë¬èv²_‘�Ü;á›úÃ‡°Öž?@�LH¨;MFaäýàJ¼ÜÊ+ª0—Eî¥ðXå,´þ©Pm¼­-iÌÌ@/*2L'æï¨¡ÍßÿgßÎPU"û™�am#WÓ¯·Fƒ-Å8âßX—ëyõž½ð}ÊD`k$Ç£é±hñÄ ^ùø0˜Ž{<"KòEê¦=Ýñ:Ÿ=¸†àü“ãÝo§¾ÿ'îOÎ\#t+}÷.’‘e³G¬YòV fÏ�o^‡S ¶]Q(àÒwG@•`@ïþ|–â�ÂSO s9'ö½>·UöÔ˜`bÌrÈ@ÂÊ>»ªA§´HÀ‹­	u!"äJÚ4ˆ�8Ä®ñŸù÷AY÷¢®Ù@7BpðJâ��IôÝ ¼¾ëò.„&)Ñ�éÀ‚þÍÌF¶LÇ4@�úˆvj9ôPiú±WÓr^ŸÆ5îÆI­Y�7ê¦
0@cHýPÕ^¿ëîÖŽ^waD&�åƒë¾Ä¼™xÅ¨ˆ²ÚU»gÕtU�M5*•ìj¢"ƒ	ê*¾@i‰mS›]“þèšê¡SÄl8Àz®¿ýàZxŸóúz©¢
Jc;¿8â½‹á”x7?«RƒÏ:Šy3¯²Œ�ŒEÇ1•>œk¹ÇSá„Š¬	¬PešzÐ�1`1´á¡xY®óÚÂHPh‚Æ½p0{nƒgô Eø¼\«‰6R_/m5Oä˜/æ[‡<ž 7pS7N#_êFðƒ-Ä·ªCâoÈ,DÛ!Äl‰<Z
K@ø\ôù‡ìCï/ø�1,@+½lÇ‰A1öGnäŽcø%A{ù™™k|Àø¼ Â NÔ‰Äž?jJY�Œ×kœ‘\	Éh9ÒÊ,cÄÔÂ	ADQ[¥¿!QwÌÓ'™‰hsl‹ž£‰ŒR†3ÅmÈ“þÈB˜Ic«‘2†×ÈÂ
«ìJŽ?žMP(*îdÊœÄ¶ö²�@D5®°yUs]GQÙñypùk°çšô6|è÷“|H'Bûø*3#¼ü_9Ê¹}`äN<4ý“Œk	Z]qÿ¼-êŸ›`*PËN W;&itô{Ž“œ>}¤{p�¶œŸ‘À
Ïu¸ü>-c¢Yea‡I¥³x	‰9â�7A/k¢¹ÈàœŠÒ�I	·üÇ¸Ë˜£âx*^ U‰õƒuÔcÔË®J|IÖ°«|nÚeÚ0=¡ß Ðæ®•Q˜lÊT½Z¨Ç2’Ds9!‚¹|8:®”	SÉt…�ËÙ/ˆ1Ó'B}ÊÃQ*B¤³þ.¤!OfNSÚä[@Õ2HÙCŸ»� $ªú4+gå*¶ÒýÊ›q‘÷ÜKx#I¤B¡�ƒÍ„Ç-àãïNìýÀÙÎ6kb2Aýg«•L¢)WFA-®ëÇ\Ùi™Ïïñ˜R«™ük¾rŸ‹<ƒË/˜±C7€…’6P§xê%]Ï‡À<faÆ>¾ð&±Í.�»©¡Nô¢M`^ºôiäö`Í)¿C…Ó„òúàÛðÏ`ï€ô¾Ïx‹ãöëÁª—# vš4U¹#61iL¥†u(õjfeŽ>œîî�~x£‰o‰å.]Öm±²I4Ë,ÞÉ§ÑÉ¦Ä”üd¤…H	²Ô›ßo7jmVÅ®ßaÝ^‘—ê‚Mó³žÒúŠ–sÉre	'¼ÀD¡ø9d
Fz{›µOJŸ·›O„‹ê§?’˜fYÙgn[v&a~/n/ô‘6ÓÉß…¬ÞP°eæ]/¹Z`ÃCW½±l&U	oÊ	“x'qº—%„fŠ;\!pŸCx‹\Û#ÌNOc&	puÚšÉK%f´.ÀÄ% †àÔk<-ýrïíÞñ^¥ªß¬ÔÕ)GeÙ¦÷D">âŽÈµ’¬!ã?ÿý·ó'ÌR“,6/íßî©½*«bR#ÝI³ÁJeËK¬Ê«½c`¨,'‹ýcÒ:±¬'tÎ‹§Žn´Î•®�UQÐ*’‹Åî™I}‡{$/¸Õj4ä{¨‡>¡€9èºY`žÝ¤¤§vN¦ÁE Väã'ŒpÈ"½èÅ"’ê9²ƒ-GBë+b21{ô�Jg/tŒšý’‰“RÚfg`k—¼W{p}|$Òë�TEÞ‘“>r°#ÜEo8³®Å@àÏW�³™}o•¡éíÜvðw	‚~”ÈhZg2�GéëuÈqœc6˜
œ3«ÝØ´ÿqè‰ØÞt²^ðÙõ½� 4–ÍwÅlîåÌˆáì³e3ƒ¦ûï¿ë¾Ýy\™œ‡²hª�üT…ÑÊO¶Þ¡™á”•ÙäãI2»¹°@÷Ì…e]Då³2³]0×+¥íæ$k7ÚäÔ€;£ð­$Ú2Ü%IÄ[Ì °ÉÏ Ì"üëaOp8ÎÃ§¨P>¹%ú!WÐÏîÈ^HpëÛÙá¦öBÒF&�4j›Ü´¥ýÏeŠ`8�éJ�2�ów2áÁÀ¢2 A[œ~Ø³>VFÜ÷ÃÊ'»ÊÄ�Nr•TŠ/ø¬RÖ¤d‰ry‹E‹%æx†©Œ5Òªõ¢_!ÊÏOÜóÔ>eè9¸‡+ g¤!Ñê8ÒÏ¬[ò9-!n´M"ChðwÍê*²y�å5“;q8æ–ÒB¤(ÅŸ&=—ˆÁ2%Wï 0Ó8ˆ!ï„n½;ÒÖ�z"£¥Õ3L½ÝÐŠË®ËÝ_SÿþvC·£QsJûS6-ÀØ¦®/7kU’+þí‘Çé•Òp®*vô²Q�ÚVõ&Ða›Ý×»©jÓK¼Ryh(
CñÕ„+ó$W+‹s3£)}îxÁ0´*?ÿËÿüïÿ›(c}³ÐÉ	Â„@Ó8j*…€!ÈsÆý˜gM¨…÷†ohRÏç-ø&2haÊx½ÜòÅ7ÝöDÝ€6³®pZ­¦¸«±¯FNÂ¯t¢XÃ€&Øw¹\,hAŒ»¾ÿn&Ñ>äñÔOLrèÉíRPöBæ®N`v×‚,ó[ÌúC§3ð‚SÒÎÏn2å’1U6Ìs)ô_ïu_~þàK&�Áì`šˆ­Þ]ë×`ÓîŒºudu*óT–L'EP³°L˜g†uZùá:K¥[ÈÜ#ÔXt””µ7]¥…´AÉ`
´?æ &ÜË;¡�€XðoLÑí6Ô)p¨b„,‡ÞšÊù4Ç
_q-u�÷T–ï¨6PÅ¾9€0^‘pGÇ#7°J)õŠk‰!=›£ñ
Ø«+/Í­‹’øcpÏ#w|[½|g„#¡’^¥³ñaFi§Ò¸|U2»ÓDÙw˜Ï·êñGªf¨—�‚ nü]3Gà[´/Qõ_bÝ„ÆÏÚµ…2«¹²uAg”ÚÍ,5cò¡w…ðø]ÑªÂhLŒ ŠDB”iì[S|!I²=À[Iö…DlåXò9¦„ˆÉ·[wÆžrVôXäÑúÅ¨™çßïZ+yòŸí†Ó`*Ò~ôó âA¯þI§çB‚‡;v¯j ­c|Ð¬·¿*i÷c°SUÛ”’ê%%”�Q‰‡/6Çj;vÿc%Ú«ð† D$µ¤ˆÔˆ0�=´Kg çöÏ ¦<¹¦¯E#×š:Ÿ"¼+5ÚüvàZ+ÁÉzm"�(‰ÔlZÙtÛÙ”ÛÙ9Ú1ðÜªãƒ­ºz]lÖ*kV—`+ÏV³†Ü_XKšÄ$@n±Yé¥¦3¿•kQ4XY†“Y6þ Eæ¢~B"Of5Q‹tgáÁÐñÎœ»o–øÆuµÜ	ü…5YJ½%$>ˆ¸Liáé<4¡Dà?Öº0A]"¾€ºkÒt¢Æ©%e„]GéîJ|³!U†TT?–VZëk*6ÎRµì¼Júö6gVÒÞÿxçV]¸êìJ¦u<IÉ}dl[éúV3uýó0Þ£iÏu0j‘dóôìÊÖM�?œà½,9Ð`¼Þ»šà™ÄmãI£QÄ'sœ›!GUŠB-ÉF˜’ØfÇ‰'ºZßþäà+Ëúè~ª²�½OÄV®ã‡}×ç»áxâFÜêÙ9pú|Ì?Q]=×Šskâ€€*¯²sr¸� Ã †·.ìùvé‹Ï6^)4q¨íw•üJgŽ�¢ø>X)ætÐ&E}þ}�=¨$~ÿ>8y´ÿêýÞËÚA÷Ox6Ç‘ËOÞôÄ�±^·;5d ¸íÉ!ÄŠÓC²1pžê¨U4 Èk1µ$'‘<Å•®Z¼ç®a¨ôÀ„Nj­ª(G4ûer%–aiZH)›e n¨b.ø¼†¿jX_%´À£ˆr5¿í…>OÖÌùÐ(z3UÆ`°úÓB™]ˆÌ÷ÚDVµG‘¬$o7ŸàÝ-¿"y777kÍF£qÒ_Bã‡&…ož
zŠŠ‹ {WÞ�Ãç+§«b2}T?Õ&ìt¼Ý0mÏÊ–F«YÊÃ®­ÔÇ
ŸVìÖÉad±J>†IWGÓF ±X«d‘ý£AlÙ3Kq}P�ØGË­{‚}¥éÖ‰|¹c}üç­O�ì­ïëúQÝ~î|l~*à© æÓà¹q‘:Í¥1µÈïV
cÏÖÒžšü%�›ëGFë¯"6™}JâT5U;ú§ûæòª€ˆP¾êÝÖWG·u3t[K™tÒ\Æ ªQ«¬Qž�¥ÈÊS›g[»ò�–uÁ‰;[à~'âÿbN;Í­ºñÛÖÞ±{¾ñ@³l³f¾U§÷¢K	°V9°Ö:Àê‹p>+­RP_OÇ¬\Ý…òd.…½\GÉ–k¬/eZÛæ•L¶8:½ZÍ~Òƒ#8E[Ï2Ê£Dõxî—á�]¤…&{£¶¹Ê"HlûF…–ëÛo¾Þ|\YD=0K@I½´—ÆÿZ�Ië°:•§^“¶êÈ•!M±è„Ëç¡-8AgŽvñø°8Xý1xtðM$€%o¦¡lg® ›T»€’Ò- ”‹Ííóóëgãó•)Ú½Ð¥*bßO_?ÃîooõEe÷ÑãQFÄïÃ£i$Y´§Ýh‰§­JÞkmÓ�U�«Ù«j�Úæ´oëëš*82Óï¹q4’Ž§¦óßXsî%çé„†²Ê^ßa¼`Åü–y•ÇÐ½àõV£õ¤ñM£¹þt]‚qkG&I•¿·£¢à7èÏŸËÚæ/òÈN�oôýv»µùtØoö›íMwØ¶ûO77Ÿ{›­vë—·›¼ý¤½ÙÛÜh÷ÝöæãÍÍfï›§�[½§�#®f56¾œuö�-rÅ{¿ázV:¹mÜ*Ï$¿ÍÖzç|<2ï	Õ�·Îò‘Àn†ÓÁÐÇëH­aÄ9›øn€GÕ½þˆáµš1ÇÃ?5Áj¿{�ÀÔ¡wºEÅ÷Æ^â0�SJ(©wê­CÄ’Øf	B©n77=Ðoi/eQq;�1Xç~xi³ÞlâÆ1Ñ÷`3O|/IÐØã™b\o„GŽ" �,ÿÔ¦añ|½8[Ž]F“¯Äz*Çþº~"4„qpn�vqwÏÄMÀzéíŒ5ák¨y«‰ýêëÿ‹òšQ.].„™âe ÏqÿígÏ-\Z+ïÄW*°ôÆÙ²›q”b¸…—ãâEÀà»«Ò7'aäžs»Š—7@/t6¨ o[ ;€ý}¯ï%þ¯šŒÀ�õÎà­7”œ8÷ÌšoºCî¨§¥ßxÙÀ}ãjSü5ë‚¸�@ßwš¡ùF•&mŸã-„DVéúl‹{PYzýCáZwNè©1„¹¸‡Úænõx¦AÜ/ìŒGQ,;W7þb�ù0c´ÑãË3)rQ\©ñµ¸5Ÿ*ø9G%{‰„nMH|yÏþ„*É†Õ�ÂK
hÉ4ZgßºxE-]@ .Þxp�:ïÀ£�Š¦æyw¯"¹Œ€ów‘TÛü¾ÆÍï0ÉÑó¼”˜rÄÕ¸à¤ž»ß<�o½+>°šö$C!ž)ß¿[ÌNèè$yÈ´’ £s;…w‹Á›H±º ¯yû�ŒQ7ˆ³Ô6ËûtAh™eG•Ksg¤ùÚGÁJÊí8Âõªª[p…÷ž¾kíÐ“+?ù‘W†�rÇ8åoð.œ
ÁQW*;—ê.Ê³TSt’¤1�(Ô“úÂÒÖF@0!Éé"uxs$VÐ‚ŒrJÜ6†3‚�³²‘›8=qPAwd®ï±Ä58CÒ9vö¨MJ$cÃ>EÐðà™¡¦INŒûf6:Ì8,A0QL¼ 6æã0š‘gVe#¼~1¸Ã«b�LãwÍÐÊv;ÒÿcX¹¤|@±ƒF9½¥ˆœrÏªXàKŽî”	�‚&€(²†a$]ž! ¸2Nœ8iú£øZ@nÐß¡ ¶CÄ9ì¾u’HÃ-ûÄhÜMDO:ò\"Ãíì¡FHïfá¢3§Ó¶4ým
.ŽÄ	Àöbü¬ÐúÅt8äF �Ù)ÊÕåyn öðþñš˜Zí’"%cd7Öç1áGãnèB¨‹â®T¨¼Xi#/å+6Àóª€Ñ1E€Ã,#^Ù‘9îvfˆPÒâTÜH`�÷à—¸$šè<Š=ÛaäÆI4í“?I ÄõÅ9PL�1‘1(†GEõ¦ç{Ÿ.˜j|Ù|ÏÄ|‘«DÎÇzp­F˜Û�³üD7Ì‰ÎÓœh·ßç“ñÁz’iÍpHÈmsR=pÓ.À Y’¤ª<FÄ� ¡Š½>™‰m&.|)!p³F/=<£.öW=äøåÇÉ3Š!•Àâ†Û2løƒ åÔ­`à²íåµš9µ�î1GzçŸ4ËLUUà¸2”÷÷Y@|JkÀÁÉY­"V”¬+ÇKîYû¦´ (½�öbÍÈç77\*c½e¡¸âuƒçP+”ì0Ð‘wóô{ÊÈúv<”ËðE6~u	‰Þ„�~3$ÔEí&”ŸUD“Z“~K§¶$à§+83Ç<¬‚ÝaÞT–qeþ“	)_šNA1}bAä%Ó ?ÿë�Î¡ï.¢~~úÚëù‰µŸ¢È—ø	Ìý‹NépjeÌ\É¥;3>a�æ?;„ ÷0CÝ‡˜�Â*\àžLBÆTï¬ûþ¥°Û<i«ÄÊ‚9‰.ÖO43G766šOà7â}»`êKçbZ|@M¦‘ˆž+¥—ît dCïÂ¨O}hõÜtÞµêaè£[é/:Ð<q#ë²½áŒ>x@'3Ê«æKKN
yjqU	¶1M¨OÖ®ŠLdŸÛ”˜¬(0)‘¹3xð.GWÂ )>À˜U›cøGñ��kq±œíÌüº‡Ç§GûÞc¨MÙC`ìV[þCê�À¡ƒˆcCr1ÏFâ<òÅ¢Ð;ß‹þ*Žè�§p8Œ)àn˜OSPð¦©éw9¢{wdŸ-�\Ñè+šAÿwè��Á«ÐÄª¦ÓªIVèl1wrŠDûªÂüQ:–½H±ßÜ}.³e6²¦4}d¯]u“»£œæ±¼cIíéWòÁˆ»D:Äœ·ð·
uMùÄR†åúª%âôjÍ…Æ–$â³îC­ívº¸Y”ÀG�RÓšU%mß°ßæ‘ëæb¥r&®¯dJ ¯àä4É†ÃTÐr¯jg„B]\ï$GÓóÅðQ_¬_‚æŽAÛòj&º®)W½T¬�§§•%M7*hº¡ÑZ(+kV0múZV¼T65Y™&k,UçÒÛLUöÔR)5óç´ŽdÁ¨©2Dæw88<½éa/�	¡1x J¾¸Ñ6îaÞ<e\Õ¨‚ƒÇ�Æˆz�‰´�[+WÑ�L`Ùø "cÉmúš% LGrUºx�H-õˆ¿1YyL•g€™5ö§kæÃÀŸÝ<]f8Ét]¦¬�Œ”€òðãÂ»-`WeâKF�ˆ]Ô� À)5‘¤oVŽÜDos’—(’iiX�?êÏ
þ­	ÊˆW1§Y �"Ç]F¨¿ðIr Ÿ\}$W:C‘·›L¸ÑÇã|¹…AË#©'½`K²øä‹.Šžýª2�-4P;ÍÆ7ß´›O[í­:=X"@ÆªQþ9; Œ²3¸#Þ¥â²N™Þ¯h\§h~k6oÀî,³lˆÐÄ�”XM@Žj®’ßQ:
Z¸¾AÔÄnÈ tŸ€¹Jlf.(o ]›¥”U‘‰¦ÜkÖÖ+b¹¼b;3‰b°šAª<úzƒ‘k–R‹ºÆäþP€º§þ€¹"E‡äœôGr©IæÃKÜàÎJwéªÞ–Æó}År�”kå«ñÒË„$‹nÔ,»SS�¯Šˆ™j¥H-Ð8.„×c¦>Dùí½Ù¯qd‚²ü5:Å™g�­/ËñŽþüGËhdy�ÕÃM—­¾�l¡rÞgfÀ�M
7õÇðÏâ]°õ£†žpyJ¯Á4Ð˜L{€Ã©§¿YõÌx‰K}j^€m¾DõuJ¿NÓ@§‘ :ŽlQlb6Öƒ«’å<·•uJ‘*‰2ÛÜKR)¥¼´Fž’Ó¹ùÜ"Mm|ÞX|Œ¢c~qA¶|†úk}TÂÔQ»êÓúbRBuÙ+:Mê2æ…º¦'‹Ä‹Ó°Æî·6ðLASâ=›†�‘K�ÿdÊ�—åÕ—kèIí©¢Æ4kJ
3É»_yŒKO -Ç8ã%¬‡t¦‹Ä»µïôyŒuuÆúh¯FRVƒd»IDÅoùÊ�uÜ*’Íù} 6ÿôMŸk&�wsƒÃdëìåëÝïYQðÌÊˆr€™oØSøTZ!Kî îÿþ\º_³kxœ¥TÑNÛH}ÏWÜ7;(¶ ”bUCÜ6*Øˆ¸Ý•²&öáxÜ™q¢Hýˆ~á~ÉÞ±cì‚�ú�‡Œï9÷œsï_”Bj¸ƒU*ù{€7%¦º\Ã=Ì¤X€5­Š¡F¥­ƒN'…ÒT>cU®ƒb	‡`_ãí”–¼¸êÁ’å¶»pø'Üu J)RTÊÅbyAå—ðãÇaSzÐ¹?X³*üÆM¾Î£@Û:Šâ$Ž¾áÄê�5ØÞÙ}·7ô�Ž�QðÑê<©�ÄÑ¹ÿ)HŽ?ûaœ$ã‘Á8ƒ~¿Á½ßÿ£ÿsäO‚äëù‰©œk]ª¡çá[”9º©X</ù±¿	)…ÒWÕ÷œpLa9WÃ­­­ÔÓlÜÁ~ßÝßîíîl{õžñ�Eç±áÙí÷ÿ#-ŒFA„ßÌ÷Û¤l[þèt&þÙ¸	Ç”HQit¦µ£Å5›€I0™Œ£09Ž¢/ã 	ýÓà”(š¯ÕŸú'&ÚIp…£z;{�äõüï æR´x‡ÀVŒV�×j[®ë)™z¼Ð(gŒï™¤½¦žŽ3¼1D-ÏœYŽáÔ/ù·ÁÛù¯¥Ès”Ê[áÔa%wÏj©íM±­²šæ<‰,sD‘ßeY; 5%wöÃªrj¨P.ÉÙ§ �X½åÀÛ‚×sªVé9Ø†°ÈJA²0‰Ð´è>að<ˆçXs5F%,*ò=EÐt.Ù
H|-§ýþÏÏ_F1‰ý^q‰>5tV’•%fnÍÙ\{»	ôÂzÐh]ºÔ§ëjq„öÓX)€{“Ic‘n¤&+É5n816×Ï¢Iì�‚“ ¼³¯ñÿ8ÛPF¡ª=µ>ˆO ƒW0«ŠTÓÊõ@	S£°IÁØœbK×²�ÝÖñMÎUÅdö†Œô®[ýb¯ÂÓ¿A`âz½1ŠkÄupÆ¢·xœ°Ô<›|š#Øô¼Ó(2¨èúšŸÂÍÐX¶à½¹Lãó!½ qÝÉºl–e„3^`f?5¦oK3x»±lV;`ëÁ�ùýØ‘#í¶TxF¹úimport { beforeEach, describe, expect, it } from 'bun:test';
import { checkRateLimit, cleanupRateLimitCache, clearRateLimitCache } from '../src/utils/rateLimit';

describe('Rate Limiter', () => {
  beforeEach(() => {
    clearRateLimitCache();
  });

  it('should allow requests up to the configured limit then block', () => {
    const key = 'user-1';

    // Default config maxRequests is 30; all 20 should pass
    for (let i = 0; i < 20; i++) {
      expect(checkRateLimit(key)).toBe(true);
    }
  });

  it('should block requests when limit exceeded', () => {
    const key = 'user-2';

    // Exhaust the limit (30 by default)
    for (let i = 0; i < 30; i++) {
      checkRateLimit(key);
    }

    expect(checkRateLimit(key)).toBe(false);
  });

  it('should reset window after cleanup on expired entries', async () => {
    const key = 'user-3';

    // Use one request then wait past the window
    expect(checkRateLimit(key)).toBe(true);

    // Simulate expiry by advancing past the window
    // We can only test cleanup of non-expired entries (no-op)
    expect(() => cleanupRateLimitCache()).not.toThrow();
  });

  it('should track different IPs independently', () => {
    expect(checkRateLimit('10.0.0.1')).toBe(true);
    expect(checkRateLimit('10.0.0.1')).toBe(true);
    expect(checkRateLimit('10.0.0.2')).toBe(true);
  });
});
5³¤¡î‰Sxœ]�±JÄ@†9‰)lÅnº½À%[X†ˆh!ˆ6÷ËÞÄ[LvãìäN�ÃWðØR|+Ëkì}˜«Lb‘éæŸ>þÿž¼ß¾9[šGØ@I®‘eÒ“–hW"�MÝ8bx�ôõÓ\1Þ›Úð¬W*T¶mþµÕy†�åßXF*•F&—Ì�¬ÍbQáZJê^Ójû±“Ð�vÖ3T=
Š~ëÒf4ÐÔËŸ[ôìó8|NÎ�Îð5)Â÷ÑuXG?Ûýé�u©kÀÙ!4¸”f³B@ËdÐ‹L(.eÇu§¢õHé…Hò�DWÑ&Éã?[¯mS³Îxœ�VÛrÛ6}÷WìKÊ#R�íæ!µ#_&ÎLâ¤V“¶Óé®DŒÁK Ðãñk? ŸØ/é¤,‰”äËƒ=’€Ý=ç`q2-rmáb4BË	öç
Ûiá¦:O�MÊì�EcÙÛ=¹˜”RÅ§<Ë3)¸úµD]�­–ÙŒ"Íøè
¿•Ñ‡ÔrZ�äquÁMò�2ŠF‹�Ì,ê)hæhÀK›P�½ÁÁÁÀy<ÃPpƒ rJÃgÓ\ƒMÐ�$CQj„U�Ú€ÌüâXÎ¾ƒKi—
]2›pÛ„Jc	'˜£Ðí‰³Èˆs4�å–v ’ÊÆR“ªŠ(Á`o!QÀZœ‚	}
•™–¶‘ ¸î±>=þw{@bL£-uF%J¥à6ÁŒŠÁ<äé÷PäœÙÐ$üð§×Ä‡Ç¨IG(4Z€ ÌŒœeD˜+•ßb¼žšSÖ‘ŒOÄ«Ã#Ú{wßëE6?ÁKªôÞRÐ=ýßÍiüårüþÝåùYøyôç‡O£³'VË*›�´®°MÔØ`�Üð<lãß®ÎGß_¾ à•Êyl ð¢;}¦ÔQàÄâ‡[tòŸ^F`	`ôûø8¼ø8:Ç#Z]¥Ô÷5ž@ì„ÇgrFMX7…ëQ¡¸L™Œþ„-¹‚ÄuZ,§SÔ-J�b©QJeaØåäãC¿›VS´Þé�®V§ïEt?ZŒZ[~‰¤ùÊ•ŒkæÁ”+ƒ›7¢Ö¹>Ícl¶²%ØÎ&xT¦”[‘<­eOq·,~Ç–õ—¾°b|Œœÿ479FKÅežu]¡^2Ðn%ï_¹–ß¹‹ÛLf¥^p·@À;O5ÆÄMr5Œ¢hÉ)°ºÄmŠûóó÷n‚\£
}µ— :ñYÀæ×˜­@X¶ÉVË\ËØ(ü$»K‘×ºI’ÛiåB^÷XGECN“¢sæ©œ¿D~kŽÃ$åba÷[Ïei«Ù¶`êøÍýD¬š€ís	Ó‚†—©s¸'­®yj6³Ü
 Èð¾\}ÓQ‹ä³Oô¤Xçrå¦)“
0d
1\cåŽ ƒ®J¤1[Y:MÇÝvõÚMAö·©6›÷ùðÕ¾±u/ÚNÂˆ]¨KñÞ¿‚: ØC�¸Gœ¤îû7…æ·ŽÏóp“+)z¬„¶*ÐYÑ!…×}G_æK�V7B ¨�zÐ C:JÖô+…[|¶éI†:ÕpÞÑçB•Ô¡þâüŽÈOÇ‹AìOÓ_a]ßŒŸ)F3³Y+±÷hI#{}}¤ftél’²VØl^è×š »é{lc´Á_ ÷vÍÌSš,\fA'lsP'`I¨-;½}…›Yu’™	½CI9AÏ¡ö~Dé�)"C<™?8*L?ÐC�R1¾?aÏ—nÙ¬>ü÷Ï¿ðÃá�}Øo>¾~DÚü¸X”†­YæÿÎÓX¿íxœÝXmsÓFþÎ¯¸i$ÍØ²Cè8ã2&	„)PŠú˜ú,­ma½õîDl<þïÝ;�¤“b‡À@™–™‚äÛ[í>÷ì³{¢4a‚¬É¦	ƒa¶ˆÜcÁZ–)x¢EA6dÊ’ˆX“,îàÂ:¾w¯0´­Q�31'ö(˜½¹ïX-b;dðYß#$A> ¦+\Œ©Èô‰X¥�LI >o[®ÛáÌë± 6¥ðïu(z´·±÷¸æòŽ+à_²ð¼šÛÇõ^Ãß&ùY�}ˆ©µ)_Åž�!^sAäF2 ôš"ÀwøÄ±ÚÛÀC:ÀÕm0‘- ÕÍ›ù3ûÂ´–!G¥`¬>ä'xü,7àd²RTHXð‘Š ‰É¨¬F	¢™e®íµúZ¹¯O¬áo£ûíóÃ“öè|xøàˆœ0ð!®ëZŠãŠä1Ø‚e á¹“çÇ@0"’Ä‡=ÃÓ”†ü6W[@¼W€DçA<Û…Ä.Bàid¡()Ñ8R[Yb==»°ZÅË\ˆ´ßé„‰GÃyÂE§\ZoŠ§8Ãº.6,`Uíæà1Õ{ÆÛ@¹hèŸêHäñ!!ÞÐ0ðw#¦í€±„�$>hKkè!Ÿù)ÄøÖ.ì®Y‚ÈQeJ0X]H„—ŒÇL½,Ì1å‚ÏoA5‡�#¬k�f�úGr;áTXNç°{xÔ}Ø=ê”X©
½æ÷ÿÒEÐ"#UTçù‡òXòßTü:ñ$éJÐ—m}lûT€…Ü,ü_vñÏŸ¥�tƒëåIë…Íñ7c�Fn+�¼„1<¯ïÈ¦2­ÓøËD¼ Â›ï¤ƒ™äVf$íH7ªÓ+Ïšp/Iá›3J¶Ñ¡ k_Ã]	u\±æÇ-�ÌƒXQŸ"ðIZtSrùúùmŠ´I³Û¯29ÖÜDq’y£��~¿Ž¿¹�Þ�&e{à½\˜†U¿Þxo¤�ÏW§�WTÿæilþ¥®‘ƒÉçkKÄÓF¢Ó>á‚a÷uŠ‡å‹–1\“ÇYìž°U*’sÊçÀl+÷ULZs7Keå(¯ú7 c(ŒÑ•+Gb{îúÁLÎŽ£�q#šÚöDû“©(ìƒ#ÇM©?ç¼Ã±ºVµå}Ä¶¥Ó3²›GÔ©¨dzy—8þ¨h‘ˆÏŒt«•ÏN¹%[m3oô^O»Êµ§¤.�D‘qæL)"Ã+z«Þ
âÜ!nÀú‘þJci ¹€¥8‹=¤	³Ô“Íu´rŒ_à¾
<-ì±Tã½uÜfìäqé=ûEæÎe‹÷¬:õuSÁ·lNØÉ
ÅÎDÀd­rnPM–iSÒ 7´õƒdjzs i6	¯ïIÞv
-tS³¦A>KÒ6õ£ ¶ÌV’�4Êâ@´¥Þ·µº˜fòkÊ(ê»°¤Q‚«ïŸ•]JÕEÊ2tÑËº¶ÁS<mi¦»àÃ²šv’^”š–õàS]L¨ò#¼HxóW”Ñˆ—¢mýÞbÓ†3	Ä<²¶Üeª^–WsZ�÷Ö%’›ÎÞºŒi³{è7žæ-_§ÝX<[¦¸¬wÔí6ƒ©MÒÈ	�ÆI`KùUNCWx+ã)V#R¸í¼+xÇåÕÖ¾¢˜’ûÀ»¹š¨·‰|SD’YË„ø/þ¸»ªYÁI¥x]³q}¢×'j½!¸WXtJW¹$\¾~&·'1Æd/¹wËÂ�ÕkdßÚžgu7c¡¼�÷Ö’|ù``�ïªßî­å?ø–¿¿�/_Žž=}yvÚ~5üãù/ÃÓq�îR’ý“Â~£ìmv3€Ztj^•!Ý‘*µ½J6.yæÒE“¨23M"•¥ú–zj„»©»fR11rC«&¡;D«*·–9G 4›q9:[äT­Š¥jLCABÏ¹¨}áˆ5–#ÇKŸ˜>ÖG˜oZi+ÒäsÆ­VYÚ[§,Ì§f,œ˜“ë¾’!y4¶’ªv÷a[ÉZ¿û /…Í©jö“YùÿP¶Ì¸õÉ–èg{	%~0�Ã2úTsø’©W‰ºy×Ä¿¡ó�z;hrx¿©±ƒzoúáðI¡ôøXB�Ï¼‡™µ³_©ë ÙGökâ:�Êº¿EPÕõm¿Áá�¡Âwå·uÅ]C}£oŸïoôâïÀÂ¯wM“ÿýP×r½�xœµWasÚ8ýž_¡ûd»¶¡i¯G.�3msmI&¡½™ËdaPc$W’IHÊ¿•,;†@i¦¹˜ùíjµïiwMg
Ý!<VDDiÚ@#2æ‚Ô~öp<m „ÈXÐi r“‘X5…ïŒÇWh‰Æ‚Ï�3ÊYG©œƒ½½˜3©Ìãn_%Ñ!ºØCè¾Ñ¤ƒœ<§I³å4Ì
Ã3kÚ¼92öA,V$‰T1r�Žà×i‡í—Í°Ç+`y–ì†-{—[JŠÈNF_á ²ƒÎHÌEò§T‚²IåìŠñköúâR¥Á© czCÀ¢€V€=ýÔŸñ$Oa[ß¤ˆÊÆ0�U.H�!©T„Å$$ã’*.(‘AqÜfµ¶pÈõÐákäêl	z{›’"�g¨ƒâKiš‚gˆ¥0;6¨$¾ ’§sâÖXð|LYÒ]ô!çÚ¨È}q$íÀ@Ð�ÜøÚ�ëŽÌv#ŸG‡‡†E}ÿŽXž¦v¯‚ÀGìsWÈhlj‰@6´ac£¼ÆFæ=´´»'È#[Sìƒi,=ï™Ó”ìâñ-`¶±XÐRä8bÉ²Øx-Ášûî¢ÐæVüâ¥ÚkÒo ¬’t]àUú$«£ŸLaÝÅ*žn5	Kiðœ©òÀ;Ñõìl_\>šNOž#®šçé*o°z
‹�’#.®±HüøÃ´û#-Ûu„Ê]4ÿÇZß:²¦š4µÂ7¢>3ú-_Åæf©†—EŸˆ”ðGc[íçû/JÀÒVÇ	Qf_6æ?¯ÖòPÒ[8`+ïwœÑªEVTëd)¦¬’1Ì°š áq>#LWÃW7ª„Ä¿"€&ê—�¨û¦yÔ{ë¬Æ_‘Yö ×ù‡ŒPtzŒæ­Š,º.ÔSÌ’”  Êè—VéXùQÓéj€~7Æ1ÜÚ©RY ýJ	ž¦pµƒk2jâŒ6ï×Ï_uÁ ûNéb¹`q-÷Šƒoé6÷»'ƒáàäC¯ª]?éÁôùàä,z×¾yõû½�Ãã#mÖ
Óß_ýn0ëFç½áç³�¬OÔ	‚”Ç8�r©:ûa¸Éæ(Du;¨JjRø–‚5–$›ÊÎ³gÏþ‚½ýV»å·^…þ«vçåþóv`}q�u¯¿6©7øSõ«ù.<¯&|›< ™‚UQ¢'·ÎF­äÙŽ]®V¯\¶ŽÊyè�}UàÒA�©¡)TNyž&E'Ù™gN1z×  ŽÌ[¶ÕKÐìCÅYä„¢Ù�«.ÁñF6×=z+NdÅÃjª\ðo¡ÅÒ—
«\z¾â]â¶ÃpÅtW¬ËmáNÃ¿JÎ\Ïƒ3Ààh€òˆêý-a8Z®ì¤é=Þã9�º]T-\gí 	�>•æ¯±ô-Ð³aÖÚNÍ‰º/ÍTbÁ+såâ�nÀÐ~¸¯k|9š*£«Ùrf]?!gôc8#BpqÏÔO°d,vf.4™£lŽSšXeU Îìøö?H¿�Ê®5#jÊu¿<=9TMdJpeF+ÔÑ%úQS7€8ËRcE9tŠÝlÊö”Àxö÷ùIß/EÇ·¹sÜÿ}<>ö£O½ßœªÑ.Ÿ’ÓGÝÃ]œú }¿�@Ëv�ã‡Dm£˜q1ì-1õ�$všDz@è.àv!Å†±6‚ê½‰ãµl“|?oÈç0îê™‚²ÅÚ  g‡¾}?|øT$ƒÌ#UˆfÌXskË9eíé{,õCžÔžìx#„aÇ|þ-ßMMÛÍG 43Ì?ësŸÕÜ¥Éú/^‡ Vê;ó?²Ò<e{¨Þ:îêD”Åus“ð­‘.â•‘Ý¾Õ^½6vºÛeæXeVòÖßÿ É¿BGârŠ!xœ­UÏoEV°k'¦mLT	j;V[Öný‹:ˆÈÅD$5¥
mÒ8Dj#+šì>ÛCÆ»Û™Ù4VHsABHˆæÆ…ò€g®\9sã@ª¸"$ÞìÚNœ ÊÉ¿ö½oÞûÞ7ßŒÿÊü~ù«Ë7JW|áÙ eÜíâƒ¥;õ�úƒ5R#–©¬Û)ý(yCž¬èŸ’eýGriÿ(~yieÕ`gÊå2b�¦•�÷ê�ÆÆbý‘Á´Gx~�:]æžD7ê+õÕ:p™*
	¶ Ã$e{®T·Èf`o�BÐnŠæT‰E
AÀ+�A—vÃm¦€bààyæüj¹\_�­F~ÎLŽFöôdlN6v'�Ý¨ý<6•ˆêêc×5Ä_)™²¤V«õÃç¹Ÿ*qÎs·±?!œIeJŒ¯GÙ¦þõ¥õ³xU{œ^oæ‰/ Åv@VÉzS_Œ¿­'bõñnÀó©Ð_ŒeÆï¾›:C7=Vœìw°½®Ï!"znRŸ¶Ÿ{Ô) "ýd‹¹Î|ïžƒék)æJj™F5ÓGX›˜‹®7�ó}<3»K¢ª!é<arU®�-qZ”KÈvÔ"ôîS±"’„ìéxì­¦mA»%»¸[à¤òmƒ•'Ù©½G²f?¢d#ÊU‰Í©”áNÆë#–6?[­€ôÑfæé<%ƒç¬eåúìMø ÍwÏ]íSKHýKì�Æ•RÙA»tðË
É^Ó&&²²R¢�êŒòÛÁZ½e’µ]p>¼ª7âßÜE5Ö(7vT" ²—ËÑ„R¡‹ÿ�÷œ^D'êiô3Y&•xà�¤ÌZ�ŠÝü!~æõ1ýiâª~˜¸©§ïèoSõFeàK<5-ÖUÌsCÛà¯MÆ™êG3“¡™H‡º‡a+ý[bæŠ¬°„ç ¬\ñ8f6™y5»{|)êøp|&}lqNŸŸ¨Íãq„+É‰œ˜Û^áØòA’tH�¹j>å¯Ÿœ½¤¿|9�öËêo'¯Î†'~n{ˆš ¼:È&ª‹÷}J™"¸…E…nÍ"D?;wiÿV¹ÜGÃŽ�ÞÊD¨ƒåŠmPYËŒ.ÞK=°tQyH\Qæf-êûœÙ!ÿÒN—[£•Lãø»'gßÿãB×S#‹?ÂI‚~Ç["Z´‡ŸÆ*Hô4
o3¡ÊOâ&w/ü»ÌéC™CrDœ}­Š]äžM¹©Q5¥ñóúð&6§ß‘½;rÿÍûo«áT±ýxÍ[ënÇþ¯§˜ ¸TÄ¥ÛE«D),Z¶ƒèbHtÓÂ6ä%9$7^î2{‘Ä:
ú}Â>I¿sÎÌî,¹¤¤@Žkµ;sæÜosØÝÞÞRÛª—Ìæ©žê8/µÊu–gjœ¤êü±zž?êT¥zfyºPY0Öùb�vuÔyžê`ÆUÌ£$eÊ‹¬ýÅÒ4Xã±N½v[V÷Ã™NŠ\MƒxÑ¦$V}éIÌ §:“•gA®UÎÂÜ,")¶Ú÷GA:ÑjFZé>ƒÌ¾ê%ñ°HSç*™ë4ÈCœ"Hvwk+œÍ“4WŸÔHgÃ4è¥¯çz˜ï¨0W7jœ&3Õññ¡õmµ!â$•KäO,ØêvÕÿóoù¯À%ðä -úI_Ïæ†�å‚?ìÃÖ–¥Ðk�•¬Þ°¬Ô9K²µ£¼¶Úÿ^}ÚRªKê  Ú¿ë4‡:Sù4ÈWIš§ÉPgà9Þk5 ­*ŒÕpZÄ!ApæS’5iÉ�ÖA&aªÕ<XÐSìÈ5Ó³Z…C 7íóæ.~†¹×Nfksâ&ÝB°‚|›PEA¶ˆ‡] ¬«~Á&`Ç0‰ó4‰"èö,‘¦£éi–«<Qû*¸
 ¢/^Ë÷»Y:ìk�Ž�ß�æù¼[ÁÉºÙãNõ§Ÿg­6”ƒÀâô´J¨3Œ‚A¤EÌ(õ1N®À? :DÌàÒz¥£(Ù±æxfÌñ+ÕŸ†™Âÿ€õL¢dàC'+bt<LF r_Åú
öv�ÊØUË„ÙfUÇïÞ€è¿<#‹þÞ#5¡Y¤¹W‘Ûf’wÕS_Ç¿ºÐžÁ6ž%§Õ6HÐÆ;l3Þ¨"ÿ¾ Ùµ‰a”dÚrI©›&ý¦I¨3xIH´B}H;�Í�j’N:Œ†w,¹Ìë½)Ò^·KþÆ&3(°åòLÃŒF{ªõúM¿%ç+¸g°Ï¦Ð*�f{ðk-£5�|1×-ìæó(²'ì&Ã\çv«‘¤ ŠVþ2„~‘	_“óÔ#c3j\ÄCò°L´ü<„¯n‡~ÆI^îÞQWe n}2•� _ùéàg¸bîX#ãõ#øŽa-ÔÐZRåPÔ%¹,Ä§2$]%©x#cÕ´üu Ñì«Ý|6g~VtþôIÜ¹×¾ùàšÅUÂÞ±å ˆ}ò=žÓöå�Up²ŸÙ´46ÚÓKó<yÅ�áÒ¦Á7OÿL~Á(<¶€±OŠ›¤,_ù�“	RìtWŠƒÝ.Wß¾ZoßC±ÙjÍÕ”"£—§…^2K‡À—Äz—ATh3ëçŸ~ÙS	Çp¬<ÚÐV¼ú(èTÈ3JÌBÉ§àé1d×®9.øó"›z¼Þy'üò‹ùÞqå­pX½ôò†9¨”Y¢ãk„ð0†Wü(I‹4…#Øª¥ðFü³ÈŽÄK‚9­Q8�MÂ@õu]f‚ßNœÂ„H†ÃÆË¬íç	<ÌßžTÑ7_2¾A³rxiÚÈ4Jvâ•GìðJÀ^šED‚�Û2¢Ô¬1¬¤ø°^e­Œ–<¹E£í×˜%~É’@ñ¼�‹9%�•Žbé•&µ£Z`3EK¨.q“‚²ñ·ejÒ‡DL^¨Yåag¦'Á`�èÊ™ˆ—…xwßkâE,à‘†Rjg$NÒ&HÙ«Ø”âôô¸ž…p¶ŠLGŽ:>p<çl\—y¼Ô1e¢Zýöû“1RO8äÍH©ÃaSÜ'‹�‚´žµ`�sdÞ£Ýož yú%,zt|ÀÌ„ `ÕFçá¿4ŽØWO]¿aD†Ç) ©”Ê–Ô)Â:Oò r”uäœF•Ó4æ’’mÊ¨àð"�ŒPw¿Å¯ïêdáÑ×_»í.)ƒ8Çãp6öÏPG$}ÎA›2:£ÉÛøb‚ÉÛ)“_ÃÉ…È¼”çLç¦²ñÒµë&ÖIÕ½%­y~Ž,ÁÍ\HbF“‰|¥UuälÃdâDÍˆ
°‡Ž�â¦qÐ†@HúÁªx {ÏXQÄK~¡ø‡
H˜Ðýl¿ÞWXï“«:Òñ$ŸVÑ´é°¨TX«BçÈQ0â+YM†7‚—Sh¹	=«¡ÇYVwHâ¸JÿÅúÕþ%” ¾±¯ëYWn£È€J‘¢x®žÀÿ1<är™¡‰¬T£M�¸ÕÓî`t KoJå\gI±‹$F‘JÒ.ßFdòqzäG,Çz¬%.Àcn ™¶¾Ý}o¶ÁË×YÉ¯ðè´<ª/¶„ß=ªbÇ†H*A®Ì¿‘­£r�'ÈdªòÉ k:[Àò©~¤HKÕ�Ò*KŠU zõPÊ°ª JvOqå n?¡èá�ÞWÅOš³¬¼Ê\ªuÿZ½”13WÀGaÌžFŽò3SH	ßÅ­6PVÏ‹8|ùHùf^Ûç0—ý„:Ðkm—%j²/:VB÷NÂX`Vƒ>8Já¦b˜­¶T†Þ
â—¾FÁØ•$€›«cªì?@a!©%e[³'Iõ$“/ëú(œhŸk[VƒÍÉiŸ-‡®ÊÓîE0×{X7›¡Õaò8úHm:GÅð™|`/Ò4t’8y#ø0ý¹­{ Wí3l
±B¼•Ð2šû­öÛGïÿf_gù‰Šì¾W¿þŠ4´ækçµ}� þhXz]hî;×6ÞyïÚÝº9/m¿U4d§l«àuÙ-TÒµ{�®‹£Ô2ÈªEåê‡ý°ÔIt09·Õ6LôÖ6¢ÄÙ-ši|Ž7(2“zm¯*RÝ£Xsb'¬˜§ò%üdø"D{‚üã
¬A³‹nÜœ%1Ÿ£0!«°ú-þÇ(»RV�eÛ
hÑöru©öîrjû#¡ÒÇTEE6ëa9k=ël^ò3{K(;÷ÚUº{›¥VM�³!ä™â‘5ÇQŸ~ÄÖ„Ü)i§ˆªA1ëOûF€0Û¸u —²î«¿A!jþ"óhq¥I{:üÃ¼é >êŒø6¦ÃõìïéòJœÐ#*þqÔæŠß(Ïm…¿]V«ÿM´mF˜ó¨ò¸¥VÜT%7¤o¿¶·\ñ¯½”¶Ýag�ß÷Ý¼"·t¿¥þƒÃ8g¯ª¨#d'Y#m/}¯¦žÏ6µ=™Ò‡jz–…›ºÞ¾dì¿Puv—î¤°Ô*ùr²V—Õ_~†ÊÌ8kžµ%Ã2ºŒ<ˆS˜Q¯ôU_�ú€e[Ù rÂå~¢{–)D\ïPOaj'™Õ¤ÒðXNbxïjd5Ë‘þ¿é,¨ÜÜ‘4GP±ÆhZ<Ù]�ú¥6n§£(ƒ%ÓÕº!('hQãÖ.Ï”P£zL	F¸I¯/¥&M	¶÷üÄÞ—oˆ(w-�²ÇÝ„o]×ùâ²®2ðI¦Ä¶X±;
¯¸áK_[Ë¹µ¤xtøòìÙñÅ‹Ã~ïÕEÿ‡ãÃÓ7ý‹ãó¥hß°õñîÅîî.­cGRaòl€›¨sæ£o±Bk5æˆWš’¨dÃNo-†í†£)—ãy…Ÿ¦I¤_#RK7Ê¦sR4ó1dœ®nAŠBñÒ�Ï&s_O¡	‡1Äîi®!Îh†Bq+ºœ®øclK
’¥BBÐáa�#3ìÑl^åø—…ÔÞá‚‘/yNPv^¥¸ãD¢…Ê“Úñ‘îÙ ;¾BBØVÃ( jkP\BŠýôú�¹\1éÁ“ëëô1p«þä›¿ª+®¶ùþ‹/ØQ€â–/£•xº�Im=Kú”Ga6§"ùn‹9-’‘‰J¸4c¤ìÐƒÝ„…Dµ\)gå<mì”²óäR”h?€+à‰´Á ÃH_7ûêL8ô¡KTÉfR7h³T1ç�Ÿó]ïíÎ,ÒØÔUç�Ï„[¸¶£PQà	)üA�¦œž"#sÍù$‰; #ÓÑ¸ñ]†èZ	õêÙÙ!Û‰ðªåUYå8ÕZuƒyØ•š†-Ž‰ò5
¼�šÈ<'m_433—jM?ypƒnçq uá§2…XWz`Q‘òÃá7â~°È ^tuÅÚ³�¥µó<·L¼ƒwiÚL]¡3C Ñìlgó|!Ey·Óú#Æ¡–ÜÌ!�ˆõhD¬Ù»ô«EI`©ŸÉc`A¬Ðû²TŒ�Å-„¼4M–¬Vøš¹«+P˜oêq�éM&*9¿ÔdW#ÝRlp'/SgëGR0«å£°díŠí-s•…ÌÔÖã ëÜ1/lÔ¢¬:€þæ¹‰{UMÿ?7LÕM‹ÂÍÑêSãER½œ©�b<hÅbr’
ISìšÊAT‹ü!Ý‡@š5Ù-wM–9«©2§nîdØêãõ1›VV•SÓD|³‹Cvg‡M%˜IÓÏëä–Ü›ƒÄi9ñºi¸ÓñwÔ² -KIå¸ÊZ›„<…ùs>æ^ã^ €Œ$³y%Fä=K¸2y!×ëycþ7‹õŒÇúÎF„­˜pD62Ç�ò¶�nôäÍñEïô¤÷æììð¤­ûÝƒK�„+¿ziÚ	¸pS	¿9 \”Ã1Íèjš'&e$ê.-éÂwO-�½Aßü“ÇånUÝœé~¨XÓ¡O¸Ðð?ZÿX¡ù5º'ýíÑî�J£ºdÜ%»”+}2J?¨Û	Bé$Ì÷¸+x‡D˜nêÔJly²f´™¶nïìn€™±ÌRd¤CÌ„'l[¾ÜH3�[ô¼Å �?«jT’Ú¿Ö€¢úµs†æ:iƒ¸3;âc«g$a4ËŸsÏ.²z…"Dò‚›š‚Ø0û@�›È¢¢ÀÍ±»¢¯!€W›¥_Öìíå�ˆ«öÜ%�W«ïÌKEw›�gÍxduÄ=G$iãÚ1É
*¦:˜/ë%im=~»“ ô¶œw”0µüÚxg¡3IOW‡#é©H £f+’¼GZ/ôQ4Äm‚¤¦°,FSm‘~¿‹Úm#÷º©ÃRÈXÚÐ'­ T£Y-%	)Ý’
”ë¬?Oøˆ›¶ö»$B(¬§îXc–™ºüfÇnfG”q0”cÔ§&y N8žÊ�û’jÎ¤E)�Ó%£`¢ÔwYnr²²“”ú„Y¹Ô`$šµf4îî&ÂS-~ÌÜõ´¹T¢H£T°)sHÐ.âõÄ+Üa E�Òæœ+qÆÏÑ-Ñ`p0º`\Äë±”÷4çiÎ÷…X¸�òy“´ò~)[£òy”‘;TkJÒú÷oL?	<‡27…fR=V/ùž”|_ËŒ»Y0Y½¡Mè‘a["a[·“Ó5îqb«_£±rx"Tªéñ¥oÉèøÒm´ÔÆpÑ�¨Ì_Ê «¶P	Ý¸>“vM®pUÔÕ™;M)‚È{º»»iÚÊ@ÌÍWÍz4\KØôÑ*LÖlC@Ë†nEÀi;Ž#‰,·€2Å’)Z: –Æ(Ç¦ƒF�ÔMˆ!Ïàa¸l!_kõþ··–¿²ÁxœÕZënÛ8þß§à�ÅH™D²'i']ÏÂ¹ Mƒ8�vPZ¢-N$R%©ØN`bŸpŸdII–d;±Ût�E�Ä‘ÈÃs¾sûH†&)
}E!‘� C²ƒÈ4%�ÚAT¡4<AÎ0c‡ŠHå¼~A‹	g#:.‡ø~KŠ EØ]uNÓx6èž!wÐ ûùøíõàóÙiÿäôz°ƒdñ®)ˆ2EÄD¶d·ÙAUÑdªÔ {”·Dý“Ï¸T�Ë¹£Be8ö"^7&Á³!9æI*ˆ”ÇQÆn›rd„	[™¢±lùÈ%úÇ”0už>:Ÿ¦Õ™cV\óLk
 ñ,Ï0c2è>*Jv½�(på„¾ · 'oßôÏ/¨—»É—ÝßµÍ'<Á”IWøÛu–ØîŽïiŠ
+A2ŠH&¨T4�[Îr·PïWôõ‚ qAT&˜D�™›± ÂlLB4‰«		©ÄÃ˜„5	Y•íì:ÊF#"|m±ëD$Ž9šp‡hÅggëuEÊW4œA¤îT×íÇc.¨Š ²·ÄÕ®Yz�p,!þÛ¹@›®‘·å+~DìÀúëeëØÑ—Y»fðüÌ±’·4•5PF\XÛ%’˜OPB™'é=A*‚1�7ÀKQ6{>@”È �Nû‡"R'X HPˆFxÈïžÂì!GE}Éhp‹†‚O4°SôW– Ü C ý:Æ÷3ò1¤’ )ÁÊÝm×ÐZª–ÿÜ�®‹˜ëèLt–àïëŸ„�Ud‡^€€È<»Rõµ�Üj!“Õ!© >Ä —âÖS†ãŠ•æ•F‚ùãŒÁÌÁŒ¹ïkêT±Ò“´:%(»N¦F¯œ­Ü–ÈŠ÷CZ\¾½)±´UÃh
f±BT‚ßÄØ¸3DYš©F €m7"‹gH@É„Ji´DÿùûßVP€ãj¾„BE–›˜°.‡EnIª@ÀÄ_ªNðŽI<"î^û—ƒ�ÂnL@UÚ~¿þigú±ñ <ÙÞÞ²�>ÒO0È¥èg´{°¿·×ÝyÐAÛH? [è'ÔžŽF¥Ç˜Q5;„€º%Hf‚è®¬1cŽB8hÍ´šYCm1!]BŒ:B®ñbáC›³•A¿öÐBÔäÐ>ÐoT(Ö(;ˆ@Ý.ÕÔ �Ç:ÁÁàXÍ\l•ˆ€�­	B¾Xý5Ò5Á�‡†Ü€Î<œ�ãY]mK®„ù˜ë�Gµ‰ìyk]ÅQÜ*up Øx±»æ2 W�Kd6M+7±‡{D0Ù§�ôÈU_¡0€™g…ûYsúX’HÆ3ì'3ŸêÂ›SŠ"™sTæs�ó¨™à9`àR›ÛÚfD¾€ù4œ«\kä2ž[¹µ¡kë½´ÕtÔ€fL�kÏXÍä†ê�)NÒ˜øc�h°†œ;/åëÇ1Tº'ä?f!ã@2Ê ’PÞè”¬0³L€ZBmõ°w‡cPÍºÏ[Ãy¸<B• ‡@ó6$Ù½!íÏÙ‡{ívû{Âû/2ON©™»PEéBUÅ°µqûïšÖ!Û6n<\?/7‰®o»ÄvÊŒï‹chC`tÈ•leLïÛåmc³}ÿŽ?¯éŒ‹l¾–‘AKÈ±…:ôàüIŽˆkBùJrùnè[G5 a“7•(Ó”Ê.R,�õ®Ï0™¹Tâ€ ¥Ñí……åø³Úê¬Iè¯Ü›ÙÑ¦}2(O &†Ð™µÂ”WÂ$U˜¾.Mš”+¿³—½»ºxÛ?ñûƒÓ«³þõýË¾ÿæÿüä9{4¬¼„š¼ª‚®¡mÑ2¸?‚ŽÑ<= .Ù8>p¼5ä 8(XdR–Òm[“©%•Ø„/C%Ÿ‡´§fëF�¡<öÇŠÑß]_ iv	@À4uÖK¦1¨Ê)Ht±¡Çèìææª\¢íƒèb¶U¥ëðˆ(#tmGºN¤TzØj­
Ø˜Q0Çü�é¸®6ð­CŒÐƒ�•‡zíiâé‚j•”ðVäD-ÂLäðÏ™5û„S2]ž!« ø3‡â_óèúØþôÐúóûçã6·ryK¯¦&ÐèŽbtW5uIp}³w§?È½uCªÎ-7k˜= ãß÷P?S@òbžk¹¡ý°+Å±6ªUÀX?7Ø‡ô*K\m±¼ÌéÇï{ÞÙ›þ±78ëïî cAôÖ’â¸7mí¶wÚ�v§•I�`©¼Ž>KÅ¹÷9ÏÈjÁ|f�ô&�Žõ¦íƒ×Oî= �a¥·Â°²˜¡œ¬‰WaîlÿjÈîáavv»{?Ùåþ´×Dêù-7Ç�¦¦±î,2,
_³½¬±ŠœuÕ4:<¡'ø%5ü|¤wÌa‘aðßòÃºMbx¥–;úf¡È‡OD0¤ñ‚6”D€�9á·„UÄÕ�Z¾ý/®Vœwv_úmøêØ2*Ju%øt¦‹ýŠCó:³(–pŸB¶ìAåš=(Óƒ¼ß¸˜`’PB#*À{Àó!+›JV(ˆåŒK¼®�çwó©å©jý1Õn²oäxý(j“Z¬„ÎÔ•¦Á'PÛÙmw5�®¿¯Ï»«À<‚uµ¥W$ó 4fÎæ*ê/ª¡©Ÿ!ånÐ‡XóÚÞ5d°w~eÑoº'¡æôèÿÇšÓyT§„Óùå•¿ßñ;í¶ÿr#Ì—M|VÐWç¥éNÃLïÄPMEýûxá™³ÿÛqkT¼¼-€­¤‚F½ê3‡²ró;�ÆŠp‚ï�þH"ôeÍvIõ)ÝFúÒxi?‰@«R’®¨_<hÝ�ùVla}ß¬5è6‡}„ÀÄÐíóµ=àŸŠ9+$ShèíÚÁÇœ)hÛ�ÌÐûr)=0@	{úRaâÙ˜ùz?/Ôå„ˆ1Tfãó²Õê­½¾Þœ ›®Îâ4a*MŽ‘£õ€ðñÔ,%t:}gOÓ÷ZÓ$†A=x@,ðÄi²a°©&¤4¢)êq·äÒ›vó„*Y‹‡bë�?;ÑöšË÷A«×wù;hRà�»˜ú?5À:)ì!ô§ XQ1€‚˜d¥~:m—8D�Ë[†Î‘×Ç;u5
2�Ñåé{TQnf.Âò{#¤/Â’LaE@/Í0RßA›;·^CºA¡†kÀgGûcbîq+Z—…¦¢ûê©6æ“VäguÊ‚×ÊÉ¥–�5þíDäšå˜ÔƒÂ� {¡Í¦ƒìqÓY¹
A+r9×¨Wïn}�´“Ó‹Ó›Ó¶•	úê`¯ÝžÃ¥¿ÿRä–èµðxœÅXmoÛ6þî_q†IJõbÙ±‡8slk×ÛZ$î—eÁJË´ÍAUŠª“fþï;¾èÅq“8mÖ‰m‰wÇãÃçž£ÄV9®�Ì%ÏI²ôaF‹D°)õ�^æ4‘>0	˜¾gZf#IéœtXå›J$ý…Ê×Ó¿Ñþ”9Ï
ô—W9sóòŒ—"¡u¨0Œ
‘D,Ã™ç$¡ETô#®­ƒBbÌU3‰Žt§$[PUPœ¤LåÝ±„²Æ�Ó‘À[°Œ¤/¨L–0†EÊ§$�,YÎÕ-4¬ýAç+Ë)\w J‘Ž SbÙâ/ö‘Ž +WS*Ô5†—4“m“ÅG–?Á”ó”’LÝ‰áÕ
Šxêé@PYŠ¬ ‰ ‚˜ã¿¾Ìqú#5›$²,ž5n:ÖŒ‡Ë"iZ ±!•±\‰óˆÃâŽê`NÓ+Ü8py.GÒ+(R–`Â8¢¢ipaIÉŒ
ÏG¸ZRµÈ9[”‚ÎàådòÆ&l’;*Uƒ239U »jòbTãy~áÁø©Fõ&þÊšWYî_-Äáx{ú+~žÒ÷%2é˜1‰xØëWxUÇÔ›�Y ?†;Ót4ï¤5ªrÂa�Z8gÙÌus"µãx�›ƒû�2õ,�Ñ5T$w�ŒKÜ·2›9>2Ô 2‚£îlZô†šÁ[â  [Z:R+Ml\€
ðRïWájPB³{….¨tS 6‹”JäãìÊ.<´¬mí\cèu»Mâ:†gÁ­X¡U¤ù4vÿœ=ñý…ô’&ÖãÄ:¨«Æ¿Š€“é=ø]“Ú]�ÇµKeC³YÛ¢×¶øÄJBEgêêÈ¾v~qË¥µ¾auwÓi>?µ+j¿NÞDðí•Åz„{æØ‚”f¹tFõT°	½”Ï³„£±ë…TÿÒ�½Ð˜{°11a6�B+ŸƒU¦î-Ñ®{WmÉzzµ ZÚ»èÇfUyù7Íß4u‹@~2l¦”I¨Jú)Æ¼ÆxM•Ãw:;Rœ‰d´Ó0)•¿[Kë»¥”y1Š"¹Us‰¾½f³Í»*µVJêg†u6~3‹Ù$ISºdõ‚¥ô.’Íü­ûoï™ÏÌø£¢ö¨ž4t†ˆ#ˆÕ-\oYì=Zëü-­·E�ÃÍ¼µù�‰×w[Iæa•f3£M®êä®sÖÓYÁtV$»a8LK–"3QzV1Ô\X‚]=#ªi¬°×²<¥F2Q€‘eÆÉ
öŽ ç±­N×QŸAŒ¶Cœ%MSÎ¶÷¶m{h;@Û5é¬2m÷÷<�`-mY¤ªºÉšàyå–3‰[•² ïÌ~«ÜuS,›`íá˜ÄÂ�ò”°¬6 ’,pD}Å½~};%…ü�ÏØœQªŠþgœÝuzÝÞ0è~�“nw¤ÿþp¼ÊKr\Ñ™®½8®nÚ~yŽ”rÕ*clÅúwÏ‡žwQ™i‘UÒ#M®Ï¨ÓÖýÃà\¦ê=¡ä?RÞÂÚ²°zfZÇAó¬£ÇÎ¶§�[ù+¸ÜÚÐltkmRŠ[Õ¹…ÖL.+ÜÓØÔ)ˆØ&§†‘nŸ-,¿˜v7¹t±EGíÎYsÜ ý�¤Lõ{Ýk”"¨v3‚>l…�½ÿŸ�ÃÛù¸ÍÁ½7Ü“qöÐRñÈœZã �ÏälOÊ"Gv¨Zä$Sçk•†¥$I/°A¯¹•Ã„�qÓ«O3ŠÀP©p`xx îÐÇ=Ó'¥€ºXplP�é©¼Ìð-dßW_¤­6}³ìA«,u˜¥Êq ˆv±'&ãaplVqà}Q=l=?R=õ^O÷UÒã‰ø×) Apí­Ýz‹wKÁ<RÎË4­ë%Í,=ð¡U÷ ý´YÑ±pÈônª*›äkœŽn¡És|ª ê‘9â‰¤õ«‰Ç ÌÑc7ý»	ÔÝo÷É4ÙmÙ,KÒÏ“€'IË: øˆõÓëÓ3uT¯ª3å8+ìÙ–?sg/\ü«+üÞ³•ÆøwßÙnßVú€�Ý*üË€¬>Â¼	PHë­4ë¿Ç�$	Í­l7tcß¢²‚§î_æ	´‰tx_…{c~ ñ?¸·Ûã“0­fn¼órŠëÃŠ ,:îÇƒþ°Ûíî0:<'ýxCðÝ<eÙ÷hsÂÒR ¨e’oQ»z|rõë¯8Ê&å=\öÚ¯zÝ^ó²è?)ýrékôÀÞc‘Ï“e�·…ÛôÜõÚÑ×  8‡ÚÉ ÓbL¥l‚*ˆðšO–‚¯Ýšêÿ_õ)ü5²÷xœÅWMsÛ6½ûW`r(¨I�è´™f'R|è4rSÛõdêñ"!IÐ8‘¢ñï )P¦¥m\�> pöa÷añ4…B[3|Áúˆ­s©>â
=¡e!R„e6QL*|vrRzøú}˜¿CÓ’'1+$î#¯‡ÎC´=A ö $zÇ¥š–ÑG¦¤¶3*7Yä#‰L*´NtŽè'
®¹!æáá�È""<S¬XÒˆI"O	âÞ™ƒ,˜,`x2Lv?¤‰gÌºÛ¢Œ¦l‚°^É`a€LT0ªXüFMPÆ>¡·Ðñ°?òŒÆð¾�&æý'î¡§û~5.Øã`Œm·¢bçY.½¡3‘)Ê3?;Œ¿`¦Cõ&Iæ›Šþ•±9x	+�uÄŒ�œéås‘éu‡]ËHËÄNùßí,ƒ*hÎb'Û–üK§ÜzuòŽÓM“ìZ
Õ/ª¨Ø×G¶q,…3¡?,†j½ƒè—äŸÙtQž ñhä>aŠ> –.¢ ¡RÍEÌ—œÅ_W—‹LyÊn6¹UëZ‘<�l9s?ÕÍF�w8_	%$ÁÍÐ’&’Õ�¬L’ºìú¸™Üeü�JßÏÿü•mÂ&ì4ÍÏLè¢Ç�€_CZBXR@LëÄû‚-ù:¬�ªHÚ»%Ýú/-ç[ÿß
š~IÇ?vÉ8fËÿOÆÝêíV¬+ÓCíqKÇþ?Öñ­œ’g¢ÌT86êµí#w Ýýs!¦ ä9…óšf1è&ß˜fÌACLþÇÂ¬kAòŒ+IŸ×þÈAc­JÜœ¤§Jól0öOqÏ]g�ˆÀ‚‰Ã�e@šÁÎUXÍ%iž°#¹hIL]Fx¥T>!$MVB*b­‰6­d»w6v{ê,l–Y¾ùmñtw›Öeðµ­Ó­ÙÞ¬n|:Îá¢…‘…Öˆí]Uùþ…Kž^h�nµº×e´š¶¾Y1$!p¦¨ ›&Q&ÄvŸs0í•»o<’f"f¡K®4zèˆ�yÅK¸U�*�Ë€ì°½†vê {{@xÎ¥äÙƒn6+C¯G¯ûu�zÆk(U¥ÔÜ¦ÌÓç+Fõ%|øÀ sëM?
ËuÀc»ê:p´Ú†ò�Þ2­v«5‰"Þ|Åè‰Ñy£oë5¬ßÐ^xÂÆ»‚�úá¾‘ßeô{É™
UQ²€Øv@*/Ø%²ÕG¤ì£GmvlþL0Z¡˜aO³n‡]ƒu\/Kšxw¸!­3Û�Ã÷m”qV¥CS<�‹µçÅ²rÀ¼‡¶ý¾,Ó+ôYçô‚‹ú¾‚‚÷* ¦mw ý. \>ö�ä—V2õ¸tSøu �èä±
ÁÖÌhÉÁ…¿ï^òw—¶•ßwïPµÕ}“býùI}¡¿�xœ­—ÍnÛ8Çï~Š¹ILDM§èZ,ÐS±Øöhil³�H-I%q‹ûû„û$RT'²CÙ9Ä�MùãŸóÁU­´…P ÉµXáð¾ÆÜN@Xø	k­*HV�¼±hlòn$‚Á WÒ¢´s¹Á	}¯¹6è¿}B^ v¿5Òp+ÌZ`ñqoz·ôt:3:Ÿ	Ókž£™™l¦ÝÚl4»¸Á|ÉàÓ×¯�·m÷Ñðÿ¿ÿBUkÜ¢4â‹²œ¤ßoQóN½µjtŽ7p`Ÿ©5�Ú-B¥Š¦D°[nÁdÌO«²¤­¸,ÜBjõ�”aÆjäðÜ6¼,wÐ»�f£NÄ4éˆõ#âdéÞÿáÅû=UªvÖÞ0�üi¢Ñ6Z�J"ÜÑ1ƒ[¯/iÊò‰!„LŸÞGê&O`1�§VýùÑ§?ÀîjÒ&q[$ðsüÎ¯áþ Àª¶; ]„ÜDÓ$I<ËÑoÙny)
·x)rrU/¡éÑÐïkŽk;CYÄó­väóï/ÙÕAPÏ@£~ñ¸¤à‘Å\õëØ‘�H%2·zïÅ¿H”±,–(DY?Q^òª6n
¬p§è_ëâÄø}0Ö5»œÇr]®å‹\57J÷á[+ªFÉh‹Óô»f¯„.´�qoBW< Î‡rÍÙ›X°y {ÓÖ8¬ã¢Á�°[
i8!$–Ñ.{ì‰fÓ¬×â>D1¤í>ìóøHRôçûìOø4î_<A¼Òo�»@øÕJ”RÝª`¬)é†°L¹ˆ¨yT@¹ªèB»èx‰ïn«¨è�xõZ€{—ºð—:\­W÷J!Û*s°¸ht²¨šÒŠºt�ªâÌ Ar‹,#2Áb’ñÑÀu°^·PTF}•1ô
v „°Xyˆ3¼Ü]eyHGCÅXÈå™$m…£<òôP†·GJnÁwÔŠ…ÜÐß`ÿ>kñþ ›
µÈOS�ó#o¡á'Tž[­ÎuÿV])»eF]ù|çê€ê‹¨g	‰¸õŒÁÙý™í[@ÉÖ?]m8±Ì½{Æ‘Ä¥ò3NÕåm«,áS³mòc‹h†žêú#Ê:·öykäˆÛAÓõÒì¨3>n¸S*¨Ë‡»ÿ€æl9£gÃS¥÷L©O¹ê1¥Nç‘éž°æ£v>ù@ÿŸ>Ûü¢�ú�õsã=ìöÓýýë	´óx47ËÈ/**
 * S3 compatibility E2E tests using the official AWS SDK v3.
 *
 * Tests that the file storage gateway is compatible with standard
 * AWS SDK clients.  All operations are exercised against the production
 * endpoint.
 *
 * Prerequisites (env vars):
 *   - S3_ACCESS_KEY     (default: filedrop-admin)
 *   - S3_SECRET_KEY     (required)
 *   - BASE_URL          (default: https://upload.asepharyana.my.id)
 *
 * Usage:
 *   S3_SECRET_KEY=xxx bun test test/s3-sdk.test.ts
 *
 * CAUTION: creates & destroys real resources on the production server!
 */

import { afterAll, describe, expect, it } from 'bun:test';
import {
  AbortMultipartUploadCommand,
  CompleteMultipartUploadCommand,
  CopyObjectCommand,
  CreateBucketCommand,
  CreateMultipartUploadCommand,
  DeleteBucketCommand,
  DeleteObjectCommand,
  DeleteObjectsCommand,
  GetBucketVersioningCommand,
  GetObjectCommand,
  HeadBucketCommand,
  HeadObjectCommand,
  ListBucketsCommand,
  ListMultipartUploadsCommand,
  ListObjectsCommand,
  ListObjectsV2Command,
  NoSuchKey,
  NotFound,
  PutObjectCommand,
  S3Client,
  UploadPartCommand,
} from '@aws-sdk/client-s3';

// â”€â”€ Config â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
const BASE_URL = process.env.BASE_URL || 'https://upload.asepharyana.my.id';
const S3_KEY = process.env.S3_ACCESS_KEY || 'filedrop-admin';
const S3_SECRET = process.env.S3_SECRET_KEY;

const TS = Date.now().toString(36);
const PREFIX = 'e2e-s3sdk'; // consistent prefix for cleanup
const BUCKET = `${PREFIX}-${TS}`;

let createdBuckets: string[] = [];

const s3 = new S3Client({
  region: 'us-east-1',
  endpoint: BASE_URL,
  credentials: { accessKeyId: S3_KEY, secretAccessKey: S3_SECRET! },
  forcePathStyle: true, // required â€” we don't support virtual-hosted style
});

// â”€â”€ Cleanup â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

afterAll(async () => {
  for (const b of createdBuckets) {
    // List & delete all objects first (FK constraint may prevent bucket delete)
    try {
      const { Contents = [] } = await s3.send(new ListObjectsV2Command({ Bucket: b }));
      const keys = Contents.map((o) => ({ Key: o.Key! }));
      if (keys.length) {
        await s3.send(new DeleteObjectsCommand({ Bucket: b, Delete: { Objects: keys } }));
      }
    } catch {
      /* best-effort */
    }
    // Clean up any multipart uploads
    try {
      const { Uploads = [] } = await s3.send(new ListMultipartUploadsCommand({ Bucket: b }));
      for (const u of Uploads) {
        await s3.send(
          new AbortMultipartUploadCommand({
            Bucket: b,
            Key: u.Key!,
            UploadId: u.UploadId!,
          }),
        );
      }
    } catch {
      /* best-effort */
    }
    try {
      await s3.send(new DeleteBucketCommand({ Bucket: b }));
    } catch {
      /* best-effort */
    }
  }
});

// â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�
//  S3 SDK Compatibility Tests
// â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�

describe('S3 SDK compatibility', () => {
  if (!S3_SECRET) throw new Error('S3_SECRET_KEY env var required');

  // â”€â”€ Bucket operations â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  it('ListBuckets returns bucket list', async () => {
    const { Buckets } = await s3.send(new ListBucketsCommand({}));
    expect(Buckets).toBeDefined();
    expect(Array.isArray(Buckets)).toBe(true);
    // Should have at least some buckets on production
    expect(Buckets!.length).toBeGreaterThanOrEqual(0);
  });

  it('CreateBucket succeeds', async () => {
    await s3.send(new CreateBucketCommand({ Bucket: BUCKET }));
    createdBuckets.push(BUCKET);
  });

  it('CreateBucket rejects duplicate (BucketAlreadyExists)', async () => {
    await expect(s3.send(new CreateBucketCommand({ Bucket: BUCKET }))).rejects.toThrow(
      /already exists|not available/i,
    );
  });

  it('HeadBucket succeeds for existing bucket', async () => {
    await s3.send(new HeadBucketCommand({ Bucket: BUCKET }));
  });

  it('HeadBucket returns 404 for missing bucket', async () => {
    await expect(s3.send(new HeadBucketCommand({ Bucket: 'no-such-bucket-xyz' }))).rejects.toThrow(
      NotFound,
    );
  });

  it('GetBucketVersioning returns disabled configuration for Gitea compatibility', async () => {
    const versioning = await s3.send(new GetBucketVersioningCommand({ Bucket: BUCKET }));
    expect(versioning.Status).toBeUndefined();
    expect(versioning.MFADelete).toBeUndefined();
  });

  // â”€â”€ Object operations â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  it('PutObject stores text content', async () => {
    await s3.send(
      new PutObjectCommand({
        Bucket: BUCKET,
        Key: 'hello-sdk.txt',
        Body: 'Hello from AWS SDK v3!',
        ContentType: 'text/plain',
      }),
    );
  });

  it('PutObject stores binary content in nested folder', async () => {
    await s3.send(
      new PutObjectCommand({
        Bucket: BUCKET,
        Key: 'folder/nested-file.txt',
        Body: new TextEncoder().encode('binary content'),
      }),
    );
  });

  it('HeadObject returns metadata', async () => {
    const { ContentType, ContentLength, ETag } = await s3.send(
      new HeadObjectCommand({ Bucket: BUCKET, Key: 'hello-sdk.txt' }),
    );
    expect(typeof ETag).toBe('string');
    expect(ContentLength).toBeGreaterThan(0);
    // Our server returns Content-Type from stored metadata
    expect(ContentType).toBe('text/plain');
  });

  it('GetObject returns stored content (proxied from Telegram)', async () => {
    const { Body, ContentType, ContentLength, ETag } = await s3.send(
      new GetObjectCommand({ Bucket: BUCKET, Key: 'hello-sdk.txt' }),
    );
    const text = await Body!.transformToString();
    expect(text).toBe('Hello from AWS SDK v3!');
    expect(ContentType).toBe('text/plain');
    expect(ContentLength).toBeGreaterThan(0);
    expect(ETag).toBeTruthy();
  });

  it('GetObject supports Range requests', async () => {
    const { Body, ContentRange, ContentLength } = await s3.send(
      new GetObjectCommand({ Bucket: BUCKET, Key: 'hello-sdk.txt', Range: 'bytes=0-4' }),
    );
    expect(ContentRange).toMatch(/^bytes 0-4\//);
    expect(ContentLength).toBe(5);
    expect(await Body!.transformToString()).toBe('Hello');
  });

  it('GetObject returns 404 (NoSuchKey) for missing key', async () => {
    await expect(
      s3.send(new GetObjectCommand({ Bucket: BUCKET, Key: 'does-not-exist.txt' })),
    ).rejects.toThrow(NoSuchKey);
  });

  // â”€â”€ Listing â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  it('ListObjectsV2 returns all objects', async () => {
    const { Contents, IsTruncated, KeyCount } = await s3.send(
      new ListObjectsV2Command({ Bucket: BUCKET }),
    );
    expect(Contents!.length).toBeGreaterThanOrEqual(2);
    expect(IsTruncated).toBe(false);
    expect(KeyCount).toBeGreaterThanOrEqual(2);
    const keys = Contents!.map((o) => o.Key);
    expect(keys).toContain('hello-sdk.txt');
    expect(keys).toContain('folder/nested-file.txt');
  });

  it('ListObjectsV2 with prefix filters results', async () => {
    const { Contents } = await s3.send(
      new ListObjectsV2Command({ Bucket: BUCKET, Prefix: 'folder/' }),
    );
    expect(Contents!.length).toBe(1);
    expect(Contents![0].Key).toBe('folder/nested-file.txt');
  });

  it('ListObjectsV2 with delimiter groups common prefixes', async () => {
    const { Contents, CommonPrefixes } = await s3.send(
      new ListObjectsV2Command({ Bucket: BUCKET, Delimiter: '/' }),
    );
    // 'hello-sdk.txt' has no '/' â†’ in Contents
    expect(Contents!.some((o) => o.Key === 'hello-sdk.txt')).toBe(true);
    // 'folder/' appears as a CommonPrefix
    expect(CommonPrefixes!.some((p) => p.Prefix === 'folder/')).toBe(true);
  });

  it('ListObjectsV1 (list-type=1) also works', async () => {
    const { Contents, IsTruncated, Marker } = await s3.send(
      new ListObjectsCommand({ Bucket: BUCKET }),
    );
    expect(Contents!.length).toBeGreaterThanOrEqual(2);
    expect(typeof IsTruncated).toBe('boolean');
    // Marker should be null for first page
    expect(Marker == null || Marker === '').toBe(true);
  });

  // â”€â”€ Copy & Delete â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  it('CopyObject copies within same bucket', async () => {
    await s3.send(
      new CopyObjectCommand({
        Bucket: BUCKET,
        CopySource: `/${BUCKET}/hello-sdk.txt`,
        Key: 'hello-copy.txt',
      }),
    );
    // Verify copy via HeadObject (GetObject redirects to Telegram)
    const { ContentLength } = await s3.send(
      new HeadObjectCommand({ Bucket: BUCKET, Key: 'hello-copy.txt' }),
    );
    expect(ContentLength).toBeGreaterThan(0);
  });

  it('DeleteObject removes a single object', async () => {
    await s3.send(new DeleteObjectCommand({ Bucket: BUCKET, Key: 'folder/nested-file.txt' }));
    // Verify deletion
    const { Contents } = await s3.send(new ListObjectsV2Command({ Bucket: BUCKET }));
    expect(Contents!.some((o) => o.Key === 'folder/nested-file.txt')).toBe(false);
  });

  it('DeleteObjects removes multiple objects in batch', async () => {
    // Upload test files
    for (const k of ['del-a.txt', 'del-b.txt', 'del-c.txt']) {
      await s3.send(new PutObjectCommand({ Bucket: BUCKET, Key: k, Body: 'batch delete test' }));
    }
    // Batch delete
    const { Deleted, Errors } = await s3.send(
      new DeleteObjectsCommand({
        Bucket: BUCKET,
        Delete: { Objects: [{ Key: 'del-a.txt' }, { Key: 'del-b.txt' }, { Key: 'del-c.txt' }] },
      }),
    );
    expect(Deleted).toHaveLength(3);
    expect(Errors).toBeUndefined();
    // Verify all deleted
    const { Contents } = await s3.send(new ListObjectsV2Command({ Bucket: BUCKET }));
    for (const k of ['del-a.txt', 'del-b.txt', 'del-c.txt']) {
      expect(Contents!.some((o) => o.Key === k)).toBe(false);
    }
  });

  // â”€â”€ Multipart upload â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  it('Multipart upload works with AWS SDK under strict SigV4', async () => {
    let uploadId: string | undefined;
    try {
      const created = await s3.send(
        new CreateMultipartUploadCommand({ Bucket: BUCKET, Key: 'sdk-multipart.txt' }),
      );
      uploadId = created.UploadId;
      expect(uploadId).toBeTruthy();

      const part1 = await s3.send(
        new UploadPartCommand({
          Bucket: BUCKET,
          Key: 'sdk-multipart.txt',
          UploadId: uploadId,
          PartNumber: 1,
          Body: 'hello ',
        }),
      );
      const part2 = await s3.send(
        new UploadPartCommand({
          Bucket: BUCKET,
          Key: 'sdk-multipart.txt',
          UploadId: uploadId,
          PartNumber: 2,
          Body: 'sdk multipart',
        }),
      );

      await s3.send(
        new CompleteMultipartUploadCommand({
          Bucket: BUCKET,
          Key: 'sdk-multipart.txt',
          UploadId: uploadId,
          MultipartUpload: {
            Parts: [
              { ETag: part1.ETag, PartNumber: 1 },
              { ETag: part2.ETag, PartNumber: 2 },
            ],
          },
        }),
      );
      uploadId = undefined;

      const { Body } = await s3.send(
        new GetObjectCommand({ Bucket: BUCKET, Key: 'sdk-multipart.txt' }),
      );
      expect(await Body!.transformToString()).toBe('hello sdk multipart');
    } finally {
      if (uploadId) {
        await s3
          .send(
            new AbortMultipartUploadCommand({
              Bucket: BUCKET,
              Key: 'sdk-multipart.txt',
              UploadId: uploadId,
            }),
          )
          .catch(() => {});
      }
      await s3
        .send(new DeleteObjectCommand({ Bucket: BUCKET, Key: 'sdk-multipart.txt' }))
        .catch(() => {});
    }
  });

  // â”€â”€ Cleanup & delete bucket â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  it('DeleteBucket succeeds for empty bucket', async () => {
    // Remove remaining objects first
    await s3.send(new DeleteObjectCommand({ Bucket: BUCKET, Key: 'hello-sdk.txt' }));
    await s3.send(new DeleteObjectCommand({ Bucket: BUCKET, Key: 'hello-copy.txt' }));

    await s3.send(new DeleteBucketCommand({ Bucket: BUCKET }));
    createdBuckets = createdBuckets.filter((b) => b !== BUCKET);
  });

  // â”€â”€ Error handling â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  it('NoSuchBucket error on non-existent bucket', async () => {
    await expect(
      s3.send(new HeadBucketCommand({ Bucket: 'bucket-does-not-exist-99999' })),
    ).rejects.toThrow();
  });
});

console.info(`\nâ„¹ï¸�  S3 SDK E2E â€” ${BASE_URL}  bucket: ${BUCKET}`);
ÐÇÙ¼‡x|ƒ÷import { describe, expect, it } from 'bun:test';
import { handleSwaggerHtml, handleSwaggerJson } from '../src/routes/swagger';

describe('Swagger Documentation Endpoints', () => {
  it('returns OpenAPI specification JSON', async () => {
    const res = await handleSwaggerJson();

    expect(res.status).toBe(200);
    expect(res.headers.get('content-type')).toContain('application/json');

    const body = (await res.json()) as {
      openapi: string;
      info: { title: string };
      paths: Record<string, { get?: object; post?: object }>;
    };
    expect(body.openapi).toBe('3.0.0');
    expect(body.info.title).toBe('TeleUploader API');
    expect(body.paths).toHaveProperty('/health');
    expect(body.paths).toHaveProperty('/api/upload');
    expect(body.paths).toHaveProperty('/f/{public_id}');
    expect(body.paths).toHaveProperty('/file/{public_id}/info');

    const uploadPath = body.paths['/api/upload'] as any;
    const downloadPath = body.paths['/f/{public_id}'] as any;

    expect(uploadPath.post.requestBody.content).toHaveProperty('multipart/form-data');
    expect(uploadPath.post.requestBody.content).toHaveProperty('application/json');

    // Verify 429 response documented
    const uploadResponses = uploadPath.post.responses;
    expect(uploadResponses).toHaveProperty('429');

    // Verify download is no longer documented as 302 redirect
    const downloadResponses = downloadPath.get.responses;
    expect(downloadResponses['200'].description).toContain('stream');
    expect(downloadResponses).not.toHaveProperty('302');
  });

  it('returns Swagger UI HTML page', async () => {
    const res = await handleSwaggerHtml();

    expect(res.status).toBe(200);
    expect(res.headers.get('content-type')).toContain('text/html');

    const html = await res.text();
    expect(html).toContain('<!DOCTYPE html>');
    expect(html).toContain('swagger-ui');
    expect(html).toContain('/swagger.json');
    expect(html).toContain('swagger-ui-bundle.js');
  });

  it('should not expose CORS * header', async () => {
    const res = await handleSwaggerJson();
    expect(res.headers.get('access-control-allow-origin')).toBeNull();
  });
});
òÏòê�
xZ ¥ÿüî°uFileDrop³�º302 redirect to Telegram CDN“_W“302“Ü ['302“»Redirect³gušÓ²»x²Mäimport { afterEach, beforeAll, beforeEach, describe, expect, it, mock, spyOn } from 'bun:test';
import { config } from '../src/env';
import logger from '../src/utils/logger';

let realPhotoBuffer: Buffer;

beforeAll(async () => {
  try {
    const res = await fetch(
      'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png',
    );
    if (!res.ok) throw new Error('Wikimedia download failed');
    const arrayBuffer = await res.arrayBuffer();
    realPhotoBuffer = Buffer.from(arrayBuffer);
  } catch {
    // Fallback 1x1px JPEG
    realPhotoBuffer = Buffer.from(
      'ffd8ffe000104a46494600010101006000600000ffdb004300080606070605080707070909080a0c140d0c0b0b0c1912130f141d1a1f1e1d1a1c1c20242e2720222c231c1c2837292c30313434341f27393d38323c2e333432ffc0000b080001000101011100ffc4001f0000010501010110000000000000000000000102030405060708ffda000c03010002110311003f00a0ffd9',
      'hex',
    );
  }
});

// Mock Telegraf and fetch
mock.module('telegraf', () => {
  return {
    Telegraf: class {
      constructor(token) {
        this.token = token;
        this.telegram = {
          sendPhoto: mock(() =>
            Promise.resolve({
              message_id: 12345,
              photo: [
                { file_id: 'photo_id_low', file_unique_id: 'unique_id_low' },
                { file_id: 'photo_id_high', file_unique_id: 'unique_id_high' },
              ],
            }),
          ),
          sendDocument: mock(() =>
            Promise.resolve({
              message_id: 54321,
              document: {
                file_id: 'document_id',
                file_unique_id: 'document_unique_id',
              },
            }),
          ),
        };
      }
    },
  };
});

const infoSpy = spyOn(logger, 'info');
const errorSpy = spyOn(logger, 'error');

describe('Telegram API Utilities', () => {
  let forwardToStorage: typeof import('../src/utils/telegram').forwardToStorage;
  let getFileInfo: typeof import('../src/utils/telegram').getFileInfo;
  let getBot: typeof import('../src/utils/telegram').getBot;

  beforeEach(async () => {
    infoSpy.mockClear();
    errorSpy.mockClear();
    global.fetch = mock(() => Promise.resolve(new Response(JSON.stringify({ ok: true }))));

    // Import dynamically so mocking is applied first
    const telegramUtils = await import('../src/utils/telegram');
    forwardToStorage = telegramUtils.forwardToStorage;
    getFileInfo = telegramUtils.getFileInfo;
    getBot = telegramUtils.getBot;
  });

  afterEach(() => {
    delete global.fetch;
  });

  describe('getBot', () => {
    it('should return the telegraf bot instance', () => {
      const bot = getBot();
      expect(bot).toBeDefined();
      expect(bot.telegram).toBeDefined();
    });
  });

  describe('forwardToStorage', () => {
    it('should forward photo to storage chat and return file details', async () => {
      const chunk = realPhotoBuffer;
      const fileName = 'test_photo.png';
      const result = await forwardToStorage(chunk, fileName, 'photo');

      expect(result).toEqual({
        telegramFileId: 'photo_id_high',
        telegramFileUniqueId: 'unique_id_high',
        storageMessageId: 12345,
      });
      expect(infoSpy).toHaveBeenCalledWith('File forwarded to storage', {
        fileName,
        message: 12345,
      });
    });

    it('should forward documents with source and filename payload', async () => {
      const bot = getBot();
      const chunk = Buffer.from('fake document data');
      const fileName = 'document.pdf';

      const result = await forwardToStorage(chunk, fileName, 'document');

      expect(bot.telegram.sendDocument).toHaveBeenCalledWith(
        config.storageChatId,
        { source: chunk, filename: fileName },
        { caption: `ðŸ“� ${fileName}` },
      );
      expect(result).toEqual({
        telegramFileId: 'document_id',
        telegramFileUniqueId: 'document_unique_id',
        storageMessageId: 54321,
      });
    });

    it('should handle error when forwarding fails', async () => {
      const bot = getBot();
      bot.telegram.sendPhoto = mock(() => Promise.reject(new Error('Telegram send failed')));

      const chunk = realPhotoBuffer;
      const fileName = 'test_photo.png';

      await expect(forwardToStorage(chunk, fileName, 'photo')).rejects.toThrow(
        'Telegram send failed',
      );
      expect(errorSpy).toHaveBeenCalledWith('Failed to forward file to storage', {
        fileName,
        error: 'Telegram send failed',
      });
    });

    it('should retry when telegram returns 429 Too Many Requests', async () => {
      const bot = getBot();
      let calls = 0;
      bot.telegram.sendPhoto = mock(() => {
        calls++;
        if (calls === 1) {
          return Promise.reject(new Error('429: Too Many Requests: retry after 1'));
        }
        return Promise.resolve({
          message_id: 999,
          photo: [{ file_id: 'retry_photo_id', file_unique_id: 'retry_unique_id' }],
        });
      });

      const chunk = realPhotoBuffer;
      const fileName = 'test_photo.png';

      const startTime = Date.now();
      const result = await forwardToStorage(chunk, fileName, 'photo');
      const duration = Date.now() - startTime;

      expect(calls).toBe(2);
      expect(duration).toBeGreaterThanOrEqual(1000);
      expect(result).toEqual({
        telegramFileId: 'retry_photo_id',
        telegramFileUniqueId: 'retry_unique_id',
        storageMessageId: 999,
      });
    });
  });

  describe('getFileInfo', () => {
    it('should fetch file details successfully', async () => {
      global.fetch = mock((url, _init) => {
        if (url.endsWith('getFile')) {
          return Promise.resolve(
            new Response(
              JSON.stringify({
                ok: true,
                result: { file_id: 'some_file_id' },
              }),
            ),
          );
        } else if (url.endsWith('getInfo')) {
          return Promise.resolve(
            new Response(
              JSON.stringify({
                ok: true,
                result: {
                  file_size: 98765,
                  mime_type: 'image/jpeg',
                  file_path: 'photos/file_0.jpg',
                },
              }),
            ),
          );
        }
        return Promise.reject(new Error('Unknown URL'));
      });

      const result = await getFileInfo('some_file_id', 'some_unique_id');

      expect(result).toEqual({
        file_size: 98765,
        mime_type: 'image/jpeg',
        file_path: 'photos/file_0.jpg',
      });
    });

    it('should handle error when getFile fails', async () => {
      global.fetch = mock(() =>
        Promise.resolve(
          new Response(
            JSON.stringify({
              ok: false,
              description: 'Bad Request: file_id invalid',
            }),
          ),
        ),
      );

      await expect(getFileInfo('invalid_file_id', 'invalid_unique_id')).rejects.toThrow(
        'Bad Request: file_id invalid',
      );
      expect(errorSpy).toHaveBeenCalled();
    });
  });
});
£6äM¶@xœ�TÏkÔ@F­kXÚRl»==ð°»²»)*H+­X×JÕÒ.x(¥L“—dl’Yf&»]J©÷"XÆ«^¼JA‹/êQü¤'ÿñ'Élí’ÉNÞûÞ÷¾÷M>Üy[|éÕd»�°óuÐã$\FÞ¤6Â6¸œ…P¨V-ÁmËa!¡‘Õ`\
KšØŠÈ‚÷VŸpt¬€yr‹Fn¾÷éR¿dMAº”Vº–/ö x(çh€ŠuMª¹^õu¤_5sƒêû(¨ß£U+«—ój?÷&¿Îä"cÁÔ9Š*ßýq§ÖŽHHm aBîÏÕ.AÈì�jÈœ8@Lž›î@lI›Øúg³HH05²”i -B¥Á,v$ ‘Ë‰�<¶eÌ1á–¦céìJC§JwSLƒ¦qNáVÍ?uØso‡jÜNœðY8°Žà K#t€Dø¤‰ÐQB”>sD¡ÅLÏÀVZ	7hË¢*U%›ÅZ†Q4dN‡T]Æ[„;su¶,'þO’×|ä²Óáê°÷gw'Jýê¿®¾^98êÛ½ ^w«Ý~÷äÛi5‘W¹«Cjdà‹˜ß±,XÂŠ™]‹JFÎ¢Ï$K‡¥¥ÐO.¡üxþ
¤���µ`ƒ4&% ¡Z4ÐbÊä®µÌ†\NÐJ„Ôc–>‘0kTopVi�è7)0rÎ¸ËæFÓ__N° ‰Bí÷Ž©Ý�Aõyèq§™eé£sì7m5Ó…®kü˜/§oßœŽ’·5]=mÊbþ·›…šXzã‚ÐŒÐŽ%>ÕJéÍ¥47F7øl*Ö6‘S·š¼ˆmÑ1ó9GòØàg'§ŽúÆkçÌpl‡,»*2ã, z™w2K<â¨¥äuŸDÅ	^k«'}¹ò?
ž0T± t§k.Ôx~o&==kæ£¡I»ÔKÎM=Ù+«eCiÛ�ËÖäž\ J7¬¸³ßxœµWmoÛ6þž_q
ˆê©i› p–Íú¶­i»ÄÃ>JK§ˆ«Dj$UÛÍìGìî—ìHJŽl§I³u@‚ØÒÝñîyîžcDÝ(már4™SÎÌì„…(´ª!š¶rlÑØhKô™Fnñ%7åÒLªÇ™^4V-É0.*\µ+LÚÐ7aÐ��%óz¢&X7K‡$I�ÎRSr�yÚZQ™Ô’Áv°&ÿ­ôîÝ-¸ÊÒ¬ùûÏ¿À–ÁN¤mS)žwVBžA‰Uƒš¬¸…™T*pío) LU¾ «€ƒ;
WŒ�ð†íÄPc­ôf¥{˜©º¡ô(äÉË'Û÷w÷€}GOwc�Ë2ÞØV;—R!4EßÙƒéÂŸI?`Ä™ädƒ	ù¤[™’dâ DýÜ�q ,+[ùÁŒá!í£'ZóÅé»xLÙòœO+<ñu=Å‚·•=öžß^š>†ƒÇ[ gkCvN&@qm¥`µª*Ô1„ç …ÒÀBn¨BJ1\Ú&(	¼YïwNƒ—Y¥²îÍÅˆþ\ÄÉvé²Øañ…õùH­¢Ãˆ_ôœb*Ô?læ³¸Ä¸‘Ô¼a”>9 ³‹]�ppp QÁwÇ	Îí3™)Ÿ9Õí>9¿1dq�|ôsh4b>†(µu“Z»…Â	„­~úX4Ì+ó§;Ô…eQß›UÕuÌZOºÓHÍ#Mhyñ	)7™By&`Ÿqá¡c§�A¥€¢™ÒU½ëø±Ôß=ñA˜N\ìC—DœXuˆlggÉsgÓ…î¦Ÿ\\¢o¹-é€Ö�¢¸sí‡ö=AI*ôòäî°•É�÷º†íîœ_Æ¼xßùùV"Hba 1ÀÑÏdéD‹Èô#Ø˜�Œ|9R…R¦)×áÏ§+±
¾Ržƒ�fÑ%§\¢8i›œ2“â¹Xq’‹3ÒÌ<Z‡U'Žk¤ƒ¯?àBŽäØ”Ü{ƒ’•Ó:”Ä«×FÌ¯�m&ly”ï.Á[ºÁ¼�~vŠ">Í¢ø]<€êó¢°c»ÅQ÷|s¼Â›‹ÛSç»VüùK>”tu&äú_yéCÝÄŽß#]_o®‘åAÃóÜÉ )iÁÒ’˜kX«™¯öû“ÃïŸ>{þâå?þôêèõ›·?ŸL®“"Y&qØi¾U'^6Ùrî×ÃÞnøý!›8*ñ5¯•Â§×%î=62w¸¾Byf{jï¯wÀÕ~›Óé·ª1Ä½¡JÒýÊ/�í¢ãØ'Ô*4Eüåâv+Õ¿w£:]«yëR·íF*î}]Ù³¥V3$Î¯Ð %Î3ÄÜ@Íç'=áBW!§mÛ6Þz¹Š¯„;$Ô•Ö%ù/$qçþƒ‡»«¢+‰�áÁhùâsºèªŸâDão”’!@'®|–öåv7åJÔÂ¦¢C:Ma2¬Õ�Y[åðúÍ„¶BÍ…Lz;¶rÏ±B‡›¢>ÕZé}'[Ž¡•‚ö	EWdJÜâ’ƒÚŽúx0mi« ³º«öîª$)`’|µ[íŽú¢tC“§KÒ©fÒŸ—Ä«Œ·¦“Qúwä£ÈiøÊnþ¢{†5w›Âç{‹{Âüú;Tß‹‰¿h›_IûÙ%O½h¸-÷•šÜýþ>¹_íºãxœíZýnÛ8ÿ?OÁ'i7Ö—�/µÛC“¶Û·m�dqÀ.-Q¶6’¨JT_6À=Ä=á>ÉÎ�’Lù#Ûd ½»º®lr8ÎüæÇ!Ý$+x)È¡±`åó4Ý%ó’i_Òp¶K"V…e2a»„],»$�wÆÃKrKâ’gÄ˜Ôy X%Œ';;)¤d4=�qÁ�ë8fe@Ôz»9LZ-ò�˜ùþ¹Ù!D”ù$$äy…:*ò=¡sš3ÎLÙIˆ1¢¨Ç©‹”ÓÈž'—IÆ¢„Ú¼œ:ø­ÀoNÈ³49#gtàœ¾ûa,JšW-Y.ÆÃNh	ÏÇž]äScWÎ`=‘�$&æŸÀ›_ZDÌJ>'9›“—eÉKÓø[;)‰ø<G;HL“”EF3\-‚–%]¨Åw‹A�Z»ÙXñˆ«6ºØÔHù[RðIã1Ç!¯hšN(ÄÄ»öŠkòöôåŸ ¶uiG‡Ðêº®çŽèht4Ú—_ðåâG|».ÈM\w4„�‡Ð´ïÀ{>È×¼]ê†ÞÈ�ÜÐ�À+ôŽ<ßº±7ò"�z±Çä3ôBßõG>óàéû¡?”m‡ÃÿÈ‡îÐŽðåÅþÁðh‡þ0ôÙp­~£zi‡×YêyÚŽ !–{`�êq7þñ\ßº#�Âµ€"
­!´á †qè´Q\ýQ„!»Ös»#ãpZ²ANŒ–$PÀ<ÉêT$€>An	&ŒQ 8®s{^&‚™†#²ÂÁaQÉ‹J
 ±»L˜÷Þ;0í�˜ŽÑdG,
F~’œ±ª ²c- ìˆ”¢ž¤I8N¢€ ô“|Š†ãTãœfl­•é�-ÔÇu™.ÛoÁ9­L�³2lß ¯Ì|[ñ¼Þ`÷ŸÉ)¸,¡éÓ5íÏ²A&z•äÑñâ5­f ŽH�_æ`.hÍë4Q•�uo
œóRQ$f@Ú.+€Pò,©ØÓ¾�ÏZÂ*™¨Ëœ˜Ë¬þDLË}+kS‹V(sSvÆ
Þ8(î¬d·©X±™ßÕ<½bæ¦…ZBP®œäE½ô ª’(]UwÓ Ù¶m5íæ“Ÿ�â­ä
0o /K‘3ò2zªÂ¹ÛNóÌ²[aòË/Ä@SX4H¢.a”‘Ñs0‰ô|1›‰nåþYq/ç´Œ.ø¹à%�2p×Ò=;ÛV$XÊ¦%ÍÐÉh¹!¦„3Ø2ðüac�.õSž|¬;ÙZ~Ci:	éJð#«*x äÑáÁþv¢í˜„h™�ñ¨N!‡mÛ©ÊÐIò¸¤à¨:¨0'J ‡UlÍ›&ÉUêA2¬3p.‹šÕÙ£-L..¿hq‚‚UÐ÷Î]1Ws+Ä3xtQÒc.ƒ¨õà˜w’(¹Û/{2Ø/$[@ÏµpŠ”&¹Ö_%ÿbÇ�	lÚ×ØŒ‹xXg,×µÞ�˜35ðÿiCzÎÊ«$ìü¯ (Øˆ-¥lÊ„òlÌƒ–Vñ¤€¿Œb»Ž1.2 ÝêÐ'…´(À§²Öpx(˜€F³n±RAAÄãCë‰Êùæ¦?ùí‡V~ÂÅXðK–ƒr õho?x~|2xñò•¡§øåÖÒ·ˆœæ<‰$oª�'¼Î…¬Ü'}«þ>bU[Ð4}h’\µbÆ|sÓÓúÝwhqßµð~�»ã¯ÿþ™,
ZU„BŽÐTí—oœ÷$ÉåYi|p®m¹`mÖ˜}¾ÛÆYq*½|Ç6»³’8Ï šQo åŠ†é•}ù/öÚÿÊò©˜!v�¯’iN‘
ÚºØ\-Ï@º©Ï žã¡éí[–]ÕYÿ™î.��u6‘XZ²È©E’V.¸ÃšI=ÁšwÛ µµ¿i¨�œñ*›×4�RVvQ!Bj&Û•h@û<&‰<ihœÀˆiÈ*y(ÓsQòÔUME?X6–­ë‹ˆv6Y;C�…ÚË}ÕÆö“”Ñ®Üî	*¢Ù"´BÛTõ÷å®î�<¦—Ò�íYà�ùFi×ŠµiV|‡¨‘L`ÆjÆë4äâ~¹SÕšÂ"´V ûÈðUdž †¡žÀ r+ùºÓÛÛG\5õƒ´‚IppP ÓtÆ+àÁÁ¡EÒ¬´µš11ã¸çœ¾?¿èhq&s»
Èrã2ûhŸ±yÃ¹]Òdûª!{	
’ˆ
ÚQ¤ôÆ¦§î8ÈÑ��«ÕE[¹Ã²!s™	þéû&ªžUºÖKNTÐ×ˆlY'£ÆX!I·%,*ôëÑ,JØ©ÈÛó÷ïÚc†9¡ÛY¤ªCìŒ–‹/0Œú‰…ô¶`âÚlµ'ñb¥¢Y£bðæ¹”5åÃÚZÏhw Ëòâó�ÄHÖPÒ�ázHYÝ‰�øêz�AËEoÆØ´Â]}¶AX?öÌrb§•‡ýÿ'’árØçÛÒ•¼yQá^¯–…þ•X�Â²s.@ókzÅ`k/X)¸ ¥d,­Ý¸êO¨*þûŒoÎãpFÅCÆeê qŸ¡
!¬ìÆl%s=û‘¾a+PeÕ%û¯M|=�ÅµxÄ4~,®“_Ñ4‰ä2�ß×î’>‘Û¡pÊàE›8¿j¾š½¡;�S>i„ð£ù�Zý'ž4g›$$;H#Òb‘F±±eyd¨ìjUïªH�å:Zzd(*µ–}äþÈÜÞ÷ï`Ôu$F,ªUÚ²u4&1ÁsÌ–Àn#·ÔÌmµ;©r¾}³áš§SýÚiËE�.­ø¿Ñ�ô	p;ŠÊãõJŸv´pxäêÇø†ŠÚ	dTzœt÷eÉæ«’».JÚ�­élº;1|×ß¸{ïðÂuù×†4úû²(ª‹èþÃn{Yôp’Ñ™0–¨š18
–Î9šßH:�ÿMÒ¹›u�;VSîSøfî÷¬ÒOg¬¶à\^ �9­D1e‘®vËu�ÕÌrÌX~"µèUUl;gKx3	¹ªWîôlÓM ®`mPmWÜ¿¹¸KïÝÜ­×Š�CÛXÒmãã5&¾KxyË!�Ëà8ÅÃÜÿ*ü«>	½¸¹y�9/S`†{Ý<î1âÑ¹»M¥{‘w‡ô‡’÷öYÿÏÞŸ‡½›C>¿‚l¢Á™£d² 'MB5?=¨œûšŽüšl*× ÒMÆÉ·Pû£þÃú\ñE‘ÃÈ>Êƒàœ¤´œö/�Ûÿf®îôøãþØ¼„"¾võ'å’xœÛås×{C0óæãÌK9'3q	oÎà½$ dp$è"xœ»ë}×{ƒ$£Éä‡üÁ›sùŠM6?äÿÁ™1yµ Ïæ:‘gŒ`®þdNé)›5Ä7Bdõ7sJßb3gm®Sðƒˆæl¶TYÂ ±‡¿²Žxœ½W[OÜ8~çWœ­VŠ#‹Ä,EBÛ•ª]Uh«¾Pë$c‘Ø©íì0Eóß÷Øqn3áRŠšØç~>ÇU­´…{È¹É´Hyü®æ™M@XØ@¡UQÚÈcË��NöD§ÐÈRÈÛVZXþ§(y/-UÎ�³¨ñMnFZ{ ™æÌòÏ¢Nð…ßYÍ2‹oï¤Õk·Tª¬ÝïW“ÂŠïýÚVñd¯óFéÂèla–Ló|ÑXQšÅwQ£Ó½LIc!+9“M§ÀÌZf@(¥5³KsÆj!o.¯c8}ëƒc+†i_´�SV–¼8­XM‚	·Ð+¹«îÌ´jeOÂÞ0»l	÷¿°‰]‚¸»Áp»�èóûp™`ÚX¾HïKX™¥jÊ<`¶ÀdÞ8	õ ]ƒÄR¡~ˆzq[šBhc/0>,Î×…­êE��Ìµª÷]»÷½Ïôº¶Šj´¯ªOŸÞÿAâÍþoÔÞÙ¯'#C†ãÿù�[:Yj‹ÖÃ‰ôÁ%-yYª(žœ£äJé2w’£è\�NƒZ?rZr–WµS?†‘O¿CÚ1D7šs‹@qÁF°IfÇ1<¥yÝE7MéyS\c¼íu('mìç�¤Î8A1ÚùŽ)Óš­[i÷Hk�1é-RÓ¤^’$pS«>zô£
>Ÿ#ê.þŠf´i€“‡?q/k�#ÿä7ý�ôVÞ}kXÙ¶òOv×[(õ;×ÛþCâ[41$•l¹ˆã³™Ä&èy‰íæ¼õqíù.„DÞ`×’ÐÔcÈL,í9zðHúŽ‘\+xÛÌ§<Ñg9GžÄ“&Ç£`C^!•ñÚÉÓV$žlä\Gpþ¸O�+Ï0Å®®Ò««lj�}I¿dÏ6áÂ t%Üß\Y3µåwýæ°wòx‰±v� Ê'ž_ñÿµÅ«�y}
lwÊ²Û©ÿ´JÏJ�Ò—(Íéì¦\±[yS—ÂÍÞ1¨gâ[Ã·rkÉª1<Gž’|¹ý½¦oI<ï(4Þ”¼Áxw¬¼XÝ.…~‰v(©�½+ß”f¦¥÷Qnvœ1õíHÎÁñžÄFKDÚ’‡©•;žˆ¶ëFd ›*ÅAá'ÿÐ°7Oûzw6£½Fó#ƒ¹FfÌ™eÑ¤ØÙñ�¬'³Ñ™s±ãýÝ�˜6Å«�B´õ³Cðòàz4ò‚JŸÈœ¢AÜ�¯ñ¤])¾ÿÍåÍÀõOL�ú9"2ãxÉ*¼X8€tX’MYB¡4àýÖ`Þ€�™ªjÍ�	
í�óu0”
ù†j¶.ËFwÎËÿ†¡±•.è.éÂŠc8;ë\cƒUFâØ7£®(Ý•h;€Ç|†¦ÏÞòl8yM”µŸg2à¥$äÂÜ‚›­ª±à¢wÈr´µZ*ü.d:[ŠûyMTM.{/ÄÔR<ILX‡Þæô“u§¤7¶ÝHTŠ©Tv§MýèÌ{íç3Ùý²Æ&/¹$¤ò%­¨ªñmË¿ŽO?S[?÷àøÈüÃYŽé\9ÅJåd‚Ý£8$Î£Ä¥ü5–iÛÛx¶3À}4úúÝF î²R>aó
˜u¿ÿŒ°e–²xr�þ{
  "compilerOptions": {
    "lib": ["ESNext"],
    "module": "ESNext",
    "target": "ESNext",
    "moduleResolution": "Bundler",
    "moduleDetection": "force",
    "strict": true,
    "skipLibCheck": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "types": ["bun-types"]
  },
  "include": ["src/**/*", "test/**/*"]
}
è¾nðï—Ñ9xœ›Ç>�}B£È…´Ûí¹úþ/´ò¶×‡+,Ø0•×yãTNf ÕQºë—Á\xœkflfœ�/—[˜zî×jæ+O5^	¿Þp`Ù1w £cpë—¼CxœÛÄ¸‰qÂ<‘?<
»L×ìyZ>G@ÅW«[áÑ› ¨çj—­/xœ;È±�cBÁÆÆÌ îwé—ÒNxœy †ÿžž��ÐfÛ‡m/Oè*n·W  °•C‘•ÊŽßüŽChbBËÇcáDÇ$3š¹³sc<wK‘õðKf”úž�çm(€*ô100644 package.json mÒ‚ô­að0>³1BqíË´äl“Œ¯4Ði—‡Lxœ[ÄÒÅ´Y‚‘‹ ¤
n�²/xœk’l’Ü°“™Ñ`ó.æ &Bl�¨sxœ{$ÿH~‚£ÁFííü !B�ê€Pxœ: Åÿžž°75ø¸›&®äÈ¸ßÆ† x¤Ø?Ñ³K‹v ø½°—¥ ®G)É3këkþÆ“ê´VI¯xœ340031QHÉ,.H,IÎÐËÍ*føÑ‘ÀdÅµp³H�§~Êç	òW§íÝjQWP”Z�X”
VÖ˜1ÿ—ÅÎ˜i©W{�Ä=®ýS w:Åê—�~xœû"p‚{Ãv¦Í[™�2*$çç—(¥ä$&§¦(Ø*¤å$f§êA4ô'G±ËM^Ã.1y{˜Mr~iNŠB^~‰BiAJbIªBYjQqf~žBfž‚J5Xg@bIF­‚FAbIIjQXmZ~i^ŠædŽ îŠ,gç�´{xœk’l”ÜðŒ‘ÙHÏ`ó+Æ³ŒŒ†›w0Ÿâ  b½>ã�?xœ›Ç>�}ƒ(£Ù‹²YT•ïˆÇz„Nl´Ó51 ½âÔÜ¢Ô†àé^üO…ËXLuã‰®(2z^¸Ù›1˜	 Ýžâ—�xœkeÛÎºaãä‹L"Û¥'ë3] BËò§xœ340031Q((J-H,JÕËÍ*fkÌ˜ÿËbgÌ´T‡«½Fâ×þ)‡  «Uí€!xœM ²ÿžö°S³{[<mî¸Û‚59èÇ_‹“õN±©‹bÙ100644 package.json ÅzveF [²¼ìº-¶Ís§É¬“Œ	d!in�·xœk’l’Ü°�™ÑpóæS &2n�­axœ{$ÿH~‚³¡žÁFííü )'ïà€xœûÆö�mƒ³Èç{*›4�»u~<ûÁEKéÖ©8!}ÑÉzÌ øŽâæ�¡!xœ6 ÉÿÂÂ�Un<YCø–;” ¾gM31k�Û‘iƒ—Ûd«ÙÞÔžþwGí'ú¾r0
Æ’BrQ#ë‹¯Oxœ›êpÏlÂÔÍüŒ«…&_WÝ,#¡ËÈh¼ÙKò4 ”�	an)xœ»gvÏlÃ$F“Í“EN³ 1.ßªxœ31 …¤ü†õB¬ZŒuÕ�Îå?ë×äûúûÎ É&`ÙŒ’’†ïÌ“ö?ÞP2ùÎ‚¹Gû^ˆŸúÌ	‘.6fð	[ÀÇôîú–SgXîh‹|Ùk˜! ’$«¬xœ31 …äü¼’¢üœœÔ¢b†}×·½V�>McÍ‡ÛŸ^,ìIø­înV•›™’’“ZžX”Ê°îv×Ÿ-w|3"�ÈÏmTsp£DQQ~iIj1CyvUãÑ’ŸääOšT¥u´û²´3 Ë0Zî‰Š@xœ{Ãô†iB…È�éE7öúË_[¼#Ó{›~Oâ"G�‰= ÞŒ}nˆä xœÛÊñ‰}‚ÎÆàËL“�˜? .á‚Lxœ! Þÿöö°§`OÍe ÅÀ£ï»uq¾©]'A=‚³»»
@ã8”Ù8xœUS¿oÓ@V:FB)¦<©¢JLc;nŠªH %ME+Uš"6zû’\}öYwç´™ÂÄŒ0#ü]øk*12³òü£”Nñ»wï}ßwß—¯jßŸgk�óÞ˜Ó$¤1Œ¤63ÅÆoŽ! †N¨fÐL”ÒPóÞíI!iÀÉjµ‡Òq]»ãuìÎžkïy½gÝ�´ê^ö£±]ÛÉ~5¶¶ )øœ ÃþiÐœ½=9&Û@F¯ONŸw]×Å�nvoýÉÚ®�}�]­oÔVC†XËˆÅš£Š
\ø¯ø%<½Ô†EA«^GñTQ4¢0a
™rCé‡LáÅSEÙ”‡ Ó€Î!@*IªQ’â �gl‘hhz®÷¬íîµ]¯e×ëm°VÖ!>ŠeõnoåÇÖ˜©÷YÞ¨(@sTg˜`ÿžš,^À‚S 3¾óÓÁïÆ ™¯˜Ñ­bõ å"Èç
WSACt�¿vÌ/I«�Oð&ùU°7ï‚^ü\¶}™,«²$éŠiC•�;3ðþQŽJì7ótâ\HNßy¡� 0Á^F%½ä†Qèû†ËX£ ¬ÓÉÍA©a$Uñh¤ðéç&@Ée(#ÊcËÊF6Jô¶`
Ã…y»\æ³û4–Ðœ¤y4+ûœÒÏjI•Ç(û]ï®i4È4öÑóDJ�F�TÌô6«,v1�Ž�>s47_F*…zN`#Z L¹`EeÛ—Q"5+Ÿ¦ùólY‚Í¨�”¡•sŽ1AÂOhH9¦Â¤!”ëáæ¿d_�ÝÿYûC#+ ï„$xœûÆö�mB£ÈbNÏñK7'æh¹|4ü9CH™aãÔ‡L Ôc%ë—Ò xœkflfœ�/b{ ®zõº„/t�N6ßY«Ø �œ¹ë—ÍxœÛÄ¸‰qÂ<‘WSzì£Íñ9¶Ö¿sOëÖÖ¾ˆÏ9 ·äm—½sxœ;È1›c‚åD?¥��˜$J,ã…;xœûÆ¶’mB£HÌ^éM¡œ_X�|š>í:k¼!?yâÔ‚ÍAŒ*L më—Ó;xœkflfœ�/Â*ùææ¥,Ë=Eb_®.­¹®2ù	 ¡‡+f—Î"xœÛÄØÍ8¡ [àkxœ[É¶’mC"“HÌ«NwÝCºSû÷_ª�Z?¹”i ç„¬à�Âoxœk’l’Ü°�™ÙPÏ`ó.æ .PsàIxœ[É¶’mÃY&‘àÁç;&>9q7þ:+³ú‘ŠÆœ�ìÉ™< û¸ëŠ:xœ;Ätˆiƒ>£ðª¦‰AÎ‹™/¿-½òçÌÝ€oÿ ŸH‹é…�xœKNžà/ré¦á�K�§^Úõèµ·Œ<o”¶ÅD «3Qé'xœKNžà/²T£Nªðõ$ýÎòÌ”>“­³Š;.º ‡ž+ç„íyxœG ¸ÿ‹‹'100644 compress.ts »ë±¶ˆzqò”ú˜O0SËª0‘'®l G[æ®Ø¦ëÙw†GQ½‘é"ÊçøîWxœëfêfšpU$ŠûãŒÊõ©á‹þ¨K÷g_¾t‰ÖÄ—J Êíç„íxœ›ÈsŠcÃlæÉÛ˜‹Å‹RKJ‹òª’*KR‹­Ò«2
RS&¿aU .xà‚xœ[É¶’mÃY&ßÛ¥E]õ¢–ø(Ý2r:/ƒ´îä‡L Ïs?î�Wxœ;ÄtˆiÂ‘^Ž$†ŸZ¢2suß\cÚzi» î$F' ¿�Þªxœ31 …¤ü†õB¬ZŒuÕ�Îå?ë×äûúûÎ É&`ÙŒ’’æc	ŒÌ_,[½élÚÂ[oðß8"]lÌà¶€�éÝõ-§Î°ÜÑù²;Ö0C
 ž�!¡¬xœ31 …äü¼’¢üœœÔ¢b†Íî¶ëW$´UhzeMíñÚ1ÅÊÍ¬*73%%'µ<±(•aÝí®?[î:øfD‘ŸÛþ¨æàF;ˆ¢¢üÒ’Ôb†òìªÆ£%+>ÉÉŸ4©Jëh÷eig Å-,IïŒ2xœ{Ãô†i‚‡ÈtÑ…{/=ÿ™¼Y+´�«¼¡bÆUî�1Œ èÓÛã$‰Š_xœµPËJÃ@Å
UjEAw^ë¢ÚDíBŒÔª¡`AÁ.¤;M¦vp:	“‰¥Ôâ'X™�ÿ‘¿ð\úâÒ�Ó<úw1÷ÜsÎ}LëßÇÓ_Y[ØŠë©—r=@à	NØ&•uâô9Æ[íô–ã<Èu1³Áq1G‚8ÌÓ5(ª€3q4€'D}e¸íãÄ8‚”ù�#W¹Ÿ#æ…´¨ŽÂQŸâ2¦x€™ á»Ï†f©Vî‘wÎ9Aò¡ÌŒ›Ìù¸P=…VXl‹­5†aö%¡øbtíw)±ê6L”""€\‡‹|N×�(ì®ÑSd/W8Ñ@½¨õškCYË¾Ê�Õ¯t¸“Kk�à3c¤åûÖ`™c›pl	¹²]—o™Í;æSZ‚qh
j^$|Ï„Êþa)†úÙ˜+,á \9Vxg»&§	}&ògï(øØi¦þs× –½Iý…ë¼ï�qxœ{o9ß|ÂO…´ÌœÔ Ô‚üâÌ’ü¢ÊÉúŒŠ›¥™6‹²hrMî”âD–ÖKËÌK™<Oª³¿47véÍó¤™ ^Ç!œè…xœ8 Çÿ©©��[sjxg"Eô|fZÐèîç’È-§±•Ìò »ý™²ÒMÙ8T4—þùòó£±<“u´ 3ë—Ýmxœkflfœ�/ÒÎTVžô)ò:ÏìÙ3?ùÜ›Zö ˜¬„«xœ340031QHI-ÈÉ¯Ô«ÌÍa�ú½“ËfÇÌsÓ25<×OÞmrÈ¹¥!DUZNbvjFi’nAiRNfq†nQ~NNf^º^e"P£ÉË¼‚Û­}ßB.ÿ½?kf±öÑ¥ý· s3‹Šò‹tÓ3KRÁ–d&í\®æy¬¤EéIÚž÷^å/Ï 1;
å—Ø4xœ›(5Aj‹‰ž�Òä8f‰ÍªLo¸ =÷¤î�Îxœk’ü!±á#³‰žÁäWŒÕ`:“ÉÂ4 ¿Ò ,Í¼ÒK?£*ÛØ8030Ë4=¯8ÃÓ4Ð<%-)Ñ×Å¼"½<³ÒÅ»ÜVÉšK&p‹NþÌt„ÙPÏ`ó.æ Å³Iëˆxœ; Äÿ©‡��¾êL…ê=:Þ�+ÚZ&úß�^å!‘•.±å|>Åf×™;xFgñ�Ìñ|†ìÔ “u´L§ë—àsxœkflfœ�/b ›+þsMsçL{%AÍŸg™KƒC	Ì«xœ340031QHI-ÈÉ¯Ô«ÌÍa0:;Wå-Kch£ Ân‡>·;Ø�!ªÒr³S3J“tJ“r2‹3t‹òsr2óÒõ*�M^æÜníûrùïýY3‹µ�.í¿Õ˜›YT”_¤›žY’š¶$3iàr5Ïc%-JOÒö¼÷*ù{ cÛ9�ç—Û8xœ›(Õ/µ�•‰-'±$µ¸dr³ÄfU¦7\ Y{å�Ñxœk’Ü*ºÁ™Q@!3/#µ(³D¡¸²¸$5×z²ótfC=ƒÍ»˜Op  ïžœé�Txœ9 Æÿ‡‡°gÜoÄ2â€ÍWk0!¡¯°íœ“{Oƒ}&‚Ÿ˜*Ú¨s@4¾/ ]”B“Þ)��í™[xœ{$ÿ_tC?ëä<nßÉ^¼1›?p19^›§óz3 ´®
Òè„�ixœˆ wÿÕÃ
�öŸ~\¦Þvµr¬„T¾z±AmÄ‚œ¿³
$“^s=Ž6F&^Š‘Î.|UÝXR*Å“ åŒ?qc^‹£cZ×^Ìéù¨C“LI©)4Á¼q&6ølAHì±ô…ÿ“©ˆ“`aª~¤8Ï-Ú »m<Õž�5{/Üƒ©š;ë‚ƒÝZxœ�U[oEqhj'ŠÓ BJs¤VõºqìØ¹ ¡´%!¢%D¨ªñî8^²Þ1;ã&Ndù™7àˆ_ ? ÄH¨âäÏOH¼ �xCœÙ];'PˆäËÎœë÷}gf(}2õáíN¢P€×„¹/ß‡µ¸†Ykº;Üš–Jxl›ƒ]oOI°*9°]É=µb;|�Ñ^[-³	S¸RA�®ùž°ä/#K/Âº'ê¶äy�KáÜçF6ÛYŒ„ø9þ[Ü‰°îpóa[wu`B{äëÂj:Üèdòù‚ôÌ‚U)Ø®Å÷29‚�ƒä2è�—#Ðrþ¾ôë–{AP¥¢‘ån�{<²vìºÝs_÷:':„vø	òó=n6/?
—önëŸ*‰!©ëÞÂ§'Øhÿ+~í<ÝÐÞÇIèÓ¼Ü¯•.tlÊý²œV9(<cšÊvdA‘ã¶ÇêÇKW…·Ë<kSl³X<˜Ã�Ø5¼›Å{±Æ¢¶¹_~Í­
òù*–ÅŸciü36…Wó¸989¤ý¨üfð¯Nÿ„ëlÕêÕÖ¥+œŠóefÖŒ£MˆèŸ×�·Î<ƒ°um^þÑa±ËK¿G[“D„+##k¢éX Ô0_HB	üúÜÜÀàãOÏB›æ�í2òÎ¨ÑU3à´f†²ãOÉÉ‘bivn~áÙçžŸaÚÄ�†¯à�¡”ó
>HOá�CU|uìr†®†b	*-Å%0sÁc�–*Ï‚ÿ$ñ·‹Oá»Éî�åÉ­™»y=Cwšõ
÷²y%nq£ˆ¯Ç'Æ‹§XJÚ’*E-øþùg>Ž'ðáØ®Æ7S!¦¨7š
?O^ÀÃädHÁÎ(ÐB—Ç˜gŠ¯2Y;%‘+,^6½VC‰�º¦‰’å½IÇ-ØX½9]š_ˆ$ä¡ÉO¸(S¬]ê'öÔt�Œøkzb…hÎMhmuDHI4	±3Ž�ä8¦&C"¶÷í†ÏiWh.€¹4/;d´ÝcfárüþÜ¥�²wÎ@™‘ ¿)9XL1zSxtl¸+šÛ5P‚€›œ[8šº¨ßþ´‹ùq3®�˜„µˆ[ÉáÙR(%¼©€¦Ÿ˜£°Ëd‘lh¸Ù/ÿ¹úØBH€+èD(ÅëuÄƒpõeÑ]7‹ß%'Bì¥ÿ�ï�Î#Ž2ÿ�,§ïôáú%­q].–µ“¥¡{ŠkAÓ=‚…oÄ_ÂÏÆÞî:xü=ýö
�ö>‰V…}î‰Hó+g.(Ã�j¿½`^Ï¬¹÷™c[°^½‘ò™Þu¤¿ÁM°yâ�ä$xœ»˜ÿ-{ƒ #›ŽBn~röf ‹‰97?e²ƒ™Äd�Éï„ä6'0Wò³s)€ÁdEq#60g3·ð.ÆÍ§EÂ˜°HNbW(Î/-JN�¼[\uóKñ}\“ãÅø‚�ŠòKKR‹2óJ2R!<…’Ä¤œT…Ä" ?±$U7'37³$5Ed˜–>�Ì,ÑPO,(ÈÉj©P «ÈÌKW(ÉW ›™_6jò~y×Éü*Ú†sSRÓ2ó€
óóŠÒò‹@J‹3òKsRJ‹SÊ3K2‚€¦ù€›\ ¢îŽ,¢®iÍöŽ¾¾BHF*’Å
i¥yÉ Sa¦%¥*dæä=IMg³ƒZ'Ëd6.ÝÉïµ'GrñmæÐàæOÎÏKËL×ÏÌKI­Ð+)ÞmVÊ '{…ä�>xœû–ý2{ƒ ãf1F6&ÆøÍ2Ì‡ø'{+‹Nþ%$»YVx#—l¾.Æ“˜œ#$W(Î/-JN…r&Wå„ó6¿#0IJn²ÇdfMÁÍRšwx¹ ÄÈÆáD�³xœÅSMkÔP…Ú 3U,ÊÔÍª/�|tfZ)Q,ˆ Zª+Í$·“È›dxï…¶qVî•§.]»páÊß!þ nýî|I¦“³°+³HÈ¹çÞ{ÎÉËkû÷•7ÏoA‡,±m‡³ÀÉDL¹ÃûN:x��°¸`è�Þþ´žiÂTïûx,À]¨§[`q24À»	Áò±âbY5ìAžÜØi·«þXÝ|JwQQ1Aò%”_ÏmÉïKëSQ�Íµã$ Yˆ\'cŸ	«K¸$BJS à9JÉNÔ‚Îüdˆày�Á‰@îu­¾jš” †"cÉßúlNã õ®	›ÆÔœÓA™òEÆ]èmÜ6ç`„~ˆL¡ Ê�ÀDXåbâÂA¹ÔZg}RŽ¦˜E”˜5¹‚›ô	ä§ƒs£ô!5íúÍ:à*¿ZS­hCnjærQ–¯´Xþ8¿×ÙGŠCæ� ˆkö•¸|²Ú¹¸˜¸.?^¾/µÖûéÜRí@Ì†ìÆ„Jä,{³�ó”QEˆ„s×qNQ[E œ†6¿Ä{E@.yNç…‚û(�¹Ð­;ró
{gVø°¡oAâÖ"¾¨°× P~iýêpõP´Êô‹ƒ Å±��W×òÿ°òîçoüsþÊÝU¹½rM~kòó%O~ºÀ–‹ûÝV>Ðê�¡xœm�¿JA‡‰FM.HQ	:Qð¹d+s$•…��]Ãz™KV.{ÇÎ%ï ,ú–>�…`#âSØj	Z¿)>¾ùóQ+Ý� ±Ô(õ—#ô áŠÐögÈ‡¨¼ûA¥3U<·Ýf¤–j£c¦æ ë;æ<çXªm	‚‰ räzÐhB··ŽyÉ�Ìš¾ã Ýpíœ&‘„Ò\i†Ò\wÌÃòžÙ_ö3|ñIB'([(‡8;—@Ç¦œ¯š×|¿òWOiŠ©·­ókµ³b!ó¹¶»¾€£Éj–éüjF¤ðýsQi‘D¸p=üÃ¥’¸
~•�ŸfÓ-µl>—Ž~¹)W7alßLÙw½—»¦¿Ñ)Ì©y}Ö‡…'ìÀdxœ{¤x“gB¾¬BrQjbIjTf�ŽBjEIQbr	�íšWRT©£0ù!«¿B­BZQ~®‚ºžž~qQòÆóŽŒ<%©9©¥9ù‰)©E“%˜œÐd7×3½bæ¨Õ´æa Yà%iægx& ÙÿÙÑ°ðfiledrop“üBfiledrop“ü³gò9
®ï“pxœkgkgÛ°šÉX;1Ù}ñw;å˜KL¯¾,Û¹pÃS P(I-.a0|¾sgË2��³'sö·oŽ‰Œw¿6ù“& `ÆÃ¢xœ31 …Ä‚‚œÌäÄ’Ìü<†Î§µîþá³÷?ø~Çãù6«=ÛW5Ú™€U¥äç&fæ14ÿmíybnÕdÍ&ãú‰Ôe[‡|C3…Ô¼2½’bËÜÍ®o§dýª>´"jnìŽMPù¹©z%¹9_§æŸ5«÷.Ï`ñºm§§rª(3/%µdP»ä›·²U¿ìxz~/9ôuæ‡Ó�F·dæ¥%—•&—”¥28ÖÎ	KNšÁç±ô\¬»Ç—§Oeca
KR‹Ò“S‹ÚNª
6fÿ¯úPžÃ±]ÿ€›�âNˆ¢¢üÒ yñMCûµ/ŽÙ{{šAx¯„ØGˆ‚âŒÄ¢Ô†üUMƒœ3_~%Z,zåÏ™»ßþ šÒ�Íë�2xœ[ ¤ÿÂÂ�U9n<YCø–;” ¾gM31k�Û100644 home.html �ßÐ¤ ´lgœ�áÉ{fO‚‘Ž^ZÃ¡�¢™'u–­£Ó§ÖK‡œ¿’BM´%ÜëŠíqxœëbìbœP&òSPÝž[hÿdAùm<¶_Ø¸™ ‚=	á©xœ340031QHÊ/Ñ-ÈÏÏÑ+)f8,xã[��Û²·
Ó>E?ce½lQ–œQš—�š¢[\’_”˜ž
Rí_ðö©KY·Çö¼=åæEë¬_ß›U]RY�ZR#´i†tšRÃ¥»Í…û½�O�ÃV¤ '°0~²±xœ­Z{oÛFÿßŸbä**¡);mW;vÏ‰ÕÂ@�¶‚àœ5¹’öLrU.éÇ¹îCÜ'¼Or3ûâ’”ã ­ Ò>fç=¿Ù•(V²ªáf<ç‹ŠÍaóJ0ªíÀhK˜EõýŠÃÃÀO²ºeUvÆU“×1œØÝÅ9¯nDÊiÌý$r~RÎe¼å('Éÿe²`¢œe5±‡ÛÊh}€T–s±€în^Þ´kr¹Xðª3¯–¬âÙÄÌLD™ñ»€&²wÕˆ<;çeö�Ýç’eÄ2¿«+–ÖW4À3âœ†µÜ´ô”×K©W*ÿí”­ü'ò)WŠ-¸Ó�ç|B«T(/kxÃÍ‰­ˆ“FlëI\¿5yñb^ÀyÎù
æ²qÃK(›â
E—s(DžÅQ[™Jp1­ÿ¨xLáê+–^Ë9®âé’•Bp»Äí,ÏáJÖ
D	õ’ÃJÊPsP±šoç¢5Ïˆ©7T¼®îE¹ ÔT©/k/5}8^U²òçÿ}Åh¢P°ï6r
µER%z=’oªRÁ¬P¸¹b5žªd~ÃQ�y�4ˆSµâ©˜”/ã9»§í“-$©jC *ÔžUÐx>‚¯o¤ÈáàP{�9J~ëæ£È6¦5Š×3QpÙÔn8FiÆãý­uk•cŽL¢DþPGÈš§4š0%~;Z
rqÍóûV�Dˆ•Ü¢c,½š�"g^Û.R”U5Z.Í›Œ£¤¼ÆÍ×P†UßßÝ2dÇî”g3)á”•÷pÆÑÉT­žAôÝ«Æ(�H—Ä Ë+Î²{@wÉrTóÕ=y
T²fµ�%Qºjj³RÉÀÐ^$°c'ç7<ï9„ÑÍ6Ìp]ÊšÅ²¶Còê_<­»¾0«b®‰šUÔ÷ˆîœKå58Õ$Ð;4­=hÊëRÞ–è!Wèü-g}Ã:SM‹í¹8ÀÊ”£*?š‰¤0{p^Wx´¡�^â¨x>?°�¥:€œ˜L`¤96¾="áÐ£:-† Šˆ^‰[2øßþh!šoMâHð;ž65ÿUÔË7²>3ôP)HŠ¯Ðü!¼JàDû
…¯¨ý”£Êš<sTÐq™l®r¾m˜Šš…èb¼Xaª zéÕ6NôÆ‘u¸Ql¾RÀdpZ�…¨”²âs<-ë�aÀ¹‘éÛ÷ïÞ�Múx>=î��Og~dvr:=~ÿÑ Óû�;¯Ú�ß¶¿sß`ÒýÕ|ËîÝ�­`ð±d7LäUá¦ìJ˜uåU2½æ5eÍª7šæ2rÎk®9ÒmÇ\œh—éöTK¹£K‘b†Z¹­0qÃôÃÉ‡©ú´ß&»�/&J˜÷V&ãÕURË_ä-¯Þ2L‡ãÄ&­º½,xÊîDÑAE"˜©^}±\è\Âq§ª&´u@cX±æ*8>üÑk�8ò�:ˆòÓ£\ÎÎŽÞ�ŸLßÍ.Ï¦³³“é9FÚ·-—ÖvTï:eh®Ã;ÃÓ³†å]&é ]Ò`÷Õ…‹¥©,
™¡_`„UŠ�´)HJSÀUÈÝlúËôç³£ÓK¤yI^‹N{yJü!ÙË���P—%¦ªÛº £.=CºZßbœkj„PµH}NÖ:k+·ÉA9h]�YI[Q†J*"±Ê¹Îñµ¼æ¥–° £š2\IÌÓ«Æ$ç_5x0�§\èÚ¬ÓSÔž?6öŠ[dah�YM³Ìµå­9KD_àcŽHc¾š·¥�Jb<0•²ÂtÆ,H1…_è…¥4dÝª_OO
”•¥ôÙG5]`ìš²·†„ÆŽüNÃ¶4g¨1Ì³´L-©þ~]FV•¸!¡H•e®«(â‡·/thnZ4ÓVØ£ˆDÎ»ëHYÈÀ	áZt rÌà/^À[¤€óLCÇ¢!lµÊEj|ÅÀê¦ÒßÏ«ªIkYEcÍ="Û¥P‰g;ª*Ä[D3¢CÎy]b~YI’Ø1–e‚Ž`ùGäÓX—Æ–6‘íž“lEÚuZ¢“œÊì°¦±¶‚9”>g¢h½‰ÜSCh¥tá2Û®äF¿‹]Ãìör ³QÜ¡ðè”ª0¹¥^<	¬‘Ò±ÎÑØ�N«@ýW­µ´¤¡eô_ÂîþÒª.Éy¹¨—††MñWá®†¦( ƒtRº8ùD?¯Qfäjì}©ƒÞBQç\
Þ3kv83ÑÞ	umœcTœ!Äc¥ÔðYÏ©6Œcl@µW‰é‚lYYRÌüÖˆŠëÌ¶Kèd~·Dp£ÓFíú¬MëljUaÀ©ý‚|[„Òå=“ÎnWóÂ~gÞäù¶Ö‹QhzŸæ¸¤âÔ ›¤žopÏÞï!#Œ›v-[ÚTUM5ÙàÈ•nC�'‰†>ÌÔ}™nÂ‘¯g‡‘ö-³s"í]Fø6�Å>e¹Œ¥ãÖu]³ÃØ9¨ÖÏ|oºâb*£á ]›~1Œz¡·,µŠÁÉ »¨GáÓ†µšÿA.êo!ž4j>Ê2‡ÓÈQV6<hDÈ‹>˜,Ó´YéöYwöX°�#KÉ0b�u ì–a·ZH°ñèÂ®t¦ˆZ	ãŽãØ¯:Û×%2VFÑeŒÇPŸEò+!lvƒQ€H[ÒlÑ)_7?ÑçNq0±÷üá´³.ÔçqÀý=²4\ìð?Ykû¬gtg±t3ÂÚý.ÏÎ¨[ÏžÿÁ¯%Wè3<ÙDD“ ÁƒèŸÙËñDŒuÉ¦?Ì{‘^×2çèQ¢|¢jtõ¢©]X:ü©æÜáž×ƒÊ²ö®›‚¹ìzÉg
4Ãìó1kX¢c‰ Þ³ƒý4¤—nòÒ®=|î˜?øâÚbsC¾ŠLpÄ.ÕÄCÅäÖ]}¸ôt;]±íe€Åþ€¹Zñ“²6–»ØýÃîN‡M«¨[V•Ñç£¯¹g³•�4Cµíùƒ=níÎÕjé°`ýÛy\¨µu‡“P4DŽ,½?¢V¬L»1ìtTÙýŸZ†[Ã˜�É±ÉXQçØ›[ßÞY|Š•Î'…À”‚0]²:,ÝTæLCC°AçÖ¢Á²ïÜ³®ÇÓX!3�z�ú„€‡j~ÐUù+Î>r ÞjxÐ2asÈ :C\�Öá¬ˆáM3ŸóŠ�Y±bcÉ€Ò;VpKHVb�]NnÖ—81\>£èà\}!­;1w�?âÉ"�g®I}Ã³ÕRÖòÙxnðZ.¦»V„x£!PstR%„˜›Îä¹1–É^7>Ç~œ$u ¡%�ÂÑ tÞ>Èy�M¸2÷{þ”}?gS¡ï–€®:QQ‘›x}°ù¡MvåË—ÎéC$ðH7ñºßë�Ï‡‘QŸ)¶ÃÔC*±OHû”l*Â_^±±þXj]zÿYïµOzÅA÷-ãÂ©þüþ;ŒhîØºËhHkåê?§DŽNì™µçÉtó˜’tºá¸l '×êüö üâÄ2å\ƒ>ža"¬²×ÖË‚GžÃýeÇ­¥rÑUâ§Èöº6?½Åôt’Å¡Íb§«NÎ3ô:Ô�‘¤	¢UŸ§"ãm±�˜€`X©Gš„M\zu1¼Ž° xÅ`aÎžuc{.EÖeÐ*$Ô>øe{îLè¢B^5ŠÙö±(�lnôäFVë+´À~»ÜÇÄ“Ú„â@]tý	 ²@žJFðÍ7ƒÇKi`±Ï~§YN±Î$…(£Ý��ê¤áÅ—Ò½Ðä£¦õÚç(ûÊeK#z4mŸ	£çöˆõäùÃF‘ÖãÏÝ~ Ôm�ˆ{ã_€>ôçeïN¬–!Bò›Ç½¬‡Àµáñ£U—fN_òS YåØ²Ü	¼€ô&!¿  ³['}¸Ïäå@™å›Öó%=ü@©/p�~¢7&ry@8jïR4-ìJÞð­–¸ŒáÓ™ˆ7‡¼fé¸½Æ¾7Ó'Ç§‘%]ÜèOãfEÏ”¨|œ»ßS(·µ·›sQ©ÚÒ·Øôrœ2w×sÃí"„,&4•¾ñOÛçÍÑÏe¹P†]òC&°‘·YBíŠ*-µ2êZ¬VˆÜ‡`°›-2óâ»lˆôs)¯1ÝõØ©Ó›ðovJüCëd�îbÈ0[éúK2öuúz´Ë¼v¿Äˆú™ÚÞ×—.½ßn|Æ¢–%2YÇ—]m;Ùæ'½”h™¾˜™ýøXÚ³ó/_†I®‹¾Á_C�XUôÔÐt‹¹ŽÉŽj=¼Çv` «F(í¥6Â¨#6ÔL÷—dÙ=\âÇ¨ÒítÓW!
~Yklì×û1]ƒKþ‰Lk^o+ÝyŒâáÁä8ýƒilXb¼T{›”ªg:Ýg�$Ÿ.´_,µúm�M‚oL¨oŠrÊ„Ü4·éº: De{Ø’Óí�9ÝVÒÖFŒúÑÕûÔ�‹°™¥4�õ¢˜mšàMzV±žO]¡¹¯÷IÂ¾¡}ººhK:‰n¢QšË
E}Bw³»+Áˆýƒ8å«‘Š=�ž?bø~ V¾ W6 LµC+ºŠ�…,�¨åO�vbøÛ˜î©áxÎ¾ã Êé•ˆxPî�¬»»{’?\6A—�¼“åvûN�Zò�&þÒ‡|ƒdÝàOÆé¹²Àã1ˆ„æhMa
:ý6	P’UO›'6D¿Ÿf ?e/�×þò	<35Í;¦ˆš©k]¼Z4â®�LÛ{;&‡_æW‰n�!KoK¥B¤úá—�½´�ˆ†Rø ÷r_5ú×^üç÷›@1¼G¥»›iJó eÈð1$Ÿ~<"z-°èÝjF4½go0Ú÷›M�9¶v(èíöQwÝþäyD¨€<ûwkvü È‚~äh|æÑçíÎó½{HÒÔÂ—rz@ú?&‘Ÿë¥œ.xœ…XMl\WV’¶$“â»ÿÍµãø½±ÇÏ“&¡ÅãIë˜i1ªpJ•Zö�7wf~óÞô½;žg4,`A+�èe$T¶HQÙ±)]!,Ù´ˆVAB©¬X°€sî}3c`¤h<÷ž{~¿ó�{óÎ7ÞÞš´M×ãdëë-Öb¤ê¹¢5ÞÄ_Z!õøð”øýiC<sæ¦øä©Ÿ¿»úÌé§M×ñ¹øÑôÈ5R$7)ù›¬¯|soóþNi{o»´³½Vº›7òQ97×U[°wwsgouscõþövicõu”)¤R–Ã™W¥&#w]^r¸wDŽS„XN…=\"N«Qf^¸{Àœ%âsÏrj)ásê˜l‰ì0›Õ<ZÅUéüR,Ü&åìU«aqV¹ïpËŽ”’ÅE’/—K¡[I¥:)‘;¿¾úó`„ì‚¯„Èh¼–É]OÏJGƒ5ÂÅü…ý.:éÚÔuépN’%Å;DWâAl¹à‡‹vÂ˜ÖŽâRª²¡P"J¨0õcôÁly^—9æÑÒ‰ÙîDç’W[�lSØÁ@çæÈ†Ìq«¤ìrœ#¼ÎHÓum2·25Æ‰o}›éÙ0§Ý %ã-Å-ßÀ³†Íœ¯'µ£Ü¹!š\ê9Ò¶x]þ¨²6ƒ¤6™S�rNý6('–ïh}õ’š\ìÊÃþ�Õ$>ã†ÜAO›žu'`ÍAau”YÃ:0ÿÅ%pƒ/«î@,AX¶ÄdU‰Þ)‹$ŸãuvA\â„’S†ÕåW¡Gj+´HÖœªåXü(PS…¸t…°²xc:ÝE7’Îuêëà’¡€–E(pË‘à'¡ß¸ß_vr‡|–Çmë=Ç‚sÊh3ru¼ÙêÞŒpq>XÄ5BÒd(³œŒ/lüô†ˆz¶•Õp­ƒßâï™³çƒt£ˆÊ^ÿøüDÞu%~“™VÕ2%˜&]a€¼æÑ¦ÀËj0·ÅÅ?¿�Y…Ò¬˜Ür�å�;º4ãŠ©¶ Rb‰èƒŒ“ƒ¶ß¼”Ðl{Z>E== ~642¥4UR:r2uac‹‡ò ±;Ê7=%F/×Åw/O]‰º6øc7[�‘Z×o•¡”PK 	z›QŸ/Ø.­ÀèÖ|EY«†ý¥r¡óÉ­ç¾ô„EÔ;€cÉ®"Ô©Ès­&*ÅSžÅ|âJxZªßOÝñmÆ ·ePeRì
)î^ýEÀýÜl4Ae Yl à®DÏéÙ¸¸Gß‚Ú­¨c(ž@	„½Ù‚q¡¬Ûu›äÉw~.b¦P74íC=×‡/h?lß¸µÐd %E?–õE›óó1�ƒÞtñ¸¤‡˜cºý‘g£æÂ™‚CÉ†€è6\B©eÓ²Íébx`A£ó=rÕZd%Ò„ÚaµÖc\v(4}>iSZ]%Žë,ô(—ÖÛJŠuWp¨‚kð'úÔ¦€œY( VM>û‚†Zúœgë´åƒ¶‘(ÁV¿ÝMü{2ýâí|>l²NyxÊ©¸˜®sp‡Èç³…„³ý	5L€½§‹©ÌT·/ é@OD‘ÛXx9'êpûAš1a¤UÜ¶YREÕ6@ž¯ÛPaéaÃrtÃ0âa€ó¾«—å”/í£G«gá(#èÑº©¦uŒ:7h|!A×�«Û=©±Ýn­Æ<£M=G×NŒ?'MA	´9Ív:¨LŽ,”®¶“›'g>–ŸjCs�ßâ´RIL©BJ<ŸžëÆÓ·©ä˜D—YìCèšã@DÔ�ì T«Âd=¥ªÄÁ¨Á-<¾-ÏÈ~‹·§'gã½dW«Ox…è~š¾'>Mo‰[£%ñ»Ñófm›ù-›^¥M‚"5�ŸSEÍ‰Æ:ó}ZcêL0i’HDn`Q<�¿ÝSc»Sj©Ó/3ŠøáØ�3¼¶->Ï�§Ei¼ÿ.‹Œ¿ ~3~5Í—�†rfÏª(mbkfXÐ™¬xwæµá˜2Ç+U Ûu±yåVÔ_H.‰=ÊÍzÞ‚ªÁ€‘M‡ÓE2d/yÝsÛHÜ®ätYM,™*.Ì™ŠU­9’^?Æ99|­ÛgÿÁ�Ý�EUD‰ìÇbo'ù®øßê°]ÙZ–Suõ}¨-¹ö¥™ãË�^’Á fŽ•?—H�èf&ÅãLZ|�ùâÆd˜ç¹êEbÒV­i‰¡ÞçXÔkÉÂ(4‹åÙWÄ&æÓ1¸1?{1�Š÷g5ñdvX‡4ce®%#ÊESœè3Ç�Ê—µ[ñä1©ÂØ &ÞÒrâ=íŠøËð%q53"þ¬�®žèYŒ8lâfFˆ4=·Ik�H8)>»>²Ù£C�ÇKC	uèû¥p°ôqBpAˆh_tÇ®Š�ÆJÏúðrð¹V	P¼9-Ì™ë"3QŸÍœúo=ðÑàÞÄ|2©*–m×<€×+óÁEêã]•V?‚¨Ô@’ÙQoœ^mx1§ hÚc]„EÃdcš´]O^žË-®5ø…‰rÔKQÖ®W�$Iù ‚bîej�&¢3£fädu0„À#yiöÞ6=Ìò¬�¿¨I`-(šÈøp'à.»Z�cÍjª#d/üzrBüqø¢02¯ƒk;Q-¥i"1­ÃÝð�Ù+?8àKx­�:`àþ(^š]X�Ÿ‡ô±6.^Êœçô¡K¨¹fâ}WuÈ·ôqafÞé*ðÙp»–àÃi#Q{Y]‰€Å ˆ0}+²=‚"É›‚¥Öû´~oº.§6aÀq0,Àpð†Ç'|â¾<¾Ãw)_�¥ã÷÷ÿ~~Ã�ð?Ê/ñÉTë¾0¿w!ùLùÕ_§_=uVÄWgÞ«×"¶ôsâ_ÚEñüì¹>¯ýI|MÏ@º~yÔümNû'‚ÑCç�>xœëxà¿áËfYVF�äü¼´Ìt½¤ü’�üìÔ¼âÉ•l³d¡‚%©9©éE‰¹Nù%ÎùyÉ¥EE©yÉ•›ÕØc¥(™¦XÊ â'˜ìŠ­cxœÛu p‚éÄèœ�w¿1²–T¤*Lv`ÑQÈM¬LJuÎÏ-(J-.vÎ(ÍËž|�I†#*2ù8“ÓæƒÌÕL›Ù4Ù6»
|’ �„ ëVxœ;¸3`Â,¾j…¤ÒÌœ¿Ôr·ÌœÔ‰¹Ê,ºi‰É“óE7.d”RáR d5ÕPA…‚Ò¤œÌäÉ9¢“�I©L.•œÌ(­:YEZwòQÑÉ)ÒŠ“›¥•&/âÃÒZ“_éYåÉÜ2j“õe”'óÉJ)èÀ+6öN­´RÈÌ+(-Ñs@r“×Èj©ZMˆÂÍ×e_r –v7Úà€#xœÛ°×wC*³]fnA~Q‰BµBbqpbZªsFi^vpfUªB­BZQ~®‚ºžž>g$¥¦è—–dæë—%æd¦$–dæçmNbþÄ¶Y�GC ´ÓÊªxœ31 …¤ü†õB¬ZŒuÕ�Îå?ë×äûúûÎ É&`ÙŒ’’†MæÖ¿§È{ïŽdH;ßÈ²*"]lÌÀ·Áží@ÀåËgòšåÔJÜŽ)n�| ~2 £¬xœ31 …äü¼’¢üœœÔ¢b†Õ�žæZÊ$½ÿq�‰¶Or®ýëœ8°ªÜÌ””œÔòÄ¢T†u·C¸þl¹ëà›yD~nû£šƒí ŠŠòKKR‹Ê³«�–¬Xø$'Ò¤*­£Ý—¥�Q~-åçÖxœ7 Èÿìì�Ë(ô•oÍ6yQWKwhJÛ>.$Î‘ß­¨ª¡‹¦=Œk(h9›yÜž“a’�~ãFxœC ¼ÿìì�¦9u·ö›®2˜á ö‘µl°ÆU}”:100644 home.html øèv)©jN«æÛü‘\Éö6—Å‘ß�L²!6îQxœ áÿìì�¦ÚúÝŽtfVØ!»k×»¥QIÈTß‘º²ö`�î,xœ{Ãô†iÂi‘Þû–0láÉLŸ#?AòáÉê4ÿ¦‰÷{Þð�à™‰½Jxœ­VmL[UNKaPÛJŒõð1ûAm·4F-:X3†à²ÖéˆšÜ¶§p¥ímzoÅ²TNLÔlÈëœ!$,~Œ,&äÄMc†®ì‡ZW?¦K–eq[¢&Kð�ñÇ¢žs[…{E£I›¶ç¼ïó¾çyŸûœÞ8[DÎMøF¤d£ÃèQ>Œ'ÊŸ�Q‰—’(…Bq!‚Ì‡3÷

Ž�:1Ûæ±èÑXs«ž�Ä„¸D1ü	>ìÅC2Ö?Ê¾7TÂ$!ž„�öÐkžÆÑçW êÅbé Ž	"Ïít-0�ˆâ —þæú1[aX…1‘DXâc\|E*d‹6ÃwÚJm�¢½:Ý#°_÷œÐyág��ô5ä£WCÍê¢Ž0/Jk+\{w+1X¾(2¬‰ñÑ qÔB©ý¼qÍn Ž9	“¬}V£œJŽ8^Õª¢¦áò.ã¶ÂC:B"*y’9~ßuimÙ c	CÖn›4•“�®÷TªÓ_“éû¶éT¶/ÄÒÐwÈPUP_n{Æ}@_Q0YîÐoÈiÈš§^€�ér¸ú¸Ó¨0Æ,T]4û~é¿­3î[¤¯ï™jN/Än–)fYÈWÃ/•TV5~ptQë¡Úá†8^B«é–Gh¡Û¨@è–ÃòB±„?Ìº‚vhðaîh#Üã­ aRßµÀÖLx2[`i¡Èè+póåx`¬ÆKRV¦Xrl¼­X…‚o_»®@þù×Ñý_ývLšòý:aš¾=\›Dpgr˜`xÀýô³iÌ3ÐŸÀö-‰®nœt£ %ú…�LãÍÚ”Žg«‰ð¦SMÓt¦ÛÛ…�ÔÁdKR'÷ªg¿Ë§ZÈÄ”^[£ ©“u$=}Lía¥ dùÔšÍ« Ø³#sÝä·wÌšM.ÆXó	�Ñe*jj#·Ï8À:û~;òpÔ;…â¢ÈëB,a‚ÿs8 9�Í©—ýÕëú+=,ûÚ NBK¨�Ç×ïF¢ç£ý­t=Ì‰R�äC<ºQ![�ðì£@w#S­z½ÓfÓ#êáb"ârŽsÖ-	kz‚öÕp«Ç8"Û²4€Qüî™L{K‰xTD{VŸfˆ—XÃv$òÃØŽXÏv¹SÉ·j§õ‚¨§«§±3@§> DYH!ƒ�9,Œu÷ŠëÆê.di7²äyrËqÈZCmmÈl¶ë!´ÏT’ãžõ×iéÊO¾î2ùAbdÁ„ÏÀdXåBä×ÙÕ|/'¤ÓÄvzŸ¢ÈÈÜÛJ ·Ï¼¨®«4Ü?ß[«ävù{`jž+®SÚg ždìþ¦žØÎýPR£ÄGELLOèþ‚Œ§G4jŽKª.-†«ŸM=øßLãÜçÍyÓ(‡'3• ÉÔæ�ÎÃ™�ðåh}™$H\ØKuš±=°a©öŽmb¶x©É‘³>+óŸ6¦H›‰±›Œ\_Z\Ç z)«Èš<NoöJÑ:Äª88ÎOÿ�XàÎ¼	ÐÇ¥ô6…�TY‘ËÚuàÒðábE½úÑØ4f¯,hþ  7¦œçˆxœÛ¼�ùÿæ¾Lþ\

É9©‰y¥!©¹n™9©:@!W7ÇPŸ�x7O×ø�È W�`j^qiQªkE	�‘™ŸJO-q-*Ê/òM-.NLj�ÜÀ¤ÇS\R”š˜’2qr=“<w	�¡Ý¼ˆiy£ýdÏT¯É•;ø'oOK©WH,VJMLILÊI«rIMK,Í)	¦Ù„fæ•X8%VÚY--J-)-ÊS@¶E£¬RG¡Z!9?· ´$Õ7ÅÔJ¡¤¨4UG¡ (5-³ÂJA]¿$·@?èÅ”¢| {ŒuÕj5­¹&›î™l¿IhóåìfQA¯oNh¬bÆž0Ic3OëR7,R	�ª›œæ ª;~âb�$xœ­SÏOQŽLÜP/E$$š1QÒ6¥@m�PP5A~„Çî+]Ùî–}oc	þ	�w0^ôf<xÐpðäÁÄ£ÿƒÿ€1ñìAgv»ËRÁÄè¥}ûfÞ7ß|óÍÏ7mß^µ½�.öLºŽiIËu˜Óœ™Üøo7¸§
Å´r‹çÝ…¡>Ã­7˜´Öm×ª®CÃ0çJ˜uM«jq³_ƒ\^30.Áqet}'z3
bkÆ,��žålèxÁ%Ûˆ¿Õ»—é3øË;»)f3ã”à“\×ÔsíºzºÚž’I_”ˆÞV–{Ï·–»:êV�ßÝnpBQ/Øåa=æÛ’‹,f¤ÆoªöØÛÍR|7[Ö´|.‡üáýaÞãF$L1Ë>Þv#£ÉîµwJB¨×C©ÔQÆ˜êãµ.•^êQŸÖ¦ö(Ÿø•�ztLPž¬qcS€‘f­9ÌÌLµo–I£¦žæ\‡'>#aû,Ç ¡rt{Ï©7ï÷Â@˜cbaé{Ž F=6˜ÇMˆ‰[U�5~D€�,A �B
Ó\s|Û^é‚Íeðûñy œkðPKz=NåêT¯Ø,ˆ|Tã29šDµ-%0*-ÇçÉù$UÂõ‘éq8jÿaïÕ*Ž¬;�Ô‘ƒ¢‰•éVl§çƒ0¥N3ž •²Àð8zÕ¬ÈÐ´K$Õ×¹Gi»4Gš±‚ý+�qs!ºvÔ�Åg0)Ÿ‡h�êàÁfát›ÓC›dƒ"½W_»Õû%¶üûZfBKk¨]õÇ»~Æ›£ÛÇ|HO­Ž}ÿï¨DÜÅý1Pûã_*Ñâ}há@ÝÃlñCûYDd+…1‚ø¶D3œb“Ls­BÚºb#G¥p2-XÙ¦—š¥[¢åÃüÄçKM#>ùTn·P9¯…Î	åÃY6èÿW2æ¦xœ340031QH,-ÉÐ+)fPœ}ÃÞT¯"ÃùU¯šdÆÝèi#Cˆ’ŒÔÄ”Ô¢b�ª½Á»ŽÎ7‹±ÈbXŸØXôRs9ÿi¨ªü¤¬ÔäÝâ’¢ÔÄ\�Ú\¾ÂoÌv{ì´ß¼ÿ×¾ÌuŸ j‹óÒSAj¬äÕ½~²IÎïc½,lxizÚÌ<y¨š²Ì¢’ÒÄÝŒüâ�RŸoÇS�q×wâ²ÿò\@V¦2©ª´"7¤â¬jTqÅ“N�&ÃEÂ*+uî\XÞ ¨TÄë€Sxœ{ÆøŒQÙÐÀÀÌÄD!±´$C¯¤˜¡ˆÛî¶p™8_Ÿ~œm¸ß:ÿí»&* 'ƒ>æ8xœ6 Éÿææ�ajEäLÅ”™}Ým,Hmô�qí�‘u;¨@àßR¾àåÅ9Ï‰ÕáOM
éÉƒ‘Ä"×æ‡‚ xœk1Ü­ºA‡wòÁ5“o	sä$æå¥¦(ä§)@™‰E%Åš
Õ\
p�œŸW\¢P\R”š˜«`«�Xž˜Y¢�–Z’œRì”ŸR©Õ¬i½ù¯p; Gh#õåâ'xœ;ÌÕÉ5AÚ†=/'¿ú’!K©Cæ¿¦É¶µZWƒÌLLKK2ôJR‹KôJŠ*žÆKï~b¢®÷�ëÊ/³•fÙVØ&†»ZO«ýîËQa¨d×•³e÷jÁßüé»¡ú“òKàÚ¯_Ì]¯Â÷2Ãl?siÀ»—:­Â'^Ÿ+r%»þÚÜãš=;$Äùö-b)ÏÐ^?¹ƒÑ6á¿\
sñ:•¢¾ Ãöýæ¿íâ¸æ57#51ÉeÌ¿ÜûÙkÛ›—[Þõö¶‰é7  *Ì)H-*fP}»Î¦BTøbçäoóÊg•Ä+í¯èœ¬Ê¤?¹�I\¤V^V¤ûÔô‡ÖEØßPI`ŠÜ_`6y'“šÈñ4>ÿÿ§Ç¼‘x²œa¡•ÉÝ¹¢~“?3UˆmP–½qp¥žCÖc=Ïí3/œX$½¹ž9ŽQDÇÌû÷.«ï�%'Lãý¶Î`½p‡þä�,â"fwU£ï±?;Èrôl�‚å®O\s&Ë°ª àe¢%ã‚xœëäêäš mÃõú(Ç?9eiÅ“W;½Ÿ,¹ZqÐÐÀÀÌÄD!±´$C¯$µ¸D¯¤˜aŽÓÂg?öVyüß=édu›ìÆçÃ]­¯ßâ9µGF‘g¢ÿ}#YÅÈÍ¡·Ø¡ú“òKàÚŸ¿ÔÝÒ°£A.ýuý^ÙV‰dŽ_›'^Ÿ+raÿþ‡¦M!g$w:NéÙ”ó8uÍäFÛ„©›î‹Þv	ªzx…™aa[·“‚ùòP¨¹©‰9H.“ôjð¹½ueˆdE0ë§-/k—Oñò41  Âœ‚Ô¢b†¦·‚>³flû±ÝîõþUn%Kš$6MVerñ[ç U°»¤>ýá:Õ�W6<X ²kr=“šˆuTx˜Þ×ƒ¼/ïËLy#|ûðûÒ“w2Uˆ”º±ì¢©�¨yÒÖÓYÚ«"sßnveŽc´¼ùïÛÞš>¦iAµ¹Wøî¶·˜ïupyj’nbA&ÜÅß£ÓØ+…&t~{ËvËâÏî©ÜË&?bQ 2â¯ØïÔ4xœkgkg›Ð(r3GüHLÎþŸ¿HïÜp0;£~š�õÆ©EL ì £xœ340031QHI-HÍKILÊ/ÑM,-É×ÍM-JOÕ«ÌÍaøÛåmàmº÷÷�ðÓ“S-•s¦”Íøgˆ®	¬Ra�Ùò×¿ô§M½>aáú>øþÎR31 …òü¢ì´œüòb…Ø\ñŸkš;gÚ+Éjþ<ãÈ\
 LK4Œ° xœ]PMk1¼çW*xÊþ€µ•VJUDO¥Hv}ê–Ý—m>”"þ÷Æ¬]!LfæeÞ°ª(Å–jâ­Ê´“Ê;-+2{Bs*€Ú—åÆÐ�'ë®ÈŒâü@¶A€D¥
î ëÈÑÇS;/íX,-N…; VÖ¼ÇóTÔdª" Íqj®Ù;›âd
G·ò–¢}ß:‹úûM&ãÙÊ>óì¼,•¾Hõ1çò;m2E¶Ø¥Ø‡l>KTî‚f4Âð>ý3œ¯aT†ëN‹¼/|#b”02,kÐ4!cÇáj`o°Xnfë�ñdÙkMÄÇ´@«H18ŸÿÒ1T”°¯22¸\:ú×éêm=Þ¬æï“Yc±”r6é2WÏmœœGï×xœkgkg›Ð(âumÑ“]ÉG¶é:&óu©›ï2Ö¬Û8µˆ	 ÓÍâî‚Kxœkflfœà-R<#õÞïCwrŽxÅ]Þ·»(àýžÉãU á¨KïØVxœkgkg›Ð(Â3Ãz³Æ;ŽW·Í²1XvÎ®¦ëÛÆ©EL Ï…·®xœ340031QHI-HÍKILÊ/Ñ«ÌÍa(ž‘zï÷¡;9G¼â.ïÛ]ðþ	Ïd P(Ï/ÊNËÉ//f0PˆÍÿ¹¦¹s¦½’Œ æÏ3ŽÌ¥ ãI×á€xœkgkgÛÐÄ(²1¿Øýý'«%Êæj…õ&opjìÜ<�±� á`>í—ð3xœÛ·ŠùÉ¦	wYMõôL6>Rbd4�¸@b²,“+«PÈxs:S„×äÂ¥›&§û.Ù¼'^Q¨ÔPÏpòÃ«°2mo×âÐ2£‚bs“€Ä`ï2ƒt��ŠçtßÏ4'×¤ÄÂD³r3Ï`Owgó¨â2??Ë|/‹“äÜ4ƒ<Óü�ªÀ*£`ŸÜ�Œ¤ÀðŒ¬}‹TïôÍ…µ}’õ`Û•t”€¸ZA))3OÉ
Ä()N2@|}S$RœZT–Z„†òjj�òÅ‰¦†Fº•F!e¥ÁQ.•�‰ÙÙnAQÞn¹ÚÞÞa…ž™Å)é–úÎQå¹–i%eÞ>~AáYÆúþ.Y~IE&ÆÁÅù…~¥Ž™Å®i†î)Î…áÉ)Nå“ƒgkM¶Þ_½ùûì`ÎÍÊËžHL^wDtóéýÅ¢ ‚xà�}xœkgkgÛ�Î$ÂóL¡.åÒõµžÛüXÃ÷,5uºökr5S Ópðåñ,xœû/ú_tÃ'.^3==c%.…Z®Z. W€Qà‚KxœkgkgÛ�Î$²•›§-÷Ð?±ð–ß˜m{Y·_™\ÍÔ Äç3íñzxœû/ú_tÃ.fS=‹É‹¹üyÍôôŒ•¸j¹j¹ •=,àƒ!xœkgkgÛ�Î$“~Bsÿ‚”öÛjï¦Lô?}£ÕÒar5S Û‰úàòPxœû/ú_tÃ"NVS==“ÍË9½yÍ€Lc%.…Z®Z. ­	©àƒyxœkgkgÛ�Î$rýÊ‹-=SßFM‰ÖX½Àvo|’æäj¦ ñ ¬éó(xœû/ú_tÃ"NVS==“ÉË92›êYL^Ì }±ôà„LxœkgkgÛ�Î$²ÞqãÆÓò›ÎýIz´8îàzï´îý“«™z ïH}ëŽ½7xœ{$ß"6AƒÙ@Ï`£v
ëä<nßÉ^¼1“?pTó–¤æ¤¦%æ–¦–¦N>Ç)±¹›s:#P‰×äé¼ïXMõôL&wñýd4�À'2y¿-¯PÌX‰KA¡–«– ©äZêhxœkû/:AƒÑp£fGR~‰nA~~Îæl., ziøï€:xœkgkg›Ð(2Õ·× øIËMÅáÞj•óçØlœZÄ »sõ¤xœ31 …òü¢ì´œüòb…Ø\ñŸkš;gÚ+Éjþ<ãÈ\
 ÚÙ³ï�xœkgkg›Ð(’d~œ!?c‰¿Õ×nÅßý¹É¿Ã{ãÔ"& ÒYu¤xœ31 …òü¢ì´œüòb‹£÷ú1i	?z ØQ›•áÆ¿4 è=‰«xœ340031QHI-ÈÉ¯Ô«ÌÍaP¶1ºæ§Ç¼’ßÎ›'ähÅ>�)I†Ui9‰Ù©¥Iº¥I9™ÅºEù99™yéz•‰@�&/ó
n·ö}¹ü÷þ¬™ÅÚG—öß‚jÌÍ,*Ê/ÒMÏ,IM[’™´?p¹šç±’¥'i{Þ{•¿ü= Üx8egødxœë—Ú!²Á‚ 
þhïƒ	xœkgkg›Ð(ÒzéhQÁÌgW²¤šv‡þRÉxûcãÔ"& áF»¤xœ31 …òü¢ì´œüòbU&>UžÄ¥ù‘,¯ºæ—\PýÞ8 Å9Í«xœ340031QHI-ÈÉ¯Ô«ÌÍa¸)Pv-ñÌæ¸•�ƒ&Ú<>öÑzV–­!DUZNbvjFi’nAiRNfq†nQ~NNf^º^e"P£ÉË¼‚Û­}ßB.ÿ½?kf±öÑ¥ý· s3‹Šò‹tÓ3KRÁ–d&í\®æy¬¤EéIÚž÷^å/Ï ®­<yàú[xœë—Ú.²a3sIQéæí,�¬ 6ª.ï…
xœkgkg›Ð(²½ôêÒóIO?Þî>ßëé·DXŽY·yãÔ"& ò ˆ¤xœ31 …òü¢ì´œüòbf¶¼|å³^Ý‰¯_à°í_�Å­er àz@«xœ340031QHI-ÈÉ¯Ô«ÌÍa˜« &Èy°©qÒZæçÏ¸_“}Æ»Ô¢*-'1;5£4I· 4)'³8C·(?''3/]¯2¨Ñäe^ÁíÖ¾o!—ÿÞŸ5³XûèÒþ[P�¹™EEùEºé™%©‰`K2“ö.Wó<VÒ¢ô$mÏ{¯ò—¿ç  ï:àoü\xœë—ú%<aõÆ½˜7ïd©g =¥'ï‡
xœkgkg›Ð(–ª³aavh@¡ã§sÆ"×8÷ÿÛ`³qj È¤ƒ¤xœ31 …òü¢ì´œüòb†ÒÆiÆòY«ã9½SÊÙèZTf å|o«xœ340031QHI-ÈÉ¯Ô«ÌÍa�1Ùê¼�=ã‘çÍkIw-ß3¦BT¥å$f§f”&é”&ådgèåçädæ¥ëU&5š¼Ì+¸ÝÚ÷-äòßû³fk]Úª17³¨(¿H7=³$5lIfÒþÀåjžÇJZ”ž¤íyïUþò÷ ˆí9âá ˜Ûxœe‘¿NÃ0‡•þa€ 1™19*–.}Ä„Ä†d·×Äª[ö™Œx*<cÄ»a
qãí÷ùÓÝÙ·»|9}ß}=N,Î}vvëï³�ÉböOã=Z´!œ’ßc\='ÜÕ$*„Rm@*¶"yAQ�Ô`la�¦P?çh;µ0¡è�Ø
ÌcüÃk!!%0‰Õ?k#ì*õ-	¥aÛ”:‡v°)Zñ4s…Ç@¡EÃtÀm�Û†•%˜Tf®?î�P£7¶3:´TáÓ
U}¾pË ]ªz-ÊôºN™ƒÇP­”<Pÿ6ºštýÙo³¯Æ7ÙþsÒN¿°$»-§0xœ31 ½äœÄÒ”T†å“7yùËP[òª�Y¤«ø™×ß×†f&&
z)ùÉÙ©E™éyùE©Œs§æ~²u
øoãe*ty•á_;˜ÂÔ¼2½ÔŠÄÜ‚œT†|Ã�þÇÏª”v[?ÿYÉÞ“½¦ÔbezfIFiCÕ�ÃÍ\å{ôïíø3Q½uW�R�Ë#˜I@%Pûâv–ÝŸ½v?ï¶@ßã?–°Ïc¿5'£´8»’Á6ÚÌò þ�¹Š-vïåkØÙÏzjŒ³�c¨‹«^n
ƒ¡ù>}ÿìØ¤/"'z¦~eþjþÂªÈì»´L ›]v¦IÞ1�öŸÛìdOZÉ¡´&PUA®Ž.¾`£Ò?ªÞ“Y^ýB�ðËÞ¬èßÎMG¾@%eæç¦êeçç1¼šý¤Oär…I“x²ÑÃÝ¹!}÷òÂT•æéå -eØ˜_ìþþ“Õesµ‡Âz“78µvÕ˜›š*¤¤ääWêg0üw~±>ZéÉãÝ›Ö/l»¿sb`ñúz¨A�xÑMÎÏ-È/NÕ«ÌÍaèÒ°8ÛV›bªqîŒ¾JÇÃ··wAB¨¸˜!Ø¯°#JÂR%üØ~Íugrvå³­�––“˜�
qWbµ¾Y¶÷ßîªÕ–óõ:Úºw;ÌFQ•—YÁ`w4í:÷Lë
·tÆ�ýg>Ö´½¹¢ UT�˜œ�˜ˆõŽ7ž–ßtÖèOÒ£Åq×{§uï‡ª+NÎHÍMÔ+.Ìa`OÏ~%´ÙÞÄ»nÂ±êþª#gú¡±\\”Ì°áL4×Ï€ï›§5ï[ýõjÒ»jˆlIjq	C«ð¢·¢]sV'ª[úè:ç"ûÀ%P+JŠ“óóÒ2Ó!nYðlmÌ]	}¹…{¦°	ìœ´ƒ]¤¯ Î5¢å„(xœå ÿ‡‡�_ œ�d…0�q7¼@Òœj#‘s 9×óp6ƒmfÿa_nQðy%Æ100644 README.md `OÍe ÅÀ£ï»uq¾©]'A=‚“L[äÊs’»a&y¸ÒÔ*‹þò“»36)¼"ã#Ûk%¸uÿ›Â©«ww«40000 docs Üh�è$‰ýPá�0â,U2‹¡�“£3+acG£÷(>#\à¤5Õév¹¡°å40000 test 1ç¹¹„¦H¹›“	�…3³\Y_GÖ“Þ)ñK`Œà€txœkgkgÛð…Qd}‰Ýn=Ó#Â_vòÕNÏ±�Ÿ~f2Ó ×ÐŸ¤xœ31 …òü¢ì´œüòb†O3—gÏíÒ,åøwÎà¼îÔo&òf ý¯«xœ340031QHI-ÈÉ¯Ô«ÌÍa¸¼0ÓNä°ç»¾ªïû•ÿ¿¾°ûè}Cˆª´œÄìÔŒÒ$Ý‚Ò¤œÌâÝ¢üœœÌ¼t½ÊD F“—y·[û¾…\þ{ÖÌbí£KûoA5æfåé¦g–¤&‚-ÉLÚ¸\ÍóXI‹Ò“´=ï½Ê_þž �=ˆàŠxœk*°�‰q² ‹Øf3_v *žA¦xœ31 …âÒ‚Ô¢‚üòÔ¢b†fg/�3Yïb®ØLJé?.ÃüFq% sÕ xœ31 …‚œÄ¼b†2ï];å=½ïúPð©¯·¬¡;ó"°|qAjr1CK{|ïŸÄwBbýUO_Qÿ3 õ‘Ùà•þcxœP ¯ÿ‰‰�4ÌÑÊÏèîÄ5Ê d~Zh’³Ž‘H!)l=à�Ê¦­D¸0o%ÂF›Ç‘}rCÚ…ð…®Î6˜ŸðvPÐ3dÊK³À7#Ym“�rxœ[¼‚ñÌ2Æ	ú›Ùeƒ'F©m’Ý(xœÛ‰éÅ¦	¦›y�W  .®k�ÄTxœëÔß¤;A{3c» ¦j�°AxœÛiöÈd‚ÚÆ¿6R ÀlŽõExœk0]i¼áãæ¹L�Å#~UéŠlxœ9 Æÿ‡‡°Õ‰&ÇQ¼héåD)Ö“eôGJRI}ò“éá[¤1E&.o¬/Ï:†ZÀ�™“Þ)âÆAá˜¡fxœ;Ê¾‡u3c£ñä©ÌŒ\ 0`rà
•ýxœ›ó�±ù+ãóÍÁŒ%œ,%©9©“ÏréZ–§Y$—ç¥8ää''ædä—X™šéƒT¥%æÆ—ää'¦¤qø…ØpMþÈ!¼Ùž[†lÖæD¡XNFãÍû¥ýƒ�'óç÷Éùùó€¤]A'PâDA#XHnrxÑ Ë ø�ü[l”Ô/Ée 28Ül€ x óÿƒõ¶�³ë#•­å–“GxœûêóÅ“[YQ?)3O?)±8csãK \õPh–Ž,xœû.Ñ,¾ñK37 žÝ¦xœ31 …âÒ‚Ô¢‚üòÔ¢b†*þ¢©ªU¯:NG~Ž:xPìº½×ü, Í÷ xœ31 …‚œÄ¼bß·:n™õœõ�Þ—–•oå\cWØh–/.HM.fp|iÏêÝÂl,½x9ÿûÆMåMf Ëóâ•ºqxœûãüÐi‚åæ4F7F–’ÔäŒÍ™ÄX�'Ÿ`Áh¼y#G93«±��×ä{J“ýyV²�%hNväÕ�¬Ã{¨†�ï:3PÃ}AmÙ‚üâ’ô¢Ôb+Ã!'?91'È³2�¬*$Tî*ÔÁ¤ÎI(3²CÌRš\()7ù �Ð@ÙÉ~R�ÒÒ»�Ô™×Ìzf¦–†VŽŽn¡U9žNÙ%i)ñæå®YæU¾^ùIFÎneîºQî‘“ß)ès[Zš™šYššLVW4H,N-ÈH,ªLÌKÔÙ99UÑ¨>/5?/%)>¿</µÈ*¯ =Þ¨*ÓÃ7 <Ê9µÄÒ!µ@7'?/]7='³¤$µH71ß¸8+¿²T· ??'µH/Y×H/±@·8¿´$#5±¸D×P/±¼Xd(Ø
ý”ä¤üûââœÜü”TÛ²Ô¢Ì´JÝ´ÒœµäŒÄ¼¼T•œø¤Ì¼”Ì¼tÛ¢ÔÂÒÌ¢Tµä| xrI|Ifn*Ð\[CƒÉm õÆ�ðâ
‚x¢ ]ÿáBÑA°my.id³ƒG
my.id³Îömy.id`)“®$“_“ý³ ‰YOUR_TELEGRAM_BOT_TOKEN“×/
1234567890“ 0yourdomain.com“P 2)user:password@host/dbname?sslmode=require“%!<¿Á8ì”×xœkÞÁ¶{+Û“æÆí5ŒŒ±•ŒÆÓoU1¾d4Þ~¤šñƒ0„Û6Ýzc2PlþÆF ýc"£<ˆ–˜Ì¸Dï™ÂèÎ R™øã‘›?xœûhsÍj‚ÆfÆ�,,%©9©›³Fs@X1|ØÀ¬ÉJ¢º¦¥Å©EV‰ÅÅåùE)9ùÉ‰9ùÅ%V¦&ÆFú UéE‰¹ñ¥9ù‰)©E\þA!¶Æ›kE¿p  >î$Šà‘ƒQxœÛ.ó^rÂ›Í&L[™�7¿bMeRìwA”''+ Î†
}æ€€1xœ6 ÉÿÂÂ�z�ßÐ¤ ´lgœ�áÉ{fO‚‘Ž^`‘S>2¦íã’ËP•JÔè$“~ ’Bš�éÊ'xœ‹Ššà&"œ~]Å0ÁÙwÞ¥ß‚Á}çxoË…Š  {¹	Ïî´xœ{ÆøŒqB¢H–«ðŸ£SfÖÞÍåÖñÈý2¡ðíÜ‰¥… Òxaá²lxœëäêäÚp”Ñnê¦û¢·]‚ª^afXØÖí¤`¾<ÔÐÀÀÌÄD!#51§$C¯$µ¸D¯¤˜AÒ«ÁçöÖ•!’Á¬Ÿ¶¼¬]>ÅËs23S†ˆß:­‚Ý%õé×©~¼Z°áÁ‘]“ë™6‰è–,;1Óy–FÆŽ§—ü2Dþþªþÿx³+s£}àÍßöÖô1MZ¨Í½Âw·½Å|¨­å©Iº‰™pko=ëY&ÐÙõ.qÇß¨Ào¿òï;—ÇN~Ä¢ LlU®è…®xœ[¡´HiÃFÑÒâÔ"«‚Äâb‡œüäÄœŒüâ+ÓÉ«�7ŸaŒá 8ó‹è…¤XxœÛk°Ý`ÃFÑÒâÔ"«‚Äâb‡œüäÄœŒüâ+ÓÉ«�7Ÿa, C²íá…ƒcxœëuêvÚpŒ‘¥$5'uóFGy ?›¢á„Ôxœ›aÐe°!›•ÑxskŸHiqj‘•–––CN~rbNF~q‰•édq)FãÉÖâ›�D.3áWs ÷D¨xœ340031Q(N-)-ÐMÍ+Ó+)f`nª0-øùö¤‹lðÏN>E¦ è„‡xœ{Åù„sÃn&ÑÒâÔ"«‚Äâb‡œüäÄœŒüâ+ÓÉ×˜�7ÿ`úÄ O6ëçƒ±kxœÛÌ»†wÃDF‘ÒâÔ"+---‡œüäÄœŒüâ+ÓÉk�7Ÿe|Æ Ü¾ì‚þIxœ{+wCnƒ#gN~rbNF~qÉdFFãÍ±Œ| –ÍúæƒŠxœû«øBqƒ4gN~rbNF~qÉfKŽ# gè�àšvxœkgkgÛpŠI¤ÛzÂ‘X§¸¶*9‚Òû'—ø=˜|�I Ë¨Ïâ–œ!xœû.Ñ,¾ñK,#£ñæ ¦¬@ê3û6&&cƒÉ¸l6?çšÀ àÔ‰á¸HxœëäêäÚ�Í$â·ÎA«`wI}úÃuª¯lx°@d×æz¦.& ì¶ëí„ŸGxœûæöÍmÃAFãÍ±0On�ðÝ|Ql#P¤Aâˆ
–âbRqòZÌ@ù8ùüÉ¯%ªXj5­¹ »zFíƒ€dxœ{+wCnÂ'Fã‰Ÿõ9sò“s2ò‹K&;0Ê0oŽeŒåR»yN32Onç{ ™Þ–ìƒŒ?xœû«øBqÃsvFãÉ/Ø�9sò“s2ò‹K6[rÔ07òo>É¹ ÈÜœÅí¢6òð1©ü¶Œ ªÌûáº9xœëäêäÚ�Í$2—oVû¹*�6,_}4’1Ù4}s=S áv†á?ƒó	xœm“ÏOAÇcÛx¨1<øãôJB*•–(ÑD±@¥
)¦-�¡»¯Ý	»3›™YÊ&˜ÊÅ«‡�/^=˜´�ÿ˜ñÍ!^šÍÌ›ï÷ó¾ïõûIæËIæôqfY ºÀàl®�Am Ç}\W„/™Ü ‹Œ˜áóýzJy(@2wyö$•ì�¤žÝ�r2ƒ-dnÛÐg0?úœýq-W.ç!)ŠCpã‘•No¹èÃ¾tcˆ4}ZÁ™Ýfûùf³¾QyY{µ½SÛ˜-a€Ä"JRP#!¥2
Ã¦¼À…‘V#À@ªØ–92#ƒä×nÔ*÷<‚Ç´W¥*[Ø!�êUIýò^¼F4*¹3ú€‡gÇÔµáRÀÀC‘ÚzÔ *«qÈüˆ¬õÈ«d@>M’“p‡à3Õóê4+*¬Œim&Ö"Íå"mzÈS-‰¦ÆlÔlßÇ³¸	‘;Åû}T/èÚ÷F11Ô=Tš#]ò9d‡’»VÓ¤æëR’¯l£èï¯¢‡~¨­Ì~2­mØ¨t9�›èåQ>¸Ñw!Ït,èEÂIƒÓ‹c¾„UÇ³_º€ÇÐ”.¾hWÏŸ'ß
å«xDô�4t½šüºr3·œ§Ýû]ø	!‹í6Ø¡Á
”.N¡”d¦?ÿ}?O/çìó…Ø!âIÃ�8¤ÉHu~99ŠVp-4Dœ$:	q,Ä„K+J×B›äù3^5ù4½yÞYsHÇ¸2¤´Æ^£'×“Ü(wûéê]G
úÚåÛNWeŠíÅn»¾ÞªwàøŠµÝN£ÛÙÙª7GÑë¯ÙdvoijRÑÝª¿²0Éû½brÚÊ%ç§“ ûlôæmþ �˜Qhâ„xœ›×ÈôõãNæÍÒÌø7¿ŸéÎ©£`f`o``°¹%^J â²šà£=xœkgkgÛpŠIä^ú�ëBÑå¦3ìŸW¾”-ä?ù“& Ô�aáÀ`xœëäêäÚ�Í$Ò°;èüþå÷£Î¾îëa|âgÐ9=…ÍõL]L 
­ã…'xœ›×ÈôæãNæÍÒÌø7¿ßœ R'!ï¤8xœkgkgÛ°šÉXÂ¦}Î›¨Ã'mßã¢R,ãÍ|Y P(I-.a�â»!UøÃ2÷oö®Ç·XøÉwÎû6ù“& fˆ÷mŒØ[xœ»³†ñÌÆ’a“5ÃŒ,gªxœ31 …¤ü†õB¬ZŒuÕ�Îå?ë×äûúûÎ É&`ÙŒ’’†lf÷§Ÿ4aZ.¬½sñ¤7“¿_W�H3§_W1Lpö�wé·`pß9ÞÛr¡" ¿"!Þ¬xœ31 …äü¼’¢üœœÔ¢b¡Ócø¾x|]Èse[¯dAÍAóª‡&`U¹™))9©å‰E©ën‡pýÙr×Á7#òˆüÜöG57ÚAå—–¤3”gW5-Y±ðINþ¤IUZG»/K; ^ë.:à�4xœkgkgÛpŠIä˜<ÿ3å'§”Šÿ[ö¯†÷Ç"ýÉ÷˜4ÚÉáÄxœëäêäÚ�Í$ÂU¢Õ9WIÚ lU©¹áïyë#B�¹7×3u1 »�sé.ˆVxœu‘KLQ…ÓÚ5¨Õ@Jù�`Û@ikY �âƒvú¤Q`èÜÒúš™’5€‘�!àqEÔ¸Å�‹Y¸!†¸#.MÔ•QY¡·Fbâ´Á�‰wuss¾ÿ?çÜg+úýMÝz®•ø‚@© ¹RâÌh7�E#n
“²|†É]»ž�ŸA\ÖùN¸$V®ˆ([”4;ºÎGz§ÍkµŠùRQRèñY�Ä;½Gí9²©ƒÅQ÷Mƒ�¼^Êð…bAÌðóT®0i‘dE3ä’5”	�”	yX!S˜àVíG_šÕïF>|øÖ˜ÇXÓSU²7j-<8F½åx]‡/ÔŒ~×*ºÒ§L|Iô.øñÞEHµµ™³B§•ˆ°ÒqVmpûXoý­NuOrš™´ÌHŒh‰˜¬�R¤L1_ª(Œx*ILg
ZÎôH„ªW^©HŒ¾.?¦&‰ÙERrŒŠÓ³,£¨}\¬ˆóÂ3æ+Ú–<¯dr$3I›â”iºJý’U}ª&w«ãA‡Î§MU[�c‹rÕá?8?_ÊñÓL©m«v¨JC÷M8õaÏßŒñpO.ÕaªlÂvø8vfmø&ü˜5ÁÂ9Ñ8gC�ëƒ0×Šçœ[\ö¸^P$YãîF|X*›l%^Rüµ"µsÏÝkÅnäæ¡¢=ÑsU•±ªº€µÑ&¬FíÈw›ñ"Ú‚W±cøýkÙƒåpÇ¡ŸkèêwÀ!Yð0ÞŒ­xñö30l‚7áÁDb±´;‰¼½nƒ=iÁ¯DN'C¸8\�|²·}õê›dÊˆí¥5pË¾†Šö£ƒW¢¡Øä`"4y#Î]�95» ~šÏè¬JxœX §ÿ‡‡°?èú}0§~BŠµMè/“¿‡*¹º“SX3ß­=ªŒœ”À±JØ|)x£ot¹40000 test ®'hsv+%¾¸vôÈ•Õd…Œä“Þ)’„&@àgxœkgkgÛ`Ï$bw4í:÷Lë
·tÆ�ýg>Ö´½¹¢09˜i Ð± æ€™/xœ6 ÉÿÂÂ�zøèv)©jN«æÛü‘\Éö6—Å‘Ž^á¦Þzýo•~2éI`¾÷žˆ°%’Búx›îFxœ;ÄtˆiÂ…ªßòa6“£§îaòªºoÈv&Î~£ ÁqÉë0Œâ$xœ}’OhAÆÙ´¶šj1HÓ&›.Ó¤Ëfiše›‚‰Ù£-ˆ´´jü"ºÙL²kãn™�m)E<öâÉwR^…9<èIôÞ“Ðƒzó*ž¬“Hi´s†÷{ß{|ßl½ä6êÜ›ÍAöhè±‚EÎñ“í{�Ÿ‡;‘ÛLþÖÍBÑßÅ4ÄüŽ‡‹1ÇïšÎòŒSu<
ÛÑ)Ø‰õABà…Ú_z=Š`+zJ‹	Ó*ã‚GÍ+ÁØŽSâa9‹¦±Që•E÷ýèï�@œ—[:ûöàŠ^s;èàƒI­¢Žmð¿C�‹iž&Ä!qéª­—jQ-abUVPÍ©Z6r©N=7‰ækXw›e²‚ôªnÙIIÎî)™‘Ãv…jB;k˜ØX°ìêAÝƒ*‡Šl'xV�¾SÿpêcAöqì-£j rêP !Ø¦s¥»Ø .Ê�rzÐwó|G`GéïOZ¶�É…âìÊ!	>…ø"Fá•ªðÍôª¸Œš	jŠ9‘×óÓ6ÅQC 4Ü£)ek)ßÆßOð­?q²s{ÖÿÜëÅP‹è•J5ÇX�`Uýõbx·¸ñÒ=LH=?âG†cÓ¦³˜À†*Â½LF2Ò±]²¬‘‰ú@=#°wÚ3~åd¸¡¥AË
ð!dOóánÖw^äØÚTø(ë�ýÉ1}î:Çb—.wX•¸¾¬[µchí—e¶V|Â�|-õÎ ¾ªxœ31 …¤ü†õB¬ZŒuÕ�Îå?ë×äûúûÎ É&`ÙŒ’’†{•e6œ»æ²žÔú+ø£¡CûÔuˆt±1ƒpúuÃgßy—~÷�ã½-* ·ÿ!€¬xœ31 …äü¼’¢üœœÔ¢b†ÉGÌÖ®;V³µçâÑªÏúŽ|ªøMÀªr3SRrRË‹RÖÝáú³å®ƒoFäù¹í�jn´ƒ(*Ê/-I-fèÜtæøŒ¥C/ÞÑqOöŸ”Þc1¡
 €Å.Ê¤xœ340031QÈÌKI­Ð+)f¸]õðÌìÿn¾|ö]'”��ÆÊ´Ú' ðvãˆ™zxœ;©Ò¡¼áªÀäÅ‚“U„ä'Ÿ¬ N×bl�xœëä:Ï¹¡�ió&O& œdé€¶vxœÛ*ºRtÂw^—Ô´ÄÒœ’àÊâ’ÔÜÍŒK9ˆ	Ùïˆxœ àÿ‡‡��¥§“:6—ß²U�Û¢–ÿ“q{¬±•rãr±¤xœ31 …òü¢ì´œüòb†6™ióø.þ®æ¼wMøüÕÛßo†ó ÷m	h®Axœëfš ;1M {¸â¶JxœÒ -ÿ‡†�_5ìŸ­eÈgíüc£VÜÃƒGe40000 .gitea p¹X–@õüâ…ùj=˜™øÉ•@?‘•XþŸØ¶äÓ
¾ÂYø‚¾¸—âINÁ³><<h±íß ìÅG²�ýIÝ¯3/|Hv100644 package.json Å®)j äbËü.ÚIˆ-Iúææ“{03ÓCçîÓ˜<�ÕoŽŽ+SÆþøè40000 test ¨Ì N)†SúˆÓØ±ËÓ3Ö=i“Þ)okaøà€axœ` Ÿÿ††�€©u´Îqë!peTZÇƒ\Éa:±”ª<èú}0§~BŠµMè/“¿‡*¹º100644 package.json ¯A±±Ë²Í2übâ£^Á¯Kf‹¿“zŒÇü*�ïoxœo �ÿ††�_o1T¸OÇÍ$u‹;çõYyŒk¬u‘sy17¾/Ok]bôÈŒ”Qõõ7è2²ª3ß­=ªŒœ”À±JØ|)x£ot¹40000 test ®'hsv+%¾¸vôÈ•Õd…Œä“Ý)™0Ù¤xœ31 …òü¢ì´œüòb†K}ç�¹<š^0·}ûNÃ›û^ß I_¦xœ340031QHI-ÈÉ¯Ô«ÌÍa0û¬±Iyƒ�†ÒûJÝ[*Ëö†Þ½ ôÈÛºšxœ­UQoÛ6~÷¯¸)^jc£Ô®è<8XÛHÐ,	"§è°-�"Î)�”g#ÍßQ²]Êžƒ<Ì€!‰¼ûî»ãwGÉKÁ‡Z)œÂoXjƒ+±özJŽz Umr÷˜k.“MûÀ äBöz«y³6w(ŒË”¥Nk§kiAA=¯¥­YÁ-ÛköŒÅÊƒ“™�9&Pµ�•2Â*½Þ˜4h#øºûx’BI„ÜZ‹"Z	E©%šÐ"/£ß×6Ÿ$…¨FS,ðŽ¸ñuè\Ã}(ÜÅî?>º…¼ž‡&çðôÔÛ£y!�åET+ª\SÁã,Z`X÷¼¥“Â QÒR5Q3Ã3‚gLHâ#)³A+e©†ÅJ•âÐCIj] ËL|¹Ë_´°aŠu)$Õ;4k*ui"I¡î=w€¯`r`†âÁÆ
!ë-HÅˆU&té³�ŸÎNßt™H>/²‚/Ðx;å"XÚÄÅöö0É¸ªP‹)÷‚eÈm­ÑÀÈ–b—%‰iÀÙÙÇýCÇÍçB³µäºyVZe¢@¥˜ñº°�ïf1L�	K9–JÒùvÓéž%82­¶!<±$ z# W•²"Æô¢*+”C„çj/Ìa¥Úœû�<¥"/Hà†˜)7ˆlYE>:3$|d·ù¾ô>ªjs:Y½«Àrý@úú|“ÛÖÛE¹yÈfv~OG�¾&­	·«Nå]Û»xr{hëVmãø|öiòÇìóûË»É¡SgÛ÷~I³üOgO¯o'³›÷Óóq�pûlÕ‡'7�!vûàö)Åox�f�ë™£BAÊõ]ÕÆi�†Ñnø¥ùù*-¤ÍàÕ÷æ^¾"‘”Ì‰$èo¡ä4àç×¯;ûIûŠ¡iiœh»ÊY
Þ"Š	MüìW•Ò®.¾8J×7Óx0»`ÀÕE‹Äž+c?áºáB>Œ¥
ö)qL£à�¡‹%Šú�[I=ýÚ¾;)>”ŒWÞÿ˜:íeE÷J•ÒˆƒÍñÃ®«(1K:ÒK‘ø‰¸>\\�ƒçÕD…òi7!…_ÜçÞ›¯Áñü:v'pK‚]"¸ü-7@"ƒ¸ê%J4zÝNÖaƒìþ–é•ÎèúNOÉø;`—/2ÿlŽ²�:—.�½À¿ã—‰^ç{£®¦+ïûî1êoªcçºšÆ‰g[œgÌMOIÈöZLlí¸`�ß›­®Žff
Ä
Þq'g[Ø£G—lÅèÕ	œnãœî`oÞy ­øÿ×ûÓ)ê•	xœkgkc›P+žšÈ°RºtË¹BÆ×Š©!QÇ›cN&Zmœ*Ædìäeb¾I£î)Ã_ç9Y¡æL€@¡$µ¸„�ã´ãÑ}+Í¶9þ7~àR×áÎ<ù“& 6#3ªxœ31 …¤ü†õB¬ZŒuÕ�Îå?ë×äûúûÎ É&`ÙŒ’’�¬ŽùÛþÍ3ZíßÄ¦(¯r¢vQq1DºØ˜A8ýºŠa‚³ï¼K¿ƒûÎñÞ–  Ä É¬xœ31 …äü¼’¢üœœÔ¢b×;—h©q¯ýdé»EC®_iÇ¶•&`U¹™))9©å‰E©ën‡pýÙr×Á7#òˆüÜöG57ÚAå—–¤3tn:s|ÆÒ‹¡ïè¸'ûOJï±˜P }-PãŠ½_xœû,8Ÿeb ÎäG¬^úiEù¹
%©
‰)Y‰É©y%
)™E©É%ùE•
‰y)
Å©Ee©Å
™%
‰Å\
Z“mØd8’3‹ŠSK&?eë™lÏ!&— R�™[�_T¢—›Z’¨4¡V?#?7U/£$7'ar,Ç4 r )zà�pxœëäZÎ¹A‡ir“ÑæLžL -#ýá™­xœ›Âñ‹}Ã*&õÜÄ
…Ò‚œüÄ…:K_'…ÒâÌ¼t_'…´ü"…âÄ´Ô’ÊÉ¯™d9LŒ�ÍL,60³0 ä·Tl˜ícxœ;©xOfÃq¶ÍFœÓÙ%7îã˜/xœ3 ÌÿÂÂ�Uïð£Õ­0Ê5v>§s‘œ\†Ž;SÁ‘iÅ€kk¾w¬…!KîËáIƒ³3Œö‘—^ä€ábxœ»göYg‚ÎÆòÇ¼›„'2O“š¼W˜srŸ€°Ž‚‘�‚–‚¡�‘	”ÒÜ|Q¬— Ùqˆé€×xœKNžà/ÂZæ?É÷FÇÂÀ—oõløJ0+d¬ ƒÃ
ä†‹8xœ{ÊýŠy23Ó�É¿˜\'W1Ÿž|šU
 [”bç ;xœW ¨ÿ‰
§	°tº",»)Ø6¹··ëé}”‡TÀ›öB“ˆ‰��eûDXÑ½3äÚËNÙÍDùUèÕ“M9³ÀãŠhñŠBÒÉ4ýW`-Ç`KŸ“·Rf”*àfxœ[Î¹œsƒ#‹ˆŽ™÷ï]Vß'JN˜Æûm�ÁzáýÉ¡,A ÓYæá……xœ›a0MlBØÆí[X�7ç°r³ >Gä¨xœ340031Q(N-)-ÐMÍ+Ó+)f0H[D2ïÅÍÄ‡WOÂl21 $(hk¯yxœ{ÂùŠmƒ4óä©,þ ~'ï‘#xœkckc›Ð ÿm†]¼xLkÜæIÏ-òï47+žÝ8¥ˆ	 Æý¤xœ31 …òü¢ì´œüòb††'ÿ;ÿIt„/-¹2ƒ}Ëm[‰©]_¹h¦xœ340031QHI-ÈÉ¯Ô«ÌÍaøóóÊ«ýW¸-ç.z)·]/ÍóO8  Ýlî�bxœ[%Ü)¸a&#W^¾BqeqIjnÊä%ŒëøÒò‹ü2+’J3sR&1UŠ(+ç—%§‚…ŠòÓ2sR'`Š4RÐ((JMK-RHÊÌK,ªTHNLÎHUxÔ0Edhb^JR~…BfžBr~^Ibf^j‘æfIæUÌÌÅ©%“O²©NVaO�¼š#v²g K02üï“Dxœkckc›Ð Â*!®ûgòž}ÝÙnÕYyÛM´Ì—.Ý8¥ˆ	 ºÃ…¤xœ31 …òü¢ì´œüòb†¬i^Oék�Þ®éa=×ñ‰ùú§ó úx¸¦xœ340031QHI-ÈÉ¯Ô«ÌÍaØw>éUÃ2FåÄŒsÓM‚¹%ˆ½  <àá�!xœë|Ì·ÁŠ‘Ï©43'E¡<³$CÁ/³b²/£µXYf¢‚KjIjQnf^bIjpeqIjnñä-ŒñBººyùºÉùyi™E¹
Fvj†\“ß0mnbfg ˆ”¯ï—xœkckc›Ð òÑuéíZ“ƒ4*o8œM²ÐÎt¾²qJ Ï2¤xœ31 …òü¢ì´œüòb†	_9µq{Íù~z^ìËÞÈÚ%Nß ùË¦xœ340031QHI-ÈÉ¯Ô«ÌÍaPµÙqmß.öÞÇ[·´ôXœp³ž¨ `è.“kxœm’ÁJ#A†Q4ÁÃÂ"‚^ŠBÂn½DwM ‹¸	ÑËhgJ§IO÷ÐÓ£Ùƒæv—¾û {òæxò|Œeß`{2JìÄ:4t×WUWÕŸ�~Ý·“ã:¸ž$œÅ– ×ç‰Ü»Ú,`d5`nˆuh`Dù¸".Øv*ç	¡>|?{çŽ;vU=çÕÁìŠº™Tiîdè¼ÈdV,×U°y"<„ïd ‘à„"T(Q„„¹2sK—Ò$‡[k�‘Aí…­j8ˆ¸�ÐùrÔÚqŠN©Õ>lÞ:'¬î”RÂ™*eŸr†ÀD	¥@uöX‚Ç}4¸n³Óî5¾uu+à!Z)&º®�Âq­<ÆÕÓ\a˜óg­]éátd€ÌÐð|xk®\�ÕÄ¹$BW»åuó÷4Æ	µ°ïµÅhBt„ý~h'“M‰¾S‘á¿ f§é‰&mÍ5‘�íÌ@{ÕrÎÞJ]Ù‚¼}Ôî6{Ù@K�®O	ëƒÀ8¡²ªîæ[ÒtcÌ)ªóKêsþLýÌ/Î¤÷¶Ç$è‚4xœ{Ïs›{Ã<&F¥É˜Œeõó2+ôË‹ÀtAQ~ZfNj±~JjZbiNÉd-féÍ¹Ì%Œ“ÿ³„OÎf}-md§f¨P£�‘š˜¢ khÊ¥ J ~¹îï€Õ0xœÛ*ºRtÂw^—Ô´ÄÒœ’àÊâ’ÔÜÉŒ·6¿f<Å Í‡åßgxœkû/ºa3GR~‰nA~~Îæl., ]´ïœsxœkckc›Ð 2ã²åG�}½Oÿ™ÍH;‘aYÌ·Á¼oã”"& æëx¤xœ31 …òü¢ì´œüòb†g“[ìí�owßÿ¿uå§cÁ]©æ—ö �%¦xœ340031QHI-ÈÉ¯Ô«ÌÍaxó²f±èã4¶Ä©–~Ý½:+`ï ÿÈïˆpxœ-ŽÍJÃ@…Ij6E(âÆ�xiê"A*ˆõ*ø‡p#è0¹1C’™2s“¶�à®0/áRÈÃ(î|Óâ·<|‡sÞ7¾:‹ÏãÒ¢$µ²‘¢ÈtIgÕa»+BP¼À!œ—2�a*)…;9s·ÞÉQ%9\"¡)¤â„ln	{× ´".ˆ5ZµK�ò
WÍÚ{]C¥ÃÆI¤)Ü�¿é~üÌM[ÑNØÒ³¤Â„7c‰6 pFM†7
¶yœK•�A[æ´ÿt	ŠTC·Ï¢ûÇ«—‡Q4îÂ©{¶ëÖšï¹o?q7Á[§OFccÈp^¿þ©ÙXåï xœ àÿ††�€›Ñ…'AË>ÉYù–fÔ×¥ðø¼±”rÝÚ„¤xœ31 …òü¢ì´œüòb†—®òŽE?~ºèÄ„¼Ú–öß=¹Gò ôÜ\¦xœ340031QHI-ÈÉ¯Ô«ÌÍaØx2&rÑñsù[Â^ñÿz«Ü�) S­ï¡xœkckc›Ð ’¾}ßÅž¦þœ®"­¿Á$7Û¶3nœRÄ Õ8K¤xœ31 …òü¢ì´œüòbïÒý.YþJ¢—+¦\{¹L»zûf èß»¦xœ340031QHI-ÈÉ¯Ô«ÌÍaØ•×i}eÖíMRŽoÙ«Èqçæm Ö·dxœ…TÛNÜ0}ÏWŒÒI
m_VZT.Q�D¶¨•*!'ñÇŽ|Ù‚”‡~D¿°_Òq²�(úäÛÌ™Ë9cAJ:ƒcËx;pJ+.awÉö<OŠ™PY]¸ UDdÕí	 €’0áy?eÚÜ¥% "ò§µSVè ¡À¦Vpb¨6^ó¦­zp¢Iæ¤ Ù=Hk@ÑJjf¤zÜš X�ñ�d†I¡£Ì™¢åçÍGo€r&´!œ–òìŒ©Ì ~>dVq‚JI#a:/Œ©ô/×›ýð �¬!¸€æaE¬sj¨*™ÀRBýˆU”:ì~ô°jÐB lq+d�I±fª„ƒÃ�ýžÍ
	¾Ã‰6D5+&¶fœê(§kb¹‰R&|8<òålµøz|{u´ZøÃÚ[:1S¼]~oG£&{ºsgä„–R„XW]ƒQ–öÐ\	@øÎPN-j€äTaÑ¬¬¬¢¸‘•#4Š$•°&\Ó¶/é«hf {ePdˆBÅ†))J*ÌxEmçó9,¤6PJ”œ<û=£æÙ¹C}AÃ3¯´¯±ØlŠ/%9ï_©²‹aô!4G¶9iž¢Vy„õaG7‘°( l„„­äL�aä©Õã îuèÝåw*³{ì›v‹KÔqßjŒCg<®|=bÜQ4Œ‡ìõ;ž$‹ú<þ^/.“U}u”$õêò<^ÖI|r¯:
ö;
yúª¸Ì‡¿¿ÿ818�0�ÂJ
7WÉÞ¸V4v% ¶w“¬.¯ãf¤æ“]…	p&îñÒ8{=»åÙ·Ûxy3ÿÿ°6Cƒµ÷[¡mŽýšlAüöÿqž#Ó8˜%—¶?éR}ÝÊ*3Üenˆ2ÐGè[sŠt|÷Goƒ
$€?XEîpûLÒ'ïg]Ù9è „<xœ¥P½JAÆ`,»€õÇ©‰G
!p…A°0xÑJ�eoôVïv�ÝÍ¢UžA®±÷|KÁ'ðQÜ»€\’N§˜f¾ø>¶?ƒ÷×Îþ˜Ê\=Ã	†±â�¤aêaq�)cÁ8'cªïÍóy€ßÚÅ•¡¼UÐ3	®Š‚ÉÔ@IØŒp=IP+µØé‚W££H¾•"�;¿jáDN÷”â¦Å"‡áhØÀ–?,/…¤QÎ,­ýx¦•²XÐL†ˆ#\ R<E$]ã­î¼7ª—��ê­ÛÃ`/™^\žÞNN¦gôû+â¨¾º‡sxÅÖ™x¦ÆqŒ„´œ`,³3
ÿ�ÂzÇL¡êmuÂàî��P¤xœ31 …òü¢ì´œüòb†¬Ó›ŒÏ…/N®åÛ£²eñ•G[ù  ó‡T¦xœ340031QHI-ÈÉ¯Ô«ÌÍaxè&µÿXÝÝ5—�\vd®|luúØb àâïªCxœkckc›Ð âš·óÐâÝ6g{>Ø<{JÇï�SŠ˜ ßõ¤xœ31 …òü¢ì´œüòb†cúæe½})´ýaÝ‚Ò®k7�wÌ 	£/¦xœ340031QHI-ÈÉ¯Ô«ÌÍa�1I®;è!ÿ^ÞäªÕsY«:ãüß ê_¾lˆ6xœ;Î³”cÃ¦Í;Yz!ÜÉï«dxœkckc›Ð Rô•½A«™yÖÉ>®Ž¿œÇšU¢7N)b ÃWu¤xœ31 …òü¢ì´œüòbù=åå<4>®?ß°mƒãœyÜw õ†&¦xœ340031QHI-ÈÉ¯Ô«ÌÍa˜³þwGu¼VÛï¯]eâ	å¿ÿ»r æè€!xœ[Êñ„}ƒ3OqiJ¾B^f…nj^ÙäDæ# i©Çï­xœkckc›Ð "ÉðÐm›ŽÌ††åü+gÌ[ø ãÅ¶à�SŠ˜ Æ¼ó¤xœ31 …òü¢ì´œüòb†Æ÷��–˜~_àR5¥ü[ù'_Á +¦xœ340031QHI-ÈÉ¯Ô«ÌÍaøõÄc§Ù‘©=ç›cNE%çgÞ� 9é€.xœ{Â~‚}C£ž®n^¾nJbjn~—¤&gä+(©xøûºêëåeVèå§eæ¤N^Ç¨‰E\?µ$YÊÖKÑÊèg(mv`ZÂ ²®!…èWxœ;Áþ›mÂ?›â�äÒ¢]…Œ’’‚b+}ý¼ÌŠüb½ü¢tK?3¯¸$1'GSAW7/_7%157?OÁÈNÍps;£# Íjï¯Dxœkckc›Ð rìÐ¾Y’E‹¹ï¾l•¨j_�ü'jÿŒ�SŠ˜ çg.¤xœ31 …òü¢ì´œüòb�ãÜ÷:¼üÚüÿ²Õ¼½\NB÷Ë< í†¢¦xœ340031QHI-ÈÉ¯Ô«ÌÍaX·ÙSUùåÝ7×+9ÝøÐ¼¸ÙpÍ2 õ�ï°Nxœkckc›Ð ¢áÄT¶¡¼CNóãAóŽV‰äNŽ÷76N)b ¶ô—¤xœ31 …òü¢ì´œüòb†£�[NmØæð]³,-þP¡ãRQÃ0e =Ò¦xœ340031QHI-ÈÉ¯Ô«ÌÍaXÝñHEòùtó��n3b·ò*ñsÊ¥ û^*o‚<xœ;Á>‹uÂÍÉ56ë3Íd 4ÿãï±sxœkckc›Ð "ó½Y ×|�Åê7‹Üµê-²:0qã”"& Ï*Ò¤x$ Ûÿ40000 workflows ’æ+	Í,vãÍ˜äë}H �|ë35¦x& Ùÿ100644 deploy.yml “ "Õ<R…Q­‘ŠÕÒÚL˜óôØB¿DxO°ûname: Deploy FileDrop

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Set up Bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest

      - name: Install dependencies
        run: bun install --frozen-lockfile

      - name: Lint
        run: bun run lint

      - name: Build
        run: bun run build

      - name: Deploy to VPS
        shell: bash
        env:
          VPS_HOST: ${{ secrets.VPS_HOST }}
          VPS_USER: ${{ secrets.VPS_USER }}
          VPS_SSH_KEY_VALUE: ${{ secrets.VPS_SSH_KEY }}
          PRODUCTION_ENV: ${{ secrets.PRODUCTION_ENV }}
          ADMIN_PASSWORD: ${{ secrets.ADMIN_PASSWORD }}
        run: |
          set -eu

          key_file="${RUNNER_TEMP:-/tmp}/filedrop_deploy_key"
          printf '%s\n' "$VPS_SSH_KEY_VALUE" > "$key_file"
          chmod 600 "$key_file"

          printf '%s\n' "$PRODUCTION_ENV" > .env
          chmod 600 .env

          VPS_SSH_KEY="$key_file" ./deploy.sh --no-build
ß2¶«+xœ31 ½äœÄÒ”T†å“7yùËP[òª�Y¤«ø™×ß×†f&&
z)ùÉÙ©E™éyùE©Œs§æ~²u
øoãe*ty•á_;˜ÂÔ¼2½ÔŠÄÜ‚œT©“{®ª/ø˜ô|ŽUi�?{CBÀå&+Ó3KRd¾7ôš¯±Xýf‘»Vý£EvA&Âª€Z—S3©óþC±MîëÏ»ktîÛÛÉó`Ô˜ŒÒâìJÛh3Ëƒúwæ*þµØ½—¯a{d?ë¨1Î>Ž¡.®z¹)tþWŸ_ßÊ2£°Ñ×ÙfŸûþ¿Ó¡Š\ÀžKË:Ù#æ÷$îc=Æ›dÖÌl?Êäðb—'TU�«£‹/Ø¨ô�ª÷dÖŸW¿P ü²7+ú·sÓ‘/PEI™ù¹©zYÅùy¯f?é¹\aÒ$žlôpwnÈAß½¼0U¥yz9@K6æ»¿ÿdµDÙ\í¡°ÞäNm��@5æ¦¦
)©9ù•zÅ‹wæ—G\gl
¹:‘å£KcpÄ
;=¨A�hÑMÎÏ-È/NÕ«ÌÍaèRmt÷Sxÿh“ó¼øèk}ïoMë�„Pq1ÃÅP�jµï¹ZKšÎý:.ça˜9jXAbrvb:Ôõ×·ý²»ÊÏ­´þ_Ñ¹Ø}Ò_ÜŸ;÷@Õ'g¤æ&êæ0hssßž5s¡Ï‰Z¥NG‡ÞË¢\Û!–%3¼ñxrné<©t©-¯x¯†Î^¬ ‘-I-.axXsyÝú9Ç…Ó»˜:=O89o*®Øµ¢¤89?/-3â–ÏÖÆÜ•Ð—[¸g
›ÀÎI;ØEú@æŠèƒnxœˆ wÿ»»�_'Q�È@žYÑÜ…•—0?ß˜,¼±s3ÿCè¯["äã»²¯¡†ß¹‘Qs¯“º9,}§±\0gï�?üüÑ.ð¾Â@“:2gkê³?4K~�Æ{�zÄ@Ì�S40000 src f½ÔEf¼ •hí`!$æw�dk“sHÔ\<˜ï€xœÛÍº›uC<“ñÄM:s4_s1tsÇ]:U¢òüÒ�‹&@ P’Z\Â0û·æÍï‘)×¥¢-í^w–-ÿ>á˜ÈäILš [rîîLxœÎ 1ÿ»»�_ìŸ­eÈgíüc£VÜÃƒGe‘syþŸØ¶äÓ
¾ÂYø‚¾¸—âINÁ’Ô[Š+8Î]†}d5(ÎÌ/$ˆáíÛº40000 docs SNqˆZ9$WÆ¿)®ÌlºlÆ=|100644 package.json Ë³NÈêQ!¹âÍ“Ü>`MX9ù¥“/03ï iwuç…·¹uò‡ â>1ö40000 test G8MìR°�›µ îeíÊE^y“’)z—[Øá€\xœq Žÿ»»°D¹fÜ5–ÿ6ÉvftÂfÀ4@“&õÅ®)j äbËü.ÚIˆ-Iúææ“/03ÓCçîÓ˜<�ÕoŽŽ+SÆþøè40000 test ¨Ì N)†SúˆÓØ±ËÓ3Ö=i“’)ö?2‚g™ÈCxœ;Í|„yÃF âÅxœb �ÿ†»�€:÷ƒ�7¬8«ì¢G*â¢>RÀ‘100644 .gitignore l|’‰ßá²G¯ÏG(‰¾½‰à¨±ºT“YQ¡9_ãvw±XÝ+-üÙžêoâ'“¾H »+ƒ¢xœ31 …Ä‚‚œÌäÄ’Ìü<†cK~„?aN‹Tút_ã“ñ…LU&`U)ù¹‰™yÍ[{†˜[5Y³É¸þ_"uÙÖ!ßÐÀÀÌÄD!5¯L¯¤˜áý‡ÅW×œ2-³[^<qNL[ŸuðA¨ŠŒüÜT½Œ’Ü†ì/„Ê4Wfù­~vûÏÄ˜“ßÌ¦…*ÊÌKI­ Ô.ùæ­lÕï ;žžßK}�ùát Ä-™yiE‰Å%E¥É%¥E©ŽµsÂ’“fðy,=ëîñåéSÙF˜Â’Ô¢´ÄäÔb†È£—Ï‰W?
û6/ò×;ž)ÑÂ¡ÏU ŠŠòKK€
<äÅC4í×¾8vdïíiá½b!
Š3‹RSÂ>-ù<KÏ¬ÎÂà¦EmÛÛ¢›õ� ÿé…³ì�2xœ;Ätˆiƒ£HCvö¾ò5­ŠÞïNK?ôlÞÌmÜó ©b˜î)xœ áÿÂÂ�ìá¦Þzýo•~2éI`¾÷žˆ°%’BñsÝ¢xœ31 …”’|†™‹ó½Øíî0MÝdy¢%örÔ’OÂ&`ÙÒâTÝäÄâÔbÁrå‡›SÒ'e\¬
ÛúûVÒ•3þ (rSë�»+xœÛÈ¸‘qÂ\W×Œssýº%â\zò‹ïj0«fˆ% ¤3´n�§qxœ[Ä³�gÂ¦É,2Ï³ *aá†txœq Žÿ»»°D¹fÜ5–ÿ6ÉvftÂfÀ4@“&õÅ®)j äbËü.ÚIˆ-Iúææ“/03+·dÅw·ûq†®K@Ù©Øõýw40000 test  ñt”Õ‚÷X,,™Öä‚–;“’)ÏÈ0Îªxœ31 …¤ü†õB¬ZŒuÕ�Îå?ë×äûúûÎ É&`ÙŒ’’†lÏ¯<6Ÿw,<¬9±íHùWÎµ«˜~A¤‹�„Ó¯«&8ûÎ»ô[0¸ïïm¹P ãæ"¥¬xœ31 …äü¼’¢üœœÔ¢b†Åêogy2ÌÕzÆÆ7½£¤æŠ_ž¯´	XUnfJJNjybQ*ÃºÛ!\¶ÜuðÍˆ<"?·ýQÍÁ�vEEù¥%©Å�›ÎŸ±ôbèÅ;:îÉþ“Ò{,&T Ø�,·à€·+xœ{Ãô†iƒ:£HŸœ¯Ò¼ÿ+{qýÍº¨¾|gªïïÀÉÖŒ† ë`_çŠŒxœëË:‘¹QÃKfrƒŒ
»—Lv�Û¬ cÄ¼ù‹ün,2ï8 Ï­èŠ"xœÛÍº›uƒ4“ˆôÑušYO’NÿÑ»ÅçÙ¡ëùëÙ³ÉúLÆjª:ºåXfŠ	,÷.Û¬Ú>ýæ´H P(I-.a�È5Ôg³Í·•d~z0?™M+¯þÉåÉ“˜4á´ þå™œ3xœ;Ê¾�u3£-ÓdfFãÉS™¹ CùJé�•xœKNžà/âíákÉ©"�±¹ò¤bÉÅ‚/n+ p 	Èî�”uxœëfêfš`&’œŸåðÄK}íáQžf‡fGçHl\5Ñë  ·ÿ"é†ünxœëJÛš²��gò-ÖÉ«¹ù&çóp°¥–”å)Lnä‘UP°WPOÌËÌM,ÉÌÏSW°RP/ÈÈ/ÉW·æRPÈLSÐ˜ÌÈ-±9ˆw‡* …ÇÀá½xxœ! Þÿ§	§	°œ^Y­1¥ ÁÖ“VÇš„¼Ô{ƒï³°÷õJQà…‹kxœ›¡9Usƒ9óä÷Lš›“™M4#vï�xœÛÍº›uC<“ñ1iîÙË³ôØ%—›i²y¸/žtìe™‰(”¤—0ÌÑ³½·jO�ÖåÏé¯Ï¾ÙÓ·oÛäILš þqëŠxœ[ ¤ÿÂÂ�UŒ‚S†.Äh·¯N†’ƒîQÄ‹‘i^9�1ùœ32­l·Öy˜ñrª’àU40000 interfaces „bM�r(˜xšÝ
sP¦çk…¥þ’B@•(DàÁxœû¬s@{ÂîÍrŒv¼›×ðg ?N®ªxœ31 …äÄäŒT†__ê;¤‹æzš¼7x]°aÕ�™«¼>˜™˜(¤dê•38‰½(	sÙ>Ùs‹+Ã¯œB;ÊËo™€(H-*Î,.IÍKNeh^öínâ‰Raùé|RÕ+ª§Þ„¨*IÍIM/JÌeÈ\’þNê‚âÂÇœ<1�A z8ˆë€xœëbìbœP&bòã˜À)+…oÏ/.bì¯›zŸß7í ¥o·¦
xœ340031QHÊ/Ñ-ÈÏÏÑ+)fÈŸ�“=ýëTãþSás5d²¬ÎXB”%g”æe§¦è—ä%¦§‚Tû¼}êRÖí±=oO¹¹DÑ:ë×÷&CU—T¤ƒÔmš!�¦ÔpénsFá~ïãSç°é@Õ”ää'¦è&%–$g¤�'}ÉÔÉûq`¥dûË/fuºVæ> @TB7í€Ö*xœëxí¿Á“M 9?/-3]/)¿$$?;5¯xs%Û$E ¯Sg¹×xœÍXïn7ÿî§à‡x•H+£Eïƒ‰Óä"œ±‹‚–Ú¥$ž÷_—\Ù®-àâžðžäf†ä.—»Ê!ßÎ"‰äçïof(óª¬5{dI-¸ŸO¯5|ÍÙž­ë2gÇE™ŠÅZŸIw¶àE)Óîýìè‡JÀ©÷2Œ+ú|Wh©¦ì£¸£eGÇsø—–9—Å\à!)Ô|Gì–HøIT¥’º¬ÆY �¡ŸÕíÑ!«‘‰MÍókQïdr@ÃKÛ£3eÎúvHÊb-7µ(v½3™àESÝˆ¼Ñ\my-Òy£eªí\ò«¬¦Fpøv¨Þãñ§¬€ÅÑüÅ‹#ö‚]
ÍS®9ã«²ÑŒ³
¬‚§YSe%OÙJ¬ËZ0©™TL5«\j»ºdz+ØŠëd+êXÍ�Ä}gÂ+ËægÃåŒ=1w2T³¬9ÈˆÊ°Šë-+–Ju‹<Ópà
VLéZ›SKxýáÍì»þÆ¶\Åš®'`c�¡9.}€#!9™VÉ?A•‚­´°çqé.XÑä+QwµÒö¤˜ƒ]./ß±Th‘h	Â[F›‚ë¦Íz-ê3Ÿ§GûÎÚoØºÉ²–ƒ×jÉ3©À”ÄµIY§ð
\D÷}ÕÂÆ²"%ÅÎ¼ò®+Ào"®<} Á·Ö¹àEè`.pËF/º@sÛ¹u¾¼
b%wÁDq‘´‚Áø§ZndÁ3c€‚ç¢óàGøz�ŒNùî ’\æâvF�N$IÆ•’k™pòX$âMÌž¥eÒä;Ï¦ìYµ-uùlÒÉÐgèyq	ñV£à•(RØl­Yóäã�S&îµÝ,ˆ<î$Ä}-T™íÄ¼ÿ„Hb	Ï²°PÆ&�‹6�Bg<'gXF¡à/&ììœí |OéÞ§D]—¡Mq[”w…wÆjÉnÀ  c‘–w˜/¹Ì0V!ÓRÅÒ-Âî¶2ÙZÕ÷¶Á¥Ë�Ð³¨Ð@]¼¹yûá·_–üé—ß.¯A‘ïNNN:“’Z�hy“iYeÖ·Ž¹, P9dX±�Õ_—Wp]²•;áÐ	þ»ã5
™ÁÝùhh³›-€b“À]B&å1 ÚÃo®–ä	…¬x‘2@ÛºÜA]6›m8‰¹¤r85#AïÊú–DEÏ™AKƒ­Æˆ((Zl'9#sÔM±ÿüëßp hŠ@@1Á‹dðø:“ÅmXÒö$�ÛjÔ%ÍÆ3y/“)þ-fc×9U-w FeÐTù ØÀÏ_ÀsŸ¿œz„ë¬Q[ˆ§O„^˜7¯0xÁªJhÜƒÒrÎžÀÚYð\O)
ÿƒ¶¯Àôë~ýž1ïZ¼µ’Jcôy ‰F÷˜è rÏ˜û†,ºH!ê 2ž·"\š«/ò"tØtœ*�q1ð%ÒMØã¾g¥‹Ff)ä�k‘lÍ RÁMö¼§¨ÕªÍ‡ËF±žµèüC„µwú–ÔZÌ(Í÷ðÑ´˜O€r'ÓÆ¢zŸÞ‰3coÌ·Y&v"Ø?�—½6UQAùTï
[×Ë�ÐˆÙ`®¬Ð‚~á4^EU!´×‘ž‹¶±2‹V‘eþ9"ËeêW¡p÷çBþÑŒœ±ª_
¥à÷»nÿÆŠ"Ñ�µ/Œí)€­½Œ ÆŽ­ÔU³ÊdBwQM¦ô±
Çýõ±Ó�~c4nwÚ×úí–k$1ÝrÜ[�4�» Ü™LF¡ì~ºí®g m÷“==±c^U™mæe¢…ž)yŽ§C³µëuâvÃ—Â»ÆýtÛ&ÙD�
�ø4¦‡í3wëîœµÀÍ7yËî^³=íþûÖ¬Ž ´¬]§„1')‹bá‚ƒ—­'z†ÇA%º3{+mhðU“Ü
m2(ËZ‡}ÿñ,=/ ã‚×ß“êG0žÁ²iOx­-LÛ¸ó¶l
í/ï1#û@þk&ƒî!è ŒÔ®¨‰4]Œi¥Ò•9lx Ú:@ýjsØÇÕC‘˜rm‹{4ÁÖ¼Ì¡³{…íß¹E¹f‘†N)îjû¤œVk[Ó§ZÄë¯·5ŸìqD¦/4EëÌô[�Xa(ˆèÄrE©èxœ‰b�ÒÙÙ;™XP;5\ÁghÀ›`†é=@H¨­V$PÂ~ÇaÚmGëÈaFØ8çUa^RÛ=zk?[Ýúô±ý¤EÜÖtžô üjùœöd²ÎþŽ|Óº¬f}t€¾��Ãï£”Ÿ„‚hnõ%}Il»¥›Ò¢Eg‹ð1(ò%mµ€F·qìf¯cÏ=9S»–4Ü
äÌ?ÅfCÜ£Z¦¤Ä°È›ã(!B´âŸ‰ôË´õ?�Ã¨±Ó0éåo„:,‚ÑØÒÂdü[‡€<4v¯`¡¨	y9:b>‡þŸzn&8$ªÿTa§!¢®ûêyÐ	ù@µ�f'øëÂ©çkëcüN9fÐ¤×WÇ†µ=4"9ö„¢�„‹ONáãóÑV^¾œxþ§ÍÏòKlgéÈÖÛÝƒÉ¡ËŒÒ#º×hO­5Ì@ÄÙ¿Š@ÀÌâ–<ä½Æw‘¬ƒ§¡åÆ°(xHŒÆáh2i¯CHõðfâ /àã9í"Bç…ŸXå�ø‘ÔÃÙêLöµÀT¤aòV¶ˆ1¯ÔØo!þœ�°çÏÙ_V£±RÓÍœQDvñ“ëœGÓVÂÓ.¦ákEèžA=Ë³¤É°ÈêRÃèCÏŒàù±
ï^{³˜ppSMÒ®ÝÛ¥–uèìˆŽÍ–tû¦ÑkùÇllÞA¢ˆdž²6ŒŒ/6¶ÌTä¾	Þ€i�y@öŸ;ÿûê£ÖnèTl+2CÔTØØØÇŽÀ%ûÞ“”a!M¡eÖ½›’Ñæho„+¡¶ef\`ßåñÕJÜ'B@¥˜.öEi%Èð#âÐ;éWféÐ´\„1Óp°­Lnƒ˜\»ç<e^„è©“ž�-$1oîÂ²î¿µF££­×÷u/…çýˆÁL9EV¸©}MìeØX˜U`Bè’â86e8 ‡ºÓ‚6¢ÁÿO®{Bõ%8€Ugn~¥Ð»ä÷KÂ§§>u˜°#„í˜ãÊš§ÖÿRÊbÔHŠ.ó\¤Ÿ­M è–´|PôT[¼(�Fë`ànEa—.‚´NJ|œÔÞ`â$½GÆƒs‰×¨uïëúw¡ƒ÷]‡¿IS×Ð‚�j.0¼ôÁæàÁ?ÁY¯S¨ó¬‘ŒFÁoAdÛš�6û£ÿÑ–Šâ’)xœûiµßjÂböj…¤ÒÌœ”‰ùZ,ºi‰Éˆm\­ÎÍôK-wËÌIÕØ|‰»‡qòM>™ÍÅ¼ÖŒ“·J‹læWã l¯}ªxœ31 …¤ü†õB¬ZŒuÕ�Îå?ë×äûúûÎ É&`ÙŒ’’mWáISßöÕ°;¿	žÓô8wƒö«~ˆt±1ƒpúuÃgßy—~÷�ã½-* ¹ß!Å¬xœ31 …äü¼’¢üœœÔ¢b†*Ã¥'}²)•övóü4ïé¾7=L]&`U¹™))9©å‰E©ën‡pýÙr×Á7#òˆüÜöG57ÚAå—–¤3tn:s|ÆÒ‹¡ïè¸'ûOJï±˜P �/7à€Õ!xœ  ßÿìì°'"Ì„hÚC…ïÁÙ5¼éŒ··IÚó“;1üïÀëxŠªxœÅUÝnÓHV…X-¥¨´À.?ÒvŠ„<.‰Ë¢ÝÕªÐ"RUÀ‚PÕ!TU0µOâiÛ;3NªÈûH‘o�Øë½ä&WðH¼CŸaÇ3þ½â_$¶Ïw¾ó÷Ï›ýö—h7ð™@GÈf@<gT@SÈû.¢ó»Èð|VZ|t£>z{yÆö½m/SÏ�ÃÑ¸v.:>qDØ.°Ñqý’ ù6„�óLÙGÇ‹?è@›‘î²v©ïiŸñÜÔŸSã{§>ŸYoû `ÀyÈ@. ~’â:)Ê‘`pP�…p¡‚xbüøÜû©øøüR$åB;3´úu}X@7Ø$Â5ïL#¤Ñ.á®B{ÐG�Ð³ÖÙ þCõÜ%·ÿÃ(9H"G9´h,�#6­6ˆhK™p	ÌiÛ#BÖ¶î†Þ_‘Z-`;»Òg7Áu „jpiºUX^—_æ¼ªFÅ)„<a;Jž³æŠœ‡ß¥îö|ê¬¡Õ5t$ý¢-„u“,õ‡íÄÑ4S;B¤O¨P©P`,çàwzP“MØ—ó0ÒäŠRRß³jBFU>ª)úªÀ€1Ÿ9c†¦wCù;,Þ¢åîólÄiéºØè¤j¿©–4CðŒ•!c3Oð„"¢‰*t:{Á){ß•ºAX°ŠŽëâŽ�ã{2£é„ —q5Í^KÎJþpž@2Æn¢=i8�Q¢2—�ŠC«ÁJV+Þœ ÐÖÍU·öäãðÚÂ-G)€k¨K›Ù£Yš½p™?ÓW}ÞHš�ãÍŸPTŒs:½Ñëf…�CráÝ©È®�vn®äRY•»¨ùèÁÓûÛÏ¶6^6^lo4Ë9ÙQº°]©Dêµe7&Ð¨>±{…HOÚ_”õ‰‡{„12À·j}Iá©[”í½„ÜÅU²¯á¬GzÂ\†Ó¥¶êÆ•W«®ÅÍ-+YÏÚ.°áÂ¡aÖ&Eÿ;w¥žŠEÖn1‘/¯Mä™»>Ÿ®�#™™?HÂÆ¯®]�/Ì/Ä~›½’+·„Ãß>ÐŽ›³ÿÍÄ/�è£A~Ú3µWŽ¹ƒ‡BõlÁÙ�ƒôum:þõ—ñ‡úùùä³¼-¤Z¾ƒñ"ºÏãO×~Œ—cünáãÙï;~·ðb¼µüñôÿP�nbn…wxœkÈúž9!Ÿ95¯lcu«	 3>{êxœûžy?v£®“XfnA~Q‰BµBqIQjbnH~Hjn�Âd&9î K"¼9Šé6§‰KjNjzbIj±BI¾BIFªBqFbQjŠBµCNf^6Š	µ
¥%™9™%•›+xÖ1Ê¥—æ”(Ø*$–'f– ¨Ô˜¼�ORUè‚ÜÄŠàÌªT§J�µšÖ\

E©%¥Ey
íÖ›ç‰D*  >üD¨Mxœ340031QH,-ÉÐ-Ê/-I-Öâ½’b†Ó‰ÛÚ]ûU.-Ë´[¾5ÑòA÷"C„r¸ºàUâ…®;ô´ÍÒ½®Ìß|½ûÂÉËPuIùù%Å%E‰pÅmÉwY/Åïî]ða-÷+µ‰KÂàŠKtòósàj÷?SŸ¼{ŽeÈúÏ®±i·­¾ÚÜD¨…+óJˆ>{ËÙ@bFçßíÒ+ë¸¾}”…*KÎ(ÍËNMÑ-.É/JLO…k™_³ì^ÙÖ¢5-!ûª6:æiš³ª%%	®j¶ÉÌ #öQÒîË,ÓÉÐŠ
]ð4¦*µ '¿R79?/-3®Á;eÏN&‡#²Æ?'zyïìa>á9Éª!5¯®ì³�ÖB§Fçù&|Ú?ëà¾mŠ&Pei™9WÞÖý’3=9Í93}�è¯eÉº,S‘Ô!bªøÏâ$…)Çl®—Ï¼y]ÿÜ›3�Bf¤&æ Åóï÷~öÅÚöÆæåÖ†w½½mbúÍÄ €
s
R‹€Ž+yï»†½ñ(—ÁÔè#‘?—0>Ò‡šUP”ŸRš\’™Ÿ§›j„p&W‰Vç\%iƒ°U¥æ†¿ç­�uæ†ê(J,IõÉÌÍDDÖ7Ç§Y*Ë—©=ÎWÜîÙ¼ 'ïÃ¬PÅÅÆº(éŠñªƒlÎŠ9~T^n}b>!CýÚìG¥I¥ÉÙ©%è1 ÷±-ö�°©•É»þ�­]=AÁÁ=)ù@=EºE©é™ÀTY	×eÛgÆí¦×5QüœžlMèÝˆ ­£]ùIY©É%À4T”š˜×Ãó´Ç¾09®›gqrÔõ¸3/®pæEÒËDPH!"Éå	ÏÉ§t«jµç]Úg—§{¦¡¡(1)}®Õ49(¿§PÍìGŽ£Ç›�2_Zÿ#Ô§dÃU~ºþ†÷“«Ã#û¿,Û2›¾ÿ;nSYž˜žžZW*¦¶åÛýâà‰×ƒ«�=ð{Èô°·Dª´$5'5½Éw¿Â/™Îu;ðfÅ–«‹ŽJ¾{è5ç7šÚÀÒÔR„‹§U,òÝµÌuÅÉê�'ßGž/XnÈúª¡˜USà*·Í4��:åµç¾©ß§ø¦,˜ueªÀc¨ÊòÔ$ÝÄ‚L¸ÒÞò)5‡�{Ò¦~Ú½n=×ëÛ`.®ÊD”)«ê–Xœ¯×½Å°;×æê¼	¦Õúwšpnðdê†xœj •ÿØ	§	�œ–}÷Mx;Q">Šl´»«ûg»‘°Äº",»)Ø6¹··ëé}”‡TÀ›öB“ˆ‰��eûDXÑ½3äÚËNÙÍDùUèÕ³%ò^k‡.Å}Å0v	ü,Ô6Å¤“\|Ú|3;áyxœ[Î¹œsÃQF‘ÿr)ÌÅëTŠú‚Û÷›/ü¶‹ãšûæ›Œç˜ êÉ‡â€›xœ[¤Ô«´akiIfNñæLA| D¾÷â†ÀCxœÛk°È`ÃFÑÒâÔ"«‚Äâb‡œüäÄœŒüâ+ÓÉ«�'Ÿa¼ËZZ’™S¼ù0S•( ð§Kæ†ŸXxœëujqš=±Ç~bˆ4OiIfN±~N~zzjÑÄÿXJRsR7Ÿat” 0Q®ã†‚_xœëä¼ÁÁ¯¯¯àPR¬›—Ÿœ‘šœÍ5!˜)%ib©�Üxv3 ÉK%h/x ÷ÿØÉ±Ie¿h…â2x ÷ÿ�)Ž)±ŽQàà„ÇxÀ ?ÿÆ
¹�Gonfig } from '../src/env±:'/    const limit = config.rateLimitMaxRequests;
“�limit“¸=“¾B“wË‘úJno-op on cleanup of active entries', () => {
    checkRateLimit('user-3');“)@});
ï«@'ì„³Axœ»¯{]wBKiIfÎÄé¾ú­'˜žlÏfl^Ä¸‹‡§$5'µ´ '?1%µhs
ß6‘I¢/Pø“?ˆ¾ Èï%ÇïJx/ Ðÿ×-Ë-°Hfiledrop³T”filedrop“ôèfiledrop“èïÁÇËâ/„œ%xœm’±OÛ@Åe ‰m!ÔA\ãD–(‚¨©”!R¤V4ªª¢da+ƒŸ©Û³|g)QA‡Šµô›;vB]Ú‘‘©@W6``åî§@3X'ù÷¾ûÞ{öyqßøz)U¯*
Û6~
+’!U�·¥GÒ‚Óª´âöGDÉ¢e.•Ìç/Ì¥Oªi¶#dSÔ¨2y-‘•,„Ñ¨Ó(F¹^à$ Ñ{kû¨Óãop‚=BBêð9«ÁÞ¸²l”™×Ãˆ”Q—Þ3ÆÁ`f³õµù$ÛVx6\õš6z‰äeà¬£^¢bŒKéÖÁl£×Œ�ëu…N&ŠÓ\@B—¾yø]‚eDî­Â¼ù1¦^ÇŽîy³[aD7R¶ÕÁ¡íðiÍ°„s¿Ãï%Ð­a¿#p¦²“Zšo”à_8/ è–‰&{Ä†|šüµëOÿcÉÕ$­±ž¤d*~f×x™˜z˜@-W„ÝÜœ”Øüž{8f©ðkøâ"7ÃÅ\eù¨²®‹É
eïD¶»BØPŠà+(Oà‡²•Âã¾Èµƒ(ÿ«�¦nÀS¨©óðN]„/ê,©…±~éøD½–Ø¿ü{ò”p0uv×ó¶îƒÖxœû$w^nÂ
–Ò’Ìœ�_1‚7ên¾È4“	ÎYÀšálæá‡	+oöçbƒqÄ&sññonæÉ€™ ¶™‹/ƒ çÍ"/è‚�1xœ[¬1GcÂ;žÒ’ÌœbýœüôôÔ¢ÍŒŒJÂ }¦äãI�ÿ~xœ•’KkQÇé#É$&j´¦Ih’[KI¦Lm©Ð‘¸¨R,¤(V¨j�&7éµ3™8�Ö6„ÒOÐÂ_kq­Ä�º(¸qå'pãJÜØÏà½3‰ÞJ@�ÅÌ�sîÿ~÷Üó¾ôüæë•¡Î§!ßÀØ
VqÅºƒM[µdd7¶úN£¼†Š¨¼'Ã‡¾Ñˆ…ŠðÔƒöùÍ¢x5PÑ¦…4½²µº‰LEl�Éˆ¨xeZ„T¦–�°c$Ú§…‹†®õÑí0?ù·µ£Ûç….Ki�:Ê¿¼»9áRÃÄF?á¶¢ÚØ”ùømê@Lœ3°©«ÛÎˆ"3„e_´\ÝÈ“F?NKˆ3©nÈˆ}"N™«)9qÓ—¹C°x›½jDe -øæ‡KÍÕEC¶ET³{ P#ðÖû&îó½=<|$ˆø¯œenë&Ù£},H¸ïÏÃ¡?ÇþÔõ¶ÈÊÕ±µ S¡žeïÎ¶EjÅ7†fà«í»Èë¤*£´UÏ²Ÿ,©f§gfÓRWÍŽdºP(ð)�hxÝÚmÒTšhJç6q�S»¨ìé.œNµÝ{¸\ºma3o7U]©Â«àœ×í6œ#^·Át9:D¯’…<Î<9+g"Y¦B“a~¸Ýñ¯…ò§Ý;Cú°§¹©[:(c…¹÷FÿÔui[FàAÒ†k|ˆæàer‚±sð%™„¹Ø8Ü�¥¡$Ü¹5°±D»Y€B~¤Æáb|¾7­u–ãÏ…è‘��](%b>·¾G£pkB€bâ ×�Ï	íox18Ž¦àIò<Åšç�" ¥Â+;0{ÂÔ?ÁÁQDé4Ñìÿ æ:ï&‚Je¢è'„±xœuQËJÃ@%�‰5ETJÅJ¥�¤¶Å•ƒ
"¨àREÒdÒ¦NÉL°ñ.Ü¹gáÖ•k¥?Ð­¢!8˜ÍœÇ=çÎÛôµþ¸/Í¸õr=vŽ FS/úÒ²^<Ótú[¨‘Ã¾,1SÃ0)âù�++Ù²Û ¦>Žü Á,4r™£Šê8°1’È{#Œè¾ˆlé­¬p@$\ëÐL"�êÙƒhòd•6ð|P	tðÏ,é:…¬Dx®î&H¸A>ˆ!äuXÑíz8YÉN<¿#
¿ÊÕQF@¡‡Wy2æõ™+÷B–��&#ä²¼w7üXÀ±ÉÏ#nGÇM;Êä[kyï€7›ˆ±QÓ�ÈnïF*Ê¹J�Hq•9r£Ì“ž’Ï]ðF€™éFà…5!a'ÏRˆ}ìX‡È?¥�P­TL�˜ê,ÙUä\�N]äAÝŠÂT>õ’}ì“BOZ /ý0tlØ&‹éÊ/Z÷.=%QÞÓàh÷yàžþv[™u6ž‡d:zÃKÒ;mÀöàÊxœÛÍº›uC<“ˆ«Ù¾c3g}û]w"Ü´ìÄE­<.™“‹™< ôù½¢xœ31 …Ä‚‚œÌäÄ’Ìü<†~5±ÚÍÑß•ã?/Pô+èt2«JÉÏMÌÌchþÛÚó0ÄÜªÉšMÆõÿ©Ë¶ù†f&&
©yez%Å=MÁmzüG2¶¯÷k›Ô,ø®>ðH7TEF~nª^FInÃöBeš+³üV?»ýgbÌÉofÓ�Beæ¥¤V€2^ÝõÌÑR· qÑuß×J³—”@Ü’™—V”X\RTš\RZ”Ê`2ýxh…|4[I¡€�¦øÓ&/¿¯0…%©Ei‰É©ÅóJçÕÞª5óËÛ€Ï«×¿NùQT”_ZTà!/¢ih¿öÅ±#{oO3ï•ûQPœ‘X”šÂÀhZqdåJ�îÉw…N^ðí­6 ‡Øíë�1xœ[ ¤ÿÂÂ&40000 application …»¹¹1l�¯ßÂ±Š÷&…ƒIè‘&Æóõ¦Úâ#Sè‰¶�vöîÇÄƒ’.hÊ*¤ŠH£ªÊÝ-oâsz,"=)¢xœ31 …”’|†™‹ó½Øíî0MÝdy¢%örÔ’OÂ&`ÙÒâTÝäÄâÔb†>{cÎ…~z¼²Ê�9õ¯L3º‹+g†å�êxœE ºÿÿÿ�Á>!­¯Zå;mDº¶´ãBê�óµ100644 upload-file.ts ôÿÌƒÿ „wS—a8•?¸?ÙÉçPo Må	Ž÷exœ[qšqÉiÆ�Z,ÌÞ\

Éù¹¥%©‰Å:@®‹«›c¨OH¼›§�k|Hd€+H05¯¸´(Õµ¢ÈÈÌÏ	¥åå&–8¥&–¤¦8–èpmŽef¶eËKÌËÏLÙœå°…QÃ¨ÍNá¬X„;Ýåå �/hn�£Mxœ{"u[j‚9sj^ÙFgE^ ,b)î¾^xœ áÿ¦¦�Aš_ªµ¸¿×BƒAÿÃáúAõ8ìh‘UQàÞ‚ªxœ31 …¤ü†õB¬ZŒuÕ�Îå?ë×äûúûÎ É&`ÙŒ’’†g::=WOø*î©ø][>¡dñÆÒébcáôë*†	Î¾ó.ýî;Ç{[.T ùX#;¬xœ31 …äü¼’¢üœœÔ¢bëÌ¦¦7‰×wŠœ–v*>µ¢æ÷?¹°ªÜÌ””œÔòÄ¢T†u·C¸þl¹ëà›yD~nû£šƒí ŠŠòKKR‹„YŽ¼Þ°|æ‹•ÝûìÂ…­æÕ„ k(-:¬xœ340031QH,-ÉÐMÎÏ+)ÊÏÉI-Ò+)f(**+y¸K^à@H@RË-{Á“†Õi™9©hª¿¸6ô|xS~<:òèâ¬sQ­‹í&��ªÎHMÌÁ0ýÆô‰¢{ýå¯-Þ‘é½M¿'q‘£L}~.ºé¥Û¿Í^g4£þ¡Â·‰[s6­�b…¤Z/£$7‡áû¡2Í•Y~«ŸÝþ31æä7³éG¡ŠŠ�Ñ¼Ì{÷ø¼�Vçs&™½�ñchzbU[Z�“Ÿ˜‚¦~fƒ]_Ê�
!Å¿ôÿÝq¨ ª¾<5I7± MÃ²*÷kë÷\Z’mù¹®èªù£–­¡ !–Eí�xœ} ‚ÿìì�*ä©Ë…¶¨I¢&�ïVCµ|<‘.PÜ ¢øßè{ÜÔ(õ3ºÑDö¬‘\›Œð(³µ¶=n<õ<PŸPjâo¼"“"Ì„hÚC…ïÁÙ5¼éŒ··IÚó“;z÷D¢©ï!G18qgpÌO6Vj9¤xœ340031QÈÌKI­Ð+)føê“Ðûd“É¤})Ê¯Üå>,:Qk^ ðyn‰µkxœ[l4Ëh‚"sj^ÙFÝo (µ‹é�ßSxœKNžà/R±|ú–ÅÛò³·‡~úþÕÝ´ì‚c" ¥­lÉ9xœëfÚÎ8¡hâ\­‰¤ @<é›…Ô`xœÍVÍoE—K[$·Mšº
éõ#v-ÅëWTršTJPZ$Z¹M‡4…ñî³w’õÎ2;[×¸‘+qâÂeŽpáÊ	)âÂ¡GÜ8pâOàÀ‰�cgv×É®ãŠˆ•µã}û>~ï÷>ì?7¾}÷»çùeáÍá&zh‹u—PV`PhsÖm€Y�•Õø ç"Ç¼À£]*FÂ¦²¡!Þ¼Ëì½êá´w¶wV—cí}}¨›úmæ‡8~a(>’»ógJ¯˜…¡Ci‘Á#}ä©vÑÏC4+ÐarÒU0Ç¡›°›/?Ñâ2ï1šÛ;•…w|êû~åz÷0¸B£ÌÄÍ8V³¢m´‚ÕeNä¡iXV-ävÍiÕÚŠ¤ÐX8Â5Tè©ïhòÖúÍ¨åQû}§$ìûvŽù„�²qÆ¬$\s÷Á$=BS=KóbV¬˜uÆÙ+xÛ‹;ðô)ø‘ç-ËæÙ‹·„òÔá¤‘ ^˜É÷6
�,ŸpZôCÚ²wª<Tt)$“ÒŽ�ÖF1²ÉtP¬11V§‘jã°é:	ŽFTZª´RÃb¿X«Å]�µˆm¶mÆ!àìI"Ü4?Æi‡úÄÛˆUVR‹-—†Vlµœå!~7ÒŒ¹H+ôqÄ½„bÈ©ßÑI(8ãÅ@}ìÁåSQÓ&{XÕÍPUAúBÑ2J6DDa–R‘‹ÄA®d0ÖƒêV?@£	Õ4òN±4“°1ê_u“¿œ{x–³H`˜´ž|8µžü4uãtÒIrqzöÕ”\ýýL&kI¦?¬Æ•c)¥#K	�ø}ùýt-iúü×óä*!w.•å�çÝ¨ßß˜ŽMñÄ�O®ÉcÿòÍu|ö“k?ó´/oÎÞ.Ôå73Õ“.rXÏ÷qÀT5RõâHº ª[T�€µ¡¾¸¤Ò¡\gðüÂ•7™Ü‹ãí·�{¿¤oƒ1}QµvŒ(¢Nõ�¥º‘%CN•Þ:¡Žû¥‹òVé²þúCém}øW/ÍìØÉ«ÿ]‚ë‡ä-Œ…úgžŸÍÝ…T¨¦ù�ùäyRž-Wùzä³ª0›Êütb G…‚í¡/ÿ˜­ï©•gæl6]y¸ä1Âh²Þ£jLCªý¤ŠÉ&pŽäªk½V:—–ÚO¦1ÁÜH—3 >	Ð
3ã£b	¶†[<nßÌ+ÓÓ¾õo‹¡ÆIX»AÇÈe‘LR˜É¥…èƒM<�xý	!¸®»7glxu¬;Ê~M™¯ÇÖ¦Ü¿ðºl^9%¿zm%ÓÇòÜü P?x6÷Iá7ª›—£‚lÂI¹}õ¹wí†üõÚÔ�ÒiéE:ëéØGåÏO#V>*3ùÛÜg'õ?�ÈRÐœé‡xùþª6³2°DFInfo = mock(async (_telegramFileId: string) => ({
  file_size: 98765,
“0Mjpeg',
  file_path: 'photos/file_0.jpg',
  bot_token: '123456:ABC-DEF',
}));
“‡1forwardToStorage: async“¿FileId: 'mock-tg-id',
    telegramFileUniqueId: 'mock-tg-unique',
    storageMessageId: 12345,
  }),
  getFileInfo: mockGetFileInfo,
})³#Info“UV“ÚÕ³Õ	_&redirect to telegram file url with 302“î‹³òø“w302);“O/U(
        'https://api.telegram.org/file/bot123456:ABC-DEF/photos/file_0.jpg',
      ³°z
d‚ €àâ9xœÛÍº›uC<“ˆâ’T[YCž0e£Yçkcžuè}öSž\Ìä ¸%¤¢xœ31 …”’|†™‹ó½Øíî0MÝdy¢%örÔ’OÂ&`ÙÒâTÝäÄâÔb†=>,íÑ–§”b{Ž°´}xèô2³! V@æ”xœF ¹ÿÿÿ�™<M·Ž¯k†ÄìxD™¾£ù4"100644 s3-object.ts |Ä³¯d€Ñ<j&�E �—Ù ‘Õ*ºeì�¿Oxœ»“t(iÃÆÍüL³T¸ÔSò“KsSóJÔ7ïR[Ä ³fˆªxœ31 …¤ü†õB¬ZŒuÕ�Îå?ë×äûúûÎ É&`ÙŒ’’›UïvNt¹œszCÐæŠ“M+›…Ý!ÒÅÆÂé×Uœ}ç]ú-ÜwŽ÷¶\¨ ç¨"h¬xœ31 …äü¼’¢üœœÔ¢b†5Ö’SÒ—Þ¸Ôô|ªWzLÈ„h°ªÜÌ””œÔòÄ¢T†u·C¸þl¹ëà›yD~nû£šƒí ŠŠòKKR‹„YŽ¼Þ°|æ‹•ÝûìÂ…­æÕ„ A,+í�qxœM ²ÿìì�÷�<0§�]7N-q\ú“h°äŒI“,ñ“cx_Å¨¨U~üûÃõmjÔ“;Ù(á��+áÃïß|ŠXÚ�?^4#,áÚ
xœëfêeœ`&’ð]wÕ¯Ù½iéöÇNÏ_£QsãtûD/�‰¤üÃ¯lÚxœÛšr yCÛæ¯lÚ )ŒàçxœÛÍº›uC<“ˆS¿ø¤Î3‡•º?ý8Ú¶Ïÿÿæ³“‹™< è9¢xœ31 …Ä‚‚œÌäÄ’Ìü<†ï‰ÏfL[k_á\ñÒË»“vùi•˜€U¥äç&fæ1ä5t¤©¾L7þõ[Þ¡êÒy=9C3…Ô¼2½’b†ž¦à6=þ#Û×ûµMj|Wx¤ª"#?7U/£$7‡áû¡2Í•Y~«ŸÝþ31æä7³éG¡Š2óRR+@¯î‰zæh©[Ð¸èºïk¥‚Y�K nÉÌK+J,.)*M.)-Je°Ì\ }(}ÙÍå™åw�‚Ê_¯<	SX’Z”–˜œZÌðÀaKß:oÙÊ²g‡”ì|ò�ï½Mƒ(*Ê/-*ð�Ñ4´_ûâØ‘½·§„÷Jˆ}„((ÎH,JMa`4­8²r¥F÷ä»Â'/øöV ròƒˆ¢xœ31 …”’|†™‹ó½Øíî0MÝdy¢%örÔ’OÂ&`ÙÒâTÝäÄâÔb¹3úÙÇæÝu˜Ê(ÙÙ�x¶ ø¯xœ340031QH,-ÉHÍ+ÉLN,IÕ+)fP?›Ÿ¹»MÈäë²"ÙüDöËçßB”¦§–è¦eæ€•ùI7>s
Ÿ¹u·diÀw³«gø¿€*ËMÌKLOÕM*MÎN-©òºñ{ICds~ŠŸµÃÃ©!…¯^ÃÔ–æ”d$•è–ää'¦€”·æœ<Ém{éÇGé�êßU?êZ,U^l¬›Ÿ”•š66êão��·Ìþúï¹ÊúdÍB·°oì…PuÃàŽÝóy¥ß2×ë=_}Œg<»wò‰Æb Ç‰j³£xœ31 …Ô¼’Ì’ÌÔb†"·'X¼$ä¥¬Þ?{÷�eÁÊ°’‚ü¢’b†E;nùg(ß]Rù®ZøìL‘ƒÜ÷5?  <zEiŽîHxœ;Ì8ƒq‚áÄ˜t €sëÝxœëbìbœP&2�ù~áî­.Mõ�s/î	)ôÑ ›(ãÜxœ3 Ìÿ¦¦�A´ÓOÌS¥�‡:œõeû’ôòRj)‘U=ò!+V—QƒƒôîÆÑï-*Fœgƒªxœ31 …¤ü†æ	×~º\|µ$ÚsÃÜ”-Ú­LÀ²%%'Ö^>áràúÇ¿%¯.å÷rþz7µŽ"]lÌ œ~]Å0ÁÙwÞ¥ß‚Á}çxoË…Š  Í©&E¦xœ340031QÈHÌKÉI-Ò+)fðè]}§úÿvñØƒÖŠú…Óí‚³tÿÊá¬xœ31 …äü¼’¢üœœÔ¢b†¼šâÓª2à_qIîÀ‰ø7&ú&`U¹™))9©å‰E©ën‡pýÙr×Á7#òˆüÜöG57ÚAå—–¤3³y½aùÌ+»÷Ù…92ZÍ«	 ä+‰íšexœM ²ÿìì�÷.þÕ¸p©qŒ5Î¶%¿ØŽº“,ñ“cx_Å¨¨U~üûÃõmjÔ“;x·QYèícS÷Ë#5Üd.= —ƒ.#Tä@��xœÍ’ÏkAÇYJU¶1”R�4ÆduÝ"
ñGQzI¢É¡=x*lv_êÐM6d6ÆDS¼ºûŽ
Äzñ ,z� !ÉÁ“ôâÅ¼øG¸Ù6­ —Š†™7Ÿy�÷å}í×cŸ^�=\Â7ãQôr‘®ÄiY…;ffÑÜÊáMnÍ¯DR¤0"3÷²¨Ü‚’Œ_¹P UF™eâj•6›lçÛ=îÑý3jA¢eU#¶—'H·e­,v—'ÎªÔ
UÒ*Æ»-$í9|Ö=�÷ÚA¾DK°Ô¨€Èãóv?·gðcÿr�_¨)«`¤U%:>ôHŽ²ÐÀ 5EŠ²Æœ¬aq¥
²óvÕH‘2ÔÉ‚Ä„mT«¨F-á"�ëï¸\÷Ù�;¾	q*D$é£–yséC¨w½xÁŒà²yÖQ5…OÌ~0ÃøÝÙ+‚I+ìJ*9§eÍâKËÕ’ý‡ZìÖÁÉ|LÈå+d§WBV¡‘"ºÄ’Yh�ùy�Š;„Ñ&\kÀ\>
FyÅ¸žòûGošÌŒºJ‹tØ¯ŽR&Èé˜ÏLcbcÜý1ä°äÞP‡¤%àøÓ-¼ôÞƒ³_ÛúFòÀÿÝ«d{q=¸oCàZïä®#ÞôNc -¸Ž÷Oa¶/ýêõýã»^ßìÏá¹ÎäÐS¿›ÅA$õ·ç	rùcv}ðÖûÈYpçƒxœ;¾•yßVæ	![¦O•dNÍ+Û,À¸ý& ~ó
% xœ31 …Ä‚‚œÌäÄ’Ìü<æí�’ÁŒûŽ.ÛxF&ñ«Cý�óWLÀª’óóÒ2ÓLOõÍ8bým¡mçí:ÏîWäË(C¤$1lÊå`¶—Ÿ=k×£W¡«C®îüP•ÌÏMÌÌc0ýý¡nãûW�ræïò4Î7”h240031QHÍ+Ó+)fØá'œ÷û‘ÐÕß‰!¹?Ú¯M¹|ª"#?7U/£$7‡áû¡2Í•Y~«ŸÝþ31æä7³éG¡Š2óRR+@Ïš1­}û·#-ªQ›ù¯Lÿ_•½â–Ì¼´¢Äâ’¢Òä’Ò¢T�x†Z“<–Y©ëÜ™qOáLaIjQZbrj1ƒBôF1É*ûŒŠõaÜiqøqDQQ~i	P�‡¼xˆ¦¡ýÚÇŽì½=Í ¼WBì#DAqFbQj
ÃÞ‰¥åÏÏ�p;(©&?ÕëÒ¤o/bs 
JK2sŠlÿÅ|ŒÖ¶#=gÏ¶�¯ßïnKW ÖÇ®¨á‚xœq Žÿ  �qn€ˆpf%ég3úû@zÒÏ.‘…€9zÛŒ~2šÿ-‡n;M‰Ç»Š40000 interfaces ™Mƒð¿D©’­Ájui ù(Ñ6Z‡“>N0Úü°Oº2=Jñ¶«Àq…·sÚÏ|L2@ì€ xœŒ sÿ ã�N‘k(Œ‚S†.Äh·¯N†’ƒîQÄ‹‘§5yÝ*=ä!œ¤zÔnø;
4új‘ð9œ”ƒ$éåƒ5Oòeì6¥H%40000 interfaces â#æïb‡	¢¤ú8Çã�#gÙùÅ¾“>.âŽ-€uäÍÆ�øÙôaCº»Ã½g?å¢xœ31 …”’|†™‹ó½Øíî0MÝdy¢%örÔ’OÂ&`ÙÒâTÝäÄâÔb†»;Ì´¶¦0Ín¿(ô¶öýœèù·ds-nÉë�-xœ äÿÿÿ�ëÊ››¼ú9n)â›•BüU™ÜÀ0m¤xœ340031QÈÌKI­Ð+)f(ñœvçËIoí˜Â�3x×}Np è±/µxœËÌ-È/*Q¨VHÎÏKËLW¨UH+ÊÏUP×ÓÓOÍ+S·æâJ­@SaÍ ê³„ªxœ31 …äÄäŒT†__ê;¤‹æzš¼7x]°aÕ�™«¼>˜™˜(¤dê•38‰½(	sÙ>Ùs‹+Ã¯œB;ÊËo™€(H-*Î,.IÍKNexüæfÜã°�M÷‚ÿÇLJÊ?SãU
QU’š“š^”˜Ë°|óî>³yË5|ü	ÙÃþ}ç†uÏ �%<í©xœ31 …”¢ÌªªœT†žl×ÞÔˆîÐ�+Æþ±m«eçœz¸×¬¢(µ ¿8³$¿(3µ˜á¹_ô¢”éÜÑ~Š%ßßDžÛÿzºÿ ¯àãì3xœ3 Ìÿ¦¦�A£†‚:aÞª!<í²Ò<Ï6º+«£‘U=ò!+V—QƒƒôîÆÑï-*Fœ8×ªxœ31 …¤ü†æ	×~º\|µ$ÚsÃÜ”-Ú­LÀ²%%�_Å–~ZãwDþÓNî­í…‹rf@¤‹�„Ó¯«&8ûÎ»ô[0¸ïïm¹P ‰`$©¬xœ31 …äü¼’¢üœœÔ¢b†•õUÛ.	ë¾´^øÄ–|Ö‰»X%MÀªr3SRrRË‹R\N'Þ¸71ŸíL™¬dÆÔð8OÛ#{ ŠŠòKKR‹Že¾³¹ÿÕ¸®²�}Jæ«I[,t§ ÿA,åªbxœe šÿìì�îÖ¡Ò^/EÞÀ�ÃæË‚Ì'‘.É_Ç-F´Z"eô€‘wœF‡ôÂI“ÕÊ®Tr  ¤Ó–� ÂÅ‚cºN,“;Bd	>fùSÉ€ec%ËæçÈšD@-Í¬xœ340031QH,-ÉÐ+)f˜ö¶ÁéõŒ-.±
U¢Ÿ*Ö­“¹õqƒ!DIQbIªnNfnf	Ha‹±î…goÞ­ìÓü(tþKØôÛâ®? �Ú!R¤xœ340031QÈÌKI­Ð+)f`ú´hïÚ¼Ø÷¯_�˜­:ûq—{ ø9\¤xœ31 …Ô¢¢ü¢b†µ+'ÝeSt,Ûô,iÁ¿š;W¿I­7+ÈÉOOO-bXp�ùÂñÛÛøf6™ÇjfÞp¢ 7µ¤(3¹˜áÕÕíò·oúºaŽ]ŒÐç+A¢\,¥%™9Åß7õ­:°æú‘°Ôž'Î8ïV ½—:¤xœ340031QÈÌKI­Ð+)f�à/pç{ÊëÌmä­·úúíO=»×u¶ixœµ”AoÛ0…ïþ¼5	Úä¾a‡kOÃNÅîŒÌÄDeÉéÅÙÐÿ>JÎ‚dõÒC€"H~ï}$¥ÕbQÁîQêØ" ”bçQ6ö½ì:Ï•c¸“ŽoØ�e™¿”B- Áºg¯w&ôP„v¬ lx ¶]Š%ÝõDžqí)«pÅà\Cî™ÃÐ¥h1<îitZU4t1é!ß§y4¢C†qõ³pÑSï4¦Y+Ûw`+S�—M é-FÞ˜¿/kmX–%á¸9Q¾ÉÛ/ÕKU­Æj=5)îì
F–è[O¢F·aoÙ1„¨°&ÃëCmPfni"ÿ£}ñ%êc>yNqJö¿,¯\þ™hÝ»gR›ÈT4°èÇ}9w}’	Ÿ7XJOhpD‡ñlqà¶oóXÇ�!
ÿ 2‰}ç#ÖiÒSŒŸ1méºM:s¹f?Uj;cª	4~Skƒj˜‰°Þ��“,3«IÉ•îîsbCQhP¨©î�÷|>uÝÛ”Ã^¯¯m.×¢·¾=ä†²È|GÏÖaÉ<Îp—HìøÞÇ3¡ëñMø\äÐõ:Âœ"Îh¹]BËFgOe¾´œÊ+D¾–ÛñA+`ãÝ¢Þæ¢Œ¹Å9ú¢'S½þz´»^!þð8á<E8%ªxœ340031QHËÌIÕ+)fHø®»ê×ìÞ´tûc§çˆ¯Ñ¨¹qºÝ¢$³ ¤`«Œfß÷¿‰]J6ó¼øQ¾ª (µ¤¨¤&W‹É9:)ãŸõ‰£]ŸïõNÛ¶€ª¦
bÊ[�ýn¯ŽÍõ³Ó�;ô{ÓãSÇú öR9µ¹†xœ­WÛnÛF}×WL�¢†r“ö¡Rœ´�eÀ@m‰Œ<E¸¢–ÒÖä®B.-‰þ½3{áE—8E
¶µ;;×3gF"_«BC¦–K^@Z¨úQ4ÄŸJ‹¬Ú‹þ¸×žœÀ[%S±¬
¦…’ Öô§„TPp]<Âœ¯Ø½PUÁÉ°'¤æEÊïèvêÄ?÷ HÙ{y•ƒ¬ò9ÚV©SÂ´æùZ—,xÊªL�àåÀ(ÈÙé¼|3rÇNÝ9Ïy€ÞpÐ+©(Jít
	¹È2QòDÉE[õO§§^¹�B–EWû¼¿gHo8—F?ºó¿œ6&Ðÿ£êQX¬3AeÀ„ò‡µ’\’C0gÉ�JÓ–Î^¡»jïêÅßp7_ˆ„iÊÓV&ýÞ¬8&« Kq�ñð¢@ëåJUÙ04ƒfÓYeV(§r�uZ„(Ãd)ÐS�˜UÜÁ´È¹ª´UVÃŠØ<-¬
òÜš0È@Ÿ#:‚JÞIµ‘8{s¥2Îä¸·íõ0©XÌóÉÅï·Î>Nof—Óë÷#Ö§J`l¯Ú{gdXAá^�MíÃNQl±ÂCyÅœ‡]¯;M†¬»æú½.Ð›V�§L&±>1oìE”ó²dK#@q!—Vó`l”‡¶� {®É¶Í­ÀÜV…„À|€Úl$d’U^ýÉÛéõõ»ÉÅíûÉy _¾|Etvy59ŸÞÎž’»žÎ.¦·×OéûùÅ¯OH8´ôFÄ½{[Ë9=Üä�'•F°2	¬|”	¤•Lm„^ìÛ÷H_"AÌ53"ÿP”’P­A+ˆ¸ÄÀe¸&¯)	[…4Y
XjSÂüâ=´Ä$l½FåØmq°ûv*³Çºz'¦Ù7+‘¬ n�+vE-!ÖEÅãÝVË2PÔº¦¿H“^¡CYžc³c¯g�‘íUõ<E½ÈV¬*±{OfÌC“ÒèñYûmÍ
–C*á¹ÉàN0sÜ–'jIûÑðlO²Ì$éN�{´+ö­™Ì¼TÙ=ºxÏ²ŠÓtˆS“Ü°GõÆ¡e›Œ`ÛãÌùöjöšZ!•Ø¡¦'op¬!1ã9µ°ómÔ�JHºÁ¨%ìÛÙÚQT”‚(Švx(¤3ò–ð›qmò:é„¿±ð93*£.)!èÁ@" A‡;=×^¹‡^ë»gÏŽ€(Ÿ]Ë9z`&4f%p´²»=óïê <�ÝÅÿÆnNŸÀÑV‡z¶’ü`ÎZât4Þ‚[b"sô-&j¨÷Ã–$ø„…­£ö¸Øñ -æ²å£o®¶ƒqý¿i¡nÒ¶>Zçæ†Òy‰)–âH¶/:®î9jÀÓ|<âÎ¶É®­»äí pÍeš£äzf	Ø‡ÖÆ ŽÇãõŠéU„kC`NlšöÈ/¬Óç@í×sY°Ù©á5nsý‡‚­�=køcžq¿fx¦º´…˜‘JShÅè)®g±Gî®jÚÐõ_�žµMžýÃlzgº³ÔD†¤*¹àEfêg,£>-dÅ�ZZ¤»9¾šN‰I¢Ñ#–×ÔÅwql=r¾ßB_žš5ôûxÖ›ùF¦­=ô;)>4~!ZGTî"*ð«Cð—�±–…vÇd=øº:‘}‡×¤­[V" ·qA<]s7ƒ|Ymþø¹vz›ã¸„MÎóçïÁøð†‚è9£¯1Ý2¹€qi6*)[%öb¤Ù�éõ˜f);;ª1JÚËL‚‡XEEËÅ††~[‡CrD-âV˜Z	ËJåŒ˜7–Ë±ÖlÉ($­Ì¹1Pì Ö°°=:ÅêØÝ|'kƒ¸vãy7´BÞ«;Šˆ¬*ø7!Ù9ƒ"Rªß†æÎÖ1°_x_vÐî”†¼Wzèö æ›AÝÓÎˆ�ÕOLêÎ@¹q5P5À)hú¢V•ÄEÞÁÖ�i��ÿ<ÄmOøÙçbðFœûÔ)ÿ ÿX!çŠoxœ{©ðLa‚8ON~zzj‘~f^JjÅF5g p3´é€”&xœ9 Æÿ»»°×¶ú>Õ"¯þrÎ]¾ôGçCŒ“/0Ž)Åèö+±¥µ­÷qÅª=“sHç{Co�›4xœk{"º�…‘)%i³ã=N (GÒé›|xœ‰ vÿ  �“.f*)-TUj^Ð‰àx$‹Ú‘§5°rÑA–ÿôws¨¥gžÓðçÛ:‘ð93ùÓ¹ÆŒ_Ò¶Ø¾«ïç<Qç\40000 interfaces †Ñp�àµÉ–ËùaœvtE¼gN“>N…ô·¶þ2^]¨ñeö\Î‡a‹|AS¢xœ340031QH*MÎN-)Ö+)fH›Êù.w_äÎ'Ó6
¨ŸTëˆNVœdQ•–™“ª[�XQèºç°‡}’…ü]A³™Nk.—xÍ�¤°X7µ¢¤îŸ‹Ú®FA•ìÏ%êbú-Ñ¥¡ZÕÏ�Õ�Ôdq)‹=©=X}Ÿ Ô]á}O<«¡j2óRR+@j¾dvÎïŸ×Õ™‘œÛnv©B®ª&73½(±$¤êX‰ã“¼5ó¿ñM×-ÓèÙðéhÎ'˜ªÒœ’L�óAêì®”²Üž{Òä†fRèî3u>— êŠ“3RsAŠüÒÒ®�^Ù¾º¬i¢¿¨Q�š÷»� ƒ¿uîµ¢x%
Úõimport { sql } from 'drizzle-orm';
import { db } from './index';

export interface Bucket {
  id: string;
  name: string;
  createdAt: Date;
  updatedAt: Date;
}

type QueryRow = Record<string, unknown>;
type QueryResult = QueryRow[];

export const createBucket = async (name: string): Promise<Bucket> => {
  const result = (await db.execute(
    sql`INSERT INTO buckets (name) VALUES (${name}) RETURNING id, name, created_at, updated_at`,
  )) as unknown as QueryResult;
  const row = result[0]!;
  return {
    id: row.id as string,
    name: row.name as string,
    createdAt: new Date(row.created_at as string),
    updatedAt: new Date(row.updated_at as string),
  };
};

export const findBucketByName = async (name: string): Promise<Bucket | null> => {
  const result = (await db.execute(
    sql`SELECT id, name, created_at, updated_at FROM buckets WHERE name = ${name}`,
  )) as unknown as QueryResult;
  if (result.length === 0) return null;
  const row = result[0]!;
  return {
    id: row.id as string,
    name: row.name as string,
    createdAt: new Date(row.created_at as string),
    updatedAt: new Date(row.updated_at as string),
  };
};

export const listBuckets = async (): Promise<Bucket[]> => {
  const result = (await db.execute(
    sql`SELECT id, name, created_at, updated_at FROM buckets ORDER BY name`,
  )) as unknown as QueryResult;
  return result.map((row) => ({
    id: row.id as string,
    name: row.name as string,
    createdAt: new Date(row.created_at as string),
    updatedAt: new Date(row.updated_at as string),
  }));
};

export const deleteBucket = async (name: string): Promise<boolean> => {
  // Cascade-delete rows that hold FK references to the bucket
  await db
    .execute(
      sql`DELETE FROM multipart_parts WHERE upload_id IN (SELECT upload_id FROM multipart_uploads WHERE bucket_id IN (SELECT id FROM buckets WHERE name = ${name}))`,
    )
    .catch(() => {});
  await db
    .execute(
      sql`DELETE FROM multipart_uploads WHERE bucket_id IN (SELECT id FROM buckets WHERE name = ${name})`,
    )
    .catch(() => {});
  await db
    .execute(
      sql`DELETE FROM files WHERE bucket_id IN (SELECT id FROM buckets WHERE name = ${name})`,
    )
    .catch(() => {});
  const result = (await db.execute(
    sql`DELETE FROM buckets WHERE name = ${name}`,
  )) as unknown as QueryResult;
  return result.length > 0;
};

export const bucketExists = async (name: string): Promise<boolean> => {
  const result = (await db.execute(
    sql`SELECT 1 FROM buckets WHERE name = ${name}`,
  )) as unknown as QueryResult;
  return result.length > 0;
};
¾CÄ»¯xû
õimport { sql } from 'drizzle-orm';
import { db } from './index';

export type CompressionAlgorithm = 'gzip' | null;

export interface FilePart {
  id: number;
  fileId: string;
  partNumber: number;
  telegramFileId: string;
  telegramFileUniqueId: string;
  storageChatId: number;
  storageMessageId: number;
  sizeBytes: number;
  storedSizeBytes: number;
  compressionAlgorithm: CompressionAlgorithm;
  etag: string;
  createdAt: Date;
}

export type NewFilePartInput = Omit<FilePart, 'id' | 'createdAt'>;

const toNumber = (value: unknown): number => Number(value ?? 0);

const mapRowToFilePart = (row: Record<string, unknown>): FilePart => ({
  id: toNumber(row.id),
  fileId: row.file_id as string,
  partNumber: toNumber(row.part_number),
  telegramFileId: row.telegram_file_id as string,
  telegramFileUniqueId: row.telegram_file_unique_id as string,
  storageChatId: toNumber(row.storage_chat_id),
  storageMessageId: toNumber(row.storage_message_id),
  sizeBytes: toNumber(row.size_bytes),
  storedSizeBytes: toNumber(row.stored_size_bytes),
  compressionAlgorithm: (row.compression_algorithm as CompressionAlgorithm) || null,
  etag: row.etag as string,
  createdAt: new Date(row.created_at as string),
});

export const insertFileParts = async (parts: NewFilePartInput[]): Promise<void> => {
  for (const part of parts) {
    await db.execute(
      sql`INSERT INTO file_parts (
        file_id,
        part_number,
        telegram_file_id,
        telegram_file_unique_id,
        storage_chat_id,
        storage_message_id,
        size_bytes,
        stored_size_bytes,
        compression_algorithm,
        etag
      ) VALUES (
        ${part.fileId}::uuid,
        ${part.partNumber},
        ${part.telegramFileId},
        ${part.telegramFileUniqueId},
        ${part.storageChatId},
        ${part.storageMessageId},
        ${part.sizeBytes},
        ${part.storedSizeBytes},
        ${part.compressionAlgorithm},
        ${part.etag}
      )`,
    );
  }
};

export const listFileParts = async (fileId: string): Promise<FilePart[]> => {
  const result = (await db.execute(
    sql`SELECT id,
      file_id,
      part_number,
      telegram_file_id,
      telegram_file_unique_id,
      storage_chat_id,
      storage_message_id,
      size_bytes,
      stored_size_bytes,
      compression_algorithm,
      etag,
      created_at
    FROM file_parts
    WHERE file_id = ${fileId}::uuid
    ORDER BY part_number`,
  )) as unknown as Record<string, unknown>[];

  return result.map(mapRowToFilePart);
};

export const countFileParts = async (fileId: string): Promise<number> => {
  const result = (await db.execute(
    sql`SELECT COUNT(*) AS count FROM file_parts WHERE file_id = ${fileId}::uuid`,
  )) as unknown as Record<string, unknown>[];
  return toNumber(result[0]?.count);
};
¡>›´º³x:Åìimport { and, eq, sql } from 'drizzle-orm';
import { db, files as fileSchema } from './index';
import type { File } from './schema';

export interface S3FileRecord extends File {
  bucketId: string;
  s3Key: string;
}

export const findFileByBucketAndKey = async (
  bucketId: string,
  s3Key: string,
): Promise<File | null> => {
  const result = await db
    .select()
    .from(fileSchema)
    .where(
      and(
        eq(fileSchema.bucketId, bucketId),
        eq(fileSchema.s3Key, s3Key),
        eq(fileSchema.isDeleted, false),
      ),
    )
    .limit(1);
  return result[0] || null;
};

const mapDbRowToS3Record = (row: Record<string, unknown>): S3FileRecord => {
  return {
    id: row.id as string,
    publicId: row.public_id as string,
    telegramFileId: row.telegram_file_id as string,
    telegramFileUniqueId: row.telegram_file_unique_id as string,
    storageChatId: toNumber(row.storage_chat_id),
    storageMessageId: toNumber(row.storage_message_id),
    fileName: row.file_name as string,
    mimeType: row.mime_type as string,
    sizeBytes: toNumber(row.size_bytes),
    fileType: row.file_type as string,
    uploaderId: toNumber(row.uploader_id),
    fileHash: row.file_hash as string | null,
    archiveTelegramFileId: row.archive_telegram_file_id as string | null,
    archiveStorageMessageId:
      row.archive_storage_message_id === null ? null : toNumber(row.archive_storage_message_id),
    archiveFileName: row.archive_file_name as string | null,
    archiveEntryName: row.archive_entry_name as string | null,
    archiveMimeType: row.archive_mime_type as string | null,
    archiveSizeBytes: row.archive_size_bytes === null ? null : toNumber(row.archive_size_bytes),
    bucketId: row.bucket_id as string,
    s3Key: row.s3_key as string,
    storageBackend: (row.storage_backend as string) || 'telegram',
    isDeleted: row.is_deleted as boolean,
    multipartUploadId: row.multipart_upload_id as string | null,
    partCount:
      row.part_count === null || row.part_count === undefined ? null : toNumber(row.part_count),
    createdAt: new Date(row.created_at as string),
    updatedAt: new Date(row.updated_at as string),
  };
};

const escapeLike = (s: string): string => s.replace(/[%_\\]/g, '\\$&');

const toNumber = (value: unknown): number => Number(value ?? 0);

export const listObjectsByPrefix = async (
  bucketId: string,
  prefix: string,
  delimiter: string | null,
  maxKeys: number,
  startAfter: string | null,
): Promise<{ objects: S3FileRecord[]; prefixes: string[] }> => {
  let query = prefix
    ? sql`SELECT * FROM files WHERE bucket_id = ${bucketId}::uuid AND is_deleted = false AND s3_key LIKE ${`${escapeLike(prefix)}%`}`
    : sql`SELECT * FROM files WHERE bucket_id = ${bucketId}::uuid AND is_deleted = false`;

  if (startAfter) {
    query = sql`${query} AND s3_key > ${startAfter}`;
  }

  query = sql`${query} ORDER BY s3_key LIMIT ${maxKeys + 1}`;

  const rawResult = (await db.execute(query)) as unknown as Record<string, unknown>[];

  if (delimiter === '/') {
    const prefixSet = new Set<string>();
    const objects: S3FileRecord[] = [];

    for (const row of rawResult) {
      const s3Key = row.s3_key as string;
      const relativeKey = s3Key.substring(prefix.length);
      const slashIndex = relativeKey.indexOf('/');
      if (slashIndex >= 0) {
        const folderPrefix = prefix + relativeKey.substring(0, slashIndex + 1);
        if (folderPrefix !== prefix) {
          prefixSet.add(folderPrefix);
        }
      } else {
        objects.push(mapDbRowToS3Record(row));
      }
    }

    return {
      objects: objects.slice(0, maxKeys),
      prefixes: Array.from(prefixSet).sort(),
    };
  }

  return {
    objects: rawResult.slice(0, maxKeys).map(mapDbRowToS3Record),
    prefixes: [],
  };
};

export const softDeleteFile = async (bucketId: string, s3Key: string): Promise<boolean> => {
  const result = (await db.execute(
    sql`UPDATE files SET is_deleted = true WHERE bucket_id = ${bucketId}::uuid AND s3_key = ${s3Key} RETURNING id`,
  )) as unknown as Record<string, unknown>[];
  return result.length > 0;
};

export const softDeleteFilesBatch = async (bucketId: string, keys: string[]): Promise<number> => {
  let deleted = 0;
  for (const key of keys) {
    const ok = await softDeleteFile(bucketId, key);
    if (ok) deleted++;
  }
  return deleted;
};

export const countBucketObjects = async (bucketId: string): Promise<number> => {
  const result = (await db.execute(
    sql`SELECT count(*) as count FROM files WHERE bucket_id = ${bucketId}::uuid AND is_deleted = false`,
  )) as unknown as Record<string, unknown>[];
  return Number(result[0]?.count || 0);
};

export const findOrphanFilesByBucket = async (bucketId: string): Promise<File[]> => {
  return await db
    .select()
    .from(fileSchema)
    .where(and(eq(fileSchema.bucketId, bucketId), eq(fileSchema.isDeleted, true)))
    .limit(100);
};
é™QQ²5xR­üimport { eq } from 'drizzle-orm';
import { db, files as fileSchema } from './index';
import type { File } from './schema';

export const findFileByHash = async (hash: string): Promise<File | null> => {
  const result = await db.select().from(fileSchema).where(eq(fileSchema.fileHash, hash)).limit(1);
  return result[0] || null;
};

export const findFileByPublicId = async (publicId: string): Promise<File | null> => {
  const result = await db
    .select()
    .from(fileSchema)
    .where(eq(fileSchema.publicId, publicId))
    .limit(1);
  return result[0] || null;
};

export const findFileByUniqueId = async (telegramFileUniqueId: string): Promise<File | null> => {
  const result = await db
    .select()
    .from(fileSchema)
    .where(eq(fileSchema.telegramFileUniqueId, telegramFileUniqueId))
    .limit(1);
  return result[0] || null;
};
ñâ€âŽ®=xœÛËüœiÂ§ÉnŒ®“{e…S+
ò‹JRRÓKs€t’5 ê.méŽ¬lxœkå?É3Áf¢“ÖÄJžÒ’ÌœbýœüôôÔ¢‰Û*'/gÌ�lÎdÉ”’4y“S}Jf‘‚¶‚º¾ž'g¤æ&êæ¨ë((èë+¤åç*¤d—ès) ’jT¥
ÕÅEÉú
I¥y
ººù%`~f^Jj…^I±&º)õx�IIÂ0	(”›™^”X’Ša†QXUÄ)IVPC6²p22ªO¶fÕ“Wš’œ˜—’™”(ÖËÊÏÌÓ š¤®	2]S}ò6ùÍ‹XKUQÜ� �_¤� 2L0‰¬ÉÎìN ¯Qv¯ì
€kx¬ SÿÉ´°ùD`${dir}/../../schema.sql`, // from dist/
    `${dir}/../schema.sql`,“B+`${dir}/../schema.sql`,“ˆ3`${dir}/schema.sql`,³Ö*`“.${candidates.join(', ')})`³Nû[8·ÅxœÕXmOã8þÎ¯˜“Vj¢Ë8>œÔn‹àèjP)w:!TÜÖ€Eštmg¡@ÿûMl'qÜ´ÇËê¤û„kOÆ3Ï¼<cØt–p	Ï ¾G°€žL¡1áìé)¢Ÿ>m´6X.“8a“BJÿ´&£â0Üdñ„>âé}TÇ,–”ß�1…^I6#\^Ì¢„Lày UËÃI„ä,¾máÞ(ßSYÝ;Gtno°˜IF$�ìÉ&àBII"SQ+¶_ùzœ U±Ìg4ß†ˆÓ(jm,Ö™~F2�3Åh^œNG”·V¸‘‰Ÿ(	[RÒˆÞr2ýÊ"Z•·O.bö=uÎ…L8¹¥=*þ9¬Ü/ØÝŸK*ìM*ÉmÅiN«€•ž"B7Lm b�Á«	Mà†&¨Ç<¨b¾ë€løM8Ãb‚~Ñ'hwÐÚ´`´FçŸ·ó›Ÿ9E“˜�!}¤ãTÒÌÊÊ]ê7Àn–é×‡'çÝþ O§0Íýjå<½²I`<UK±3¼§ó ôk8Â_æŠ¡Ä;|øsïø¢{Þ§çÜÒE Ÿžs¼Ô”ZY©ß–¹ÿÚXÜü¹ÄFÿ: Â›S™ò¸ˆ&QËÉ¢ì +sÈ-+îî7:5œ<àT êóêŸAvÞ=îþ1€×BCd`G5¨ðµÚ«Áü¯oÝ~·ÔŽ¶X€ÂÞÉ�Ñ†g<¹E«ECcè#�Æ÷qògË>'|b’>ÈO:—Wª{Ý€§=#ßÊ;h·Û°åçQÐ«ÀoÔÒ—[W¿XÁzVÐ”Ðó°´M(KÔ.p¨¹B¦ây¨Át�+­9¦ªÛx<´!/¿ñ�NÓºQ©Fo•Ò¬©4�[æMD*¬„¯P`2É‡—¼Û ,jwœLg]Ó ×$ïlIEª®NÍ‹³ƒ½A·&©Î»+mrK&�õÙ–—å’+d„ëû±¹	ß~oÂ×#1&
IÍ±À1Ëp	Xhƒn q"A{ÂÍWä' mŠØ˜Éh®µ�èMÂ)z3!2Œ¼£Æ5ã|X‡^œ¾Ê-I}ËzˆÖ°Ä"¢ }<X,¨5EØ¼šm6átÊä—ŠP€-/}Á”ÈÉ»Ñ©°å+¯žF4~6‰¨m=BÅD2¼Á‘DWwR5¥è¶ª‡“áTO'z‡$œJ5‹ø†ØÀb¢ì¾°BGj§œ�Ê½êäT¿ŸÏMå©;5Y'ùÌTneV–d·Åˆ‰jÅÛˆ-ûäòêýŒ–�úÅ*È§EìÕEØÞ^}pÚ?èöaÿoÛØw¡!2Ã…S2ó<îg8zšÛ˜",ÍT‘×sž=¨óÐ2ÔÕæÎî<tQwU×ÏôîwEl–ˆviäçárô\3­Ç€v™±Œ¯á[ý8à*åÝk­‡‚EßeB8ä½ðM±è„Æèô“‡A²L?¼"ÌX.Kò&º¯ˆá+¦–µ3Ë;&–õóJeZ©(Íë=Ò¼w Yøk{•FTìÏ÷8ÿú¤›’GóIÓÊ,„­Gø}V&«_oÏ&^b) XÉÀÄ€§ñ8C¢	£$‰(‰[ˆø£<Z¡N¿Œ²$zÐ#ò.œ²ØÓòè•VgÀlomm°íjå·^Ýp7+/Ç<*ìà½t©›Ø,ÝYË|nÛ�µf3MqoõsCédïà—…[ëŠ¢=×y`Éö¨Dƒÿ+l/ª¯Ôÿ:?ƒ·™óïQ<ØB�s2õ0a•v_QÜŠFê¯xÙ	óP²K¬úxìhíZÎ)ºZIÌ}£;$Òû¼íï†ª�æ�šÎƒêô3Ôrï2Ž¬wxœ}RÍnÓ@V~¨Å‰—é¥¶•4�¶HU‚+U¥¨?	�Â�È±ÇÅÔ±�õ¦MZEé‰Ÿjx.< GÄx }.œàÀ®Ýˆ€D»öÌ|ß|³3óñòé•–ß‹#Æá°_>Š¬ÐCf…	2~/r1˜u?Â �ÌcðXÔÍeþÑQ€ËëiõÍc5ôû´B‡§ŒS'
ž`&Ä{m» ®h©O«ˆßcqèiî:½Ì­Ñ‡Ü:ýÈmÓzþ.}¾ø�¾æËôSXØ",”éUa›¾é{Á¤rñ	�¯Ñ»â
}ßKÊÎ"@wàì#·Ü:pr]ËìŽïj†T£¾r•^(›ôI¹A¿”5ª––¨¤šyz]2' c	Ó¹¬Ô stY @­;+uh1<À�C»¹Õnî³„(”uŒÉHk·g'o…7‰Áv¸€ ‡Q¸,Zˆ]cš2ê>­…è²ÒIZj’ÕÎ>ŽˆmŸUSp–¥™â-wX‡ÁŸŽëZídéD†¡–I T£0{LuÚ1ÚÔNVï‰)ìð2Ô—°Žö“[Y­ðì A#mßXÜFC}�Îòõ|Êé¦Ü3þwanÊ`äe;°Ñø‹rgY3Û7�%)-[Xî|5‰ù�bJŸ¯:eÿ„ecà�´Zxœû¬³S{‚OiIfN±~N~zzjÑDÍI›åíx7¯á;Î Ò›:oŽÖ;xœ{"uDjC(3SJÒæræ·œ 1_Ðh€…zxœëbÌœ 0ÑÑ £Àªxœ31 …¤ü†æ	×~º\|µ$ÚsÃÜ”-Ú­LÀ²%%ºv?O>�}÷óÊ=«¶ëN{°<"]lÌðÁ·]Ñ"ã¸ÊÕ‰Ë|ï
›�>Ì÷ zÖ%å¬xœ31 …äü¼’¢üœœÔ¢bÖÇ&S›žê=(òãÆDõ~5£Iþm&`U¹™))9©å‰E©ÂA\gÈæ•²¦9¹+ÚÙ¸úäqˆ¢¢üÒ’Ôbýý_9Öëžphÿû,ãKÈ…¢ÿ¾v Ë�,íƒ‰xxœ{oùÊrBÈÄ%®“³§NV�VW¨UH+ÊÏUP×ÓÓ‡ Ò’ÌœbýäŒÒ¼ìÔÝâ’ü¢ÄôÔÉ2ŒþC‚&1)lVgzÍ»YQà (š|éWxœ{eùÊrBÈäýŒAÃœ'W1ºNœùx³ ÓM) ˜ðãŒðaxœ{¼�ùôVæŒ
)I:
i™9©Å
‰Å`FprFjnâÄý¢¬™y)©›c÷4mNh	æ�œÐâµÙeR ùæ;'ÛsÅ%â€Ý;xœkÈº‘9¡]#-3/Å-3'Õ©Ò#±8C¡V!­(?WA]OO‚R’ôÓ€ÒÅ›õ-93sò‹Jª&÷ËKcª.-ÉÌ)ÖOÎ(ÍËNMÑ-.É/JLOU·æBè“×eÓQ(©,H�ø\Q¢¾´ '?1Å)±$9#µhs2Ó{I6ˆ£6'ËÈ2oî—¿Ê¸Y^O•qót¥“l0©BÍ}L@Wto>©ã’~È „Hpå‹íbxœ;^z¼tC:ãä&»Í	ŒŒŒ›—1)Z  cØ–ë#xœ;^ú­dC&£xJ’ŽBZfNj±Bb1˜œœ‘š›8q¿(kf^JjÅæXFÙÍäîñM¾ çµÙ^¯ƒ ½Nê¬xœ340031QH,-ÉÐ+)f˜ö¶ÁéõŒ-.±
U¢Ÿ*Ö­“¹õqƒ!DIQbIªnNfnf	H¡–yáLncãF¶½9«r¦óè KË1´	xœK­(È/*Q¨æRPHÎHMÎJ,IõÉÌÍ,Ñ‰ä¤&æ•ÀÅœ�j`E˜Âé©%pÁà’Ä’b�`yfI’©µ
iEù¹
êzzúTZ’™S¬_S¡nÍ Åæ1�¤xœ340031QÈÌKI­Ð+)føÎø*¾;´ÕŽ½&¦Ò³Èn~ÆK Ù¼)¦xœ340031QH,-ÉÐ+)føxëñŒþŒu9K~ž÷Ú>{Â«¦í†%©‰)©EÅ USÕ¯æØÐÿ:ýàþeìKîˆ}þÏU•Ÿ”•š\¢[\R”š˜R›å*üÄçè”™µws¹u<r¿L(|;ª¶(1/=¤ÆJ^Ýë'›äü>ÖËÂ†—¦§ÍÌó—‡ª)Ë,*)MÌÑÍÈ/.)}×³i�Âßÿ<z�×>/*aÿ£Æ~a	TiEnHE¶nìNþ™©Ç¦Ç>»YõCîžþñPa Ù«^Ìã€Uxœ3 Ìÿææ�5{gH-DéŸ‰6p ÕÜa'Ò‘I‰4kþ?T$Ý}«¿&(ß7¶*þ¶h½xœ}Ž±Â0D÷|…·.U;”�‘‰ZÑ=P7X
IêØˆªâß)b`@Bºåî¤{‡�Y@æ„°@G®ß´˜ÕK	=2�ó�1“8œØïCR�'ŒoPTU½J…|®sS[•k±5ø\ÀYÉ;b ‹õGEž;a
®\KÊ]Óâ¤˜åmï?°oº¾
V”±4ÿÙ/Frº
xœE�M
ƒ0„÷9ÅÛ	¡MYvÓÒ
î„ä ôù#‰Ð"Þ½…Â,†¾N)
¯ø‰!x§à Ì£öÁ›Q[˜QO˜2-:ôE<«ÜŽÞ]˜‹Õrrn¬@Çh¿Jt{©‰Ã³—jèÚÇ«•j�ò¿ßŽç†1^µc3Ï‚ŸßÍ�ü B4Û·xœK­(È/*Q¨VH­()JL.	6v*MÎN-q+ÊÏõÈ/.Q¨UH2Ôõôô�¨´$3§X¿ØX¿,³¨¤41G7¨FÝš ¡aÐµxœu‘Ok!Åïû)¼–v…æØKÉ¶·„”@C®Æ�¤Óª#:B–Òï^ÝlÚü¡àAß¼ßÓ‡²®+Q‹ÜÃÁS`±d¿ƒÐÊ‘C­ŒØ,æ­7`Á±b$×d¦`O–ºd oe5ò_•Û¤?�×bö¢Û·äv¸Oa`7ÖÜe÷DK%–áU.¢Ï‹d}ß¼!Õ�ˆ&ß/· y1;F¹ƒ‚\Hè�Qý—c0òlxâô'Çeýpc½Ê>gJ�Ó9;"´×½fÔõ¿Óç¡À±Y<Mâô%
ùZO.Â™4?jÒ42¯Äh¢ŒSy°fòXý Q¢p¤xœ31 …Ô¢¢ü¢b†µ+'ÝeSt,Ûô,iÁ¿š;W¿I­7+ÈÉOOO-bÈŸ{èHTåAÇ™¢:>Þ¤µNn¿®Q�›ZR”™\ÌðsõÞGA©lAY/æt&±´…®‹L0ƒ¨(-ÉÌ)fàÖ»+ó_yÕ¤¢äW?§–�ç%	 8©6Â¤xœ340031QÈÌKI­Ð+)fhšL¨jÉ›+=ñï½ý–öÊhLû  ð”U·xœËÌ-È/*QˆÏÉOOO-RH+ÊÏUP×ÓÓ¢Ò’Ìœb}ˆ„º5WjXeJjZbi\\¼¤² U¡ZÁbN-Ô¤òÌ¼â’ü<„öj¸U‰Å
PV­5 ­/Š¤xœ340031QÈÌKI­Ð+)fPh®-jp=ûw�Û„“ž—7º?? è±íºxœK­(È/*Q¨VðM-)ÊL.vÎÏÉIM.É/ÒQÈEQ¨UH+ÊÏUP×ÓÓ¢Ò’Ìœb}¨"uk. 
³Wªxœ340031QHËÌIÕ+)fxöÛ©2cÍõÃºÏŠ˜ëô•…%Ó§B”d€l•ñÏì;âþ7±KÉ†aÞƒŸ"Ê·C¥–U‚ÔT\\¶‰áxç†®÷Uîà�US1å-�Æ~·WÇæúYˆéÆú½éñ©c} €7™á‰†xœëJ;œ<Á‰_OO¿´$3§X?'?==µh£ŸÛæ¯l‚¬“h±N^ÍÍ79Ÿ‡ƒ½(µ¤´(Oar#�¬‚‚½‚zb^fnbIf~žº‚•‚zAF~I¾º5—‚Bfš‚ÆdFn‰ÍA¼;TmGR® xœ340031QH,-ÉÐ+)fšõŸÏä7ó“o&D¼®u‘<©nQ’œ˜œ‘
Rã{©ëú�„+©B=y®­·©°:ÂÔd”æe§¦è—ä%¦ƒU›Ù3¯0?·ªpÇ×þSÚ§®kV±¾U�–™V²sŽÏï§êXæ,—`ñ5Mžx¤ª$³ lã´Ïá‹×L[Äså]c¬úGÓô´õ—¡
ròÓÓS‹@ŠÂÓŸ«ÿ÷1,äþ•Mê–]³¤¼Å ŠrSKŠ2“‹Aªþx
Øù«Ùó>Ü"Òö-ó­»ñ5Æ¨ª¢Ä’TŸÌÜÌ�ºç©%5¶÷™*þˆnøu=êÝë	ÚB0u@Ó*Aj.¯ŸüzÆ±žŠ)EŸ™<\øß61 …bc†5AM+æí:xïÚééþÓÿ;¼Æ®	j@IjNjzQb.ÈŒýû®|X™±/ôëœ½+ÎV³?óMY`ij)8œòÔ<ÏåvtO~(:+Ã¸_áÅ¤s5{ jKròSœK€q�SõÜ–ü<U¡Yn×bü˜ãŸ¼üQ
U[	Ô5–æ�O÷mý=û	ßÏ¶r5-“ ö ×Éà‚BxœP ¯ÿŽ»�a±Ýb¶ &!¡x/NFáU~Ë…$Ó‘uä~�7æ¾2Ï:¸(/Ëÿ©¢™}“ÀCã\ç¦†Õ¤§,�R ¬5z\“ì"u²#ç_xœ7 Èÿ»»�aÜãÇGß£¶@œ;Ù¨!àU•º‘uä^ýŒôL}*U(¾…v8»g“mNÐº¼bx,Óù// Simple in-memory cache with TTL support
interface CacheEntry<T> {
  value: T;
  expiresAt: number;
}

class Cache<T> {
  private store = new Map<string, CacheEntry<T>>();
  private ttlMs: number;

  constructor(ttlSeconds: number = 3600) {
    this.ttlMs = ttlSeconds * 1000;
  }

  set(key: string, value: T): void {
    this.store.set(key, {
      value,
      expiresAt: Date.now() + this.ttlMs,
    });
  }

  get(key: string): T | null {
    const entry = this.store.get(key);
    if (!entry) return null;

    if (Date.now() > entry.expiresAt) {
      this.store.delete(key);
      return null;
    }

    return entry.value;
  }

  has(key: string): boolean {
    return this.get(key) !== null;
  }

  delete(key: string): void {
    this.store.delete(key);
  }

  clear(): void {
    this.store.clear();
  }

  size(): number {
    return this.store.size;
  }

  // Cleanup expired entries
  cleanup(): number {
    let removed = 0;
    const now = Date.now();

    for (const [key, entry] of this.store.entries()) {
      if (now > entry.expiresAt) {
        this.store.delete(key);
        removed++;
      }
    }

    return removed;
  }
}

// File info cache (1 hour TTL)
export const fileInfoCache = new Cache<{
  file_size: number;
  mime_type: string;
  file_path: string;
  bot_token: string;
}>(3600);

// Cleanup expired cache entries every 5 minutes
setInterval(
  () => {
    const removed = fileInfoCache.cleanup();
    if (removed > 0) {
      console.log(`Cleaned up ${removed} expired cache entries`);
    }
  },
  5 * 60 * 1000,
);

export { Cache };
­£çHê‹9x åÿ¬¾
°,export { Cache };
yž‡ê½Ž‰xœmV]l#WVÓf³x«tÃÆNbÇö±Ãffb{Lù«p~Äf—… Z¢ÍVT•:ž¹¶G;ž;Ì½“à]"#�@Ú"Êêô¥< @â¯íK_à* ¨H¨j%$„HˆÄ#ÚÎ�ÛÙn¤hÆ÷Þ9çûÎùÎ¹çÍ¯ünwË<”pBËwøðÙg®Á)ôB>ÍçëØá(�\ÛÎ}ÿöø8Ý&ô\�	°DürdØÐš|bšm§K‡'f]_°P^§“‡V(E<WÌþ”£€Á3ì$[:ðƒHž5×V~Z�ÎIq/(,ŸÝ1 ¼ÿHeŸùÇ³®»\rîÍsý^h	F¶ŒBÖ–ÌcýÐ¶éh+ ³ø›¹Ú|ìaîŠ¹�ÇÛâãmŠMŸ‘i¼ý¨ñ„Ö¿í|üÈóhíÜcú%'Ÿ‰†]vhY=·ñ‡�•Ñªðâ|»^ÈÖïÍ¯âûó¥Gl¼rîþ~±´¼�`Òêw€ ¹~;wªÌVs7˜ˆ<¢’|©PÊ«s
Ûç-1˜œÅŸ.T°p~sI²aphÉ™�Ÿ¯áßÏ//Ý!»IqžîMY:³JÖ£Àã–ÃÂgÊ£‘«âwsE¼{á½í-Î=Î¾Ç×æ¾�ø—RivI#ßÝUÉë(¼ðP4óö òoá_çóøZ>�ß\¬ÎQ ÿ•¯ÍïîÑWøDa�
Oâ·¾^(â
+ø�Âsãv®rßŽÂ�ù2	‚‘+Zö-A*kõ<·?È6Iž¾CRºRÀ€ŸÀÐòG„ŠÅh?dÐe,ãì€%�œ°D-rà
´ï1ø„
•IŸÇœÉÙõÄ×.øìŽ˜Ü9¤O\ÁvŽ¹ëìíéÆv.‡»+Vnà¯VKS}@cž$Ò÷Wš¸µZÂÏ­î—cHM²¯þâš6!¢™ÃŸ­cc;ÞMü§qPÕB¦¦É©:D¯»Ä.“=	œ7‚¿}ãp¸¯I°N,WQ®Ãˆ,ƒ�Ña�ó`ÆSbâ04cvä,-©ªÙãá‰:7ù‘ä¡Õgzw$™hâ§×.ïÍ <}±	šÃíhHš‘}M^}]Om$R7  Þ]+•fëjÆ Š¾¾¶‡o®-â°zÏøâÆ2^-êè5ü^±…Oéüc1‡ïëc€Ó4„É[¨«³|¢™ä|ª"Òœ<,zÈú‘ºž ÞÙ6½¶{–ëQ1fË1…+‹ß8^0{®oyÞH×'4“„'r2êF’é³6RÐÈ“³–ã<x0ã´O«±v[ÜH>æqÌjÂµ	±( ¸JžÙ!´®P‘ÐV"U-CõCÉˆ\S:D~—G¾* !£ÝôC~"f¸§ºÂ8E-ÜÛö&22ûL~¶×c¶t�Ù¤ÀíÅg>fL"”h5ejk¦g�˜d• Ùe�pí�KKÜ"*@%•ºã$î„‘w†��³,ë‰©3ÎTa§uJ¿àÞ1‹³FÊ¸Ií”G2[nÂG�,O9õŸô™ç²¨’ë#åÐ¢xNÚ�Pè’ç$HÌò£ý�Ñuå§ñâ‹3‡PÒ/KbêQT÷ãÂSîéò oÎ4é_¯?4iÒøîºVÞË8[çêˆ¢š:~¦¼€å
Êü2žÒó•üEüyyÿTnQß^ÃbE›�e‰_¨h(*|©òÔNL>±éS×Mä5‹TG(ÙQÝô@y›Žjßª”?9{¦j( s	o—î¤äÓ¤UQ×smõß©/ã•j…®Ûþ j¨ëß¯Þ§µu¨àl¢CÏoAU|jøoØÀ¥Z	ÍÚ:~‘ž.=¿SÛÄ_Ö ß©Uñ¿µæêe4êÛ¤~Éœ+tá­Ä}8¬�Æ¹JrétÍdòÑ§C’a[^ÄD¼”$vJ6‡:˜„ž�øãI½~>£Ž¿¨_Æ?×�x¶(lTSÂ-¼Gïol4Ò¹âi½öB2S(½˜ê5Æh3(Îô	�8[©êÔâEx¡±¶<½ú§	Ãöf5½Ïÿ³UX=3ïÅÇL×Qé~u³…¿Ý„ÊŒ(üÏD˜õÌkÚ¢àQH-×"1ÐïàÛõÞÕžÇj5bìàõZ#éï§3Õ’~ô�zl­:´LIÄüÕÔ×�êAèKî¨$4_U#Òd\3:YõîÐ¡€¼°´ÄT(T~ªQ¡YCÞX{”`âËm95“\ùñÊïÛ�-ü_ãK—<TŸæŽÛs™ÓIšÞkæ1j^Ä—›+øZSÃ÷š­…¬,µŠ*
D÷ÿæáä2ä/ŸxœuR=KÃP%¶
ÄïX¿_[QŠµ*Bƒ
BA�ÔMÊkó¬“Ø&/ƒVEtpónþ Aèæ¢³¢›‹?@PÜ‹`[›VœÞåžsÏ½çðîÒ|…ƒ(Õ1Öpš 9ª’Â°‚Fy¡¬•Ri:¡Ä‘Éªgdxõõ‚ìG’É6œ!³ë˜9°ni)bÈðâï�¯Ù*‹X#¿C¶�F5²´•­è™t›Ìl1b–ÇŸj; µN‚�hÐ/‹»"¿¨Ë
ü¢¾Æ7W\i*øá¬ý¸™ÙRîµótƒ )”‡}) •–L—·ìÊ"ôuóB`E€Ü`€?tëNÑÆûŽxCçðŒ49''©ÇæN¨îCR«#™dšwÙCÝp9$Ö <‡žL	<9@Öá!z·#ñŸø¼Â¶mFT’1°–¬^êÅ`/Ô.fœLÛ!»ÄbLNvE>ÃR‹+¤{‡ÕX NcRÄ…XUðVVÝÄ
1¼šðë„‰ÑNX;Ùû{¢SÄJm'ø„µ•ªÝX:ÍYÿL,»Xiî�7—ÿÓ�(~«J¦FLÓy«Èö‡uûð2vÐSáÌ%•:	–Çõo3%è‚HxH·þ³7÷6Kimport { unlink } from 'node:fs/promises';
import logger from './logger';

�‚export const cleanupTempFile = async (tempPath: string): Promise<void> => {
  try {
    await unlink(tempPath);
  } catch (err)a {
    logger.warn('Failed to cleanup temp file', { tempPath, error: getErrorMessage(err) });
  }±}Ù“ÎU“:º“¯z“Kh&rbâ‰�Kxœkc[É<Aš-5¯LÝšksc# 4�Z°4x@¿üimport winston from 'winston';

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json(),
  ),
  defaultMeta: { service: 'teleuploader' },
  transports: [
    // Write all logs including error logs to file
    new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
    new winston.transports.File({ filename: 'logs/combined.log' }),
    // Console transport for docker logs / CLI visibility
    new winston.transports.Console({
      format:
        process.env.NODE_ENV !== 'production'
          ? winston.format.combine(winston.format.colorize(), winston.format.simple())
          : winston.format.json(),
    }),
  ],
});

export default logger;
Ù~¥å…Mx êÿÀ¼°filedrop³%P'¦à‹²xœ»$8[p‚4sj^ÙÄUfýÌ‚‰ºìú9ùéé©Eç˜q  ±@
¿ç€¹nxœ7 Èÿé ��
/logger';
‘[‘¯““o“À³z$³µÕ³#³
_úÌðé‚ÜxœÛ­zLyÂ”�ÿ$Ù&rKr*Ø*ÄLæc��|“[h2‡øäùÜ@>�ïäzƒÍ·8™­‚RKS‹K<ó2K€zª'Oã¶ÕËLSÐP,OÌ+)ÏÈÏIH,*ÑT¨æR ‚´Ô’äÿ‚’b½ŒÔÄ”Ô¢bÛÉuÜâ¬Ö@ÙÚÍ¸_³ Ë,îd�±ixœ•TMlEVâ´)›Mêÿ6qH')±×•kc¢–Æ.©(-¢ «MA4ô0ÞÇ#Ö³›ÙÙüÔ2áRq€CÑ» qBâÊn@âÂ.‘(‡ž8 .¨ffíÄN‘‡ÕŽÞ|ßûùÞ{ówíÛÅëë]ä4K¨E] èÃ-»M:õP‹{T(—+N³P7hÇ÷¸@bÏ'¨‹^';/Kì(ªh*TÇ®¶}Hê¢¦'žçã)kqÚ"ä¤"ˆK69îT$ô¼/±p{l>[™¬Ü£¾ôÅñ%ðÇÓ	•ä+8h×�dS¶Y‡‡ã§ÁŠ=Ær™åQ×@Èæâ¼(jèš<Ô¥)ô�QSOúþ+vV'æak"ŸM¤3*Æº,õ †Fí»lÞö];ÄQAà›c+påø™˜Kü1YÐÿ…õyÛc�@Í�ºÎ0½€,¹Âß«¡;Ô¿®N%™æv›nËˆ]Ð‚«SK@RIØ�2uZ¿ŒŸªôJF±†F]®!KUê‡M—Ú7œb˜yÔ±Š%þœ*Á’yÞ6ð¾Y†¯Ìüd®Bf:W¦s'ÃÈW´g%~KÚ?™®Â�Óà©™exN~3y¸?SØ’’‘­�Š1"èðE¯X7ŒHˆ–í(ë@j€ƒ=f#KVÒ�Ã@ry[&¼¦JQ•Ð²4c�v/jBû¶K0W&/Ã÷u}}h�XèºÊÜ3ÔD½ÀÂnË+Ÿ0Gö³ŸŒ�Z$dãn½Ÿ…&•]Â6E>Ž§àóø¹ã²]ªE�â0wb2>¼“x>J\‚ó“ýI‡Ç‰YÈ&óRÆ“ðZò¼™ŒÏ)ET²QJáð$ÝO.ÃÉ‹ð›þL(›	XK-Ë®ÍÂ»©Eø4u¾K¡k=-9BWxS!—¸LY@¸æ`ƒ‹åmì†$°†ãŠ_¦Wà×tâüÈÍ½Û×²§¤C¶VÌ"œ{º	p.“‡›™ÓñèºEv]-Å{™·àçÌ*d³×'”lÐÊ.¤Ÿì Bð`ÎÊ¨^�L„¥$ü={¦ª«Eë�Í&�¨9·è=½<rdXØiª&¯I0|êÕ¼Avõ#qÛ
IHœø˜“þöI*|=»X^œƒqãD¾?L�nafnªs—fU1‹ÿ¿�d/ê�¢ÉÜóÐÈU÷}ü+ý@þ¢~qFkÓàÆÑqý�mŠdç�A 'œŠ¹~É™8ªù‘eé¯FÝø‰Õëˆæ/xœ{›´Ôqƒ0£AjEA~Q‰BIeAªBTf�g^Ai‰[fNª‚­B5—‚BIjnA@bI†•BqIQf^ºõäŒ’“™¤'fQPHÍ+)ªôKÌME(P`™œÇ,5y³Ìd.ùÉq,²VÆ �.Î¬Juª,I-¶RÈ+ÍMJ-	¦­öH,F²¯“UfòGÖ›“·³ŸÝ\ÁéÆ89Ÿ{Ùä}<‹'§ñíœ<‰¿gó{Á�L›{Dy7wJXðlvP;Ë<9BKI¥¸$±¨ÙìœÔ¼t�`"µÖ\›�è¬d L^†â�ÍvxœR ­ÿ»»°Ô€W"ÿ§Ï†:#°öØè°2�KY“è3×¶ú>Õ"¯þrÎ]¾ôGçCŒ“/0Ž)Åèö+±¥µ­÷qÅª=“sH5‹%“j‚­/xœk_-6¡wãÓy\ Ã·ê�ËxœŠ uÿ»»°H\û’ÆŒ3²¬™‡Å@èºI
“&®€W"ÿ§Ï†:#°öØè°2�KY“è3×¶ú>Õ"¯þrÎ]¾ôGçCŒ“/03“YFl ‚(Z@Ô&°ÒV$äô^3k40000 test G8MìR°�›µ îeíÊE^y“’)Rt;½à€¹~xœ[À¼€yƒ£H»>_P`‡ˆöê�ùs§oJ5Þœ2ÙŽ1	 ¯8‹ªxœ31 …¤ü††ú«¤ü™Ðôê?-‰©woMÀ²%%ºv?O>�}÷óÊ=«¶ëN{°<"]lÌðÁ·]Ñ"ã¸ÊÕ‰Ë|ï
›�>Ì÷ À³'.¦xœ340031QÈHÌKÉI-Ò+)fXÎÀí°Àô•o´‰Ëz[%»+¯DÝŒ!ìŽ†jxœ;mµÃjC.ßæB>F¾É2²âözÅ¥IÅ%E™yé:
†šµzzz	“·ÊL U°œé�ÒKxœ9 Æÿ»»°óSNqˆZ9$WÆ¿)®ÌlºlÆ=|“Xê{ÇÀš/ü7÷Xýrø©},óõ~	“sHáléê›ô1xœ;©øFfÃ–ÉY”¹]\<C<ýý}â'aQ3©LÌKWHÉLOL*1RóÒóœüCâCü½]ýJKs4JòKsL’òK47K±®eÜlÄ9� ™Ënå™Ï&xœû.1M|â—ÒÉ]ŒÇ�'1Eh‡øø;ºÄ;ûû9‡¹ú9GÚªTc
ZéZÔn^ÈÈÊh¼ù3û6&&cƒÉ¸l6?çšÀ î\¸ä€Útxœd ›ÿ  �“Ñš…O;]è,x!Ê÷aä…-îîð‘§5°rÑA–ÿôws¨¥gžÓðçÛ:‘ðXK�§À[©D|J#F­"ëWÆ“s…ô·¶þ2^]¨ñeö\Î‡a‹µõ0æêÑHxœÛ©=Os‚�|R~IH~vjž•BqIQf^º5—‚BbJJfIf~^bŽÓÄyO$KròSœóó’K‹ŠRó’+­òJs“R‹6×1Naœü…E‚ÛÉ?$>ÄßÛÕÏJaòf±Íœ¬'9˜ÀlQ5ET#¡[)$§B8“·3+n~Ëû–QÃºÉòüRb¡>þŽ.ñÎþ~Î¡AA®~Î‘:
š›¯ñ¯eñ²RÈM,ÎNM.J-ÑHÎÏKËL×ƒùKd;û¡J‘ä6÷‰h3 [úcá€Xxœ›§¹Gcƒ#ãæ8Æ»<›øäØ <ìþéÒtxœËÌœ*Ò×È÷Ù0Åù~“èã¸Âà‹þº«&t ‹Œ@¡xœ340031QHÊ/Ñ-ÈÏÏÑ+)fø½ôú[Ë©òi-ëWÄÿý6÷¡¾!DYrFi^vjŠnqI~Qbz*Hõâ¶&«Ä{«mÞnºdsÞl—öêÅPÕ%•©Å 5B›fH§)5\ºÛœQ¸ßûøÔ9lE:P5¥9ù‰)ºI‰%É©E ÅŸ8åµÃ¦67ó}ywìâ{]-·9¨ŠKSKÁv§97¶ÙOúÇzdémÛ{«7ˆä úöS„f€>xœ»È¸ŒqÂ2 	R°ªxœ31 …¤ü††ú«¤ü™Ðôê?-‰©woMÀ²%%J†•Üçé›ºÊ<õ¸]i’å{"]lÌðÁ·]Ñ"ã¸ÊÕ‰Ë|ï
›�>Ì÷ I $ ¬xœ31 …äü¼’¢üœœÔ¢b†‡¬/S.²]óNã8Ëóiåÿ‰­MÀªr3SRrRË‹R„ƒ.¸Î�Í*eMsr=V´³qõÉãEEù¥%©Åúû¿r¬×=áÐþ÷YÆ—�Eÿ}í Š-&ä�‡*xœ4 Ëÿìì�÷ÝgAUçzVG¢y Ê?øÒ¶‡+N“M·Iâz³ì2Ñ¯€÷ÃÊqkWl~ˆïDxœ{Ãô†iÂw‘sºÚO�nçÍºþù”ŽÊI÷8Ë˜ûe“¹ÿRäk�©xœ»Á¹œsƒËäd–R Öçà‚‹Hxœ›&öWtÂ»É›˜46‹3j2i*gä—æ¤(¥–”å)8ù‡Ä‡ø{»ú)¤åç*å'§ë¥æ•MVd›,ÏÆ>9�“còava>„ÒšõÉ
Ì›ÿ3÷³p'¦¤d–dæç%æ8MîçˆFá¿æ6R@ˆ(”äg§æOžÈ&Œ¢.‘SƒÑhsg# ÀF;û¨xœ340031Q(N-)-ÐMÍ+Ó+)fhœa�õƒ§_8ÞýÆOm&Nß(½ý öáä
‚½:xœ{ÂÙÃ¾á+£ˆBM�­‚º¡‘±‰©™•£“³®‹«Ûd¦ÏõŽ))
¹ùÉÙ
‰))™%™ùy‰9
Iù%
%ùÙ©yÅ
Åù
¹¥9%™º ±¢ü’D�…œüôÌd…Ìb…’Ôâ’Ô â|®‚¢üäÔâb½Ô¼2=GÏO?GŸx'ÿ�øoW¿`ˆÌ-,�¬Ü=<u½¼}t&K1	 Ý5Ñk�›yxœ›£1CcÃ/áÉÿ„å |à�ßxœ  ßÿ»»°_ê{ÇÀš/ü7÷Xýrø©},óõ~	“sH³U¦xœ31 …âÒ‚Ô¢‚üòÔ¢b†ðà‡Wžï:R´çãé¿_{-W‹ 2Å� xœ31 …‚œÄ¼bÁ�óÿ°ý5à>¨¸ïO§ö‚'°|qAjr1ƒãK{VïÞ`céÅËùß7n*o2³  ëÕœg™Ð/xœëdicÞÐÆ 	¥Näxœkc¾Ä¼¡�ÑÇÐÀÀÌÄDÁÈÀÈL×À\×ÈB7%%E79'51O7±(9#³$5¹¤´(U·(µ¸¤¨ÌÖËMaP`™öç–hcã®ÏÛã‹,—6Ë¬7 sr`ç€çHxœ7 Èÿ  �Ü°rÑA–ÿôws¨¥gžÓðçÛ:‘ðXK�§À[©D|J#F­"ëWÆ“‡÷ÚžçÆIxœ7 Èÿ»»�aJoõ}èI«tá,¬ôå†€Bðœ·‘uä^ýŒôL}*U(¾…v8»g“mNò	å¹	xœ;m{ÈvƒŸ€Dr~^Zfº^iAN~bŠs~^riQQj^råæ|�> ÷~ï�ãwxœÛÍº›uC<“±Äé„Û}³NM8±á�ˆk	›iÓþS7M€@¡$µ¸„á«æõ¦¯Ýg‰xWfma6ú=iò$&M yi^éÞjxœËÌœ*"SsÇã�ZÎ‘IÁï$¬6�?Ùç ‘Nô¸±xœ�VMsÛ6½ëWì!3¢[r¹(•Ç®+g|p¢LíioD®(Ô$  >êÑï. RT$;mgt‰�}ïíÛ]Êr©�ƒÉ×
+„¹Ñ%t—ñS÷cG†ÕHµšËvqG¿? ªÕ~O¡óÍÁº]ƒÙ ¬¤ÊpCû;ƒ³³œÁƒØÈ²*AUåŒê9,QeRå�øð¼©.æ…Ì®Õ²Ð"'ì³…ÎµA¾Eál5+¥µR+þ‰©Ã¬ƒ+TÎB¥fº" ”Xj³…Üèµ[Àz�Š¯yÄs#J�l¡× �+!1+°O{Á:x¸ùãÛ×§ñÓøÛdüù×ûÏŸ`?]^^î™M‚~„Þ:¡R’•n³h¤(¤e‚M´6)ŽÁ§oµJ+cP¥[F“ëEÀg[x¹.¤zŽÉè‡Ã­í;¾~©dá.¤òáhXJR`-Œ¢'ë)ƒ[ �,ºÕ*ƒM¤ÃÒ¶i‡hÁ&#/{ðLòÒFTƒ¾
ï¼³ëy‘àA+éH“=J… øu�”e+„Éf"}&:–á´€ôµJº"Ëºç�ô`tÁ%å‰ôrt~ëoüœP| 9‡Ä/÷­üá
>ôüAˆî3€¤ûr0ÎHT¯Kµ¤`a7ÔbC´~|<�«|{½Äÿë÷¥Ø�]Vwá.(õ=S…÷ß¨FBÎª<éNŒNÑzòU1¥Ìç5&G<À˜FïÖ±
	Õ638M–Ü`Z¹àÝÆqµÏ}%s½6ÛÈ´w÷w_¨ø24ç\×\È|Ÿn™
IN©oº^
.&øn»U)Ì+•:j
Ô–-õfÎ7E¤z‰Fð_7×]e¨…ÜÀ’š˜´H{…£ÓV+¤Â‘äR>Î�º–ªÂ…Á�o‚!!4‰ÁÏ�W	ŸÆÔMÂõôº7l=ü×Õú}d®Úà�b³e}É5=6F›úÀô æUQ@òîåõØ;hœC.‡w/GHv½i]½ð':¾×v=Õ¶WŽM·÷Ü'$¥½\DÆ¤u2mºè>›
t°‘Ïß4œB"È+,¶Ñô²ç›Ð”™ÐúZÈð2¥Ñáì‰Dä†r•P>[µ&ÛÇXDqÎí8Í¾i6ûÚŒ[=$k/†.Ò.?–‚-¹¢¾qCÄÎµoõ‰Ò¶è½e¨ÞzGb=\ÇpkœfÏ¦äãYbø=ìCŽ¥|Ì�¡/C·¢1þ6Êæ«¡õ±ðäIØ~Ë9}u¼wü¶@a@PIïÖÛÏ«hE=ƒZ×\*iÇ¾|­ËŽgú�YAýuîbÊ(ò	j~©Ï¡&­ö²Ò2k:L›¦?úŠð,§Î=OËï÷šë¥o÷©.1€MX¤ð¥ØŸòÚ›(Ïó|¨ånË{BF§ÿ­? û%«xµGxuŠûimport { describe, expect, it } from 'bun:test';
import { enqueueUpload } from '../src/utils/telegramQueue';

describe('Telegram Queue', () => {
  it('should process tasks in order and limit concurrency', async () => {
    let activeTasks = 0;
    let maxActiveTasks = 0;
    const executionOrder: number[] = [];

    const createTask = (id: number, delayMs: number) => {
      return async () => {
        activeTasks++;
        if (activeTasks > maxActiveTasks) {
          maxActiveTasks = activeTasks;
        }

        await new Promise((resolve) => setTimeout(resolve, delayMs));

        executionOrder.push(id);
        activeTasks--;
        return id;
      };
    };

    // Enqueue 4 tasks with delays
    const promises = [
      enqueueUpload(createTask(1, 50)),
      enqueueUpload(createTask(2, 20)),
      enqueueUpload(createTask(3, 10)),
      enqueueUpload(createTask(4, 5)),
    ];

    const results = await Promise.all(promises);

    expect(results).toEqual([1, 2, 3, 4]);
    // Concurrency limit is 2, so maximum active tasks at any time should be <= 2
    expect(maxActiveTasks).toBeLessThanOrEqual(2);
  });
});
G—`€ãˆxœûÊ1…cB¸Bf^ZQbqIQirIiQª~IjNjzQb®~iAN~bŠnáÄ4O±‚Ä¢ÄœœÔ…òÌ’ŒüÒ…œÌÜÌ’‰§Ü6[2J3N.e2Ÿ|ž)–Ñt².³:«©�¦¦Îä‡L
¬&:
¦“k™[�‹RsóËRStŠó“K2ËRJ‹³‹Š�Æå¤($¥*$&å óŠR“3L4ÒJsr*`VkN6gQà×0Ñ´æRP¨’  ½qAFà�÷~xœÛÍº›uC<“ˆ‹/ÛÍŒGr¦Ïöœ{ò'.GÒjJøäb& Þ–Ëà€ý;xœ  ßÿ  °Ç_Ç¸>?Ÿr�ë31ß›(ÊÌäN“‡ÖÑÓéóxœËÌœ*²Íý�Š×>±þbÙCº¾}†ÉÇg8è ˆ£
QïŸxœ»Èx‘QÝÐÀÀÌÄD!)¿D· ??G¯¤˜aOã†Ç�vÿnÈVna\\¢°L¶}¢ú* ‹Šÿâƒ¼;xœëhñÜ°—eò>nñÍŸXJ˜7Oáèf�lÇ§Ìdh0¹ˆÏHm>È·��Í)¿D¡<qòFa™Äœ¢ÔÄ”J…ÜÄ¢ìÔ…’¢J…¼ÔŠ…¤ü’ÍäÕÙ&sK«LîÚÌ¡ÎÌ ª ¹j�Ç-xœ»Á¹†sBßÆ]r, ÂBï�ÿ2xœo �ÿ»»�_'Q�È@žYÑÜ…•—0?ß˜,¼±s3ÿCè¯["äã»²¯¡†ß¹‘Qs¯“º‡2gkê³?4K~�Æ{�zÄ@Ì�S40000 src f½ÔEf¼ •hí`!$æw�dk“sH0Ö1–éxœ9 Æÿ»»°ó,}§±\0gï�?üüÑ.ð¾Â@“X±­zB1Y·Ö×)Oy>®�#Îå?“sHó¤˜à��xœ[À¼€y+£ˆóƒøÛöÑ›óïL¸’ª«9/ÃùÚdIÆv ÌåÓéö^xœËÌœ*Ra²ú¿xŸÁƒ•[–TVIÖ‹  ‡‰ï¢jxœ»Èx‘QÝÐÀÀÌÄD!)¿D· ??G¯¤˜Á!.3n“Dã‹Bræë‚~GR6˜¨¾
 rïÛèƒÝExœH ·ÿ’VÕQ°v³¦.executed with³E“©A“ìJ“8?“L““˜3“ÍX“'U³Š�“—*{Ååçå�†	xœ5 Êÿ  °*™Mƒð¿D©’­Ájui ù(Ñ6Z‡“>N0Úü°Oº2=Jñ¶«Àq…·sÚÏ‡mÌ£xœ31 …Ô¼’Ì’ÌÔb†"·'X¼$ä¥¬Þ?{÷�eÁÊ°’‚ü¢’b†IÙeüŒ[d[·êWÍ;jÑËùØ¶ ›¥í�€!xœ âÿŠi� ‘A5–íP§�Êã»>¥Î+ƒá�¾•!æÄÔzï¥:xœ»Èx‘QÝÐÀÀÌÄD!)¿D· ??G¯¤˜á®g‹|÷uÆðsûªWs­½\õ÷uõDõU ‡°:¦xœ31 …âÒ‚Ô¢‚üòÔ¢b†´i[U¼êgZäž>½,9£DÚèI. Y xœ31 …‚œÄ¼bÁ�óÿ°ý5à>¨¸ïO§ö‚'°|qAjr1Ã»û²öVÇÊe‡Ì^;ÛÉøSÀ vòg•†
xœÛÀ²ŒyÃ2F –µäxœ[Æü‰yÃ2FC3##3]s]#Ý””ÝäœÔÄ<ÝÄ¢äŒÌ’Ôä’Ò¢TÝ¢Ôâ’¢R0[/7…!^ ¿F¬êÃç‡‡.¿çjœÉ-³è. ˜{„é‚�Pxœ9 Æÿ»»°óÑU H{&÷m*¤‚ÎúÇ^H1i–“XÔYG] b‹�áEAá¼|þtÕ“sHÍðëå�Š0xœ5 Êÿ  °*™Mƒð¿D©’­Ájui ù(Ñ6Z‡“>NÀïrÌ¢ym1oØG¬5íhŸér„`áçæ'xœ7 Èÿ»»�atõÓ, È*4Øb&)·œMÑ*’î‘uä^ýŒôL}*U(¾…v8»g“mN•ý¸lž^xœ;d»ÇfÃ4�Í2‚Ëx$¬áà‡wxœÛÍº›uC<“È�§!ç–Î“Jg‘ÚòŠ÷jèìÅ
z“‹™< åPtì€Oxœ[À¼€yC£Hæ©Õ:W¿LVT\˜±!¶ÆkylÊ ­–¥àçZxœÛÍ¼›yC$£HÜßž/¢>µZ¡¼ûx[Ë¸-v§OÎeô ÉÍ•áŸxxœ;d{ÈtCãäÝŒË&3ÚoNdêaß¼ˆk"£[Z~QybQJPjqiN‰‚­Bbybf‰BR~I@~~ŽT6$?¸$¿(1=UƒK’*KR‹u œ•êÉýB“ßópz$•ø•æ&¥Õ&ÀäÕSò“KsSóJÔ!"šÖ\`¤¶X¯ ´8C£ª¡]‡kr¯Õä�Lb“Ûx¥&ßã˜ü‰×b²ŸòäP>ÍÉM|2“ñÉMf’Ú\%èÁ w]Gêä€Fxœ;dzÈtCãdm&³ÉÅŒë7'2%J  UWá‚‹xœ! Þÿ»»°¦ÿCè¯["äã»²¯¡†ß¹‘Qs¯³º$�×à1xœÛÍº›uƒ#“{zö+¡Íö&ÞuŽU÷Wq8Ó<9”) ÍŒ(éœì*xœ[Éñ�}ÃE¦É,Ì’F–æ†¦F›U™Yggå’ÄOxœ»%wTnÃWN.Mk®ÉXd6kq° W=¥ïšÔOx/Ðþ¤MˆH°·“ú@D   - For CI: Gitea Actions secrets injected as environment variables³8#in CI“‰V“ï#+ to SSH private key file
#
# Local defaults“‰ 45.127.35.244“¸root“ï~/.ssh/id_ed25519³.„³Ý?“c
¶ALocal defaults for this project. CI-provided environment variable“Ew³U(“ÌÒ“É`³}F“ž+³…Egîà‚ŽxxœÛÍº›uC<“HájÞœ�O~ð]I“ÉøÂsßãáK¶œÉÅL ìä‡å�’Oxœ5 Êÿ  °*†ù~!~I5ÂŒ8}‡<‰×U“>Nµpì’;uéuBÄHËÛVn<Ýîë7¡ªxœ31 …¤ü††ú«¤ü™Ðôê?-‰©woMÀ²%%Ì÷g?^>G#vY2ß¿¬­U¾?z[Ò!ÒÅÆ|Û-2Ž«\�è°Ì÷®°ÙéÃ|� ²&†¬xœ31 …äü¼’¢üœœÔ¢b)­‚k&ÅNŽæ7.¼<ÝûdƒQøëÛ&`U¹™))9©å‰E©ÂA\gÈæ•²¦9¹+ÚÙ¸úäqˆ¢¢üÒ’Ôbýý_9Öëžphÿû,ãKÈ…¢ÿ¾v â-"ï¯:xœ àÿìì�÷°¬¨ŽUTâ"./Úïéã�Á‰ö“aÿ®†â�ö!xœ{¼�¹+ó†;Ö“£û…7ÙÔ1ò*—$–”[)˜èLVŒWfM-IL×™œï ?Y1Öpr©�Îä×ñŠLµš“/ÛEO¾c�6Y2î5†6Umñ@-Ê@-›»$I¶‹«VS¡VÓšKA¡v²u”ÁäW±:¤Û{€qóÍÄ®J ¬úUdëƒ§=xœ{ÆøŒQÙÐÀÀÌÄD!±´$C¯¤˜áá¿§ªÏ¦Ù|5uN1Ëliu7Ô™¨| dPì‹èFxœ=�M/A†ï¸Ì²q ”DbW˜_Árò¹$+’i3µ«e¦{U÷ˆ�'÷ö#%ûø®.âèHtÏ†Ó[U©~ûyë¥ýsù\÷Ìgqà¦V�Ž1’1ÆÐdúT O"&¤àK€ð<C¥{ ‚ öEÒ‚Ð�‰Øª-6RZ#´»©¼pVHPÛZ‡¹å¥E»i.,ø³þ|×f3mê–{jž
}Ça„ 	41žpÑ€0Ë¹_“P!9Ç�ÛGÇÕrã®G“‘YÏikÕœÈÕ{ xC(×¥¾yð¦Ì·wß[†H
ÍE†ó^˜1Ãý•“]iãþ}¢ s¬:'¥ÿrA©N2…YìÈË•i™2ín“´,
¡ÎHXQY¢ý3ëXš&ÊÐnƒÕŽ7ðZ4o#ƒfhrfS&è#‘¤RxPÝ^�ŒøÚøÕÿ­‰_çìÛ±¨0ö»ýŽ@R×a¹bîFVMut¬s4õåýr¶Ÿ?à�wxœû[±¾bÃ;ÍÉ^æ¡›'j½ MÊ8à‚–<xœ  ßÿ»»°_¾.Óñ Uè�pŠúÎ9õ±¾)Ç“sHëôøå�šxœ5 Êÿ  °* ½kÃ|ê+p)€©Ÿø~õE•·“>N¦vD£_–ö~în¡²¥¦‰�ã4øuÿ–ìExœ[À¼€yC£ßÖ‚7“¬K_–:ñ8};,Ïæî»× ¯xªxœ31 …¤ü††ú«¤ü™Ðôê?-‰©woMÀ²%%9/ï6Ìå^¿åÝ‹Ðta¦Ó�v¯+!ÒÅÆ|Û-2Ž«\�è°Ì÷®°ÙéÃ|� ´Ñ&U¬xœ31 …äü¼’¢üœœÔ¢b†¢ØžÍÖñw’\”?û©füt)ÛZaV•›™’’“ZžX”Ê tÁu†lnP)kš“ë±¢��«O‡(*Ê/-I-f`Ðßÿ•c½î	‡ö¿Ï2¾„\(úïk ª¾+¼ï·(xœ{Ãô†iÂw‘ª™+LŽÎ�4¯ZœP•¿ãYvVcÈdnÆD õÁdè†lxœëßÊ¼~=ó)�Ì4�‚Ä’Œ¼ÄÜTE[[u}uM…äü¼âüœT½Ô¢¢ü"õ`ãø ×@+um…ÜÔ’Œü C]Ä…iÕÜ,&’¬¹ÙÑnãæãêÒ“íÔ'ˆÜü.¦$r€q3kbW% ((Ønzxœ[¿žùñ:æR"›ÓD< 0¥à‚šfxœÛÍº›uC<“�à±«§¦u²Ok3N¨¼eû¾ir1“ Ö‰mªxœ31 …¤ü††ú«¤ü™Ðôê?-‰©woMÀ²%%\+X‹=]%.M]ñéÇÇ(­ÙaW\ ÒÅÆ|Û-2Ž«\�è°Ì÷®°ÙéÃ|� |t%Z¬xœ31 …äü¼’¢üœœÔ¢b†%·þMLU:ÄvIìcÃ¿I§¯Z¼ò3«ÊÍLIÉI-O,Jeºà:C67¨”5ÍÉõXÑÎÆÕ'�Cå—–¤30èïÿÊ±^÷„Cûßg_B.ý÷µ B”-”ïºdxœ àÿìì�÷ÔûësÄÝ\ÿÛŽÔà	l¶�ò“a ï…àûlxœÛÍ¼›yC$£×·ˆè'K+
.ØÎi±|·iÎ¡©y,“sý ê¢yà‚�>xœÛÍº›uC<“È•Ü•*o´ôÏ}6´å}µSzRÏŠÉÅL Ût†ì† xœ[À¼€yC£HòËßDB=.>ìu›#¬ÞÅ”fð ­úüàüpxœ  ßÿ»»°Y|ëÙ“5­à0íÒûPnÃöà‰ø“mNÃVëƒ³xœ{ÆøŒQÙÐÀÀÌÄD!±´$C¯¤˜áòâŸ[VtÆ7L:¦šd¯vÒvÉ®Ì‰Ê‡fóæ‹ôxœ;T³¿bC†°É£†)
E©Å©Ee©)
‰Åº™Å
Á™éa&
™yÉ9¥)©Å
%E‰™9™yé
Å9‰Å©Å“Û„c6Š�|UN|²´–Îd~ËÐÍa:oÛ— ìà‚Ÿaxœ  ßÿ»»°_IÈèÄãáeýŒöò·-‚PDÎ‡»“sHï]å�£:xœ5 Êÿ  °* ½kÃ|ê+p)€©Ÿø~õE•·“>N³<ü÷ 
$8�±óRÐ™šbú8
k£�ìExœ[À¼€yC£H±!ƒ'ß¹Æeîf&ÇrÚ--]È2 �5àÿYxœÛÍ¼›yC$£ˆW„üåïIvª¥³/Ú7+î¼â‘=“sý Û1%ëƒµyxœ{ÆøŒQÙÐÀÀÌÄD!±´$C¯¤˜ÁBÞÞµór�{‡ôì;Ì_ãÃvoMž¨| 7Sûà‚¡dxœÛÍº›uC<“ˆÌÁ?ç8¹äØ[º)…¯È½ÙÈ£zr1“ è†à€€mxœÛÍ¼›yC$£ˆiûÊ7¡Î	Ök°}’\˜ò(;éðóÉ¹Œ~ ä€eëƒ·xœ+ Ôÿææ#100644 auth.ts •öYè—½¼Œ9­E¯ºº‡ôG_‘#Ãp*pè‹øxœ;Ts£bÃÍÉWåÄ'KkéLæ·Ý¦óF �þ
?à‚£!xœÛÍº›uC<“È²8ý¸Ïrº�þH†ÌÙñråäb& ×Zxà€‚*xœÛÍ¼›yC$£Hà­Pé›i×ª™'nJ�4Ám·!Ãä\F? ÏÄ†ëƒ¸Kxœ{ÆøŒQÙÐÀÀÌÄD!±´$C¯¤˜¡kHáRy1»`í]jU›N_(cß7Qù0 B¥Qã‹ùTxœ;Tó¤j‚¾nfnA~Q‰BN~zzj‘BZQ~®‚ºžž>g$¥¦èCdô3óRR+6êXOnŽÞ,(b/Xï’šTš®�œ˜—Ÿ—™œ˜£P”ZXšZ\¢�–_¤ Ó-ÊÏ/Qpw)VÐH*MÎN-QÈ/H-J,ÉÌÏ+ÖäR€Ú©—™—–¯¡œ™f‚i˜ºŽB5P©‚BnjIF~Š˜]�X’a¥ T�XTœšZ”£ÈKÌM�<‘Wtò)i£ÄâŒÔ”€ÄÊœüÄ+…d®^qNfrª†�Ž‚‘�¦‚6È·zê:\“[´Ô8M4AVÔnvWíe™|UN|²´–Îd~ËÐÍa:oœËfÒà‚¦HxœÛÍº›uC<“ˆ7ß“×ôþt#Ð¥4ÏÊ±fêÕÉÅL îˆ#ì…exœ[À¼€yC£ÈÍ5N¶×tú_ñÍœc»P²(š™E6 ¥¢
™à€…zxœÛÍ¼›yC$£HÌš§_“ZF¾ÿ ½åö+!	Ñ´î…“sý öHµëƒ¼xœ{ÆøŒQÙÐÀÀÌÄD!±´$C¯¤˜�i©œæ³ó\-çtö[ÏÐäNz =Qù0 6.7çˆ	xœÛ_ÑV±!]xó\a&!V½”Ä’ÔÍËUgˆ qÇÕà‚¨*xœÛÍº›uC<“ˆUËì£‹n*ÚùßUNž²\©áÃ®ÉÅL Þ�Õå�¬xœ5 Êÿ  °*+*Z1-êä7(nHúâ¡÷¶ÿ*“>NÙ¬B=Ö,�ê™œ=¡r[W-Hêªxœ31 …¤ü††ú«¤ü™Ðôê?-‰©woMÀ²%%u£}§°°}©g5w\d>�§ßaDºØ˜áƒo»¢EÆq•«–ùÞ6;}˜ï 4e#Té�DxœËÉ™!²­µ^â+ÏÏÞ6c‘[JÂ“& �e
à¤xœ340031QÈÌKI­Ð+)f8#µƒQ%®¶öe½£¥»^¿�xlæG Ð‡ŠlŒ‘uxœ[ct\mÃÁÍgÅã'Slà‚ªgxœÛÍº›uC<“ÈÂY§¦jg†UžNð¿nzwÆ6ïþ}“‹™< è}é�®>xœ‰ vÿ  �P1‚ÂÕï“ÿŽúlŠ¥&£z
ór‘dx'œÂØ
6±Æ;£û¾!ŒG¸o\‘ð9q/÷{GÕ¾—Ú¡p¬´I40000 interfaces íðŠ_Q™P>á#k6\¹ßÌUu5“>Nq˜ÎN´×�F¥8ª_ÙßÝn§¤fk=	ì€xœL ³ÿ  �P²m?›šºâêU«TÕ¹ñ]Px‘dÆw§¢{CXlTzsfZ"fr“>NK
L™´KaFÆÍ9ïòð-³bæ¸æ·å[xœ5 Êÿ  °*ÊYtó5ŒdP³¬:å™ÂÁýíY“>NÙ¬B=Ö,�ê™œ=¡r[WkbRä�úZxœ{"µ\jC(3SJÒærfM¶Í{9—3 NÖé€£&xœËÌœ*²ÐéÏ±®š”×ö}‡—2‹~˜¤h •œRäÏ3xœD »ÿÑÑ'100644 bot-pool.ts ªÆz«j²ánÜµïéypÄÔy‘'–‘?S¿ÁV‰ õ„m7k¹<ò><Çéòí„Š%xœ›v#`C#+ŸzQjIQ¥BbZIj‘ºÎf;¶ML›§q®bçŠ—å)$–'f–LÞ*¨ÈdÍ59QEuóO!I	 ÁN´ìÄZxœ›!6]hBÕæÆGŒ“m˜%&Ë±ˆ-žÌÌ´¹›­— Ž	ªxœ31 …¤ü††ú«¤ü™Ðôê?-‰©woMÀ²%%O®žzœZºæÇ£ŠµNÕN–‹foû ‘.6føàÛ®h‘q\åêD‡e¾w…ÍNæ{ îÁ'ß¬xœ31 …äü¼’¢üœœÔ¢b†?_ý„=Þ}ââõÿüN+p²ÞŸÃ&`U¹™))9©å‰E©ÂA\gÈæ•²¦9¹+ÚÙ¸úäqˆ¢¢üÒ’Ôb†m­õ_yþxö¶‹ÜR:è˜4	 Ðx*ŽïÏ+xœ àÿìì�÷¸!4¢?�ÄæÿÜDD€5Ñ$øZË“aù„Þè!—xœMPÏkA&FEZ5öà”ÝÅd­`[1Ä­
"µbÑR0ê$;“šÌ,³3nƒè
½	äÝ•âÅz‰°XDQ„"äèÉ‹þâ?à&ÛŠsxÌ|ï{ó}ßëõò¿ßæß)&¬¸~}ø$ô±ôà%.îÃ»dûä®5^!{2ùÞÜÈa}ót²E÷çF#Å5U.žíTq¼u
/~,bÜêâ—Í ùéÿ©<¤Š³.Ì_ž‚È§æ¤ÐTèÊ ð)ñ¨B h˜¢¸KŸ�x4éiJê´v£©7ïMAfc© S²cI $"<¥©n ¥š†nS×ã-ê€•ªYåOA#¸Í…>{I)Òµ³f�poSÜzrèŽK�LÍvœ�Ñí‹ãj¹¨-Ûj��NŸ±œê°ÃØÍ,áÀð±Zíÿ X‰æð~ý0~ÐcØ××ðWðYXNLœÃRy?/Mâ‰è n˜¬=ˆî0þTÀÚZ)ðŸßšÀ~´P0A[ïŠRRáº™Õ¾’l£Éòê�½ø"vw<á¹øx9ó¥¯¦o�½©(ã«ny²i:©çáŽðë·•×½§Kaº¹%é‚µ'xœ9 Æÿ»»°�©†½®–pð4Fóô—ÿ�KvÌ»“•Ê{k©¸Žë³ÈŽl[Ê¸26'K1d“sH.°ãƒ»/xœ{ò‡iáo¦L›ƒ™ÌS6¥… [iãªxœ31 …¤ü††ú«¤ü™Ðôê?-‰©woMÀ²%%ì;|>…-^kn¿,ª ±¦<ÂÞÔ	"]lÌðÁ·]Ñ"ã¸ÊÕ‰Ë|ï
›�>Ì÷ d^$^¬xœ31 …äü¼’¢üœœÔ¢bÖt7Yfÿæóëß\SäÈ`Êwð»	XUnfJJNjybQ*ƒpÐ×²¹A¥¬iN®ÇŠv6®>y¢¨(¿´$µ˜a[k½ÄWž?ž½mÆ"·”„:&M Z)Yê…xœû¸ŠyÝBæ‰y|Îùy%©y%º¾.¦
ùE“òº6/*fÕ˜l±Ø³ÀœDK u¬ãà€–qxœÛÍ¼›yC$£È‘-í·Ì¯ÈìvuÍoûýü #Ï;þÉ¹Œ~ ò ãƒÍxœC ¼ÿææ#100644 auth.ts Æ“A^ÓwÌîg¬c`c,­ Ð>‘#>Ê!†yïÛó­‰<ÿ×Âƒ$T¸‘uq¦î�xœMŽ1
Â@E{/ ˆÈG-"ˆ6VB*A¬ì<ÀšlÜ…ÍŽìNÔTZy�xÁx+[{ï`»aøÿ¿w><÷×G÷¸˜L1£t›±„^!q”BDœ	ƒ5Å9´õ,EJÀ.ó¬íª|H‡µÑ66ù¨ŒÇ˜“ƒg'EZEÊ®–A½†!lfÌ`ˆ½D$¬%®q++í±%mïÓå7ÆªÊSR‚ˆ,;ªîÒÒ ;ét’ÿôji‘°üƒ÷^«øôÚÅrÒ¹ýWóDœUååƒÌfxœÛ­úSqÂ”�ÿrÙ&ßäâ.IÍIM/JÌ-Ê™ÌÇã;¹žÃ`ó-&�$+—L~Í-)f•'æ•‡gäç¤$•(Ø+”æ¥¤¦eæ¥¦L.äVaS¨Õ*ÛÌÇ³– K!�ä‚»SxœT «ÿ»š�º‘Û¦©†½®–pð4Fóô—ÿ�KvÌ»“•†‹4¤%ºb\ÀÊ+ÏbEÐïÄZà“/0{k©¸Žë³ÈŽl[Ê¸26'K1d“sHúô'Ýçƒ¼+xœkÛ*º�…‘)%i±#Çf;ÎÉŒ“_pÉ  R¤ãà€	xœ›Å:‹uƒ“ˆ_vÎÓÙ?'®m8Ý°ïÍtiÝgq“ƒ˜< êîŽà�Ÿxœ@ ¿ÿ²²�ù9ÖãÉW¿Ñ¿�Cxmvæp(­%100644 schema.ts '^X¯)·ž¶¦ W8g6aˆrd?=‚÷«á€Â*xœa žÿ×(«$�ý}‘a³89“�O“ì	³¿V“ ³:Š²%“]A“0	.$  // Parts are cascade-deleted by FK³]	8³ÏˆV¨ &à�íQxœë’\ 2áàæZÆÙ¬›×²?bš|žËx²'·Ùä£ÜÖ“}xì ÚÓHªxœ31 …¤ü††ú«¤ü™Ðôê?-‰©woMÀ²%%ŽŠ'²K§¿ýàé>§ÃeÊ7�.'ˆt±1Ãš˜¬¼â_�ÏDNÞöÊy^}@oçs# ‰¨&¿¬xœ31 …äü¼’¢üœœÔ¢b‘(ñÅ�kô¿¤ë,á¸}ÍäÂÕŸLÀªr3SRrRË‹R„ƒ.¸Î�Í*eMsr=V´³qõÉãEEù¥%©ÅÛZë%¾òüñìm3¹¥t <Ð1i Þ*ÿïÞGxœ{Ãô†iÂw‘––Õ‚oj{+kz6[õíü¾ð“ÇdnÆD ò£ìå0Žxœ}’_HSaÆÙ9såQ‡mQI+>S›ÓÓq¶
3ujX“D¥Ð.l¹ã9Ÿó°¿ž�USh`ÉdÃ ÈBó£$"ÌœHta$u�Úº‹þœIfÕwù=ïï}žïût“¾ô�R„¢“5P´)•˜µ×(¸GU‘¸Ž¡áSMl)KÌê·kŠa¨;ƒ,êÇ7ÀSS*¹…44g› d›‘°Ù¯’´R+ÊOÞ>ˆqfRŸoJ'_Ê]Úõ2%
9Q5¯iHe
«äæ‚ràLä˜í0V,¨“Aê)(@5ØÍd³îF«$
òJÊ=Ý†ýh…AR‰RˆoñbÑ¢‚0Õ·¸ûº|•bU¬½ZdQ'ò^‘þâß›d/ŠZ¨H’NfM!ÍŽëÉ¦ÿ¥[5Ùû/“P ,8ÔduÚ9{±[æ}	C»™Í°Î‚�•:xàp‘ÇGí´±%,x°¢bŠ,ùÝ,òàpœb£`Ï1@ùñpaÖ ZÉwç!›—ÒÈ¢²l¡§úÕ4ßz2`°/�Žîp%J‡Æ¸z;V4[©Þ÷2Ñ1\žgÈí‹™É°n`'Dxx3�çë¨&'Äú7ÃÜ�FrðŠHCÑpÝpú�<OÞþÊ„àüHy6RFC÷hÌ]5”yY©ûZ°l¶d²¨ÐºRp¢Ê5• BÔÕ…þ¸)S'­V¨¨ÍƒôÉ$"�u�IQ°/X¯>¸…eÀ>±Ë`BØ‡ýŠ™U×ª»h?µF'RÈç‰;ZXx>E^<ŒkàõäK¸[e ÝtŽ>È…¤\QpˆEVÐ�‡™øWõŸŒo%�fº4P3½Ÿì™-Õÿò!ç„xœëÏÔÿžiCÓdM6»Í™L;™7§³il tžöî‹ßjxœûÆÙÃ¹QÉ€YPOOO£(µ°4µ¸Ä3EÁ^¡zr+³¼”‚z…nbn•nfŠ®‘º•BAíæ'ÌBŒ Y–pà‹¸JxœûáÿÎoÃNåÉ•õXâlbµ'©¨%çç—(–f¦–(Ø*$å§Têeæ%ç”¦¤k¨Û‚ÄíJŠJSmô!luM…š<êÔ7?Uédš\ªÞ Q·)dà€¦!xœ  ßÿ»»°Y¬R‚¨žºÁÞÖË—XO—ÿöÃ¬>‚“mN	?£¦xœ340031QH,-ÉÐ+)f0m|»èŠ¼°P¥‹iÓ:6)îò›÷!J2RSR‹ŠAª¦ª_Í±; ÿuúÁýËØ—ÜûüŸª*?)+5¹D·¸¤(51¤ö”œ¢LoåûÛŸ×vÚü¿~¨Y%dTmQb^z*H�•¼º×O6Éù}¬—…/MO›™ç/US–YTRš˜£›‘_\RÊ”Ô¸ryUÝªi_,ìöŸô¯U›Ñ¸ª´"7¤"[7v'ÿÌÔcÓcŸÝ¬ú!wOÿx¨0 .ÈY¬çµ"xœû[ñ0lÃ"–Í�¬k¹&s	†MNt•+J-)-ÊSHIMÎOIòtÎÏ-ÈÏKÍ+Ñ(-ÊÔÜüBø2ãd~±~I¨ºD½œüäÄœT�²Ä¢T�$ÍÉ7ÅZ'¿’‘’KÎÏ+êÎH425³R(.)ÊÌKW¨QÈ+ÍÉÑáš\(.§‚¢DSj&Š¨õäñäÍ2~Ì›³[�@ÒÅ%
‰Å©)‰•9ù‰)
¶
‰å‰™%
é©%ÈIù)•:¨&NÖWù:9^U“Y]Ýz²¢ºÒä;¦&›;5R'{™‡Nž¨µsó	‰ÇŒ›½´ÓÙ7›° ôká‹Áxœ{Ì~�m‚�Zfš‚FF~q‰^qIbQIqxfI††z´º¦¦BQjIiQžHÒzãmf »×ˆï�0xœ›Å:‹uƒ“ñ×åS•NÝ‹¼´õ—àÙ€¥§Ì’S÷˜ �BIjq	Ã†8þ•=
‰É½š[uF_ÚÊ8¹�I �ÔòáŸ!xœq Žÿ  �P1‚ÂÕï“ÿŽúlŠ¥&£z
ór‘d¡90è¾›OÆE­ô `¿×7	Iá40000 interfaces ¢È.0\#êŒGœ¸Ð³XÏý”°“>NenÈT
Ø‰í'Þ—\37@�	*:01éÈ`xœËÌœ*òu:ƒùe…_7þŸúðáKpéö¯yR§°�¸äïxœD »ÿÑÑ'100644 bot-pool.ts TGÝr„ö›èdh1�ÀæŸ¦‘'–‘?S¿ÁV‰ õ„m7k¹<ò><bµqäž`xœ»°:`Cžôäjé f…j®É÷¤•&³KO–—Ÿ¬$,µ9Næ7 ñ³ïªxœ31 …¤ü††ú«¤ü™Ðôê?-‰©woMÀ²%%ÖQ�ÝìŸ©lrü÷ÿQÀ#Y©¦ç‚ébc†51YyÅ¿Ÿ‰œ¼í•ó¼ú€ÞÎçF £'W¬xœ31 …äü¼’¢üœœÔ¢b�YRw´ß¬)<î¼b+Çª¥Ûw7p®7«ÊÍLIÉI-O,Jeºà:C67¨”5ÍÉõXÑÎÆÕ'�Cå—–¤3lk­—øÊóÇ³·ÍXä–Ò�ð@Ç¤I ím*ýì�örxœL ³ÿìì�H<Ë�Ì±94ðÖ�ªÄýøZEŠçº‘\›l�‰x*½ŒJøRçÈq�9'ù•œ“MìÉNî‰ÐðŸ¨|æ™ÅqgœÜ›ñ6(0ë€³@xœ; ÄÿŽ»�a{Ü¤q¤óÌwv…¾ŒäÀÖ™2‘uÿ“Ç²RÏ•pF[Ëë·ÐÂ[?€\×<“ì"\ §â€…@xœ;¨xPqÂÕÉ"ŒZ¯:l¶glâ K5&å‚šxœU ªÿØ	¬	�òvD±ÑU5	œ½þ§ðþ5s‘/_±º‰Y‘ Ž 'ŽwÈ^¿ úÂ7j|M³WÀh¢|ý³r©ÿ‰k#ƒ§±ÈvDš“+­2%îƒ®]xœ[¤Ô©´akiIfNñäLn“×1IL6d–ÙìÇ|… ²î
ÜæRƒŸxœ]”]L[eÇ�®¶lP:N?(åZÎ)ý<=´å´=ôc¥-´ÎL�8—Q:�+u0*Ù¥ñJÓ–™%Ž-Ën4*F�Á%3Æ]#—‹&Ä,ñBã�,^£ñ=§o�íâä}“ç÷üŸÏ÷üv¹í“Kmk5w›u·—ÝþÓƒº×Ô.„À1Aüã³c®bÁ‘aGçÊHÒ`À¹á1•Ù”º°>ˆU^�#?„¿-F(¼Ø”0âÞILNê·É'výú0.XqLÁ;Ù‘øRRîá!nÆãÀëž`“ÓQæžGÙÂà=/C±DDÄç#
µ{ŸkB1bäq]éÇ†bE§¯‡€6Gôˆú8
ŸôÉM˜¥ñ{Jï€ä?¨ÝOúû´ójÀB?ö'ZŽ¶€ë¬|C²áf U×v`b<€õPO;»‚5VØáÕ@ÛAF½›ËóË¹…sµ%�ø²lSAÅ©Bç¬Ÿ}Ó€Cr«ùXMcJ4ÑxsbF¥7&]¸'ÚIEVcd�X¸Ô`t8Á5iÂ§a…žØ%ŠdÃ1éEÜûñ³Äá?i€ŒkÊ¨éÑ�_ŒöÒ�?Ž–öŽ®±â…HáQÞe(/EóXŽ¡�ž4wh…=!ðÇl;`QoÇ•X
[±¯qá1�ìë·cÏÀ)(Ê¼&Oã;y ¿ÊƒÐÅ]ªÂñ8^‰ÆÍ8‡­xeðå…—j�4Ñdâ-¬Ü¸cˆÃžÌâù®&}ø!éÆƒäqRxfÎ[¬jò(ÙžÍèDÊŽÞqº1!”.�«…â—qu™™FÊÊ³ÃWfmèN«�tê"á˜šìg‚½‘ØçÉjzøÓçÖzu7ÝêíýôSê¼Ò™i¼›q×nÜÊ8±•ñÁ˜­`&ëÆ…l_eãØÎ>ŽÜ¡±¾çN-'ØzõìRØOæÞœ<.:J¬L#ÇÁ•3iBÅÙôÜÞÎýŽÅ‰§ñý¯»7oÖìržÃñü4>Ê»°™—a(<©Õ°\èÒìï±^(¢½(Òl“ÅVŠCd²^òÒ`Õ»O»wMö‘sB»×3æ¾&ŽÑGgEWX�î©œö+©N¥èßßL±aÜê×ï<—t©Õ›gKYœUt¸;°%£ñô|õÔüÙÅ„ºM?•8ü[zõ²×"œTÏ¼˜[¨-Í×–‹§«R4&$p»\n[ëtë"âèîHO*÷ÿPŸýâ‚‘Exœ›£Ñ¡1!}ãÖßì~Õ
iùEå‰E)!ùÁ%ùE‰é©V
n¥yÉ%™ùyÖ
é©%n™9©žyiùÈÂ©y…¥©¥©¡9ù‰)	…ÚÍ8¬¸'ÿ– t~%¨éžPxœY ¦ÿšø±"ØKMàÙŸûG‚Ìûv#PÂ†”Pîõ“03ˆË²�1YJ•ü×ÓªT¬Ü†2ék40000 test KeŽKWg+;a3"$É¬}ê²“q)Z.&Õéhxœ9 Æÿøø°ØÀÚÔq„Á£/®¦;j`šZs¸»“ìOÒàe ì­Ê ö<=ÎYÃ ÔDÞ“O)JæàGxœûÁòƒeƒ“ˆ´¤×ª�kÍü´_è:fYKõ	�\2q²“ ÆHØãƒÜrxœk»+°�…‘)%i²ãçÉöL'‡1+LžÃü~òF¹É_YT';°*NngUŸ|…5eò=vþÉ×Ù„€b"“O°	ðç+Y)(%•æ)”¤—LÎ`ŸVŸÎ±³çdÆÉ/¸d æ"Ñ xœ31 …Ä‚‚œÌäÄ’Ìü<æí�’ÁŒûŽ.ÛxF&ñ«Cý�óWLÀª’óóÒ2ÓLOõÍ8bým¡mçí:ÏîWäË(C¤$16ºú~òÿ>ñ_9]KÕWq}.â†Jæç&fæ1˜þþP·ñý«F9ówyçÊ4˜™˜(¤æ•é•3pÞ(9ºÜdY­_í�¬àõ¿ª¯�ªÈÈÏMÕË(ÉÍaøÁþB¨Lse–ßêg·ÿLŒ9ùÍlúQ¨¢Ì¼”Ô
�AêsÝà2ÛxÌzñï}Š=üî;òcd nÉÌK+J,.)*M.)-Je0x±o¶ÿ1Wþµ_$ö_ç3çô|SX’Z”–˜œZÌ�¦jæ§Ãu¶@”áIËœÛÿ¾<ŠözQT”_ZTð>D»tëÚ½½g"ŠëJ�ß©”Ï˜	QPœ‘X”šÂ°wbiùós'ÜJªÉOõº4éÛ‹Øˆ‚Ò’Ìœb†›Mo•}ˆ²/óå8–#réº§; ®ƒê„ï1xœ»g¶]i‚˜{iIfN±~N~zzj‘º5Wf^IjQZbrª‚cA�s~^ZfºB5—‚BR~IH~vjž•BqIQf^º5P,1%%³$3?/1Çiâ±É™Œç$KròS€ú’K‹ŠRó’+­òJs“R‹63Ma´LÎÏ+.Q(J-,Í,JMqÍ+S°›îäâïíêg¥PP”ŸœZ\¬—šW¦ÖÙ¼�õ"ãfEnKæÉÂº8”)ê º
êæb+ °Š?×xW¿0[[[õ’Ôâu{…èX É‰EÅ©}“sX7ß~ËÈñØäÂªb¡>þŽ.ñÎþ~Î¡AA®~Î‘:
š›Š
0N“š¼W˜srŸ€°Ž‚‘�‚–‚¡�‘	”ÒÜ|Q,œyò)3Xxjê€Ý†ÍÑ
Éàà×C’Ûl,¥Í FÍ‘�ªxœ31 …¤ü††ú«¤ü™Ðôê?-‰©woMÀ²%%õË—ÈkýÜå§mðFûS­ã£{¿ ÒÅÆkb²òŠ5>9yÛ+çyõ½�Ï� ´k'æ¬xœ31 …äü¼’¢üœœÔ¢b¥•~tíœÇÞ15w–¼ëÙG»MÀªr3SRrRË‹R„ƒ.¸Î�Í*eMsr=V´³qõÉãEEù¥%©ÅÛZë%¾òüñìm3¹¥t <Ð1i ìB*äã“Uxœ3 Ìÿìì�H¶o›òÂ‚¢øL"¹€ÎRð8ëª ‘\üyägX7ñuzÖí)-ü¤ec{\E™Ãt æ€Ù3xœ{e9ÛrC£`zj‰[fNªg^Z¾ŽBIeAêä£ŒÊ|¥%™9Åú%©9©éE‰¹›Y™ö°l>É¦( :9þä�Å\xœ;^º»tC“mZ~QybQJH~pI~QbzªŽBzj‰[fNªg^Z¾B­BZQ~®‚ºžž>•–dæë—¤æ¤¦%æn^Ì”(½™G.�ssªz’0 A^´¡x!Þþ100644 auth.ts ºkl%áhù|m"fŠ$ðV‹14100644 files.ts ¨ª=¯fÕX=Ouàb¬hš¿è100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 home.ts $ÝGL&J¼äšÞÖY' áÓRï100644 s3.ts Ç¸�l»p3�/»�	'.‰}ÿ100644 swagger.ts  )>èÅâ¿È¦�™Ë•7Ä68|100644 upload.ts µ&â*¢Øk¿k#ë›¨Yê100644 web-api.ts BiYZt­‹FC"¸HâìcQ]Õ|”m­¸px÷øimport { config } from '../env';
import {
  checkBearerToken,
  clearSessionCookie,
  createSessionCookie,
  getAuthSession,
  isAuthEnabled,
  timingSafeCompare,
} from '../utils/auth';

const json = (data: unknown, status = 200, headers: Record<string, string> = {}): Response =>
  Response.json(data, { status, headers });

const notFound = (): Response => json({ error: 'Not found' }, 404);

const readLoginBody = async (req: Request): Promise<{ token: string } | null> => {
  try {
    const body = (await req.json()) as { token?: unknown };
    if (typeof body.token !== 'string' || body.token.length === 0) return null;
    return { token: body.token };
  } catch {
    return null;
  }
};

export const handleLogin = async (req: Request): Promise<Response> => {
  if (!isAuthEnabled()) return notFound();

  const body = await readLoginBody(req);
  if (!body) return json({ error: 'Token is required' }, 400);

  if (!timingSafeCompare(body.token, config.adminApiToken)) {
    return json({ error: 'Invalid token' }, 401);
  }

  return json({ username: 'admin' }, 200, {
    'set-cookie': createSessionCookie('admin'),
  });
};

export const handleLogout = async (): Promise<Response> =>
  json({ success: true }, 200, {
    'set-cookie': clearSessionCookie(),
  });

export const handleMe = async (req: Request): Promise<Response> => {
  if (!isAuthEnabled()) return notFound();

  const session = getAuthSession(req);
  if (!session && !checkBearerToken(req.headers.get('authorization'))) {
    return json({ error: 'Unauthorized' }, 401);
  }

  const activeSession = session ?? {
    username: 'admin',
    expiresAt: null,
    method: 'bearer' as const,
  };

  return json({
    username: activeSession.username,
    expiresAt: activeSession.expiresAt?.toISOString() ?? null,
  });
};
»sKy²Îxâëimport { createReadStream } from 'node:fs';
import { unlink } from 'node:fs/promises';
import { nanoid } from 'nanoid';
import { findFileByPublicId } from '../db/files';
import { fileInfoCache } from '../utils/cache';
import { formatCreatedAt, getErrorMessage } from '../utils/file';
import logger from '../utils/logger';
import { getBot } from '../utils/telegram';
import { locateZipEntry } from '../utils/zip';

type RequestWithParams = Request & {
  params?: {
    public_id?: string;
  };
};

const getTelegramFileInfo = async (telegramFileId: string, public_id: string) => {
  const cacheKey = `file_info_${telegramFileId}`;
  let fileInfo = fileInfoCache.get(cacheKey);

  if (!fileInfo) {
    const bot = getBot();
    const apiFileInfo = await bot.telegram.getFile(telegramFileId);
    fileInfo = {
      file_size: (apiFileInfo as any).file_size || 0,
      mime_type: (apiFileInfo as any).mime_type || 'application/octet-stream',
      file_path: (apiFileInfo as any).file_path || '',
    };
    fileInfoCache.set(cacheKey, fileInfo);
    logger.debug('File info cached', { public_id, cacheKey });
  } else {
    logger.debug('File info from cache', { public_id, cacheKey });
  }

  return fileInfo;
};

const buildTelegramFileUrl = (filePath: string): string =>
  `https://api.telegram.org/file/bot${process.env.BOT_TOKEN}/${filePath}`;

const cleanupTempFile = async (tempPath: string): Promise<void> => {
  try {
    await unlink(tempPath);
  } catch (err) {
    logger.warn('Failed to cleanup temp file', { tempPath, error: getErrorMessage(err) });
  }
};

const sanitizeFilenameHeader = (fileName: string): string =>
  fileName.replace(/[\\"]/g, '').replace(/[\n\r]/g, '');

const fail = (status: number, error: string): Response =>
  Response.json({ error }, { status });

export const handleFileRedirect = async (req: RequestWithParams): Promise<Response> => {
  const public_id = req.params?.public_id;
  try {
    if (!public_id) {
      return fail(400, 'Missing file id');
    }

    const file = await findFileByPublicId(public_id);
    if (!file) {
      logger.warn('File not found', { public_id });
      return fail(404, 'File not found');
    }

    const archiveEntryName = file.archiveEntryName;
    if (archiveEntryName) {
      const archiveFileId = file.archiveTelegramFileId || file.telegramFileId;
      const archiveInfo = await getTelegramFileInfo(archiveFileId, public_id);
      const archiveResponse = await fetch(buildTelegramFileUrl(archiveInfo.file_path));

      if (!archiveResponse.ok) {
        logger.error('Archive download failed', { public_id, status: archiveResponse.status });
        return fail(500, 'Server error');
      }

      const tempZipPath = `/tmp/teleuploader-dl-${nanoid()}.zip`;
      await Bun.write(tempZipPath, archiveResponse);

      const loc = await locateZipEntry(tempZipPath, archiveEntryName);
      if (!loc) {
        await cleanupTempFile(tempZipPath);
        logger.error('Archive entry not found', { public_id, archiveEntryName });
        return fail(404, 'File not found');
      }

      const fileStream = createReadStream(tempZipPath, {
        start: loc.start,
        end: loc.start + loc.length - 1,
      });

      fileStream.on('close', () => {
        void cleanupTempFile(tempZipPath);
      });
      fileStream.on('error', () => {
        void cleanupTempFile(tempZipPath);
      });

      return new Response(fileStream as any, {
        status: 200,
        headers: {
          'Content-Type': file.mimeType || 'application/octet-stream',
          'Content-Disposition': `attachment; filename="${sanitizeFilenameHeader(file.fileName)}"`,
          'Content-Length': String(loc.length),
        },
      });
    }

    const fileInfo = await getTelegramFileInfo(file.telegramFileId, public_id);
    const tgResponse = await fetch(buildTelegramFileUrl(fileInfo.file_path));

    if (!tgResponse.ok) {
      logger.error('File download failed', { public_id, status: tgResponse.status });
      return fail(502, 'Server error');
    }

    return new Response(tgResponse.body, {
      status: 200,
      headers: {
        'Content-Type': file.mimeType || 'application/octet-stream',
        'Content-Disposition': `attachment; filename="${sanitizeFilenameHeader(file.fileName)}"`,
        'Content-Length': String(file.sizeBytes),
      },
    });
  } catch (error: unknown) {
    logger.error('File redirect error', { public_id, error: getErrorMessage(error) });
    return fail(500, 'Server error');
  }
};

export const handleFileInfo = async (req: RequestWithParams): Promise<Response> => {
  const public_id = req.params?.public_id;
  try {
    if (!public_id) {
      return fail(400, 'Missing file id');
    }

    const file = await findFileByPublicId(public_id);
    if (!file) {
      logger.warn('File not found', { public_id });
      return fail(404, 'File not found');
    }

    return Response.json(
      {
        public_id: file.publicId,
        file_name: file.fileName,
        mime_type: file.mimeType,
        size_bytes: file.sizeBytes,
        file_type: file.fileType,
        created_at: formatCreatedAt(file.createdAt),
      },
      { status: 200 },
    );
  } catch (error: unknown) {
    logger.error('File info error', { public_id, error: getErrorMessage(error) });
    return fail(500, 'Server error');
  }
};
<v¶Çå9¨px•jüâ)ÿ'�5‘`ƒXcreateChunkedObjectResponse } from '../utils/chunked-storage';
import { cleanupTempFile,‘âiWmetricsCollector } from '../utils/metrics';
import { getFileInfo, type TelegramFileInfo³QQ\ as TelegramFileInfo | null;

  if (!fileInfo) {
    metricsCollector.recordCacheMiss();
   “�FileInfo(telegramFileId)“è{"metricsCollector.recordCacheHit();“^Ž
, botToken“ä;botToken}/${filePath}`“ ³³Õ¬if (file.storageBackend === 'chunked') {
      if (file.archiveEntryName) {
        return fail(501, 'Archive entry extraction is not supported for chunked files');
      }
      const range = { type: 'none' as const };
      return createChunkedObjectRe!sponse({ file, range, reqId: '' }³s"	
        “•	* , archiveInfo.bot_token),
      ³À	 unknown as ReadableStream, {
  “ï³‚^redirectUrl = “ù'I, fileInfo.bot_token);

    return new Response(null, {
      status: 302’Location: redirectUrl³êø�³7Âå†$x êÿÿ'û'°û
filedrop³ønò	 ¸"x(×ýimport { sql } from 'drizzle-orm';
import { db } from '../db';
import { getErrorMessage } from '../utils/file';
import logger from '../utils/logger';

export const handleHealth = async (_req: Request): Promise<Response> => {
  try {
    await db.execute(sql`SELECT 1`);
    return Response.json({ status: 'ok' }, { status: 200 });
  } catch (error: unknown) {
    const message = getErrorMessage(error);
    logger.error('Health check failed', { error: message });
    return Response.json({ status: 'error', error: message }, { status: 500 });
  }
};
P(¶ô´xûþexport const handleHome = async (): Promise<Response> => {
  const html = await Bun.file(`${import.meta.dir}/../home.html`).text();
  return new Response(html, {
    status: 200,
    headers: {
      'content-type': 'text/html; charset=utf-8',
    },
  });
};
b.RÅµ¨x…‚z}import { createReadStream } from 'node:fs';
import { nanoid } from 'nanoid';
import { createBucket, deleteBucket, findBucketByName, listBuckets } from '../db/buckets';
import {
  countBucketObjects,
  findFileByBucketAndKey,
  listObjectsByPrefix,
  softDeleteFile,
} from '../db/files-ext';
import {
  abortMultipartUpload,
  completeMultipartUpload,
  createMultipartUpload,
  findMultipartUpload,
  insertMultipartPart,
  listMultipartParts,
  listMultipartUploadsByBucket,
} from '../db/multipart';
import type { File } from '../db/schema';
import { config } from '../env';
import { createChunkedObjectResponse, storeFileInTelegramChunks } from '../utils/chunked-storage';
import { cleanupTempFile, computeHash, ensureExtension, getErrorMessage } from '../utils/file';
import logger from '../utils/logger';
import { verifyPresignedUrl, verifySignature } from '../utils/s3/auth';
import { S3_CORS_HEADERS, s3Headers } from '../utils/s3/headers';
import { createGetObjectResponse, type ObjectPartSource } from '../utils/s3/object-stream';
import { parseRangeHeader, unsatisfiedContentRange } from '../utils/s3/range';
import {
  bucketVersioningConfigurationXml,
  completeMultipartUploadXml,
  copyObjectResultXml,
  deleteResultXml,
  initiateMultipartUploadXml,
  listBucketResultXml,
  listBucketsXml,
  listBucketV2ResultXml,
  listMultipartUploadsXml,
  listPartsXml,
  parseCompleteMultipartBody,
  parseDeleteObjectsBody,
  s3ErrorResponse,
} from '../utils/s3/xml';
import { forwardToStorage, getFileInfo } from '../utils/telegram';

const REGION = config.s3DefaultRegion || 'us-east-1';
const REQUEST_ID = () => nanoid(16);

const s3Response = (
  body: string | null,
  status: number,
  reqId: string,
  extraHeaders: Record<string, string> = {},
): Response => new Response(body, { status, headers: s3Headers(reqId, extraHeaders) });

const s3OptionsResponse = (): Response =>
  new Response(null, { status: 204, headers: S3_CORS_HEADERS });

const parseS3Path = (pathname: string): { bucket: string | null; key: string | null } => {
  const parts = pathname.split('/').filter(Boolean);
  if (parts.length === 0) return { bucket: null, key: null };
  if (parts.length === 1) return { bucket: parts[0], key: null };
  return { bucket: parts[0], key: parts.slice(1).join('/') };
};

const headersToRecord = (req: Request): Record<string, string> => {
  const record: Record<string, string> = {};
  for (const [key, value] of req.headers.entries()) {
    record[key.toLowerCase()] = value;
  }
  return record;
};

// â”€â”€â”€â”€â”€â”€â”€ Main Dispatcher â”€â”€â”€â”€â”€â”€â”€

export const handleS3Request = async (
  req: Request,
  virtualHostBucket: string | null = null,
): Promise<Response> => {
  const method = req.method;
  const url = new URL(req.url);
  const pathname = url.pathname;
  const { bucket, key } = virtualHostBucket
    ? {
        bucket: virtualHostBucket,
        key: pathname === '/' ? null : decodeURIComponent(pathname.slice(1)),
      }
    : parseS3Path(pathname);
  const headers = headersToRecord(req);
  const searchParams = url.searchParams;
  const reqId = REQUEST_ID();

  if (method === 'OPTIONS') {
    return s3OptionsResponse();
  }

  const isPresigned = searchParams.has('X-Amz-Signature');
  const authResult = isPresigned
    ? await verifyPresignedUrl({
        url: req.url,
        method,
        headers,
        s3AccessKey: config.s3AccessKey,
        s3SecretKey: config.s3SecretKey,
        region: REGION,
      })
    : await verifySignature(
        method,
        req.url,
        headers,
        null,
        config.s3AccessKey,
        config.s3SecretKey,
        REGION,
      );

  if (!authResult.isValid) {
    const status = authResult.errorCode === 'NotImplemented' ? 501 : 403;
    const message =
      authResult.errorCode === 'NotImplemented'
        ? 'aws-chunked streaming payloads are not supported.'
        : isPresigned
          ? 'Presigned URL verification failed'
          : 'Authentication required';
    return s3ErrorResponse(
      authResult.errorCode || 'AccessDenied',
      message,
      pathname,
      status,
      reqId,
    );
  }

  try {
    // Root: ListBuckets
    if (!bucket) {
      if (method === 'GET') {
        return handleListBuckets(reqId);
      }
      return s3ErrorResponse(
        'MethodNotAllowed',
        'The specified method is not allowed against this resource.',
        '/',
        405,
        reqId,
      );
    }

    // Bucket-level operations
    if (!key) {
      if (method === 'GET') {
        if (searchParams.has('versioning')) {
          return handleGetBucketVersioning(bucket, reqId);
        }
        if (searchParams.has('uploads')) {
          return handleListMultipartUploads(bucket, searchParams, reqId);
        }
        const listType = searchParams.get('list-type');
        if (listType === '2') {
          return handleListObjectsV2(bucket, searchParams, reqId);
        }
        return handleListObjectsV1(bucket, searchParams, reqId);
      }
      if (method === 'PUT') return handleCreateBucket(bucket, reqId);
      if (method === 'HEAD') return handleHeadBucket(bucket, reqId);
      if (method === 'DELETE') return handleDeleteBucket(bucket, reqId);
      if (method === 'POST') {
        if (searchParams.has('delete')) {
          const body = await req.text();
          return handleDeleteObjects(bucket, body, reqId);
        }
        if (searchParams.has('tagging')) {
          return s3Response(null, 204, reqId);
        }
      }
      return s3ErrorResponse(
        'MethodNotAllowed',
        'The specified method is not allowed against this resource.',
        `/${bucket}`,
        405,
        reqId,
      );
    }

    // Object-level: multipart checks
    if (searchParams.has('uploads') && method === 'POST') {
      return handleCreateMultipartUpload(bucket, key, searchParams, reqId);
    }
    if (searchParams.has('uploadId') && searchParams.has('partNumber') && method === 'PUT') {
      return handleUploadPart(bucket, key, searchParams, req, reqId);
    }
    if (searchParams.has('uploadId') && method === 'POST') {
      const body = await req.text();
      return handleCompleteMultipartUpload(bucket, key, searchParams, body, reqId);
    }
    if (searchParams.has('uploadId') && method === 'DELETE') {
      return handleAbortMultipartUpload(bucket, key, searchParams, reqId);
    }
    if (searchParams.has('uploadId') && method === 'GET') {
      return handleListParts(bucket, key, searchParams, reqId);
    }

    // Standard object operations
    if (method === 'GET') return handleGetObject(bucket, key, searchParams, headers, reqId);
    if (method === 'HEAD') return handleHeadObject(bucket, key, reqId);
    if (method === 'PUT') return handlePutObject(bucket, key, searchParams, headers, req, reqId);
    if (method === 'DELETE') return handleDeleteObject(bucket, key, reqId);

    return s3ErrorResponse(
      'MethodNotAllowed',
      'The specified method is not allowed against this resource.',
      `/${bucket}/${key}`,
      405,
      reqId,
    );
  } catch (error: unknown) {
    logger.error('S3 operation error', { bucket, key, error: getErrorMessage(error) });
    return s3ErrorResponse(
      'InternalError',
      'We encountered an internal error. Please try again.',
      pathname,
      500,
      reqId,
    );
  }
};

// â”€â”€â”€â”€â”€â”€â”€ Bucket Operations â”€â”€â”€â”€â”€â”€â”€

const handleListBuckets = async (reqId: string): Promise<Response> => {
  const buckets = await listBuckets();
  const xml = listBucketsXml(buckets, reqId);
  return s3Response(xml, 200, reqId, { 'content-type': 'application/xml' });
};

const handleCreateBucket = async (bucketName: string, reqId: string): Promise<Response> => {
  if (!/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(bucketName)) {
    return s3ErrorResponse(
      'InvalidBucketName',
      'The specified bucket is not valid.',
      `/${bucketName}`,
      400,
      reqId,
    );
  }
  const existing = await findBucketByName(bucketName);
  if (existing) {
    return s3ErrorResponse(
      'BucketAlreadyExists',
      'The requested bucket name is not available.',
      `/${bucketName}`,
      409,
      reqId,
    );
  }
  await createBucket(bucketName);
  return s3Response(null, 200, reqId);
};

const handleHeadBucket = async (bucketName: string, reqId: string): Promise<Response> => {
  const bucket = await findBucketByName(bucketName);
  if (!bucket) {
    return s3ErrorResponse(
      'NoSuchBucket',
      'The specified bucket does not exist.',
      `/${bucketName}`,
      404,
      reqId,
    );
  }
  return s3Response(null, 200, reqId);
};

const handleDeleteBucket = async (bucketName: string, reqId: string): Promise<Response> => {
  const bucket = await findBucketByName(bucketName);
  if (!bucket) {
    return s3ErrorResponse(
      'NoSuchBucket',
      'The specified bucket does not exist.',
      `/${bucketName}`,
      404,
      reqId,
    );
  }
  const objCount = await countBucketObjects(bucket.id);
  if (objCount > 0) {
    return s3ErrorResponse(
      'BucketNotEmpty',
      'The bucket you tried to delete is not empty.',
      `/${bucketName}`,
      409,
      reqId,
    );
  }
  await deleteBucket(bucketName);
  return s3Response(null, 204, reqId);
};

const handleGetBucketVersioning = async (bucketName: string, reqId: string): Promise<Response> => {
  const bucket = await findBucketByName(bucketName);
  if (!bucket) {
    return s3ErrorResponse(
      'NoSuchBucket',
      'The specified bucket does not exist.',
      `/${bucketName}`,
      404,
      reqId,
    );
  }
  return s3Response(bucketVersioningConfigurationXml(), 200, reqId, {
    'content-type': 'application/xml',
  });
};

// â”€â”€â”€â”€â”€â”€â”€ Object Operations â”€â”€â”€â”€â”€â”€â”€

const handleGetObject = async (
  bucket: string,
  key: string,
  _searchParams: URLSearchParams,
  headers: Record<string, string>,
  reqId: string,
): Promise<Response> => {
  const bucketRecord = await findBucketByName(bucket);
  if (!bucketRecord)
    return s3ErrorResponse(
      'NoSuchBucket',
      'The specified bucket does not exist.',
      `/${bucket}/${key}`,
      404,
      reqId,
    );

  const file = await findFileByBucketAndKey(bucketRecord.id, key);
  if (!file)
    return s3ErrorResponse(
      'NoSuchKey',
      'The specified key does not exist.',
      `/${bucket}/${key}`,
      404,
      reqId,
    );

  if (file.storageBackend === 'chunked') {
    const totalSize = Number(file.sizeBytes);
    const range = parseRangeHeader(headers.range || null, totalSize);
    if (range.type === 'invalid') {
      return s3ErrorResponse(
        'InvalidRange',
        'The requested range is not satisfiable.',
        `/${bucket}/${key}`,
        416,
        reqId,
        {
          'content-range': unsatisfiedContentRange(totalSize),
        },
      );
    }
    try {
      return await createChunkedObjectResponse({ file, range, reqId });
    } catch (error) {
      logger.warn('Chunked object content fetch failed', { key, error: getErrorMessage(error) });
      return s3ErrorResponse(
        'InternalError',
        'Failed to fetch object content from storage',
        `/${bucket}/${key}`,
        502,
        reqId,
      );
    }
  }

  if (file.multipartUploadId) {
    return handleGetMultipartObject(file, bucket, key, headers, reqId);
  }

  const fileInfo = await getFileInfo(file.telegramFileId);
  const redirectUrl = `https://api.telegram.org/file/bot${fileInfo.bot_token}/${fileInfo.file_path}`;

  const totalSize = file.sizeBytes;
  const range = parseRangeHeader(headers.range || null, totalSize);
  if (range.type === 'invalid') {
    return s3ErrorResponse(
      'InvalidRange',
      'The requested range is not satisfiable.',
      `/${bucket}/${key}`,
      416,
      reqId,
      {
        'content-range': unsatisfiedContentRange(totalSize),
      },
    );
  }

  if (!config.proxyS3Get) {
    // Legacy 302 redirect path (when proxy is disabled)
    return s3Response(null, 302, reqId, { location: redirectUrl });
  }

  const part: ObjectPartSource = {
    telegramFileId: file.telegramFileId,
    telegramUrl: redirectUrl,
    sizeBytes: file.sizeBytes,
    partNumber: 1,
  };

  try {
    return await createGetObjectResponse({
      reqId,
      contentType: file.mimeType,
      etag: file.fileHash || '',
      lastModified: file.createdAt instanceof Date ? file.createdAt : new Date(file.createdAt),
      totalSize: file.sizeBytes,
      parts: [part],
      range,
    });
  } catch (error) {
    logger.warn('Telegram content fetch failed', {
      fileId: file.telegramFileId,
      error: getErrorMessage(error),
    });
    return s3ErrorResponse(
      'InternalError',
      'Failed to fetch object content from storage',
      `/${bucket}/${key}`,
      502,
      reqId,
    );
  }
};

const handleGetMultipartObject = async (
  file: File,
  bucket: string,
  key: string,
  headers: Record<string, string>,
  reqId: string,
): Promise<Response> => {
  const uploadId = file.multipartUploadId!;
  const parts = await listMultipartParts(uploadId);

  if (parts.length === 0) {
    return s3ErrorResponse(
      'InternalError',
      'Multipart object has no parts.',
      `/${bucket}/${key}`,
      500,
      reqId,
    );
  }

  const totalSize = parts.reduce((sum, p) => sum + Number(p.sizeBytes), 0);
  const range = parseRangeHeader(headers.range || null, totalSize);
  if (range.type === 'invalid') {
    return s3ErrorResponse(
      'InvalidRange',
      'The requested range is not satisfiable.',
      `/${bucket}/${key}`,
      416,
      reqId,
      {
        'content-range': unsatisfiedContentRange(totalSize),
      },
    );
  }

  const sources: ObjectPartSource[] = [];
  for (const part of parts) {
    const fileInfo = await getFileInfo(part.telegramFileId);
    sources.push({
      telegramFileId: part.telegramFileId,
      telegramUrl: `https://api.telegram.org/file/bot${fileInfo.bot_token}/${fileInfo.file_path}`,
      sizeBytes: part.sizeBytes,
      partNumber: part.partNumber,
    });
  }

  if (!config.proxyS3Get) {
    return s3Response(null, 302, reqId, { location: sources[0].telegramUrl });
  }

  try {
    return await createGetObjectResponse({
      reqId,
      contentType: file.mimeType,
      etag: file.fileHash || parts.map((p) => p.etag).join('-'),
      lastModified: file.createdAt instanceof Date ? file.createdAt : new Date(file.createdAt),
      totalSize,
      parts: sources,
      range,
    });
  } catch (error) {
    logger.warn('Telegram multipart content fetch failed', {
      uploadId: file.multipartUploadId,
      error: getErrorMessage(error),
    });
    return s3ErrorResponse(
      'InternalError',
      'Failed to fetch object content from storage',
      `/${bucket}/${key}`,
      502,
      reqId,
    );
  }
};

const handleHeadObject = async (bucket: string, key: string, reqId: string): Promise<Response> => {
  const bucketRecord = await findBucketByName(bucket);
  if (!bucketRecord)
    return s3ErrorResponse(
      'NoSuchBucket',
      'The specified bucket does not exist.',
      `/${bucket}/${key}`,
      404,
      reqId,
    );

  const file = await findFileByBucketAndKey(bucketRecord.id, key);
  if (!file)
    return s3ErrorResponse(
      'NoSuchKey',
      'The specified key does not exist.',
      `/${bucket}/${key}`,
      404,
      reqId,
    );

  return s3Response(null, 200, reqId, {
    'content-type': file.mimeType,
    'content-length': String(file.sizeBytes),
    etag: `"${file.fileHash || nanoid(16)}"`,
    'last-modified':
      file.createdAt instanceof Date ? file.createdAt.toUTCString() : new Date().toUTCString(),
    'accept-ranges': 'bytes',
    'cache-control': 'public, max-age=31536000',
  });
};

const handlePutObject = async (
  bucket: string,
  key: string,
  searchParams: URLSearchParams,
  headers: Record<string, string>,
  req: Request,
  reqId: string,
): Promise<Response> => {
  const bucketRecord = await findBucketByName(bucket);
  if (!bucketRecord)
    return s3ErrorResponse(
      'NoSuchBucket',
      'The specified bucket does not exist.',
      `/${bucket}/${key}`,
      404,
      reqId,
    );

  if (searchParams.has('tagging')) {
    return s3Response(null, 204, reqId);
  }

  const copySource = headers['x-amz-copy-source'];
  if (copySource) {
    return handleCopyObject(bucket, key, copySource, headers, bucketRecord.id, reqId);
  }

  // Read raw body â€” S3 clients send raw binary, not multipart/form-data
  const body = await req.arrayBuffer();
  const fileBuffer = Buffer.from(body);
  const contentType = headers['content-type'] || 'application/octet-stream';
  const hash = computeHash(fileBuffer);

  const existing = await findFileByBucketAndKey(bucketRecord.id, key);
  if (existing) {
    return s3Response(null, 200, reqId, { etag: `"${hash}"` });
  }

  return await storeFileToTelegram(fileBuffer, hash, key, bucketRecord, contentType, reqId);
};

const storeFileToTelegram = async (
  buffer: Buffer,
  hash: string,
  key: string,
  bucketRecord: { id: string; name: string },
  contentType: string,
  reqId: string,
): Promise<Response> => {
  const tempPath = `/tmp/filedrop-s3-${nanoid()}`;
  await Bun.write(tempPath, buffer);

  const signatureBuffer = buffer.subarray(0, 16);
  const fileName = key.split('/').pop() || 'file';
  const { fileName: finalFileName, mimeType } = ensureExtension(
    fileName,
    signatureBuffer,
    contentType,
  );

  const bucketId = bucketRecord.id;
  const partFileNamePrefix = `s3-${bucketRecord.name}-${key.replace(/\//g, '_')}`;

  if (buffer.byteLength > config.telegramChunkSizeBytes) {
    const file = await storeFileInTelegramChunks({
      tempPath,
      partFileNamePrefix,
      fileName: finalFileName,
      mimeType,
      sizeBytes: buffer.byteLength,
      fileType: 'document',
      uploaderId: 0,
      bucketId,
      s3Key: key,
    });
    await cleanupTempFile(tempPath);
    return s3Response(null, 200, reqId, { etag: `"${file.fileHash}"` });
  }

  const forwardResult = await forwardToStorage(
    createReadStream(tempPath),
    partFileNamePrefix,
    'document',
  );

  const publicId = nanoid();
  const { db, files: fileSchema } = await import('../db/index');

  await db.insert(fileSchema).values({
    publicId,
    telegramFileId: forwardResult.telegramFileId,
    telegramFileUniqueId: forwardResult.telegramFileUniqueId,
    storageChatId: config.storageChatId,
    storageMessageId: forwardResult.storageMessageId,
    fileName: finalFileName,
    mimeType,
    sizeBytes: buffer.byteLength,
    fileType: 'document',
    uploaderId: 0,
    fileHash: hash,
    bucketId,
    s3Key: key,
    storageBackend: 'telegram',
    isDeleted: false,
    createdAt: new Date(),
    updatedAt: new Date(),
  });

  await cleanupTempFile(tempPath);

  return s3Response(null, 200, reqId, { etag: `"${hash}"` });
};

const handleCopyObject = async (
  _destBucket: string,
  destKey: string,
  rawCopySource: string,
  headers: Record<string, string>,
  destBucketId: string,
  reqId: string,
): Promise<Response> => {
  const copySource = decodeURIComponent(rawCopySource);
  const sourcePath = copySource.startsWith('/') ? copySource.slice(1) : copySource;
  const parts = sourcePath.split('/');
  const sourceBucket = parts[0];
  const sourceKey = parts.slice(1).join('/');

  const sourceBucketRecord = await findBucketByName(sourceBucket);
  if (!sourceBucketRecord)
    return s3ErrorResponse(
      'NoSuchBucket',
      'The specified bucket does not exist.',
      copySource,
      404,
      reqId,
    );

  const sourceFile = await findFileByBucketAndKey(sourceBucketRecord.id, sourceKey);
  if (!sourceFile)
    return s3ErrorResponse(
      'NoSuchKey',
      'The specified key does not exist.',
      copySource,
      404,
      reqId,
    );

  if (sourceFile.storageBackend === 'chunked') {
    // Copying chunked objects is not yet supported.
    return s3ErrorResponse(
      'NotImplemented',
      'Copying chunked objects is not yet implemented.',
      copySource,
      501,
      reqId,
    );
  }

  // Conditional copy: if-match / if-none-match checks
  const ifMatch = headers['x-amz-copy-source-if-match'];
  const ifNoneMatch = headers['x-amz-copy-source-if-none-match'];
  if (ifMatch && sourceFile.fileHash && ifMatch !== `"${sourceFile.fileHash}"`) {
    return s3ErrorResponse(
      'PreconditionFailed',
      'The preconditions you specified did not hold.',
      copySource,
      412,
      reqId,
    );
  }
  if (ifNoneMatch && sourceFile.fileHash && ifNoneMatch === `"${sourceFile.fileHash}"`) {
    return s3ErrorResponse(
      'PreconditionFailed',
      'The preconditions you specified did not hold.',
      copySource,
      412,
      reqId,
    );
  }

  const publicId = nanoid();
  const { db, files: fileSchema } = await import('../db/index');

  await db.insert(fileSchema).values({
    publicId,
    telegramFileId: sourceFile.telegramFileId,
    telegramFileUniqueId: sourceFile.telegramFileUniqueId,
    storageChatId: sourceFile.storageChatId,
    storageMessageId: sourceFile.storageMessageId,
    fileName: sourceFile.fileName,
    mimeType: sourceFile.mimeType,
    sizeBytes: sourceFile.sizeBytes,
    fileType: sourceFile.fileType,
    uploaderId: 0,
    fileHash: sourceFile.fileHash,
    bucketId: destBucketId,
    s3Key: destKey,
    storageBackend: 'telegram',
    isDeleted: false,
    createdAt: new Date(),
    updatedAt: new Date(),
  });

  const xml = copyObjectResultXml(sourceFile.fileHash || nanoid(16), new Date());
  return s3Response(xml, 200, reqId, { 'content-type': 'application/xml' });
};

const handleDeleteObject = async (
  bucket: string,
  key: string,
  reqId: string,
): Promise<Response> => {
  const bucketRecord = await findBucketByName(bucket);
  if (!bucketRecord)
    return s3ErrorResponse(
      'NoSuchBucket',
      'The specified bucket does not exist.',
      `/${bucket}/${key}`,
      404,
      reqId,
    );

  await softDeleteFile(bucketRecord.id, key);
  return s3Response(null, 204, reqId);
};

const handleDeleteObjects = async (
  bucket: string,
  body: string,
  reqId: string,
): Promise<Response> => {
  const bucketRecord = await findBucketByName(bucket);
  if (!bucketRecord)
    return s3ErrorResponse(
      'NoSuchBucket',
      'The specified bucket does not exist.',
      `/${bucket}`,
      404,
      reqId,
    );

  const { keys, quiet } = parseDeleteObjectsBody(body);
  const deletedKeys: string[] = [];
  for (const key of keys) {
    const ok = await softDeleteFile(bucketRecord.id, key);
    if (ok) deletedKeys.push(key);
  }
  const xml = quiet ? deleteResultXml([], []) : deleteResultXml(deletedKeys, []);
  return s3Response(xml, 200, reqId, { 'content-type': 'application/xml' });
};

// â”€â”€â”€â”€â”€â”€â”€ Object Listing â”€â”€â”€â”€â”€â”€â”€

const handleListObjectsV1 = async (
  bucket: string,
  searchParams: URLSearchParams,
  reqId: string,
): Promise<Response> => {
  const bucketRecord = await findBucketByName(bucket);
  if (!bucketRecord)
    return s3ErrorResponse(
      'NoSuchBucket',
      'The specified bucket does not exist.',
      `/${bucket}`,
      404,
      reqId,
    );

  const prefix = searchParams.get('prefix') || '';
  const delimiter = searchParams.get('delimiter') || null;
  const maxKeys = Math.min(parseInt(searchParams.get('max-keys') || '1000', 10), 1000);
  const marker = searchParams.get('marker') || null;
  const encodingType = searchParams.get('encoding-type') || null;

  const { objects, prefixes: commonPrefixes } = await listObjectsByPrefix(
    bucketRecord.id,
    prefix,
    delimiter,
    maxKeys,
    marker,
  );

  const isTruncated = objects.length > maxKeys;
  const displayObjects = objects.slice(0, maxKeys);
  const nextMarker = isTruncated
    ? (displayObjects[displayObjects.length - 1]?.s3Key ?? null)
    : null;

  const xml = listBucketResultXml(
    bucket,
    displayObjects.map((o) => ({
      key: o.s3Key ?? '',
      sizeBytes: o.sizeBytes,
      etag: o.fileHash || nanoid(16),
      lastModified: o.createdAt instanceof Date ? o.createdAt : new Date(),
      mimeType: o.mimeType,
    })),
    commonPrefixes,
    isTruncated,
    marker,
    maxKeys,
    prefix,
    delimiter,
    nextMarker,
    reqId,
    encodingType,
  );

  return s3Response(xml, 200, reqId, { 'content-type': 'application/xml' });
};

const handleListObjectsV2 = async (
  bucket: string,
  searchParams: URLSearchParams,
  reqId: string,
): Promise<Response> => {
  const bucketRecord = await findBucketByName(bucket);
  if (!bucketRecord)
    return s3ErrorResponse(
      'NoSuchBucket',
      'The specified bucket does not exist.',
      `/${bucket}`,
      404,
      reqId,
    );

  const prefix = searchParams.get('prefix') || '';
  const delimiter = searchParams.get('delimiter') || null;
  const maxKeys = Math.min(parseInt(searchParams.get('max-keys') || '1000', 10), 1000);
  const continuationToken = searchParams.get('continuation-token') || null;
  const startAfter = searchParams.get('start-after') || null;
  const encodingType = searchParams.get('encoding-type') || null;

  const { objects, prefixes: commonPrefixes } = await listObjectsByPrefix(
    bucketRecord.id,
    prefix,
    delimiter,
    maxKeys,
    continuationToken || startAfter,
  );

  const isTruncated = objects.length > maxKeys;
  const displayObjects = objects.slice(0, maxKeys);
  const nextContinuationToken = isTruncated
    ? (displayObjects[displayObjects.length - 1]?.s3Key ?? null)
    : null;

  const xml = listBucketV2ResultXml(
    bucket,
    displayObjects.map((o) => ({
      key: o.s3Key ?? '',
      sizeBytes: o.sizeBytes,
      etag: o.fileHash || nanoid(16),
      lastModified: o.createdAt instanceof Date ? o.createdAt : new Date(),
      mimeType: o.mimeType,
    })),
    commonPrefixes,
    isTruncated,
    maxKeys,
    prefix,
    delimiter,
    continuationToken,
    nextContinuationToken,
    displayObjects.length,
    reqId,
    encodingType,
  );

  return s3Response(xml, 200, reqId, { 'content-type': 'application/xml' });
};

// â”€â”€â”€â”€â”€â”€â”€ Multipart Upload â”€â”€â”€â”€â”€â”€â”€

const handleCreateMultipartUpload = async (
  bucket: string,
  key: string,
  _searchParams: URLSearchParams,
  reqId: string,
): Promise<Response> => {
  const bucketRecord = await findBucketByName(bucket);
  if (!bucketRecord)
    return s3ErrorResponse(
      'NoSuchBucket',
      'The specified bucket does not exist.',
      `/${bucket}/${key}`,
      404,
      reqId,
    );

  const uploadId = await createMultipartUpload(bucketRecord.id, key, 's3');

  const xml = initiateMultipartUploadXml(bucket, key, uploadId);
  return s3Response(xml, 200, reqId, { 'content-type': 'application/xml' });
};

const handleUploadPart = async (
  bucket: string,
  key: string,
  searchParams: URLSearchParams,
  req: Request,
  reqId: string,
): Promise<Response> => {
  const uploadId = searchParams.get('uploadId')!;
  const partNumber = parseInt(searchParams.get('partNumber')!, 10);
  if (partNumber < 1 || partNumber > 10000) {
    return s3ErrorResponse(
      'InvalidArgument',
      'Part number must be an integer between 1 and 10000',
      `/${bucket}/${key}`,
      400,
      reqId,
    );
  }

  const multipart = await findMultipartUpload(uploadId);
  if (!multipart || multipart.s3Key !== key) {
    return s3ErrorResponse(
      'NoSuchUpload',
      'The specified upload does not exist.',
      `/${bucket}/${key}`,
      404,
      reqId,
    );
  }

  const body = await req.arrayBuffer();
  const buffer = Buffer.from(body);

  if (buffer.byteLength > config.telegramChunkSizeBytes) {
    return s3ErrorResponse(
      'EntityTooLarge',
      `Your proposed upload part size (${buffer.byteLength} bytes) exceeds the maximum allowed part size (${config.telegramChunkSizeBytes} bytes) for this storage backend. Use smaller part sizes.`,
      `/${bucket}/${key}`,
      400,
      reqId,
    );
  }

  const tempPath = `/tmp/filedrop-mp-${nanoid()}`;
  await Bun.write(tempPath, buffer);

  const forwardResult = await forwardToStorage(
    createReadStream(tempPath),
    `mp-${uploadId}-part-${partNumber}`,
    'document',
  );

  await cleanupTempFile(tempPath);

  const etag = computeHash(buffer);
  await insertMultipartPart({
    uploadId,
    partNumber,
    telegramFileId: forwardResult.telegramFileId,
    telegramFileUniqueId: forwardResult.telegramFileUniqueId,
    storageMessageId: forwardResult.storageMessageId,
    sizeBytes: buffer.byteLength,
    etag,
  });

  return s3Response(null, 200, reqId, { etag: `"${etag}"` });
};

const handleCompleteMultipartUpload = async (
  bucket: string,
  key: string,
  searchParams: URLSearchParams,
  body: string,
  reqId: string,
): Promise<Response> => {
  const uploadId = searchParams.get('uploadId')!;
  const multipart = await findMultipartUpload(uploadId);
  if (!multipart) {
    return s3ErrorResponse(
      'NoSuchUpload',
      'The specified upload does not exist.',
      `/${bucket}/${key}`,
      404,
      reqId,
    );
  }

  const parts = parseCompleteMultipartBody(body);
  const storedParts = await listMultipartParts(uploadId);

  // Validate ascending part order
  const partNumbers = parts.map((p) => p.partNumber);
  if (partNumbers.length > 1 && partNumbers.some((n, i) => i > 0 && n <= partNumbers[i - 1])) {
    return s3ErrorResponse(
      'InvalidPartOrder',
      'The list of parts was not in ascending order.',
      `/${bucket}/${key}`,
      400,
      reqId,
    );
  }

  if (parts.length !== storedParts.length) {
    return s3ErrorResponse(
      'InvalidPart',
      'One or more specified parts could not be found.',
      `/${bucket}/${key}`,
      400,
      reqId,
    );
  }

  const totalSize = storedParts.reduce((sum, p) => sum + p.sizeBytes, 0);

  const publicId = nanoid();
  const { db, files: fileSchema } = await import('../db/index');

  await db.insert(fileSchema).values({
    publicId,
    telegramFileId: storedParts[0].telegramFileId,
    telegramFileUniqueId: storedParts[0].telegramFileUniqueId,
    storageChatId: config.storageChatId,
    storageMessageId: storedParts[0].storageMessageId,
    fileName: key.split('/').pop() || 'file',
    mimeType: 'application/octet-stream',
    sizeBytes: totalSize,
    fileType: 'document',
    uploaderId: 0,
    bucketId: multipart.bucketId,
    s3Key: key,
    storageBackend: 'telegram',
    isDeleted: false,
    multipartUploadId: uploadId,
    createdAt: new Date(),
    updatedAt: new Date(),
  });

  await completeMultipartUpload(uploadId);

  const location = `${config.baseUrl}/${bucket}/${key}`;
  const combinedEtag = storedParts.map((p) => p.etag).join('-');
  const xml = completeMultipartUploadXml(bucket, key, combinedEtag, location);

  return s3Response(xml, 200, reqId, { 'content-type': 'application/xml' });
};

const handleListMultipartUploads = async (
  bucket: string,
  searchParams: URLSearchParams,
  reqId: string,
): Promise<Response> => {
  const bucketRecord = await findBucketByName(bucket);
  if (!bucketRecord)
    return s3ErrorResponse(
      'NoSuchBucket',
      'The specified bucket does not exist.',
      `/${bucket}`,
      404,
      reqId,
    );

  const maxUploads = Math.min(parseInt(searchParams.get('max-uploads') || '1000', 10), 1000);
  const keyMarker = searchParams.get('key-marker') || null;
  const { uploads, isTruncated, nextKeyMarker } = await listMultipartUploadsByBucket(
    bucketRecord.id,
    maxUploads,
    keyMarker,
  );

  const xml = listMultipartUploadsXml(
    bucket,
    uploads.map((u) => ({
      key: u.s3Key,
      uploadId: u.uploadId,
      initiatedAt: u.initiatedAt,
      initiatedBy: u.initiatedBy,
    })),
    maxUploads,
    isTruncated,
    nextKeyMarker,
    reqId,
  );

  return s3Response(xml, 200, reqId, { 'content-type': 'application/xml' });
};

const handleAbortMultipartUpload = async (
  bucket: string,
  key: string,
  searchParams: URLSearchParams,
  reqId: string,
): Promise<Response> => {
  const uploadId = searchParams.get('uploadId')!;
  const multipart = await findMultipartUpload(uploadId);
  if (!multipart) {
    return s3ErrorResponse(
      'NoSuchUpload',
      'The specified upload does not exist.',
      `/${bucket}/${key}`,
      404,
      reqId,
    );
  }

  await abortMultipartUpload(uploadId);
  return s3Response(null, 204, reqId);
};

const handleListParts = async (
  bucket: string,
  key: string,
  searchParams: URLSearchParams,
  reqId: string,
): Promise<Response> => {
  const uploadId = searchParams.get('uploadId')!;
  const multipart = await findMultipartUpload(uploadId);
  if (!multipart) {
    return s3ErrorResponse(
      'NoSuchUpload',
      'The specified upload does not exist.',
      `/${bucket}/${key}`,
      404,
      reqId,
    );
  }

  const parts = await listMultipartParts(uploadId);
  const maxParts = Math.min(parseInt(searchParams.get('max-parts') || '1000', 10), 1000);

  const xml = listPartsXml(
    bucket,
    key,
    uploadId,
    parts.map((p) => ({
      partNumber: p.partNumber,
      etag: p.etag,
      sizeBytes: p.sizeBytes,
      createdAt: p.createdAt,
    })),
    maxParts,
    false,
    reqId,
  );

  return s3Response(xml, 200, reqId, { 'content-type': 'application/xml' });
};
¨ý¹(¹æxi&–Ùimport { createWriteStream } from 'node:fs';
import { nanoid } from 'nanoid';
import { findFileByHash } from '../db/files';
import { config } from '../env';
import { storeFileInTelegramChunks } from '../utils/chunked-storage';
import {
  buildUploadResponse,
  checkFileSize,
  cleanupTempFile,
  computeHash,
  ensureExtension,
  extractMimeType,
  getErrorMessage,
  getFileType,
} from '../utils/file';
import logger from '../utils/logger';
import { metricsCollector } from '../utils/metrics';
import { enqueuePreparedUpload, type PreparedUpload } from '../utils/uploadBatcher';

interface JsonUploadPayload {
  file?: unknown;
  fileName?: string;
}

const parseBase64File = (file: string): { base64Data: string; mimeType: string } => {
  if (!file.startsWith('data:')) {
    return { base64Data: file, mimeType: 'application/octet-stream' };
  }

  const match = file.match(/^data:([^;]+);base64,(.+)$/);
  return match
    ? { base64Data: match[2], mimeType: match[1] }
    : { base64Data: file, mimeType: 'application/octet-stream' };
};

const normalizeFileType = (mimeType: string, fileName: string): string => {
  const fileType = getFileType(mimeType, fileName);
  return fileType === 'application' ? 'document' : fileType;
};

const JSON_UPLOAD_LIMIT_BYTES = 50 * 1024 * 1024;
const SIGNATURE_BYTES = 16;

const getContentLength = (req: Request): number | null => {
  const value = req.headers.get('content-length');
  if (!value) return null;

  const parsed = Number.parseInt(value, 10);
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
};

const rejectOversizedRequest = (req: Request): Response | null => {
  const contentLength = getContentLength(req);
  if (contentLength !== null && contentLength > config.maxRequestBodyBytes) {
    return Response.json({ error: 'Request body too large' }, { status: 413 });
  }

  return null;
};

const streamFileToTemp = async (file: File, maxSizeBytes: number): Promise<PreparedUpload> => {
  const tempPath = `/tmp/teleuploader-${nanoid()}`;
  const writer = createWriteStream(tempPath);
  const hasher = new Bun.CryptoHasher('sha256');
  const reader = file.stream().getReader();
  const signatureChunks: Buffer[] = [];
  let signatureBytes = 0;
  let sizeBytes = 0;

  const writeChunk = async (chunk: Buffer): Promise<void> => {
    if (!writer.write(chunk)) {
      await new Promise<void>((resolve, reject) => {
        writer.once('drain', resolve);
        writer.once('error', reject);
      });
    }
  };

  const finishWriter = async (): Promise<void> => {
    await new Promise<void>((resolve, reject) => {
      writer.end(() => resolve());
      writer.once('error', reject);
    });
  };

  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      const chunk = Buffer.from(value);
      sizeBytes += chunk.byteLength;
      if (sizeBytes > maxSizeBytes) {
        throw new Error('File size exceeds upload limit');
      }

      hasher.update(chunk);
      await writeChunk(chunk);

      if (signatureBytes < SIGNATURE_BYTES) {
        const remaining = SIGNATURE_BYTES - signatureBytes;
        const signatureChunk = chunk.subarray(0, remaining);
        signatureChunks.push(signatureChunk);
        signatureBytes += signatureChunk.byteLength;
      }
    }

    await finishWriter();

    return {
      tempPath,
      fileHash: hasher.digest('hex'),
      sizeBytes,
      signatureBuffer: Buffer.concat(signatureChunks, signatureBytes),
    };
  } catch (error) {
    writer.destroy();
    await cleanupTempFile(tempPath);
    throw error;
  } finally {
    reader.releaseLock();
  }
};

const writeBufferToTemp = async (fileBuffer: Buffer, fileHash: string): Promise<PreparedUpload> => {
  const tempPath = `/tmp/teleuploader-${nanoid()}`;
  try {
    await Bun.write(tempPath, fileBuffer);
    return {
      tempPath,
      fileHash,
      sizeBytes: fileBuffer.byteLength,
      signatureBuffer: fileBuffer.subarray(0, SIGNATURE_BYTES),
    };
  } catch (error) {
    await cleanupTempFile(tempPath);
    throw error;
  }
};

export const handleUpload = async (req: Request): Promise<Response> => {
  const startTime = performance.now();
  try {
    const contentType = req.headers.get('content-type') || '';
    const oversizedResponse = rejectOversizedRequest(req);
    if (oversizedResponse) return oversizedResponse;

    if (contentType.includes('multipart/form-data')) {
      return handleMultipartUpload(req);
    } else if (contentType.includes('application/json')) {
      return handleJSONUpload(req);
    }

    return Response.json(
      { error: 'Unsupported content type. Use multipart/form-data or application/json' },
      { status: 400 },
    );
  } catch (error: unknown) {
    metricsCollector.recordError();
    const message = getErrorMessage(error);
    logger.error('Upload error', { error: message });
    return Response.json({ error: message }, { status: 500 });
  } finally {
    metricsCollector.recordUploadTime(performance.now() - startTime);
  }
};

const handleMultipartUpload = async (req: Request): Promise<Response> => {
  try {
    const formData = await req.formData();
    const file = formData.get('file');
    const fileName =
      (formData.get('fileName') as string) || (file instanceof File ? file.name : null) || 'file';

    if (!file || !(file instanceof File)) {
      return Response.json({ error: 'No file provided' }, { status: 400 });
    }

    if (file.size > config.maxRequestBodyBytes) {
      return Response.json({ error: 'File size exceeds upload limit' }, { status: 413 });
    }

    const prepared = await streamFileToTemp(file, config.maxRequestBodyBytes);

    const existingFile = await findFileByHash(prepared.fileHash);
    if (existingFile) {
      await cleanupTempFile(prepared.tempPath);
      return Response.json(buildUploadResponse(existingFile, config.baseUrl), { status: 200 });
    }

    const rawMimeType = file.type || extractMimeType({}, req) || 'application/octet-stream';
    const { fileName: finalFileName, mimeType } = ensureExtension(
      fileName,
      prepared.signatureBuffer,
      rawMimeType,
    );
    const fileType = getFileType(mimeType, finalFileName);

    if (!checkFileSize(prepared.sizeBytes, fileType)) {
      await cleanupTempFile(prepared.tempPath);
      return Response.json({ error: `File size exceeds ${fileType} limit` }, { status: 400 });
    }

    if (prepared.sizeBytes > config.telegramChunkSizeBytes) {
      const file = await storeFileInTelegramChunks({
        tempPath: prepared.tempPath,
        partFileNamePrefix: `direct-${prepared.fileHash?.slice(0, 16) || 'upload'}`,
        fileName: finalFileName,
        mimeType,
        sizeBytes: prepared.sizeBytes,
        fileType,
        uploaderId: 0,
      });
      await cleanupTempFile(prepared.tempPath);
      return Response.json(buildUploadResponse(file, config.baseUrl), { status: 200 });
    }

    const uploaded = await enqueuePreparedUpload({
      prepared,
      fileName: finalFileName,
      mimeType,
      fileType,
    });

    return Response.json(buildUploadResponse(uploaded, config.baseUrl), { status: 200 });
  } catch (error: unknown) {
    const message = getErrorMessage(error);
    logger.error('Multipart upload error', { error: message });
    return Response.json({ error: message }, { status: 500 });
  }
};

const handleJSONUpload = async (req: Request): Promise<Response> => {
  try {
    const { file, fileName = 'file' } = (await req.json()) as JsonUploadPayload;

    if (!file || typeof file !== 'string') {
      return Response.json(
        { error: 'Invalid JSON. Must include "file" (base64) and optional "fileName"' },
        { status: 400 },
      );
    }

    const { base64Data, mimeType: rawMimeType } = parseBase64File(file);
    const estimatedSizeBytes = Math.floor((base64Data.length * 3) / 4);
    if (
      estimatedSizeBytes > JSON_UPLOAD_LIMIT_BYTES ||
      estimatedSizeBytes > config.maxRequestBodyBytes
    ) {
      return Response.json(
        {
          error:
            'JSON base64 uploads are limited to 50MB. Use multipart/form-data for larger files',
        },
        { status: 400 },
      );
    }

    const fileBytes = Buffer.from(base64Data, 'base64');
    const hash = computeHash(fileBytes);

    const existingFile = await findFileByHash(hash);
    if (existingFile) {
      return Response.json(buildUploadResponse(existingFile, config.baseUrl), { status: 200 });
    }

    const { fileName: finalFileName, mimeType } = ensureExtension(fileName, fileBytes, rawMimeType);
    const fileType = normalizeFileType(mimeType, finalFileName);

    if (!checkFileSize(fileBytes.byteLength, fileType)) {
      return Response.json({ error: `File size exceeds ${fileType} limit` }, { status: 400 });
    }

    const prepared = await writeBufferToTemp(fileBytes, hash);

    if (prepared.sizeBytes > config.telegramChunkSizeBytes) {
      const file = await storeFileInTelegramChunks({
        tempPath: prepared.tempPath,
        partFileNamePrefix: `direct-${prepared.fileHash?.slice(0, 16) || 'json'}`,
        fileName: finalFileName,
        mimeType,
        sizeBytes: prepared.sizeBytes,
        fileType,
        uploaderId: 0,
      });
      await cleanupTempFile(prepared.tempPath);
      return Response.json(buildUploadResponse(file, config.baseUrl), { status: 200 });
    }

    const uploaded = await enqueuePreparedUpload({
      prepared,
      fileName: finalFileName,
      mimeType,
      fileType,
    });

    return Response.json(buildUploadResponse(uploaded, config.baseUrl), { status: 200 });
  } catch (error: unknown) {
    const message = getErrorMessage(error);
    logger.error('JSON upload error', { error: message });
    return Response.json({ error: message }, { status: 500 });
  }
};
7fmšãËwx# ÜÿéLáL°Åfiledrop³Ñâfiledrop³¿ª£M´»x´+KÔimport { createReadStream } from 'node:fs';
import { nanoid } from 'nanoid';
import { createBucket, deleteBucket, findBucketByName, listBuckets } from '../db/buckets';
import {
  countBucketObjects,
  findFileByBucketAndKey,
  listObjectsByPrefix,
  softDeleteFile,
} from '../db/files-ext';
import { config } from '../env';
import { createChunkedObjectResponse, storeFileInTelegramChunks } from '../utils/chunked-storage';
import { cleanupTempFile, computeHash, ensureExtension, getErrorMessage } from '../utils/file';
import logger from '../utils/logger';
import { forwardToStorage, getFileInfo } from '../utils/telegram';

type RouteParams = { bucket?: string; key?: string };

const json = (data: unknown, status = 200) => Response.json(data, { status });

const jsonError = (error: string, status: number) => Response.json({ error }, { status });

// â”€â”€â”€â”€â”€â”€â”€ Bucket endpoints â”€â”€â”€â”€â”€â”€â”€

export const handleListBucketsV1 = async (): Promise<Response> => {
  const buckets = await listBuckets();
  const result = await Promise.all(
    buckets.map(async (b) => ({
      id: b.id,
      name: b.name,
      createdAt: b.createdAt.toISOString(),
      objectCount: await countBucketObjects(b.id),
    })),
  );
  return json({ buckets: result });
};

export const handleCreateBucketV1 = async (req: Request): Promise<Response> => {
  const body = (await req.json()) as { name?: string };
  if (!body.name || !/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(body.name)) {
    return jsonError('Invalid bucket name. Use lowercase, 3-63 chars, no underscore', 400);
  }
  const existing = await findBucketByName(body.name);
  if (existing) return jsonError('Bucket already exists', 409);
  const bucket = await createBucket(body.name);
  return json({ id: bucket.id, name: bucket.name }, 201);
};

export const handleDeleteBucketV1 = async (
  _req: Request,
  params: RouteParams,
): Promise<Response> => {
  const bucket = await findBucketByName(params.bucket!);
  if (!bucket) return jsonError('Bucket not found', 404);
  const count = await countBucketObjects(bucket.id);
  if (count > 0) return jsonError('Bucket is not empty', 409);
  await deleteBucket(params.bucket!);
  return json({ success: true });
};

// â”€â”€â”€â”€â”€â”€â”€ Object endpoints â”€â”€â”€â”€â”€â”€â”€

export const handleListObjectsV1 = async (req: Request, params: RouteParams): Promise<Response> => {
  const bucket = await findBucketByName(params.bucket!);
  if (!bucket) return jsonError('Bucket not found', 404);

  const url = new URL(req.url);
  const prefix = url.searchParams.get('prefix') || '';
  const delimiter = url.searchParams.get('delimiter') || '/';
  const maxKeys = parseInt(url.searchParams.get('max-keys') || '1000', 10);
  const continuationToken = url.searchParams.get('continuation-token') || null;

  const { objects, prefixes } = await listObjectsByPrefix(
    bucket.id,
    prefix,
    delimiter,
    maxKeys,
    continuationToken,
  );
  const isTruncated = objects.length > maxKeys;
  const displayObjects = objects.slice(0, maxKeys);

  return json({
    objects: displayObjects.map((o) => ({
      key: o.s3Key,
      fileName: o.fileName,
      mimeType: o.mimeType,
      sizeBytes: Number(o.sizeBytes),
      fileType: o.fileType,
      etag: o.fileHash,
      lastModified:
        o.createdAt instanceof Date
          ? o.createdAt.toISOString()
          : new Date(o.createdAt).toISOString(),
      downloadUrl: `${config.baseUrl}/f/${o.publicId}`,
    })),
    prefixes,
    isTruncated,
    nextContinuationToken: isTruncated ? displayObjects[displayObjects.length - 1]?.s3Key : null,
  });
};

export const handleUploadObjectV1 = async (
  req: Request,
  params: RouteParams,
): Promise<Response> => {
  const bucket = await findBucketByName(params.bucket!);
  if (!bucket) return jsonError('Bucket not found', 404);

  const formData = await req.formData();
  const file = formData.get('file');

  if (!file || !(file instanceof File)) {
    return jsonError('No file provided', 400);
  }

  const key = (formData.get('key') as string) || file.name;
  const buffer = Buffer.from(await file.arrayBuffer());
  const hash = computeHash(buffer);

  const tempPath = `/tmp/teleuploader-web-${nanoid()}`;
  await Bun.write(tempPath, buffer);

  const signatureBuffer = buffer.subarray(0, 16);
  const { fileName: finalFileName, mimeType } = ensureExtension(
    key.split('/').pop() || 'file',
    signatureBuffer,
    file.type || 'application/octet-stream',
  );

  const partFileNamePrefix = `s3-${bucket.name}-${key.replace(/\//g, '_')}`;

  if (buffer.byteLength > config.telegramChunkSizeBytes) {
    const file = await storeFileInTelegramChunks({
      tempPath,
      partFileNamePrefix,
      fileName: finalFileName,
      mimeType,
      sizeBytes: buffer.byteLength,
      fileType: 'document',
      uploaderId: 0,
      bucketId: bucket.id,
      s3Key: key,
    });
    await cleanupTempFile(tempPath);
    return json(
      {
        key,
        size: buffer.byteLength,
        etag: hash,
        downloadUrl: `${config.baseUrl}/f/${file.publicId}`,
      },
      201,
    );
  }

  const forwardResult = await forwardToStorage(
    createReadStream(tempPath),
    partFileNamePrefix,
    'document',
  );

  const publicId = nanoid();
  const { db, files: fileSchema } = await import('../db/index');

  await db.insert(fileSchema).values({
    publicId,
    telegramFileId: forwardResult.telegramFileId,
    telegramFileUniqueId: forwardResult.telegramFileUniqueId,
    storageChatId: config.storageChatId,
    storageMessageId: forwardResult.storageMessageId,
    fileName: finalFileName,
    mimeType,
    sizeBytes: buffer.byteLength,
    fileType: 'document',
    uploaderId: 0,
    fileHash: hash,
    bucketId: bucket.id,
    s3Key: key,
    storageBackend: 'telegram',
    isDeleted: false,
    createdAt: new Date(),
    updatedAt: new Date(),
  });

  await cleanupTempFile(tempPath);

  return json(
    { key, size: buffer.byteLength, etag: hash, downloadUrl: `${config.baseUrl}/f/${publicId}` },
    201,
  );
};

export const handleDeleteObjectV1 = async (
  _req: Request,
  params: RouteParams,
): Promise<Response> => {
  const bucket = await findBucketByName(params.bucket!);
  if (!bucket) return jsonError('Bucket not found', 404);
  await softDeleteFile(bucket.id, params.key!);
  return json({ success: true });
};

export const handleDownloadObjectV1 = async (
  _req: Request,
  params: RouteParams,
): Promise<Response> => {
  const bucket = await findBucketByName(params.bucket!);
  if (!bucket) return jsonError('Bucket not found', 404);

  const file = await findFileByBucketAndKey(bucket.id, params.key!);
  if (!file) return jsonError('Object not found', 404);

  if (file.storageBackend === 'chunked') {
    const range = { type: 'none' as const };
    return createChunkedObjectResponse({ file, range, reqId: '' });
  }

  const fileInfo = await getFileInfo(file.telegramFileId);
  const redirectUrl = `https://api.telegram.org/file/bot${fileInfo.bot_token}/${fileInfo.file_path}`;

  return new Response(null, { status: 302, headers: { Location: redirectUrl } });
};

export const handleCopyObjectV1 = async (req: Request, params: RouteParams): Promise<Response> => {
  const body = (await req.json()) as {
    sourceKey?: string;
    destBucket?: string;
    destKey?: string;
  };

  if (!body.sourceKey || !body.destKey) {
    return jsonError('sourceKey and destKey are required', 400);
  }

  const destBucketName = body.destBucket || params.bucket!;
  const sourceBucket = await findBucketByName(params.bucket!);
  const destBucket = await findBucketByName(destBucketName);

  if (!sourceBucket || !destBucket) return jsonError('Bucket not found', 404);

  const sourceFile = await findFileByBucketAndKey(sourceBucket.id, body.sourceKey);
  if (!sourceFile) return jsonError('Source object not found', 404);

  if (sourceFile.storageBackend === 'chunked') {
    return json({ error: 'Copying chunked objects is not implemented' }, 501);
  }

  const publicId = nanoid();
  const { db, files: fileSchema } = await import('../db/index');

  await db.insert(fileSchema).values({
    publicId,
    telegramFileId: sourceFile.telegramFileId,
    telegramFileUniqueId: sourceFile.telegramFileUniqueId,
    storageChatId: sourceFile.storageChatId,
    storageMessageId: sourceFile.storageMessageId,
    fileName: sourceFile.fileName,
    mimeType: sourceFile.mimeType,
    sizeBytes: sourceFile.sizeBytes,
    fileType: sourceFile.fileType,
    uploaderId: 0,
    fileHash: sourceFile.fileHash,
    bucketId: destBucket.id,
    s3Key: body.destKey,
    storageBackend: 'telegram',
    isDeleted: false,
    createdAt: new Date(),
    updatedAt: new Date(),
  });

  return json({ sourceKey: body.sourceKey, destKey: body.destKey, destBucket: destBucketName });
};

// â”€â”€â”€â”€â”€â”€â”€ Router â”€â”€â”€â”€â”€â”€â”€

export const handleWebApiV1 = async (req: Request): Promise<Response> => {
  const url = new URL(req.url);
  const pathname = url.pathname.replace(/^\/api\/v1/, '');
  const parts = pathname.split('/').filter(Boolean);
  const method = req.method;

  try {
    // GET /api/v1/buckets
    if (parts.length === 1 && parts[0] === 'buckets' && method === 'GET') {
      return await handleListBucketsV1();
    }

    // POST /api/v1/buckets
    if (parts.length === 1 && parts[0] === 'buckets' && method === 'POST') {
      return await handleCreateBucketV1(req);
    }

    // DELETE /api/v1/buckets/{name}
    if (parts.length === 2 && parts[0] === 'buckets' && method === 'DELETE') {
      return await handleDeleteBucketV1(req, { bucket: parts[1] });
    }

    // GET /api/v1/buckets/{name}/objects
    if (
      parts.length === 3 &&
      parts[0] === 'buckets' &&
      parts[2] === 'objects' &&
      method === 'GET'
    ) {
      return await handleListObjectsV1(req, { bucket: parts[1] });
    }

    // POST /api/v1/buckets/{name}/upload
    if (
      parts.length === 3 &&
      parts[0] === 'buckets' &&
      parts[2] === 'upload' &&
      method === 'POST'
    ) {
      return await handleUploadObjectV1(req, { bucket: parts[1] });
    }

    // POST /api/v1/buckets/{name}/copy
    if (parts.length === 3 && parts[0] === 'buckets' && parts[2] === 'copy' && method === 'POST') {
      return await handleCopyObjectV1(req, { bucket: parts[1] });
    }

    // DELETE /api/v1/buckets/{name}/{key+}
    if (parts.length >= 3 && parts[0] === 'buckets' && method === 'DELETE') {
      const bucket = parts[1];
      const key = parts.slice(2).join('/');
      return await handleDeleteObjectV1(req, { bucket, key });
    }

    // GET /api/v1/buckets/{name}/download/{key+}
    if (
      parts.length >= 4 &&
      parts[0] === 'buckets' &&
      parts[2] === 'download' &&
      method === 'GET'
    ) {
      const bucket = parts[1];
      const key = parts.slice(3).join('/');
      return await handleDownloadObjectV1(req, { bucket, key });
    }

    return jsonError('Not found', 404);
  } catch (error: unknown) {
    logger.error('Web API error', { path: pathname, error: getErrorMessage(error) });
    return jsonError('Internal server error', 500);
  }
};
*Ý>»åÖBx êÿ´W°W°sfiledrop³5b°5îx“´DxœmTKlUU“ºn•&Äÿâ™7vãñÄ?UP�œ$Š F´DI+$ª.&ž{T{f˜OMR¬.@ µHm¸lX „X‰OK7l`‘ U‘Jb�X¢,¸oÆ‰�&³yóî»Ÿsç«WnŸšÔZ¦a9ä
±]5ZçÏWH‡¬ZF‹Hº¡ÒJÍZ3Cš
Ý\¿z…¨+²ª5©MÛûY®5hKÙ5)•Êê
*ïºÕt›ZÎs¨¹¨XŽ] MÍî¿:k&%gi{GTÕM×Ùë®ÌâMÅpì“ï}´= °}ˆ¤úeøv@x@À˜>]¶Ÿ(cruÚkÕ°ÚŠ¥ž3–ÃRê´@êÔƒUÕW�^ø²C›´n)-´…õAù¸T_×L‰¼Nt·ÙDÙ‘Ã¹1Dæœu[+Ôª ˜�SðÑá(#�¬dîÈ71xHªÁÜ‘1ø~8ž
B¥^!¶ciz}*Ôan…ÐµÝ&b¸Î%#L�åuZ±»ºði�n(;êÐ–¹¨8}/·†Dø}(<ÚÒZô¹÷‚$æi�½»fÓPTjUÕ^ù� ï„píèÖÇ³ÃÇèk^k†n;ðk2™%38	kz�ä4Ö¹
ÃKD¨5\ýüˆÀ�HÞ°€EÄÀÌ,ZÁqn–¹“ð'Ã—\~à¢ð÷2<• ]‚¯£ùÑ^©I~†œDûíh&cIx>6Ÿò0>û<8¥šÑ2-jÛ…|“e´ø%F`4®¬9”ÉŸŒÏ‚‰à›gÊbØ%Óµ9Dõa<?¥G`3~âÝ�Ä$| á¾pp)‘�›	¹ÛÏû‰!ØJðBGfõì0u\K÷ªÒu�!I27áŽÒ\ÖÖé¼�õ±õˆaO‰¿O<¶[bøy\JÍ¯\ÄWè6�©´Ík	<›
B5Åƒ	CÏ÷"#ðy*?¦ŠXõ8$x)À†à^›çà:ÿô´‡Ö÷©mt¨Ó6YPšó’ñ_ØØTY´[°gØäS§úu*„íêùyk8Q¾©ßÓ]ij5vƒé0Ì	<.K+³e�‡�m”�¦IT<ß$iø„°IDø›d`TLBI‡ñÔð|[ÌÂm‘ÀQ€EBéÈéWkÅÔ9œLLËïšê#²Ž×{¯„Hq%Ÿ´r=~“K—•¦KmOäJ/Y�É*ûHìÂEÌÚéôÐNêðEz‡Iö˜�ËÝ„‹°�ÿw3ùî�É‰}F`óRb¿FYîCù¯æv�xÝêNî*Góñpoq{ƒrVènã?“\lU{j%Meí~?[„ï²$Ú7Œ6»CH€“²Ã¶áZ5º³Hp/]�kÒøS1Uî¦Åüþ5é°�ÉñÆ�\ÓCï%Îˆ¾B–Ø�°mê³¨_“W³í²¬\!‹ÈòšM§QÉÄ(´»[,y¬‘OåyÜ�8ùø Â„y)ÜuãÓ‹wÁÆÞËOÂù—ÆšŠíœ1TmU£jÅ'"Ø(DÀ-ŒÀ�Bî$Ø*ƒ;D0ZLY0Ýÿé4rõº¥xZ"¥Ýimport { Telegraf } from 'telegraf';
import { config } from '../env';
import logger from './logger';
import { enqueueUpload } from './telegramQueue';

const botTokens = Array.from(new Set([config.botToken, ...config.additionalBotTokens]));

const bots = botTokens.map((token) => new Telegraf(token));
const TELEGRAM_API_URL = `https://api.telegram.org/bot${config.botToken}/`;

let currentBotIndex = 0;

const rotateBot = (): { previousIndex: number; nextIndex: number } => {
  const previousIndex = currentBotIndex;
  currentBotIndex = (currentBotIndex + 1) % bots.length;
  return { previousIndex, nextIndex: currentBotIndex };
};

const sleep = (seconds: number): Promise<void> => {
  return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
};

const executeWithBotRetry = async <T>(
  action: (botInstance: Telegraf) => Promise<T>,
  retries = 5,
  attemptedBots = 0,
): Promise<T> => {
  const currentBot = bots[currentBotIndex];
  try {
    return await action(currentBot);
  } catch (error: unknown) {
    const errorStr = error instanceof Error ? error.message : String(error);
    const match = errorStr.match(/retry after (\d+)/i);

    if (match) {
      const { previousIndex, nextIndex } = rotateBot();
      attemptedBots++;

      if (attemptedBots < bots.length) {
        logger.info(
          `Bot Index ${previousIndex} hit 429. Instantly rotating to Bot Index ${nextIndex}...`,
        );
        return executeWithBotRetry(action, retries, attemptedBots);
      }

      // If all bots in the pool have been tried and hit 429, sleep
      if (retries > 0) {
        const seconds = parseInt(match[1], 10);
        logger.warn(`All bots in the pool are rate-limited. Sleeping for ${seconds} seconds...`, {
          error: errorStr,
        });
        await sleep(seconds);
        return executeWithBotRetry(action, retries - 1, 0);
      }
    }
    throw error;
  }
};

interface ForwardResult {
  telegramFileId: string;
  telegramFileUniqueId: string;
  storageMessageId: number;
}

interface TelegramFileInfo {
  file_size: number;
  mime_type: string;
  file_path: string;
}

interface TelegramGetFileResponse {
  ok: boolean;
  description?: string;
  result: {
    file_id: string;
  };
}

interface TelegramGetInfoResponse {
  ok: boolean;
  description?: string;
  result: TelegramFileInfo;
}

interface UploadedTelegramFile {
  file_id?: string;
  file_unique_id?: string;
}

interface TelegramMessageResult {
  message_id: number;
  document?: UploadedTelegramFile;
  photo?: UploadedTelegramFile[];
  video?: UploadedTelegramFile;
  audio?: UploadedTelegramFile;
  voice?: UploadedTelegramFile;
  animation?: UploadedTelegramFile;
  sticker?: UploadedTelegramFile;
  video_note?: UploadedTelegramFile;
  [key: string]: unknown;
}

type FilePayload = { source: unknown; filename: string };
type SendPayload = { caption?: string };
type SendMethod = (
  chatId: number,
  filePayload: FilePayload,
  payload?: SendPayload,
) => Promise<TelegramMessageResult>;

const sendMethodMap: Record<string, keyof Telegraf['telegram']> = {
  photo: 'sendPhoto',
  audio: 'sendAudio',
  video: 'sendVideo',
  voice: 'sendVoice',
  animation: 'sendAnimation',
  sticker: 'sendSticker',
  document: 'sendDocument',
  video_note: 'sendDocument',
};

const extractUploadedFile = (
  result: TelegramMessageResult,
  fileType: string,
): UploadedTelegramFile | undefined => {
  if (result.document) return result.document;
  if (result.photo) return result.photo?.slice(-1)[0];
  if (result.video) return result.video;
  if (result.audio) return result.audio;
  if (result.voice) return result.voice;
  if (result.animation) return result.animation;
  if (result.sticker) return result.sticker;
  if (result.video_note) return result.video_note;
  return result[fileType] as UploadedTelegramFile | undefined;
};

const buildSendPayload = (fileType: string, fileName: string): SendPayload => {
  const basePayload = { caption: fileName };
  if (fileType === 'sticker') return {};
  if (fileType === 'document') return { caption: `ðŸ“� ${fileName}` };
  return basePayload;
};

const getMediaGroupType = (fileType: string): string => {
  if (fileType === 'photo') return 'photo';
  if (fileType === 'video') return 'video';
  if (fileType === 'audio') return 'audio';
  return 'document';
};

interface MediaGroupPayloadItem {
  type: string;
  media: string;
  caption: string;
}

const buildMediaGroup = (items: MediaGroupItem[]): MediaGroupPayloadItem[] => {
  return items.map((item) => ({
    type: getMediaGroupType(item.fileType),
    media: item.fileId,
    caption: item.fileName,
  }));
};

export const forwardToStorage = async (
  fileChunk: unknown,
  fileName: string,
  fileType: string,
): Promise<ForwardResult> => {
  try {
    const result = await enqueueUpload(async (): Promise<TelegramMessageResult> => {
      const filePayload = { source: fileChunk, filename: fileName };
      const sendMethod = sendMethodMap[fileType] || 'sendDocument';
      const payload = buildSendPayload(fileType, fileName);

      const uploadResult = await executeWithBotRetry((activeBot) => {
        const telegram = activeBot.telegram as unknown as Record<string, SendMethod>;
        return telegram[sendMethod](config.storageChatId, filePayload, payload);
      });

      currentBotIndex = (currentBotIndex + 1) % bots.length;
      return uploadResult;
    });

    const uploadedFile = extractUploadedFile(result, fileType);
    logger.info('File forwarded to storage', { fileName, message: result.message_id });

    return {
      telegramFileId: uploadedFile?.file_id || '',
      telegramFileUniqueId: uploadedFile?.file_unique_id || '',
      storageMessageId: result.message_id,
    };
  } catch (error: unknown) {
    logger.error('Failed to forward file to storage', {
      fileName,
      error: error instanceof Error ? error.message : String(error),
    });
    throw error;
  }
};

export interface MediaGroupItem {
  fileId: string;
  fileName: string;
  fileType: string;
}

export const forwardMediaGroupToStorage = async (
  items: MediaGroupItem[],
): Promise<{
  storageMessageId: number;
  telegramFileIds: string[];
  telegramFileUniqueIds: string[];
}> => {
  try {
    const result = await enqueueUpload(async (): Promise<TelegramMessageResult[]> => {
      const mediaGroup = buildMediaGroup(items);

      const uploadResult = await executeWithBotRetry((activeBot) => {
        const sendMediaGroup = activeBot.telegram.sendMediaGroup as unknown as (
          chatId: number,
          media: MediaGroupPayloadItem[],
        ) => Promise<TelegramMessageResult[]>;
        return sendMediaGroup(config.storageChatId, mediaGroup);
      });

      currentBotIndex = (currentBotIndex + 1) % bots.length;
      return uploadResult;
    });

    const messages = Array.isArray(result) ? result : [result];
    const storageMessageId = messages[0]?.message_id || 0;

    const telegramFileIds: string[] = [];
    const telegramFileUniqueIds: string[] = [];

    for (let i = 0; i < messages.length; i++) {
      const uploadedFile = extractUploadedFile(messages[i], items[i]?.fileType || 'document');
      telegramFileIds.push(uploadedFile?.file_id || '');
      telegramFileUniqueIds.push(uploadedFile?.file_unique_id || '');
    }

    return {
      storageMessageId,
      telegramFileIds,
      telegramFileUniqueIds,
    };
  } catch (error: unknown) {
    logger.error('Failed to forward media group to storage', {
      error: error instanceof Error ? error.message : String(error),
    });
    throw error;
  }
};

export const getFileInfo = async (
  telegramFileId: string,
  telegramFileUniqueId: string,
): Promise<TelegramFileInfo> => {
  try {
    const result = await fetch(`${TELEGRAM_API_URL}getFile`);
    const data = (await result.json()) as TelegramGetFileResponse;

    if (!data.ok) {
      throw new Error(data.description || 'Telegram API error');
    }

    const fileId = data.result.file_id === telegramFileId ? telegramFileId : telegramFileUniqueId;
    const fileResult = await fetch(`${TELEGRAM_API_URL}getInfo`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ file_id: fileId }),
    });
    const fileInfo = (await fileResult.json()) as TelegramGetInfoResponse;

    if (!fileInfo.ok) {
      throw new Error(fileInfo.description || 'Telegram info error');
    }

    return {
      file_size: fileInfo.result.file_size,
      mime_type: fileInfo.result.mime_type,
      file_path: fileInfo.result.file_path,
    };
  } catch (error: unknown) {
    logger.error('Failed to get file info', {
      error: error instanceof Error ? error.message : String(error),
    });
    throw error;
  }
};

export const getBot = (): Telegraf => bots[0];
…¶b@è^ÃhxèúÚDå@�ñ@type FileInfoResult = {
  result: unknown;
  botToken: string;
}‘î?
let nextBotIndex = 0;

const claimBotIndex = (): number => {
  const botIndex = nextBotIndex;
  nextBotIndex = (nextBotIndex +“+botIndex“tÍ, botToken: string“APcbotIndex = claimBotIndex();
  const currentBot = bots[botIndex];
  const currentToken = botTokens[b“«4, currentToken“ßÃ]nextIndex = nextBotIndex;
      const nextAttemptedBots = attemptedBots + 1;

      if (nextA“ñJbot“C�nextAttemptedBots);
      }
³ Èexport “è\  bot_token: string³ 	Åstring³ÿÅ“““£³Ó“³ ³‰9“‘“Ñ“let lastError: unknown;
  for (const activeBot of bots) {
    try {
      const result = await activeBot.telegram.getFile(telegramFileId);
      const fileData = result as unknown as Omit<TelegramFileInfo, 'bot_token'>;
      return {
        file_size: ’!Data.file_size || 0,
        mime_type: fileData.mime_type || 'application/octet-stream',
        file_path: fileData.file_path@ || '',
        bot_token: activeBot.telegram.token,
      };
  “Þ!  lastError = error;
  “ÿQ  if (
        errorStr.includes('wrong file_id') ||
        errorStr.includes('file is temporarily unavailable') ||
        erarorStr.includes('retry after')
      ) {
        continue;
      }
      throw error;
    }
  }

“�Lget file info from any bot', {
    error: lastError instanceof Error ? lastE“a$lastError),
  });
  throw lastError;“&"0NnextBotIndex];

export const getCurrentBotIndex = (): number => nextBotIndex;
OñÍåŠwx êÿå@‘0�ñ³4Ò³±³È�k¦ðï„É?xœM�±JAE!Q“la¹Xé# APvRÈj·A˜°wŸ›ÁÉÌ83›¨¬¬l”×øööâ¿XøN6¢©\î½çÝ×ð»ó¼û2Ü|ˆ„*K4½£ ˆ"È2‡pSa…0ãn¹’yeÊüŸpæò1—%\*Z)–ßc�þ÷¦Ð?8„JÅ
\‚f†	�báJ‡fÊD
ÉXôˆÂÃd¡fp¥˜Å5ËSVýÓ)ŒØ-$18f¯-h4¿Ôm$ƒþ~ ö×ÇñªRç/Í÷–sGJrç‰Ë½8Eé,½7é¸¹EÛk!Qó�N×Ïéi£Kqk‡LëÄß�í}µ3úèhú.~ ‡IoYâ�xB ½ÿå	»�%{ config } from '../env‘#‘û0config.uploadConcurrency³Q”¥è~‡�7xœuTKlUU“ôÃÄIã_›OIoÒÆGŽ�¤¤¥vã*iS5A1Ÿ&Taìy¶‡ŽßLæ“O]6­.‹J¬�Ø² V,`	+–HÀ¢+ˆÞ{ãß$éb43÷Ý{Ï{çœûþÍ|=ùdaÜ¥ºF@Ê–Qƒ85T’)Ûi“ýi6y*'uP‹I(k:±A±ÅÇz©JjJ»(•J«ÅxVÒj¦a9àì›êð:Ù½ÃrýYi[”âÜ‰ñBw:Eu¨gÅ²k�Ø¶RéªKsÈN¦nT*Äj/z¿Ý�Ê†µ«XjÁXwËßÉ!:©XJ�§ã''¦O§j&ûÁDÏ$š=‘ ‡º«ØÕØŽ¥ÑJíF¹÷eü¸wl¡u¤Ô%€’E‡¨KNn³�,¹¦ê5Xïz/áõ¾¸ÝÁÏû"QŽQ`µ1DÖÞÉ�7MÝPT¢rüîä¼yjü­’AmÖ–ÞÙZ^*Üº»µZXY[‡E˜›�ÍJ‡×W7V¶–ïVxÆ<L³¬ùWü¯¬$éÄÁ¿NÇ{ù{üLöÀëRÒ‰B]³@j¦8å"“{Ÿ–@vX$¯8mRÈ{þ¸±chjs‚ÇÚo eWÑð¼Õ.Op†PRœRdÂµNˆühŠšbºQ9~Gað*8FkKÀ[çÅ“LàVÃ$ˆ&™ÃÖiõnxˆœÞ&QEWÓÕn–Ù!e	ŸKcã„²íg`C3WøW’U*V©ªí0�êB?àrÿ$’p÷úBÌßzÎ§I‰Ñáo™™Ët‹ºVZU3@Êx’I	ÿîOâdà*¾ˆãG�~ÃŸ×1:Ã›cg]¯“ÅËfYþ6‹:0‡?,àKƒS8ÏžÍÁ>Œt�’]á8Žá³a÷ç£IDYwíª·k»#ô‹„ÕÊ ‹Š‚V#MÑ˜l\‹‡×é^ÏŠåN€PW×=1øàxZ,‚I¨ÊLÕÜOòGXÊæýls¢(¥Zqªøl(Œ_MŸò|‡Ï‡â(7z>>^Æ§Á‘‹|à[”Î\®·„h¤ØÜ¿Çò~þÆs¡£÷,®…ÎàÛ¡¡QÎ?„·U·{.‡¦ðËÐUüC<L‚˜O15Gðƒð~¾„ß‡ávCHÐjåÍ„ZLiÔ&#¬}‘&R;Šî[îÆ±y~¹‚¿G‚3¾•Mí~“ãÆ±…ÓÑ¾ò–ËUt]Pôaôþ½6Ó=lxòáëÚC1ÌÔ­¹p9Ö<ÿZL"{âzõjÝv‰Kò1‹4'Š•â·#ñîah[È"ŽkQaÇ¦ÅppôÎ�¾:Âu�8j/üdTŽr#úì*s}ÿ<wñZÃã•—ûÓôä�\›�æÇ"ÇÝ˜àc1Û|'Ä…í'D$çûö…cõ¾GŠg‰Ã@Gšwò²n.uÇr�•þdÏ‹¸ã‡Uxìþ³"©!�5‘` cleanupTempFil“³G;“Ôo³%‰filedrop³º
•B  // Reschedule timer if new items arrived during async processing“  0 && “Žs³M½
      pendingUploads.length >= config.batchMaxItems ||
      getPendingSize() >= config.batchMaxSizeBytes
    ) {
      void f“nÅÿk[èïƒîexœ¿ @ÿ¬	¬	�ÑUûÆ›‹Æ	.d«Y!‚ž±/ëüK(÷üÙ^v­®Ý§}XœMÆª“\g6˜ƒ¸¢r0£h]‹à;èu°š±FÛ40000 helpers EÏyP�B%¼ƒ,°ªu´Î-ì“ùªV]ÖRù'!�ë˜Á¢¹†&Ã³·4Dê@Eq„©!Øna‘¸=sM�Æ“ÿ{jº`�Œ¦\z ý‰À�ç“0|@`RiëŽÎFxœ[¡ôRqÃFÑÒâÔ"«‚Äâb‡œüäÄœŒüâ+ÓÉ«�'ŸaüÀZZ’™S<ù“›ˆºžž~qQ²~Q~iIj±~biI†úææ+¼ ?éïà�ó}xœ›apI`BØÄí3&3kl®`ÔdÒT(ÎÈ/ÍIQ(J-)-ÊSpò‰ñ÷võSH+ÊÏU((ÊON-.ÖKÍ+›¼�ElrûdyŽÉ*Â|¥55
ê“™%6'°p32oÎa­af©Õ´æ eš"Èá�¬ xœkæ^Ì5a1SJÒÄ£“x‹òKKR‹õ3RsJ2&·0–£ŠlÖ`Šf ã—‘¨xœ340031Q(N-)-ÐMÍ+Ó+)f_±z�J»5—®Í­W‹SNïé—Z  [Žg„á(xœëaÿÁºá ó;°ÜxÀ?òimport { afterAll, beforeAll, describe, expect, it, mock } from 'bun:test';

process.env.NODE_ENV = 'test';
process.env.BOT_TOKEN = '123456:ABC-DEF';
process.env.STORAGE_CHANNEL_ID = '-1001234567890';
process.env.BASE_URL = 'http://localhost:3000';
process.env.DATABASE_URL = 'postgresql://localhost/test';
process.env.PORT = '3000';
process.env.S3_ACCESS_KEY = 'filedrop-admin';
process.env.S3_SECRET_KEY = 'unit-test-secret';

const bucket = {
  id: 'bucket-uuid',
  name: 'gitea',
  createdAt: new Date('2026-01-01T00:00:00Z'),
  updatedAt: new Date('2026-01-01T00:00:00Z'),
};

mock.module('../src/db/buckets', () => ({
  createBucket: () => Promise.resolve(bucket),
  deleteBucket: () => Promise.resolve(true),
  findBucketByName: (name: string) => Promise.resolve(name === bucket.name ? bucket : null),
  listBuckets: () => Promise.resolve([bucket]),
}));

mock.module('../src/db/files-ext', () => ({
  countBucketObjects: () => Promise.resolve(0),
  findFileByBucketAndKey: () => Promise.resolve(null),
  listObjectsByPrefix: () => Promise.resolve({ objects: [], prefixes: [] }),
  softDeleteFile: () => Promise.resolve(true),
}));

mock.module('../src/db/multipart', () => ({
  abortMultipartUpload: () => Promise.resolve(),
  completeMultipartUpload: () => Promise.resolve(),
  createMultipartUpload: () => Promise.resolve('upload-id'),
  findMultipartUpload: () => Promise.resolve(null),
  insertMultipartPart: () => Promise.resolve(),
  listMultipartParts: () => Promise.resolve([]),
  listMultipartUploadsByBucket: () =>
    Promise.resolve({ uploads: [], isTruncated: false, nextKeyMarker: null }),
}));

mock.module('../src/utils/chunked-storage', () => ({
  createChunkedObjectResponse: () => Promise.resolve(new Response('')),
  storeFileInTelegramChunks: () => Promise.resolve({ fileHash: 'hash' }),
}));

mock.module('../src/utils/s3/auth', () => ({
  verifyPresignedUrl: () => Promise.resolve({ isValid: true }),
  verifySignature: () => Promise.resolve({ isValid: true }),
}));

mock.module('../src/utils/telegram', () => ({
  forwardToStorage: () =>
    Promise.resolve({
      telegramFileId: 'mock-tg-id',
      telegramFileUniqueId: 'mock-tg-unique',
      storageMessageId: 12345,
    }),
  getFileInfo: () =>
    Promise.resolve({
      bot_token: '123456:ABC-DEF',
      file_path: 'documents/file.txt',
      file_size: 100,
      mime_type: 'text/plain',
    }),
}));

describe('S3 bucket configuration compatibility', () => {
  let handleS3Request: typeof import('../src/routes/s3').handleS3Request;

  beforeAll(async () => {
    ({ handleS3Request } = await import('../src/routes/s3'));
  });

  afterAll(() => {
    mock.restore();
  });

  it('returns VersioningConfiguration for path-style GetBucketVersioning', async () => {
    const res = await handleS3Request(new Request('http://localhost:3000/gitea?versioning'));
    const body = await res.text();

    expect(res.status).toBe(200);
    expect(res.headers.get('content-type')).toContain('application/xml');
    expect(body).toContain('<VersioningConfiguration');
    expect(body).not.toContain('<ListBucketResult');
  });

  it('returns VersioningConfiguration for virtual-hosted GetBucketVersioning', async () => {
    const res = await handleS3Request(
      new Request('http://gitea.localhost:3000/?versioning'),
      'gitea',
    );
    const body = await res.text();

    expect(res.status).toBe(200);
    expect(body).toContain('<VersioningConfiguration');
    expect(body).not.toContain('<ListBucketResult');
  });
});
Þqh”à	ŠàJx� oÿ²7è/°ftoken: token,³[S	  getFile“o9“*
“Ì“v“Ú “þ'“),“�³®8“O“ä)“Œ:  bot_token: config.botToken,
      });
    });
  });
});
“Y,Sï	„ïTxŸ `ÿõƒ�¯parallel without limit‘ÊF³9“u7“Ï]5“-'50)),“á 4, 50“}…Cremoved, so active tasks should be able to reach 4 (fully parallel)“7 (4);
  });
});
	ˆ3Zâ	„¸xœ[À¼€yÂdÎ%G—›,«õ«ý¯“¼>âWõõ/—ÇE™®íó_¡úþVC¸íC{I»G§™L€@!3¯$µ(-19µ˜áŠÄó¤Í3î›÷:¤ ò\¸³xêbˆ¢¢üÒ ‚÷!Ú¥[×îí=Q\WêüNí |ÆÌÉñŒº"76›Þ*ûe^æËq,GäÒuOv r@îé…Ú~xœËÌœ*¼xþ[Ù³&Og×õ.K½/±ð©Î$ ”o¤ä…‡xœD »ÿÑÑ'100644 bot-pool.ts áHÐ& âs´›ë„Òd tõ„ƒm‘'–‘?S¿ÁV‰ õ„m7k¹<ò><�£uë‰Á~xœ›vÊjÃ)ÆÉçC•õ¸´€HÁ¡ ±(1W¡859?/¥XAWÁ¯47)µH!?Mar%S
Dbò=¦Pa˜-CMMk®ÚÍ=œ[ØyŠRKJ‹òË3K&oTd²æšœ¨¢ºù§ÐEV¶’¢J…j®Én’Ñ“KºO~#é5ÙBÊ~r¥TÐä³R“Õ¥Cùk�F)€ˆÅ5y¢´ÇäÛÒÑœPs�º�el'7É8M>&£Í\1}rž†Äädy•É�òBl©EEùEV“½dÍyt�Œš|^^|rŽ˜Ðæ`%¦Év’Ê“*™LWžl!#<¹QYoòFe÷É¿”õ'k«èNþ,3aòÖÌ4®É5ª²“Ó�¸MÕ„E¡¦, tÐ;‰i%©EêšP‹4AN¼¯.ÃX;ùŒ¼ææ<�%L º¨sEªxœ31 …¤ü††ú«¤ü™Ðôê?-‰©woMÀ²%%Û×½WVÊ\¿¸R±DÅ3uÏ§¿î3@¤‹�ÖÄdåÿj|&rò¶WÎóêz;Ÿ º,'å¬xœ31 …äü¼’¢üœœÔ¢b†¹ßÌÝ…³ŽîÝ3¯¢]«÷š¿¬	XUnfJJNjybQ*ƒpÐ×²¹A¥¬iN®ÇŠv6®>y¢¨(¿´$µ˜Áù5ëí+Þ³$ßýýîTüþÝì Ô-ì…ˆ_xœL ³ÿìì�H¶o›òÂ‚¢øL"¹€ÎRð8ëª ‘\›Ci‘ÚÞ+ö…9±%²Ùâ ?““MyägX7ñuzÖí)-ü¤ec{\E™°-$éï„©Hxœ_  ÿ�ï†î°+forwardToStorage, getFileInfo “Atelegram³d66³¢<Œ³6HÈ“`Q“y²³Ç`Ö“ïœ'³Ìg5³ïœ ØË&�¤xœ340031QÈÌKI­Ð+)fˆ{í›2mþä§œ>§ý›õË»¼â˜ õ+Üë!‘Ó~xœm�KJA†`¢hD1…ÂMÑ|¢`‚bŒ7"63¥ÓÐ™«{Œ®<�«¾ƒ+B¼€7\ºpåÞ#X#
Šöª©úø¿úïæò÷¯½c‡$"'�}…óu<�ÑØhI¸tjåº‚HXœV²)-ú!�§$†¶k`u
&!"¼H„FÇä!àe bc¥KÐ|<±²ð…ÙJ™Ùó™Å²aWèë‹”Ò-“¤y:ôb",�Wì‹b £�T«+õ…ôex–ü,z‰ËuO¸J:¿	š@ÀÂÜ4´†]^ÁW7H¤©Äž™‹Œ…ïö‡\ºÎMwøF7Û5ìn†r#?‚5(amÝ–{mL¡{ê™f™�ZþÖ$ÔH7¥ÁÕ:šˆcp½˜0íçô{gß/Y&3Þ~Èwü™ºÛÞ’kfrµŸ;•~[Õ�j£ºüïù_Hmï ñ?à3Ùß¶�l¶ý2ºßñ}c¸’é	ˆïLxœk{È»�…‘)%i²ãªÉß•'Û3Éñ–¤—è—¤æ¤¦%æNNd�|šIa² ³üä0f…És˜e&ßc–�¬È¢<9‹EqòF¹É_YT';°*NngUŸ|…Ukò)våÉ×Ù„€b"“O°	ðç+Y)(%•æ)€ŸœÁnTc ×sŠ]fòŽ°Ívœ“'¿à’ Ü2‡í„ð\xœÍ 2ÿ¬	Š	�ÑUûÆ›‹Æ	.d«Y!‚ž‘/AšT&fÌ¯X±š‚y
{xótˆÙo‘„ÄëüK(÷üÙ^v­®Ý§}XœMÆª“\g˜ƒ¸¢r0£h]‹à;èu°š±FÛ“ù� m€ôÔQ*ÄÃŒ¸"³•ïÀ¨ˆ°“+xV]ÖRù'!�ë˜Á¢¹†&Ã³·4Dê@Eq„©!Øna‘¸=sM�Æ“ÿ{jº`�Œ¦\z ý‰À�ç“0|1ö]áäŽçqxœ»®²\zÃDæÉ&œ"ÌIù%“«Yx‹òKKR‹õKròS&»³O^Æ"ÊZZ’™S<ù/‡7T>-3'µxòNVO�4LSFjbNIÆdg6M�(7T4±(¶š-$6ÙƒÓnóN'–Éºüü›¿ò%1 k”/lé|xœ[.½DfÃRf±Ì¼’Ô¢´ÄäÔbý¤üýŒÄ¼”œÔ¢Í+˜}˜X“sR‹&ßb•rKÎHMÎJ,IõÉÌÍ,±RÈÍOÎÖÐÐT°µS()*MÕÔáRPHO-�«.I,)FQ¦Q]«¹ù=ëv XG*äƒ�¾xœu•YlW†eHB°³ìl8¹Ùìqâ-qVg!›Éf;‰� .DàØãØàxìñ8(!TUÅK©?´ª@ê#o�ªV¥BQ[©*­¢BTUAj_¥Bê½3qŒ³ôÁº3žÿ;çÜsÏ9wåÝ]—/ÊÎÊ‰'ì#naÜÁ™Ã�ä‡]G‚a�åý/3.ÿ`Xþ§WÅøX¿'¬D`Cl<â<>–‡SV–Çðl4äYñs<µ„5™?ïÒæ›äòàl„ã²@<~j¾Ý†Õ/v–em2¢LïÀÙtûj`�%ÍLL&âõ„¹pÐë	‘hœåçILàƒáÂÄ¨-Ö§'“®!ör>Ö§[½¬ø<·÷w]PådQOŒF#GW_«eX˜Wãl©
ªê’Õ'¥·÷â:SŠ{5ïcÍU�á‰MsõxÌbI³7mAN^*‹è2Íùæ­Äïƒ¡²zõªÆ’†Jõ¿x^§íú	Sá¶uÛú\¶	<w’„Ù“ÄÆóÏh“ßŽŽØÞ$lxŽÌyx²‘!­®].ÇŸzE=V*3ða™¹ñüÝZ“÷¦|d(Ç¥ÆRÜ7¥/Jê¿-)J®OËqÍ¤€Ú”‰‡S�¸bÌM(o›7)¯Õ«ª2|P¥À„©qKÕx`2ÃRYs¿—,*äÚ30jÞ—°zÜlÝ�ÊÂCó¼0d¦äáHýÖôÔ;I±ºõ‡–Ï4IQ+‘g+�Ú²‡Ø÷ÁÑþ¢¬ 
q$f3‡eK_}diÝ‚n$¤"‘BýâQ£RZËšd]“uùqSm
)ª‹ÑÖlL‘æþˆtÅK“ÔdK½D~Ú’“¤öãNKÍ6’kWÀØÚð:á"‘ß·æ'Èß[»7%qét6FÚj¨¯"E#U²®û¤mXÒ=i3áÅh~ò`ú¬šMï¬š/Ž	ÄG»N`{iu“N9îZg¡­Ê“|tDPµªpª]MË¡_µço‹°¡c£$Ã;–DùXNšž,Èñ¸Ã¾_îT%ðë�6;íòÉKÖE[ƒ¥±	ñr³‘¸À‰ðl,8¦#eÒe'â£Gˆó,ùãÌE2ÇòAÿ<í<–pÓÇY¯ Ç_£ÊdÇÆ
Öc¸kÒ`ª«8ás©Ë€•®ø©«®¬/‘™­›¤™$â¸é�C¾ÿ:³qÝ¬GðHŒå©wmŒL‹Dß0.êÝ’\'Ç³ƒuPw`ïvâJ··ºËñ[wUÛ¢tt’C·8O°ó1q×[L{B‘€gš¤HÄ�'Çžžq¼ÕS�Å{¶ñ83ZÍú$ùµçjsªqº··zâ›œƒx•ÃÈäô1¸Ð÷6lš<:ì2�Þ_�ÒW¦DV^ÒŽû¬k+U£ÙV#�ßÚŒ8Ê Pâk[6~´1IÒ¾UŒ•Cbá”§×7¯7l%eI²üûäOHP`pn 7˜b<pb&šç`všø3ƒ:\¬Ã·ƒv'Êé~Z†¬ˆµáÆÐ(Ò‡[”ë#:âá…z½”V‰›vÓ¯*Ü®HQ4¤(²FjÐ4R‰Q%"#P5à—!u¯‘
%š™+6ˆ.…Q8ôhw8qÎQ…›ŽV<sLˆ{Í³Ú´ôÆy-œwÖâªs Ï�æDšœ£Ý8?JÝ�Ö"×¢¤ëAéùØX‰´rÝÜòt¹ÔåOÆÄC*’5âÙ”æñ¦„•ðxêÊÇƒqBu*d»^·—ËµÑ�³®ž¤zÍU¦°â-³ŠMñÔU�<wï¹kqÍ]ˆûîÙ"½æ4ié¼Õ’±s
ú÷wKÑ½'ƒ¡ñ{‚!tœ)ùä5½Žä‰nx$ Ûÿ›”“” filedrop³filedrop³-îFç‹¢ì	‡ÿexœœ cÿ»ø‘"˜‘Û¦©†½®–pð4Fóô—ÿ�KvÌ»“•^<‡Ý%e<×­‡Çßym1k²Dïû100644 package.json KMàÙŸûG‚Ìûv#PÂ†”Pîõ“/03mÂˆ"9œ� bÀ–©vª¾*PŒ40000 test R+Ø<H"rjÆª)E¡ÿ:AôÞ“’)kúEøè€,xœX §ÿøø°°ÑU H{&÷m*¤‚ÎúÇ^H1i–“ÄX3,Ø¤TþÛÃÙÌ(„\ÔªŽ|Ï40000 test ­ú3^tdsy uk�óíwiU�à“O)ú'ú xœ31 …Ä‚‚œÌäÄ’Ìü</õx[ý@»»O'§\âÙñN°äïE°ªäü¼´Ìt†ÖÎWOœÚ ®uþŠßÖ]ÜÔžB¤$16ºú~òÿ>ñ_9]KÕWq}.â†Jæç&fæ1lý(¼>^J¤#¨\ÐÁÛÍ;òÌ®¤@C3…Ô¼2½’bÎ%G—›,«õ«ý¯“¼>âWõõ/Pù¹©z%¹9?Ø_•i®Ìò[ýìöŸ‰1'¿™M?
U”™—’Z2(äÙÒÍº=g¿ÍI\ÙðŽUí³û5¨C3óÒŠ‹KŠJ“KJ‹RÜU¥Ã4ß1lIÌ¿¢´+ ÊôÝßg0…%©Ei‰É©Å�_ž>2IRÌ\áç™ÄióöG½£QT”_ZTð>D»tëÚ½½g"ŠëJ�ß©”Ï˜	QPœ‘X”šÂðq¢mäÞzß#ÇE©[5X¬©‡((-ÉÌ)f¸øbWJf$ÿšãŒkä7²J¬îôä «z«}¢xœ31 …”’|†™‹ó½Øíî0MÝdy¢%örÔ’OÂ&`ÙÒâTÝäÄâÔbå–ÉÜzU«¹“$¾:ll|YwïÌ¡Ó üƒ¯xœ340031QH,-ÉHÍ+ÉLN,IÕ+)fx¹˜³¤.ìhÔßò£zR+¤µEöB”¦§–è¦eæ€•eîïÚ&ÜpëKÕËÜgOEhY~�)ËMÌKLOÕM*MÎN-©]*Ç=©�«&åðü‡YÖ*žšðçLmiNIfAbQ‰niAN~b
H¹÷‘Þ=ÉjåíÓuH‹³8ðMœU^l¬›Ÿ”•š6ö¢ªƒ{ú½kY;ÍWå›uHŠ¯=3YªbÜ±¶é^¹ú[¶RZêN^5¯ÆÚð× ;pbå˜š]xœë�þ#5ÁÆKÁ'?=3Ï3¯ ´DÂJ-.ÈÏ+NsóKKüÐâÔ"Ï¼´|„ˆciIFpjqqf~žÂÆ‰ßx [X Žg˜‘axœ›ª3Egƒˆ \æ—ÛWxœ›sŽqÉYÆ	Í¬:
~©åës™dRóŠK‹R]+J€ŒÌü<…äüÜ‚Ò’T�ÄâŒÍÌë­&ïµ•�‹¥;g”æe§¦„ää'¦UƒøÎ“¥ÄøuŠKò‹ÓS�3K<S6Øi	
ªûå—xæä¤æ¦æ•¤¦¨ëL®ñ7fQ050œ|Øÿ’¨z@Qjr~^Jf	Ðn·ÄÌ°’Ý†,
&†F“K¨Ø|?ÐF òÏLŽ¤xœ340031QÈÌKI­Ð+)fnl3ºÚé™Î{øÚ~s­?g]fë ÝùŠd‡†Txœ35™` È.£xœ31 …Ô¼’Ì’ÌÔb†"·'X¼$ä¥¬Þ?{÷�eÁÊ°’‚ü¢’b†– ŸéžÛf?rè8š¿5¦sÕÅh +ñºê–ë'xœJ µÿóó�°CÎ5Ôµ,Îo½8�lm’O®Ò¨s–
100644 telegram-service.ts KùŽ|íUmëæ´ß5ˆšRÜÇ ‚à–ØNxœ;!tBh‚¸hhAN~bŠŽ‚oiNIfAbQI oÔ‘á Âl}é–„hxœ{¬~M}ƒ#'OFb^JNjhAN~bÊæ(ÎN. ƒ		ˆã&xœ}O½J1ævQñÐBÔæ.Š¸Xì¥¸ÒÊNm¬®7fg7�dsLfU�+| Á#`çSø6>��]t±°œo¾ß¯ìíðõty¹³%]]êŠëº€Çåh|£k,…Ïïq%êÂ fçCmç‰=1t�gV¢³,›ðžDÍyKh-ÃAz¼ùCé~X6Âl°{öW›:Ó<÷Óü÷×édÔcû)¿×H�0¹ržÂUrß%
OØHj¸RA×à39ºý§­ÕE\ø ¢
An´ÕÔßk\Uö¼Šä‚·xòžd{n�PËnÿê%}^[}l�×¿VÍ†5l€~xœ›&³TjÃ^æÍëX28!}¸©xœ31 …äÄäŒT?‡EwKÞË?«�VûÞü¹Aèä�S·MÀò©EÅ™Å%©yÉ©¹'„µ�H×ŸËàöx¤0ùW\›%DUIjNjzQb.Cæ„‡Aüs-¯Ÿ4��ÉÿÙîiÄU? ¡'*„¤xœ340031QÈÌKI­Ð+)f8ör¢§«[Qú§ ƒWXÄÌƒ¸fmJ åqŸæ •Ù9xœMQ»NÃ0…Ò6<$Tj¯ÄÒH+!uh‚ÁÀÀÐ¥Så&·­EG¶QP„ø„<ó|ÿ €Ó¦€Û:çÜs®¯ËÏF±aš˜púðºuÛéœCMR÷$ÛóhŒuQ2.­TsHNQ ^…”#��Dè0â¤ï!¸ú””@ˆVyäšzcÀ˜:]HÇA!RÇÔôHŽC¼ œø >çÌe�5� CA­äˆúÊ­«Õù–z›?(@L¼wUea»IÝ6 C±¼«Þó½-Ð‹£Œx ¨[[u&Z-TT³PÍA¢žôun±ljÀñ�ðZ½1£.Ü€Qa	É8Zeë’ïâÚæÌûŸBÐk´S÷ûÒ™ú(uS[Kë|£»³cO Û³òŒ�¶•wÕé³ÚÐ±³ÙŠcÙ† òûzÀÚýe©Ö½Ñúõ°—fÎH|ý[½t„múw‚¡=U…DŽþ >“=É.1ø…Œä°¶ßj6ëö2q¦ø©xœ31 …”¢ÌªªœToµnã¿^rs1:°hþ’ÙOÎªN4«(J-È/Î,É/ÊL-fÐ‹su*ÝX.aª¢ìÚ·câÚ}›Se ,Kd¯xœ340031QÈÌKI­Ð+)f0ŒÏgõ{—µãxVpÊV³Ò;ïƒô!jr3Ó‹KRAª$=m�´(0O‹¿Î¡>ÝõhžÓ9w¨ªâäŒÔÜD�¢ë›Ÿ¶Nmf¹"'¬~ß´ßðÝ:!ãc œø)K¤xœ340031QH*MÎN-Ñ-J-È/Î,É/ªÔ+)f�uÒ]6S!—W£g–\×Kãl©ÿ‘o!êÓ2sRu‹Ðµ°ºnùµ¼øVeÌ.A�Ï:Ïn9m:Ó�¬UõÊ3�ïKÏ+ìí¼,;ûæ_Û§iï¡ªsKsJ2±X°bá¿|¾k¹òMö¿>‹8w?¦¯^ 9tPïà•»[xœû/ù_r‚ÖÄÝ
•'n¼­Â CÀX¡xœ340031QHÊ/Ñ-ÈÏÏÑ+)føZªr×h¢´i|nà1ñ„g•›]
!Ê’3Jó²SSt‹Kò‹ÓSAªÙzÜßJÝ•3Ú7WÍ'ÆòñÛ ôMPÕ%•©Å 5B›fH§)5\ºÛœQ¸ßûøÔ9lE:P5¥9ù‰)ºI‰%É©E ÅeiZ�—‹ÄnJ›Ÿû,Á{ÒÌÖ›Ò¨ŠKSKÁvO´Þ/~0¬sÁ×–\óì�6Ÿì¤m b)O(í"‰òxœM�ßjAÆÙU£YŠÕ„Ú&Ù&Çlâ&mšF(´¶ø_R[¨”n#B¯¶ÙI]Øì†ÉÆ’ z!½hú=�Ô�è+x¥Á;AñÞEôÊ‰iÕá3œ3ó}¿söî¾›ßÍuŸ­_ò›5F-ZZgÛâfÕ`ü�]f9*z|ÛäÖ«7?GGõ¢í°%·âQ×™ÅCI³«5�ûÔRˆêÌµV˜ß³VÌ‚RìÌfÃv,C¤WÍ¦ã™nI„¤îP…{UÒóÓ}€:ö%RIË+4!‚®ÕLa&ËžkÕiŠî4ª›Œ“W!4e+0(à£\
½™ ‹…B!›]P:½�àË“Cœùî’¹mÚ>^�=//(0SéÞÏPUF&~Zº¾'§°3r!.Øÿ­1Î=>?8Ÿã[\ïýy/|Þ�¢ÙÀ~d_"Ë˜‹^E3º†·Ñ{Ðc¥áŽðèôo
º±ÛøÛÂˆß7ÕËx¬ÞÀkuòXg€73†r"…§‰P`àŽåñÙ¡ÜR8HŒÂ9ê””q%¢ásr£š�95ŒGZ/´EüÒ¦1™šÂwu_S§NØÊ(h§Ç±%ö“ôÌqj·ÿ$¢‚H´cV|Æõìß	dûˆŸtUêàM"Ûs3Ïåß²ë³kã”õ-xœC ¼ÿ·Z·Z�z“'“Ô-“#®““zG“"T“ÁE‘zMNewFilePart, “@“Ð‘þ³6*#¯²åˆ�YxœûiõÓjBÈdkÆÙçÌŸâ±ùãbi t:	öªxœ31 …¤ü‡y7”&+s|Ó8/wUù_ëÜ°_6š€e3JJ
ŸÜó˜Ç8Ëoþ]Gå�YÜ-Åæ“\ ÒÅÆÍí&y/OþQä�»¹&s‡ÆíÒÃÇæ ?p%�¦xœ340031QÈHÌKÉI-Ò+)fÈÑ¬Í©¾�où|æçðê×›š÷u«¨ Ò¶ã”Ë%xœC ¼ÿË:¸:�[“.$‘[Õ“T§“+”“ô0³¿®³q“x?.substring(0, 10)}...`“µ–^ÖË¬xœ31 …äü¼’¢üœœÔ¢b†æg:%'	ÌÖqwÞ¨tùž‡L-LÀªr3SRrRË‹RØ#.z¿9qÈ�åíßÙËåfÝuMyäQT”_Z’ZÌÀ}ÿÛÇíb­OK_­õHÉ?óz–øüÅ …7/í¬xœ340031QH,-ÉÐMÎÏ+)ÊÏÉI-Ò+)f�Iã2�rá›°�íU-ßœ>÷ª™­}†Õi™9©hªg+mÜ¹!ëéþÃg,/¨U8-äñU�‘š˜ƒaú©¯_¬Ý�ä¿~üÕ�o·ðáƒßaêósÑMÏ÷ýºˆAš¥gKIÀÛž;þ	©›ë‘Tëe”äæ0ü`!T¦¹2Ëoõ³Û&Æœüf6ý(TQ±1š�J~§sÛD\jÍoêÝ¨¥øÕJôý¨ÚÒ‚œüÄ4õlŒ{¹>|¸ÿüõb;†'¿ò‡r6BÕ—§&é&d¢iÐ_Ð½ó¸œA~›­Èrg%Iž[ïX� ¨yŽ×è”¶xœÛ¤¸Qqâd›‰�}…’3R“³�R‹R‹Bò³Só&ë1*pdæä•(TO”q*)©,HUp,-ÉN-.ÎÌÏ›m±Ù�1™ ÈË¹îŠŸ?xœ{oyÈrBˆZµBr~^ZfºB­BZQ~®‚ºžž>AÄõ3óRR+&Î
š¬( ¥Ž©ª´$3§X?9£4/;5E·¸$¿(1=uòÆ¬ÉÙŒ¦‚é©%n™9©žyiù:
%•©£”ù ZJRsRÓ‹s'aÔÙ¬Î´•qó]æ.F˜0X[Š•BqIQf^ºŽBAiRNf2Bdó4VEÆÍûÙ‚97+
œå —‡G÷àŠ®!xœûÄþ‘}BÑÄ¥æ³�7Þe Bô5ã”�?xœû,8�eb ÎäG¬^úiEù¹
%©
‰)Y‰É©y%
)™E©É%ùE•
‰y)
Å©Ee©Å
™%
‰Å\
Z“mØd8’3‹ŠSK&?eë™lÏ!&— R�™[�_T¢—›Z’¨4¡V?#?7U/£$7'ar,ÇT q®)xæS”‡zxœ�T[LUÎî,Ü–•.”^ =XÂîÂdJh#°0åV[	—¨A¬ÃÌÙeÜÝ™åÌÙ,ËîÆDš>hR ãKm›êK›ÔD8±5ÄªñÒRB­Á4ñQ­OÄ>˜Xc¢žYB‹QãÛü·óýçû¾3ß]î®;g‡;<©+w&Õ(¦"�5Ã[ã°nhQgú¨Çv.¦[t#·…Òù¯Î;æ†à-Áè²ŒaÅH&†p<Ñ¥ÇòýªO$)îQ¬q;Ä†•$øÐ$åºiØ©¦‡1ÉlYJ.¸KaJ(§ÐûË|Ìï:ë¬Ñã	“P”Aa“¤¢™ƒÔ$J‹ö¸�Õk„MÈ¹vR~‰Qâìcw‘ ?zzYã¶u'|á}ÂÑ'ßÜÃ~ð.ì€åŠíì2êàXu´V–0±úÛ—FˆÖ€’Ÿõ×U³Ÿ;%Ôv×¬é«”½ÔýµcÀn¬RM#¬G¤1'Óƒ�Ý˜Q†ß¡úzô,Ž(j5ÊûÑæ$J(tRãØ@ù¤[HÓ-e,†µ ¬NTÖñv‘÷Oôj"¿kÌTÊIj~xÄ0‰¡\°…ƒä  oÔó¨ÂŽõ�(dëeŽ²ÞnM Ð¿�o™I¢bkDm—6ù³±ÚÛ‘ß¿‰È:†®%cyíù%ºQ§A’ÚÙk/\wÀÅ;>@•°ß•.hêóhœë+ê9,ôA1~²ø"Ÿ‡½‹up<R|`ÌÔÒ6Ž�Sh +šÍÆ åÆŒ2D!4 r^‘*©1ÓÂ� Êñ…ØýHÈ;®5°”¾ÝQ”":ÅDÂ†¶Àò«õn“SÑ2h‰NÀZÔÇ¯½Ìfb¿¸¡kªVêÊáL³…b1ÜÍH0ûº‡3åBÎã�xf�íÊÞrû7¬6€­dŒ¢RRŠNÿfÀ€_ÚyjÚ'Šyí|cÛ�ö{é'8¬Onäýš©&ãØ ~;f¦W<0<óTÍ”ÂO3{À?S$äžz&FžyÃÕÙ�ùÚ�ÙÊ­5&ÏÍ>¿ž©�èÛJÙÆƒÕã´%%’Öx€«Á	HœpŽŒÂû§‡á�÷v³¦sš µ—ž{×ùŽFWïÁíµ DçûØ'óm¤>Ø¡K¾�½øÑd|°JDr^uÛL�*¨5 lmÉ´ñNY†ŽþZxòó6r¥ÇÙÌ&ý?]á~dÛ»8$HÜÀÖC¶ÿ‹ÕÐ´ôþ•f8y¢ä·±ï—æ]pd�±ÅOï8`õ³oàÞ—½	ÉÒ§pgšbKD2<˜^€«7ã¿‰þ›åla9ë€¶O³š•VïŸ�èè€“§ xœ½UÑnEU¨`�Ò„
H"È´RÙÙ`¯$â&Ž…V(ŠSUU‰Êd÷Ú;�½»ÌÌÚ5‘µ|R5/Há�x±Ô Ÿ€xC¨ÿÐoèìÌzwí˜º²¼»sÏ½sï¹çîüôàºI{QÈ:E."à£ZB=÷ÐµYØCVz°Õæ�®ÖýüÎ¢mÚ‘?.ü-×úß"¾�!†}aàÝŽº!ñªH#@Ó‹y@Ç©›_,h—×cmmáúÀ¬F%OI6ÖV�þËËõãà¼!#(ûµià}A»ÐÞ Ü?À;®·•™[�ñÎ…_Ûm÷$bÀyÌÀ`…h�R�¸á‚r¤ ¸<Ô§D#<Fh€ �ß|ã·ùìâf¢øáÂ83´}žV, íáÛ�
Bí«45:€jÆ�³Ë†‘oèelqŸ|ôÉ§VÉAò´CZ‡crÄ¶Ó‘h.�9íD¨ÚvSÊø–Ú¡ÝvïHùß;Jq](¡šC\™®–Ê‹y\]£Ž©„a7Iß'[Ø[Ja�r¸Þ©·ƒ¶wÐ©òGˆ¶¾lHrôë~ÚvfGˆš�©«>„Ý>T	T?ì"hz%YÐ0p[ºCV
Õ>šsMÁ€±�YyÄ	j”=�Ôÿ¨T¸Òåþ�I‹³ÒM±É¼jÿW-Y†xkCÆvžàœ"’™*L&{Á†Yô�¯tƒ°`1Œ›âN‘*£>éÆ fg;ËÞHÎIo8O mc
·Ñ±2œ¨]’r,7‡Qƒ“ŽÖqó …¶>Ø6pçX½Þ‚ #üò.põÈÃÖäÕ.õ^ø,|} yÞKÉÀrÿ-”í¬dfÜœ8òH.¼Æ”ì
içæ©\¦Få:j}õå7ŸÞ>Ø»ß¼{¸×*çä&ÙÀö”iÐQlÌ Qmfö
‘Î›_4á‰ÇÇ„12Ä×ªEø’Â3·d2÷NsO;gE;¦¡sú2ª”h5Ä•GkÖä×Ë›ÖÖ„s�v€lùðÐ²«³"�¿,¯Ö2±¨Ú]"fòåÕ™<åÓå++Ùx*2‡é¶ò»õ5ùæÊ†|üñÒj®Ü.·B÷Äx4n-ýúÊ¢9+ÆKo¿{aÜX{² ï[‹Ÿ™3I}ë'òŸ{ÌáIŸ£l¹Z‘¾wU>®]\I¿Ì‡ê¬æc(/£5¹d]’®¿*ë–5>Û¸’oÿ×ûO^ŸÙ¼¸íåÙÆÝñAý÷—Ÿ#s“&ï“’xœ;^z¨tBøÄöC,&³0‰M–a’bT˜lÁd4y£ wr~nAiIªGbqÆäÕŒþ“ÓMmÓò‹Ê‹RBòƒKò‹ÓSuÒSKÜ2sR=óÒòjÒŠòsÔõôô!¨´$3§X¿$5'5½(1wób¦DéÍ<r�œ›SÕ…K’+¿¬xœ340031QH,-ÉÐ+)fhr•ïÕöcÉŒ§äŽ¶|$ç^àzÐ¢¤(±$U7'37³¤°’oñœÉ»:û^Ÿ7ê7+áX/`Æt q–e¸xœK­(È/*Q¨V(Ï,ÉJ,IõÉÌÍ,ÑQHÎIMÌ+-€‹8'&g¤…3R“³Ñ”¡+JO-�—$–+Ô*¤åç*¨ëééCPiIfN±~L•º5 qí0«¤xœ340031QÈÌKI­Ð+)fHm«*9ÆþÀ&i×KÏ…âë…%�æ Þå â%’œkxœm�AKTQÇy™R3E�$�`ph33¡ã¨)d©	N`�ã€ "^Þ;ú.Ü¹÷yî}ŽÒ¢µ‹ á~‡VíÞ'hÛJhÙ:pé®mç•‚’wu9÷wÿ¿óÿ:�U>/ù¡`«ÜÆH†nb¡#…o¤ÂU½güQð»¿ôÖË[gýïsý“ål}`x0‹î|Æ6I$vÿ}Ø˜iãAŠÖíBOºØ/?Ö@	‡Jv¥Ã$•Dí`µÎà$„‡ù€Ðš”B<ŠEj�4º�!Â=‘*C :5Û`ö`r®aÙ¥#Óc‘R¦gó´Ðè0%âÀ:(qÌ¾$µ12s'àX¥Xie$õ~~sÜ›]~{ð©Ÿ-V–Áx>ý:ÆÀ;¡�á¢›$2TgÏd�EÖÁeûM.Ýæ¦k¼£ŸºýÈ¹Š,@µ‹~çacˆÐ¥¤ýéý‘	–Y¨r¡ùKMNA‹LWZ|ÕF›p.Ör&ûQ<¸wMV*=É~=¨ÿMý—»uß-�¶®>¸ñðYi®5;Íù×¿@Zï7:7þ[©|Ýöº\Î~>^þ ˜4Ìrã†§xœC ¼ÿææ#100644 auth.ts jÀúäI[æiqþÅ²ÆmÚªá‘#�ÐípR£=F©ò·¶
ý3å1‘Ä"²„É³xœ}�1Â0FwÅm]J3ÔÍÑÉMìíµÄ$^îÄ þwâ$ßôà{)²À(Û~À›b–NJ~Úº��?(r±Â–îÈ4—=c¦%àtdÿe¶'Ê/˜9^¡é:S§B>›Ü§ri6+ü$¥$¬Ýú×fõµ;þÈw!©ü¾hrEªd†¤0xœsw›à Xd¤xœ31 …Ô¢¢ü¢b†®y¼LÏbÃÝŽË~ùylr÷õ·�÷ž™€ää§§§1.`3{TÜŸá¸ô›ÝÌÐ“wU‹z³!
rSKŠ2“‹„z9Õå¾þªŸ[%ªo#­~d›õâÃ¥%™9ÅÁoº„Ç§Î,¿ºA?Üãà‰-º¿ ×7C¤xœ340031QÈÌKI­Ð+)fX›¿!Ô eûÖ8¾7ÛkE}Ž»? ×•jg‡±6xœ›Æ;•wÃT6 
Ì‘¤xœ340031QÈÌKI­Ð+)f—¨<}#aEp¸ÿ‡£¦gsåi àq£·xœËÌ-È/*QˆÏÉOOO-RH+ÊÏUPÒÓÓ¢Ò’Ìœb}ˆ„’5WjXeJjZbi\\¼nFb±”U—-©,H*ñ�ŠCí)ÏÌ+.ÉÏ ª×/v¤xœ340031QÈÌKI­Ð+)fèd5àßžsÀøÈ&>‘�«�Õ~  ÓÐ×¹xœK­(È/*Q¨VÈM-)ÊL.vÎÏÉIM.É/ÒQðEQ¨UH+ÊÏUP×ÓÓ¢Ò’Ìœb}¨6uk ò�Mªxœ340031QHËÌIÕ+)fxöÛ©2cÍõÃºÏŠ˜ëô•…%Ó§B”d€l•ñÏì;âþ7±KÉ†aÞƒŸ"Ê·C¥–U‚ÔØ-d¼P×‘òíõã%þa×¯·xUS1%0.ïlü††õ_nfÿëÜjöU´) c{9g‡¯Gxœ{©ðBaC†  :å†¡\xœ5 ÊÿŽŽ°YáLÇóå¤écêÅ$uÐ¥q-“m�kjÌïÁKá1r¼>‚»!'/{|BåExœ5 ÊÿŽŽ°Y�½$,ÂgW�âS�Î4Zåpd˜µ“m�Š¤^ó�{B¥7	2Ž8ŽÊ'çÛövZë„÷Qxœ{ÆøŒQÙÐÀÀÌÄD!±´$C¯¤˜arÜÚÍ+®“g¼¸¹€×Ôö_F›Þå‰Ê‡Y’·ë9xœ{ÆøŒQÙÐÀÀÌÄD!±´$C¯¤˜ÁRæ½bé_ý:n÷ˆ–[??rË/m~7Qù0 I#5áS‘ñQxœ•TMoÓ@RRÄ…Â¡$¦h%NP+@­RÑ�€* Tq«–´‹=ŽWµ½fwÓµ•OÜ¸å/p®�ò?ø9rdÖvÒR—Ä^½÷æÍ›Yßü´›ò(RÃ	hñ¸ã0[º,„3ð¥ˆ`..º²—h1·T*ý/¾^©” Ûº¦JKzWD	K%W"0‰Ä#Œµ*¤�iÍÜCeßHì(Tp�+Û¿U?€ã€»Èn¬€Ç¤+Íbm¤$v˜ôBT
„o¤Ž”HEÒÜŒ�û>JÚ¨»’D|*_Hˆ¸Š˜vôj!ÆP�¤«•ÒØ‘à„È�ç‚˜ÄâÔÛg€L†=òa´‡ý<M˜diD_C¶É�Ï¥ÒÃ|H;‹H¢=‚G y'ÂRŸÞUxYt²-»Ü‡÷‚ŒÛ% %—†\/e��Ít-W‚&”�¿Å‚XÍë_­ERÔ{Íe8)A|ÖÒj×Jùƒm¶#Ó²–F8ÉSR›ˆÌê”°ä½|¡hÏ6›¹™KÇVæŠ¨ó)šzgF¨8ß�û´<câÕqIª~F¾Z·Îþ‘L™~z6s]Ú«Ø«‚š_¾Xýó™ƒš1~5“¶’Ö£
ùÆèž¾ÙwoVï<›z<�Št5zïÄŒšÁ*jjxbÎgn¤õ:¬ì:àÌƒ"
„$E7‹®˜¢;í—Ê¬ÌcðXOAùQcáI£Q¬”²F#zµ²·¿Õn9Ï7[ëû­½­�ö›}§µözsÝ¡‘å´¥bJ±8¶;¨é²÷Ü³*pz
ä˜SQXþ«RÿÛôn?�Ÿï&ô|EÂ$žMzÃ•èÑ‡„³°}9ãÁ—Ùµ©Æ¿åz(MæíÀ}pýÚ/ˆÌé�ø?xœëO>á¸ÁŒqr£üäƒŒ"Ni™9©~‰¹©V
Å%E™yéÖ\µÖ\\©ùE%
%•©
Q™®y%E•
¶
Õ\

&+èŠYe’‹’��¬òJs“R‹¬'÷1ËN¾Ì,3Y—E~r#‹,È6µâÌªT§Ê’Ôb¸2¨‰‰Å×°ÊLa»9ùûÙÍ³9Ý'Oâ^6ù!ÏâÉ�|;'oåïÙ,$´‘iózQ^ÆÍk$,x6'«�ež\­¥¤R\’XT‚lvNj^zIBè¹ÍÏuV°0r .’[Öìˆ�Uxœ»Áy…c‚´ÈÅÐßÇfw‹ãÔKY-Ç©Ø$ ?o¢¾£5Ç*c­îÏ
ZóCtª&z\x‰ñ‘¡��™‰‰BR~‰^Ij1�(fhnú`æ3Ã§Ó^p¾ü¹‚ÅíŠm¦G&^Ÿ+òú�·Æ÷?7ãÊÖJ¯»»¼6bŽï±U“;ÓÝf4‹ïXTd°8#¶û�õ‹Ò³6ºÝ†š[P”ŸRš\’™Ÿ§›j”
·¢qAnÃ—+�ZG÷ìPÚ<õý�&‡3Uˆ„Å^ú©.¯Ø(ñzÆÁE;Û8dÕO–dþ'âòÊÁµ°e¥â�<ŽÄ‰;l‹}'“š¬Í"+R�µ+¡¿gYL•œÂ_™Nñ�ÏeÅ'Ç°Ô  $sÃè�xœ»ÂÑÅ9¡ÀzVˆZÚ™õg5UrUW|.î¸™oh``fb¢�”_¢W’Z$ŠÌXâŒšunm4>É•g—¼çËÝ}LWaÔIÉOÎN-Ò-JMÏ,.)ª„k°Þ|ÁX©¼$·¡¸Í³¥ËÏí¥§ïlžÊ´Ÿ Œ+2ZëG�«;xœ}RMkQ%VÓtŠ-15ÄB'Ñ�Ak1Ml°…‹õk¥´Nf^ÂÐÉ¼8ó&¶ÔP—Šwç¢EÄe%2?@WâÂ­[qåVpåFñ½ÉÄNÓÖM&÷ëœ{Î}ÏçÎnÝë<;/ØTÓ-©¦é¸s3éE:©×±Ùùôç8Å:v>^•$	Ý Ê*Re*We
1,Š,·hXØ¤hÖ2™,š-£Ì†€PKÖmla9’¾žÚÙlI ëMŒÖ4‹jF½ÂXÙomÚU]SÕ"²¨ÉJ%–ãäuSnð6ÞÆKý ]|«T4Cåíóëwí¡ÍÆöÖ*¢%“44_ÙÇÿ¶®—a.…­Pp“áñ�|ƒ¨¶Ž3b>/Y¦"©U‰aã51‡|
ÕjÑ]!Í5¡è3$ÇòmþÃÍµX_Ûóà(|·os?Aí€ âBûàýCÕˆùH6Õ;ä6%¦\÷¬2PòŸ.Vf¬žQy[Doa¶	ìŸƒn,ß‡ã0š˜ngs£;LJïõ¯ç×†'ÙaùÄSx|"ìYÝ`lü DžõÓ0K�ÉðÛeKîQ¿ƒó0ŽÂ·Ðý¡*¡pkä¬óqäKÀÞ…ãN2²ñ/€§Øž(8»£×�
ü8=yYT‰²ráâS!*­|S­ñ,i7°AE�¯5±B3>$Õ…é±Ú 4ìŽ†�÷ãUßL\‚­‚_‘$ìD_ûZ�`O¢3›<æv‚î\tºl(•|åÇv¶'®z+Dö¶•Ulö×æÏŠ½ô‘;;$5rÐ‹NzÌ.‹-MÅdÅ bïUþgÉøŠ¤¿c<}‡ïZ„ xœûd»ÃqB[qFbQjÊÆÖ‹,“Ó9j´sRKró“³CRsRÓ‹sƒS‹Ê2“S­ªÒò‹Ê‹RBòƒKò‹ÓS'¯f’V³æRP€éqËÌIJ-È+ÎÌKqªÍË,,MõL*•ä°VH.JM,ëc²æšü€£e2—¾Ðlv*Ø*Tƒå0¬¶«×ÐÐT°µƒ*™\Ã.Á¤&°k�¨mìFõ‰bˆßÔâb å™b¥`	:Pm

µš06”QkÍwÌKÈnAñÜ%V
Eù¹™Å©6¥yÙyùåyv““Ø¤ø`fC½>�U‘lÇd}®"±Ì¼’Ô¢´ÄäÔbý¤üýŒÄ¼”œÔ¢ÉK¸”a¶• Ç–€Ò�»þÈN‡zjóq®«Œ¸l¼(!N·ñTÓ×ÆÉïE’'ÈXl~ÆçÁ(ˆf‚^Úd}�p~˜=hŒÈÉ-�Ü/Í=ÙF0m`Ü.”‹Í­]â&“íå&KŠì¡·»ò¸¡AJê›“EÕ˜°ºÏB-,5õòòK&ËË%LÖ•X80�Ù,É�=âÕ'o–2Äù)twëæO\î|(kŽÅ�—êäœàÒxœ  ßÿøø°…êyDß¤Ç²Óµ ”ºOÕk±9“0HòrìÑVxœl “ÿ  �ÜŸ øCG^b¦Å¼öÜw\àX™Ç‘ð:Xžz¡O¶{ôÂVjæ(¯ÐíÓ“>.4³ð§ly)"ûAD¬ªç“i"Ì!40000 utils -UÿæÜ†Pâ¾ÛçÚkÞõ”¨"2žªxœ31 …¤ü‡y7”&+s|Ó8/wUù_ëÜ°_6š€e3JJ
fê™Vøôpî=à¼n¥¸’L]¥DºØ˜¡¹Ý$ïåÉ?Š¼s7×dîÐ¸]zøØ\ ç$i¬xœ31 …äü¼’¢üœœÔ¢b†¶Ü½µ3Òºîœþ³õœ¿ë×µ“ÿ´z›€Uåf¦¤ä¤–'¥2°G\ô~sâ�!ËÛ¿³—ËÍºëšòÈ¢¨(¿´$µ˜�ûþ·�ÛÅZŸ–¾Zë‘’æõ,ñù‹-1²ì¸xœ{Ãô†iC£ˆì3÷ïvêŽéšõœ´ÖÑÝ¾(ü ª‚\è£cxœ;Tº;Ã6±�¤Ò´´Ô"['0C/­(?W#±<1³D!-3'U/±¨(±"§¡©iÍ¥ �œŸW\¢�‘XœÔ•œŸ[PZ’êäi@Œª™¼VÌ\b†Siž^yQfIªFIjnA@bI†ŽBÝD)iQO¯¸4	l•†�Ž‚¡Ùæ›R,Œ‚P©¤Ê’TŸÔ¼ô’ŒÉÏ¤�[)`Š¯•9ˆ©zs¹ì,&lÊ7*(c1}³­b«$ „Íeë�xœkalaœP R¯ªÓš¡T&;30®�5¿ÈÕºÑé; yÀ	jë™{xœëbìbœP&ÒÓd ª!½0²¤æƒØ¯Ëÿ®	–Ky  Ž–Bo�‹+xœëOÞ‘4áàÆ7Û•7ŸW9À E£Gí=�‹GxœëO~ã<QeîdYFÉÉeŒò“2Š8¥eæ¤ú%æ¦Z)—eæ¥[sÕZsq¥Vä•(”T¤*De¸æ•U*Ø*Ts)(`h˜¬ +"d”I.J66²RÈ+ÍMJ-²žÜÇ,;ù2³Ìd]ùÉ�,² ÛÔŠ3«R�*KR‹áÊ &z$g L\Ã*3Y„íæä[ìg7Ïætcœ<‰{Ùä‡<‹'wòÍªÏK-W(ÊÏÍ,Nµ)ËÏL±ÓÐ(J-ÎÏ)KÕQ(JÍJM.ÑT°µ»Ê‹2KR‹ôòó’S5ÔSŠ3óÔAêÀ4­±)J-*Ê/R‡QS¦k¹j™¬¹&oáO¬O,OÌ,Q É1P[RóR44ÀÂP¥šPK9ähôlZÇ4y½h’	Pœd·LÓ×V Š&¦€í†LbI"Ð2�äŒÒ¼l+§Ò´´Ô"„ú1…”�@Òvl^
F°¢*Àî›ÉSÄ4&¯‘Èâ)IÍI--ÈÉiÙ,'9ƒÅ�tOm�µ©'ÁS0 2ô T˜šQ®©—œX’œ¡�’
�ý�5¬1Óá€€šºÙY.�mòyÑÍÉjg™'Wk)©—$• ç•œÔ¼ô’„05l~®³‚…‘ £ì8Ní‚hx=ÂþìCñ@$import { once } from 'node:events';
��%finished } from 'node:stream/promises±‘Zonce(writer, 'drain'“bk%writer.end();
  await finished(writer³B“�/$for await (const chunk of reader) {
“ê  as Buffer);
  }“^’filedrop³ü˜“’ “ë(for await (const chunk of reader) {
    “chunk as Buffer);
      }³»1F•lˆïÞxœûÁòƒeƒ“±SOŽÖCÁ¢	Ÿg.d×ääfœµ]ÔÄ JR‹K¤—>
ª4vìôßÓ¼ôVÛ™ŸjB“ý™4øà‹xœ[À¼€yƒ£ÈŠçŸ™«MM>]~ó®¤·†hÃ‡ºÉvŒI ãÕ«xœ31 …¤ü‡y7”&+s|Ó8/wUù_ëÜ°_6š€e“óóÒ2ÓBÏn|ñOÝ@X¡ã]sKÒc¹u1š%%Þ¹gbÏ×^WÞœÆpì…ÈëSÒ!ÒÅÆšÿ&M¹Ò¤ S–q:WèÚSù>u¯x Û-20g|xœ«Žš 2ÑÕ S–¬xœ31 …äü¼’¢üœœÔ¢bÓÏ�L¸ýhÕÇLßÉ‰•oÎ.0«ÊÍLIÉI-O,Je`�¸èýæÄ!C–·g/—›u×5å‘3DQQ~iIj1Ãƒ§ÿ“m¿í»ÿ†áðÙÍö\ÍÓWoy 'ï2®ïŠ|xœ{Ãô†iÂw‘ÏÓœr^z×}y¼T;áÂœï±6Â“¹i™é2¿ xœ…RÏkAe–š"Ò¥¶Ú|zé¦ì¦j1‡Äˆz1¢B¤›Dkf7³?B³f'Æ¼‹Þ¾‹´õ ž¬ÂâUý
‚—zQô^QâEp²»4…¦xÛùæ½·ï½ù6·¾o¼žºŽOêGq°r<Øª$÷áÃ³CÅ’–FIiƒî¸„u@÷j�¾LÉÆHçbË4)“S¹€á¹>ÓY¤Ñò}¤Mæ5ä.3•Ãkåc6ñmqkx�f‹Ó‚8É=n,<Å\j?¾5§%?Ý–'5O£‹Ôb¤±§@WG«ç†»CÐ#ö­ª‚3¤RŽŽ ‚fÓ0@9uyN'`Î7‰ÅºÈ¨ûªÁ|á‚zúLlzjŽE}Ž_×ŸIJR{žDâw\dQB$œ�“+bºüùjwðÆ¼Z"mâppÓmæp*sÚh	·•˜,ÊÜnÓw,—ðëU;÷[zø òINeD¯Œû¸\{>ßëN¯R×â6>[‘Ñ¦yüPÂ÷T;´Ñu‰yg
f	¿¬Œ‹æ—°ºv0¸imH}¥sè:	ñ8KÎº c¡>'ÙÁr=£â;>ˆo’#WöX}ïÕè¦vL�û'¸<›Ä«Ù>vŠ«E¯8ýhìÄÿ»Å¿æ%îÞèÎí‹wçÇÚ¯±,ìúCðíñïñö&+Ñ¤xœ340031QÈÌKI­Ð+)f`[ Àeò´zâ.Íô(Í–ó—Õ—  Ì× á±9xœÛ¬>EaC·žž>•–dæOžÁ¤%ˆÄ×/J,Iõ™|™I‹&´ùÓ#¶Í&<�˜'ßãPŸüB€g²…€j}Fb^JNªobeRj°qP~~‰—x¸:ºX)à�tqõqqÅ)à‚SÒ? ÄÓß/&Ï�¤`ò!©Í©B~Œ ‚ðHRá€xœ›¢Ð¥°¡�‰+73%%'µ<±(uòL&-AW¿(±$U7gò&­ÍìÌ¼¼ ÄY,î¯Cxœ{ÆøŒqB¢È)9E™6ÞÊ÷·?¯í´ùýP³JÈŽ‰¥… Ù[ölžxœëâ¼Â±a>Óæ«L[! àˆcxœûÁòƒeƒ“HQ”è$Ëo;æ¾èÉË¿µéyÉåÉL àÜ­à”_xœ[À¼€yƒ£ÈÉ´ê’l†Îôü�oøE¤O|vöûd;Æ$ Ö{8¤xœ340031QÈÌKI­Ð+)føûì;û)¹«Âö‰×8Z/vœ´% ó·y·xœ]�MnÂ@…÷9ÅÛ!E49@—Ý°dÁp'p•Ú#ãÒ?qwfRQ%¯,ß{û¾C�Ó,‡§S8?ñW1dsÄ‘!ì™Ÿ0Ó7ûP¡ÆmÝÎ2Õ-!±†Ó,?<�R½<¡Xån*e–´ØÓcV“|JEÿçŒØìv[ÐD¥î–Øõ_5ÑC#ŸHM«}Æ~qïa¯oœgÑÖ'nöÇní‹±»¿ú{/†²Û;VÃ0Öa=¯ž»+O\e‡à‹,xœ  ßÿøø°°‡Ý%e<×­‡Çßym1k²Dïû“Ä´ú•Ã¦x& Ùÿ40000 superpowers “�Ýí\òw0c‹Úvf¥W>™Û«*Ò x@ ¿ÿ40000 plans ^\8Æñ†eP8ßqÑ@�®0û40000 specs sù8Ÿô8Ún¾Qßzd Ûûh§5=/à€/xœûÁòƒeƒ“ÈU™À]ŸêVÝ«ftß§‘qnFÞd& á4eå˜Wxœ[À¼€y+£åõ»ý¹í¶æZ•¿éé¹½5£þ–‰(dæ•¤¥%&§3œ5oqóˆÞ½ŽõëYÍ»7Z½·õM¶cL Ù	ßéßlxœËÌœ*²ùÛÄÛw“öòå;Ìz~!hÓÂ„ð2 ¤dÍîØxxœ áÿÑÑ�AçìgùØešÁ ßR«Ð« Ó�¡¢‘U|ñ±	ìÓxœÛµ,jÂ®‰ƒYJK2s&Ë2:�›3Ïk µûŽªxœ31 …¤ü‡y7”&+s|Ó8/wUù_ëÜ°_6š€e3JJ
vw$^™ãÿ÷D½QìŸ\wß{›9ÛÌ!ÒÅÆšÿ&M¹Ò¤ S–q:WèÚSù>u¯x s%¬xœ31 …äü¼’¢üœœÔ¢b†¹úu·dšÏÿìžZ=5 ûf„tHI¢	XUnfJJNjybQ*{ÄEï7'²¼ý;{¹Ü¬»®)�œ!ŠŠòKKR‹Ž�_"v\dµoÓ#óõ5{>î7[õã  P%/`î�+xœ{Ãô†iB…H¿Ú©ð¨>xˆ©]Í`)îhÕÊ½=±ç ÐOýíÎHxœûÈ~„}‚SJÒÄŒ8žÒ’ÌœbýœüôôÔ¢�7%˜©åD¤xœ340031QÈÌKI­Ð+)fp;xêê’Öy„ÿO[4aáãTg™ ô#}à„yxœûÁòƒeƒ“Èùc_ÊÚyü�Kø«gÞ¸ÞuðçíâÉL øÃ;å�!xœu Šÿ  �q½Õe2÷ÜÁaß"Ö^ý&÷ Aï¹ñ‘…€9suØ×¼¥VÏ0CQâª’‡Ñ·<„40000 interfaces ëæÒ¶²ê6!ÈÅ‰¹nëª‰“>.ýä *½ñ€Ô;Ð£báíz¡æùáj“€ Úˆ<— xœ31 …‚ü¢’b†– ŸéžÛf?rè8š¿5¦sÕÅh ¸ä„ xœ31 …äÄäŒT†%“cÝšÞ\î±Ûw°Aà•é¿Ë· ¿ff¤xœ340031QÈÌKI­Ð+)fˆ=?i:ÿGÉÌÐk„'ÆMÓÐ¿Ý
 äÕR¹xœK­(È/*Q¨VHËÌIõÌKËwNLÎHÕQ S
µ
iEù¹
êzzú@TZ’™S¬Ÿ’Q· @‹ªxœ31 …¤üŸÀ¿ëŽ÷¥NX—Qwyõ³üçÍ^2Ëf””0tqo–q_ÆÁ0OQ&|ý¦©³ï-vz‘.6f`Ø÷ì¥Eû‚ð`‹¦­±œe'D˜~³ �A$‹¦xœ340031QÈHÌKÉI-Ò+)f8»JU¡6ðÐÑŒ®¬]oÚ„³b÷Gñ   îÛéØpxœÛaµÑjÃJ&žÒ’ÌœbýœüôôÔ¢Í{˜j¤ ˆì
,¥xœ31 …ÜÌ””œÔòÄ¢T†¹Ë:<§þ’y;íFùu† Þƒ«v;‡ ¸º©xœ340031Q(J,IÕÍÉÌÍ,Ñ+)f¨ä[<gò®Î¾×ç�úÍJ8Ö˜1Ý 'É‡®xœ340031QH,-ÉÐ+)fÈ9ðë‰gô³ÌÂG7Ë½Å¿ê¡!DIYfQIibŽnF~q	Hé…·òÂAü‹mÝØV~Ú¾�ë¯ñSC Ò b£xœ31 …œüôôÔ"†S­¯s"'Ílãq}l\#}7Bç·’	XAnjIQfr1ƒP/§ºÜ×_õs«Dõm¤Õ�l³^|¢¢´$3§˜¡^U§5C©Lvf`\!k~‘«u£Ów ún#¹¤xœ340031QÈÌKI­Ð+)fXþi›Ë97µ¹Ç3Ù“œýî}} ã�y¼xœËÌ-È/*QÈÉOOO-RH+ÊÏUP×ÓÓ¢Ò’Ìœb}ˆ„º5WjXe5Lm­5 xfxï˜LxœûÁòƒeƒ“±£è=ýgÛ¢|½¿¯žþõÑþä—…&@ P’Z\Â¤Í{ÃÆC©(ë˜ü*M×…ÿ­¿Ü›ìÏ¤	 eÿ¶á…›Hxœa žÿ ë"100644 bot.ts ¬®ß·:õˆ5zªe©Ø8³â‘G‘…W–G0S)>¢Ò&èÅ|þÛo^ÿ
ö“>'utils -UÿæÜ†Pâ¾ÛçÚkÞõ”}†+.áqxœq ŽÿëÚ�°Hinfrastructure ’¤1¨­cæd»ð �ÂÓ…‡øœ40000 interfaces ñR>½Ã	²&u$	äÀú¸ïµž‘ª'shared ½|¡:}úß�©­]4ÕMÖø»1‘Ë €,4‰½Õx]%¢Úimport { eq } from 'drizzle-orm';
import { nanoid } from 'nanoid';
import { type Context, Telegraf } from 'telegraf';
import { db, files as fileSchema } from './db';
import { config } from './env';
import logger from './utils/logger';
import { forwardToStorage } from './utils/telegram';

interface MediaGroupBufferItem {
  ctx: any;
  fileId: string;
  fileSize: number;
  mimeType: string;
  fileName: string;
  fileType: string;
  fileUniqueId: string;
}

const mediaGroupCache = new Map<string, { timer: any; items: MediaGroupBufferItem[] }>();

export const startBot = async (): Promise<Telegraf<Context>> => {
  try {
    const bot = new Telegraf(config.botToken);

    bot.command('start', async (ctx) => {
      await ctx.reply(
        `ðŸ‘‹ Halo! Kirimkan file (document, photo, video, audio, voice, animation) ke bot ini. ` +
          `File akan disimpan di private channel dan kamu dapat download link permanen.`,
      );
    });

    // Cast bot.on elements individually or explicitly as any to bypass Telegraf v4 typescript deprecation warnings on array syntax
    (bot as any).on(
      ['document', 'photo', 'video', 'audio', 'voice', 'animation', 'sticker', 'video_note'],
      async (ctx: any) => {
        try {
          const fileType:
            | 'document'
            | 'photo'
            | 'video'
            | 'audio'
            | 'voice'
            | 'animation'
            | 'sticker'
            | 'video_note' = ctx.message.document
            ? 'document'
            : ctx.message.photo
              ? 'photo'
              : ctx.message.video
                ? 'video'
                : ctx.message.audio
                  ? 'audio'
                  : ctx.message.voice
                    ? 'voice'
                    : ctx.message.animation
                      ? 'animation'
                      : ctx.message.sticker
                        ? 'sticker'
                        : ctx.message.video_note
                          ? 'video_note'
                          : 'document';

          const fileObj =
            fileType === 'photo'
              ? ctx.message.photo.slice(-1)[0]
              : fileType === 'sticker'
                ? ctx.message.sticker
                : ctx.message[fileType];
          const { file_id, file_size, mime_type } = fileObj;
          const fileName =
            ctx.message.document?.file_name ||
            ctx.message.photo?.slice(-1)[0]?.file_name ||
            ctx.message.video?.file_name ||
            ctx.message.audio?.file_name ||
            ctx.message.voice?.file_name ||
            'file';

          const maxSize =
            fileType === 'photo'
              ? 10 * 1024 * 1024
              : fileType === 'audio'
                ? 200 * 1024 * 1024
                : fileType === 'voice'
                  ? 200 * 1024 * 1024
                  : 2 * 1024 * 1024 * 1024;

          if (file_size > maxSize) {
            return ctx.reply(`File size exceeds ${maxSize / (1024 * 1024)}MB limit`);
          }

          const mediaGroupId = ctx.message.media_group_id;

          if (mediaGroupId) {
            if (!mediaGroupCache.has(mediaGroupId)) {
              mediaGroupCache.set(mediaGroupId, { timer: null, items: [] });
            }

            const group = mediaGroupCache.get(mediaGroupId)!;

            if (group.timer) {
              clearTimeout(group.timer);
            }

            group.items.push({
              ctx,
              fileId: file_id,
              fileSize: file_size,
              mimeType: mime_type,
              fileName: fileName,
              fileType: fileType,
              fileUniqueId: fileObj.file_unique_id,
            });

            group.timer = setTimeout(async () => {
              mediaGroupCache.delete(mediaGroupId);

              try {
                const itemsToUpload: MediaGroupBufferItem[] = [];
                const responses: string[] = [];

                for (const item of group.items) {
                  const existing = await db
                    .select()
                    .from(fileSchema)
                    .where(eq(fileSchema.telegramFileUniqueId, item.fileUniqueId))
                    .limit(1);

                  if (existing && existing.length > 0) {
                    const url = `${config.baseUrl}/f/${existing[0].publicId}`;
                    responses.push(`File *${item.fileName}* sudah diupload! ðŸ“Ž\nDownload: ${url}`);
                  } else {
                    itemsToUpload.push(item);
                  }
                }

                if (itemsToUpload.length > 0) {
                  const uploadItems = itemsToUpload.map((item) => ({
                    fileId: item.fileId,
                    fileName: item.fileName,
                    fileType: item.fileType,
                  }));

                  const { forwardMediaGroupToStorage } = await import('./utils/telegram');
                  const batchResult = await forwardMediaGroupToStorage(uploadItems);

                  const dbInserts = [];
                  for (let i = 0; i < itemsToUpload.length; i++) {
                    const item = itemsToUpload[i];
                    const publicId = nanoid();
                    const uploaded = {
                      publicId,
                      telegramFileId: batchResult.telegramFileIds[i],
                      telegramFileUniqueId: batchResult.telegramFileUniqueIds[i],
                      storageChatId: config.storageChatId,
                      storageMessageId: batchResult.storageMessageId,
                      fileName: item.fileName,
                      mimeType: item.mimeType || 'application/octet-stream',
                      sizeBytes: item.fileSize,
                      fileType: item.fileType,
                      uploaderId: ctx.from.id,
                      createdAt: new Date(),
                      updatedAt: new Date(),
                    };

                    dbInserts.push(uploaded);
                    responses.push(
                      `File *${item.fileName}* berhasil diupload! ðŸ“Ž\nDownload: ${config.baseUrl}/f/${publicId}`,
                    );
                  }

                  if (dbInserts.length > 0) {
                    await db.insert(fileSchema).values(dbInserts);
                  }
                }

                await ctx.reply(responses.join('\n\n'));
                logger.info('Media group uploaded as batch', {
                  mediaGroupId,
                  totalFiles: group.items.length,
                  uploadedFiles: itemsToUpload.length,
                  uploader: ctx.from.id,
                });
              } catch (error: any) {
                logger.error('Failed to process media group batch', {
                  error: error.message,
                  mediaGroupId,
                });
                await ctx.reply('â�Œ Gagal mengupload beberapa file di media group.');
              }
            }, 600);

            return;
          }

          const existing = await db
            .select()
            .from(fileSchema)
            .where(eq(fileSchema.telegramFileUniqueId, fileObj.file_unique_id))
            .limit(1);

          if (existing && existing.length > 0) {
            const url = `${config.baseUrl}/f/${existing[0].publicId}`;
            await ctx.reply(`File berhasil diupload! ðŸ“Ž\n\nDownload: ${url}`, {
              reply_parameters: { message_id: ctx.message.message_id },
            });
            logger.info('Duplicate file detected in bot, returned existing link', {
              publicId: existing[0].publicId,
              fileType,
              fileName,
              uploader: ctx.from.id,
            });
            return;
          }

          const result = await forwardToStorage(file_id, fileName, fileType);
          const publicId = nanoid();

          const uploaded = {
            publicId: publicId,
            telegramFileId: result.telegramFileId,
            telegramFileUniqueId: result.telegramFileUniqueId,
            storageChatId: config.storageChatId,
            storageMessageId: result.storageMessageId,
            fileName: fileName,
            mimeType: mime_type || 'application/octet-stream',
            sizeBytes: file_size,
            fileType: fileType,
            uploaderId: ctx.from.id,
            createdAt: new Date(),
            updatedAt: new Date(),
          };

          await db.insert(fileSchema).values(uploaded);

          const url = `${config.baseUrl}/f/${publicId}`;
          await ctx.reply(`File berhasil diupload! ðŸ“Ž\n\nDownload: ${url}`, {
            reply_parameters: { message_id: ctx.message.message_id },
          });

          logger.info('File uploaded via bot', {
            publicId,
            fileType,
            fileName,
            uploader: ctx.from.id,
          });
        } catch (error: any) {
          logger.error('Bot file handler error', { error: error.message, chat_id: ctx.chat?.id });
          await ctx.reply('â�Œ Gagal mengupload file. Coba lagi nanti.');
        }
      },
    );

    bot.use((ctx, next) => {
      logger.info('Telegram event received', {
        type: (ctx.update as any).type,
        chat_id: ctx.chat?.id,
      });
      return next();
    });

    await bot.launch();

    logger.info('Telegram bot started', { botToken: `${config.botToken?.substring(0, 10)}...` });

    return bot;
  } catch (error: any) {
    logger.error('Failed to start bot', { error: error.message });
    throw error;
  }
};
#~VìRÉkx,ÓúÝJö#‘"�%findFileByUniqueId } from './db/files‘£*{
  detectFileType,
  extractFileFromMessage,
  getErrorMessage,
  getFileSizeLimit,
  type TelegramMediaMessage,
} from './utils/file‘Ã^type BotContext = {
  message: TelegramMediaMessage;
  from: { id: number };
  chat?: { id: number };
  reply: (text: string, extra?: {“Í!!number } }) => Promise<unknown>;
};

type MediaEventRegistrar = {
  on: (events: string[], handler: (ctx: BotContext) => Promise<unknown>) => void;
};

const replyWithDownloadUrl = async (ctx: BotContext, publicId: string): Promise<void> => {
  const url“L!(“|!J“Î!<});
}³#•YLogging middleware must be registered BEFORE the media handler so all events are captured“¡#[2'type' in ctx.update ? ctx.update.type : undefined“$MDconst mediaBot = bot as unknown as MediaEventRegistrar;
    mediaBot“Es“½- = detectFileType(ctx.message);“Ñ'Obj = extractFileFromMessage(ctx.messag“›	{ file_id“ù-Size = fileObj.file_size || 0³	81 getFileSizeLimit(fileType);

          if (fileS“b„$existing = await findFileByUniqueId(’“.A) {
            await replyWithDownloadUrl(ctx, existing.publicId“Pw³Ê–Siz“q"“Ž“} Æ,await replyWithDownloadUrl(ctx, publicId);

““."¥unknown“Ö"60
            error: getErrorMessage(error),
    “$%““|!“W#O“a$šunknown“þ$5getErrorMessage(error)“@%¿êÆ. xœ31 …äÄäŒT7¡0†¾×ê”í«îˆ.7˜â°f]9 Ÿ‚¤xœ340031QÈÌKI­Ð+)f¸¬”Åv�Y÷\eƒZûÒª>»òÿÕ Ò, xœ31 …äü¼´Ìt†Ð³_üS7VèxWÁÜ’ôXn]Œ¦	XAFIIƒÉ¬jÕÔ»B¹‹•ë®zñUªn úýe¥xœ31 …ÜÌ””œÔòÄ¢T†‡Óu~ïàf»Öo½ÎÞ�Õ¿^% ñµ&¬xœ340031QH,-ÉÐ+)f`ÉX\UÛÊÄ²°R1~¡ÌúªÛ]¼!JŠKRus2s3K@
ßl.<4ùÈ¢÷ÿ.±p†™œ¬v è©j“Üxœk�j‰š¹1fµ ‡�¢xœ31 …Ô¢¢ü¢b†®y¼LÏbÃÝŽË~ùylr÷õ·�÷ž™€ää§§§1ˆu´…þLœý°såâ%¿¨èÈyž
 œ$Õ¤xœ340031QÈÌKI­Ð+)f0{»¸�M¡ðÎÚ‰NÚY]Ù{ücc¿ Þ à€ÔxœûÁòƒeƒ“ˆÕ´Ö«ey’Ïû7
O•è{`ÚÄ5Ù€É ÍjÜîá7xœ^ ¡ÿ Ó�&"100644 bot.ts ¬®ß·:õˆ5zªe©Ø8³â‘G•–G0S)>¢Ò&èÅ|þÛo^ÿ
ö“>.¼‹b�l³I²Œ]
¦u:¸¼“€ üƒ'
 xœ31 …Ò’Ìœb†zU�Ö¥2Ù™�q…¬ùE®Ö�Nß›3
‘à€ÕVxœ  ßÿøø°Vä4Ï¢Âÿõq_ìàDDÔŒ¢¸âZ“0HR—ï€Exœ»Ìt™IÍÄ 
r2“K2óó6�b>ÉzgÓzîmÖS_ÖÌm|înµQM— ¢<´¢xœ31 …”’|†™‹ó½Øíî0MÝdy¢%örÔ’OÂ&`ÙÒâTÝäÄâÔbýÏ5.g]ežÊf^_ýü¹�˜ "”h€Ñ(xœûÏX3!râe ±öà€×xœ  ßÿøø°ÚoAä¡·ô;#°Æ3žº\�ÓÃ“0Hó/žï‚xœ»Ìt™IÍÄ 
r2“K2óóv®�üÇ&÷DÒû‚Îœ’ÎäÅ3vœÛ¨¦Ë ›0>®xœ31 …”’|†™‹ó½Øíî0MÝdy¢%örÔ’OÂ ™ÄàÞ,xœûÁòƒeƒ“ÈŸ—yžÞ*•[Þºñ[Í<4Íüüd& å=ƒiÝxœ»Å´•iÂÍ‰ÿb èì	‰ÙVxœœ cÿ»ø‘"˜‘Û¦©†½®–pð4Fóô—ÿ�KvÌ»“•^<‡Ý%e<×­‡Çßym1k²Dïû100644 package.json KMàÙŸûG‚Ìûv#PÂ†”Pîõ“/03Ÿ7UÃâ!ì¤Ï0úÓ •ˆISËP40000 test R+Ø<H"rjÆª)E¡ÿ:AôÞ“’)£ÒFóh„\xœ»Ì´–i£š.# WŠà€?xœûÁòƒeƒ“HþrïL®+ékûd¯Ê5iž§plÖd& Ó
Éj…xœ»ÌÔÃ4QÍ~bÛY t>é�xœ9 Æÿøø°°ÑU H{&÷m*¤‚ÎúÇ^H1i–“ÄX,Ñ~qY^qÁ°’»úëQ’£“0HÜÉißyxœ»ÅÔÃ4aÃÄ1 >–à\xœûÁòƒeƒ“wî!Á%Ë9$˜¶²lÕ>­€žÉL ×'é€xœ9 Æÿøø°°ðˆs¿60o•LÞ„~òvÈØ¨“ÄXmÂˆ"9œ� bÀ–©vª¾*PŒ“0Hïë–¦xœ31 …âÒ‚Ô¢‚üòÔ¢bÑêâ½wç0Ôˆ±^œ¯eÏR±ñ üû: xœ31 …‚œÄ¼b†¸A‹cÛR,îK^t˜°Îà·¸	X¾¸ 5¹˜áÝý?Y{«cå²Cf¯�íd|�)à f°ï€Ý8xœûÁòƒeƒ“qÇöw?î¥|gg^¾-�{•Ôƒ}šmM€@¡$µ¸„AùÅ»ïÓÏìÓÉœVmºÆ]bö¼B�ÉþLš ¦‰Ýîã5xœ{ÍøšqÂ4‘æW×ì4ÒŒš&	—wl©|¯Ôa->q•# ÕŸ_ï€•Qxœ»Âq…cB�ÈÔ–žiŸþœô‹byYªÑbËÌp}êÆ–Ì êr<ì€ëmxœ[.½_hÃ?ÆÉÎLþ“/3‹N^Ê\0Y…Uuó–¦ÍÆì�˜¹j5­¹@ kä€]xT «ÿø×�Q‘rñe8æø/†T•ÒU!ÀÑ`‡H“w9Fâu´€<‰þÍàIuØß®”ºU“Äw:ßk‹ìk¥ÁÂ¨Û„OÀ¬ñe6“O)uÒ'Ž¦x& Ùÿ40000 superpowers ºSW¶ãnÛ(­øúx6ÿþnß4Œ5 x@ ¿ÿ40000 plans  •›ŸN,·Ü?¿‰®g_òôÏã!40000 specs ß}A>ºü˜"Ó™æ¬�j‰„jð€D6ik†ú&x ôÿ†È°“S3Ð4k†Û)x ôÿ¦á°'“l:ÏŸl�|x óÿÔ£°³<rOæ�1x¶ Iÿ××�=Ê²\F}²!×xpDgfÞc½Q ‘QñBƒ^à¹G
~ÿ÷Sz¥Û>46d
100644 docker-compose.yml ’ŸDÑ¸öê—¸£®ïY£Y"<Ç“„3 Ñä/@Ã½ýR!Ñ]¨«êXŸB“Ë03Öv mîoÂym\hòû}cmë_(n40000 test îµ é<ÿO˜-*þä Íf¬ý;Áõ“.)M¬Rié†× x æÿö¾°üteleuploader³6‹k	qî¢¬/x> Áÿ¤M¤M�TeleUploader‘ ™±Å·teleuploader³„$teleuploader³°ô
MØá‡‰hxîþ–’�2teleuploader±:�““teleuploader³ºeteleuploader.rule=“Q“F¬teleuploader“ú6teleuploader“8	)teleuploader“i	=teleuploader“®	Bteleuploader“ø	=teleuploader“=
;teleuploader“€
;teleuploader“Ã
M<teleuploader.middlewares=teleuploader-rl@docker,teleuploader“@VCuh4ï€ö0x/ Ðÿáø{
  "name": "teleuploader±ß“Þ“ŒP³À!…<o«xë ÿ100644 bot.ts ¬®ß·:õˆ5zªe©Ø8³â40000 db 1‚ÂÕï“ÿŽúlŠ¥&£z
ór100644 env.ts C¶@Z)é¡Y½º¬‡×$¿9€ô”100644 home.html ­j¸u(6	X6ïxkøÔÛ£²¸ª100644 index.ts �ÉÅ;[L*’½Ã„‡6µÈ°ëY40000 routes åN�t˜„•¶^RlÎgUoW—uZk40000 utils ßlrÊmC(ïz0¨VbtŽRì§"‡ÆaAæ†‡Kx& Ùÿ·"¦° ³q«
“ð+teleuploader³#=“Ðg™éŠ®QxY ¦ÿ·¡±}�“TeleUploader±›à“Ó“‰,TeleUploader³½T³yí³+¶#“ÒO° });
    loadBuckets“—P ^Z"wî—ãvxŽ qÿã'ó$�n±³v³UìhandleUpload³Z	÷“chandleWebApiV1,
      POST: handleWebApiV1,
      DELETE: handleWebApiV1,
      PUT: handleWebApiV1³·,‡h-=®xþ ÿ100644 files.ts ÎAàF, 82þ°Øì~>·5÷ƒ100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 home.ts $ÝGL&J¼äšÞÖY' áÓRï100644 s3.ts „Zv÷Ï¼ò)ŒÒS`1X'nÌ100644 swagger.ts ‘•Þ�Æ5OÃ%&
)÷È?T100644 upload.ts ¼éŸËØê3ÍÑÓç`ÏK t9ê100644 web-api.ts –ë®ÃFd “!*WæµÛ¦‚o„(bñä…½GxD »ÿ……º€°k“rx³©0³L
“XY ³2&)teleuploader³cCf*teleuploader³Ñm´>”Dî’†xÞ !ÿä>ð;°È“ø£³ž–stream³<;
           “w
L“i!³Ð
/413“Ñ"equest body too large“1'“_Request body too large“"³Ï“lDown“U
$-Redirects to Telegram CDN for direct download“h·“ï“0•³ÎÐ status: 200³ñ“04ˆfKuï€°
xo �ÿŽë‘#§K™] õ’™ùv_ýÝEž³ ‘Þ{ãCòÿßv26óÈ©1Ä˜™“<¶Ü“mk6yšýF1[h1Ä?zK7…,·‚100644 zip.ts Òˆ—jËÂšL˜Þ	B^ü·¹bÝ 2ë€°x äÿææ�Òenó‹�›ÿY�ðs8cëQ8‚–‡ÅÏ½l†ªx óÿîN¸M°î³¤Ê",X*à	�ºx� oÿ³"ü!°‚³Ô6
      pendingUploads.length >= config.batchMaxItems ||
      getPendingSize() >= config.batchMaxSizeBytes
    ) {
      void f“nÅÞ²/Uæ€ x† yÿü!­!�5‘` cleanupTempFil“³Gª³Ó*	K  // Reschedule timer if new items arrived during async processing
    if (“¿ 0 && “<s³û—-Nï€™Zx/ÐþìCõ@$import { once } from 'node:events';
�H$finished } from 'node:stream/promise±;°once(writer, 'drain'“bk%writer.end();
  await finished(writer³B“�/$for await (const chunk of reader) {
“ê  as Buffer);
  }³^6“’ “ë(for await (const chunk of reader) {
    “chunk as Buffer);
      }³»1nöeØà�?x ïÿõ@õ@�l‘�U‘l1±òƒK4]ç”xÇ 8ÿ£˜-100644 bootstrap.test.ts =ŸÁ”2	”¸µü6¦îÇ÷`ò`‘„“dåh-ü$¨M1Nñpn#¨ “+™¥{µÃ·°‹ðèjÊ~Œ×ô�JÀÌ“ØDïj� åJ×¡`çâ¥ÿ`;%“d§›€äî;`‰ž.h‰Ÿ’/ôèÔj“tnÜ²Ó’4"Æ4^(z`”‰“JÅûíƒ8‰õ8G8Y›6ŠŒî0V íjXå˜5x êÿ¿º°¥³});
});
Kù²á�´ x îÿÒû°ƒ³%ô});
?®àë�øbxK ´ÿ´nþk�VTeleUploader S3‘b¾teleuploader³(³d]teleuploader³É�“û#
³“¡"lK:ì�Š{xL ³ÿüò°;302 redirect to Telegram CDN“_W“302“Ü ['302“»Redirect³g¹}‘ã—Hx³ Lÿ××�=:‰ÊxxÎ™¢f¥ûŽ
ÄÚ
�100644 .gitignore ë=kÃ;Fª¬I“ŽyˆøÓR‘wùÅÆQ	,!�A<ä(‹÷Ô¨K“„YQRº
1mŽÎòü·eý(e6©ïQ740000 src uÜLo²¤#a �:Ü[žÒKÑX40000 test Ý°-ËÒ3Ç”ÂmcNÝ“.)—nGg—Ex øÿ¾—°—¤ªk‰óx ôÿÄ“°#“Tp9Šl–}x óÿ’³°�³`²rl†ïhx óÿÅµ°B³}ó!fØ«xë ÿ100644 bot.ts ¬®ß·:õˆ5zªe©Ø8³â40000 db L�ã�[áy”�%y 0ƒ#í100644 env.ts øz¬EpªÉðõ§ÊÙ}ÜwtÎ”100644 home.html ­j¸u(6	X6ïxkøÔÛ£²¸ª100644 index.ts �ÉÅ;[L*’½Ã„‡6µÈ°ëY40000 routes ·úägóh¶°ýE¸3¥:Ž40000 utils ºIMhï¦Ã—55n¥ eñ+Ìùß”`‡ä†ÁxT «ÿ²‰�1s-ext.ts Ò‹õ½>µ÷cÉÑemPR{ý2‘w4ËvQfzÂZÀ¢Ìx±Öü.vÏÇ‘¿_ú”Û:–bÑËïÒ%Ì
/¼Oê7\'»lˆµAx óÿº&Ê%°¢³(!é)áˆˆ5x îÿçÆ�a‘l¸“/“OA¨+æ†ÀgxF ¹ÿ ÿ�O0 bigint, boolean, pgTable, text, timestamp, uuid‘0±µ“ß„“E	b„¢[á–Hx îÿ¦¬°³³ ž	³ËÛ??Õ®xþ ÿ100644 files.ts _ûó�y“!Ò)¸®�oÛâïo¬œ100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 home.ts $ÝGL&J¼äšÞÖY' áÓRï100644 s3.ts ò„5	EáRÕŠcµÚçbü•‹
ˆ100644 swagger.ts ‘•Þ�Æ5OÃ%&
)÷È?T100644 upload.ts gM&ÖÒ‡ œ6ÁS˜Cy¶@†Ø100644 web-api.ts Ä°IÉè™ßCÑ?³ëeÀ™æ¨8eZí†Špxm ’ÿâ)ò&°àredirectUrl = “ù(4;

    return new Response(null, {
      status: 302’Location: redirectUrl³êø\%©é}xæþò&‘&°NFileInfo³QQ  as any;

  if (!fileInfo) {
   “�FileInfo(telegramFileId)“èÐR as { file_size: number; mime_type: string; file_path: string; bot_token: string }“¸4
, botToken“ä;botToken³4a	
        “•	* , archiveInfo.bot_token),
      ³À	U, fileInfo.bot_token³]Áè]é�'xù ÿ‘&Š%�5‘`ƒcleanupTempFile,‘âi)metricsCollector } from '../utils/metrics“?, type Telegram³N]TelegramFileInfo | null“®#metricsCollector.recordCacheMiss();“Ã¬ metricsCollector.recordCacheHit(““‡=“˜“Œª³8³unknown as ReadableStream³î#Óu[jè™WxH ·ÿº€�ð°L³¯Ú#³Ö)^“ÆkM“nB3³ÃD: bucketRecord.i“ÌtM“°Q;³_G³Lp³ck×B·TçnƒÓbxçùéL�Gimport { createReadStream,‘.unlink } from 'node:fs/promise‘).%db, files as fileSchema } from '../db‘K8#type { NewFile } from '../db/schema‘y,‘ë,“*›forwardToStorage } from '../utils/telegram';

type UploadedFile = NewFile & {
  createdAt: Date;
  updatedAt: Date;
};

interfa³NØ“A= {
  tempPath: string;
  fileHash: string;
  sizeBytes: number;
“[,;
};

const cleanupTempFile = async (tempPat“q“÷“ú
kawait unlink(tempPath);
  } catch (err) {
    logger.warn('Failed to cleanup temp file', { tempPath, error:“0	) });
  }“G/³Œ£³™VNconst closeFileStream = async (fileStream: ReturnType<typeof createReadStream>“ì if (fileStream.closed) return;

“ð	") => {
    fileStream.once('close', resolve);
    fileStream.destroy();
  });
};

const performUpload = async (
  prepared: PreparedUpload,
  fileName: string,
  mimeType: string,
  fileType: string,
): Promise<UploadedFile> => {
  const fileStream = createReadStream(prepared.tempPath);
  try {
    const result = await forwardToStorage(fileStream, fileName, fileType);

    retur“
publicId: nanoid(),
      telegramFileId: result.telegramFileId,
      telegramFileUniqueId: result.telegramFileUniqueId,
     V storageChatId: config.storageChatId,
      storageMessageId: result.storageMessageId,“½mimeType: mimeType ||“0,
“4+
fileType,
“mfileHash: prepared.fileHash,
      createdAt: new Date(),
      updatedAt: new Date(),
    };
  } finally {
    await closeFileStream(fileStream);“°“˜“éQ“ú“q5
   “�³5[“À%:“îf“Ë³¹•“0!“}"“ø³)í“0!“%performUpload(prepared,“A!<, fileType);
    await db.insert(fileSchema).values(uploaded“A%¹³[y““ð“Á ³dN“performUpload(prepared,“A!<, fileType);
    await db.insert(fileSchema).values(uploaded³A%(½ÃRåRŒwx%Úú�G´Dimport {‘z‘Å,“éJenqueuePreparedUpload, type PreparedUpload } from '../utils/uploadBatcher'³|âHconst getContentLength = (req: Request): number | null => {
  const valu“žlength');
  if (!value) return null;

  const parsed = Number.parseInt(value, 10);
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
};

const rejectOversizedRequest = (req: Request): Response | null => {
  const contentLength = getContentZLength(req);
  if (contentLength !== null && contentLength > config.maxRequestBodyBytes) {“ #@'Request body too large' }, { status: 413 });
  }

  return null³Ì, maxSizeBytes: number“^V³0Mcif (sizeBytes > maxSizeBytes) {
        throw new Error('File size exceeds upload limit');
      }
³v
]“9�    const oversizedResponse = rejectOversizedRequest(req);
    if (oversizedResponse) return oversizedResponse;

    if (conten³Ø±*if (file.size > config.maxRequestBodyBytes“.)0File size exceeds upload limit' }, { status: 413“ž“�&, config.maxRequestBody“¬7³çÒ>enqueuePreparedUpload({
      prepared,
      fileName: finalF“ß,
      fileType,
    });
“ “^“¦"ˆ³Ýy
      “V,> ||
      estimatedSizeBytes > config.maxRequestBodyBytes
    “.“:
³«_>enqueuePreparedUpload({
      prepared,
      fileName: finalF“ß,
      fileType,
    });
“ “^“¦"÷„Íº”à‰4xð ÿ´D’D�6‘a›leanupTempFil‘ö¡)metricsCollector } from '../utils/metrics³‹d³Íè$const startTime = performance.now();³²2metricsCollector.recordError();“ß¥N finally {
    metricsCollector.recordUploadTime(performance.now() - startTime³~¶yZ¤çƒ cx' Øÿ´WËO°O³²í“L“´.³yç³ã““¦³ ¥G«oâ²Mx" Ýÿë½�+‘YÝÅT]«Ú‰FÑävb‹º9ûòo“J¡J©ã€ã x3 Ìÿææ�a+OÏ€Ú_:kõháT<Ëõæ#‘u]enó‹�›ÿY�ðs8cëQ8‚–‡mìTê†é#x: Åÿù!Ÿ‘(ì³Xì“¥_“„	O“h³Ë	Mnew Response“'=.body!³d•˜¶æë­3x; Äÿ˜å�[‘ŽƒîL±,^âá®Hv$Ã%2ŸCÓl³%¢{¼¶-Þœ¿î¡di3´ÏŠ“FR
~tà
‰È.x  _ÿª6ø3°4&redirect to telegram file url with 302“î‹³òø“w302);“O/U(
        'https://api.telegram.org/file/bot123456:ABC-DEF/photos/file_0.jpg',
      ³°z
%ª3”è€0xØ 'ÿø3Ÿ1°DNInfo = mock(async (_telegramFileId: string) => ({
  file_size: 98765,
  mime_t“ÐDfile_path: 'photos/file_0.jpg',
  bot_token: '123456:ABC-DEF',
}));
“‡4FileInfo: mockGetFileInfo,
})³#Info“UV“ÚÕ³Õ	#þÓE{l‰àx óÿ™HÓF°[³!	ø#Yýï­]x? Àÿ××°û3”Ãa³�œí|œÒáfŽÄí8)¯mÓ40000 test Ý6�f§“ÊQÌ³[�o[ßS V“.)í$Ø«xë ÿ100644 bot.ts ¬®ß·:õˆ5zªe©Ø8³â40000 db õ>ó'Ôw}@éŒ`º¬¯¨|s7—100644 env.ts ”ãv �áüôìS²9z}�M˜˜k100644 home.html ]ìÃ×•q$sÎ€KÀ](100644 index.ts �ÉÅ;[L*’½Ã„‡6µÈ°ëY40000 routes c_è�€‡~¡Kc‡fº}µ!Ù>P40000 utils ºIMhï¦Ã—55n¥ eñ+Ìùß__™î¬x áÿ‰‰�:	¼é²ýÓ£�èò›»o&à:ë‘N»ÌoÊäˆâxÄ ;ÿº&¶$°x“�B as number,
    storageMessageId: row.storage_message_id as number“Í_row.size_bytes as number“D9row.uploader_id as number“–– “m
 as number““™Ûas number | null“¦ü“´³	2
�FæÃXx éÿ¦¦°³³ ž	³Ë(³ù­dê‚áÃGx! Þÿ±}ù|°*
if (!bytes“`*?bytes³£*Þb.ë¬Ex äÿþþ�êë~z8	˜¢"F8óœ;Láµ· ³ +èŠ7x çÿËOÃO°,o.sizeBytes³?Œy	$àˆix  ßÿåå°*¾ˆ’Úh‡sÍªb tì¹Ô³:á±“>'°Øâ‰á<x2 Íÿ‹ ñ�‘*Ë“ò³_.[], prefixes: []³°.³'});
Ç^Åà…^x  ßÿ××°ûÉÝÌ,êÍÚ}u„ú@ „J¹Í“H Âî…?x áÿëë�·ÁÙ€¢ÄÝ¼ó8å¼XD¨ü`6�‘Ë û38î�x áÿþþ�y"úsd¸O…Uu€›¨¦³KZ·Z•‘�qÞhoà'Â0xp�ýº€Ýô°L³¯Ú#³Ö)^“ÆkM“nB3³ÃD: bucketRecord.i“ÌtM“°Q;³_G³Lp³ckÍparts match stored parts“1q and correct ETags
  const sortedParts = [...parts].sort((a, b) => a.partNumber - b.partNumber);
  for (let i = 0; i < sortedPa>rts.length; i++) {
    if (sortedParts[i].partNumber !== i + 1“='2“q“q0+ Parts must be ordered by part number.',
  “Á/!  400“œ    }
  const storedMap = new Map(“ïu[p.partNumber, p]))“Š3*Ustored = storedMap.get(part.partNumber);
    if (!stored || stored.etag !== part.etag“='2“Þq“éq2S The part might not have been uploaded, or the specified ETag might not match.',
  “Á/!  400“œ ³‚q¸€ÿÔÀà‹fx  ßÿ××°ûø¤¡BÍg¶ ’:f£š“¢sO¬J¼“Hkî…x áÿëë�+tÓEÎ£N´üwÔS	â8 À‘‘?¬Ô¿Sî·x áÿ‰‰�:^¹Óã¥¬;ù…Ê„L‘ÞìBÊ‘N»Ð}ë‰|x; Äÿ¶$ø#°»³W)>= ${prefix} AND s3_key < ${`${prefix}ï¿¿³y	½kÚé�9x9 Æÿ××°påZÞ±¡&¸Å‚´]¾žâ‚C(§“„wø¤¡BÍg¶ ’:f£š“¢sO¬J¼“H–¤áÒjx îÿ’Ù°�³`³ÍE:FÁéŽ!x9 Æÿ××°p¯ŠÔW¸ÚNO|Ýi.+á¾ç\ŒùP“„w.ï&=Ú5å²SDÅ0ÐYãÊWa“H°íÓRx- Òÿ’Š°�³`³Íý“è&“œ“›
“è&upload³y�\î‚x áÿëë�Mjä¸iirÁ»ê~VVa¯»2‘aŠÍ…FêÏcx åÿ¦“°³³ ž	³Ë(“ù³'…Ý	$á�{xQ ®ÿ××�=ÈðG@Ñ´wcìÅÿtH¬u^P±Qdí…°x¶³h¼6Áêå�›�FÈt“„w2n´¯aE.ÄALªÝ…:f«ÞÝw“H!�%ÌgÖ;x øÿ¾Ñ°Q6�l�ox óÿÙù°‰³éð'ßË«xë ÿ100644 bot.ts ¬®ß·:õˆ5zªe©Ø8³â40000 db sßbî«¸C¶b|ÝÎ’1ï!100644 env.ts ?àÔœ¡yó4‹ó•iÒ“ê4100644 home.html ]ìÃ×•q$sÎ€KÀ](100644 index.ts ]˜³“¸‡+{%¯À
gÙF8Oãc40000 routes ËŸÛ3„|èKžþ°º`_:ëÕ­c40000 utils æµrå6wDuà–�ì	 Êî'Êõ€^-æ½Ax6 Éÿ‰‰�:‡a"ÐàI¢ŽwÇ7Ž^&n3®oÄ‘N‚«X«%8�Ã“Ë#­©z¢£X¨S‘ä%e|¸ï�Tx àÿ¶$ú"°»“ì“ýh
prefix}%`}³Õ	aÛîg†ÿlx øÿ«$â°b6èîÔx áÿ¦¶°³“ ^³šs³Ïï“Ëå³H^½ããÓ*x3Ìþó$´$°Ü“b5“Zo“`³É‘³o“X 
      if (“¥?“®
(“í
>“9
YisS3Request(“¦
0“í
“7.      },
      HEAD“7
[isS3Request(“¦
0“í
“7.      },
      DELETE“7
[isS3Request(“¦
0“í
“7.      },
      POS“6
\isS3Request(“¦
0“í
“7.      }“½¿“U
5,if (isS3Request(headers) || new URL(req.url)“À$“˜$³p×§[²î‘^x áÿþþ�y²†@u7²ÏE¤Lõ#ç“QÍÓÞÏ�‘�qìDî˜ÒpxŽ	qöº€›ß°½“Ýo“¯Í“b5³ÕB³2³ˆ™req: Request“áR"“
p“ˆ‰H// Presigned URLs: only supported for GET (verified in handleGetObject)
“�“z) {
    if (method !== 'GET') {“�(]AccessDenied',
        'Presigned URLs only supported for GET',
        pathname,
        403“œ !  } else {
    const authResult =“~2method,
      req.url,
      headers,
      null,
“Ø “úREGION,
    );
  “(!“•"“ .:  'Authentication required',
        pathname,
        403“œ ³	–³­“B  }³æ?
     “%?.url³_¹1new Response(xml, {
    status: 200,
    headers:“€z$, 'x-amz-request-id': reqId }“q;³lL“B)0, headers: { 'x-amz-request-id': reqId }“±v³æ9“B)0, headers: { 'x-amz-request-id': reqId }“±v³M!P“B+{ 'x-amz-request-id': reqId }’W)“ß#e“À;GuestUrl“º@7“Ÿ“z) {
    const presignedResult =“³"5url: requestUrl,
      method: 'GET',
      headers,
“-“7'“`'});
    if (!presignedResult.isValid) {“�'	presigned“¤* “2$,
  “Á/!  403“œ   }
³±$Ø³Ö)Þ“B\
      status: 302,
      headers: { Location: redirectUrl, 'x-amz-request-id': reqId },
   ³è,“B&
      status: 302,
      headers: { L“ 5", 'x-amz-request-id': reqId },
   ³?5Ò“B#
    status: 200,
    headers: {
  “0:'“U:0“ƒ:0“±: “ÿ50“ù:/"  'x-amz-request-id': reqId,
    }³q;ã“B  }“o=„³ü=““BX
      status: 200,
      headers: { etag: `"${hash}"`, 'x-amz-request-id': reqId },
   ³¿?u“ÆkM“nB3³ÃD: bucketRecord.i“ÌtM“°Q;“_G+“BO
    status: 200,
    headers: { etag: `"${hash}"`, 'x-amz-request-id': reqId }“q;“ÏGAcopySource: string“HHM³ÍH©“ŽU“ãD ³ÌOx1new Response(xml, {
    status: 200,
    headers:“€z$, 'x-amz-request-id': reqId }“q;³˜R�“B+{ 'x-amz-request-id': reqId }“±v“˜R³STG“¡UÓ“–V+1new Response(xml, {
    status: 200,
    headers:“€z$ , 'x-amz-request-id': reqId },
 ²W¥³çY“Xz1new Response(xml, {
    status: 200,
    headers:“€z$, 'x-amz-request-id': reqId }“q;³]]¦“çY�³Ò`Í“Xz1new Response(xml, {
    status: 200,
    headers:“€z$ , 'x-amz-request-id': reqId },
 ’W"³d@1new Response(xml, {
    status: 200,
    headers:“€z$, 'x-amz-request-id': reqId }“q;³³f³˜hW³ckP“BO
    status: 200,
    headers: { etag: `"${etag}"`, 'x-amz-request-id': reqId }“q;³øm,“1³šqØ1new Response(xml, {
    status: 200,
    headers:“€z$, 'x-amz-request-id': reqId }“q;³¹zÛ“B+{ 'x-amz-request-id': reqId }“±v³Æ|-1new Response(xml, {
    status: 200,
    headers:“€z$(, 'x-amz-request-id': reqId },
  });
};
à1!âãLx" Ýÿë½�+‘YÝ˜Yµš©nG7«G;±:Ã„&^;“J¡üžÕ¥	x• jÿ100644 auth.ts ƒ2ÈJTL@tÎæÆÏƒ;ù�^YÃ100644 object-stream.ts R¯fy™ÃÐ6}ü’ˆR	HuO¿:¯100644 range.ts :'JùŸŽÓ1Ò—f™nO100644 xml.ts Ò-ùe7rÇÇ]B%M{z8`ñ€3Nj‚8jl”‡x óÿèNåM°(³«½(ø—à°yx ïÿŸÖ±,µ“ð!£9~Ëí$‡�;xM²ýîN¹?±(“¤“ì{“~ I³±=³¤†“~ I“šwscapeXml(prefix“.scapeXml(marker || ''“j<%scapeXml(delimiter || '')}</Delimiter“12“^7“�	o.key³±
scapeXml(p“ß	X“ÚMarker³X
¡“ü!“� 8“š“yiscapeXml(prefix“ÿnscapeXml(delimiter“�“ú+scapeXml(continuationToken“M…“�	o.key³±
scapeXml(p“ß	?“[/"scapeXml(nextContinuationToken)}</“q³Ð±“~ I³Ë³ŠÚ“„!�“A"i“¿"4A(requestId ? { 'x-amz-request-id': requestId } : {}),
    },
  })“¡“)#š$: string[] = [];
  const keyRegex = “ß#^;
  let match;
  while ((match = keyRegex.exec(body)) !== null) {
    keys.push(match[1]);
  }³$af‚ËRíƒ]xÍ 2ÿ¹?³?°¤_“±	M““W4³7y_“±	Y³	¨_³±	º_“±	M³¸t	+  extraHeaders: Record<string, string> = {}“*Ù  ...extraHeaders“ýÃJ = Array.from(body.matchAll(/<Key>([^<]+)<\/Key>/g), (match) => match[1]);³Xa1ˆD—ï¢x? Àÿ××°û3Ûéê—êE>gŸ+	·Òz6S[40000 test f/€žc•ÖPèÜ+�mê¥ÂØÂ”k“.)w­ùî¡Ox áÿëë�rÖXÍ)îÙƒº>w’±öÇì‘†eß™å¯x êÿù|ù|°;³*“AÄ³)P-f°Õá®=x! Þÿåå°MíEæò!»Œ9ø‚–èy–oÉÃ³aeQíänxÝ "ÿþkÿi°P,“3“™13, UploadId: uploadId, PartNumber: 1, Body: 'hello '“ç,P“3“™1:, UploadId: uploadId, PartNumber: 2, Body: 'sdk multipart'³Õ-¸“°3“å)“™1“á/ƒ“ 	
“ó
“Ž+), UploadId: uploadId }))“21(“Þ
“°3
“°	“y16³¸1F©-MŒà‚;x  ßÿ××°é’PD…š:¿Æ!°ÕÊJÊ(Cª“.)Ý	4á±x! Þÿåå°M0‡2U5ˆÒ_¹´ˆm7{ÀëóÀ³aäÉíbç9x-Òùþk�b°:describe, expect, it, afterAll“X!S3Client,
  ListBucketsCommand,
 “á“à
HeadBucket“ù"““J“w“×ListObjects“­ “/“ä “$“”$Abort“ž“€
“ç³7É /* best-effort */“
`“0,“€
*,
      “ª
} Key: u.Key!,“LF /* best-effort */ }
    try {“¡31"b })); } catch { /* best-effort */³!”redirect to Telegram (302)“°// TeleUploader returns 302 -> Telegram CDN rather than the raw object body.
    // The AWS SDK follows the redirect but the target (api.telegram.org) returns
    // non-XML, so the deserializer throws UnknownError.
    // We accept either a 200 with bod+y OR an error with 302 status.
    try {
  “ì““ÿ-“š/)“¾“Þ/“ƒ2“³3“l,  } catch (e: any) {
      // 302 redirect is expected â€” that's how TeleUploader works
      expect(e.$metadata?.httpStatusCode).toBe(302);
    }³Ës“0,“`“É%“&!“‹ ³™&¬“0,“`“É%“&“x)’³ã1O“
*(via manual signer â€” AWS SDK adds extra signed headers
  //    that may differ from what the server receives through Cloudflare,
  //    causing a 403 SignatureDoesNotMatch. Multipart is verified in
  //    test/production-e2e.test.ts using a manual SigV4 signer.) “15“È*â€” verified in production-e2e.test.ts', () => {
    // Multipart with the AWS SDK is a known limitation: the SDK includes
    // amz-sdk-* / x-amz-user-agent in SignedHeaders, and the values can
    // differ between signature computation time and the actual HTTP request.
    // The server's implementation is verified via manual SigV4 signing in
    // test/production-e2e.test.ts (S3 API > Multipart upload).³4à‘V%ã,‹<xÃ<ý�bàa°:afterAll, describe, expect, it“X“â“G“™“Å“\/“ð“´“°““‡“““<““   PutObjectCommand,
  S3Client³5Ë
      /* best-effort */
   “
`“°+““š
“±
}%
            Key: u.Key!,
           “;F&
      /* best-effort */
    }
    try“.'“´+“Ð*
    } catch {
      /* best-effort */
   ³ò”%stored content (proxied from Telegram“Ÿ““ê“23“@“í$“f2“š1“ö“M“ô“ã Iexpect(ETag).toBeTruthy();
  });

  it('GetObject supports Range requests“ ““êRange,“$,“7, Range: 'bytes=0-4“¿Range).toMatch(/^bytes 0-4\//“ïKLength).toBe(5);
    expect(await Body!.transformToString()).toBe('Hello');³Šs“°+“	“ß	“ï³M&Ä“°+“	“ß	);“ñ&F“õ“N&³¹)Wo7Ù0à‘mx  ßÿ××°û7N…!gþïÕ…ÅæîÌgˆÄ§“Hõw(æ•	x6 Éÿëë�+C-Å�	Š) Äfw¼ÚÉ�÷fd¼‘?x§ëõž.]=n9Bù›PÊíë4Aà;‘Ë ÂC¹æôfx6 Éÿ‰‰�:‡a"ÐàI¢ŽwÇ7Ž^&n3®oÄ‘N‚‹Þ³Œ˜ñ¶€uÂ`¢	"‘ä%`Ï“â‡¶cx" Ýÿ«$ä°õr.size_bytes as number“	Yõ.®xþ ÿ100644 files.ts _ûó�y“!Ò)¸®�oÛâïo¬œ100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 home.ts $ÝGL&J¼äšÞÖY' áÓRï100644 s3.ts _¶ÔqçR´Þ�ðSÖìS¬Àp5#100644 swagger.ts ‘•Þ�Æ5OÃ%&
)÷È?T100644 upload.ts gM&ÖÒ‡ œ6ÁS˜Cy¶@†Ø100644 web-api.ts ë~z8	˜¢"F8óœ;Láµ· �dâµx íÿ›ß“ß°'-“Oe³@-[BK,ï–x? Àÿ××°û3±¸ÄFn§~ÿO®²ïŒ04èWÊæ40000 test ¶uÌÎZõ.e6ûuÖ–?Í“.)¾îƒ6x áÿëë�·„§»àê@j”0„ÛD<ß‡Ïñœ‘Ë çzî‚'x áÿþþ�y¿¦¿˜'æ½‹k�w¥N›]Aòo‘�qëÉPí¶Kx]¢þ›ß•Ü°%“
³-%d“Œb<³ð+“è.!“f“h%l“0h“ï'?;tgResponse = await fetch(redirectUrl);
  if (!tgResponse.ok“2Gstatus: tgResponse.status,“H2-});“Ñ$“°*ƒ“û^	“e“-“Oe“JtgResponse.body“!H#“µ5EtotalSize || “ú52''“66B

         “x6

         “Ÿ1“¤65R  'accept-ranges': 'bytes',
      'cache-control': 'public, max-age=31536000',
   “�H³T3G<¿~pÖá™ix! Þÿåå°:I¯Î*U¡’`MåÁö(ÍíÓH§³N÷Í…ã‚‡x ìÿ›”Æ…°s=³„D³âE9RŠïškx? Àÿ××°û3 ÍðP‹³ß¤/^.Ÿa^ú40000 test åL³l¨°d`Ã€1¤°úâÚ“.)T�fëˆx; Äÿëë�·4ÌÍ¿AìÑ MŸbB¥3ë¸}¥Ì+40000 utils :ÓÆÛ¿–•�½@¯Uì=ÐÊZ™P[î‡+x áÿþþ�y0tá:òà¬:©%™ô¯Øù”N‘�qáã	„x“ lÿ•Ü…Ù°ø³£("P// Proxy the content from Telegram CDN so real S3 clients get the body directly.“m-!“c'Â“.Y“*“™.!f“M( “à.­“ô3“å/w“t42³›03³:+ÛB”Æ6Mâ€Žx" Ýÿë½�+‘YÝñ—ßY	Ôû8£ÂÁKò)ÑR“J¡kÿë©Tx äÿ••��(t1zßyç{³x%ç:fk³©üöšöçà£Ex ïÿ³?ï>°0“]×³Kh> €êžx: Åÿåå°:Òw@Àjª ß0rä&úq’WM)¿‰“NÿsÇS+Ó–}nÕ^yÓ�MõÔ©nB³a0
ä‚‹hxt‹þ›”Ñ‚°0:// Presigned URL: 200 (proxied body), 302 (redirect), or 403 (auth fail)
    if (r.status === 403) {
      console.warn('âš ï¸�  Presigned URL returned 403 â€” verification mismatch');
    }
    expect([200, 302, 403]).toContain(r.status);
    if (r.status === 200) {
  “L:%“o:1#  } else if (r.status === 302) {
  “@&location')).toBeTruthy();
    }³vD(³âE9¸-qìl”x óÿàaï^°³ŒT(é6ï¢4x? Àÿ××°û3ª•3sòI¸„jø|Á%$ãö™—X—40000 test ¬Ù06½Ï$¡ÕÝWâ*ÍŽ¶¥­¸#“.)Ó$!ë†Ix äÿëë�×`JÅ…A£¥ÞŸë
dŸb4v4³Æâ€“yx" Ýÿë½�+‘YÝkø5 ÞÁ«hV2ó®÷¤qÀb“J¡M›h„Zx ÷ÿ•i�*‘V?Càl„'x óÿå±°±³å€$žï¤x? Àÿ××°û3ÆæÝP‰µýQÝ²è­QS3Úh«40000 test ï®`&ÎÄTÙ�ß•1L[™�ê“.)äuRë‘@x; Äÿëë�·4ë6^ÅJ�q´,g&ÅdO´Ì@™40000 utils ÞjÎüéS)�¹)Œ¤Æï¬vþ/»î�Nx áÿþþ�y®¤[Ô*/0õÎx¯YrÖX£�‘�qÖÐìáˆ#xÁ >ÿ…ÙšØ°Ü“â3““Ý“³[““/i+“R 5IfullUrl = `http://localhost/${bucket}/${key}?${searchParams.toString()}`;“| 6
      fullUrl,
      'GET'“ 
T“d!A“3	Request has expired“/“�`“,³N"7Jg?Ùâ€—px" Ýÿë½�+‘YÝCÚø€ý|Ž,rl Q©Fd”“J¡ §©xi –ÿ100644 auth.ts V¨S,\¬,ÎL¯öô©Sº€+å100644 range.ts :'JùŸŽÓ1Ò—f™nO100644 xml.ts (t1zßyç{³x%ç:fk³©üöÄ*('àv”º{x`ŸøèNªG�º³z®“ÖsearchParams: URLSearchParams“=rams: string[] = [];
  const keys = Array.from(searchParams.keys()).sort();
  for (const key of keys) {
    const values = searTchParams.getAll(key).sort();
    for (const value of values) {
      params.push(`${“Qkey)}=${“Q}`);
    }
  }
  return params³ì3['authorization'];
  if (!authHeader || !authHeader³³«“§)
  url: string,““}c“i"BqueryParams = Object.fromEntries(parsedUrl.searchParams.entries())“°TqueryParams['X-Amz-Algorithm'];
  const credential = queryParams['X-Amz-Credential']’ = queryParams[“P];
  const signature = queryParams['X-Amz-Signature'];
  const expires = parseInt(queryParams['X-Amz-Expires'] || '0', 10);
  cConst amzDate = queryParams['X-Amz-Date'];

  if (
    !algorithm ||“E“n“‡j// Check expiration
  const dateObj = new Date(
    parseInt(amzDate.substring(0, 4), 10),
    parseInt(amzDate.substring(4, 6), 10) - 1,
    parseInt(amzDate.substring(6, 8), 10),
    parseInt(amzDate.substring(9, 11), 10),
    parseInt(amzDate.substring(11, 13), 10),
    parseInt(amzDate.substring(13, 15), 10),
  );
  const expiresMs = expires * 1000;
  if (Date.now() > dateObj.getTime() + expiresMs“ÎW“p +9const presignedAccessKey = credParts[0];
  if (presignedA“1u=  const dateStamp = credParts[1] || amzDate.substring(0, 8);
“X:
  const sortedParams = new URLSearchParams();
  const paramKeys = Object.keys(queryParams).sort();
  for (const key of paramKefys) {
    if (key !== 'X-Amz-Signature') {
      sortedParams.append(key, queryParams[key]);
    }
  }“‘:bsortedParams);

  // Build canonical headers for presigned URL â€” only 'host' is typically signed““W"hA) => `${h.toLowerCase()}:${h === 'host' ? parsedUrl.host : ''}\n`“‹#"hashedPayload = 'UNSIGNED-PAYLOAD'“ì“¸#“ýb“m$D“Ý	“¹$*s3/aws4_request“ü$i“ñ­“ý%f““ª
“w&“€ }“¡“Í&T['authorization']“/'9õtmiâ�pxB ½ÿªG–G°è".authorization;
  if (!authHeader?³E.authorization“q#9.ó±á˜?x! Þÿå±°’¯	h¢œ„³;T²ICoÙ5.b³Ú‹Û$ºáŠÖ|x! Þÿ×-Ó°„	
     “�	&'GET'“Öš});
¨ý	Sï¸mx? Àÿ××°û3ÉZ6Û“ÑÌvµÄ%dÖÑB¢î=40000 test xëÿšŒ¢?tƒºŒð·ßÒEæ¬“.)Ùb`ë“_x äÿëë�×_{Ô|ºòF»?˜�Eé”ÌTâ(­ñÉ}â€ª2x" Ýÿë½�+‘YÝ> ãœ¾+þ.sd¥™®ÖCâ“J¡I=¥xE ºÿ100644 auth.ts V¨S,\¬,ÎL¯öô©Sº€+å100644 xml.ts (t1zßyç{³x%ç:fk³©üötZGl�]x óÿ±…°á³$±¼à�x  ßÿ××°�Áÿ‰ß£Êt?pø&8´,cÐ¬“£´òAø¦x& Ùÿ40000 superpowers n1O ÞóÇÔ~ÊgÅú<?ƒ¾h1 x@ ¿ÿ40000 plans @˜ÿ=4ä]”ÿ©óKF¢‚n'£140000 specs ß}A>ºü˜"Ó™æ¬�j‰„jð€%…gˆ·x øÿ†ƒ°qÃà‚[x  ßÿ××°�0üŽf
°±V9/tbÈÓ%6^<{“£´Ý£?¦x& Ùÿ40000 superpowers OùvÃÛi`cÈ‰ŒXuÂ)¿w²ë˜Ú x@ ¿ÿ40000 plans @˜ÿ=4ä]”ÿ©óKF¢‚n'£140000 specs ¦¯;“Xçž¨Ø.¯ ¿ÚšzA�³%Ígˆ™?x øÿ¦•°	Ïï€Ex? Àÿ××°û3”,=½þvgc�CþîÔ)Â³Åoá40000 test ŽÆÐñ3‹hn‡ÏYº@ÌÀ­¦]“.)Ã£s«xë ÿ100644 bot.ts ¬®ß·:õˆ5zªe©Ø8³â40000 db _í~ˆŠ]_ñêÊÞJÛ…¸ã:100644 env.ts ?àÔœ¡yó4‹ó•iÒ“ê4100644 home.html z¡¥úÓã2EžAøù˜¥^100644 index.ts míe(3qóÂM3ßì½kûÀí­40000 routes XË€rÌg'‰¥ÍÀó†°'Ž40000 utils -YQ*PÕnFl�óápÉˆ‰x8ÄW_³©x	öþ100644 buckets.ts TÅÐ}f9Ð+’PªÖ²ÉˆDF100644 files-ext.ts Ÿ©A#*O„Ÿ8_˜ÌL†ô²½é100644 files.ts jr#ä}ÏÃ'¾ÝèŽäI{100644 index.ts ËvQfzÂZÀ¢Ìx±Öü.vÏÇ100644 migrate.ts Òe<…'Üh-—O]æjZ„º�AŒ100644 multipart.ts ¥8Êƒv\ fJ†ƒFýY69•100644 schema.ts ú”Û:–bÑËïÒ%Ì
/¼Oê'fäæŠ…x6 Éÿ¥û°©“È“Î’)“h“È“Î
“³M)“h“È“IK)³œ‰)xäøx4 Ëÿ¶$ú"import { eq, and±«“ì“ýeprefix + '%'}³Õ	ah�è‰­x çÿÉ¹°“*“CB“‰G³×raûµà¯tx ïÿää�,‘M‘#!±b8{žå÷xµ Jÿù|Úz°;³*“AÄ³)Ý“k	onclick="“–4	onclick="“1>“üµ³¸É'“./' + “³_³c“~ w
part + '/'“ÿ <³B!î“7&vdisplayName + '/'³¾&-p+'%';pp.textContent=p+'%'³	2B“›(	“/8“x=“N>+å\;`îèx. Ñÿ´$´$�ã“>y‘ã,“·-“	/“¶&“ÛÒ“5“l“ 	³†®±*Ãã±bxS ¬ÿþþ�X4Ã_:`æ­·‘õzâ¾RÑBm\ë,100644 s3.ts j ëîs“Jpn™ë�bi_B‘Œ^ÀmÇ•Ñ{Š¡±.a-¼Z4ÞY$÷$–ë†ûx+ Ôÿ„„�Y!import.meta.dir + '/../home.html'‘zŠ92ˆë¡xôþšØšØimport { verifySignature,“¡ “Ô'!listBucketsXml,
  s3ErrorResponse“]%V2“6/PartsXml,
 “^e*,
  deleteResultXml,
  copyObjectResultXml“Í“³“ü#‘V‘r, deleteBucket‘�%“c/“Ga“á“ŽP{‘ÆD‘±“
!“ÞM“:import {“;., cleanupTempFil“h!‘3)‘$“‰³YÁgvø\´ïûqx_  ÿÃOÃO‘M‘r, deleteBucket‘�$‘ÉA‘±“
$‘
+“-!“Ô:import {“^., cleanupTempFil“‹!‘3!“¬³µ%lë!â€»,x" Ýÿë½�+‘YÝ›§Û`ÉÒïhQæ9ÝË5nXµ“J¡…>¥xE ºÿ100644 auth.ts W¾ûƒmœy!ÎÞs’§1±é=/100644 xml.ts 7Ã Þ®&Ð;ÙÊ;?kQ²íq“ï’Wx_  ÿ±…°:õº8›vdÈÝ”¤L
gfá	“ND™‰
«¶…J ïeô‘ÄEËe&C»“¦; sdk.test.ts p°}$ðâî¡ÎÜè\öV4QPfd³-kû(4ã¬;x£\þÑ‚¸�°™describe, expect, it, afterAll³·“Ú,³L'AWS4' + secret“f$“‹‚“Ú,“C¦           //“±5³ body?: Uint8Array;“- “Q!³u(method, url.host, url.pathname,“Í payloadHash, opts.headers³I“_I+ }
    catch { /* best-effort */ }
  }
});
“Á!ò³ÏÎawait r.json(³¬ü“Š,“à“Q@!³¸await r.json(³Üi“±fd“Q@“Žawait r.json(³±,await r.json(³ìò“p+
“»,“þ-“Q@“ð'
³?.(³m05“©7E³õ7N³M?Ju‹Ká–Gx1 ÎÿÓÓ(import { describe, expect, it, beforeAll±(+
ÇîrêÏdxŠ uÿ�bÚ^°‹““â³ f%stored content (proxied from Telegram“Ÿ““ê“23“@“í$“f2“š1“ö“M“ô“ã expect(ETag).toBeTruthy();³Š†fy0Õï”x? Àÿ××°û3NÁ>ÔÆ�¾?Dg%þPÕ)`Ò¹}40000 test Ù ŠÉâM³‘M›fuªx]+ÀAk“.)•†�æ’Ox6 Éÿëë�MgéÔú½"µY ØÔŠùD´.oƒ‘aV:‡¶§.C?$+È„ƒy(tcÐˆ‘Ë ±	î€ÊFx áÿ¦ë°³“ G³šs³Ïï“Ë±³H^À�ìî‹cx áÿþþ�y„)’4ýÁü�±[«„2¿À0•*‘�qã^åŠsxe šÿšØàÈ°µ$“–k“?“.^“½+	headers: {
     “ž%“23?³?)6“–k“?“.^“½+	headers: {
     “ž%“23?³Ó/G<íÆ éê‡[x: Åÿ……°:¼l>l—áXÃAu—ð…`“NŸü�2�OQ!B‘;;TjeÔ5$zì³h¾eî	‡6xž aÿ¸�ô°/(redirects to Telegram“ñ%“ž<“�=“.,“2“ (“k:	“y:-“;“)“B;“¯“Ï+³f)K) should return 302 (redirect to Telegram)“Ö9–“q:5“;“)“B;“¯³o;I`Y6M§%xW¨ý100644 .dockerignore Ám•mò=BPÿ<\J5Óª1ý>100644 .env.example ÈðG@Ñ´wcìÅÿtH¬u^P100644 .gitignore ë=kÃ;Fª¬I“ŽyˆøÓR100644 CLAUDE.md €ü,ÿ{Ï¯…˜q�MC<¾G¿ý—100644 Dockerfile H\û’ÆŒ3²¬™‡Å@èºI
100644 README.md gñ%Þ¯Ï'Ðpé�j[ûC‚Äô100644 biome.json ê›äŽÓx4‚c2á»mTÁM½100644 bun.lock —Ö?XŒ‘¿ëvë»EÞ8ŠÕ¯€á100755 deploy.sh ƒ^à¹G
~ÿ÷Sz¥Û>46d
100644 docker-compose.yml dí…°x¶³h¼6Áêå�›�FÈt40000 docs 0üŽf
°±V9/tbÈÓ%6^<{100644 package.json Ã*hW¤)œ­AÜ�Uhm$T¬100644 schema.sql Rº
1mŽÎòü·eý(e6©ïQ740000 src NÁ>ÔÆ�¾?Dg%þPÕ)`Ò¹}40000 test -ú’ðÊyùñRË©6$6ítü100644 tsconfig.json  æ­\Ý/¡¼”¹’¸Ž�q§íjåˆ†>x% Úÿ¡ûò³°¤“Ï«³$&³KM“ËP³ðOÿ2³yƒ(:;©k€Õ[x ôÿøÑ°Ø“ÿy"©k†)x ôÿ…Û°ß“	üO–ï„Lxo �ÿ××�®—qÄÚóHÞšRî5Jo0ñò‘Â®™Yà3F¦²Ôp°^Þâ³EVtn“„w3¸á=J¾1lC#kÏÄ¿œƒøLp40000 test "Û…ñÕ¤¶¥…ÕH0B½®hï“.)T/2nç
‹¬<x§ Xÿ·®�b,including devDependencies for build and lint‘„"biome‘ "^rc and test directories
COPY src ./src
COPY test ./test

# Run lint and build
RUN bun run lint³“LkÃh9læ€7xv ‰ÿ®ü�b"devDependencies included for build‘Ž"‘»Aource
COPY src ./src

# Build (lint is run locally before deploy)³5yí)Úá€‡x îÿÙÝ°ø³”õ³éðNQZ«xë ÿ100644 bot.ts ¬®ß·:õˆ5zªe©Ø8³â40000 db üVá.\)²ZûÀ³[©W #100644 env.ts géÔú½"µY ØÔŠùD´.oƒ100644 home.html z¡¥úÓã2EžAøù˜¥^100644 index.ts yš¨7èœâÍ®�è¯Ú|5°WÑ40000 routes ín¨iŒïÕbH÷2Á�Ìh~·i40000 utils frhTE=Ùf�{f4ïô ÜŒ	3€`jã€xxS ¬ÿëë�+’ BîòX®åi|‹A
´æ©C‘?x4:‡¶§.C?$+È„ƒy(tcÐˆ40000 utils -YQ*PÕnFl�óápÉˆ‰x8t] ú©x	öþ100644 buckets.ts *ÕW@¡Ž­oîá»<f¬Ë%·ë›100644 files-ext.ts Ãžò»nj]ÄÐXAå�Í¨XÄ100644 files.ts jr#ä}ÏÃ'¾ÝèŽäI{100644 index.ts ËvQfzÂZÀ¢Ìx±Öü.vÏÇ100644 migrate.ts $ð×pûãy‡÷½æµ‰`Ÿ÷100644 multipart.ts “ŠÏð±_��´i\í"QÆi¦|(100644 schema.ts ú”Û:–bÑËïÒ%Ì
/¼OêÛn4æ�x6 Éÿ‰‰�:æhg%•ÈÅé	t<‰ÑA;±¿‘NZÒe<…'Üh-—O]æjZ„º�AŒ‘¼MmnçŠ§.x· Hÿ¥ù�£Pinterface QueryResult {
  rows: Record<string, unknown>[];
  rowCount: number;
}±ê.rows[0]³ð«rows“š0.rows[0]“ðÁ“�Ðrows³^“�	*“Ú[rowCount“;	Ûrows.length > 0;
};
fB\â€›*xÒ -ÿ¶$º#import { eq, and±«“ì“ýeprefix + '%'}“Õ	Î{
    rows:“û}“¼
�.rows³K@rows³ŠO{ rows:“û };
  return result.rows“<“keys: string[],
³_�{ rows:“û };
  return Number(result.rows³-	®¢Fÿá€bx! Þÿº#³#°­“©, keys: string[]³ÔæÔ”¸ìˆŒ&x¼ Cÿ«$¨�,‘M‘#!±bˆinterface QueryResult {
  rows:“Ÿ
owCount: number³å“QueryResult;
  if (result.rows“Ÿ..rows[0]³ÑÏ!QueryResult;
  return result.rows³Ê
+r.size_bytes as number“	YCõî£8x áÿ´$ž°á“ðHome³É“P
³³é	Îo•$x ðÿ…©°#“UŠ“	ü' ©'xy†ý100644 .dockerignore Ám•mò=BPÿ<\J5Óª1ý>100644 .env.example R]º’ËÉK�Å¦èÏP}ñ¬Ù40000 .github ´°Á|÷Ÿ°(‘f ìSïîÜ*Ù100644 .gitignore ë=kÃ;Fª¬I“ŽyˆøÓR100644 CLAUDE.md €ü,ÿ{Ï¯…˜q�MC<¾G¿ý—100644 Dockerfile —qÄÚóHÞšRî5Jo0ñò100644 README.md gñ%Þ¯Ï'Ðpé�j[ûC‚Äô100644 biome.json ê›äŽÓx4‚c2á»mTÁM½100644 bun.lock —Ö?XŒ‘¿ëvë»EÞ8ŠÕ¯€á100755 deploy.sh ƒ^à¹G
~ÿ÷Sz¥Û>46d
100644 docker-compose.yml ™Yà3F¦²Ôp°^Þâ³EVtn40000 docs ž+{È¢Ã˜däŒl^à„ÞâÉ100644 package.json «²„EÁø¢%>xä
¦XXÉG›100644 schema.sql "”ëW›ÿìÛë�,8ñÙQ¼“40000 src 1G¬ò[þý4n‹±u6or¬40000 test 
—%áÕÕínènöKM*’100644 tsconfig.json  æ­\Ý/¡¼”¹’¸Ž�ª×þsä„xÄ ;ÿù×�=ÈðG@Ñ´wcìÅÿtH¬u^P±s>0üŽf
°±V9/tbÈÓ%6^<{100644 package.json ÔøÀµA6"˜—o¢¬–t-�¡†z100644 schema.sql Rº
1mŽÎòü·eý(e6©ïQ740000 src Ýý»¾dS.Ù(”G:Ðô¯o
 ´40000 test î²¢;nb°`fhKåD@Á¡å½x“P)‚…QOï€Sx? Àÿ××°û3¸á=J¾1lC#kÏÄ¿œƒøLp40000 test "Û…ñÕ¤¶¥…ÕH0B½®hï“.)c¥-à€ì4x ïÿøµ°{³—A“ÿy:Õ“à�Ax  ßÿ××°ûuüä†FÔØÑ²/ylDK·éØŠ“H3wî�@x áÿ‰‰�:Þ¿èf¦>ò(+œ^È’”Òœœ‘N»ÈÁÕî-x áÿ‰‰�¨:3pöËwÈsI¹½<ónà˜™œ‘¼MÊBzãŸ1x3 Ìÿþþ�y6ò7i˜ØŽ¾#2V81x•%Ê‘�]�ÔSçãÞ¸4N‰ù¬’¸W­dJïšâ°ŸFxýôàÈ�¾�b& s3ErrorResponse, listBucketResultXml,‘Ž5 listPartsXml, comple“ñP,‘ó(““7³“Q_, completeMultipartUpload, a“á’“7“MQ“ & softDeleteFile, softDeleteFilesBatch,³É¶SVerify auth for regular requests; presigned URLs are verified per-operation
  if (!“Ê+“0 “Æ	#3method, req.url, headers, null, config.s3AccessKey,“A
 REGION“h
?“°
'“ß
 pathname, 403, reqId“/®'MethodNotAllowed',“>	 '/', 405“�³€O'MethodNotAllowed',“> `/${bucket}`, 405“�³{®'MethodNotAllowed',“>“¡Y 405, reqId“¿“'InternalError',“o7 pathname, 500, reqId);
  }“+³÷'InvalidBucketName',“QC,is not valid.', `/${bucketName}`, 400, reqId“xk'BucketAlreadyExists',“/ `/${bucketName}`, 409, reqId³j?'NoSuchBucket',“QC(# `/${bucketName}`, 404, reqId);
  }“'“°I“`=“•¬'NoSuchBucket',“QC( `/${bucketName}`, 404, reqId“ºp'BucketNotEmpty',“H0 `/${bucketName}`, 409, reqId“­ä“-(“ W “A)“T-“Ü“0 …#fullUrl, 'GET', config.s3AccessKey,“A
 REGION“!D&'AccessDenied', 'Request has expired',“¡Y 403, reqId“Ó!Y“'NoSuchBucket',“QC(“¡Y 404, reqId“Ä"S“'NoSuchKey',“^9%“¡Y 404, reqId³©#¿file: File, “-(L“_“Ö%…'InternalError',“x&"“¡Y 500“$4³ÿ&½“'NoSuchBucket',“QC(“¡Y 404, reqId“Ä"S“'NoSuchKey',“^9%“¡Y 404, reqId)“°$-“pN“ð$“�*U'etag'“Ú*<“+¬“-(“ W“,! req: Request, “A5“ï!=“'NoSuchBucket',“QC(“¡Y 404, reqId“N-úconst formData = await req.formData();
  const fileField = formData.get('file');
  const file = fileField instanceof File ? fil9eField : null;

  if (!file) {
    // Direct body upload
““ª.“¹.*“./(  ““t/,“ž/“r(“»etag'“-6* });
    }

  “10HreqId);
  }
“èSawait file.arrayBuffer())“,/b“N/�“°etag'“-6*“'0-b“Y0 “†0/*buffer: Buffer, hash: string, key: string,“è0, “A/“_1úfileName, signatureBuffer,“/³�2™'etag'“-6VF_destBucket: string, destKey: string, copySource: string, destBucketId“@(9³7 “'NoSuchBucket',“QC( copySource, 404, reqId“¾8k“'NoSuchKey',“^9% copySource, 404, reqId“03¬³^:“-(�“'NoSuchBucket',“QC(“¡Y 404, reqId“¦>¯bucket: string, bod“?(}“'NoSuchBucket',“QC( `/${bucket}`, 404, reqId“@D’AsBatch“À>s);“UAkeys.slice(0, deleted)“Aðbucket: string,“W “A5“ï!=“'NoSuchBucket',“QC( `/${bucket}`, 404, reqId³ªC_ prefix, delimiter, maxKeys,“5E““0L=“qL"³/F+bucket: string,“W “A5“ï!=“'NoSuchBucket',“QC( `/${bucket}`, 404, reqId“ªCÒ“gJâ prefix, delimiter, maxKeys,“uK·“0L=³qLÉ“-(_“ W “A5“ï!=“'NoSuchBucket',“QC(“¡Y 404, reqId³ŒP9“-(“ W req: Request, “A/“_2“}RÀ'NoSuchUpload',“0X(“¡Y 404, reqId³¹SÜ'etag'“™Vc“-(“ W bod“?(:“_’'NoSuchUpload',“0X(“¡Y 404, reqId“�X½'InvalidPart',“hY3“¡Y 400, reqId³ÓYÅ“-(“ W “A/“_’'NoSuchUpload',“0X(“¡Y 404, reqId“`©“-(“ W “A/“_’'NoSuchUpload',“0X(“¡Y 404, reqId“�X“ÿ%$³…bÛ'œë •xôý�¾¸¼°¬³ÂµDPresigned URLs: only supported for GET (verified in handleGetObject)“a2if (method !“¿“û"“›%Presigned URLs only supported for GET“ë	# else³îU!I// Read raw body â€” S3 clients send raw binary, not multipart/form-data
“ý**“)+(Kconst contentType = headers['content-type'] || 'application/octet-stream';
“Q+(“€“-Ñ“q,%contentType“.ˆcontentType“�:9³Ä.contentType“½OY³L0?Keys: string[] = [];
  for (const key of keys) {
    const ok =“;/$  if (ok) deletedKeys.push(key);
  }“š=deletedKeys³Ï=2!dð¹ÂéÃKxù ÿÃO®N�}‘ softDeleteFile,±¨Ë
 “sW
 ³Ê_“Á®³–U prefix, delimiter, maxKeys,³i“ˆ%“¯$“Õ­
     “‚3
     “µ>“´D“€“¯o³²Ó“Š] 201“Á“_“Á®“Ûƒ_“Á·³è
“š “$-objec“* 8“ž"_“š “$-“N#
“:$/³’#L“Í$4“Þ$“ &“0 “%7³—&,ÿfQ´àÄex  ßÿ½½°&ñÀ?7LïmšqðÓ	Mß�ƒ°W“¡ÚCÄ¥xE ºÿ100644 auth.ts W¾ûƒmœy!ÎÞs’§1±é=/100644 xml.ts £z<Wfšoî…pà¥;ø5¿[ìg‚F$ã€›9xÃ <ÿ¹?ù=°N.map(“c‚)³õR.map(“Xç).join('')}
  ${prefixes.map(“sR)³Ñå.map(“Xç).join('')}
  ${prefixes.map(“sR)“Ñ³Sh.map(“Ì¼)£”.map(“¥B).join('')}
  ${errors.map(“ˆ)³­	:Ü=Ïê§6xj •ÿ©©°`E‡+v?as©�ý�!‚ª­h(²Á100644 s3-operations.test.ts ùn=BÕXeR>£AçLR�ÓS¨ú“¥Éè`’©Ç5³à¨XÃ=[!"µì“‚'Ô+ðïÃx?ÀþÓ‹°™B'GET', 'http://localhost/', {}, null, 'key', 'secret', 'us-east-1'“ä“‡“ÅN'GET', 'http://localhost/', headers, null, 'correctkey', 'secret', 'us-east-1'“N“¨š“‡“hÏK'GET', 'http://localhost/', headers, null, 'testkey', 'secret', 'us-east-1'“N“¨.“/	U“‹	$ 'GET', 'key', 'secret', 'us-east-1'“d});
Ÿ#c|à‹°Axð ÿÏå°ì{’ sizeBytes: 100, etag: 'abc',“0“N }³¶+{ key: 'a.txt', sizeBytes: 50, etag: 'def',“0“N }³xà“_!);
    expect(“›Z“û‹'<CopyObjectResult“,“¾	ƒ'NoSuchBucket',“]
' '/bucket', 'req-1'“@"³Ë
‘³b‹ '<C“ ³BµEI$ç€Ê	xG ¸ÿñÖ�f" id: 'uuid-1', name: 'test-bucket'“ÿ‘ª‘¼"‘áŠ“t“€T“t³éˆ
�~âà¨1x  ßÿ××°ûâžeˆÂJâÁ¡kEÓÝüÇ& ˆr“H{«xë ÿ100644 bot.ts ¬®ß·:õˆ5zªe©Ø8³â40000 db ×¸N wßÒíŠãÏ‹‰)öÑ”ii 100644 env.ts géÔú½"µY ØÔŠùD´.oƒ100644 home.html z¡¥úÓã2EžAøù˜¥^100644 index.ts çñ¾í=0  À7§>ô@—F'·40000 routes kï#)–&Ø÷suØo*°ã±40000 utils frhTE=Ùf�{f4ïô ÜŒ	©�_–î€xx áÿëë�·ín¨iŒïÕbH÷2Á�Ìh~·i‘Ë ðÉ™ïŠ†x €ÿÉ€�š7const schemaSql = await Bun.file('schema.sql').text();
“~7try {
“¿!“â-““0P“‚!} finally {
  await sql.end();
}
Ý/){l±dx óÿžê°Ý³!þ%äÉxT «ÿþþ�X5Ù¯¿Š—Ú›DsÐÒâÕäK�ð^100644 s3.ts îÑh÷v‹©;H°Ý	-+�W2×‘�]�ÔSçãÞ¸4N‰ù¬’¸W­d±e(DnÕzx ñÿ„ò�Y'src‘o•&Skæ²3xV ©ÿù×�=ÈðG@Ñ´wcìÅÿtH¬u^P±sŒ2Rº
1mŽÎòü·eý(e6©ïQ740000 src …X€ïÈ‰ž×�ž:Bb‘¾¦Idq”“1H½`&åéex9 Æÿ××°�0üŽf
°±V9/tbÈÓ%6^<{“£X-Ì+!—º~Lx ÞLaÚ±ò“H›_ÀàGx  ßÿ××°ûÝý»¾dS.Ù(”G:Ðô¯o
´“H ±ul¬fx óÿµ°	°Ê³Oæ!ãäo´Dx ðÿ©¢°Q“­±“‰ .B‡à€,x  ßÿ××°ûª9
™ŽuÈYã~Ìè‘À´§‹qÁ“H˜
¦xÆ 9ÿ100644 bot.ts ¬®ß·:õˆ5zªe©Ø8³â40000 db ×¸N wßÒíŠãÏ‹‰)öÑ”ii 100644 env.ts géÔú½"µY ØÔŠùD´.oƒ100644 index.ts “vU7+Ó{JüÔMÛ×Â¾:ÁÜ	‡40000 routes >T6@êËêþ„|�éEjÅRÖ1‘40000 utils frhTE=Ùf�{f4ïô ÜŒ	ŸÀTšå·x êÿž�°V“Ê³¼³]ÁRLCi„6x	 öÿþÛ�P‘s‹6K¦xÆ 9ÿ100644 bot.ts ¬®ß·:õˆ5zªe©Ø8³â40000 db ×¸N wßÒíŠãÏ‹‰)öÑ”ii 100644 env.ts géÔú½"µY ØÔŠùD´.oƒ100644 index.ts “vU7+Ó{JüÔMÛ×Â¾:ÁÜ	‡40000 routes k¶r7vê*[%zÀ‡QžkÂ40000 utils frhTE=Ùf�{f4ïô ÜŒ	5šQüf€jx ùÿÛµ�µ	ÙØà„Px  ßÿ××°û˜Yþ„ySBå§>–‘oP”ñÏY“Hø7^ë�x; ÄÿÆÆ�’4S¸¿3‘‚¸/?‚Ú >+.ïM40000 utils ‹¼!z§Ô&4?Šœg™­oWæ\«ôkëJx äÿÆÆ�²frhTE=Ùf�{f4ïô ÜŒ	•yÆi‚ x	 öÿÛ”�Q‘rCÄ™à†	x  ßÿ××°ûLqPC�|˜q#A
¡ÔXfÊ€“HÒü[àÚpx  ßÿ½½°Ör.&ÓŽ@IŒÍ¿”˜Ú­òöB¬õ“¡êŒ»¥xE ºÿ100644 auth.ts •%ØßJrQky4·�#ËI([100644 xml.ts Õ,Ì…�5'òf’¼rƒ˜0mSðèþxx ‡ÿªG³D°û
return uri³\R³*°parsed.³Ú§³“_“]
G`${h}:host\n`)
    .join(''“?Str = signedHeaders;“ L“=LStr³´ ö5)è–x( ×ÿù=Ž=°R“lB''“·“Ü" },
  })“Ð"³l�Îàˆwx  ßÿ××°ûvÅgÓ^6Û~È°ÒRþ¤öøÎÕ“Hw%ëƒ'x äÿÆÆ�²�Ì<‹¼&/àÞªÏ:E˜çÜËMô
»õ?jÞx
 õÿ½ �ÿ“¡:DàÌx€ ÿ××°�<ž+{È¢Ã˜däŒl^à„ÞâÉ100644 package.json «²„EÁø¢%>xä
¦XXÉG›“Ë03µFOø$ž€HÞ®X1h[.v°v)40000 test 
—%áÕÕínènöKM*’“.)ªl4•¦x& Ùÿ40000 superpowers }§&¾,«“f©Åek»,zéþ £j x@ ¿ÿ40000 plans %[Ï½_$ó?@3Ü¡ÖKõ›40000 specs Xk*ª2¶&â©ƒ>Lu2âCàÓÙ:Þ+åfî\x ùÿƒ¿�¿ •fí+x ùÿ•Ê�Ê£½¦xÆ 9ÿ100644 bot.ts ¬®ß·:õˆ5zªe©Ø8³â40000 db xg!NŸ<73‡'MU”y$Õ100644 env.ts géÔú½"µY ØÔŠùD´.oƒ100644 index.ts “vU7+Ó{JüÔMÛ×Â¾:ÁÜ	‡40000 routes S¸¿3‘‚¸/?‚Ú >+.ïM40000 utils �Ì<‹¼&/àÞªÏ:E˜çÜËMô
â=NRçÆ2x÷þº#û�†interface QueryResult {
“ë	%rowCount: number;
}±„Ó“Ç“ÈÓ6// Use raw SQL for the complex prefix/startAfter query³˜“Ú“Ñ	QueryResult“
‡“›
Ê7// It's under a subfolder â€” extract the folder prefix“\ÀM// It's a direct child object
        objects.push({
          ...row,
      “V+8      s3Key: s3Key,
        } as unknown as S3FileRecord“@œ“ß!3
      (row) =>
        ({
          ...row,
      “V+  “}%'    }) as unknown as S3FileRecord,
    ³&QueryResult³[QueryResult“”2ßÆžî‡Rx áÿÆÆ�+98ÔÔÜ�+)ÇJœÆ²ßCF‘?‡½aæÏx& Ùÿ‰“‘Nuschema.ts Z/W‰á¹6{<?lîì7ª4X ¦Kn�“Vx ñÿÿš�X±a\“™æ.w"ï’wxo �ÿ××�=R]º’ËÉK�Å¦èÏP}ñ¬Ù±Q>ž+{È¢Ã˜däŒl^à„ÞâÉ“£:2"”ëW›ÿìÛë�,8ñÙQ¼“40000 src 1G¬ò[þý4n‹±u6or¬“Hw™20g€Ùwx øÿÑÊ°Êyg�˜:x øÿµ¶	°¶¼÷¦xÆ 9ÿ100644 bot.ts ¬®ß·:õˆ5zªe©Ø8³â40000 db 98ÔÔÜ�+)ÇJœÆ²ßCF100644 env.ts _¶†×2ÿŒ9;{’:Na¸Ë100644 index.ts “vU7+Ó{JüÔMÛ×Â¾:ÁÜ	‡40000 routes S¸¿3‘‚¸/?‚Ú >+.ïM40000 utils �Ì<‹¼&/àÞªÏ:E˜çÜËMô
?]Nièâx çÿë�°±³ú`“õ},
});
z­d¤x$ Ûÿ40000 workflows ‘àÁ—Öî@V+ý°–Žf†áõŸ:­x- Òÿ100644 notify-parent.yml ‡¢y2ŸT‚@Lü‹0¯ü„àI­æ4nvË³%xS¬ýname: Notify Parent Repo

on:
  push:
    branches:
      - main

jobs:
  dispatch:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger monorepo build
        uses: peter-evans/repository-dispatch@v3
        with:
          token: ${{ secrets.DISPATCH_TOKEN }}
          repository: MythEclipse/ultimate-asepharyana.tech
          event-type: submodule-updated
          client-payload: |
            {
              "service": "teleuploader",
              "ref": "${{ github.ref }}",
              "sha": "${{ github.sha }}",
              "actor": "${{ github.actor }}"
            }
\£©¦åÏ~xu Šÿùù�=w¦“6ê‚˜øNõD4M* CR¥±Q®Q¼¯Y	`¿”ÐX˜.˜Î½î340000 src  àù|‹Rö·8–[Å¿Ók(é‚Ø40000 test ¯Ä€úâtó¼Á1Ù}m¾op/Ì“P)—ˆ3Qg€âlx øÿÑß°_Çì�¡/xŒ sÿµ©°è“!+file_hash VARCHAR“-archive_‘“-“é!“-““-“+“-archive_mime_type VARCHAR“-archive_size_bytes BIGINT;
“Á&³©bò,Ô¦xÆ 9ÿ100644 bot.ts _öWT)èØÎ¯ýD ÓUu»D40000 db 98ÔÔÜ�+)ÇJœÆ²ßCF100644 env.ts _¶†×2ÿŒ9;{’:Na¸Ë100644 index.ts PIYpr Imw&`iñZÐÈITÁO40000 routes Ê³ãœà�¥EÖì®Í¦ûmp>eÍ40000 utils “IÜ¡ˆF'&¹Höâ­#`ïy wM}å�Úx êÿö#•#°«³á
“Õ³í	c¿áÔvx! Þÿžø°T“@'“›B³¼³]"“¡
+“ì2ÃH¤	x” kÿ100644 files.ts qàUd_TÙ©µŸÌ+Š,C½100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 swagger.ts ‘•Þ�Æ5OÃ%&
)÷È?T100644 upload.ts ®ýŽ—œ¯Àšðo`9W³½%ÆMÇîú9µl�žcx óÿ‘&�&°6³8Ù³/¨xÈ7þ100644 botHealth.ts ÷ÀÍ\,V{ñ£nPiC¸Näw100644 cache.ts »Ü9„AúÞæäÆ¾˜ÒîqŸ¥GXR100644 file.ts Å�8?’Á®_Åƒíá¶ZÔrðÜ100644 ip.ts M–óW£¬–¢Ôî�]'ñ5gf¯Ó100644 logger.ts ‘fcµ’Ä{¶_¤ò5>Ír(100644 metrics.ts XgT*çü ÚþÛ9At\'²100644 rateLimit.ts hC•t|=ßxü°ú×Zîë�+100644 retry.ts Ó¯“ë˜ÆÈIX!drhóHDàOÛ100644 telegram.ts nÓ~ý0À]
¨Ý-…SbÏÁ)˜Õ100644 telegramQueue.ts n&IÎmˆ‹“ášh3� è’Î|¼100644 uploadBatcher.ts ±K¤�°Žñ*rýÍÐ<~î`�100644 zip.ts söbµ c•L¯¼ft­ÓËKPÅ¦Ëá»ç‚UxW ¨ÿÈÈ°:àåöÁaÆò­ÑZ«4(ÔV`“ND6µªî©ËŠ‰õvmj—Y
"×¾100644 zip.ts Òˆ—jËÂšL˜Þ	B^ü·¹bÝ¢…&ú³Âx#Üóimport logger from './logger';

interface BotHealth {
  index: number;
  isHealthy: boolean;
  rateLimitedUntil: number;
  failureCount: number;
  successCount: number;
  lastUsed: number;
}

class BotHealthTracker {
  private botHealth: Map<number, BotHealth> = new Map();
  private totalBots: number;

  constructor(totalBots: number) {
    this.totalBots = totalBots;
    for (let i = 0; i < totalBots; i++) {
      this.botHealth.set(i, {
        index: i,
        isHealthy: true,
        rateLimitedUntil: 0,
        failureCount: 0,
        successCount: 0,
        lastUsed: 0,
      });
    }
  }

  recordSuccess(botIndex: number): void {
    const health = this.botHealth.get(botIndex);
    if (health) {
      health.successCount++;
      health.failureCount = 0;
      health.isHealthy = true;
      health.lastUsed = Date.now();
    }
  }

  recordFailure(botIndex: number, retryAfterSeconds?: number): void {
    const health = this.botHealth.get(botIndex);
    if (health) {
      health.failureCount++;
      health.lastUsed = Date.now();

      if (retryAfterSeconds) {
        health.rateLimitedUntil = Date.now() + retryAfterSeconds * 1000;
        health.isHealthy = false;
        logger.warn('Bot rate limited', {
          botIndex,
          retryAfter: retryAfterSeconds,
        });
      } else if (health.failureCount >= 3) {
        health.isHealthy = false;
        logger.warn('Bot marked unhealthy', { botIndex, failures: health.failureCount });
      }
    }
  }

  getHealthiestBot(): number {
    const now = Date.now();
    let bestBot = 0;
    let bestScore = -Infinity;

    for (let i = 0; i < this.totalBots; i++) {
      const health = this.botHealth.get(i)!;

      // Skip rate-limited bots
      if (health.rateLimitedUntil > now) {
        continue;
      }

      // Calculate score: prefer healthy bots with fewer failures and more successes
      const score =
        (health.isHealthy ? 100 : 0) +
        health.successCount -
        health.failureCount * 10 -
        (now - health.lastUsed) / 1000;

      if (score > bestScore) {
        bestScore = score;
        bestBot = i;
      }
    }

    return bestBot;
  }

  getStats() {
    const stats = {
      healthy: 0,
      rateLimited: 0,
      unhealthy: 0,
      bots: [] as any[],
    };

    const now = Date.now();
    for (const health of this.botHealth.values()) {
      if (health.rateLimitedUntil > now) {
        stats.rateLimited++;
      } else if (health.isHealthy) {
        stats.healthy++;
      } else {
        stats.unhealthy++;
      }

      stats.bots.push({
        index: health.index,
        healthy: health.isHealthy,
        rateLimitedUntil: health.rateLimitedUntil > now ? health.rateLimitedUntil - now : 0,
        failures: health.failureCount,
        successes: health.successCount,
      });
    }

    return stats;
  }

  reset(): void {
    for (const health of this.botHealth.values()) {
      health.isHealthy = true;
      health.rateLimitedUntil = 0;
      health.failureCount = 0;
      health.successCount = 0;
    }
  }
}

export { BotHealthTracker };
UðªºçŠ„#x èÿ³7Ç4°V“ÎU“:º“¯z“Khyð	©â‰þx² MÿÀ˜�÷±ø8
  ]
});

// If not production, also log to console
if (“w%) {
  logger.add(“q“W‘r$  “ÀF
      winston.format.simple()
    )
  }));
}

export default logger;
_ï6bá€Bxq Žÿ˜—�÷,±÷,
  ],“j“¼Console({
    ‘p!?winston.format.colorize(), winston.format.simple()),
    }),
  “û )#øk„›x ôÿå@¢@�ñ³41"}Áæ	ö$x– iÿ©¢�;¯*2›Îyš?YâíìÈ ¹îN100644 bot.test.ts G
Þ*ýÔ¤DM;âåLE«ˆ‘T9`r»CY’£.†õ®åÓßHò+Ä@‘¡=þüÀ«2è½Äåé–	\±'¯Ýþ‘ò_“­;f�Æ
PŸxqEmD÷…2”­Óÿ©“üb“‰ _·Eè€%x8 Çÿ¢¢�@ƒ‚ð6L˜L‰?ŸÎp£‡!†5Ä±T8•Ó–´vÕ‘”»½Z€0áQÔ“ ‚”Èl�Ìx óÿº·°á³dV"�—í�Ìx âÿûù°3“L30);
  });
});
}lƒãwx óÿè/Ð/°f³~j%„ã¤%xT«ý100644 .dockerignore Ám•mò=BPÿ<\J5Óª1ý>100644 .env.example ‘‰
ÞüÑ/Ï�Ý D,ŸÉ'¹Ñ40000 .github ûó=µŸÁq1»W6#‡ÃÕ.ÿ­Ú100644 .gitignore ë=kÃ;Fª¬I“ŽyˆøÓR100644 CLAUDE.md €ü,ÿ{Ï¯…˜q�MC<¾G¿ý—100644 Dockerfile ’"â2ÉŽ/VÓr1Çü/*©@y100644 README.md Õk2`¶Öî†bûjÍß6AéÍ100644 biome.json ê›äŽÓx4‚c2á»mTÁM½100644 bun.lock —Ö?XŒ‘¿ëvë»EÞ8ŠÕ¯€á100644 docker-compose.yml ¼�0eqÜÌ qê‡–JSØWI´(40000 docs s÷Õ”%Mþœ¶Gb¬Zke�100644 package.json «²„EÁø¢%>xä
¦XXÉG›100644 schema.sql ¼¯Y	`¿”ÐX˜.˜Î½î340000 src `ÿ+Äö§»M±å(‰,ÉV40000 test Ä•Žd—ì q Ðr¢=8100644 tsconfig.json  æ­\Ý/¡¼”¹’¸Ž�ìTèÒèƒax8 ÇÿÔÔ�_´°Á|÷Ÿ°(‘f ìSïîÜ*Ù±s…¡§i�çÈ–»¥P±9¤(å­´“H0kkç
Gx§ XÿÔÔ�=w¦“6ê‚˜øNõD4M* CR¥‘Q¤gñ%Þ¯Ï'Ðpé�j[ûC‚Äô“	d3æÿB±3ˆòB…ã8NÍ,HfÎþ‘40000 docs ž+{È¢Ã˜däŒl^à„ÞâÉ“ X3R!lž¤?h‰XO$|)s¥í40000 test ¯Ä€úâtó¼Á1Ù}m¾op/Ì“+)Z-Ií€Šxx� rÿÝÆversion: '3.8'

�.image: ghcr.io/mytheclipse/teleuploader:latest±J“”Ò/curl -sf http://localhost:3000/health || exit 1³Øtech³Æ¤Ž1l€x óÿÆ£°«³Îø$	l¦xÆ 9ÿ100644 bot.ts _öWT)èØÎ¯ýD ÓUu»D40000 db 98ÔÔÜ�+)ÇJœÆ²ßCF100644 env.ts |7p"o÷C†žâ%j×†<¥b½E100644 index.ts PIYpr Imw&`iñZÐÈITÁO40000 routes ŸtÌOÔcÏ	$e¬€sœ·›Rî40000 utils AhàDœ¼i½œÏQ�lÄ¯Cj�òKèá€šMx! Þÿë‡°±³úJ“F•³ß{“õ},
});
ã9Î¤	x” kÿ100644 files.ts ×NÉˆ×%Zv¶¦H40ü:Ñš©100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 swagger.ts \\Ø\d—k‰º[ çô»‚â100644 upload.ts ?–µÎð.ë…¹×NÔ/6>¥�^:Ál�âIx óÿð;ä;°B	³N	¢&~è�ºx( ×ÿ´D¢D°§“a“Á“`“÷“}“\“‹
³Aó<Ojå­Jx5 ÊÿÈÈ°:¾¤Õ!IQ íëAÀ”™çB't�“Nfg‘—%QT^qÏ7\+¨wÐ¨ÏÀ,jÈ<ã�à-x ìÿü!ê!°¸“¿0“õ-“'Õ]q”à†Kx  ßÿÔÔ°ø¡§i�çÈ–»¥P±9¤(å­´“Hÿ¬ž¦xÆ 9ÿ100644 bot.ts _öWT)èØÎ¯ýD ÓUu»D40000 db 98ÔÔÜ�+)ÇJœÆ²ßCF100644 env.ts |7p"o÷C†žâ%j×†<¥b½E100644 index.ts PIYpr Imw&`iñZÐÈITÁO40000 routes |üáÄäOXœI79oË¹40000 utils Öü‘/º³C%û»\›”£úk'S#K¤	x” kÿ100644 files.ts Á#J‹/#¬
¿cÆÞ’‡©Ïñ8/z100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 swagger.ts \\Ø\d—k‰º[ çô»‚â100644 upload.ts ?–µÎð.ë…¹×NÔ/6>¥¯Û:÷á�ÑRx îÿ‘&¸%°§³®³û=m>ï„®&xðþå@º>°“Ú{ result, botToken } = await“ñL<FileInfoResult>(
      async (activeBot, activeToken) => ({
        result:“u13,
        botToken: activeToken,
      }),
    );

“ª*TelegramFileInfo;
    return {
“
0“<C“�+bot_token: botToken“çBget file info“V{“üiøRV‡¤%xT«ý100644 .dockerignore Ám•mò=BPÿ<\J5Óª1ý>100644 .env.example ‘‰
ÞüÑ/Ï�Ý D,ŸÉ'¹Ñ40000 .github ´°Á|÷Ÿ°(‘f ìSïîÜ*Ù100644 .gitignore ë=kÃ;Fª¬I“ŽyˆøÓR100644 CLAUDE.md €ü,ÿ{Ï¯…˜q�MC<¾G¿ý—100644 Dockerfile ’"â2ÉŽ/VÓr1Çü/*©@y100644 README.md Õk2`¶Öî†bûjÍß6AéÍ100644 biome.json ê›äŽÓx4‚c2á»mTÁM½100644 bun.lock —Ö?XŒ‘¿ëvë»EÞ8ŠÕ¯€á100644 docker-compose.yml ¼�0eqÜÌ qê‡–JSØWI´(40000 docs s÷Õ”%Mþœ¶Gb¬Zke�100644 package.json «²„EÁø¢%>xä
¦XXÉG›100644 schema.sql ¼¯Y	`¿”ÐX˜.˜Î½î340000 src ¡§i�çÈ–»¥P±9¤(å­´40000 test +ðüVXJXÄÝ[)-4­“)˜m-100644 tsconfig.json  æ­\Ý/¡¼”¹’¸Ž�¬’ë|n�¡wx ñÿÑÞ�®tech‘³¬.êŒí�Æx âÿƒõ�õ°¼	tech³Á	”tech³Z)i»ã~l-x óÿ�õµ�³ê#n¨¦x& Ùÿ40000 superpowers ‘UªNx®DÂõé¨¥=éÆË“HÀ x@ ¿ÿ40000 plans xIeÆX NŒr “Í%+UPÇ40000 specs z¾ñ3ïœöá‹uDÝ>¡ÖOšnÉËæ€À=x6 Éÿƒ¿�4yGkÇ‡�ú�‹[”Îºxw=‘H!ØCŠ<‡y³cÈv…äááû+£‘}Bm|÷îFx áÿ¿¿�i-‚:Ñ}¾�ë%¥ty.'ŽA‘}B·Àcë�ÂTx; ÄÿÑAÎA°tech³„G
tech³Ðötech`)“±$“€“ÿ“¹“€³Wz­9Óæ€À5x6 Éÿ•Ê�6Ò,}’+�5¸Ÿ�s{jõzâcí‘J#.ÿ&´1ÝJù~È^Ëü0Ý—Ñ�‘�I‡ï’å�Ádx% ÚÿÖ:Ó:°êtech³ïZtech³N¿tech³D	%eµç�Á1x èÿïî°×tech`)“ -³äseà�Hx  ßÿ¢¢°Œ•Ó–´vÕ‘”»½Z€0áQÔ“ ‚âÅ=áƒ–9x îÿò=ñ=°Étech³Î$KwCå„€[x êÿè/¬/°f³~0“y});
YQ¨ï›nx àÿÔÔ�_´°Á|÷Ÿ°(‘f ìSïîÜ*Ù±sáÿeƒë”x; ÄÿÆÆ�’4ye�Lóâ�ÉºœÛ¦„ÜHræ
`40000 utils “²4Þ:‚4r0%ù¯ýw¯”¥ŒŠA;�¤	x” kÿ100644 files.ts @™Êá;E±²‹Þlš£ÓÆŒ100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 swagger.ts \\Ø\d—k‰º[ çô»‚â100644 upload.ts ?–µÎð.ë…¹×NÔ/6>¥µM:øçÀEx7 ÈÿÈÈ�8¡+ÿÛYë]œ„Ö2ÙŠM9ÜË2§‘Lîÿ‰+­]¢ˆTCöšÈo±2­{“Nzîq+lŠÃox óÿ¬—°³!çŽ1xg ˜ÿº>”<�ñ³4¬“ò““±()“î#³®“À\³1Ãkeyof Telegraf['telegram']³ú
3“ê“K“Ù
     “¯1“	é³-ð$!àŸ^x  ßÿÔÔ°m àÐÛ¤�%tïÌS%æÜ†9Ù‚“�ÓXòç/x7 ÈÿÔÔ�_c÷G|M‡{Ó–ï�ÀÊ²êù‘sú¼�0eqÜÌ qê‡–JSØWI´(“�Ó××Í¤x$ Ûÿ40000 workflows »¼&M1!/}7H\þ2w~[U ÙUR£xS ¬ÿ100644 deploy.yml où1ÞÆïÃ!¼ç<´ñXNö˜º1100644 notify-parent.yml ‡¢y2ŸT‚@Lü‹0¯ü„àI­æ4‰‡#Þµâx%Úñname: Deploy TeleUploader

on:
  push:
    branches:
      - main

permissions:
  contents: read
  packages: write

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest

      - name: Install dependencies
        run: bun install --frozen-lockfile

      - name: Run Tests
        env:
          BOT_TOKEN: "mock_token"
          STORAGE_CHANNEL_ID: "123456"
          BASE_URL: "http://localhost:3000"
          DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/postgres"
          PORT: "3000"
        run: bun run test

  build-and-push:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and Push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ghcr.io/mytheclipse/teleuploader:latest

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to VPS via SSH
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: 45.127.35.244
          username: root
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            mkdir -p /opt/teleuploader
            cd /opt/teleuploader

            # Log in to GHCR on VPS
            echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin

            # Write dynamic docker-compose.yml
            cat << 'EOF' > docker-compose.yml
            version: '3.8'

            services:
              app:
                image: ghcr.io/mytheclipse/teleuploader:latest
                container_name: teleuploader-app
                restart: always
                environment:
                  - BOT_TOKEN=${BOT_TOKEN}
                  - STORAGE_CHANNEL_ID=${STORAGE_CHANNEL_ID}
                  - BASE_URL=${BASE_URL}
                  - DATABASE_URL=${DATABASE_URL}
                  - PORT=3000
                  - NODE_ENV=production
                  - LOG_LEVEL=info
                networks:
                  - app-shared-net
                labels:
                  - "traefik.enable=true"
                  - "traefik.http.routers.teleuploader.rule=Host(`upload.asepharyana.tech`)"
                  - "traefik.http.routers.teleuploader.entrypoints=websecure"
                  - "traefik.http.routers.teleuploader.tls=true"
                  - "traefik.http.routers.teleuploader.tls.certresolver=letsencrypt"
                  - "traefik.http.services.teleuploader.loadbalancer.server.port=3000"

            networks:
              app-shared-net:
                name: app-shared-net
                external: true
            EOF

            # Write .env file from secrets
            cat << EOF > .env
            BOT_TOKEN=${{ secrets.BOT_TOKEN }}
            STORAGE_CHANNEL_ID=${{ secrets.STORAGE_CHANNEL_ID }}
            BASE_URL=${{ secrets.BASE_URL }}
            DATABASE_URL=${{ secrets.DATABASE_URL }}
            PORT=3000
            EOF

            # Pull latest docker image
            docker compose pull

            # Run DB migrations
            docker compose run --rm app bun run db:migrate

            # Start service
            docker compose up -d
øðéÓë›3x‹ tÿ¥Áname: Build and Test‘)  pull_request‘%OXconcurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: tru±qOY21aï¿0x àÿÔÔ�_Fží ‡¯ËŒaNŽÅLXáuqã±sáäÓ½¤x$ Ûÿ40000 workflows …4j#éîYªˆ|Å�+}+Å„ð*@£xS ¬ÿ100644 deploy.yml Ý—g–²ièëW:¢‘'7Óé}nÑ100644 notify-parent.yml ‡¢y2ŸT‚@Lü‹0¯ü„àI­æ4`z#+¤x$ Ûÿ40000 workflows 9�}‰q¤Gý£Ø‰ÓÎl3?ÆªäúZ¦x& Ùÿ100644 deploy.yml Ý—g–²ièëW:¢‘'7Óé}nÑ÷aàºvx  ßÿÆÿ°Ž“Öµletsencrypt“à³­êÂ'èÂxX §ÿÔÔ°m àÐÛ¤�%tïÌS%æÜ†9Ù‚“�w3úËKeôÊ^•Æ9a®·å:»Ä)40000 test Ó…‰'µaè.‚Ìùù�Ž^â¸É$“+)eq)õëµGx; ÄÿÆÆ�’4¢4r¨2§¾ž ñ )ý×²ÝB[40000 utils “²4Þ:‚4r0%ù¯ýw¯”¥ŒŠjt¤	x” kÿ100644 files.ts ÈíT\@„kƒç&úÖ;/jÏ6R100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 swagger.ts RÀû¬HÆ5iç â1› ã!°ô100644 upload.ts ?–µÎð.ë…¹×NÔ/6>¥’u:èë‚œdxk ”ÿð;æ;°B	³N	ô0Proxies file from Telegram storage as a streamed“fp“ý)binary stream.'“q“.”³�“/E³Ô£í'âÈxb �ÿ¢¢�Þ\^R-æœŠJÉœk[�GÔžÉ…‘òC?lDõÌ]„Ÿ‡A¸¸*ì$k]'ˆ100644 swagger.test.ts –º›UÛæ3Éò	œ†-=ð1ï‚“t®=Ÿ*œèÆ@xX §ÿÔÔ°m‰¢75Âm.å#éÛCíÐ¹Ø¶“�w3.kNZÂûæCÓÓ`G‘ëm™é:š40000 test í¦Ì€÷£Þ�’Ž\§r£l"È$1ê“+)D)éÀJx) ÖÿÆé�Å‘þ´“ó›“Öµletsencrypt“à“­5“}HµÛ¦xÆ 9ÿ100644 bot.ts _öWT)èØÎ¯ýD ÓUu»D40000 db 98ÔÔÜ�+)ÇJœÆ²ßCF100644 env.ts ,•ÕÓ³ø5)‰(-‰ [ß?ºó100644 index.ts YDµÜe.|—Ø~8`¥Pú� 40000 routes &~comàJ÷äÉZ]á=ª, œ½40000 utils ÑÙ!?Ï­ULtUüžòŽˆÆü_)÷N–ï€Ûxß  ÿë�°(³ú“~�
      ? []
      : “"$ || '')
          .split(',')
      “.  “H³58Int“Á“‹	!, 10) || 60000“œInt“Á$!, 10) || 30“?G ...config, botToken: `${config.botToken?.substring(0, 10)}...` },
});
f¿9wë€éyx[ ¤ÿ´$¼°T“@'“›“¿“YhandleUpload“…+“ðFileRedirect“Ñ3“ðFileInfo“!¬³s"“·+“2­ì/¤	x” kÿ100644 files.ts ‚›]ÏÀ]�=]ð»{ê³ÂºŽGŒ100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 swagger.ts Nê#FPå7÷êhfuçXS¸�SÞ100644 upload.ts ²!Ø½jÚ÷ñw_p©9À–ošçˆÔ>²ì=‚�fxÜ#üò&ç"‘xÓ)checkRateLimit } from '../utils/rateLimit“?8extract³}È“/“yconst ip = req.headers.get('x-forwarded-for') || '127.0.0.1';

    if (!public_id || !checkRateLimit(ip)) {
      return Respon“¼/{ error: 'Rate limit exceeded' }, { status: 429“z“ß(“S“¶,{ error: 'File not found' }, { status: 404 }³sá“¶*{ error: 'Server error' }, { status: 500 }“ðarchiveBuffer = Buffer.from(await archiveResponse.arrayBuffer());
      const extractedFile = await extractZipEntry(archiveBuff/er, archiveEntryName);
      if (!extractedFile“á	"““C“¶_{ error: 'File not found' }, { status: 404 });
      }

      return new Response(extractedFile“cS“Ô<file.fileName.replace(/"/g, ''“4)extractedFile.byteL“b¶“ó“¶*{ error: 'Server error' }, { status: 500 }“$¶“¶+{ error: 'Missing file id' }, { status: 400“z“ß(“S“¶4{ error: 'File not found' }, { status: 404 });
    }“ª×uploader_id: file.uploaderId“wÙ“¶4{ error: 'Server error' }, { status: 500 });
  }
};
æ‰JNéÉEx9 Æÿ¢Dª<°É³ï³£³æ³jÀ“þ“> “Ô³Î“Ï“‹
³/óp…»ê	¶Zxš eÿÈ§�[Èu :?´ZaCq$¨ ØbáZFù�‘�_Gt¹³ù-<wáG•·6¨ lìÓ“cbÖ‰Uœ~åI|4iÆ¸£Hv¯oLç100644 uploadBatcher.ts ¹gÊÝ’Û±43G7ŸÎeü”�N{100644 zip.ts 0À¸ 9Õ¼:f¡KÅ¼Hàs1w.=nB0âŠÔPx²Mþ›ö
‘I %// Simple sliding window rate limiter‘h…WINDOW_SIZE_MS = 60000; // 1 minute window
const MAX_REQUESTS_PER_WINDOW = 100; // 100 requests per minute per IP

export const“Tt0// No entry or window expired - create new entry“Å.“AWINDOW_SIZE_MS“j"G// Check if limit exceeded
  if (entry.count >= MAX_REQUESTS_PER_WINDOW“ºb// Increment counter“2“».“Š“G¬“Ñ<WINDOW_SIZE_MS,
  maxRequests: MAX_REQUESTS_PER_WINDOW,
});
1Î�Gç„í x' Øÿå	š�>‘û0Number.POSITIVE_INFINITY³Q”%d¢íƒÈxí ÿìCå7�H±Nå“@™“@*³ê³†<let crc = 0xffffffff;

      const chunks: Buffer[] = [];
  “á9“Ék“î#      chunks.push(chunk“Xk&const crc32 = (crc ^ 0xffffffff) >>> 0“m³ì¨"for (const chunk of chunks) {
    “chunk);
      }³»A
ƒKœëßOx{ „ÿ¢¢�Þ«$¾õU]Á4íe{úò ð…�¾"‘òC?~cM0bR!„RŒB‰100644 swagger.test.ts ² ÍþŽ°	Eo¿so¦L?Šr8w“tsMÔ�{–N
÷PÝVn8‘'—×;“û'9ê1²ê"ŒÈx*Õýª6ž4°Kuploader_id: number³GÉrateLimit
const mockCheckRateLimit = mock(() => true);
mock.module('../src/utils/rateLimit', () => ({
  checkRateLimit: mockCheckRateLimit,
})³#'CheckRateLimit“UV“Ú®“Ü	>29 if rate limit is exceeded“îCheckRateLimi“false);“oV“±2“�29“±YRate limit exceeded'“°"³Ê)“Bò.redirect to telegram file url if file is found“î‹“òä“"Á“�302);“O/U(
        'https://api.telegram.org/file/bot123456:ABC-DEF/photos/file_0.jpg',
      ³°Oif file is found“î³-[
uploader_i“Þ“�/“·÷“%j“¬z“…	*});
});
7Á»Þí’°8xM²þÆ
ž"// @ts-nocheck
import { afterEach,‘!, spyOn‘) logger from '../src/utils/logger‘:1‘€#;// Spy on logger.warn
const warnSpy = spyOn(logger, 'warn')‘ =warnSpy.mockClear‘ð/ways allow requests as rate limiter is disabled“<%“¿%“¯“	“Ý““Ý““Ý“warnSpy).not.toHaveBeenCalled‘ðno-op on cleanup', () => {“)@});
èqá�]x îÿž„import {±"ü4P>æ
�¾x¦ Yÿü–°<post?: { requestBody: { content: Record<string, unknown> } }³L“ñ['/api/upload']“P*"
      'multipart/form-data',
    “î['/api/upload']“P?³°});
Na5bâ‚„-xr�þÓFÝ@°Ä000b08000100“ÐÆtelegram_file_id: string;
  “³Štconst mockGetFil“`'“·,file_size: 1000,
    mime_type: 'image/jpeg'“òšGetFile³#	XGetFile“ï	³{
~telegram_file_id).toBe('tg-file“Á.“).toBe('tg-unique-id-abc“Q“/“
8“¦“³Þ“¦“	³Ntelegram_file’tg-id“Q““	tg-unique“Q“/“�K³*telegram_file’“©“R“/f“ù“$#/cw7èéyxX §ÿÔÔ°mß%}5üá…¤v²=Œ»ïU…á[“�w3.kNZÂûæCÓÓ`G‘ëm™é:š40000 test í¦Ì€÷£Þ�’Ž\§r£l"È$1ê“+)5Ì)kç�øzxw ˆÿÙèversion: '3.8'

�.image: ghcr.io/mytheclipse/teleuploader:latest‘r‘È´“É”tech`)“`-“ˆletsencrypt“`“À5“�	H÷Û+ƒæëgxv ‰ÿÔÔ°mß%}5üá…¤v²=Œ»ïU…á[“�YQþöB–#–Þ4 Ï�‡…5²â40000 src ¤ÒŸ³ë¡g<aþÝ÷…º“ø;W40000 test š”K{l?¯‰Ë± L^tò»¬!sñ“+)Aƒ4gæ€”*x éÿ©Õ°Å“ƒ£“p&’Ú“<miÅ¥¦xÆ 9ÿ100644 bot.ts _öWT)èØÎ¯ýD ÓUu»D40000 db ØiÕfwW.œ‹]bCZü%œÓ100644 env.ts ,•ÕÓ³ø5)‰(-‰ [ß?ºó100644 index.ts YDµÜe.|—Ø~8`¥Pú� 40000 routes  §'Ã²uDPœï�n]Ÿe(40000 utils ˆ97~ÖÒ~•÷RÕáBô;ùA$0h»aLBë€Sx; ÄÿÆÆ�’4íì¥0Sb/)’¨WÈLjþCU40000 utils í+,Ä"ºEÑÚ§®(4£¨yFÇtë€¡Hx äÿ““�&[å¹xÐÄ¿B¦¥?îì|÷–s�n‚¶x ñÿÿ¸�X±aú“™æ3hÙ¤	x” kÿ100644 files.ts yþ=,b÷§wç$7µc¶±;u100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 swagger.ts Nê#FPå7÷êhfuçXS¸�SÞ100644 upload.ts q¸ª@ÑQª©÷ àºiBà{†qµ×<Oî¢kx^¡þç"ò°+“[T³#P// Check cache first
    const cacheKey = `file_info_${file.telegramFileId}`;
  “41C  if (!fileInfo) {
      // Cache miss - fetch from Telegram API
  “v“’1+file.telegramFileId);
      fileInfo = {
  “å<“Q“n7  };
      // Store in cache
  “¨/““ï%  } else {
  “E  }

    const redirectUrl =“¸BInfo.file_path}`³fî(jg�$x øÿ§Ù°Y²�g–x øÿ¢û°û‡Qè‡Hx8 ÇÿÔÔ�…-'NW|ÝŸû0²”ad/µ
[¥Ž×±™_Pvl°mŠŽw{\•R;|¥Ï\“H¸é[m�³4x òÿÄ‰�C‘MÖ“Tp$û3¦xÆ 9ÿ100644 bot.ts _öWT)èØÎ¯ýD ÓUu»D40000 db ØiÕfwW.œ‹]bCZü%œÓ100644 env.ts ,•ÕÓ³ø5)‰(-‰ [ß?ºó100644 index.ts YDµÜe.|—Ø~8`¥Pú� 40000 routes W _ec>exû }Ó‚k¹40000 utils })Ð£i„sfz-‚*vÆÀ¹•ÏbMK0¤	x” kÿ100644 files.ts §m–¾8yšðd3p•vûÑ�’100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 swagger.ts Nê#FPå7÷êhfuçXS¸�SÞ100644 upload.ts 1í‰9|·Š(Q„(Ñóìˆ8ŸÈs;Há…nx îÿòò�<‘l¿‘20³+Ç
I>ä+‚µ\x´Ký�Gñ0�
unlinkSync‘,±s¤, getBot“hFtype TelegramFileLookup = {
  mime_type?: string;
  file_size?: number³yœ“ÓfileBuffer: Buffer“*“C&“†c“c$
tempPath);“¢A“­);
    const bot = getBot();
    const fileInfo = (await bot.telegram.getFile(result.telegramFileId)) as TelegramFileLookup;

 ³ðfileInfo.mime_type ||“÷:<fileInfo.file_size || fileBuffer.byteLength,
      fileType:“¬ “S'computeHash(fileBuffer)“‹UWsetTimeout(() => {
      try {
        unlinkSync(tempPath);
      } catch {}
    }, 50³.a“\Fawait file.arrayBuffer();
    const fileBuffer = Buffer.from(fileBytes“ˆ%uffer“±Â“´¬fil“qv
fileBuffer“0!Š“ù!
fileBuffer“ "“C"ë³Ý3³\^“ù!	fileBytes“ "³C"Z ðë"ã„Dxìþñ0‹0�: } from 'node:fs';
import { unlink } from 'node:fs/promise±4±“íh³¡³!
“A=“”à.async () => {
      try {
        await unlink“š	(err) {
        logger.warn('Failed to cleanup temp file', { tempPath, error: getErrorMessage(err) });
      }
    }, 500);
  }³Æ	«P¬V¢ä©+xd ›ÿ§Ù�8ß'-Ø¿L ßxÿ‰RJ‘qßR‘LZšˆ3ÞÚh:±R•Aw?†@@ÏV‘º_˜,YéÂ/ñÕa½'²LŠZ=X“-ùˆ*Ø‚’Êu} ƒy"¯ÓéGC*ãá2x3 Ìÿ—†°T() => {
“a-“�“©>}
}, 5 * 60 * 1000“V_þá•ÔkxA ¾ÿ÷à°() => {
“5“E&“m+“š<“Ø.“2});
}, 5 * 60 * 1000“V!Oj|ë…åixk”þÚDë>°-³yØ³/	®“ýC“ß"
      activeBot.telegram.getFile(telegramFileId),
    );

    const fileData = result as unknown as TelegramFileInfo;
    retu“ê Data.file_size || 0,
      mime_type: fileData.mime_type || 'application/octet-stream',
      file_path: fileData.file_path || ''“q!å“£B
export const getCurrentBotIndex = (): number => currentBotIndex;
[+|>é€…xé ÿÔÔ�…-'NW|ÝŸû0²”ad/µ
[¥Ž×‘™¦B9D±ä%Æ7a¿”Ýqµ‰R�-_100644 docker-compose.yml ß%}5üá…¤v²=Œ»ïU…á[“�3ÂÞ]©½DW­_ËpS'‘’w¤Ñ�I100644 schema.sql þöB–#–Þ4 Ï�‡…5²â40000 src ŽFó”<xK­�X¤×èhÇ,óÎ=40000 test š”K{l?¯‰Ë± L^tò»¬!sñ100644 t“3!êfb%è��Cx( ×ÿò³Ì¯�ï³!?³Ç@ï“ŠJ“§kz³Km “†
³,lÆ-xÄ$o€ùmx ðÿµ—	°Ê“OÕ“=ø1öfë‘Jx; ÄÿÆÆ�’4Îp²@­‘j\.EF´ª¼)*Î40000 utils ¸ŠÜPÔ»¯-Ó^O?={;?äâ­Î»Û¤	x” kÿ100644 files.ts ÷>Õ!YÐ7	;óÔ¡ e/Ñ100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 swagger.ts Nê#FPå7÷êhfuçXS¸�SÞ100644 upload.ts ÖIá&ÄXwºâÌ`uÅŽÔifÑg$;ªï—/x àÿò¹�<±l6“jconst f“�;³‡k¸Ð
Øî�Ox áÿñ0÷.°å“íh³¡³!
“A=³”ÝÉì`£xÃ <ÿ100644 file.ts Èu :?´ZaCq$¨ ØbáZFù�100644 logger.ts ‘fcµ’Ä{¶_¤ò5>Ír(100644 rateLimit.ts ±tû©Zlø.A<}*•öÏà100644 telegram.ts ñô£Úñÿ[Ú‚ëæc¶c´²ä‚100644 telegramQueue.ts .Q¥E5LfYð†³œÚ‡ÈÙF]M¿î€Px áÿÃÃ�ƒ˜,YéÂ/ñÕa½'²LŠZ=X‘—,Èºï´x? Àÿö
¯“=_“\“]F,// No-op karena rate limit dinonaktifkan
};
{C¤ã±!x ìÿš^“ýStask();
};
D˜ûé€�xé ÿÔÔ�…-'NW|ÝŸû0²”ad/µ
[¥Ž×‘™¦B9D±ä%Æ7a¿”Ýqµ‰R�-_100644 docker-compose.yml ß%}5üá…¤v²=Œ»ïU…á[“�3ÂÞ]©½DW­_ËpS'‘’w¤Ñ�I100644 schema.sql ZwõjbÁŠ{}‘í(àˆTTÿ40000 src E÷ÚoKÆ¸êâf€ðÕ¼,V¶³40000 test ƒEö±r®G{ÃÚš¼Ñéå#ãá100644 t“3!cÐe'ï€yx? ÀÿÔÔ°ø3žÆÞ†K£FÔÃ9óTÌGâ?Kñ40000 test xÏvA],EZ1iù8�g¸AâH/“+)¤8¼k¡jx ôÿÕ‘°$“hmw·¤	x” kÿ100644 files.ts H%œ­7 •ÝÄjN«ÌNDã¦ªj100644 health.ts Äð)°pv�‰›r'ç�Þ*Cš�ö100644 swagger.ts Nê#FPå7÷êhfuçXS¸�SÞ100644 upload.ts ê$I�ÔEçtœ2_•!“Âè»ÍÞ<ÓêžxxZ ¥ÿòÇ�<‘l“³+w6const { getBot } = await import('../utils/telegram');
“fconst f“�;³‡k½¿ à³yx  ßÿ¢û°ŒãÃ œŒ¦¹–�F$©¢ù«Šbêl÷“ [5{¦xÆ 9ÿ100644 bot.ts eÿu¯×ÆueÒ�@x�×»Ì40000 db Ð|Ý/]Ö¼COC»”>ýòû 100644 env.ts #®/|¼ÝöZ+<‚%$Y cü‰100644 index.ts YDµÜe.|—Ø~8`¥Pú� 40000 routes 6h¼N¨†Ðw&_vÌfÄÞZÒ¸40000 utils L 0°ãwq(†ªµßÂ¨†›—:^MLã€Sx3 ÌÿÆÆ�M,•ÕÓ³ø5)‰(-‰ [ß?ºó‘aQ¹ú¨Òº±
‘+ÎxÑ7Ó¥IùV›.îBx áÿÆÆ�’Þ­Ø‡F7üä:ÈÉqæÍG™Ý‘¦ ÞøTáƒß/x îÿÝJ½)°!³&À	³�Ü	@¥¸¯xo �ÿ100644 index.ts ËvQfzÂZÀ¢Ìx±Öü.vÏÇ100644 migrate.ts R7Aq2†J†fÒíäøJPŠ100644 schema.ts &[å¹xÐÄ¿B¦¥?îì|÷7+ìé
‹åhx© VÿÉÂ�I‘z 7const schemaSql = await Bun.file('schema.sql').text();
“~7try {
“¿!“â-} catch (error: any) {
“.5error.message });
“€!} finally {
  await sql.end();
}
'•7¦¤	x” kÿ100644 files.ts á5z\AªÌUh¿P)Ù=*7100644 health.ts Ž¯Ã
§FÕøô9ŸÊ7÷ëMZC100644 swagger.ts ì�©à.{S ¯¿ ¸™’çÝKc‰100644 upload.ts ŒaŠhÞ_ÔàÀÙ
è®ì:øªøøšò—=¸ë€!x äÿ””�€û=õ¥%pxÀíˆ­–sB—J
ç¶¥xV
©õimport { eq } from 'drizzle-orm';
import { db, files as fileSchema } from '../db';
import logger from '../utils/logger';
import { checkRateLimit } from '../utils/rateLimit';

type RequestWithParams = Request & {
  params?: {
    public_id?: string;
  };
};

export const handleFileRedirect = async (req: RequestWithParams): Promise<Response> => {
  const public_id = req.params?.public_id;
  try {
    const ip = req.headers.get('x-forwarded-for') || '127.0.0.1';

    if (!public_id || !checkRateLimit(ip)) {
      return Response.json({ error: 'Rate limit exceeded' }, { status: 429 });
    }

    const result = await db
      .select()
      .from(fileSchema)
      .where(eq(fileSchema.publicId, public_id))
      .limit(1);

    if (!result.length) {
      logger.warn('File not found', { public_id });
      return Response.json({ error: 'File not found' }, { status: 404 });
    }

    const file = result[0];
    const { getBot } = await import('../utils/telegram');
    const bot = getBot();
    const fileInfo = await bot.telegram.getFile(file.telegramFileId);

    const redirectUrl = `https://api.telegram.org/file/bot${process.env.BOT_TOKEN}/${fileInfo.file_path}`;
    return new Response(null, {
      status: 302,
      headers: {
        Location: redirectUrl,
      },
    });
  } catch (error: any) {
    logger.error('File redirect error', { public_id, error: error.message });
    return Response.json({ error: 'Server error' }, { status: 500 });
  }
};

export const handleFileInfo = async (req: RequestWithParams): Promise<Response> => {
  const public_id = req.params?.public_id;
  try {
    if (!public_id) {
      return Response.json({ error: 'Missing file id' }, { status: 400 });
    }

    const result = await db
      .select()
      .from(fileSchema)
      .where(eq(fileSchema.publicId, public_id))
      .limit(1);

    if (!result.length) {
      logger.warn('File not found', { public_id });
      return Response.json({ error: 'File not found' }, { status: 404 });
    }

    const file = result[0];
    return Response.json(
      {
        public_id: file.publicId,
        file_name: file.fileName,
        mime_type: file.mimeType,
        size_bytes: file.sizeBytes,
        file_type: file.fileType,
        uploader_id: file.uploaderId,
        created_at:
          typeof file.createdAt === 'string'
            ? file.createdAt
            : (file.createdAt as Date).toISOString(),
      },
      { status: 200 },
    );
  } catch (error: any) {
    logger.error('File info error', { public_id, error: error.message });
    return Response.json({ error: 'Server error' }, { status: 500 });
  }
};
í<Úâ“dxÂ =ÿÖÓimport { sql‘"‘B7“Health = async (_req: Request“A+try {
    await db.execute(sql`SELECT 1`);
’status: 'ok' }, { status: 200“0Health check failed', {“ï	5status: 'error'“î	“;
ÁO>I¶Æxf$™Ûimport { createReadStream, unlinkSync } from 'node:fs';
import { eq } from 'drizzle-orm';
import { nanoid } from 'nanoid';
import { db, files as fileSchema } from '../db';
import { config } from '../env';
import {
  checkFileSize,
  computeHash,
  ensureExtension,
  extractMimeType,
  getFileType,
} from '../utils/file';
import logger from '../utils/logger';
import { forwardToStorage, getBot } from '../utils/telegram';

export const handleUpload = async (req: Request): Promise<Response> => {
  try {
    const contentType = req.headers.get('content-type') || '';

    if (contentType.includes('multipart/form-data')) {
      return handleMultipartUpload(req);
    } else if (contentType.includes('application/json')) {
      return handleJSONUpload(req);
    }

    return Response.json(
      { error: 'Unsupported content type. Use multipart/form-data or application/json' },
      { status: 400 },
    );
  } catch (error: any) {
    logger.error('Upload error', { error: error.message });
    return Response.json({ error: error.message }, { status: 500 });
  }
};

const handleMultipartUpload = async (req: Request): Promise<Response> => {
  let tempPath = '';
  try {
    const formData = await req.formData();
    const file = formData.get('file');
    const fileName =
      (formData.get('fileName') as string) || (file instanceof File ? file.name : null) || 'file';

    if (!file || !(file instanceof File)) {
      return Response.json({ error: 'No file provided' }, { status: 400 });
    }

    const fileBytes = await file.arrayBuffer();
    const fileBuffer = Buffer.from(fileBytes);
    const hash = computeHash(fileBuffer);

    // Check for duplicate in DB
    const existing = await db
      .select()
      .from(fileSchema)
      .where(eq(fileSchema.fileHash, hash))
      .limit(1);

    if (existing.length > 0) {
      const existingFile = existing[0];
      const responsePayload = {
        public_id: existingFile.publicId,
        telegram_file_id: existingFile.telegramFileId,
        telegram_file_unique_id: existingFile.telegramFileUniqueId,
        storage_chat_id: existingFile.storageChatId,
        storage_message_id: existingFile.storageMessageId,
        file_name: existingFile.fileName,
        mime_type: existingFile.mimeType,
        size_bytes: existingFile.sizeBytes,
        file_type: existingFile.fileType,
        uploader_id: existingFile.uploaderId,
        created_at:
          existingFile.createdAt instanceof Date
            ? existingFile.createdAt.toISOString()
            : new Date(existingFile.createdAt).toISOString(),
        download_url: `${config.baseUrl}/f/${existingFile.publicId}`,
      };
      return Response.json(responsePayload, { status: 200 });
    }

    const rawMimeType = file.type || extractMimeType({}, req) || 'application/octet-stream';
    const { fileName: finalFileName, mimeType } = ensureExtension(
      fileName,
      fileBuffer,
      rawMimeType,
    );
    const fileType = getFileType(mimeType, finalFileName);

    if (!checkFileSize(fileBuffer.byteLength, fileType)) {
      return Response.json({ error: `File size exceeds ${fileType} limit` }, { status: 400 });
    }

    // Write file to disk temporarily
    tempPath = `/tmp/teleuploader-${nanoid()}`;
    await Bun.write(tempPath, fileBuffer);

    const fileStream = createReadStream(tempPath);
    const result = await forwardToStorage(fileStream, finalFileName, fileType);
    const bot = getBot();
    const fileInfo = (await bot.telegram.getFile(result.telegramFileId)) as any;

    const uploaded = {
      publicId: nanoid(),
      telegramFileId: result.telegramFileId,
      telegramFileUniqueId: result.telegramFileUniqueId,
      storageChatId: config.storageChatId,
      storageMessageId: result.storageMessageId,
      fileName: finalFileName,
      mimeType: fileInfo.mime_type || mimeType || 'application/octet-stream',
      sizeBytes: fileInfo.file_size || fileBuffer.byteLength,
      fileType: fileType,
      uploaderId: 0,
      fileHash: hash,
      createdAt: new Date(),
      updatedAt: new Date(),
    };

    await db.insert(fileSchema).values(uploaded);

    const responsePayload = {
      public_id: uploaded.publicId,
      telegram_file_id: uploaded.telegramFileId,
      telegram_file_unique_id: uploaded.telegramFileUniqueId,
      storage_chat_id: uploaded.storageChatId,
      storage_message_id: uploaded.storageMessageId,
      file_name: uploaded.fileName,
      mime_type: uploaded.mimeType,
      size_bytes: uploaded.sizeBytes,
      file_type: uploaded.fileType,
      uploader_id: uploaded.uploaderId,
      created_at: uploaded.createdAt.toISOString(),
      download_url: `${config.baseUrl}/f/${uploaded.publicId}`,
    };

    return Response.json(responsePayload, { status: 200 });
  } catch (error: any) {
    logger.error('Multipart upload error', { error: error.message });
    return Response.json({ error: error.message }, { status: 500 });
  } finally {
    if (tempPath) {
      const p = tempPath;
      setTimeout(() => {
        try {
          unlinkSync(p);
        } catch {}
      }, 50);
    }
  }
};

const handleJSONUpload = async (req: Request): Promise<Response> => {
  let tempPath = '';
  try {
    const { file, fileName = 'file' } = (await req.json()) as any;

    if (!file || typeof file !== 'string') {
      return Response.json(
        { error: 'Invalid JSON. Must include "file" (base64) and optional "fileName"' },
        { status: 400 },
      );
    }

    let base64Data = file;
    let rawMimeType = 'application/octet-stream';
    if (file.startsWith('data:')) {
      const match = file.match(/^data:([^;]+);base64,(.+)$/);
      if (match) {
        rawMimeType = match[1];
        base64Data = match[2];
      }
    }

    const fileBytes = Buffer.from(base64Data, 'base64');
    const hash = computeHash(fileBytes);

    // Check for duplicate in DB
    const existing = await db
      .select()
      .from(fileSchema)
      .where(eq(fileSchema.fileHash, hash))
      .limit(1);

    if (existing.length > 0) {
      const existingFile = existing[0];
      const responsePayload = {
        public_id: existingFile.publicId,
        telegram_file_id: existingFile.telegramFileId,
        telegram_file_unique_id: existingFile.telegramFileUniqueId,
        storage_chat_id: existingFile.storageChatId,
        storage_message_id: existingFile.storageMessageId,
        file_name: existingFile.fileName,
        mime_type: existingFile.mimeType,
        size_bytes: existingFile.sizeBytes,
        file_type: existingFile.fileType,
        uploader_id: existingFile.uploaderId,
        created_at:
          existingFile.createdAt instanceof Date
            ? existingFile.createdAt.toISOString()
            : new Date(existingFile.createdAt).toISOString(),
        download_url: `${config.baseUrl}/f/${existingFile.publicId}`,
      };
      return Response.json(responsePayload, { status: 200 });
    }

    const { fileName: finalFileName, mimeType } = ensureExtension(fileName, fileBytes, rawMimeType);
    const fileType =
      getFileType(mimeType, finalFileName) === 'application'
        ? 'document'
        : getFileType(mimeType, finalFileName);

    if (!checkFileSize(fileBytes.byteLength, fileType)) {
      return Response.json({ error: `File size exceeds ${fileType} limit` }, { status: 400 });
    }

    // Write file to disk temporarily
    tempPath = `/tmp/teleuploader-${nanoid()}`;
    await Bun.write(tempPath, fileBytes);

    const fileStream = createReadStream(tempPath);
    const result = await forwardToStorage(fileStream, finalFileName, fileType);
    const bot = getBot();
    const fileInfo = (await bot.telegram.getFile(result.telegramFileId)) as any;

    const uploaded = {
      publicId: nanoid(),
      telegramFileId: result.telegramFileId,
      telegramFileUniqueId: result.telegramFileUniqueId,
      storageChatId: config.storageChatId,
      storageMessageId: result.storageMessageId,
      fileName: finalFileName,
      mimeType: fileInfo.mime_type || mimeType || 'application/octet-stream',
      sizeBytes: fileInfo.file_size || fileBytes.byteLength,
      fileType: fileType,
      uploaderId: 0,
      fileHash: hash,
      createdAt: new Date(),
      updatedAt: new Date(),
    };

    await db.insert(fileSchema).values(uploaded);

    const responsePayload = {
      public_id: uploaded.publicId,
      telegram_file_id: uploaded.telegramFileId,
      telegram_file_unique_id: uploaded.telegramFileUniqueId,
      storage_chat_id: uploaded.storageChatId,
      storage_message_id: uploaded.storageMessageId,
      file_name: uploaded.fileName,
      mime_type: uploaded.mimeType,
      size_bytes: uploaded.sizeBytes,
      file_type: uploaded.fileType,
      uploader_id: uploaded.uploaderId,
      created_at: uploaded.createdAt.toISOString(),
      download_url: `${config.baseUrl}/f/${uploaded.publicId}`,
    };

    return Response.json(responsePayload, { status: 200 });
  } catch (error: any) {
    logger.error('JSON upload error', { error: error.message });
    return Response.json({ error: error.message }, { status: 500 });
  } finally {
    if (tempPath) {
      const p = tempPath;
      setTimeout(() => {
        try {
          unlinkSync(p);
        } catch {}
      }, 50);
    }
  }
};
¦5¸—£xÃ <ÿ100644 file.ts 1Ô2NFßRî™Ò)©9² 100644 logger.ts ‘fcµ’Ä{¶_¤ò5>Ír(100644 rateLimit.ts ±tû©Zlø.A<}*•öÏà100644 telegram.ts åS:¸Ü¯˜¤�Oñ“{Â¼cc‚	100644 telegramQueue.ts ‘ãq)=o¿@9);}»îp%±��È¾I£ë€Px äÿÃÃ�¯.Q¥E5LfYð†³œÚ‡ÈÙ™peâŒ…"x"Ýþ³7ˆ³­
“Name = (msg: any, request: any): string => {
  if (request?.headers?.['x-file-name']) {
    return request.headers['x-file-name'];
  }“»“SÃany, request: any): string => {
  if (request?.headers?.['x-mime-type']) {
    return request.headers['x-mime-type'];
  }
  ret³ÁoS_Éé¬†à6xÉ
6õÚDç=°š“!	 = async “/any“O2any“‚rany“û.message ||“<`B// 429 rate limit hit! Rotate bot index instantly
      const prev“ì“3A
const next“ì“Ðo³C¿“·?³5“Œ“*!any“Ra$: any = await enqueueUpload(async ()“òW“ÊB“ƒphoto') {
          return“?Photo“}$={
            caption: fileName,
          });
        } else“ž{
          return“?Audio“}$j{
            caption: fileName,
          });
        } else if (fileType === 'video') {
          return“?Video“}$j{
            caption: fileName,
          });
        } else if (fileType === 'voice') {
          return“?Voice“}${
            caption: fileName,
          });
        } else if (fileType === 'animation') {
          return activeBot.telegr“P	Animation“}$l{
            caption: fileName,
          });
        } else if (fileType === 'sticker') {
          return“?Sticker“}"$);
        } else {
          return“?Document“}${
           “ÑT,
          });
        }
      });

      // Advance round-robin index for next job“0>“m)let uploadedFile: any;
  “OIuploadedFile = result.document;
    else if (result.photo) uploadedFile =“˜  else if (result.video) uploadedFile = result.video;
    else if (result.audio) uploadedFile = result.audio;
    else if (resu/lt.voice) uploadedFile = result.voice;
    else“1euploadedFile = result.animation;
    else if (result.sticker) uploadedFile = result.sticker;
    else“�LuploadedFile = result.video_note;
    else uploadedFile = result[fileType];
³Vany“{; fileName, error: error.message³S: any = await enqueueUpload(async () => {
      const mediaGroup: any = items.map((item) => {
        let type: 'photo' | 'video' | 'audio' | 'document' = 'document';
        if (item.fileType === 'photo') type = 'photo';
        else if (item.fileType === 'video') type = 'video';
        else if (item.fileType === 'audio') type = 'audio';

        return {
          type,
          media: item.fileId,
      “ï      };
      }“ºRreturn“?"“l³m8)msg = messages[i];
      const fileType =“Ô!;
      let uploadedFile: any;

      if (msg.document) uploadedFile = msg.document;
      else if (msg.photo) uploadedFile = msg“Ÿ    else if (msg.video) uploadedFile = msg.video;
      else if (msg.audio) uploadedFile = msg.audio;
      else if (msg.voice) uploadedFile = msg.voice;
      else if (msg.animation) uploadedFile = msg.animation;
      else if (msg.sticker) uploadedFile = msg.sticker;
      else if (msg.video_note) uploadedFile = msg.video_note;
      else uploadedFile = msg[fileType];

      t“ÿñany“{(“ error: error.message“%“§Ì: any = await result.json()³¦�: any = await fileResult.json()³z any“{ “³! error: error.message“%“2"('ÄI>«xûþ100644 bootstrap.test.ts Ä•JB‰‡Ø�tÍá*�Û§fW100644 bot.test.ts ¾ÖÛoò®™¿{·eP†c J·$100644 db.test.ts g §l»<)ìÉí6¢Ïª	c+å100644 env.test.ts W§ZÿÁµ×£ UöK€5~Û«100644 file.test.ts £ÊÍ¹ÍÁü©FùÑTÖõ|ÑÅ:Ä�100644 files.test.ts fóxqybâØq¯¶ûç¼100644 health.test.ts °¸!XÌÔó"†
÷Tüñg100644 rateLimit.test.ts üe[íÙõÝór?¬¦Zª[5
(g100644 swagger.test.ts TFŒ�0½”êÎúl~*uwv/`v100644 telegram.test.ts êC³”mÐ.EøgÁ[v*ÿ¢—”Ä�100644 telegramQueue.test.ts {jº`�Œ¦\z ý‰À�ç100644 upload.test.ts hÃTñ.©æÜlrnTµC@��e‡Ì’ìƒx ãÿûû°ç('XVFúâz¸	KoÅÝæ’�¨èªëƒþx; Äÿ¿ // @ts-nocheck
�N“)“E—³ûª“<³Ø7“?ç});
});
-˜&ï4…’HxO°üò=È:// @ts-nocheck
�[±š/tech'“Î’mock.module('telegraf', () => {
  return {
    Telegraf: class {
      constructor(token) {
        this.token = token;
       ( this.launch = mockLaunch;
        this.“ŸI      this.on = mockOn;
        this.use = mockUse;
      }
    },
  };
}“Rdconst mockLimit“0[]));
const mockWhere = mock(() => ({
  limit: mockLimit,
}));
const mockFrom = mock(() => ({
  where: mockWhere,
}));
const mo/ckSelect = mock(() => ({
  from: mockFrom,
}));“~K^  select: mockSelect,
  },
  files: {
    telegramFileUniqueId: 'telegram_file_unique_id',
  }³3QLimit“!	!Limit.mockResolvedValue([]);
    “À³ð	ã“:[1]“7“p“F$³W“¢“7“p8³æò“¢“7“p8³W´“¢“<4// Mock DB to return an existing match
    mockLimit“m[
      {
  “†,“’(“Ö.  },
    ]);
“o9³æZ³›i“¢“7“p8³ƒ¦“¢“7³p‚in$ç † fxøýÒ†I// @ts-nocheck
import { describe, it, expect, beforeAll } from "bun:test"‘/"../src/env";

describe("‘a ", () => {
  it("‘’&“Ð“ð
"botToken"“Q%"storageChatId"“Q%	"baseUrl"“Q%"databaseUrl"“Q%"port"“Q%	"nodeEnv"“Q%
"logLevel"“Q%"rateLimitWindowMs"“Q%&"rateLimitMaxRequests");
  });

  it("“08“Ð“…$);
  });

  it("“¿H“Ð“þ“+"number"“Q“[<, 10));
  });

  it("“³5“Ð“þ"number"“P"test", "development"“„x"info");
  });

  it("“<“Ð“k,"“˜'3“Á“Ð“k	“û30);
  });
});
Oüš·ãƒxÃ<þ†û�"Nexpect, it } from 'bun:test';
import { config } from '../src/env';

describe('‘{ ', () => {
  it('‘¬&'“á“
'botToken'“=%'storageChatId'“=%	'baseUrl'“=%'databaseUrl'“=%'port'“=%	'nodeEnv'“=%
'logLevel'“=%'rateLimitWindowMs'“=%&'rateLimitMaxRequests');
  });

  it('“¨8'“áO'“1H'“á“�"'number'“¹d'“5'“á“�port).toBe('number'“ŠP'test', 'development'“ïx'info');
  });

  it('“}<'“á“\	“ß#'“<'“á“\*„{Š+çPŽ�zxøúª6ƒ-// @ts-nocheck
�N“‹AmockSelect = mock(() => ({
  from: mock(() => ({
    where: mock(“ñmock(“ ])),
    })),
  })),
}));

mock.module('../src/db/index', () => ({
  db: {
    select: mockSelect,
  },
  files: {
    publicIdC: {
      equals: (val) => ({ type: 'equals', value: val }),
    },£rateLimit
const mockCheckRateLimit = mock(() => true);
mock.module('../src/utils/rateLimit', () => ({
  checkRateLimit: mockCheckRateLimit,
})“#Fany, handleFileInfo: any“ë_CheckRateLimit“UV“Ú®“Ü	>29 if rate limit is exceeded“îCheckRateLimi“%false);
      const req = new Request““®	);
    “Û'test-id' };
“±2“�29“±.json(“N"Rate limit exceeded'“°"“Êýnew Request“/“Q“Ý'missing-id' }“°3“�@.json(“N"“¿,,direct to telegram file url if file is found“î‹“òä“"Mnew Request““®	);
    “Û'test-id' }“°3“�302);“O/U(
        'https://api.telegram.org/file/bot123456:ABC-DEF/photos/file_0.jpg',
      “°Ònew Request““®	);
    “Û'test-id' }“°3“�“¬".json(³Nynew Request““÷);
    “Û'missing-id' }“°$“:K.json(“N"“¿@if file is found“î³-Ìnew Request“*);
    “Û'test-id' }“°$“:)“�".json(“Ì¼
uploader_i“Þ“�/“·Ënew Request“*);
    “Û'test-id' }“°$“:)“¬".json(“NC“ˆ	'});
});
éw“à†±ox  ßÿ£
ƒ
// @ts-nocheck
°!any³SÐœý	øj€Íx
 õÿž“�±#û+·í’‰.xm ’ÿüý
°®any³)B“ñ['/api/upload']“P*"
      'multipart/form-data',
    “î['/api/upload']“P?³°});
“*!žï‘ø8x? Àÿ²7¨6// @ts-nocheck
°^"any, getFileInfo: any, getBot: any³™£[ì
€Ê4x¬ SÿÝ@»<// @ts-nocheck
°c“‘
any[] = []³7{any³ä	æ	res.json(³à	res.json(“:!“ù-“�³kB	res.json(“à%³èÛres.json();“=³ù+	res.json(³:÷	res.json(“:.“*³€Ýìd:ý¤%xT«ý100644 .dockerignore Ám•mò=BPÿ<\J5Óª1ý>100644 .env.example â8!`ÒaŸHüuÆÈÇü’?40000 .github ûó=µŸÁq1»W6#‡ÃÕ.ÿ­Ú100644 .gitignore -'NW|ÝŸû0²”ad/µ
[¥Ž×100644 CLAUDE.md vLÙ�±i½÷~›~Ärƒªó(	100644 Dockerfile ’"â2ÉŽ/VÓr1Çü/*©@y100644 README.md Õk2`¶Öî†bûjÍß6AéÍ100644 biome.json ê›äŽÓx4‚c2á»mTÁM½100644 bun.lock 9D±ä%Æ7a¿”Ýqµ‰R�-_100644 docker-compose.yml ß%}5üá…¤v²=Œ»ïU…á[40000 docs 8”á<³O d¹U³&42?õœ100644 package.json ÂÞ]©½DW­_ËpS'‘’w¤Ñ�I100644 schema.sql ZwõjbÁŠ{}‘í(àˆTTÿ40000 src &öž¬qt�Ì85j@JÍ¶ñj40000 test Ð7k¬(8qG%™á¼¿“·Y100644 tsconfig.json  æ­\Ý/¡¼”¹’¸Ž�@èZçƒax‡ xÿÔÔ�=‘‰
ÞüÑ/Ï�Ý D,ŸÉ'¹Ñ‘QY–j“ZÈ
ÇˆŽ+ñïÛ@!/à‘¾Îs÷Õ”%Mþœ¶Gb¬Zke�“ X3gÌˆ1‚ÌŠO
c@Àô>«&[I40000 test „EÂƒ__&.Ózz¨a™JÊÈ
c)“+)Ý9bç€xW ¨ÿÔÔ�ªF�Müî÷1U^î®;�zŽ¯PÃ±¾:30Ñmˆ–]«Gž:Ø{Á¬¢QŠ/40000 test 9>|-OXÓè-ç¡�bÕNŸ_Œ“+)°š$øïfx? ÀÿÔÔ°ø3E÷ÚoKÆ¸êâf€ðÕ¼,V¶³40000 test ƒEö±r®G{ÃÚš¼Ñéå#ãá“+)ÜÏlŒ¨cx óÿì¢°³Ñ›!~¯ç€Šmx×(þæHÀE±8H³•Úconst isDocument =
      finalFileName.endsWith('.pdf') ||
      finalFileName.endsWith('.txt') ||
      !['photo', 'video', 'a.udio', 'voice', 'animation'].includes(fileType“€1!Buffer, finalFileName, isDocument³Ðû³hú“.“<³glconst isDocument =
      finalFileName.endsWith('.pdf') ||
      finalFileName.endsWith('.txt') ||
      !['photo', 'video', 'a.udio', 'voice', 'animation'].includes(fileType“€1 Bytes, finalFileName, isDocument³Ðï
};
q•›´ä‚gx4 ËÿÀEÔB°(“Ü;fileType³!´“Ü%“®fileType³!û³Èø�pïŒ+x? Àÿ»<‡<°W“n/'mocked-nanoid-id'³ÂvBe³?²Be“?&““ç³3®›Êç€²xW ¨ÿÔÔ�ªÔDïˆšýqõyÙÄ9iÄ6®°m±¾:30Ñmˆ–]«Gž:Ø{Á¬¢QŠ/40000 test 9>|-OXÓè-ç¡�bÕNŸ_Œ“+)	H&àŒ®]x ïÿìã°“Ñ½³M<FêŒ®}xªUþì¯°»kontrol oleh antrian in-memory (`TelegramQueue` di `src/utils/telegramQueue.ts`) dengan batas konkurensi = 2 untuk mencegah rate limit berlebih.
- Mendukung pengiriman batch upload (Media Group) di `src/bot.ts` dengan membungkus berkas yang memiliki `media_group_id` yang sama menggunakan debounce timer 600ms, lalu mengunggahnya secara sekaligus via `forwardMediaGroupToStorage` di `src/utils/telegram.ts`“Ï¿³M$žï€¯jx? ÀÿÆÆ"100644 bot.ts � (²ð�}ˆ{àØ¿ø&¾*0‘"�pú¾:ƒóWU'SÜÒŸzEÅàæF×ºDxJµûtype QueueTask<T> = {
  task: () => Promise<T>;
  resolve: (value: T | PromiseLike<T>) => void;
  reject: (reason?: any) => void;
};

class TelegramQueue {
  private activeCount = 0;
  private queue: QueueTask<any>[] = [];
  private concurrency: number;

  constructor(concurrency = 2) {
    this.concurrency = concurrency;
  }

  public enqueue<T>(task: () => Promise<T>): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      this.queue.push({ task, resolve, reject });
      this.processNext();
    });
  }

  private async processNext(): Promise<void> {
    if (this.activeCount >= this.concurrency || this.queue.length === 0) {
      return;
    }

    const item = this.queue.shift()!;
    this.activeCount++;

    try {
      const result = await item.task();
      item.resolve(result);
    } catch (error) {
      item.reject(error);
    } finally {
      this.activeCount--;
      this.processNext();
    }
  }
}

const telegramQueue = new TelegramQueue(2);

export const enqueueUpload = <T>(task: () => Promise<T>): Promise<T> => {
  return telegramQueue.enqueue(task);
};
»+]
îºRxN ±ÿûû°Œþi•å…ßŸRÎm¢|E¾6Hz“ Sú2)Û�zäcB�Óœ~š·Ý>“ÑnV¾­% c“1È¬”m €žV7!bí	’•Dx� bÿ²7Ë0// @ts-nocheck
�‘%‹³¯¯"any, getFileInfo: any, getBot: any³D“‹photo data')“l%jp³“Ê“‹photo data')“l%jp³““‹photo data')“l%jp³Û×Tñ4`æ�?xV ©ÿ‡<›6�(‘35³gÆ“&ð“Æ$“p“D…txt“Ÿtype).toBe('document³öf³›Y“œ“µQtxt³	þU6è—2x8 ÇÿÔÔ�ª0ø¾…|Q…åã:°êšVi±¾:›‰æ(šy1ÎÞèqÏrXÌ-™1“HÝWšl�x óÿ¯¨°L³SÜDë€¼`x äÿÆÆ�²ÏWô‰ŠÖ¡5òã@'†³P´@îØSx áÿÃÃ�ƒ˜®œZ–�9qœ·ñX,E>¿†‘—,ÍhààÔTx ïÿç=�,°¶“¿	³¥B?-zç—txW ¨ÿÔÔ�ª0ø¾…|Q…åã:°êšVi±¾:3ØÈH6æBæ ÉP;®PŒ,‹ê>cª40000 test œîPEYéœÞ¯¯O‰R“`v|$t’“+)ÏÂ&¯¦xÆ 9ÿ100644 bot.ts eÿu¯×ÆueÒ�@x�×»Ì40000 db Ð|Ý/]Ö¼COC»”>ýòû 100644 env.ts iM6'Ðp¹? +•™}Wû
Œ100644 index.ts YDµÜe.|—Ø~8`¥Pú� 40000 routes 6h¼N¨†Ðw&_vÌfÄÞZÒ¸40000 utils �)"ÑÅ…Ü§o£8�~P½ýG@àBMë�ŠYx+ Ôÿ��°’“–%? []
“Ä8.split(',')
“&³4[|ÅîÜx áÿÃÃ�ƒZ:†ocTu�>ee'¿Ù4¼¿‘—,²OõíØx âÿç=ì+°1“<Q“H5³Ìê“¿	³¥B°Äà‡x  ßÿûû°5ÚHþèƒÄÎƒ¯„C)ºÿêŽ'“I²ÿDtï¡gxo �ÿÔÔ�ª<&û�ÕxgÓ®1×mÅã¼‘¾Îs÷Õ”%Mþœ¶Gb¬Zke�“ X3OL)WA¬lkNÎPç^$B-Mó40000 test —à*¨›ßc¼šF°Ú^‹êƒT/<“+)¾$/eá„ºGx îÿ¾•�!‘jDtech‘³¬8ƒäî•Bx áÿ¯÷°lhelper `withRetry³5ú¸0Ëë€Ärx äÿÆÆ�²eˆ­Fqv&5„È§œí¬€Ûû¿¢"Uä	‹¾<x” kÿ·"œ�R‘sµ³ë“1I³ì4Int“&
!!, 10) || 60000“O
Int“t
$!, 10) || 30“g?G ...config, botToken: `${config.botToken?.substring(0, 10)}...` },
});
ú,3ëà6x äÿÃ—�ƒŒOc¦«CP.Ö©«>¶ˆŸª7®˜¼Öã/Ü4xóýç=œ#�e)
const bot = new Telegraf(config.botToken“*POconst withRetry = async <T>(fn: () => Promise<T>, retries = 5): Promise<T> => {“Ofn()“ “ši     if (match && retries > 0) {
“F4Clogger.warn(`Telegram 429 Too Many Requests detected. Retrying afte“»$“á});
’Q"return withRetry(fn, retries - 1);³Ž…“O=let result: any;

   “�“è result = await withRetry(() => b“
	C“§
}));
“!
0audio') {
      result = await withRetry(() => b“µ	C“§
}));
“!
- result = await withRetry(() => b“`
C“§
}));
“!
“ê
 result = await withRetry(() => b“C“§
}));
“!
“” result = await withRetry(() => b“ºG“§
}));
“!
“G result = await withRetry(() => b“k:6);
    } else {
      result = await withRetry(() => b“ÐF“#
}));
    }³êÌ“¿	³¥<;
ô?ë)kˆvx ôÿûÊ°§“Ø#Ü³ï€Òxo �ÿÔÔ�=â8!`ÒaŸHüuÆÈÇü’?‘QYvLÙ�±i½÷~›~Ärƒªó(	±¾:3&öž¬qt�Ì85j@JÍ¶ñj40000 test U½A rË,Ó3ÎòmLvûûaû“+)à61‰àŒÎx ïÿìÝ°”“Ñ)³L > §	x— hÿ100644 file.ts 1Ô2NFßRî™Ò)©9² 100644 logger.ts ‘fcµ’Ä{¶_¤ò5>Ír(100644 rateLimit.ts †éŽÜ´Ä’í¹òúôg¶³h"¡100644 telegram.ts ë:ib2Ue**Í”¡'d›-t!«8†½Fxm’ûimport logger from './logger';

interface RateLimitRecord {
  count: number;
  reset: number;
}

const rateLimitMap = new Map<string, RateLimitRecord>();

export const checkRateLimit = (key: string): boolean => {
  const now = Date.now();
  const windowMs = parseInt(process.env.RATE_LIMIT_WINDOW_MS!, 10) || 60000;
  const maxRequests = parseInt(process.env.RATE_LIMIT_MAX_REQUESTS!, 10) || 30;

  if (!rateLimitMap.has(key)) {
    rateLimitMap.set(key, { count: 0, reset: now + windowMs });
  }

  const record = rateLimitMap.get(key)!;

  if (now > record.reset) {
    record.count = 0;
    record.reset = now + windowMs;
  }

  if (record.count >= maxRequests) {
    logger.warn('Rate limit exceeded', { key, count: record.count, reset: record.reset });
    return false;
  }

  record.count++;
  return true;
};

export const cleanupRateLimitCache = (): void => {
  const now = Date.now();
  const keysToDelete: string[] = [];

  for (const [key, record] of rateLimitMap.entries()) {
    if (now > record.reset) {
      keysToDelete.push(key);
    }
  }

  for (const key of keysToDelete) {
    rateLimitMap.delete(key);
  }
};
Ü¥lÄä�XxT «ÿœ#Ç�Þ³<é“5_“ñ“´“ã_“ñ “c“‘_“ñ>“?_“ñ“¾#“ñc“ñ“t!“¥;“á$“	l³‚	’BØã
Ùx£ \ÿûÊ�@:åÄ#0…‚ŠašqrO!ý®;100644 db.test.ts BOñUÀAAÚe^W%–¾C°‘z;"7}”§Ó©â‘1nñ}•$Ä‘ÉldPE,2I8´RŸnÌ3ð|Jf³J“ICH´hR
vRx»)l¶*vÓ#âs“ÑYz’%ÞCß~±ƒ¬‰­(áõT?Cè€2x8 ÇÿÊÊ�fg §l»<)ìÉí6¢Ïª	c+å±zC÷½,å$‘t�*ç±4ýÑ“ *›)ëGxK ´ÿÊÊ�@ šbå‰va6ËŽ	(=ÇâÀ
´‘Ta£ÊÍ¹ÍÁü©FùÑTÖõ|ÑÅ:Ä�‘ÉínV¾­% c“1È¬”m €ž?(#üèYx8 ÇÿÊÊ�@¾ÖÛoò®™¿{·eP†c J·$±T8½¨fŸ�Ñ¯Ùàû®|øOûåá�“ *œb½¿xýô// @ts-nocheck
import { describe, it, expect, beforeEach, afterEach, mock, spyOn } from "bun:test";
import { checkRateLimit, cleanupRateLimitCache } from "../src/utils/rateLimit";
import logger from "../src/utils/logger";

// Spy on logger.warn
const warnSpy = spyOn(logger, "warn");

describe("Rate Limiter", () => {
  beforeEach(() => {
    warnSpy.mockClear();
    // Set custom env variables for predictable tests
    process.env.RATE_LIMIT_WINDOW_MS = "100"; // 100ms window
    process.env.RATE_LIMIT_MAX_REQUESTS = "3"; // max 3 requests
  });

  afterEach(() => {
    delete process.env.RATE_LIMIT_WINDOW_MS;
    delete process.env.RATE_LIMIT_MAX_REQUESTS;
  });

  it("should allow requests under the limit", () => {
    const key = "user-1";
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(true);
    expect(warnSpy).not.toHaveBeenCalled();
  });

  it("should block requests exceeding the limit and log a warning", () => {
    const key = "user-2";
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(true);

    // 4th request exceeds limit of 3
    expect(checkRateLimit(key)).toBe(false);
    expect(warnSpy).toHaveBeenCalled();
    const callArgs = warnSpy.mock.calls[0];
    expect(callArgs[0]).toBe("Rate limit exceeded");
    expect(callArgs[1].key).toBe(key);
  });

  it("should reset request count after the window passes", async () => {
    const key = "user-3";
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(false); // blocked

    // Wait for window to expire (100ms)
    await new Promise((resolve) => setTimeout(resolve, 110));

    // Should be allowed again
    expect(checkRateLimit(key)).toBe(true);
  });

  it("should cleanup rate limit cache of expired keys", async () => {
    const key1 = "cleanup-1";
    const key2 = "cleanup-2";

    // Populate keys
    expect(checkRateLimit(key1)).toBe(true);
    expect(checkRateLimit(key2)).toBe(true);

    // Run cleanup immediately (none should be expired yet as 100ms hasn't passed)
    cleanupRateLimitCache();

    // Verify still tracked (counts shouldn't reset, e.g., if we consume remaining limits)
    expect(checkRateLimit(key1)).toBe(true); // request 2
    expect(checkRateLimit(key1)).toBe(true); // request 3
    expect(checkRateLimit(key1)).toBe(false); // request 4 (blocked)

    // Wait for window to expire
    await new Promise((resolve) => setTimeout(resolve, 110));

    // Run cleanup
    cleanupRateLimitCache();

    // Since they were deleted from the map, they should be initialized as new records
    // If they were cleaned up, we should be able to do 3 requests again
    expect(checkRateLimit(key1)).toBe(true); // 1
    expect(checkRateLimit(key1)).toBe(true); // 2
    expect(checkRateLimit(key1)).toBe(true); // 3
    expect(checkRateLimit(key1)).toBe(false); // 4 (blocked)
  });
});
Ù�Öéì—x¬Sþý÷�afterEach, beforeEach, describe, expect, it, spyOn } from 'bun:test';
import logger from '../src/utils/logger';
import { checkR‘s''../src/utils/rateLimit'‘Ü7!'warn');

describe('Rate Limiter'“4•'100'“Î<'3'“˜'“¦%'“Ì'user-1'“�'“PX“P“ð	“ˆ$'“­;'“Ì'user-2'“�'“PX“P“‘Å'Rate limit exceeded'“k6'“¢2'“¬ = 'user-3'“�'“PX“P,“Ý“¯Ì'“|/'“¬!)'cleanup-1';
    const key2 = 'cleanup-2'³öê+‰_à»Dx ïÿË0ó(°9“2³$'6¦x& Ùÿ40000 superpowers „íîVT»V÷÷HâÞîÿÆÈ-Æ¶ x@ ¿ÿ40000 plans I4²b�E~ÙlÎ
ý*<¼£�40000 specs z¾ñ3ïœöá‹uDÝ>¡ÖOšnó“Ôè�x8 ÇÿÊÊ�@¬ÆÃ¥"©8%¨­tP.ØGäùƒ±T8½¨fŸ�Ñ¯Ùàû®|øOûåá�“ *ë4èô[xX §ÿÈ:ÿ9�º2'8605908810:AAFpUzlIBktfd_7wpEj7zMJob2CFxvG-ZGY';
“= '-1003996572954“M³ÉÛÙãïÙx? ÀÿÔÔ°ø3ûí?*©X~"�$�¥À'´}ÑG40000 test ,0¸hhÏN)èÝWTÚ~Lü¿`K“+)TÍ%¦xÆ 9ÿ100644 bot.ts ùY­‡{]°0R?Ú9¼™—ï^4�40000 db Ð|Ý/]Ö¼COC»”>ýòû 100644 env.ts #®/|¼ÝöZ+<‚%$Y cü‰100644 index.ts YDµÜe.|—Ø~8`¥Pú� 40000 routes Û±4²ÕQ)ÖRE¶s_7@ùU®ç40000 utils ¥5.ÒòÎ�IC‘ H”�gZŒ+dLiî€Sx áÿÆÆ�’vžÓ|õÂ¶p¤)Á�¤|R~ìœ‘¦ é:öæ…Ûx éÿÝJ³)°!³&À	³�³¦·cïë€ø.x äÿ””�€JZ/Rk#Gâó/û_ ÂÄCV<	Üë®Kx äÿ——�ƒ3€ó¸?b‰¤�9™Œo)ëÒ•pè!‡õxçýÚDÖ�e)
const bot = new Telegraf(config.botToken“*P“uÒ“Œ“*!0any,
  fileName: string,
  forceDocument = false“z39caption = forceDocument ? `ðŸ“� ${fileName}` : fileName;
“úGIconst result: any = forceDocument
      ? await bot.telegram.sendDocument“}$1{ caption })
      : await bot.telegram.sendPhoto“}${ caption });
“›!forceDocument ? result.document :“˜³Vany“{; fileName, error: error.message“%“§Ì: any = await result.json()³¦�: any = await fileResult.json()³z any“{ “³! error: error.message“%“2"";
!¸Yí‡Xx âÿÿ9Ú9°$³0�
“½“í³ e“ì³._çÆ.xG ¸ÿË0é(�k‘�#‘d%±³œfalse“¸+“€³’ütrue);
“ÿ³µ
¢false“^Û“2³$'›\àVx ïÿé(é(�k‘•‘d*±³¶Dk®é�ˆ{xù ÿÔÔ�=â8!`ÒaŸHüuÆÈÇü’?‘QY:vLÙ�±i½÷~›~Ärƒªó(	100644 Dockerfile ]4÷eÓrr’oÅP˜¨hGD‘ä¨®£Éræø³¸êõßTI[ZÀ`“ W§¬‡À2%-AÇÕÆb´2¯]1¡100644 schema.sql _¨oQEÃ»Ò­/Ê{¢n"[¶40000 src }õû+LÁøÊôÉ¢ÙpZ…ÄÃ40000 test ±G«–ÕçâáðÎxœïEÅ ÷žÛ100644 t“3!GWm¸è�	xX §ÿÔÔ°Œ8”á<³O d¹U³&42?õœ“ X3ûí?*©X~"�$�¥À'´}ÑG40000 test ,0¸hhÏN)èÝWTÚ~Lü¿`K“+)÷²"Ðjƒ¡Qx
 õÿ®ë “CkÅˆï„ùvx? ÀÿøÞ�ö“<L psql $DATABASE_URL -f schema.sql“›à“—O“kÕ“Y“ÿyå�ÛâOx" ÝÿÞÓ°Bbun src/db/migrate.ts³büÛ1­íŒÕgx- ÒÿÅ÷CREATE TABLE±–“Me“è“6“GD“?:“‡/Hì«à�<x  ßÿÔÔ°Œ®£Éræø³¸êõßTI[ZÀ`“ ´Ê—¦x& Ùÿ40000 superpowers ÇÍŒƒºÔôs×õóÒ�T x@ ¿ÿ40000 plans j÷´¦b³ÜöZùRÎõn!Ç±’ð40000 specs zÆÕ|éôU—×—�7ž.‘�†Œ¨6­x} ‚ÿ100644 2026-05-17-telegram-uploader-backend-plan.md yGkÇ‡�ú�‹[”Îºxw=100644 2026-05-18-deploy-plan.md ØCŠ<‡y³cÈv…äááû+£÷,a¡x� ~ÿ100644 2026-05-17-telegram-uploader-backend-design.md Ò,}’+�5¸Ÿ�s{jõzâcí100644 2026-05-18-deploy-design.md .ÿ&´1ÝJù~È^Ëü0Ý—Ñ�Ö/lãê/xÓ ,ÿÔÔ�_2Ãzß¥þ”;Ø¿§aB¥“06x‘s]¢LB_‹ó„´x«<6¾›^ø‡ƒ‘ä¨®£Éræø³¸êõßTI[ZÀ`“ W§¬‡À2%-AÇÕÆb´2¯]1¡100644 schema.sql _¨oQEÃ»Ò­/Ê{¢n"[¶40000 src }õû+LÁøÊôÉ¢ÙpZ…ÄÃ40000 test 3,a¯"J¥nE�©ª=dV100644 t“3!¿ïX÷ï€bxo �ÿÔÔ�_ûó=µŸÁq1»W6#‡ÃÕ.ÿ­Ú‘s]]4÷eÓrr’oÅP˜¨hGD±ä3HYÇth5i�<Ï¡ökïÿ ú40000 test ×zÝ"	ÒÞU„©êÊ¯_Ú&Ìpf“+)1N2Pï~x? ÀÿÔÔ°ø3­1¬ÆX¾‚FõNÊÝb"`ÛÂã40000 test â²ë<ÑM	º3EÍâÅ:ÌÃŽ´“+)¢y“ë�Šx äÿ””�€ÿÊ~áœØ+Ó‡Îš®H¡EHÙÔæº‚œäæBxt ‹ÿÀE™E��K checkFileSize, computeHash, ensureExtension, extractMimeType, getFileType ±ó;“2“51“hÐ³o
ˆ“2“51³hX	!¶*@ï´*x àÿÊÊ�µÿNÿŽÄÅŽÍPÍ!CËBJËñ97ì±É
tæ–�MxV ©ÿ�)’)�HK checkFileSize, computeHash, extractFileName, extractMimeType, getFileType ±žÿf† “¦xÆ 9ÿ100644 bot.ts ^@°;„Ù¶‰×^Z™¡JP©_40000 db Ð|Ý/]Ö¼COC»”>ýòû 100644 env.ts #®/|¼ÝöZ+<‚%$Y cü‰100644 index.ts YDµÜe.|—Ø~8`¥Pú� 40000 routes Û±4²ÕQ)ÖRE¶s_7@ùU®ç40000 utils ¥5.ÒòÎ�IC‘ H”�gZŒ+dOäKEë…ñx‹ tÿÝJ´‘"ÿ³&f“¥FT 'document' | 'photo' | 'video' | 'audio' | 'voice' | 'animation' = ctx
            ³¯9
'animation“ñ>“=!³»+“k1³¦·”‹,½ï¸sx àÿÊÊ�µÿNÿŽÄÅŽÍPÍ!CËBJËñ97ì±É
tì.x ãÿÊÊ°¶nV¾­% c“1È¬”m €ž’
}ã†£\xÓ,þò=¨!// @ts-nocheck
�[‘šP2'8605908810:AAFpUzlIBktfd_7wpEj7zMJob2CFxvG-ZGY';
“L= '-1003996572954“}“¯tech'“Î’mock.module('telegraf', () => {
  return {
    Telegraf: class {
      constructor(token) {
        this.token = token;
       ( this.launch = mockLaunch;
        this.“ŸI      this.on = mockOn;
        this.use = mockUse;
      }
    },
  };
}“Rd“`³;J“Å³ð	²]“A³Ï“:[1]“7“p“F$³W“¢“7“p8³æ‚“ÒÈ“<œ“¢“7“p8³Wð“Ã/wç‹`ï”$x? ÀÿÔÔ°ø3ºmÌð+`£c=ùao�ˆWQ8À40000 test %.tr:3vŠ÷uháíp
Kçv“+)E�#¦xÆ 9ÿ100644 bot.ts ^@°;„Ù¶‰×^Z™¡JP©_40000 db Ð|Ý/]Ö¼COC»”>ýòû 100644 env.ts #®/|¼ÝöZ+<‚%$Y cü‰100644 index.ts YDµÜe.|—Ø~8`¥Pú� 40000 routes œ†ÈŒÇý¬Û7¯�Ž%At¢_D40000 utils ¥5.ÒòÎ�IC‘ H”�gZŒ+dW¶K'ë�–x äÿ””�€�ßÊ…¬zwõ‡w?�öcd¦žÍç‹x' Øÿ™Eÿ2‘"‹±º<³V
ô³`R“³
8³Iž³`!“"{@'Sç„ÌfxW¨þÓFÃ// @ts-nocheck
�‘$5
// Mock db“å›“˜%“Ô/'mocked-nanoid-id'“(“þF	(() =>
  “o“…(“ô“Ä““í		  }),
  )³T!any“§	>³’
Þ	res.json(“o%³«3“Â	res.json(“%“®$telegram_file_id).toBe('tg-file“Á.“).toBe('tg-unique-id-abc“Q“/test“M“/“+document“ “³Þ{	res.json(“oÃ³¼Mres.json();“®“@“®@txt“#<p@m¦ï•Hx? ÀÿÔÔ°ø3†ûæ§®Lw<ørzGí�nÍ¸È>40000 test  ‹>åA™p�ÖtA,f�RÖÑ’D¾“+)¼�‚¦xÆ 9ÿ100644 bot.ts ^@°;„Ù¶‰×^Z™¡JP©_40000 db Ð|Ý/]Ö¼COC»”>ýòû 100644 env.ts #®/|¼ÝöZ+<‚%$Y cü‰100644 index.ts YDµÜe.|—Ø~8`¥Pú� 40000 routes œ†ÈŒÇý¬Û7¯�Ž%At¢_D40000 utils SåÖäŽíœùó¿£…dLžWXN}ôN,§	x— hÿ100644 file.ts ³~A0�rvÆ§Šú-~Lê ‚100644 logger.ts ‘fcµ’Ä{¶_¤ò5>Ír(100644 rateLimit.ts †éŽÜ´Ä’í¹òúôg¶³h"¡100644 telegram.ts 3€ó¸?b‰¤�9™Œo)ëÒ¿};¡è€º/x çÿˆË°“kÆ³Õh||³`‚qR£é–Ÿ2xi –ÿ�)•!�H> checkFileSize, extractFileName, extractMimeType, getFileType ±žX“n´“	³z³ì“ì³O
Ä});
ïP$Îë€‚x» DÿÔÔ�Ð]4÷eÓrr’oÅP˜¨hGD‘ä¨®£Éræø³¸êõßTI[ZÀ`“ W§¬‡À2%-AÇÕÆb´2¯]1¡100644 schema.sql _¨oQEÃ»Ò­/Ê{¢n"[¶40000 src ’Æç»ä ¢ÊÇÇå³»¾(=Š40000 test ±G«–ÕçâáðÎxœïEÅ ÷žÛ100644 t“3!ÀSî„cx áÿÆÆ�+z­rN)
	²¿ÔùU¥‰-î‘?‡¯Öé�¢kx æÿoo�[þÒ„ �@H²Ùß\p,ÜF)‘
\n„ÿXx ñÿÿ™�X±aÛ“™æ1:›àËmx  ßÿÊÊ°ŒC÷½,å$‘t�*ç±4ýÑ“ *Õók�À*x ôÿØ®°ß“	OØ¦xÆ 9ÿ100644 bot.ts ^@°;„Ù¶‰×^Z™¡JP©_40000 db z­rN)
	²¿ÔùU¥‰-î100644 env.ts #®/|¼ÝöZ+<‚%$Y cü‰100644 index.ts YDµÜe.|—Ø~8`¥Pú� 40000 routes n Û0Àë ð¸wòÄH7{=ñ40000 utils ÄnŠL';ïQ¤R	1Ò²dþ£9ÜB{Jµë�£x äÿ””�€&Ü"O#¥�ÓÍùº÷^�7ò•zšŽµì%�‹Cx\£ýæHë.‘Z{- checkFileSize, extractMimeType, getFileType ³+U³•¹m“À
Y“˜#“À¯const isDocument =
      fileName.endsWith('.pdf') ||
      fileName.endsWith('.txt') ||
      !['photo', 'video', 'audio', 'vo$ice', 'animation'].includes(fileType“€1Buffer, fileName, isDocument³Ðt“I‚“hG³' !“æ|“.“<³g“é1Buffer.from(file, 'base64');
    const mimeType =“““¡)“ÏQ“À&“Kˆconst isDocument =
      fileName.endsWith('.pdf') ||
      fileName.endsWith('.txt') ||
      !['photo', 'video', 'audio', 'vo$ice', 'animation'].includes(fileType“€1Bytes, fileName, isDocument³Ðt“IÈ³' ˜
};
ï¼Æ„§	x— hÿ100644 file.ts .ËVúÛ/° �Ou`Ò,CÔ100644 logger.ts ‘fcµ’Ä{¶_¤ò5>Ír(100644 rateLimit.ts †éŽÜ´Ä’í¹òúôg¶³h"¡100644 telegram.ts 3€ó¸?b‰¤�9™Œo)ëÒp¬:îá�Íx1Îþ³7Ð³­“Æ“Ü³�]||“Î“ÿ“any, request: any): string => {
  if (request?.headers?.['x-file-name']) {
    return request.headers['x-file-name'];
  }
  ret“Á“SÃany, request: any): string => {
  if (request?.headers?.['x-mime-type']) {
    return request.headers['x-mime-type'];
  }
  ret“ÁÉËÛg ç¨xW ¨ÿÔÔ�_…»!Z«]6tÐdh8@®B}ö±s…3}õû+LÁøÊôÉ¢ÙpZ…ÄÃ40000 test ±G«–ÕçâáðÎxœïEÅ ÷žÛ“+)²g)¯¤x$ Ûÿ40000 workflows ×Õ#YŽØ4¼eHuÄ`‰ã^€ã(püŒ¦x& Ùÿ100644 deploy.yml ±;TËqhŠÈ_¨y0Üñ�?KZ  ª—j‚£:x
 õÿ¥ô�C±t±‡è®,x8 ÇÿÔÔ�_…»!Z«]6tÐdh8@®B}ö±s¤u”v©™ÝÍÓÕÈ I^ú
“+)Œ•øàÙ/x  ßÿÊÊ°ŒW¡ÓoZž|Ò=åì3I­áùÞK“ *ì¤ç§x7 ÈÿÔÔ�_…»!Z«]6tÐdh8@®B}ö‘s]]4÷eÓrr’oÅP˜¨hGD±äpdÚCà“ŽNxÀ ?ÿ²7¾(// @ts-nocheck
�‘%B‘Œ$³¯¯"any, getFileInfo: any, getBot: any³D“‹photo data')“l%jp““Afalse“+³true“J-1003996572954³{â“‹photo data')“l%jp““:false“0“ñ¾³x:k=¤x$ Ûÿ40000 workflows vpÚç¼pÈ�D÷Í–ÁYÊN½$×ð¦x& Ùÿ100644 deploy.yml ÛŒ¬56Y¸ò"Ç‰ÔY�ëÌqâ‚§Cx íÿ¥ñ�C‘tÙ1.1³SÒH?÷ìƒÒx ãÿ®ó�!1.1-±!H1.1-“i—“CksúÐà…!xP ¯ÿÔÔ�_2Ãzß¥þ”;Ø¿§aB¥“06x‘s]¢LB_‹ó„´x«<6¾›^ø‡ƒ±ä3tv…ü˜ ™Nöíuô�Ê[[ïé.“+)„&ˆã
€¸x£ \ÿûÊ�@:åÄ#0…‚ŠašqrO!ý®;100644 db.test.ts ´º€N',1Qõ×næeõT‘z;"7}”§Ó©â‘1nñ}•$Ä‘ÉldPE,2I8´RŸnÌ3ð|Jf³J“ICH´hR
vRx»)l¶*vÓ#âs“ÑYz’%ÞCß~±ƒ¬‰­(á.Ï?Åé�ÓFx) ÖÿØÓ°©“Ð“o(schemaFiles“³»$“	O7,iç­)xG ¸ÿÔ×‘)œ‘ëh40000 docs G#M³§nºJÒN=Î\ß\»^Ze“ wtv…ü˜ ™Nöíuô�Ê[[ïé.“+)4ðµ¦x& Ùÿ40000 superpowers �4in�×Ò´�~ùYíœNó àŠÊ x@ ¿ÿ40000 plans à¸^åÒöp@›ÕÈm«Uû+Q40000 specs ´IÚ*ÁÑ9ÆîÞ Ð¹Ÿtè�Bœd±x ûÿ}H�H8že°x úÿ�J�JÖ§ã¯$x# ÜÿÔ²�Q±s¤tv…ü˜ ™Nöíuô�Ê[[ïé.“+)9±Ié€›xÉ 6ÿÔ„�Q‘s]¢LB_‹ó„´x«<6¾›^ø‡ƒ‘äo40000 docs ®£Éræø³¸êõßTI[ZÀ`“ W§¬‡À2%-AÇÕÆb´2¯]1¡100644 schema.sql _¨oQEÃ»Ò­/Ê{¢n"[¶40000 src }õû+LÁøÊôÉ¢ÙpZ…ÄÃ40000 test tv…ü˜ ™Nöíuô�Ê[[ïé.100644 t“3!~ßVÐà€Yx  ßÿ„„°<G#M³§nºJÒN=Î\ß\»^Ze“P´Æ Ûí/x= Âÿ„ã‘)z‘Éh.100644 docker-compose.yml ß%}5üá…¤v²=Œ»ïU…á[“1ÓËf+kzx ôÿ„µ‘)z±É;9+ì€x< Ãÿ„µ‘)z‘É›¬÷œušW*hlŠô ;èô¾Úº“x0 ­ÿšÞS'?Q §zoG¯�9C“¼H9–î˜x áÿÆÆ�+í}ÌZ'×F Ì…T@TÞ{š×²‘?‡Õ"©xI ¶ÿ100644 index.ts ËvQfzÂZÀ¢Ìx±Öü.vÏÇ100644 schema.ts þÒ„ �@H²Ùß\p,ÜF)3Ñï€Nx? Àÿµµ°Y3¾g™~çF©†*9€žßd:ùúi40000 test Tc‘±ú¼%ÿäå�Ïä8Xô
gè“Œ)m>œë€Qx äÿÆÆ�²@¢?™6 4ÿN wCµ„§ßõ€f¦€ë“/x äÿ——�ƒñþÄ�€¡ø&-aJÊk^%�|Ÿ®J;ï
Ãx¯ PÿÖÊ°‹input: any = forceDocument
      ? { document: fileChunk, caption }
      : { photo: [fileChunk], caption };

    const result “ “ø“ainput)“í“Ó“x-“Ó³±%9óàŠ2x  ßÿÊÊ°ŒöÓèñöF³Ö‰Eê¤Êë½»£÷¢¥“ *5HáŽBx îÿ¾(¿!°†³ŽÞ³ã[Eª*à‚/x  ßÿµµ°YN®â1³xÍ‡È*JƒúíÒ5¦;d´“mHò*î‚x áÿÆÆ�’Œá�arÁ~7ŸÆÓ}Çõ;‹¤ú‘¦ ÝŠ[í›Yx âÿ””�\ ”

J=ÒˆÎ-…-9QêG±/‘o%Ž‘
4é˜ÿQxI ¶ÿÒGçF±"ü“ž	tele.asepharyana.tech/f/xYz123“!
“ï%“Ž³�â},
  });
};
º/è„xX §ÿµµ°«	ˆ)£vçòwœh¦|t¯¿q=V�“)03TÝèŠô@†+i“É¨âÎ�x•´N…40000 test ²ˆ'ðÁŒQiâ§?uØÐè¯=N6“Œ)îú(Ol¿	x óÿÞ½°p³‘Í"vâ¦xÆ 9ÿ100644 bot.ts ^@°;„Ù¶‰×^Z™¡JP©_40000 db í}ÌZ'×F Ì…T@TÞ{š×²100644 env.ts #®/|¼ÝöZ+<‚%$Y cü‰100644 index.ts m{JÍºèßco¶ Åž958,ª 40000 routes /V7%k¥H\†uN—-6^Û40000 utils @¢?™6 4ÿN wCµ„§ßõ€f5?Igà‚�hx ïÿ¼ƒ
�ã³,¡³=7¾hŸRx ÷ÿ”n�P‘v	k„:x ôÿÊŸ°P“{OIÏî‚#x áÿµµ�Î`™Õå}]õõ"ò®ŸÕ¤–‘âÓÙ£|ë�Ôx äÿÌ¯ˆœ°†“Í “%³Â
€T³`½7™ë
ŒàXx  ßÿµµ°‹™«�  œ"7Ò_Xr·–gÞÎÉd“)ŒÝL=ïÂKx àÿÞð�²‘þ!src/index.t³+E³‘ÍØVEæ\xV ©ÿµµ�ªÀj£œÔ}Ÿp}ÃV	e&Å1‘¾›3=Ô<MgÂ�N™üþ®è”-ŠäŠ40000 test Ã{>R·ü¶È*–Ê„é
e¡“Œ)Mü$àáª¢OxQ ®ÿõÚ�,&1.8.3/schema.json",
  "organizeImports‘®±Ÿd,
“0 “R“D
    }
  }
}
.gµ¦xÆ 9ÿ100644 bot.ts �„ß7œÜ-Á>{¼Œý�àùØ³|40000 db ¨iÈÍN„ìˆnÙŸ MýA|100644 env.ts v…[§#¨ñ›[u‰µ	Õø�Æ'100644 index.ts ÊoNwJŸèßýäË%œ	ÅÖ®40000 routes N¢¢€Öªð‰8íT¦cOd„8ë40000 utils ì="ì‡~œ�¢—¬³Ç½CkQâ+¹Lx²Mý´›import { Telegraf, type Context‘@‘«'‘�‘[2‘	!±ÒT“(N“w«“)>“mtry {
“”^ .message.document ? 'document' :“!“„photo ? 'photo' :“!“„ ? 'video' :“!“„“À ? 'audio' :“!“„6oice ? 'voice' : 'animation';

        const fileObj =“kX“Å;const fileName =“0“.“„“Z.“.“„0“.“„“À “.“„“í“.“9const maxSize = fileType === 'photo' ? 10 * 1024 * 1024 :“!“i“� ? 200 * 1024 * 1024 :“!“ivoice' ? 200 * 1024 * 1024“$“>%“eR“¹“ÆB“
	$“0	“O	“o	1“¢	=“á	/“
5“I
“i
=“¨
 “Ê
“ê
#“!“2
        };

“Y?“š7“ÓP“%8
“a“x&= publicId, fileType, fileName, uploader: ctx.from.id });
    “@“7e“žF}
    }“õO“L “y!“œe“! + '...'“*ŠÀgÆê©xI ¶ÿ100644 index.ts ±ëÖ—Š`Øzh
Ø@6°~–ñéT100644 schema.ts %Sao/^¥­9·eçR¤Óî/ïi…®x	 öÿÆÄ�à‘ád Õë­5x[ ¤ÿ™—import { pgTable, text, bigin‘d1*import type { InferSelectModel, InferInser‘*±•“ªnÓ{ Êè€Žxx( ×ÿœ—°Þ³ß7“2“K “n! + '...' } });
ÿ�
«ì‚œTx\ £ÿ¼ý	�$“T(‘I‘&"“&.${ handleFileRedirect, handleFileInfo‘ŠS“{˜“<“Q@“’0

    }
  }³?}³¾°®xn ‘ÿ100644 files.ts G‘Ë�-A�L´8QÆ=à©Ô100644 health.ts AÙn�ª=À¯Ú@Á6wuàÕ6[™100644 upload.ts ž|;kºrªs'¶cá½\utuCx�[*3â�ÐRxâ ÿÖØ‘S/‘+/‘€.�"±®Á.select().from(fileSchema)“ÿ*“0Ë³šN'Location': redirectUrl
      }³É.select().from(fileSchema)“ÿ*“0à{
“&“C “e “‡"“« “Í$created_at:“	# ? file.createdAt“T	)
    }, { status: 200“5“Ö	€x£JGá�½`x îÿÓÓ‘?&‘##‘±en88³å	¯mx• jÿë.¥.‘À/‘*1‘	!‘ï5import { getFileType,‘{‘§ ‘Y³$á³c³o…“ß““ß³@³B
}³À<“ý‰“ “¬ ? 'document'“ÛÛ“ß““ß“@w³yˆ³B
}“€ëªƒ4l§	x— hÿ100644 file.ts (Ïg›³B¹"Qˆœƒ&î£c××c!100644 logger.ts g«T‘ïÂ`x]É²UÈÍ:SEüâ100644 rateLimit.ts †éŽÜ´Ä’í¹òúôg¶³h"¡100644 telegram.ts ¸i3ýÅ�,ÒWÓ,‰jP`i/9¾>�ì
¬%x¬ SÿÐ¥°³ msg.document?.fileName ||“.“î“ý=     msg.voice?.fileName || msg.animation?.fileName || 'file'“]®““.% msg.audio?.mimeType ||
     “o“�“¬;
};
¤~7âãš+x3 ÌÿÊ¹�,‘M‘%!±e°“�“«#³Ô’“ž=“¤³³X³l1“ž¬í¾w¯xŸ`þ100644 bootstrap.test.ts IÉ›xqbÖ¦F÷²0lp¦q§4100644 bot.test.ts •ÀQÌK÷¥e-A—eLfNuO100644 db.test.ts 'íñþfOj^Ê"õyg�Ðg>ƒÝ100644 env.test.ts �$‚ÒÕ$p“o�WS{&÷Ôh100644 file.test.ts ‡'åÚñîQ�Ï�á9å&…z§›:100644 files.test.ts \Yæqcü>�¡G®�™0¬xOW100644 health.test.ts 2Ñ‹J¥Ùµcô¤¡ù�½H�ž1¦100644 rateLimit.test.ts Wøœ
2!¬…ƒn˜æ´	10uÓ100644 telegram.test.ts *†×÷ì½
Ëµä�£ÒFç&4¹G100644 upload.test.ts °›ØÌ}¸H?SÀ£r?U(‹µQ%]’£‰à €Ütx ÿý ‘�Bdescribe, it, expect, mock, beforeEach, afterAll } from "bun:test"‘Zx‘Óc@ Promise.resolve({
  stop: mock()
}));

mock.module("../src/bot"“#)
}));

mock.module("../src/routes/upload"“�“ß(
}));

mock.module("../src/routes/files"“�“).)
}));

mock.module("../src/routes/health"“�!+
}));

mock.module("../src/utils/rateLimit"“Î*"
}));

describe("Bootstrap Server"“�"“¹-"“ç!"../src/index"“»"port"“�“À"routes"“�3"/api/upload"“�3"/f/:public_id"“�3"“Á"“�3"/health");
  });
});
£*â=Ï%xÒ-ü¨!Ø �Idescribe, it, expect, mock, spyOn, beforeEach, afterAll } from "bun:test"‘a"../src/utils/logger"‘‹/"‘»."‘ê#"-1003996572954"“"“7"“Už
"telegraf"“ýë“éd#
}));
mock.module("../src/db/index"“q)
  },
  files: {}“­G“<&{
  telegramFileId: "stored_file_id",
“1"stored_unique_id",
“_
}));
mock.module("“�"“¥3“Ù%"info"“"*"error");

describe("Telegram Bot Handler"³P
"“[$"“€6"../src/bot"“Âr"start"“;I;"document", "photo", "video", "audio", "voice", "animation"“¿“Ýg"should handle /start command"“€6"../src/bot"“Ñ$“È+call => call[0] === "start"“	c“1“‡	V"Halo"));
  });

  it("“ô	."“€6"../src/bot"“ÑÃ“(	"doc_123"“P"doc_uniq_123"“z2"application/pdf"“½"cv.pdf"
        }“y'“¡ “1["doc_123", "cv.pdf"“ŸY“K="File berhasil diupload"), expect.any(Object));
  });

  it("“`."“€6"../src/bot"“ÑË {
          file_id: "photo_123"“P"photo_uniq_123",
“ûL“I"image/jpeg"
        }]“y'“¡ “1F“[	"exceeds"“l<¿¯*Ðã
²Kx£ \ÿÓÓ�"it, expect } from "bun:test"‘>"../src/db/index"‘k'>"../src/db/schema";

describe("Database Layer", () => {
  it("‘Ð"“ÿ‘ÿ"“5"“ÿ“oi"“Ù%"³ÿTÚÀ35åyÊx•jø•!â �"3it, expect } from "bun:test";
import { getFileType,‘H0k } from "../src/utils/file";

describe("File Utilities", () => {
  describe("getFileType", () => {
    it("‘ð)"“ñ“ð "video/mp4", "")).toBe("video");“8"video/quicktime", "")).toBe("video");
    });

    it("“¯)"“ñ“ð!"audio/mpeg", "")).toBe("audio");“2"audio/ogg", "")).toBe("audio");
    });

    it("“i1"“ñ“ð)"image/jpeg", "my photo")).toBe("photo");“."image/png", "cool image.png")).toBe("photo");“-"image/gif", "funny.gif")).toBe("animation");“?"image/png", "funny gif")).toBe("animation");
    });

    it("“Ï4"“ñ“ðC"application/octet-stream", "this is a voice note")).toBe("voice");“S"application/octet-stream", "cool animation")).toBe("animation");
    });

    it("“×0"“ñ“ð,"application/pdf", "")).toBe("application");“null, "")).toBe("document"“‹!"checkFileSize", () => {
    it("“É'"“ñ6"photo"“.Y"video"“Ž5"“Ä+"“ñ%“"photo"“.Z"video"“�6"“Æ9"“ñ%“o	"unknown"“F[	"unknown"“ª*#"extractFileName", () => {
    it("“¾	“	"“ì)"x-file-name": "custom.txt"“0“þ

“�	 "custom.txt");
    });

    it("“¾	>"“Á“þ
“&
$"doc.pdf" } }, null)).toBe("doc.pdf"“Ñ“þ
“‹
@"low.jpg" }, { fileName: "high.jpg" }] }, null)).toBe("high.jpg"“Ñ“þ
&"song.mp3" } }, null)).toBe("song.mp3"“Ñ“þ
“d("voice.ogg" } }, null)).toBe("voice.ogg"“Ñ“þ
“Â:"anim.gif" } }, null)).toBe("anim.gif");
    });

    it("“+"“Á“þ

“f"file"“‹#"extractMimeType", () => {
    it("“»0"“ì)"x-mime-type": "text/plain"“02 "text/plain");
    });

    it("“»“  "“Á?4"application/pdf" } }, null)).toBe("application/pdf"“Ñ"“pE"image/jpeg" }, { mimeType: "image/png" }] }, null)).toBe("image/png"“Ñ5*"audio/mpeg" } }, null)).toBe("audio/mpeg"“Ñ"“R("audio/ogg" } }, null)).toBe("audio/ogg"“Ñ#“°<"video/mp4" } }, null)).toBe("video/mp4");
    });

    it("“,"“Á(“W"application/octet-stream"“€vPùç]€ê(x×(úƒ-‹,�Bdescribe, it, expect, mock, beforeEach, afterAll } from "bun:test"‘Z–
    }))
  }))“"../src/db/index"“%)“OA!"equals", value: val })
    }
  }“´Z<"photos/file_0.jpg" }));
mock.module("../src/utils/telegram"“JH
    }
  })“ R"../src/utils/rateLimit"“
/%
}));

describe("File Route Handlers"“_", handleFileInfo“›±"123456:ABC-DEF"“\'"../src/routes/files"“˜}&"handleFileRedirect", () => {
    it("“;+"“}“�R"“"“!#	"test-id"“ �)"Rate limit exceeded");
    });

    it("“J2"“}ž
          })
        })“i+"“þ""“!#"missing-id"“aS“I$"File not found");
    });

    it("“	5"“}œC{
              id: "uuid-123",
              publicId: "test-id",
“P
"tg-file-id",
“€
2"test.jpg"
            }])
          })
        })“i+"“"“!#	"test-id"“aS“­#"Location")).toBe("“ìA");
    });

    it("“{0"“}>“ê"DB Connection Error"“,"“"“!#	"test-id"“aœ"Server error"“@“	""handleFileInfo", () => {
    it("“J2"“}ž
          })
        })“i+"“þ“y"“!#"missing-id"“a&“À)“I$"File not found");
    });

    it("“x-"“¦9G"test-id",
        fileName: "image.png",
        mimeType: "image/png"“&-"photo"“Z9"2026-05-18T00:00:00.000Z")“¯Ÿ
          })
        })“i+"“þ“]"“!#	"test-id"“a&“À)“8[I"test-id",
        file_name: "image.png",
        mime_type: "image/png"“Ü/"photo"“2"2026-05-18T00:00:00.000Z"“_"“{0"“}>“ê"DB Connection Error"“,"“þ“]"“!#	"test-id"“a&“Àr"Server error"“@Cpy—Îë€ëxx+Ôþƒ
ü	�8describe, it, expect, mock, beforeEach } from "bun:test"‘PZ"../src/db/index"‘»+F
  }
}));

describe("Health Route Handler", () => {
  let handleHealth“3`"../src/routes/health"“©<"“æ2"“€“-"“"“+I“¬L"ok"“ü="“:E"“€V"“á"“ì!"“"“+�"error"“Á"“á");
  });
});
ÂZY6áYÖWx‘nú¾(ž�describe, it, expect, beforeEach, afterEach, mock, spyOn } from "bun:test";
import logger from "../src/utils/logger";
import { config } from "../src/env"‘Œ*
"telegraf"‘À›“[“·“Í“”“©{ file_id: "photo_id_low", file_unique_id: "unique_id_low" },
              { file_id: "photo_id_high", file_unique_id: "unique@_id_high" }
            ]
          }))
        };
      }
    }“Ÿ)"info"“Î","error");

describe("Telegram API Utilities"“ , getFileInfo, getBot³`"../src/utils/telegram"“‚Ð"getBot", () => {
    it("“l'",“+“À_$"forwardToStorage", () => {
    it("“C<"“€0"fake photo data"“Á"test_photo.jpg"“ë"photo_id_high",“Q“€
"unique_id_high"“ž12345“Ë6"File forwarded to storage"“	,“ñ“\	"“Ü)"“€“»	“8="Telegram send failed"“‹&"fake photo data"“Á"test_photo.jpg"“ìP"Telegram send failed"“þ*With("““!"“	error: "Telegram send failed"“ñ%"getFileInfo", () => {
    it("“5&"“€“‰url, “�$	"getFile"“À&“ß“ð“*“D"some_file_id" }
          }))“”#	"getInfo"“À&“ß“ð“*“J“f “Šc"image/jpeg",
              file_path: "photos/file_0.jpg"
            }
          })));
        }
“Á“9"Unknown URL"“U6 "some_file_id", "some_unique_id"“/+“Ö%4"image/jpeg",
        file_path: "photos/file_0.jpg"“ñ“\	"“L&"“€“‰“Î.“ð	ok: false,
“"“Ù"
      })))“s#:"invalid_file_id", "invalid_unique_id")).rejects.toThrow("“Ù"“þ@“·®ãNú9xãûÃ„�Bdescribe, it, expect, mock, beforeEach, afterAll } from "bun:test"‘ZV‘±"../src/db/index"‘Õ)
  },
  files: {}“""nanoid"“;"mocked-nanoid-id"“i*"../src/utils/telegram"“ª)‘�{
    telegramFileId: "tg-file-id-123",
    telegramFileUniqueId: "tg-unique-id-abc",
    storageMessageId: 98765
  })),
  getB“t6‘�%{
        file_id: "tg-file-id-123",
“ì!Rmime_type: "image/jpeg"
      }))
    }
  })
}));

describe("Upload Route Handler"“o“�_"“ð"“<"“B7"“ 	,"“a ", {
      method: "POST"“àC"content-type": "text/plain"
      },
      body: "plain text data"“@™"U“Q");
  });

  it("“0"“ 	,"“a ", {
      method: "POST"“à""content-type": "application/json"“ØAH"hello world").toString("base64"),
        fileName: "test.txt"
      })“@O“z)“Û!"mocked-nanoid-id"“*"tg-file-id-123"“!“i"tg-unique-id-abc"“‹#
"test.txt"“¸#"document");
  });

  it("“õ*"“ 	,"“a ", {
      method: "POST"“à""content-type": "application/json"“Ø3Name: "test.txt"
      })“@™"Invalid JSON");
  });

  it("“É
,"“ 	“F)"multipart hello")], { type: "text/plain"“%"file", fileBlob, "test_multi.txt");
“/	"“a /", {
      method: "POST",
      body: formData“@O“zI"mocked-nanoid-id"“‹#"test_multi.txt"“;Ù^r�ïÕOx? Àÿµµ°Y3r¹îvãøüò'1aÑZõ^Á40000 test l>�Ÿ8g]d7ÑÄYÕIï†ˆ“Œ)b›+¦xÆ 9ÿ100644 bot.js ërÂÅè¼P&8êèy\IJ_*m40000 db ¬Á—|Qåçc[î—©ÎÔ/100644 env.js M g`•'¿«~0äØ6~>µºÄ100644 index.js [J&(è»:J†„ó›¢³Ð˜€Gx40000 routes jÿt	\ –}[Y§”­
˜cžx™40000 utils  Q÷ÅÿI>4Ãj½Mø&�¬NnFLŒ»Ûx»Dòimport { Telegraf } from 'telegraf';
import logger from './utils/logger.js';
import { config } from './env.js';
import { db, files as fileSchema } from './db/index.js';
import { nanoid } from 'nanoid';
import { forwardToStorage } from './utils/telegram.js';

export const startBot = async () => {
  try {
    const bot = new Telegraf(config.botToken);

    bot.command('start', async (ctx) => {
      await ctx.reply(
        `ðŸ‘‹ Halo! Kirimkan file (document, photo, video, audio, voice, animation) ke bot ini. ` +
        `File akan disimpan di private channel dan kamu dapat download link permanen.`
      );
    });

    bot.on(['document', 'photo', 'video', 'audio', 'voice', 'animation'], async (ctx) => {
      try {
        const fileType = ctx.message.document ? 'document' :
                        ctx.message.photo ? 'photo' :
                        ctx.message.video ? 'video' :
                        ctx.message.audio ? 'audio' :
                        ctx.message.voice ? 'voice' : 'animation';

        const fileObj = fileType === 'photo' ? ctx.message.photo.slice(-1)[0] : ctx.message[fileType];
        const { file_id, file_unique_id, file_size, mime_type } = fileObj;
        const fileName = ctx.message.document?.file_name ||
                        ctx.message.photo?.slice(-1)[0]?.file_name ||
                        ctx.message.video?.file_name ||
                        ctx.message.audio?.file_name ||
                        ctx.message.voice?.file_name ||
                        'file';

        const maxSize = fileType === 'photo' ? 10 * 1024 * 1024 :
                       fileType === 'audio' ? 200 * 1024 * 1024 :
                       fileType === 'voice' ? 200 * 1024 * 1024 : 2 * 1024 * 1024 * 1024;

        if (file_size > maxSize) {
          return ctx.reply(`File size exceeds ${maxSize / (1024 * 1024)}MB limit`);
        }

        const result = await forwardToStorage(file_id, fileName);
        const publicId = nanoid();

        const uploaded = {
          public_id: publicId,
          telegram_file_id: result.telegramFileId,
          telegram_file_unique_id: result.telegramFileUniqueId,
          storage_chat_id: config.storageChatId,
          storage_message_id: result.storageMessageId,
          file_name: fileName,
          mime_type: mime_type,
          size_bytes: file_size,
          file_type: fileType,
          uploader_id: ctx.from.id,
          created_at: new Date().toISOString(),
          updated_at: new Date().toISOString()
        };

        await db.insert(fileSchema).values(uploaded);

        const url = `${config.baseUrl}/f/${publicId}`;
        await ctx.reply(`File berhasil diupload! ðŸ“Ž\n\nDownload: ${url}`, {
          reply_parameters: { message_id: ctx.message.message_id }
        });

        logger.info('File uploaded via bot', { publicId, fileType, fileName, uploader: ctx.from.id });
      } catch (error) {
        logger.error('Bot file handler error', { error: error.message, chat_id: ctx.chat?.id });
        await ctx.reply('â�Œ Gagal mengupload file. Coba lagi nanti.');
      }
    });

    bot.use((ctx, next) => {
      logger.info('Telegram event received', { type: ctx.update.type, chat_id: ctx.chat?.id });
      return next();
    });

    await bot.launch();

    logger.info('Telegram bot started', { botToken: config.botToken?.substring(0, 10) + '...' });

    return bot;
  } catch (error) {
    logger.error('Failed to start bot', { error: error.message });
    throw error;
  }
};
Ï3S©xI ¶ÿ100644 index.js ‡A0£úÀ“qé+IáÀ,WXVe100644 schema.js †š"Ö>cðhõ…F}$Ýâôà½˜Ó¿xo�þimport { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import logger from '../utils/logger.js';
import { files } from './schema.js';

const client = postgres(process.env.DATABASE_URL, {
  max: 10,
  idle_timeout: 20,
  connect_timeout: 10
});

export const db = drizzle(client, { schema: { files } });
export { files };
export default db;ò®}t¶6xf™üimport { pgTable, text, bigint, timestamp, uuid } from 'drizzle-orm/pg-core';

export const files = pgTable('files', {
  id: uuid('id').primaryKey().defaultRandom(),
  publicId: text('public_id').unique().notNull(),
  telegramFileId: text('telegram_file_id').notNull(),
  telegramFileUniqueId: text('telegram_file_unique_id').notNull(),
  storageChatId: bigint('storage_chat_id', { mode: 'number' }).notNull(),
  storageMessageId: bigint('storage_message_id', { mode: 'number' }).notNull(),
  fileName: text('file_name').notNull(),
  mimeType: text('mime_type').notNull(),
  sizeBytes: bigint('size_bytes', { mode: 'number' }).notNull(),
  fileType: text('file_type').notNull(),
  uploaderId: bigint('uploader_id', { mode: 'number' }).notNull(),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().notNull()
});!'d´Jx¤[ûimport logger from './utils/logger.js';

const requiredEnv = {
  BOT_TOKEN: process.env.BOT_TOKEN,
  STORAGE_CHANNEL_ID: process.env.STORAGE_CHANNEL_ID,
  BASE_URL: process.env.BASE_URL,
  DATABASE_URL: process.env.DATABASE_URL,
  PORT: process.env.PORT
};

const missing = Object.entries(requiredEnv)
  .filter(([_, value]) => value === undefined || value === '')
  .map(([key]) => key);

if (missing.length > 0) {
  logger.error('Missing required environment variables:', missing);
  throw new Error(`Missing environment variables: ${missing.join(', ')}`);
}

export const config = {
  botToken: process.env.BOT_TOKEN,
  storageChatId: parseInt(process.env.STORAGE_CHANNEL_ID, 10),
  baseUrl: process.env.BASE_URL,
  databaseUrl: process.env.DATABASE_URL,
  port: parseInt(process.env.PORT, 10) || 3000,
  nodeEnv: process.env.NODE_ENV || 'development',
  logLevel: process.env.LOG_LEVEL || 'info',
  rateLimitWindowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS, 10) || 60000,
  rateLimitMaxRequests: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS, 10) || 30
};

logger.info('Environment variables loaded', { config: { ...config, botToken: config.botToken?.substring(0, 10) + '...' } });
ê‚»Oxûûimport { serve } from 'bun';
import logger from './utils/logger.js';
import { config } from './env.js';
import { startBot } from './bot.js';
import { handleUpload } from './routes/upload.js';
import { handleFileRedirect, handleFileInfo } from './routes/files.js';
import { handleHealth } from './routes/health.js';
import { cleanupRateLimitCache } from './utils/rateLimit.js';

const server = serve({
  port: config.port,
  routes: {
    '/api/upload': {
      POST: handleUpload
    },
    '/f/:public_id': {
      GET: handleFileRedirect
    },
    '/file/:public_id/info': {
      GET: handleFileInfo
    },
    '/health': {
      GET: handleHealth
    }
  }
});

const bot = await startBot();

logger.info('Server started', { port: config.port, url: config.baseUrl });

const gracefulShutdown = async (signal) => {
  logger.info('Graceful shutdown signal received', { signal });

  logger.info('Closing HTTP server');
  server.stop();

  logger.info('Stopping Telegram bot');
  await bot.stop();

  logger.info('Server shutdown complete');
  process.exit(0);
};

process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));

setInterval(cleanupRateLimitCache, 60000);

logger.info('Application running successfully');
EeŸk®xn ‘ÿ100644 files.js w,â#ðSh�«>ä½ù˜xø°&100644 health.js ¨íÎTB÷ñ�ú€u¢s„<ÛU©?100644 upload.js )Y Ë�úô¢í­)�ap5!j±,V¶“x6	Éöimport logger from '../utils/logger.js';
import { db, files as fileSchema } from '../db/index.js';
import { checkRateLimit } from '../utils/rateLimit.js';
import { eq } from 'drizzle-orm';

export const handleFileRedirect = async (req, ctx) => {
  const public_id = ctx?.params?.public_id;
  try {
    const ip = req.headers.get('x-forwarded-for') || '127.0.0.1';

    if (!public_id || !checkRateLimit(ip)) {
      return Response.json({ error: 'Rate limit exceeded' }, { status: 429 });
    }

    const result = await db.select().from(fileSchema).where(eq(fileSchema.publicId, public_id)).limit(1);

    if (!result.length) {
      logger.warn('File not found', { public_id });
      return Response.json({ error: 'File not found' }, { status: 404 });
    }

    const file = result[0];
    const { getBot } = await import('../utils/telegram.js');
    const bot = getBot();
    const fileInfo = await bot.api.getFile(file.telegramFileId);

    const redirectUrl = `https://api.telegram.org/file/bot${process.env.BOT_TOKEN}/${fileInfo.file_path}`;
    return new Response(null, {
      status: 302,
      headers: {
        'Location': redirectUrl
      }
    });
  } catch (error) {
    logger.error('File redirect error', { public_id, error: error.message });
    return Response.json({ error: 'Server error' }, { status: 500 });
  }
};

export const handleFileInfo = async (req, ctx) => {
  const public_id = ctx?.params?.public_id;
  try {
    if (!public_id) {
      return Response.json({ error: 'Missing file id' }, { status: 400 });
    }

    const result = await db.select().from(fileSchema).where(eq(fileSchema.publicId, public_id)).limit(1);

    if (!result.length) {
      logger.warn('File not found', { public_id });
      return Response.json({ error: 'File not found' }, { status: 404 });
    }

    const file = result[0];
    return Response.json({
      public_id: file.publicId,
      file_name: file.fileName,
      mime_type: file.mimeType,
      size_bytes: file.sizeBytes,
      file_type: file.fileType,
      uploader_id: file.uploaderId,
      created_at: file.createdAt.toISOString ? file.createdAt.toISOString() : file.createdAt
    }, { status: 200 });
  } catch (error) {
    logger.error('File info error', { public_id, error: error.message });
    return Response.json({ error: 'Server error' }, { status: 500 });
  }
};
â°öþî	‘Dxž aÿ¶½�4‘I#sql‘¦+DHealth = async (req) => {
  try {
    await db.execute(sql`SELECT 1`“ístatus: 'ok'“u<Health check failed', {“Ï5status: 'error'“Î“""3ï¿öxo�èimport logger from '../utils/logger.js';
import { db, files as fileSchema } from '../db/index.js';
import { nanoid } from 'nanoid';
import { forwardToStorage, getBot } from '../utils/telegram.js';
import { getFileType, checkFileSize, extractFileName, extractMimeType } from '../utils/file.js';
import { config } from '../env.js';

export const handleUpload = async (req) => {
  try {
    const contentType = req.headers.get('content-type') || '';

    if (contentType.includes('multipart/form-data')) {
      return handleMultipartUpload(req);
    } else if (contentType.includes('application/json')) {
      return handleJSONUpload(req);
    }

    return Response.json(
      { error: 'Unsupported content type. Use multipart/form-data or application/json' },
      { status: 400 }
    );
  } catch (error) {
    logger.error('Upload error', { error: error.message });
    return Response.json({ error: error.message }, { status: 500 });
  }
};

const handleMultipartUpload = async (req) => {
  try {
    const formData = await req.formData();
    const file = formData.get('file');
    const fileName = formData.get('fileName') || (file instanceof File ? file.name : null) || 'file';

    if (!file || !(file instanceof File)) {
      return Response.json({ error: 'No file provided' }, { status: 400 });
    }

    const fileBytes = await file.arrayBuffer();
    const fileBuffer = Buffer.from(fileBytes);
    const mimeType = file.type || extractMimeType({}, req) || 'application/octet-stream';
    const fileType = getFileType(mimeType, fileName);

    if (!checkFileSize(fileBuffer.byteLength, fileType)) {
      return Response.json({ error: `File size exceeds ${fileType} limit` }, { status: 400 });
    }

    const isDocument = fileName.endsWith('.pdf') || fileName.endsWith('.txt') || !['photo', 'video', 'audio', 'voice', 'animation'].includes(fileType);
    const result = await forwardToStorage(fileBuffer, fileName, isDocument);
    const bot = getBot();
    const fileInfo = await bot.api.getFile(result.telegramFileId);

    const uploaded = {
      publicId: nanoid(),
      telegramFileId: result.telegramFileId,
      telegramFileUniqueId: result.telegramFileUniqueId,
      storageChatId: config.storageChatId,
      storageMessageId: result.storageMessageId,
      fileName: fileName,
      mimeType: fileInfo.mime_type || mimeType || 'application/octet-stream',
      sizeBytes: fileInfo.file_size || fileBuffer.byteLength,
      fileType: fileType,
      uploaderId: 0,
      createdAt: new Date(),
      updatedAt: new Date()
    };

    await db.insert(fileSchema).values(uploaded);

    // Prepare response matching original snake_case fields as expected in task description
    const responsePayload = {
      public_id: uploaded.publicId,
      telegram_file_id: uploaded.telegramFileId,
      telegram_file_unique_id: uploaded.telegramFileUniqueId,
      storage_chat_id: uploaded.storageChatId,
      storage_message_id: uploaded.storageMessageId,
      file_name: uploaded.fileName,
      mime_type: uploaded.mimeType,
      size_bytes: uploaded.sizeBytes,
      file_type: uploaded.fileType,
      uploader_id: uploaded.uploaderId,
      created_at: uploaded.createdAt.toISOString(),
      download_url: `${config.baseUrl}/f/${uploaded.publicId}`
    };

    return Response.json(responsePayload, { status: 200 });
  } catch (error) {
    logger.error('Multipart upload error', { error: error.message });
    return Response.json({ error: error.message }, { status: 500 });
  }
};

const handleJSONUpload = async (req) => {
  try {
    const { file, fileName = 'file' } = await req.json();

    if (!file || typeof file !== 'string') {
      return Response.json(
        { error: 'Invalid JSON. Must include "file" (base64) and optional "fileName"' },
        { status: 400 }
      );
    }

    const fileBytes = Buffer.from(file, 'base64');
    const mimeType = 'application/octet-stream';
    const fileType = getFileType(mimeType, fileName) === 'application' ? 'document' : getFileType(mimeType, fileName);

    if (!checkFileSize(fileBytes.byteLength, fileType)) {
      return Response.json({ error: `File size exceeds ${fileType} limit` }, { status: 400 });
    }

    const isDocument = fileName.endsWith('.pdf') || fileName.endsWith('.txt') || !['photo', 'video', 'audio', 'voice', 'animation'].includes(fileType);
    const result = await forwardToStorage(fileBytes, fileName, isDocument);
    const bot = getBot();
    const fileInfo = await bot.api.getFile(result.telegramFileId);

    const uploaded = {
      publicId: nanoid(),
      telegramFileId: result.telegramFileId,
      telegramFileUniqueId: result.telegramFileUniqueId,
      storageChatId: config.storageChatId,
      storageMessageId: result.storageMessageId,
      fileName: fileName,
      mimeType: fileInfo.mime_type || mimeType || 'application/octet-stream',
      sizeBytes: fileInfo.file_size || fileBytes.byteLength,
      fileType: fileType,
      uploaderId: 0,
      createdAt: new Date(),
      updatedAt: new Date()
    };

    await db.insert(fileSchema).values(uploaded);

    // Prepare response matching original snake_case fields as expected in task description
    const responsePayload = {
      public_id: uploaded.publicId,
      telegram_file_id: uploaded.telegramFileId,
      telegram_file_unique_id: uploaded.telegramFileUniqueId,
      storage_chat_id: uploaded.storageChatId,
      storage_message_id: uploaded.storageMessageId,
      file_name: uploaded.fileName,
      mime_type: uploaded.mimeType,
      size_bytes: uploaded.sizeBytes,
      file_type: uploaded.fileType,
      uploader_id: uploaded.uploaderId,
      created_at: uploaded.createdAt.toISOString(),
      download_url: `${config.baseUrl}/f/${uploaded.publicId}`
    };

    return Response.json(responsePayload, { status: 200 });
  } catch (error) {
    logger.error('JSON upload error', { error: error.message });
    return Response.json({ error: error.message }, { status: 500 });
  }
};
D¹ºt§	x— hÿ100644 file.js .õõ%L$€PESO¬±…
Ø~100644 logger.js g«T‘ïÂ`x]É²UÈÍ:SEüâ100644 rateLimit.js ø
„ŒÅµ÷­ï_}gM4Éî100644 telegram.js 3Æ:±ßŽÙ’áÒêZ	œI®íªô;Þ°cx0Ïùconst FILE_TYPES = {
  document: 2 * 1024 * 1024 * 1024, // 2GB
  photo: 10 * 1024 * 1024, // 10MB
  video: 2 * 1024 * 1024 * 1024, // 2GB
  audio: 200 * 1024 * 1024, // 200MB
  voice: 200 * 1024 * 1024, // 200MB
  animation: 2 * 1024 * 1024 * 1024 // 2GB
};

export const getFileType = (mime, caption) => {
  const mimeUpper = mime?.split('/')[0]?.toLowerCase();
  const captionLower = caption?.toLowerCase();

  if (mimeUpper === 'video') return 'video';
  if (mimeUpper === 'audio') return 'audio';
  if (mimeUpper === 'document') return 'document';
  if (mimeUpper === 'image') return captionLower?.includes('gif') ? 'animation' : 'photo';
  if (captionLower?.includes('voice')) return 'voice';
  if (captionLower?.includes('animation')) return 'animation';

  return mimeUpper || 'document';
};

export const checkFileSize = (sizeBytes, fileType) => {
  const limit = FILE_TYPES[fileType] || FILE_TYPES.document;
  return sizeBytes <= limit;
};

export const extractFileName = (msg, request) => {
  if (request?.headers?.['x-file-name']) {
    return request.headers['x-file-name'];
  }
  return msg.document?.fileName || msg.photo?.slice(-1)[0]?.fileName || msg.audio?.fileName ||
         msg.voice?.fileName || msg.animation?.fileName || 'file';
};

export const extractMimeType = (msg, request) => {
  if (request?.headers?.['x-mime-type']) {
    return request.headers['x-mime-type'];
  }
  return msg.document?.mimeType || msg.photo?.slice(-1)[0]?.mimeType || msg.audio?.mimeType ||
         msg.voice?.mimeType || msg.animation?.mimeType || 'application/octet-stream';
};
ƒ›ô<±Cx1Îûimport logger from './logger.js';

const rateLimitMap = new Map();

export const checkRateLimit = (key) => {
  const now = Date.now();
  const windowMs = parseInt(process.env.RATE_LIMIT_WINDOW_MS, 10) || 60000;
  const maxRequests = parseInt(process.env.RATE_LIMIT_MAX_REQUESTS, 10) || 30;

  if (!rateLimitMap.has(key)) {
    rateLimitMap.set(key, { count: 0, reset: now + windowMs });
  }

  const record = rateLimitMap.get(key);

  if (now > record.reset) {
    record.count = 0;
    record.reset = now + windowMs;
  }

  if (record.count >= maxRequests) {
    logger.warn('Rate limit exceeded', { key, count: record.count, reset: record.reset });
    return false;
  }

  record.count++;
  return true;
};

export const cleanupRateLimitCache = () => {
  const now = Date.now();
  const windowMs = parseInt(process.env.RATE_LIMIT_WINDOW_MS, 10) || 60000;
  const keysToDelete = [];

  for (const [key, record] of rateLimitMap.entries()) {
    if (now > record.reset) {
      keysToDelete.push(key);
    }
  }

  keysToDelete.forEach(key => rateLimitMap.delete(key));
};
AóW!»}xÛ$øimport { Telegraf } from 'telegraf';
import logger from './logger.js';
import { config } from '../env.js';

const bot = new Telegraf(config.botToken);
const TELEGRAM_API_URL = `https://api.telegram.org/bot${config.botToken}/`;

export const forwardToStorage = async (fileChunk, fileName, forceDocument = false) => {
  try {
    const caption = forceDocument ? `ðŸ“� ${fileName}` : fileName;
    const input = forceDocument ? { document: fileChunk, caption } : { photo: [fileChunk], caption };

    const result = await bot.api.sendPhoto(config.storageChatId, input);

    logger.info('File forwarded to storage', { fileName, message: result.message_id });

    return {
      telegramFileId: result.photo?.slice(-1)[0]?.file_id,
      telegramFileUniqueId: result.photo?.slice(-1)[0]?.file_unique_id,
      storageMessageId: result.message_id
    };
  } catch (error) {
    logger.error('Failed to forward file to storage', { fileName, error: error.message });
    throw error;
  }
};

export const getFileInfo = async (telegramFileId, telegramFileUniqueId) => {
  try {
    const result = await fetch(`${TELEGRAM_API_URL}getFile`);
    const data = await result.json();

    if (!data.ok) {
      throw new Error(data.description || 'Telegram API error');
    }

    const fileId = data.result.file_id === telegramFileId ? telegramFileId : telegramFileUniqueId;
    const fileResult = await fetch(`${TELEGRAM_API_URL}getInfo`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ file_id: fileId })
    });
    const fileInfo = await fileResult.json();

    if (!fileInfo.ok) {
      throw new Error(fileInfo.description || 'Telegram info error');
    }

    return {
      file_size: fileInfo.result.file_size,
      mime_type: fileInfo.result.mime_type,
      file_path: fileInfo.result.file_path
    };
  } catch (error) {
    logger.error('Failed to get file info', { error: error.message });
    throw error;
  }
};

export const getBot = () => bot;
[–(¯xŸ`þ100644 bootstrap.test.js '•sR“Ýµa€ûX3ˆ�.¶8Ã100644 bot.test.js 'y‘ì<v}GüÙAÊ@ª—­
dË100644 db.test.js šÜtûVÉ§i^—Am÷gÕÐF100644 env.test.js éògÆ3ÿS.s(D_Ú B%·ý‰100644 file.test.js úd4•û#×4ï½èçb2„‘§c‡0100644 files.test.js ˜Ôú«wDé×ï3¹Ý³�‚Oï
100644 health.test.js {�h±þ2Œ†¿cÇˆ¶\7ó\|100644 rateLimit.test.js ¼7ÐŠ•§õp¶	Úx~€Ù¸®100644 telegram.test.js 9ùùó…p+ ¦¸	Ø”‘qŽ100644 upload.test.js .¯Ðü-?%%‹=ž_-�3¢‘o£?þÊ¦1´axëùimport { describe, it, expect, mock, beforeEach, afterAll } from "bun:test";

const mockServe = mock((options) => {
  return {
    port: options.port,
    routes: options.routes,
    stop: mock()
  };
});

const originalServe = Bun.serve;
Bun.serve = mockServe;

const mockStartBot = mock(() => Promise.resolve({
  stop: mock()
}));

mock.module("../src/bot.js", () => ({
  startBot: mockStartBot
}));

mock.module("../src/routes/upload.js", () => ({
  handleUpload: mock()
}));

mock.module("../src/routes/files.js", () => ({
  handleFileRedirect: mock(),
  handleFileInfo: mock()
}));

mock.module("../src/routes/health.js", () => ({
  handleHealth: mock()
}));

mock.module("../src/utils/rateLimit.js", () => ({
  cleanupRateLimitCache: mock()
}));

describe("Bootstrap Server", () => {
  beforeEach(() => {
    mockServe.mockClear();
    mockStartBot.mockClear();
  });

  afterAll(() => {
    Bun.serve = originalServe;
  });

  it("should bootstrap the application successfully", async () => {
    await import("../src/index.js");

    expect(mockServe).toHaveBeenCalled();
    expect(mockStartBot).toHaveBeenCalled();

    const serveCallArgs = mockServe.mock.calls[0][0];
    expect(serveCallArgs).toHaveProperty("port");
    expect(serveCallArgs).toHaveProperty("routes");
    expect(serveCallArgs.routes).toHaveProperty("/api/upload");
    expect(serveCallArgs.routes).toHaveProperty("/f/:public_id");
    expect(serveCallArgs.routes).toHaveProperty("/file/:public_id/info");
    expect(serveCallArgs.routes).toHaveProperty("/health");
  });
});
øøÂ¾…x^¡ïimport { describe, it, expect, mock, spyOn, beforeEach, afterAll } from "bun:test";
import logger from "../src/utils/logger.js";

// Mock environment
process.env.BOT_TOKEN = "8605908810:AAFpUzlIBktfd_7wpEj7zMJob2CFxvG-ZGY";
process.env.STORAGE_CHANNEL_ID = "-1003996572954";
process.env.BASE_URL = "https://tele.asepharyana.tech";

// Mock Telegraf
const mockLaunch = mock(() => Promise.resolve());
const mockCommand = mock();
const mockOn = mock();
const mockUse = mock();

mock.module("telegraf", () => {
  return {
    Telegraf: class {
      constructor(token) {
        this.token = token;
        this.launch = mockLaunch;
        this.command = mockCommand;
        this.on = mockOn;
        this.use = mockUse;
      }
    }
  };
});

// Mock database
const mockInsert = mock(() => ({
  values: mock(() => Promise.resolve())
}));
mock.module("../src/db/index.js", () => ({
  db: {
    insert: mockInsert
  },
  files: {}
}));

// Mock forwardToStorage
const mockForwardToStorage = mock(() => Promise.resolve({
  telegramFileId: "stored_file_id",
  telegramFileUniqueId: "stored_unique_id",
  storageMessageId: 9999
}));
mock.module("../src/utils/telegram.js", () => ({
  forwardToStorage: mockForwardToStorage
}));

const infoSpy = spyOn(logger, "info");
const errorSpy = spyOn(logger, "error");

describe("Telegram Bot Handler", () => {
  beforeEach(() => {
    mockLaunch.mockClear();
    mockCommand.mockClear();
    mockOn.mockClear();
    mockUse.mockClear();
    mockInsert.mockClear();
    mockForwardToStorage.mockClear();
    infoSpy.mockClear();
    errorSpy.mockClear();
  });

  it("should initialize and launch the bot", async () => {
    const { startBot } = await import("../src/bot.js");
    const bot = await startBot();

    expect(bot).toBeDefined();
    expect(mockCommand).toHaveBeenCalledWith("start", expect.any(Function));
    expect(mockOn).toHaveBeenCalledWith(
      ["document", "photo", "video", "audio", "voice", "animation"],
      expect.any(Function)
    );
    expect(mockUse).toHaveBeenCalled();
    expect(mockLaunch).toHaveBeenCalled();
  });

  it("should handle /start command", async () => {
    const { startBot } = await import("../src/bot.js");
    await startBot();

    const startHandler = mockCommand.mock.calls.find(call => call[0] === "start")[1];
    const replyMock = mock(() => Promise.resolve());
    const ctx = {
      reply: replyMock
    };

    await startHandler(ctx);
    expect(replyMock).toHaveBeenCalledWith(expect.stringContaining("Halo"));
  });

  it("should process document uploads and save to db", async () => {
    const { startBot } = await import("../src/bot.js");
    await startBot();

    const fileHandler = mockOn.mock.calls[0][1];
    const replyMock = mock(() => Promise.resolve());
    const ctx = {
      message: {
        message_id: 42,
        document: {
          file_id: "doc_123",
          file_unique_id: "doc_uniq_123",
          file_size: 1024,
          mime_type: "application/pdf",
          file_name: "cv.pdf"
        }
      },
      from: {
        id: 999
      },
      reply: replyMock
    };

    await fileHandler(ctx);
    expect(mockForwardToStorage).toHaveBeenCalledWith("doc_123", "cv.pdf");
    expect(mockInsert).toHaveBeenCalled();
    expect(replyMock).toHaveBeenCalledWith(expect.stringContaining("File berhasil diupload"), expect.any(Object));
  });

  it("should reject uploads exceeding max size limit", async () => {
    const { startBot } = await import("../src/bot.js");
    await startBot();

    const fileHandler = mockOn.mock.calls[0][1];
    const replyMock = mock(() => Promise.resolve());
    const ctx = {
      message: {
        message_id: 42,
        photo: [{
          file_id: "photo_123",
          file_unique_id: "photo_uniq_123",
          file_size: 20 * 1024 * 1024, // 20MB exceeds 10MB limit
          mime_type: "image/jpeg"
        }]
      },
      from: {
        id: 999
      },
      reply: replyMock
    };

    await fileHandler(ctx);
    expect(mockForwardToStorage).not.toHaveBeenCalled();
    expect(replyMock).toHaveBeenCalledWith(expect.stringContaining("exceeds"));
  });

  afterAll(() => {
    mock.restore();
  });
});
ãgyºDxJµûimport { describe, it, expect } from "bun:test";
import { db, files } from "../src/db/index.js";
import { files as schemaFiles } from "../src/db/schema.js";

describe("Database Layer", () => {
  it("should export db instance", () => {
    expect(db).toBeDefined();
  });

  it("should export files schema from both index and schema", () => {
    expect(files).toBeDefined();
    expect(schemaFiles).toBeDefined();
    expect(files).toBe(schemaFiles);
  });

  it("should have correct schema properties", () => {
    expect(files.id).toBeDefined();
    expect(files.publicId).toBeDefined();
    expect(files.telegramFileId).toBeDefined();
    expect(files.telegramFileUniqueId).toBeDefined();
    expect(files.storageChatId).toBeDefined();
    expect(files.storageMessageId).toBeDefined();
    expect(files.fileName).toBeDefined();
    expect(files.mimeType).toBeDefined();
    expect(files.sizeBytes).toBeDefined();
    expect(files.fileType).toBeDefined();
    expect(files.uploaderId).toBeDefined();
    expect(files.createdAt).toBeDefined();
    expect(files.updatedAt).toBeDefined();
  });
});
°f#ºgxz…ùimport { describe, it, expect, beforeAll } from "bun:test";
import { config } from "../src/env.js";

describe("Environment Variables Validation", () => {
  it("config should have all required fields", () => {
    expect(config).toHaveProperty("botToken");
    expect(config).toHaveProperty("storageChatId");
    expect(config).toHaveProperty("baseUrl");
    expect(config).toHaveProperty("databaseUrl");
    expect(config).toHaveProperty("port");
    expect(config).toHaveProperty("nodeEnv");
    expect(config).toHaveProperty("logLevel");
    expect(config).toHaveProperty("rateLimitWindowMs");
    expect(config).toHaveProperty("rateLimitMaxRequests");
  });

  it("config.botToken should return BOT_TOKEN from process.env", () => {
    expect(config.botToken).toBe(process.env.BOT_TOKEN);
  });

  it("config.storageChatId should be parsed as integer from STORAGE_CHANNEL_ID", () => {
    expect(typeof config.storageChatId).toBe("number");
    expect(config.storageChatId).toBe(parseInt(process.env.STORAGE_CHANNEL_ID, 10));
  });

  it("config.port should default to 3000 when not specified", () => {
    expect(typeof config.port).toBe("number");
  });

  it("nodeEnv should be 'test' or 'development'", () => {
    expect(["test", "development"]).toContain(config.nodeEnv);
  });

  it("logLevel should default to 'info'", () => {
    expect(config.logLevel).toBe("info");
  });

  it("rateLimitWindowMs should default to 60000 when not specified", () => {
    expect(config.rateLimitWindowMs).toBe(60000);
  });

  it("rateLimitMaxRequests should default to 30 when not specified", () => {
    expect(config.rateLimitMaxRequests).toBe(30);
  });
});
Ø)A¶…xV©ïimport { describe, it, expect } from "bun:test";
import { getFileType, checkFileSize, extractFileName, extractMimeType } from "../src/utils/file.js";

describe("File Utilities", () => {
  describe("getFileType", () => {
    it("should classify video mime types as video", () => {
      expect(getFileType("video/mp4", "")).toBe("video");
      expect(getFileType("video/quicktime", "")).toBe("video");
    });

    it("should classify audio mime types as audio", () => {
      expect(getFileType("audio/mpeg", "")).toBe("audio");
      expect(getFileType("audio/ogg", "")).toBe("audio");
    });

    it("should classify image mime types based on caption", () => {
      expect(getFileType("image/jpeg", "my photo")).toBe("photo");
      expect(getFileType("image/png", "cool image.png")).toBe("photo");
      expect(getFileType("image/gif", "funny.gif")).toBe("animation");
      expect(getFileType("image/png", "funny gif")).toBe("animation");
    });

    it("should classify voice and animation based on caption", () => {
      expect(getFileType("application/octet-stream", "this is a voice note")).toBe("voice");
      expect(getFileType("application/octet-stream", "cool animation")).toBe("animation");
    });

    it("should default to mime first segment or document", () => {
      expect(getFileType("application/pdf", "")).toBe("application");
      expect(getFileType(null, "")).toBe("document");
    });
  });

  describe("checkFileSize", () => {
    it("should allow files under the size limit", () => {
      expect(checkFileSize(5 * 1024 * 1024, "photo")).toBe(true); // Photo limit is 10MB
      expect(checkFileSize(1 * 1024 * 1024 * 1024, "video")).toBe(true); // Video limit is 2GB
    });

    it("should block files exceeding the size limit", () => {
      expect(checkFileSize(15 * 1024 * 1024, "photo")).toBe(false); // Photo limit is 10MB
      expect(checkFileSize(3 * 1024 * 1024 * 1024, "video")).toBe(false); // Video limit is 2GB
    });

    it("should fall back to document limit if fileType is unknown", () => {
      expect(checkFileSize(1 * 1024 * 1024 * 1024, "unknown")).toBe(true); // Document limit is 2GB
      expect(checkFileSize(3 * 1024 * 1024 * 1024, "unknown")).toBe(false);
    });
  });

  describe("extractFileName", () => {
    it("should extract file name from headers if present", () => {
      const req = { headers: { "x-file-name": "custom.txt" } };
      expect(extractFileName({}, req)).toBe("custom.txt");
    });

    it("should extract file name from various message attachment types", () => {
      expect(extractFileName({ document: { fileName: "doc.pdf" } }, null)).toBe("doc.pdf");
      expect(extractFileName({ photo: [{ fileName: "low.jpg" }, { fileName: "high.jpg" }] }, null)).toBe("high.jpg");
      expect(extractFileName({ audio: { fileName: "song.mp3" } }, null)).toBe("song.mp3");
      expect(extractFileName({ voice: { fileName: "voice.ogg" } }, null)).toBe("voice.ogg");
      expect(extractFileName({ animation: { fileName: "anim.gif" } }, null)).toBe("anim.gif");
    });

    it("should return default filename if not found", () => {
      expect(extractFileName({}, null)).toBe("file");
    });
  });

  describe("extractMimeType", () => {
    it("should extract mime type from headers if present", () => {
      const req = { headers: { "x-mime-type": "text/plain" } };
      expect(extractMimeType({}, req)).toBe("text/plain");
    });

    it("should extract mime type from various message attachment types", () => {
      expect(extractMimeType({ document: { mimeType: "application/pdf" } }, null)).toBe("application/pdf");
      expect(extractMimeType({ photo: [{ mimeType: "image/jpeg" }, { mimeType: "image/png" }] }, null)).toBe("image/png");
      expect(extractMimeType({ audio: { mimeType: "audio/mpeg" } }, null)).toBe("audio/mpeg");
      expect(extractMimeType({ voice: { mimeType: "audio/ogg" } }, null)).toBe("audio/ogg");
      expect(extractMimeType({ animation: { mimeType: "video/mp4" } }, null)).toBe("video/mp4");
    });

    it("should return default mime type if not found", () => {
      expect(extractMimeType({}, null)).toBe("application/octet-stream");
    });
  });
});
�Pc²ÝxÒ-êimport { describe, it, expect, mock, beforeEach, afterAll } from "bun:test";

// Mock database layer
const mockSelect = mock(() => ({
  from: mock(() => ({
    where: mock(() => ({
      limit: mock(() => Promise.resolve([]))
    }))
  }))
}));

mock.module("../src/db/index.js", () => ({
  db: {
    select: mockSelect
  },
  files: {
    publicId: {
      equals: (val) => ({ type: "equals", value: val })
    }
  }
}));

// Mock telegram utils
const mockGetFile = mock(() => Promise.resolve({ file_path: "photos/file_0.jpg" }));
mock.module("../src/utils/telegram.js", () => ({
  getBot: () => ({
    api: {
      getFile: mockGetFile
    }
  })
}));

// Mock rateLimit
const mockCheckRateLimit = mock(() => true);
mock.module("../src/utils/rateLimit.js", () => ({
  checkRateLimit: mockCheckRateLimit
}));

describe("File Route Handlers", () => {
  let handleFileRedirect, handleFileInfo;

  beforeEach(async () => {
    mockSelect.mockClear();
    mockGetFile.mockClear();
    mockCheckRateLimit.mockClear();

    // Set up mock token
    process.env.BOT_TOKEN = "123456:ABC-DEF";

    const filesRoute = await import("../src/routes/files.js");
    handleFileRedirect = filesRoute.handleFileRedirect;
    handleFileInfo = filesRoute.handleFileInfo;
  });

  describe("handleFileRedirect", () => {
    it("should return 429 if rate limit is exceeded", async () => {
      mockCheckRateLimit.mockImplementationOnce(() => false);
      const req = new Request("http://localhost:3000/f/test-id");

      const res = await handleFileRedirect(req, { params: { public_id: "test-id" } });
      expect(res.status).toBe(429);
      const body = await res.json();
      expect(body.error).toBe("Rate limit exceeded");
    });

    it("should return 404 if file is not found in database", async () => {
      mockSelect.mockImplementationOnce(() => ({
        from: () => ({
          where: () => ({
            limit: () => Promise.resolve([])
          })
        })
      }));

      const req = new Request("http://localhost:3000/f/missing-id");
      const res = await handleFileRedirect(req, { params: { public_id: "missing-id" } });
      expect(res.status).toBe(404);
      const body = await res.json();
      expect(body.error).toBe("File not found");
    });

    it("should redirect to telegram file url if file is found", async () => {
      mockSelect.mockImplementationOnce(() => ({
        from: () => ({
          where: () => ({
            limit: () => Promise.resolve([{
              id: "uuid-123",
              publicId: "test-id",
              telegramFileId: "tg-file-id",
              fileName: "test.jpg"
            }])
          })
        })
      }));

      const req = new Request("http://localhost:3000/f/test-id");
      const res = await handleFileRedirect(req, { params: { public_id: "test-id" } });
      expect(res.status).toBe(302);
      expect(res.headers.get("Location")).toBe("https://api.telegram.org/file/bot123456:ABC-DEF/photos/file_0.jpg");
    });

    it("should return 500 on database or external errors", async () => {
      mockSelect.mockImplementationOnce(() => {
        throw new Error("DB Connection Error");
      });

      const req = new Request("http://localhost:3000/f/test-id");
      const res = await handleFileRedirect(req, { params: { public_id: "test-id" } });
      expect(res.status).toBe(500);
      const body = await res.json();
      expect(body.error).toBe("Server error");
    });
  });

  describe("handleFileInfo", () => {
    it("should return 404 if file is not found in database", async () => {
      mockSelect.mockImplementationOnce(() => ({
        from: () => ({
          where: () => ({
            limit: () => Promise.resolve([])
          })
        })
      }));

      const req = new Request("http://localhost:3000/file/missing-id/info");
      const res = await handleFileInfo(req, { params: { public_id: "missing-id" } });
      expect(res.status).toBe(404);
      const body = await res.json();
      expect(body.error).toBe("File not found");
    });

    it("should return file info JSON if file is found", async () => {
      const dbFile = {
        publicId: "test-id",
        fileName: "image.png",
        mimeType: "image/png",
        sizeBytes: 2048,
        fileType: "photo",
        uploaderId: 99999,
        createdAt: new Date("2026-05-18T00:00:00.000Z")
      };

      mockSelect.mockImplementationOnce(() => ({
        from: () => ({
          where: () => ({
            limit: () => Promise.resolve([dbFile])
          })
        })
      }));

      const req = new Request("http://localhost:3000/file/test-id/info");
      const res = await handleFileInfo(req, { params: { public_id: "test-id" } });
      expect(res.status).toBe(200);
      const body = await res.json();
      expect(body).toEqual({
        public_id: "test-id",
        file_name: "image.png",
        mime_type: "image/png",
        size_bytes: 2048,
        file_type: "photo",
        uploader_id: 99999,
        created_at: "2026-05-18T00:00:00.000Z"
      });
    });

    it("should return 500 on database or external errors", async () => {
      mockSelect.mockImplementationOnce(() => {
        throw new Error("DB Connection Error");
      });

      const req = new Request("http://localhost:3000/file/test-id/info");
      const res = await handleFileInfo(req, { params: { public_id: "test-id" } });
      expect(res.status).toBe(500);
      const body = await res.json();
      expect(body.error).toBe("Server error");
    });
  });

  afterAll(() => {
    mock.restore();
  });
});
ben³Oxóûimport { describe, it, expect, mock, beforeEach } from "bun:test";

// Mock database layer
const mockExecute = mock(() => Promise.resolve());

mock.module("../src/db/index.js", () => ({
  db: {
    execute: mockExecute
  }
}));

describe("Health Route Handler", () => {
  let handleHealth;

  beforeEach(async () => {
    mockExecute.mockClear();
    const healthRoute = await import("../src/routes/health.js");
    handleHealth = healthRoute.handleHealth;
  });

  it("should return status 200 and ok when DB is healthy", async () => {
    const req = new Request("http://localhost:3000/health");
    const res = await handleHealth(req);

    expect(res.status).toBe(200);
    const body = await res.json();
    expect(body).toEqual({ status: "ok" });
    expect(mockExecute).toHaveBeenCalled();
  });

  it("should return status 500 and error details when DB health check fails", async () => {
    mockExecute.mockImplementationOnce(() => Promise.reject(new Error("DB Connection Failed")));
    const req = new Request("http://localhost:3000/health");
    const res = await handleHealth(req);

    expect(res.status).toBe(500);
    const body = await res.json();
    expect(body.status).toBe("error");
    expect(body.error).toBe("DB Connection Failed");
  });
});
R”Å´¿xôôimport { describe, it, expect, beforeEach, afterEach, mock, spyOn } from "bun:test";
import { checkRateLimit, cleanupRateLimitCache } from "../src/utils/rateLimit.js";
import logger from "../src/utils/logger.js";

// Spy on logger.warn
const warnSpy = spyOn(logger, "warn");

describe("Rate Limiter", () => {
  beforeEach(() => {
    warnSpy.mockClear();
    // Set custom env variables for predictable tests
    process.env.RATE_LIMIT_WINDOW_MS = "100"; // 100ms window
    process.env.RATE_LIMIT_MAX_REQUESTS = "3"; // max 3 requests
  });

  afterEach(() => {
    delete process.env.RATE_LIMIT_WINDOW_MS;
    delete process.env.RATE_LIMIT_MAX_REQUESTS;
  });

  it("should allow requests under the limit", () => {
    const key = "user-1";
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(true);
    expect(warnSpy).not.toHaveBeenCalled();
  });

  it("should block requests exceeding the limit and log a warning", () => {
    const key = "user-2";
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(true);

    // 4th request exceeds limit of 3
    expect(checkRateLimit(key)).toBe(false);
    expect(warnSpy).toHaveBeenCalled();
    const callArgs = warnSpy.mock.calls[0];
    expect(callArgs[0]).toBe("Rate limit exceeded");
    expect(callArgs[1].key).toBe(key);
  });

  it("should reset request count after the window passes", async () => {
    const key = "user-3";
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(true);
    expect(checkRateLimit(key)).toBe(false); // blocked

    // Wait for window to expire (100ms)
    await new Promise((resolve) => setTimeout(resolve, 110));

    // Should be allowed again
    expect(checkRateLimit(key)).toBe(true);
  });

  it("should cleanup rate limit cache of expired keys", async () => {
    const key1 = "cleanup-1";
    const key2 = "cleanup-2";

    // Populate keys
    expect(checkRateLimit(key1)).toBe(true);
    expect(checkRateLimit(key2)).toBe(true);

    // Run cleanup immediately (none should be expired yet as 100ms hasn't passed)
    cleanupRateLimitCache();

    // Verify still tracked (counts shouldn't reset, e.g., if we consume remaining limits)
    expect(checkRateLimit(key1)).toBe(true); // request 2
    expect(checkRateLimit(key1)).toBe(true); // request 3
    expect(checkRateLimit(key1)).toBe(false); // request 4 (blocked)

    // Wait for window to expire
    await new Promise((resolve) => setTimeout(resolve, 110));

    // Run cleanup
    cleanupRateLimitCache();

    // Since they were deleted from the map, they should be initialized as new records
    // If they were cleaned up, we should be able to do 3 requests again
    expect(checkRateLimit(key1)).toBe(true); // 1
    expect(checkRateLimit(key1)).toBe(true); // 2
    expect(checkRateLimit(key1)).toBe(true); // 3
    expect(checkRateLimit(key1)).toBe(false); // 4 (blocked)
  });
});
e:ÔH¹øx‰vðimport { describe, it, expect, beforeEach, afterEach, mock, spyOn } from "bun:test";
import logger from "../src/utils/logger.js";
import { config } from "../src/env.js";

// Mock Telegraf and fetch
mock.module("telegraf", () => {
  return {
    Telegraf: class {
      constructor(token) {
        this.token = token;
        this.api = {
          sendPhoto: mock(() => Promise.resolve({
            message_id: 12345,
            photo: [
              { file_id: "photo_id_low", file_unique_id: "unique_id_low" },
              { file_id: "photo_id_high", file_unique_id: "unique_id_high" }
            ]
          }))
        };
      }
    }
  };
});

const infoSpy = spyOn(logger, "info");
const errorSpy = spyOn(logger, "error");

describe("Telegram API Utilities", () => {
  let forwardToStorage, getFileInfo, getBot;

  beforeEach(async () => {
    infoSpy.mockClear();
    errorSpy.mockClear();
    global.fetch = mock(() => Promise.resolve(new Response(JSON.stringify({ ok: true }))));

    // Import dynamically so mocking is applied first
    const telegramUtils = await import("../src/utils/telegram.js");
    forwardToStorage = telegramUtils.forwardToStorage;
    getFileInfo = telegramUtils.getFileInfo;
    getBot = telegramUtils.getBot;
  });

  afterEach(() => {
    delete global.fetch;
  });

  describe("getBot", () => {
    it("should return the telegraf bot instance", () => {
      const bot = getBot();
      expect(bot).toBeDefined();
      expect(bot.api).toBeDefined();
    });
  });

  describe("forwardToStorage", () => {
    it("should forward photo to storage chat and return file details", async () => {
      const chunk = Buffer.from("fake photo data");
      const fileName = "test_photo.jpg";
      const result = await forwardToStorage(chunk, fileName, false);

      expect(result).toEqual({
        telegramFileId: "photo_id_high",
        telegramFileUniqueId: "unique_id_high",
        storageMessageId: 12345
      });
      expect(infoSpy).toHaveBeenCalledWith("File forwarded to storage", {
        fileName,
        message: 12345
      });
    });

    it("should handle error when forwarding fails", async () => {
      const bot = getBot();
      bot.api.sendPhoto = mock(() => Promise.reject(new Error("Telegram send failed")));

      const chunk = Buffer.from("fake photo data");
      const fileName = "test_photo.jpg";

      await expect(forwardToStorage(chunk, fileName, false)).rejects.toThrow("Telegram send failed");
      expect(errorSpy).toHaveBeenCalledWith("Failed to forward file to storage", {
        fileName,
        error: "Telegram send failed"
      });
    });
  });

  describe("getFileInfo", () => {
    it("should fetch file details successfully", async () => {
      global.fetch = mock((url, init) => {
        if (url.endsWith("getFile")) {
          return Promise.resolve(new Response(JSON.stringify({
            ok: true,
            result: { file_id: "some_file_id" }
          })));
        } else if (url.endsWith("getInfo")) {
          return Promise.resolve(new Response(JSON.stringify({
            ok: true,
            result: {
              file_size: 98765,
              mime_type: "image/jpeg",
              file_path: "photos/file_0.jpg"
            }
          })));
        }
        return Promise.reject(new Error("Unknown URL"));
      });

      const result = await getFileInfo("some_file_id", "some_unique_id");

      expect(result).toEqual({
        file_size: 98765,
        mime_type: "image/jpeg",
        file_path: "photos/file_0.jpg"
      });
    });

    it("should handle error when getFile fails", async () => {
      global.fetch = mock(() => Promise.resolve(new Response(JSON.stringify({
        ok: false,
        description: "Bad Request: file_id invalid"
      }))));

      await expect(getFileInfo("invalid_file_id", "invalid_unique_id")).rejects.toThrow("Bad Request: file_id invalid");
      expect(errorSpy).toHaveBeenCalled();
    });
  });
});
õ©·„°Ðx ÿòimport { describe, it, expect, mock, spyOn, beforeEach, afterEach } from "bun:test";
import logger from "../src/utils/logger.js";

// Mock db
const mockInsert = mock(() => ({
  values: mock(() => Promise.resolve())
}));

mock.module("../src/db/index.js", () => ({
  db: {
    insert: mockInsert
  },
  files: {}
}));

// Mock nanoid
mock.module("nanoid", () => ({
  nanoid: () => "mocked-nanoid-id"
}));

// Mock telegram utils
mock.module("../src/utils/telegram.js", () => ({
  forwardToStorage: mock(() => Promise.resolve({
    telegramFileId: "tg-file-id-123",
    telegramFileUniqueId: "tg-unique-id-abc",
    storageMessageId: 98765
  })),
  getBot: () => ({
    api: {
      getFile: mock(() => Promise.resolve({
        file_id: "tg-file-id-123",
        file_size: 1000,
        mime_type: "image/jpeg"
      }))
    }
  })
}));

describe("Upload Route Handler", () => {
  let handleUpload;

  beforeEach(async () => {
    mockInsert.mockClear();
    const uploadRoute = await import("../src/routes/upload.js");
    handleUpload = uploadRoute.handleUpload;
  });

  it("should reject unsupported content types with 400 status", async () => {
    const req = new Request("http://localhost:3000/api/upload", {
      method: "POST",
      headers: {
        "content-type": "text/plain"
      },
      body: "plain text data"
    });

    const res = await handleUpload(req);
    expect(res.status).toBe(400);
    const body = await res.json();
    expect(body.error).toContain("Unsupported content type");
  });

  it("should process JSON upload (base64) successfully", async () => {
    const req = new Request("http://localhost:3000/api/upload", {
      method: "POST",
      headers: {
        "content-type": "application/json"
      },
      body: JSON.stringify({
        file: Buffer.from("hello world").toString("base64"),
        fileName: "test.txt"
      })
    });

    const res = await handleUpload(req);
    expect(res.status).toBe(200);
    const body = await res.json();

    expect(body.public_id).toBe("mocked-nanoid-id");
    expect(body.telegram_file_id).toBe("tg-file-id-123");
    expect(body.telegram_file_unique_id).toBe("tg-unique-id-abc");
    expect(body.file_name).toBe("test.txt");
    expect(body.file_type).toBe("document");
  });

  it("should reject JSON upload without file key", async () => {
    const req = new Request("http://localhost:3000/api/upload", {
      method: "POST",
      headers: {
        "content-type": "application/json"
      },
      body: JSON.stringify({
        fileName: "test.txt"
      })
    });

    const res = await handleUpload(req);
    expect(res.status).toBe(400);
    const body = await res.json();
    expect(body.error).toContain("Invalid JSON");
  });

  it("should process multipart upload successfully", async () => {
    const formData = new FormData();
    const fileBlob = new Blob([Buffer.from("multipart hello")], { type: "text/plain" });
    formData.append("file", fileBlob, "test_multi.txt");

    const req = new Request("http://localhost:3000/api/upload", {
      method: "POST",
      body: formData
    });

    const res = await handleUpload(req);
    expect(res.status).toBe(200);
    const body = await res.json();
    expect(body.public_id).toBe("mocked-nanoid-id");
    expect(body.file_name).toBe("test_multi.txt");
  });
});
IU*å™xU ªÿ€ù�%beforeEach, afterAll‘A±‚z2
  afterAll(() => {
    mock.restore();
  });
});
·1Øï	‚“>xŸ `ÿµ³� un.lock ¬ä¸1%¶KÂà¿{Æa58 é»‘â&index.ts ö{,dTîÏŽsxÏé6¢Ñ¦“¢S³=ße�$Žr9ý†
J®ñõP“)O=j;l±†ó¢ŠÒ8…‚Þø�Î3_100644 tsconfig.json ²çI}‰r¨´­ˆÀâåRå‡%ê¾FSé€/xY ¦ÿ³æ�¨`™Õå}]õõ"ò®ŸÕ¤–‘¼&!package.json ô¶w”vÑÓññ­Ý[ÖñØ…júô“'Ol>�Ÿ8g]d7ÑÄYÕIï†ˆ§d+¦çƒ°xW ¨ÿÞ°�ž‘î1“$kj“�j“®!j“Ðj“ðj“j“/j“/otstrap.test.js“îö“#
  }
}
ü��ä€Ox4 Ëÿææ�ïÑ€TÇa\;‚‚�Ÿ˜=Wzï�œ“Oj;l±†ó¢ŠÒ8…‚Þø�Î3_s2áƒ±Gx1 ÎÿÞ¾�ž‘î1“$?“ïötypes/bun": "latest“ 
  }
}
î kAx ôÿ¾¢°“çñê€ò xJ µÿŸŸ�@ÂQ—Lý¾ß›ÌúñR¦!×³8¡ˆv‘T‹¢™òˆIA6‰
Bév _#š=‘ò™šÿé IBéÄà(ß–ëk,Ó”U&#Üo€ãx ðÿÞ ¦ �6±@ì});
3fï€‹}x_  ÿÒ+Î+�%OspyOn, beforeEach } from "bun:test";
import logger from "../src/utils/logger.js±JV});
tl!0ë…ÇxË 4ÿÌ¯è�°‡types/bun": "latest“Á,“%³Â
bun": ["@types/bun“q/#bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],

³Í)u5³`½7û|C(¾x áÿconsole.log("Hello via Bun!");§à	î¿-xß ý{
  "compilerOptions": {
    // Environment setup & latest features
    "lib": ["ESNext"],
    "target": "ESNext",
    "module": "Preserve",
    "moduleDetection": "force",
    "jsx": "react-jsx",
    "allowJs": true,
    "types": ["bun"],

    // Bundler mode
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": true,
    "noEmit": true,

    // Best practices
    "strict": true,
    "skipLibCheck": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,

    // Some stricter flags (disabled by default)
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noPropertyAccessFromIndexSignature": false
  }
}
v@äPïŒsx? Àÿ³³°W3VN_û�‘yŠŒ%sÕs™/Õ×ý40000 test ]ŠÁV¥Ñådž€t¯¤¨¼a=r“Š)Gmi‚¡1x	 öÿÆ¢�a‘…AÍ³h‰?x ÷ÿŸò±-rûèï�ox? Àÿ³³°W3üžDÈy–’©&ç	«¸¯Bë¯ëC40000 test œ["˜ð�Çà¿U*|¨¬x‰\Z“Š)Ž�Éj‚¢-x
 õÿÆ€‘"?‘…Aà’hŠ<x ÷ÿŸË±TK8ÁïŽlx? Àÿ³³°W3¼ÕÞ°$£Yø6üÚÄÍ¼Ý¹40000 test ›\^ØˆOfîâ“òÔêK}?“Š)žmî}x áÿ€€�Le²©wÑýbiH[¢za­æð#€‘` ÅS©xI ¶ÿ100644 files.js w,â#ðSh�«>ä½ù˜xø°&100644 upload.js )Y Ë�úô¢í­)�ap5!ç³kŒ"x ôÿŸ¡‘T¥“#|ï�Ux? Àÿ³³°W3¸(Ù^<“!G+{h·Ôô�üƒO»40000 test ÒAdè­OõûŠûÌõÛüŒ&4“Š)bpµî�fx áÿ€€�Lk<Ç^½ùØˆÄ]�Ã¤�Ÿl]L´‘` Îô¥x% Úÿ100644 upload.js )Y Ë�úô¢í­)�ap5!ÞÜMk�hx ôÿŸø‘T|“#|L0ï’x? Àÿ³³°W3°‘—@�þeU1¸tb˜—.�j40000 test r<ºñÐO+PÍÄé³ÃŠ“Š)ôŒ hƒ,x ÷ÿ€_�E‘fÆÇf}x ùÿøÎ�Î'ï“x? Àÿ³³°W3.o’s¿Ý\ð€K2ÿ£’ò„\:40000 test ;VÆ1ÐŒÔHé6§ÄïïAtÄ“Š)JÔéwx æÿ__�Kp–Z}®³¿ÃÈÂ)9ÔŸ¼�ø�šae�¨tx úÿ—p�pÜ	f�x ùÿø¢�¢
(Ïï”/x? Àÿ³³°W3µ—ï¾(yÇ :k²vû/j40000 test 6¨Î6ë7UwkŸ÷C˜4‹"ÂÎ“Š) ¿sé�x æÿ__�Kr ÀÒuo4óR½®…¾*/ió#ŽìF­xM ²ÿ100644 logger.js g«T‘ïÂ`x]É²UÈÍ:SEüâ100644 rateLimit.js ø
„ŒÅµ÷­ï_}gM4Éî˜ˆèh‚}x ÷ÿøz�T‘|&œ‹ï–x? Àÿ³³°W3�6J	Ø¡dð	ã%”êºŽimG40000 test EÃnÇµ›¶]ŸNƒÀ5•—#U“Š)�é‚zx æÿ__�KzeÊ1Svß•øQÂ{>yZ�±2ˆ
æ¥x% Úÿ100644 logger.js g«T‘ïÂ`x]É²UÈÍ:SEüâûKçd€>x ûÿzM�M@¥ï—Oxo �ÿ³³�¨?ê;5¿në8.DQ°ë‘ÈÀÜ#‘¼W7\Æ®bü†‹'Èðˆ‘ë>F—Ï“'03¥)*ØWdZÎ@YýŠö§ŽUÈc-¬40000 test ÏùHŽ_þ7VÝ°YìŒˆ‡ßîç“Š)“3žê’Lx
õþè�À	�s“k8},
      "peer“ltypescript": "^5“½.³ü Ž“C'»typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/tB7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw“WŸ  }
}
ÞVê—%xJ µÿ¾ä°-“„drizzle-orm": "^0.29.0“«6,
    "nanoid": "^5.0.4“à8
  }
}
;…F¢xB ½ÿ100644 env.js M g`•'¿«~0äØ6~>µºÄ40000 utils zeÊ1Svß•øQÂ{>yZ�±2B1§x' Øÿ100644 env.test.js éògÆ3ÿS.s(D_Ú B%·ý‰¨ïƒsx àÿ³”°XÇßÞwlÿè—È a*�ªt“Š)×èõ x  ßÿ40000 utils zeÊ1Svß•øQÂ{>yZ�±2µ�~k„Nx ôÿ³ö°M“Š)Hôè„gx8 Çÿ³¨‘(\µ´6sóÉ­›y–Ãg§-çÉéÚQ‘˜{ÎÃþ'ï¢5œH§ŒÏRÑK“Š)@ˆ´xä ÿ# teleuploader

To install dependencies:

```bash
bun install
```

To run:

```bash
bun run index.ts
```

This project was created using `bun init` in bun v1.3.14. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
¹¢Mm·xÇ 8ÿ{
  "name": "teleuploader",
  "module": "index.ts",
  "type": "module",
  "private": true,
  "devDependencies": {
    "@types/bun": "latest"
  },
  "peerDependencies": {
    "typescript": "^5"
  }
}
4B:·¨x(×þ100644 .gitignore -'NW|ÝŸû0²”ad/µ
[¥Ž×100644 CLAUDE.md vLÙ�±i½÷~›~Ärƒªó(	100644 README.md µ´6sóÉ­›y–Ãg§-çÉéÚQ100644 bun.lock ?ê;5¿në8.DQ°ë‘ÈÀÜ#40000 docs ?6¯ÜÖÞØ'x°ž3ÑHÉÛŸ�“100644 index.ts ö{,dTîÏŽsxÏé6¢Ñ¦100644 package.json ÎÃþ'ï¢5œH§ŒÏRÑK100644 tsconfig.json ²çI}‰r¨´­ˆÀâåRå‡%Ð~wÚ¦x& Ùÿ40000 superpowers JŸÑ¾©÷JÜË5&™õŽ(zé x  ßÿ40000 specs ´IÚ*ÁÑ9ÆîÞ Ð¹Ÿtè�¿ìÍi‚x	 öÿ¨‰�”‘³uzÑV„’Îê+Ó@¸rÍ �ôïBk