From ab1429dd6388d611e81d485756eb8b5bc942a2ab Mon Sep 17 00:00:00 2001 From: Andrew Masiye Date: Fri, 18 Apr 2025 21:41:21 +0200 Subject: [PATCH 1/7] feat(core/scenes): update Physics engine --- src/Core/Scenes/AbstractScene.php | 6 +-- src/Core/Scenes/SceneManager.php | 15 ++++++ src/Game.php | 33 ++++++------ src/IO/Console/Console.php | 1 + src/Physics/Physics.php | 84 ++++++++++++++++++++++++++++--- 5 files changed, 116 insertions(+), 23 deletions(-) diff --git a/src/Core/Scenes/AbstractScene.php b/src/Core/Scenes/AbstractScene.php index 846cd07..4c3241d 100644 --- a/src/Core/Scenes/AbstractScene.php +++ b/src/Core/Scenes/AbstractScene.php @@ -174,7 +174,7 @@ public final function start(): void { Debug::info("Scene started: " . $this->name); - $this->createWordsSpace(); + $this->createWorldSpace(); $this->loadStaticEnvironment(); foreach ($this->rootGameObjects as $gameObject) { @@ -388,9 +388,9 @@ public function __unserialize(array $data): void } /** - * Creates the words space. + * Creates the world space. */ - private function createWordsSpace(): void + private function createWorldSpace(): void { Debug::info('Creating world space for ' . $this->name); $width = $this->settings['screen_width']; diff --git a/src/Core/Scenes/SceneManager.php b/src/Core/Scenes/SceneManager.php index 39c289e..bd8c76d 100644 --- a/src/Core/Scenes/SceneManager.php +++ b/src/Core/Scenes/SceneManager.php @@ -15,7 +15,10 @@ use Sendama\Engine\Events\Enumerations\SceneEventType; use Sendama\Engine\Events\EventManager; use Sendama\Engine\Events\SceneEvent; +use Sendama\Engine\Exceptions\IncorrectComponentTypeException; use Sendama\Engine\Exceptions\Scenes\SceneNotFoundException; +use Sendama\Engine\Physics\Interfaces\ColliderInterface; +use Sendama\Engine\Physics\Physics; /** * Class SceneManager. Manages the scenes of the game. @@ -44,6 +47,7 @@ final class SceneManager implements SingletonInterface, CanStart, CanResume, Can * @var EventManager $eventManager The event manager. */ protected EventManager $eventManager; + protected Physics $physics; /** * Constructs a SceneManager @@ -52,6 +56,7 @@ private final function __construct() { $this->eventManager = EventManager::getInstance(); $this->scenes = new ItemList(SceneInterface::class); + $this->physics = Physics::getInstance(); } /** @@ -290,6 +295,16 @@ public function getSettings(?string $key = null): mixed */ public function load(): void { + $this->physics->init(); + foreach ($this->activeSceneNode->getScene()->getRootGameObjects() as $gameObject) { + if ($collider = $gameObject->getComponent(ColliderInterface::class)) { + assert($collider instanceof ColliderInterface, new IncorrectComponentTypeException( + ColliderInterface::class, + get_class($collider) + )); + $this->physics->addCollider($collider);; + } + } $this->activeSceneNode->getScene()->load(); $this->eventManager->dispatchEvent(new SceneEvent(SceneEventType::LOAD_END)); } diff --git a/src/Game.php b/src/Game.php index 413c654..c8e808d 100644 --- a/src/Game.php +++ b/src/Game.php @@ -155,6 +155,18 @@ public function __construct(private readonly string $name, private readonly int } } + /** + * Destruct the game engine. + */ + public function __destruct() + { + Console::restoreSettings(); + + if ($lastError = error_get_last()) { + $this->handleError($lastError['type'], $lastError['message'], $lastError['file'], $lastError['line']); + } + } + /** * @return void */ @@ -204,7 +216,6 @@ protected function configureErrorAndExceptionHandlers(): void private function handleException(Exception|Throwable|Error $exception): never { Debug::error($exception); -// Console::reset(); $this->stop(); if ($this->getSettings('debug')) { @@ -221,6 +232,8 @@ private function handleException(Exception|Throwable|Error $exception): never */ public function stop(): void { + Console::reset(); + Debug::info("Stopping game"); // Disable non-blocking input mode @@ -495,19 +508,6 @@ public static function quit(): void } } - /** - * Destruct the game engine. - */ - public function __destruct() - { - Console::restoreSettings(); - Console::reset(); - - if ($lastError = error_get_last()) { - $this->handleError($lastError['type'], $lastError['message'], $lastError['file'], $lastError['line']); - } - } - /** * Run the game. * @@ -525,6 +525,11 @@ public function run(): void while ($this->isRunning) { $this->handleInput(); $this->update(); + + if (!$this->isRunning) { + break; + } + $this->render(); usleep($sleepTime); diff --git a/src/IO/Console/Console.php b/src/IO/Console/Console.php index 997d47e..1885389 100644 --- a/src/IO/Console/Console.php +++ b/src/IO/Console/Console.php @@ -229,6 +229,7 @@ public static function saveSettings(): void public static function restoreSettings(): void { shell_exec('stty ' . self::$previousTerminalSettings); + Console::cursor()->enableBlinking(); } /** diff --git a/src/Physics/Physics.php b/src/Physics/Physics.php index 6d5bd8c..9052bc9 100644 --- a/src/Physics/Physics.php +++ b/src/Physics/Physics.php @@ -35,6 +35,22 @@ final class Physics implements SingletonInterface, SimulatorInterface * @var Grid The static collision map. This is used for detecting collisions with static objects in the scene. */ protected Grid $staticCollisionMap; + /** + * @var Grid The world. This is used for detecting collisions with the world bounds. + */ + protected Grid $world; + /** + * @var int The width of the world. This is used for detecting collisions with the world bounds. + */ + protected int $worldWidth; + /** + * @var int The height of the world. This is used for detecting collisions with the world bounds. + */ + protected int $worldHeight; + /** + * @var array> The collisions in the physics engine. + */ + protected array $collisions = []; /** * Physics constructor. @@ -42,12 +58,10 @@ final class Physics implements SingletonInterface, SimulatorInterface private function __construct() { // This is a private constructor to prevent users from creating a new instance of the Physics class. - /** @var ItemList> $colliders */ - $colliders = new ItemList(ColliderInterface::class); + $this->worldWidth = MAX_SCREEN_WIDTH * 3; + $this->worldHeight = MAX_SCREEN_WIDTH * 3; - $this->colliders = $colliders; - - $this->staticCollisionMap = new Grid(100, 100); + $this->init(); } /** @@ -70,13 +84,17 @@ public static function getInstance(): self public function simulate(): void { // This method will be called once per frame to simulate the physics in the scene. - # Get clean slate of physics world + # Get a clean slate of the physics world + $this->clearWorld(); # Update the physics world + $this->updateWorld(); # Record the collisions + $this->recordCollisions(); # Dispatch the collisions + $this->dispatchCollisions(); } /** @@ -168,4 +186,58 @@ public function isTouchingDynamicObject(Vector2 $position): bool return false; } + + /** + * @return void + */ + protected function clearWorld(): void + { + $this->world = new Grid($this->worldWidth, $this->worldHeight); + $this->staticCollisionMap = new Grid($this->worldWidth, $this->worldWidth); + } + + /** + * Updates the physics world. + * + * @return void + */ + protected function updateWorld(): void + { + // TODO: Implement updateWorld() method. + } + + /** + * Records the collisions in the physics engine. + * + * @return void + */ + protected function recordCollisions(): void + { + // TODO: Implement recordCollisions() method. + } + + /** + * Dispatches the collisions to the colliders. + * + * @return void + */ + protected function dispatchCollisions(): void + { + // TODO: Implement dispatchCollisions() method. + } + + /** + * Initializes the physics engine. + * + * @return void + */ + public function init(): void + { + /** @var ItemList> $colliders */ + $colliders = new ItemList(ColliderInterface::class); + + $this->colliders = $colliders; + + $this->clearWorld(); + } } \ No newline at end of file From fdfa7e403910bdfdc33c19ba68d4a32341001f98 Mon Sep 17 00:00:00 2001 From: Andrew Masiye Date: Sat, 20 Dec 2025 21:45:00 +0200 Subject: [PATCH 2/7] chore: update repo logo image --- README.md | 4 ++-- logo.jpg | Bin 20909 -> 0 bytes logo.png | Bin 0 -> 61094 bytes 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 logo.jpg create mode 100644 logo.png diff --git a/README.md b/README.md index 4c3c544..65ca666 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -
- Sendama 2d Game Engine +
+ Sendama 2d Game Engine
# Sendama — A 2D Game Engine for Terminal Based Games diff --git a/logo.jpg b/logo.jpg deleted file mode 100644 index 3b81c018a5a64805b9c1908165c5ae8ac57f10a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20909 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 QJ+#YR(|Xe>?D|jt**5Bkv;Y7A diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d95815a4a3ad1877b04288a095e887a508cd9a66 GIT binary patch literal 61094 zcmYhj2Rzm9`#*kAQ8JoFWva(k~vXXH+_9!{_%s3=< zjN;fF9Ebn?I{N&+e~-ueQSZ9Pb>H_jp4aobUOm5MsLgPK{R9jKW6-&I-53Tt3VwU! z8r?DQ%N`B<68Pn~`%QCC7>xc5^dA*0HJud(SdZT^d(2Q&TJty6%sNGucId11_Xz*QtouzjZcE;8 zK^{MM>Nxd_OZVtnyCkPcfs?7(QbZp!Z!==yihrXGafZ8PWyrIqWQmmF*~`XG4f8rG z8~0V$^`Z|akPZ5A2s1s|9gkYRTv7JncA8N<73EKns=1Xu#S^BT1TIXy(eFgE-N+Hj zU%;}%@mm%cr}fQ;_FLBs^dJ4-jO^z-N?3wf=sD`ykv!d!Q?+t17;-GT>y7hcd$$^T z@IPTWqJ2I+UT&;-l6n)^|>WExBi=&3FDZm%D&BaD4iA zlQXrk(eJbW`m*$eq4#2M1HpLKyK7$rkHgSbYR2rMJ|*B!;%_?(VhM49WUh6t8t`h! zFhQdpXWk^RP784%BmwiMHkgd-TaB2po*whkc%@xmIZKrSt+gV`fn3eN4th z^o5R4Gac14wT56^_CkB>QP)W1&Zc+X@X>M?S{N)`gW{8NO7s2*q-tKA zd03j%vh8Ya>e6@|2_MDyjLZgusfWgyEl*9vT%Aht>zFcVQTtlT0!GhU=o0J)>A<1U z!WAqZY^Hd(=3ssP>=(w&%>5!h;Zt2p!!cVrd7&hifaGm<+U& z=l%-99vhYT^jmxDZFctYSC6SBXMB78?B)Iao=z-1S|!HP?}R`~Barj_Ibmk{7h42! z$2&#&F+0OXsd9VL$Z7wz4-UvVpUIPrEHB{wE(pHY>EkEQPx`(hW2n4X{H4UvMYwGn z^W1WLbNK>fIW?K=e1h$SOh)C2thBPV=VX6arBBK9*?xL!2=UYZ#*dPgO$PS}S4R#ESLjY9j(hwXFOWB;uEwJBFSgN%!p8MF{U zSIqeMM)Z{_pX^%Hk2bfRKsM*BcI>BxxP9Cl-2UJ4M%WJ+L$_VCX z&s{e7Tar;Z(sNGS)PnOlc?_wM2h7AZpq)|PSL5TzlK0b3D>A0ruwYE@)H&(B5I?iU zUg$PAME%!t%>ABCjVhm#p+|k&4{FM8H~RA58|4NXrm;zP>%yT-4Q9Fxxx6Ynd2T70 z!Q}{-|H_t6k=DUvn2H_)&IlpjHK4gCY=7^@e$C^>-s2n6(c;SgA>b)r)x|^Ym%&3D z$!A>(DO9!+L0c*{#`!IHd$0G!ozq;uPh?eJkdgbeJR7|K-Fvo{hppfMtlD%^)(D?I zIjlS)OC&21cHQz9?I+KXPV2Zv0W z_I4~EcUp@^Y&QVE2HQ1k=x>;4P(tzAHlTam62}HcvpDCEvKN`+x8|ys%DWmqXsvzB z5ziAXlygb{;oDv_t3i{_c%qB&BX_5vLD~|lG+d?2_3Hjzpf*VJx3>ddPkirkrTfhP z?tS+|+pxI6K^N@opr2)ZS%?r?drMa>aa?B&|JEr4WtCEfcu8hMh7?3HbP`COD@oxJ z&&lS{It}QZM6Mg3mz+vx&8^0ie3+1kw=(|f7Ydw96LBsNf8UD1WXqV#g_9Yk&eeuw zb2}As4oo!q&eHBwuCP48_O8=J+z)jC$ID)J9nb0vIVW_atIRTCjD8Hy?U?zlR{KKO z=sDdY`rZ3VPlhrtqdN;$$*XTiH)+V%&QvetlLOLF%Uxf454>+dL^Gzjr%+S() zp{H73DClV6yHBKkrjwaIJV1^cPm-1UtcEEgKng)^uj#t+5;8JO40^`%6I$_r6`?J)(&%RHg-;;1)6QK- zJX}A)+t(I6bn|>CVI(#*b1FNM@a;2n!?8k^ey=AGgeQGqC9S-z^16mm6>3GDlzPt> zm_}2(Y2NT3n+Adj0^&@9E|2i-qnoh$Yfgl_{%d*S+X~|L*xzMcIV`jhA zL=$XWc6ZMHcakx08f((EsWu}rb${e|_BY0UEV0+Swwyl1#tpO7H{YUZM}(q9Q@x|t zHXUYi_$U*@*O9wxw#OLi4H0vOS4QAxYqenYns!y%vc!|oWa-@>W)8hklOX=WVEhT= zBDrcZ399-ZW0(UhLy%w3md#FPG+yKYv3ODfZap#~D8JP^`)h$VHF;FkCkC37&F|g@v%1J7`Z7H*(Yy?8M7Hrbscj9EiSX!E;-IslgRZz*DTsbwLENG^_ zMs(~MypIv_DAI>99A)!{+d=vdM{mpE{SmtCTA`|MS_l5hJH*i!;LgGDzKi}I78|H% zu4N)@>KmVg;3J)j+W-g6BRm3KS@x90YsF3K8}t^rpu2ONJI99>srh;DS6Nx~Voaos z027P8RHMQlc6ItGJ|7=we2v`Yl2?)uV6?hhrs(H#dM@F84|^S1A2{+)94bmMWol&5 z9A*+4Giu)-dY(S7#oSDsFRuwQlTf6Wc2}@`bKLIm}fu`j9mF#;c(%TL!zXon>UJVshHuI7LjO_cJ zFcuGP^8R3=z|^xzCfgeP)^D9zqN~^Wrgu}kPHRJ8FuK0SVF(OHs`4_y=bxZXVq3e* z1F7_rCVH|Sgt&E?O$=DWW~O+tPOG7e2i;Z1dIV;zlVHO98Fp?3eT1)9b8BRRnDS*G zHRPYyg50TIh=Qt@J=Dpw@Qc^C{kt0Kz*zOE+YS<(;JY=BfOJ(|3fG zq^?2oXT_6xIu_+majP!PPSe!u7G}uIa2UmhMq3Lamo6Ipm^wN=1wj%bv~&{(mK&DJ zL|x8xO)8KpJlf_R1lzW1g=V+e?R|n4EvUSh{5@`Oz$7bt+m;|5Kq?5JiPwXeF*;Eq zfztEyH#%BOERuhl30wNz0>i%~WmPL}?&nm5kZm@1xxn*)IkvB`MS)mCjUN-=bo5EZ z0KaoT%fK9#IimSu0M(bg{%O7%+s!HCS!l3i_8RSzXH1uhWK})xE^@&JbF@C)MO?5< zo>Macu54aP@^gYf=dWYP=}AcwwD}FR; zz{yrpsLJ!=F(iZnSZe`}2AJ|$o7UDH3(i>7Lm@pwGY5Dgj-g`P_LBy{jUYNma`o95*tuO>XzzJBr`VcDGhaFGBd*1B>&CP5IrN3)ocr+?UlI@|TY z-)p(5a?&1~U5Q!NJ_&2=fh$5gCSUR2++UOYAnIQ}utMeU5j$a8)p)JhI1;>&-S7*5 zo$Y$c5^*?Ln=v&2SRG=d0*$|{xFxn}y?PJ?T}#)A44EKq`+H9(OsiM3Jqkfe!+Lr{ zI8Ud(TOkYX+fGnh3l#7n!xZ(FlsXB2Z%$8fC-C>)(}(7I!I_i!A4-bO+k+sR%JhSv zRy-w!CSr@eece9^Q`9W1f5iBt!E$D6a+av=An_>EuqO%7v>jE<@W(whJi8J}yyS9o?kLfL}>SH(fii z_sD9eqZKY(bzu5NIdfQBop#9%?uDEPQL$f{!&!r zV*V;?KK50p!K)i0_O0J8(~ID@is@Pg8p(o@h00wM=iCgR+GX@OM6ZiKe$98mkLz3Y!K!T%vfx*xNCwn+&t z$4H@#$6{}P6Hx7eCcc%|6nZ5$THnUWC`k& zvIUn7FS;U;UoC59ByvQUp4-&n+hFKPYM?{~GC|q+knI$pd57seY(v?8t2&OhOmA(3 z56JIz0U{-wysxL!-%1It!Gt=@g*QcakMJI+?w?DD^i7zcX~aKdhPL(c*u3GeEM^_t z>=e~BM7$O9@fmI~t&FkOda{|vJwr+<{v2jryJNAt_!{KIkP2@BFvV{@Z=o$8YDg;c zyDy29TPnOA*f$$2w|>T8#EXZ{O2}xRe@Z0!vAWu5&}rXhtB2ec&{oNl2BT?Z(r?n6 z<{P95wE37ATlP#*bZ;kNiS1aJ+U;DqtjFpS@wj!pSz@%mA6Km^G;7`^HMuc?t#Tg$!~}q-nmKIpM3pc41Vh_N zzLv2WgCiP+&5!#9Ebbyhasz@{^019^L=6{_BPSd0q$>0Hkk#4pe2mwJH(lqJE&P9Z zL5Vu-;(+7TTPfsxNw1snCTtwGa!a*GYelN$T~BCRFjAM<^7GWh*1c&b^gL0hwSib1 zCTLdQNxt)3!ssGB8`=;B18N8D5cUrf4Z~V=+u=b>Mr|~c*C?>hrP*pSyDRhT;iWdg zZ@0dBgWEjH^!P#|e{YU?(_$ac$Q@@~T=c-=6IY?-^*$R(FyIy;$D$(xCH zLIDk?9P_xzs*~w$EH01VbItlC#n?8O0fp#cX}yBvumVa1e)1d$~f=$$T02CkVcS>8Rm?cE0R)UcVkdIE%hKdU^({E5A_n7 zk<4p9?bIEQLeyk!og^idhCY(>RoSlYF_FJL>i@ErfyXB4&i**+F_l6AH|klp z*lli|R<|kukmUXdE%1N$zMiG}+5O=^pO@BxyDIM(2w{Dex534k;j5{M6RMEbo1I{)EUp&hq?oQMztIolHFduf zKtNmoSH_KvugC|7j~tbK+uNdsCMI;D_14S=|80=GN!z-j_OLC`IX?L zal3-6C-Ff6uU>V_Gr5VSJWLX?^kdF|k|1P`MyRYP<|YKdT#oEMZxbOIor3KvG)mZw zJa72nxyEik{m-yqH4iEaM`t|yM_8DMac{fe!bL$$oXB65u+8*l#5r?p2 zuLWk8-*q+IU2ED-+&hS|=IfJ45SU7qfjt`SFL$}VYo%z}u*&%PEBRN--gLTJH>X?VX}O-_glHnzqdba!#eE3anLxn=EW>h^~|r{ zMxf7io|(T04Y$=@b5EGqzsqX;02}4R+s8G$5SYL*@NuRlyk4$4>M^j ziIVGRRqMY2%~045dEc|KO_zv9&e1}q2PGXu zx_79{>1q(Wy;T&4S%-U3Vr1VRW;=s>e*J(;A6CcK|DINgr$u7THyFQq7Stq7gjh-W zseq1W!VyRX2GUSqcbYj1nZJ{WU z|4zWoW@%jhBr9#`-kfEtp^c4si(5Y zSEvHiUS7#*HZgRMJRxo0O5VE#P+H-4czktel(lpX3gE(l#7f}E0jz=qX_8wzV=%Lo z7)Lwa3mjNj^`^M&q9cav$6iEbCQp=Ks2F?#*v*c`4|2OJ&x5LL7i;=C@jklJm!^C& zCj`)_w!^TEGzQsTepiDvEArjel@iueZBl?TzI!pl19vr=XT>mYn$yA=b#n9Lp3L{| zf~x8t;X^(5&WTp{al8i2|GdJZGO&wAoe?u1t?Q z*0;Z-L4gN6c%ps@;nb)VThdG4^@Zu~UW`*&^_05$4awF|SR4K;3q!i`UE)tzc% zrN9yOUMzew0hjo-?0Vz~kR~Iit^GK_;xd{|Vk4UXM^07>f5gn)b8w5tl?-kx?>ujk zh$gf3sesHZF5K(8h#nXBAx44a8-V1`;FHG-Q-lrYSt zIufWQY{13CB#|OU$2l}DhKvzZb0MR#x*W6~!r7OwF^ z_Wrr4(9Av0e@Z>i3fKzliDZV3MV-nTZp|16cXW=l#?Prbi$H(R<)*#F>7mRL8D-un zHG}B!ivW>EM{|q?1^X6TIF(*}bSWxvJYryl+JEi5J@(to))`F6lC8DKdZHC5Y>4|T z_lt;Rw~7Ej0KIb4#8Oz|_z|&XHFiC&I)88L{=x#XyX$`-&of)*t{s`rQMpy|@gD|- zeNi|%c&1>JtTWaUj-QV90F>pmT0?6VVFr3T;`vGKhI!rnpZj+E-g;J!yvovZ$;UnlZvsZS+(~}VW0;!s(;0|-ybBfPD7HJ$C!pdhohk`b2}%AK`fQbGwY z&kW7|bXR##O*_asuiK5FFoVj)d=Z+!gnNxG|VyU+O)*d#qxpb<@L%6MGt1Ch@jx5VMdX|vGj<(4gB=Lo?-M2Vy@0MZrF7C zGJvTaaSuBPpk$1&pKe{l1EgTqt*O<*k}y_~Ml11|oTThuHDTzq9|(NRx*DqVzQ7ud zN8xU$!)e9}Cno z8+;kPsP`M@MPt9^U@As=AxQ>^EJS2o_W!uAN^+ulX5i$lHq>5*np+)ss z!76l|KItQfXM>nZjL&mv5Gl(g^NN)%4=Nx1>~7SA_hLV3TYb4I9$#Jalk~7e`LW`m z&p`o_?R{5>2?urLIT?2K0<6pPi6znPALf7&8GKB+A4UCtXr+t>W!C2o<0LgWy-epp zThceU$z84x?02FJb9m0~Q#ny8;dgoJveAA@8%^>mQQtzz6d`{$v^U~m|KVdeqySO~ z@1r-}>hIiZ3vdsK^tJW22gwfm=eI=O;e&LY0tLjlk#)$U^{T@hNr7LBh-MCE=ORPF z^&g^0+gCyirk^wpEM!|)X{fVH!Xqcd#mskz;))d5sD6n8C2M|$kr8=18DUhCGsO0_ zT~!Z~)2h&KESnc4gGaU}fXX<)r>!Jw-6B4aYW3CQhwJfMxxYq22v9B~afoAJwu5bC zL3!ctLN&f2Z>+|s-*%awuzZwI~IU(UP7!l zAXzrEx%UfJG}QhdG_gfsTP^ik8aorkl%K6q24%BoHg)xghWs=VH?D8cUSy~0VgMCr zoo(OfPwrQLQ^u%=BDM~^K@$fcfSMMK9VA3|7`MX#@A>Y4^7k884Z5au0O@lHwhdt1 zD{DUFSPo$E5Etl|$Z7cD-}6e8alxQgSTxaKUMH9xY`VG22r{2ObKTbXnd+RM4e-Kp z=cgf*!}UFssj>C--!{bRHbEIi9aKptxg076mC`^ue9!h#JO=3y9X3!v`uz&e{YoVuP&ED3*ZT)yp~_lJU) zU^Lfk>KmnMKRhsM0zifvxg|s|*g>d%LJFuKSUD0vVMdyev8Y`9*0;Dsq{MSD=DSeE z?-?JxW^&za^ELediVij*jpc$~P9QFyWKiBS0fA57fI-OuF~U+dZ(yU(M6pp;cV~(z z`MWaDtFT-9y`Lx*KY3srX#X!xswcf3IgOb#_j5-I{s#K_Q7k%=X2!?-@pe*OW+r6` zWFs$A%p2U_CzAI{r1YFvfVv&BC_h>U_eBob2UxbAP*vo__p?V%6oGb{7eL}!p&x+a z|HeN(rCoX7kt-PTX&`+{5QvEf7i)VjukFJ*3)(MGBY;Qk$E$mKs=1)laL5?LB>D5O zCNtIda7f+>(K0S=`Y=qvp^N*qA`zqk(uIFo=a@2)3153L%>caNfUfNlUWA1A@xiL> z$N~kD2dDXl~2hxzoB zw%@{b%MsLbF!g0CTwRAb_AbSp!5s86(@jrP+q{h=Xh(&O@uJa56M;!i=9>*0puY$F zc)6c5Ufch5=jN6sK+`%!VLfpu`Gkm23Ky`;KpEpO&HMxqjOxE?K?8%57_FjLBc}d6 z-li79!+j4aVaa2J0hD5H|^P#Cxd3nja562>a&9alrJadUl#hwDpB z7+d8RNrhs?S3t7wo~y{LQ~sxzu)(Q8(W>L)D2@C)pPPR&Q=mHYVd4XG2>DPbXn5hu zGe8+84RFIwhixXC-HNSyvXqfPKPow8IYb7E}TIPdKM?K-4uaGW_eh7 zT^kblA9qR_C~2qL>N!(g&I7~T26YLGmm%+T?X@u(mUU~r4Z_5Xe<>fV?+TPc3BO-R z5Mat4o)-jVXq(LRJ3-@R!A5{E0dx#yIrVQ?f;T_!&*h=JUTPMvLSA#v=KzZOLFoYm zVE$3;Fx&5!E6|BY_!@_FT8TNE^WM6xO1iV#{9T6uT9UOWS1Qo$&)@flIj4S-RG@T` z6~%Cxng+o!JxJGeZmk6aa^qxBe4l4RAi-yzBRD0wKgmMPMsmXtl)}xhCSc^{CU6KnSYm z*gy5}*vX&V?_+5;1N_&B(6HBL%Vk}q!i=tQW}r4jX8wQUMlaEdPvmN`g5Y%Qr!6~L zPmemy7g!eg?Ef2*%#OW}DG~R(+hFNr9AnMB8xP7pu{a}o&D5cNKuOPkTZv(&U?9n+ z|6!oDLky%D8f!+3!^P-Qr^Yx_!tGo5{~aYB6)#UXKzFyU>B`2TpiMO>Vf@bNt{_87 z6u)lO5$C)fSdgJEQ9%^0kf>%mVv}1Q%g5$Mc`xgM2U(Lwe+8Jv%#j~`U;vqd1GeL&A4@FagkJV7GduU)ALSY0e3oZs^=t%WHr{uN`j zVMM6qf#@}LXe`Wv5AeI6{P*3eiUZ)g&*WMfi3t%`8*jC4<;I7X#7|FsSBpQqE~IIA z+KSo{`Q+pNelAY0(*bl^Vyqsc13QmJAfdHjm8{ebJ`PyT?HhG9&voXE&YP|XE8K^+ zyAXkd2Ee*jR*s>|iU5hLdOyuSevDp^DW2_uLfk8cY7Ddz`kyX3u2LyhNW4)_&I0%H zhp)OP=@3WFb{MSveeC7@vDJU!ZW)}a)=g-Q? z{^&|eW66Q*HFZb`Sbl#0(b~>dUtO*twejaDuMP1-P=zVib@IOC6U;iX;^x8-frQ2a z>;mPl1i`0F3x#BP6%PH54|;e&)!IyyfR4fYBdE_cyn8qcdR`uAG|VJkLQx%&1To2m z0ax$JIA|bz&vl^!6F`I9CT}U?#6k7<>;DrQRD^Fkm?bLLHNN0tJO*-o%%rI#({~XD zBVd5P{;65c#noq8|o@36SbY$lHiwRE|3 zgyPg~ojW39VsQ*ld0(R*_P6f8i{unTFGRsZN(9?!ghF3mvO)-?jJB4TVI_lU(mDyrthk8cnRwC2^N|NmlIrDW`(yc zub&BwKXieo@j=&t>V7RfwYs6XjXuiD1Kk2l?AAYqcGpa}NF`lh>wL7CPdx4^KX1=N znJeYNi|nV2)SiY~G9P-DK578h(oo1pq!07m&O@_}Nf9AJ)We@k{ zpN^kzC+zU^Hc>#^m!&sli}8R{wcCa;8wg3O8=n6sNr|$0;M#|{VLMP~MGbygNe)@pr`rkkERAAy9VhE=K;))!tn``HR`D~|Vlu}mAUaU|Wg#fPAuS>7l5 z*~8Z@jbaqt2bq~E%jK%$rO#AW+&_UVLWQMo5|4G_pKjt;blO%0{t@0zAn@CHLnRr# zvSTAW;NlOD8~grVt4D2aJ?*Jd^FJESbMFDym_`*}Cam`Zel=8zlmj~c-UY5=TQBG? zt&3E?^Kl0c*UJMm=WxEKehQ}qTJ6ld_wDQye(vQ0T9vNvIj6Da{Jd*KC>Pn~ikYza z&m$BLRh?MVns03TF2|R*vW%11*h&*oxfdCE!D0IXLErg|Pw+-lRMF)l%6waU>b@qR zxniP6%>QNsas@4yvSU~afJF99`y6`a{c{@;lqX2GUi%BW(`BbEUk+j=YXQrOJqWtz7Aib+di>G zqaUCaW9DPxFeRR@R%fXCFk#$orn=OY17XH~zQ>w>UM?b3Y~vYa+j&t4T%xm>8#eW~ zKZ8t$156KwqoVX?`)Jq0HRpdW#-eUxN`&UglOBPW>bb2VLR~jr9Eu#Cc+F~ZB0RdI zW$-hT%W+qKosHjX?KP``y{T5lC>qO&fSxs&;~> z7|I}pd_x_wMukP$G8!mf z56uzXv%XN!@uLLrq5IxRLDGJpo+5Xx3I6^biLx4^6X@TH1l7PtNuS_JU8u!iTIng# z=1ov~>xE-1)9{GQLs#29qOF})wpn$aeRUVka%elQDo6AWxrv(o3PHoEwq~iLfdulfK433W(M1$lo_u-8kULZ|t9}lyh)zVpK^G={ z@Yi?i9fUvpmPgz_>UZ)6eO6ymk{&#EW*Q!JIRB>e=*NjA$19h{DM2Q+pB?HDDGV-~ zVV11&yqg#yG?$2r(w*(|PCu53qCbP{jIYpLDqo%<@>e{PG*H^J%WCh5IVu7L8p)fK zWmSj#m8cz@by`0_=_f2wn#zi z9bzar+Uc=jvDx|kSF24M2XVYR`i&(5J&ppq)`gUw7hYfjKPZ_7@<*_WM3qrh z`P@`*WnF{OV$Bs2zS;`0V3E$^l4ueBxOoV7ax`W)SnYNmRths|vcd+1d_cc{0bW3j z68-uD3%v5DXhlboO)NJJO-nr9%W8(p;nVY7(U=69aX{=r$Z@JnYK8TXcKmNSi& zbPz@KnrmGUz5>GRYeK*RnaOKp=)$*G3#rt%QZ`Q`M6a7&W^n>elTA=xu>fEf(0a=6 zIq$8~5IUQ2Gz+SvgBmF$O5>p@n+XDCZhS*Kxd3*9NBwm>HGRA-2opPKaAhX_A7eH4 za3e9;>&X_V|9$zl{o!4d=E>A$Z%PV6BoRxk!^BhG4`KyN_c!MvnKL5$7%;TB!F)aErwaE4dOZuOK>etZU z%{a8G6Ri~h2BLk9xL!Gz#)&hwz)v5p<9E0Y_>v2dTf665(WGZ#eHQeAqfU5Ee7)_9zP7ofDaANfiige2f-^=4OYK#AdP z4&{hY$G{%STV+?I4WgoObO);6GVjb^q9i8Yf9>HwakQJx_V02vUJRIyhdn)0>deZr z3jYBCig8y0!ci+sgPBT{!_4>1Zm?9ZjxQfwG=DmleTCkmw&*M7vqqsxY;4nzhShc! zhVd5&*Zfmi6!M;}s#$Xz#n#JvO&tc(og~1q2#%#U%OAcEtasIos(Tv>9ml?QP)VzWWDNrrDx z*T&gr69({w-SztxX}LvsooL>c1d&W>(tRw4VrahQg?126J+}lvHBsx~3iJNG@3ZH5 zdBAZjAaC@$3T|Vmc@|Jii#1oukM2Nn z!Sf|0PQ>?v-X@RQ+GyLEvx+ZR*Ce$NjR?EEzXwvr$sRUPOy+7Zk&03M0J<#4QwtEC zdN$^bdca$rVgqlQT!GP|ctq^`Q*Cmb*?q%z@C%cW-XEsvvSgY%#1;)*s6v)!1QMJh`K9s>D$eZt3scj{;@g4DoL{YOv;XLC z6CQ?t01BY!=<;gkEe+ns37Z};l5nt}{B%~h(e+?5aINvdJ(*)n)7)>nRj#RnmGreB zQ22UA5vaJ9_7z;h({rv}og!85d?a(9ICWR$`8)%a2tNdWnK+p^!Byiwg$+Ciq__@t zNX^PAPh)GW8$W_B|2FO>%g*>NFB!N2PoTDl|Kx&B=7zC#D==we2{HVhi+Q-cbtt*9 zJh49(4VuDVw*BPY(?s(?p|9y3ek*ISD)L8w*rch)UkODy;-_glypQI+_jA+cmIoM0 z`b0`x!uR|#!E85mWr3CQ7Kp9dxJJ&siP|Ae zS=j;rzX_Jj$(QZWLCYuyRTlzv(Ol6T{|mf4MQ};6G@^;fxUYc;;p?$3fxPdpaj80U z56-I=g9a?t=grr)Q8tIM)EIJ0%iec3c+%^VeeRpi-E}T|*P5)Si^}>e0OJpjQn>Z7w&ZKJ zLp`v~$}{o(0QLdTeW5HlG+{*a${g)Q3>i-Exehg2vt{fT$cQH3I^-JAse$;l$)^ZD zV#W+MX*|7iu-z5dUHrQlXB_L#-qgc=CfBNuuPtJynQC{b5ecvUk?Bh6DA3_uSkj1G~NRs#tfFM-!w}o9cc!PDdj%pO0 zExN?T4x(RDP@|0hT8bQ(Vnb;_aMGx^ojbm$3cuA5x3evsx78DOurm`=Mfl)P2YnuvEKYp z0fiEL7Un^xTKN7Ax@R^wwn5GoXD3obk}El_ z{{vp5>kfjaR$1dI>bE1z`yfWzT0qHBghJzgfZQ7n*F%)K+GXXqP(EHsy>b`7^)^ny z-@1Q4Bn|#ht!QORhY$#=K!NE-PcYZO#u}83om@(hI<2%8Ox|%(emq=JvWd_B*9)Le zvArm_U)S$+?QnE2`sd+TUyg_8UlrP(h%*~)OwFoPKxZBC1liu?s}-nfC0*@w^kv3m zTSqDV+8J9X^3PiiMsc{v^K*2x-)xuW-s6P$JvXXN8Ks;voioOh^kz5a6q;)ez=w9JLT97EN2U_Fpw|{z?Fhah>5iASv?c=pHu!5l8~E6XQUp1N&;QP1>Xp|;CI?n) z)*DQmq~(_}9H#q8yx2~q%_$Ay#Uo$Rdlg#LAj^`KeRLP$TU~dMCfIX%vswopENb(3 zWeH?sA1_}}&41wi7kq@dXujL|`qzQY8|8w*0~IgsYwBHNR(yM{2M?Ej{KkJ+AjL@V zUb%{UZ&3Vr1r^h{*3+hd=rdE6_WROBY*xhYpA+VJdg9`f=R?c&$8)w$=mF4c>i8k59)gb@@8U0 zk#<6Sx6+A0y+j-^8{6mX#Nq^ot$3S-p{xAIij<-=kbgTlQ(D#Fs=Z-!j>{aJma#;~ zj-a|?`ziU?gT|&DZ8^W6mh3jnb3`B=cR2W)VMhSyqVc-Z;})6mdSZMtV~y2U8>==EZ|zeYeV z*QT_=^9dm}Hv0Pl=|ilrW!oE4#=yLIh6?@iOr&@^t8UfB)In zrJdmWBRF0ok@wh5FFht~{iyYXJoZ;Lo#pAj0mi|;rO~NNw_~#N-F3Y;Zi?piwhqfZv{ZqQf`?w;I zqYD^Kii;^7lNNtnCU=S}xOJ;r(I>wR^Z7Zm?_KhM&;~Q%?bDVJXS}7*vY>p?6%(a< zGDGc0?F6X=$vqK0dXGj>-}#PQAM}i*xEE`*;Dl z%Pi@>Kjzczk4~dG8E3N~>pnMD%BFz3-{h;7vA56b>;0EX#u*vGN$`A$-Vp|G{GHy0zJU z^n0Pq@sYer)xa0*JBW_uF@x2E! zD0Aui<$e1zBE4_C2UX$LpLXZ-7steJykPD1O9ClN?hSd8fqt{(ot~_P!Vt9WPNK@7S-N-oXcA0+q$G_Irm;-eYia` zV&}6C&1#^lj2Eyy*)?gd+Fto=i7=hH zB>U;&s;{j34~JUSN?KmqWs3U2P8)ska_i(UKI(DKpi7Hp?yW#e<2^%bEv3T8;G5Jp z1N; zmPh@uC1rujiobqFsjH?FV@C;Lm&1mDGuh%fvZzYYa&=FVZAllaRkZWZeW7l0Jy&X6 z{AsRB^>Qx541ajXZ?N29yv;|<=kQyVc=^SUcRw;h)zujOVMBCLF-w-r5QN9~Jks+m~#*%dsQQKztHY~83eJrl8$lF3J z5x5aZk|2sCO*Y0;h3|?(rcZsJ- zmcKZIF>SpcmN^3ya*pDlVQo6m(#fB8`FEJ<`6N0w_n$k_3SVmc;R-wu>ia{Z+rNVZ z?Ztn*Ytyf?wi7g&@+XhJp{?CQ+%;JJ54V}4LPwmeU<^F_eqmhv_%T!@O0(svKN;LDrAB{W%ultCbawXY> zg6C}okq-MCukLh%-`yUb;X+k6LuUru~B+UQVs;t9oF-$uU-r|SE@p%-}31^7_$HLFkmJHtmj0$o;p=kP# z>Fri0-D+8P)WG<|E9&+)(nH|Xo=v-^vvb8}QXa-H-+bp58_Gf|nGZ!!0`T*e-EDEx z;z}Nvq8kl0nY696m`Y;xCjhxXompot9vZ@emcr}0YgsZw(s)kHH3l3zi!g9S!lqf} z;LdTJ@_4s{y8y}k-eRf!Piu$Wq946x7U|ABx~#v{2nGtVVxejMLsPtV+L=-+Mn{oS zFzeHP%-&*B{F)l6m9XW5Df(+4PL_t>J4)2^u32hH;*#*;_1-(AfQ{~*?d zsQf#x<=V=PNa;jh+LA6_kw9O;T~FmZZ$V+^&Bzmi{dby50>524KKLw|jv?}g*0juG$!=J$Kp&*`@?XCk6wf#*qs+r9)0HeCSd221VJQw zKqFp667E*=*`+2WtW{rQ_m;L@o^>hInbF|-vKVM7lJwc=mj@ov;n!>><>Ec=hOS|8lEgf`O*xojI|5ALU`~sw(GI-_K2C*7QeONl(dr*Hv!UI zi=j`e{f?c=Od8jkww{qZH|lLWYI#S{(kce5E}=yA!Fb9A_?(&R}65ue0UJ zv9ud&=AM`h%(OZE*_dT5#WP_JH(snvRxW}Fb#yekf8+hanbO`IZ=7PwNVno)8snH+ zIVs%aM{@Ah@5&b}9&Vu~8IQ;e8KAbZ1uM5pm|NHKBhD|&ZkZ%@Ng??w(r4R4~A-H29Y4trAjZ|#}(M0=M!CPnGq7qYxC`0i3%(JdPd3_ zn~N4XdM8@O}VB^}-JOItIcs0_~L;LC`dt80C-HyN>!$}5m& zs%-%n&H=!F_9c8hO;y>Sb5W82*?&%aEcE-XMjGw=0*t*UeX&A_Ra$#S9cj(Ip{dax z#WX|muNgyB?V%+(p8y|owy(*_%5T2s1!)hIjPYUXYNq-U^>4cF<`Cl7V#cz?-@i3bY+o&$*!2M-r%OH=OM80p}J<_#f zlB87bwl`QnTNt>AhGiGGpSHSA3N5Nv+x=$-%$C*@Io@swpQC@IK-=Bv zC43yZ6HX;flS3YmNe@KS=uqS#4iwqqeoCn@cQVYpd4bwWAwQ1=5OVjvXo{%YRZ`KV z)at3;uELmKKSJ`uwv4CW?96dX;Y7~AVoXc<$q1L2y}YDXJxq*4cnT~RQn;MzDEp32 zprQ%~`btqa3=G>?Hs}>o4Ty+HLyGQxx6J3B%k*Epw^_?0IUoM(@ID6T>yKZ!;3aSJ zQV{d9tEI2xL>mWMq^T~hj#t`ZH3EU6XIYj{Rku@O|IJpo;tX+S$o(0DQ;efgu@-JJ zM!NzW0o(M31zy;5@wnIsHk-HgW9qrpiirgWy+P-uCyq!*#)&!TnQl)_s=(ISs96gQ z;z)0#W?^)IfND1_u!4HGAFMkY>c49ByVdnO>%x+Cbs_h;-*h{e&gMoDpAIuJtVPG$ zN2XS9Cxw>n!?Kl_d1$}q9o#6*l|6VlOPDQa)V<_pB6;qnwb0!;f3PwfAT<*1tafiT zO{zM~<(oyjk`ykvC^GQP7h9skJl{^4MW^Z)`u~2E>2%5q>rCSw8?Fpae5^6AkMU*H zVW_iH1!=p0SBQAqe0)era(k5l20aPJbCO@hy@nAGA&W7!cfQa#yyMx>jd+DXk4WZ{aZ|0;(3WriqFuwAH%5ylBbsHm2xz zahF`xz3fBPppL=G#Vzm9=xIOR?`UDEix%2&Xp8cnoA7rS3NLkAd(ZNYj$*FsegY;0 zifb%lolFMS84Ge34j9T_b`uMXrEnkq&Waysq5QUx`YsJb4sPy)-ui!Icg|^N6)lGF zg^hyyk*Oc~_%gT@mz1hs=$tPDWKk1@;g?Jr-NPNmiL54Kfg94D8#)dh}lSqH4v*47kQvuWn05|a)f`92VypODGOJ}O% zIp*cb|L0MYzi~NIgSW@7SSe4s7p)z7rOM0jZ6QKMa8AQ5=jDC>kCAf5{*Zza^8HRS z9TO|iWXR(yWWAdaa-D?@XHrge+lcSKMi(T$&V9VSCr24>9}t zfpkGGTZyz#lSwB4>MZ}9U_Tw~rh%9X&w%~=ZXmz?aXk@0nJji-9*Kr7d|=@W7v75VTDAPWOHh2Roew zxT#|1ajm%MH`~X1nu1D#E$64%AYHxU9M1?(!qJk z+mM&N=`1dcJL-xKvDsqfZ(u}s`yT6xk<21kf;%UFVwf=#_Q4je-EpAb6o1^9#>6pT z?~9kzBTP(Z!$ZTo_)M{C681qxzy6k6u>3nW^PHsMiuqtcWzU(8uh&^5&HvZrRTBTp z11(J-^Yp3?Tn{j2Y`+Q3Cd!8cEsBJ7zg0+LQ~~Q)yr}-|EiXnKsdWUP8T-`EJ(5{oSU&5` zFI39f9ED(XGX15TN=$8vdlxF(|7*%P+bJKa7D9DGy`YEK#i@Gw;e;|HMC$qC31#sL z*3uv%kMZZ}v!v*sdC>N_`vH&AN(GFjzh(L3WDRCZ1_QwMXeU6&+Nr=KbFHDKR@1yf zl##4IloqySH$4%Jks~os7u+K4wb2(5BGj)GJWAQ9m9d;b4{UaW1-Gv=O!c`(Op+>M>5;Y!ggAnfTbc=TiAAE z-wm{cf1K>PJK(r5`fqsr7(b|XvXs#Hkjq@#$qrxNe85ir)^%H zKdQF}L$6pz9(EG`dmXx4qTLekH?h}lAH4t}8oZ2`{&L%OmYNEgB6U&!YRr}ej{5;H z%z{&YELd~;K>dQ<8ghekpAH*17#I3Wkw&{$x-Vxr#mcATOp{ba0x!n=3PM-p`5jG# z_QM&MT-kLW;6IJKhI{hST^QSD3$@&pi!Nk$ozFrBm+7p%slG;iYt7Z{aKk?41XmOH zJ&N5qSC+djo+6}#;3ctbsy}sKc!M!)S|^1My^*{Oiynfnzvp-#@dG{e9ED;ZEcAjp zio?xc5L_5E`P-SaB#!LHnDtmZv#*yodSw4E$T^&y$RSK_5|M$MKPA8efQ7i@l1uQJ z8KlzgZF2#;!!+kb_`_RKiAny>M~KEiX>M|lII=f!njMyZ8+6n?wx zIC=1@^=*xI-*bcfH7Jf9^Lfxh1r@2=cjlcREGtU`sxGvpM6tdG2@)&sCT|DoUcc9y z9&))!4tHW#ME|wK!kx1#ryw$z=nr>wf4=XC#aDH8!*7;W5O;~HICCF1epn*7HYp!i zd1tO_)`o7;6Gsy}gSC$nlB&^5p)`A^gNeP5h>1Mz5>*aKC8og>+%9T9T1NYapi(N+ zgfl-(*qrs_^{^Mnv9?Jk@3LDHi7$B=m1+q*AgY?Pfl&EOq)v@iep@1S*q+!*oJHM0 zsR{xadjUH?#&{&Ncq~(sm<@#fgV%z;>hT&lTT+9IloI?np2I;p%POjaeX{l%Y-_z= zdIuNfbm>ypb)SjPuNEzaSs=8oVOe%W8Au)wCNe@enQMml9rGKpKfx%C&Wg*-$BB-& z;LXT@`raW&l2c(i(kktyyHESLR`|7aWF|LR2=S59t z@Y2-ezGCZ=CpwKI!NEgz*pXDYPi9~;h}F>`dSm3cvs((jL;eA(S`47-)?ei|Vh#KA zYH_@#0s^K2#wL3}Q0_NgeM$^~d58wNM6Gx=vsv^v#qFKflKvt!4EHl>e@Ly%663fZP^x1)6aUt4`Hh6{UoC*6BwRSwx}Zu8 zFQeyW@@Lgu1L9K;RJkUI(1}UIGw5C*PF>onVF||b{7#B>{4XJw)|Wswt6p|bWp`Y) z?`bkM=~!P&cfl0U(CLdbvP^*&5d?0?EMHSeN?w?M*s zrJ|3ZQX`t=)S)sQcIgsnLPg8tEZeVO=c2_ew@N#I$&*xY)@;VU6Jbp2koJl#wj^R| za!<8GT8j4cptbavT;mmz)D_MDY5m4lI*W~yW&s-+aSwp*j7H#bH+jf)F$ zy~S+tHg@*Z9uA^nPo3PAwup8G8aBi>{0a zcN0dD)YIP=bDjA0YEgveDvr*Py4p5tAzepzW&sG8Po2mR$=>^R*dwrCG)(>RvqCC> zk9I9hS3d)cWg$Ry&43u?5wbZsp0;~fUF^wWM@cePP?CFyn5#06Rgp0`kD1*LSZFdh zyU}&vkI4|+SpBn+uppVp7d7P-Y;3XEK}*`|Xe4wc+3xDNbRW{J$$f74#IvG0H+vw# zj`{5Ffmz@46oJ6eyP8B0G0ujt)R?uKzqY1Y{@}#wM-*EmZbP@m@xH*Z-^dAd4@g*P z)g0gct2u#?DpZdG&*roZ7J3OW71-yQ%_r=Y>I?k`qp3iqW@fp@@QY%s8x!WT~ z*ozLjK@wq(g|GHs!g&3f&xGvA-p2Ms5js~#MF|V*_3T?RqVjVXKwBA6S*8CCWDA&}z-g6!3uC7Nej}Fiy^}n}-5ZmNGjL5{e z*LOc7eX7S2@!pZ9G#xy0}QEssO`%{J}Lf95JNDZNX2k#V5{|%CQed1KC>;r%ks_!OQAxB?- z%YBxtA6ETm`wwQ+El=R=3P2*Yw2oJrjFqA3`Kb&OU?!?cimmbg75TV`Dk0lNn%BA7{~W zkFd$D4n~d-p-#AGCf=j2bn8^wJ<|1smfbbD^B1;Rche4gZ$ftnFNG>x_?xvv#j|4t zkYltIp99Nb zAC(ksxNw*?Q-w0#f^s3TE0dN5rO$*WHEOZKNm!Gfey8%qqqiLagY2&#e7M@^WFi~X zBTg>|&ZkTGVc`s&1Rf?4_`KAc?hJsJt1_``e&6gF!)Km`_}ts6j@d+O3UG(Q% z0U!_-xL|hz1PROxKz2;9z&w)b0h?+!(I#s}VAiJP_hF<)x$q3C%K26e z=ay{N1Vehr03k9B(a-M*5Id%0aZ(^}U=%v4lY>rIAXKkE6ux~|GO>h9f}q4BMO#4k zRt+k@{6)ZHWXKEr_x=21m&m1M|GZ+7zq_8YEEB7R#Ra2M_?P{%Lh$*ku4VTUv`zjg zCd##Q4yZoO;-MGLw*wl3#w)z7XK>eW=t!-}GNxmQ8Fn=f;zOG~atA<4oQgiM`RQv? z9?|y|Ke--Qi)WROgFS%9_e(@^=B3$hXVB9rUNcup>m0X>ll7gkfzMOgf88DdEekNP za>R$-5yc--CAVcZ2Ghej%?$20c)Pk#xM=Y7Z}mR~IZ8IOMSy8-ItuaZoMyb_NXz#){z=Qg{$OI|j-EyhK$4YNqOH zY^ahX=GlNtTTP*4TDR!FP@dDdZoKJc7uXlhJMnvLm=s6&TAi=kXVo5qH^+|l8Y@IH zR)3rrTjYoF$g>mvBOo8+-jiS^wk49k{UwuX+L+VHIsb5JXwf+cbh@tTo3Qp71h^+c zW&sRV!Hyn3B8nnPJ7vr>?j&N(TgmVHbT9o&XBdcn}c&N2*I?*P^r+6g$fi+p7!=S&>~ z!{Q3#s}5I9zE!nP6KNobLtBEsNr*47ZO+u6GdVi~*t!>V$2gszji{qf@S{FZART>cQs8`7tN%Rnyv#P@2kaQR)jZW z3J^~y4-&j%!t?80F*x&_7Z!dwBYgl>Ia78>v%zqYgXZ|2p`0tJA;uT9?$5B+8o<51 z@xVN~b!YCRAvc#}jLyU7<#ZN?5Rd1{ZXZ*Vz-2zY&eRh5+MF;$(-OFp(sp(9rp~uU zU~wMC_du$ec}m2y;F7}m6zbqEff5*)t$b_T%lL#5;}iq?$QwQrOlr9KA$hb8GseVZ zf1Uv@)4suzqrIdmm#4|O^&gk?tny6}115)=MIGF8E*To;d7297PLFY+0!27SSLq>g z-je?Fy^Rx=nJ(uKYw97KrureEBxQq7C-zYCoYQ*QoDY5 zPMkPAf3?Q$IbU#7%gd#Ndpj!nggB~$mHulkA)LzT~Yh^NfE<5i5tS2+9Ug zdZpqZjy7g9fXH1&NIjMdh>GfI#49* zJ4Osil0o^}Y}db;$IZ!x>r9xq3lu)wF_cQ=`X6SPoSK7_(Rz)U1G72~kPigK~4%u`toWh_|m^SM$%%e@1Pj-!WHIbgPCQSlALQ zR-SK$-QUh?O74sWg2J=PItTX_EJ8KCFOgn^MW`!nT)^((kCD~vH~(5MYU)&IiJ!4u zFb0J)U1_bH@N!H^kNNPGTm^u|axn3ULof93&=+5XQ&C5K8|xeiMvIGN@qy@t^MOxz z61PRv{L*CD%DNiP(Z2t51rMu)TBUt@jPwn3ujqBiuB9)IA5N{V^yR?dSn-vIdY%j& zKKGeGRnw1djgUJn7%m&wf2!uOSNRs~#}SE+(b7ktj+d0q12TZwI?lB^&1Yu?bj*I9 zmN{gKUhc2{9xFB}oAV9l6p>5nE^P%KSB!xEkj4XQ31Ig+(i{buW;-l;jY>#$%7c~( z0cCZeFxQF!N~GcIH=7Jh*^nT2Fn5RT?gNDib(IJcK(N2wxj;+Uc2nd@+RI651*T~7 zlZV-TwXx@3yGZmu6#2Q^*Yq`Lh!s5VC>BhZ6DTGf{eub8BPuxI5gahX%rzJ9$en66AvA!|hjY$s1ATivQf~5pm3tGTy+yV><+<*A?76MIh_E7C&#We@VGT?GbMq=vQY*tQkh?o5 zG*z&S310-+P?5)XKdHuJz-ZW-I=KGrUZPVj#5N;L4+kaD}Todo(GBv#CZ; z*X=;*oWF;Gl+Y1kzKu@sf#0_}%rIvCpk?ClK<2m@en!q>O@D(+Ld)bxYY zkRYGvtJjAv#dwceRlCjXe(bx8;~h9`RTppJVGITPCJA$(UQ!hRMa!VLmrR^*3A$PS zq14S^{lJ=BA5pm61vKrZL>C{)(0nWiD2$&64WgiBcMQVN@J%KpRu&cSQeX<`2RRFx zALls>ryQvcw0P2oh8f1K5u3raXjH%CBOxe1RTNm|cn^HSU5owKCVH+g)A39~cw zLq&;AItfmt-WA2!#~lrjBe&{g@R>NKCOhGubf~H_$E-Ak3xYeaF$(aGkHFo=d(JiZ znOaX-6j&Ba#`3zn3KJVSNFt`$jMx_Di2lli5(OqmrQHc!WDAF{ehku-V@IS!wvVLL{9jJ3AKQY8e zB;AsjLC{HpofYC>xOkLQww+ef2eEJv-dJM`ldgu>d^3qLozQC{7=L`0 z=$S$;d|R$~cfR0nF}3)ITU7u&V$z+FDv65sv)T&p?a=?(aWa87Z<#~szkd7Cjrgwn z6B~(N-@jq}9_kzm9SPsy+rbD&QJ9Q!Eh`9Wct~f_? z3YQh`kI%5%4FDk8lm90M72bO(q55jl8Dx-4U*I2V1L+4dDFWo+4!WiTrL8zm+X&Jy zQXmNc9hdc2VAh|U0P~^CFaAyrW5h#AX-teMjDez#S@@XA zOp4qnB)lx0+(ZB^H^NAP*(Vx=I9eyOIYpBENmoRa4`w1!iMBCUe_{r@&tQa z1CuDdMbt)V@wSFLs6Z7Js2ZpVa6Q@AV^E~T&@#g<8 zTZkD!=GAbqIE!8FrOS3j+pzjAA)fV%`x1yETvsO2gQoiG*#Xq#Cy9DOLa2mQDh4zy z#-=|y4d7+J!E5a=_?kZ^1VfW;#m|jy{%!Upj3eUf7xjk91D*GsZ>{mTKZpi`hy(hc zU2#A&l^A&}j-*BG;<`$JRDMGBSO`Es8p}dRo=I?Gu4lTswGLq$sI%qF$mYAZ-TD75&fljn7SyEI>GEMr`zQ+6fS%Q2%elH zIYiN|3jX1?(zF>Xk2a=niwc-3W9d(6H?)HD2QaDY?xb^%<-?8Uy)p@+5)Z*=7n&)K zEk+Mopf~EUTnOZ~mM_76{(#XDb3#RS^KZDD#bj8h^oNI8HkULuMZ8tiOxye0gFlmo zQ*vD!_iL^Qp)Aj{D};`ivRrU`45|pue$Lm=>4M<=9ba`X*y_??AYJP(ch7jgCi?3{ zvpK(xJp;!I--;!yIIfcI{P}hK-mUC098J1A#|=(snEkDM1d2OqP(|_EA~Do)x z&_ommssExoJa6Zhc`$B7Zh=|RHag?7m<0U1a*0GEJ zPrQ}TfYw#^0j^tBa*mC4e@gpF>^+b~<#l0=RSbDpi3j_hVsE%2YpK4)Vam0v->|8^_4r2@A%5s|Tkg zPw!fs9*Gx#l*6WJF0ps_a&y-bs1|h;`@b=9LI7UvJ}gs;!+-*~8b}{Yojiw|7dVB; zLq6qP%U$;eusATQ4nqlPvMi$P655=w+cxPAjOG40G zdko(;Y)ur>Vf-=WaseH|lt==oN-jre0RO%@v#)JZp^|_GeG1kcTnHY(7_;jmkZb4> z7d1cr5eK=VY51Q?E6!%7)#y<5-RV4Rb?JY{!-lOuobqSVm%QF`q69@>Vj@J;So3rE zo42QWpn-9aJ@st0+mK#7_FGf$5q#C55o2@xZ$}_r`RBR&kM?#BQ!#}(>sP_=x;L?B zrWV-ftgoJq4Gk2darL?WNwtdw7^&7fXDeL+ZtDBB}n7e?%M}|K&bZw zGiwy!d;|oxbxd07bQc0XknwR2sjt3!-3eKw$);|_f3J!a!CJf33vwO?FS<#}86kn% zjw^0*su-VeNDN^0OXqa7tCe)EZ#$rn@Rg&?oo?N4S;%jyt;GQm+Lae0ooxTGV|n}D z667P+v?=vnMABgbO%64n0k5D$AS6NgQTDZsf;f!jz5i{K-Q>AMfQ65yT`lJXp+UyC zY!jo^QoQ{jHzbLZV9H3AWut9eVT5VK9vo=P3i11?olFk-5g_^KAU`nc)G}CpxEM45 z^rkFsI%0CCA?}BZc6+&mQ6Aad;&KJ!Agc+0D3DmQLJa7vZK}}`NRKF!_j1=st{VZl z8+-_&0Axmqz)PU-XSF82daV^^5cKnB7@C^Nj(cV3f-6cr+gq`49CQ0~!Wsy2{Vmq` zZ$iNkvKq6Bfh_v!Bza9;w+kqD0)4_6&W`!dDPr3XVRaKVaVpyLh;@w1bU%caZuhr* zqqF9c14jFh`?V;OR#Di^VYLFiJloPW7u{f>I|H3rY$l_2fk$OgX)u?t2{oE7bz@{5 z8`5l-BXD8))p#8B1vR(_#EN*a%vvcR>1~10%-XDg?XQ}BcQ!H@W3!zzRTLg}s1ol* z$L*TE->fN4M;;L-0oU`2OQ}Yi!t%@TII0ODJ-a_@T`h_?y3i>%h|qw3#NH&Tqyg@r zP+jkkEgpQyYtt+mit3`)1uw8eyF15Oti7RLv&am2GDMT3N*egs%DJgoAPdag0$`QD z0cX?yE)?x+wk-!1q&o>@L!rtuM=&N(DdrWI==~=D`0>zTZ;d*iF&K~C5?c%CEF7DbSV_oVs-{bNk-es@F;r;?zcf8^o zF7LFbr~Ckt@%7o;T_YJ21Lp(Qv>8@+T-4#<0Bi0*DfmAn=^<7bHo2-ys#zhPO5n@} z6t%XA!)O|_UAXKEj<-N>T!y;bHPuWVS-%ek_~kQ{M;Y#xjmPmDEQBJEM`!_hj>N51 zUkbJu3h3m+7zk071@(OYKm5GVTkGxv9U1mr@xte|<^cbd4Nl~!#9jmvbU%Ppr0pUq z5?0JiAY6I9>G2l8H)a&eV;H^)`GMj!ow!3Se|-F`vb+t5%cSt`3g(&2UZC(29f^|2 zc!J7p(>0hEngn~f{%S(^H8$gawE){_GDRjw1lbDn(Qe=&cf7Z+Y+I(SF-5ql0f9+z zHpWlx>km*_2Lp4k2rq+O3++rUSmad?^MK`N`V8Srj%SrGbKlpZOTR259V{}TaleD! zwZBB2bwy`t<1-RdJJWqh9TaL;_m?2sW-6b#AJD)$@nP1YiBc!B@a?c)1K!?aDN^)G zOu?BUFuLjx6m(Zp7Z)($ot)16=WJRX1{ErH@%Pv@qd*8<`^h1B7kYUpklm`I$jf!R zbFz)ZQFzT79aMEPZ5B0|9&%kM$Lyj0epF3wt;mE{Gw2_38MBLnqtYdwkJn=oy@lkD z6%xRq?_cnIyafx>SrNd1`&(cZM5j>o-4icD9E2|(3fNz12EKVX}oX%5r(D)5)=B2jR@^Z93P* zvGTX_vAHIUe5+3VC{s|(wz*Va?binxiY;7!3#07fR;mklP^|vUJ z+v*<0^lcR~Hy7SM3LpdD0QRK93zEC)lhVfH%T>AP4HvNfur_&BCs>|wf2)eHRBHb~ zPJ5_DyJ%6OMRs$dIS_-9%YC6n4-hO)u2&(TYL(ohXH7`VA9bS;0sDK(epb1W%`|*# zY;{&3FMnQnze@6MA5egV;#jG=?CU=Wxy#N}W=MiqHk#RV%;QRjK?B?hGe^ImBiz3z zf6p<+9Um$SnCR6Mqe+0)W8~HIYF zt1sO`)bP~C8Ea39zI7!+4ivo?uIw8kn*DkM-%CiV6Q5cQ4A71iwl1XZa8>v({lO}V zaJG=}AgcN(fMY#czr?IIhfNs(oi3O3h5&l~c)TnIF~4KQp1vX0Hir}FR`}ipueS_J z9`DVrV_ND?Tiy!3Uj^~7gc#2nxMc=uUH6#_AegQyAG2}G2Iq_sk=%<<%p|M~ zDO{L}8)&t5bU5d0u_Ygf8VPX~kY?cfrM;OMdxEHS)^NqE{WZill8gDc)t=no_MF(L z?G#PB`$WMcAB7`lO(!Ul<$IvnJ(YFp(HcN+H%FTuCaRBiBrx_p?EC_1qNWaY*W^xP zZ6+;m0&r8Z2(klGm`VbRB#@{Bhj>OEdeCFwbL|UjjYGr9)%}sTAdtT!X8w6Qex|b- zB5x#qB0xT`B_$jKtV%kKx-iaH=*@Aj{pHNW;Nay6(}Q%Qni|gS=p*Ei38UyYU@EJ& znu=j3`&^Ub?w?BgYEX0B0l<`8UQ&h!8QunN*3G@COKUniQLMeXP76_q-nSac)ZY0H zYD*Y*4^!2nvcZAsH!^|iJ64!_T^oi1iW3n!hUeUS6iwj#8`Uc?f)}Oy9~GHu@@(tS zG4kcGFX~OFmi9*(qKcsT)lI>>J90|kcuHmJ>oGo)G*22VglckhpV3N_5(Ot{#4(g& zyhaJ+K_>-N!NIIw5?zlM?qF&PK>2APBj`b04zTPmF0aPcA>A!Mh)+9+zyUID7}zxD zS7I@uPQL!o3K5Tegr^V?R=nR$YH1^s=3Yu+#QX8B*DqYPU;^_aE^Zmfy~#^kdEkJg zLk0M^a;J)K@oRP#AzP){AR_#z9{Z&_^bp|0wbLc=ORo!1|17DvJm^UyeaCDWwU$zkfgn=eL`pkpGPY@&ac(fSPR zyryFXyw+VRz-cdaN+M9%A#6>1zqqypV4bSGUqER@MKgUPo%X&X8mVm;ycYcJ$O<_K zE;1Ndp#U{H87uModGLMc+dS)i%To*Yz0hAP@rF4zZVMmxj(I^>;+HcxBj8NfVtV}b zEg1aGmklecp2S{oUy5jgMH$f{4ukrlg5(vJ=X}>{jckp&+`ZYJO~i^ItFE+?U%7H6 zh2ZJclZ3*Abc&E$jJWT;yqLWR-V-D;X}*l2xcZpv;{{y8C3MaF7|pYKHqXhFWE5S-^)C zZw^e671Y;pO!sR_q}JRr#6 zh%1QSK>nm?tFY6Qf_SOPaE);^ZhVLr#t#$f7lc4QJkUyii=3KV${^+jq%q6G{hlJ(q4aPNz^_ zK9zfJ@4S-Jb`jDYM$ty2c99C=RrgfcsnMsthFsBoShj)#TN&8>Rmo4)%GWbqrlLe! zyAi`vYrT>MX(ROZmDI(K1T`bD%-vYce~uyf?O;x(9%h67-@pHzf&ZO>|EDtmVuP7k zgv{=8Zc6z+oT9BcS+Mef@)Su1sg*pJ+r7q(&lC-mitJ*=zkD211S2I-=yqIxttIz>fT?S1LPNu%MtBktN^T8D_!adpC`>@>b)t zB#WI*>ZUxdoG2l$4%v_ zbsA*y+^-BGvZpHut1zh~89UA5lytPB60`q4{kgLW9{8fDJ<);8r$i5&Xs(lm?4E~A~tiwh$XxP1*!8u zOs!j}Qcb?r+b^ML;8P@CDC#Gr)J1FgPsy^}s3`iXYfS&5ne1l=+yvQtG)vKzmI4Bm zp!%M*-Pj&VUD_y_D(-s_oo4=uEq4_dJhSo~GMJ%XZ(qfK>PSG_s3u--U&?=Oflo}U znh6BY45DCJ<!Gm436 z+L#d#af=Vn+z75_&W|8J>}2uHMKOK?*DJV@`(Vz^uiGqgO#ohF0JAfTQW)iMhQh?Bdd({=-{!6dix3vo`#4h@M%1I(nCFe zwwf{fGS`~}e&cop0jDS-j}}pZ#_0?b)ZU7t=3P6pv23>!kw-4o*K-FrC>1l?Xp*U_ zA5L+Ji6|RT-H%lA8?q=1%G`?6jEE4P;rtb=tyoYpue&X#Ko=L6x%JY*tM0`vd4nr= z*Jnq*Q}-csZGvmc$?@;Ce8Pg=m$B6+D;s|U&FU!?+!u=w{3m&Q8LTI_w>@^V3+XL| z)m2OYqG+l@9#R(aY9q9(`|*uFr%-uR;9KOyYg}I+-rdyY?%>7G;5gwyevi6Ld6N(A% zosKX$3UM}WS!9q_RPN+GRiLvRd2Hmh9Tm*4iM-sGMf7Vn@2D%w+ei+)^Fw=-DPjfbA$|Y58jV`*c)6} zBVMz%;mtayx}64C}7RDBB>zCL$zzUo;HZ z2^uG^dm1b2s9QvHxg{4F6=qf~@jmnnDee}8kwsi$afv|M0laiQ&mEDA-8Zxles zojDzwtx5iq;DyG}hgdd*y2nG;Ax1Je|HkE}juqvEpI5j#R;xuuC}W8~OD`a$6yF}E-I<6Iz}5cZU|FL+)Qro)E8D0#S|#su3LE=c z47_Jp5ABx3#8aX>BsSs@Uh`wZWX|Pf7^mey+3HbYL1|+`kevUN!w0RGi5ziOuRE-h zGheS%%I>;n`4{B`lfP5@;lZGBFmAWg+C{QUMF8=;KQI?7RO7q%R*0o>nqHFu#OS%s zk;2VenVO?t^!DS!5%ZdKk;nZguvE{uVtP$uRoVQ~-=$XHpeuxhx865MC;wJfwoXYfFCZ;UN|F&Ly+H zChfH-U*q&6;BHahTq;{yO4~N3#Dq>JJ1)Rw$$qnraVgKuC|*C|YTQ_&Xb|8b!3oCf z8;5DCIDg3$h_%)Ku8Boyd0NHJ^e5Kq!f`!C|w>PA0;I4y?`4S&;6pR+zR$||q4 z8fx~N`>J;kN?o#Y*Rd*?y*l!OS4X9`GXIYi_m!!QR;%_|bw-H(=$=}(dHm9GCMko~ z;)hT@{_45s2c7&MKg|VE%owDxBDl;{!Di&@-)Q!bVN9dC_M@0tF0)!YByV4br$+i6 z)4L1Q))4-dn<`)!W_KXwD$M4h9L4;04wcio+na@IpQ|~nFpPvEAnjqpTJvRS`&Q!i z53&S#tJdB@k2+DrQL#nDGcGr?CW}KF48;n<6(NR1uX$eLTFt$aLCP63QX~#(DO}?T z_Y~VU%G;e*rQ8ySHPoyB{1lw}dg9KSzShjx0qv=^u*NYGl*Ob z$Ab8Z%PZdvph%Bfvy3VOyiWe5d zzY3kv)$LQQ1(3+0D|@UX0tLdP%^MVT$CX*G;ApzWmd``{o4 zUqeR2!-qJGeT6-&!Y-nytc@fenn&L=w;~nScbq$}jLtvqp0fTZ4YnozT^w`Y)%Ns- zTkgb=fFVH+b`uCc>18Rrn-JP!7Tnqz>fM*yFzMF`Kh&A8gCj zq>A6RA1{N|vb1c!E|fBQ!z%~nBXH3RK(v$`wF!tdzlhF7uZD1HNZcfG9o_C5ojKew3=_04=LU^OJB9#1xVgf#^HAZ;U^tvMgOVQ$e~=wm$8THxwNna^g!B; zd7Tu&qd5x7s@&Ar#`{ttU-v$?1uxV^&O-(pJsqkT+V4P)@?8uIS212jimeSVz3 zd2<)N=9;Af92v~ao_{lRb;p8HBxOV1ftyHD;tG09?uLFJ67nD>WUv7qv2+iH3ZrO{ zp0_2r0{M!NTiGZVn9PmZE3ZP~|5U3p7A8}gkc;alzDTvJN+8SRiAsbv-`k6l8GF3zTeYL!Qdfh?wp!@KGMbY>Q+ap(hyfl&+)xjMWwNb-oO8 z;b@!=&L{gDB#|?0e-Pn@(J z-B^6d+D{EkE30a-o!$pyWgW9BywDre;9*nww{C3Ne(p;SDzfpLo|B$5DvKvN%um|= zAwB(9#}L<5<43$?)zs+y4zPvN;?@e%ro}B~9e_}a-w~6Sbnyz)#C{KY%sVDte_L z<6ZpEo6B>mJcuzr1?91fNCZ!#wTo6XN$~pGBQxJp08842e|7O@tq0v$(o^=EVHURT zS}7lUAq8pa1|JH3h#(9Q43vcMo3~Lxv=Sl}Qx6^p%$2!sU70^jreO)^Y%z13yUcMY zG?iQ6k;V~+9PZnOSZ^=|fX-akyHaq)nJv=no&DPP_A)L9Lt>{LPtp#&5N&uv#8VxJ zjOm=Di+!~16b*NaOM}x=G^ffnBO?D0K&YdFaBJtGrwKi{*C3<_ZhS9FsKc($l2&qk z4QDU@o5P!QRW5TjWSf5wW2in5TdP{E*2X-F+9wYm0UDy|Fg2e*sC{a zOP!7#_cla-s~Fm#!9D3YEq|;+HxY-@m@_=}Pc5!CobhEtlL1pB zk(^4X_|u7k7VtghXvaCG5={f%2OU4uV}-vh6=>sC_14S?^BcMi>iy`NRNXeFe=3 zp?!$+*HGkVwkAvCMjT7$+P3h@gPyXI-K)Hm6P@zAx_sj+NFXY4ot)l1?FbgKWfzC= zR|fxo?7in-lUdgWdIKsd2s$*8rZOWU5Co)GD=JNCN(sGpDWR9q0hFpD1_(u^cj>)o z1f+yW4K*|&K6KF)9AzSmxR?NzRIt&Nt~(7;p0vruAr;i0dI zkq~d~YE1$fDIP>hHi@~8F@!Q;L6)~5dqwU+VJi^&#&nTwSvR;#aum6SL8Pue5O z;Ehf}{@C|qsD6Aiaw00*XlK>t8>Z)FC%2f-H*yUFyWY^!Bd?C%+c$(l$_6S8@gl3f zIRWeJb4>~oGgVE4b+Lsr)-eL(?@~sCbBXR~Jk^v7psz6A9i1i?bp8e(zGGXY|L5h7zhCkgokfaicaU%EcAX;71dOmc1wzQ#{3=La$ z-aL_Pv7VW?6L()p8^sVd*Ofi!M37Q^ExO?^>oIf|8g}rHo*FlB_9Apt(j6jrOj3M# zxyyDGAzal2s1W`~p0mt5xf2G=&-j|&o~zG(d#s?s!Ol7TdYkU>cPHOl`1sh2?#&b) z1d-2ohGTYp<tu|Ck%sdWQvb_*AMk!3s_IWMOua_^)wUvDQWeLtN16?{^ zYuwEI3|FHcAcrn~mwADblU|nD^fTJy(tCj}z~9%X0oX)WBm492E6qa6q`oGddbxR3eku1|nYJTKR=r;gP{=z((V-u{ z;R}WE=1`O8@Gs)(lJUXT@Ur6F9z)2dYeX;m(xa#0;nD05N#@sE-1NzZ`sRVfO+t_r z`?A+|rH~ayloOqc39g%MVu=j5!E>YyqUh2r){n#y(~g0`XRKt;07C%)_Ik%>or2%` zp{lWd>c`iLt{)2!9xe}`PkI?)u@T|@3G=5&nA{JFI93)WN(oE zT3dl5d0?90?v(RDE|fV3#g@C{u_#%o2Z|s70T-jcc)k86ajdfR28WBPYB9xFe>KfU zZ}ALAw1XTQsCwS_xL_Y|ea0as6P`gbEt>`$3TX$#tK5YY6U~|BjB@|c#uS;&|8fB& zw+}|cAcjc<>`w#IF ze-_e*s1_blScQtSb>p|Wx1Lk;)fu+wvTfI+lz&cyl;dr1k){}@;?*RJ^84NUeg6I) zn>xZu2?qAXi?vH`=D_>gfXi2Y_iy4_csehLMkvTFa*GvxR~-GJ4YPB;R0e(2nA{WA z{aC)k(fRI2US2ejQ2)Oy9WJ9bR}E&ROoWVb~U{Y74U`!Rfm9Elc)z(ua8c>?&6)qAZ+r` zBC3k~#|Ohs1c|&mxv9iyVsyIYG$r=D$PW{DqC) zQ{LW%V}G`|Js?Zy^5i{@z18rC^qqH02fk|`4`X;k%7E`E9gQ%w?wo3i7H*b}lh1#0np>3M$ zl+F0^N<`)`u6Z!yM!iKupzo1B$I%6-*P$+YDvon`3t({4PE)SR^2C7L$O+D^+hw#J z)#4jk6q|kBJbk7GKhx(XRLd9y`*i)WLDpeBsU%eB+BA?Oa9rjle{Hnz(QlOvd^`f$ zVG?rxFs`Rc;3L8-H?}Q(eDD*ZX}zrzwmvqOprS&!9Ke7D@hNGFi+}5eZV8g7x*q9k zvq8OvbXIW|7ylQu<+1uP`07bZzqcBisGPgn*I@dIj``8If0yS&6H2kH(j|atOfae zX1nc?-76Jpi&~qKh#N!y5h3BJtyqA|Q>-ofG{qrH+xJoDtdD`dY~I&w zjQdT-GA2m+s8u4%$M(<)Ipt-ewVw7N@H_7et%{+26&3GeMaPzj=PCNb1_qx#EU9+n zTS4hO0=cAM*vsW*8_FthUwOm>3t6j^A!6JUUNxVIkQ=4SqI#TF=XP=mz{qi`+Q*(E zN-lEh3@3BeYRb2%bb>Yy9v_~{s1cjfwf{?gM3^3Z6A8Il-VTXs^7!Uqw7WAr&`g7^ zNS0MD^A|k}*=H`duH`5tJKAHv(Pv(4OnLgTZ*)<1Vx7XbKaNiujO4GwONWU_KnkCU z?sxfUn{%&~V<>-&0mlhKyu_kY;MQc6$F8tWjW80!rr{w61z261eKVyj-r*jUr|&8i zx$(7n?gA8Zq{$YO@P!4FXs=qm>*Qe8bobDM{YkZ%zy9Dv#o3$A!WY@l9O5HO!?{YE zBhGvwtewis?ki*ZxAJ!~E}AH}oHTD@J`0+e!&_=s(H}UWpoW^ao^Ztcec(OV&%gkCNz`%qy^km zgAx2u)j18V96>yxDS7|hL+EF}vv-4xIEh={g}q{;;Ciy`%SDNFrAO2@hu#iO5XMM9 zl2mglEg{Bax29-2zNeHvM6hMx3$c`{9)`laczM>HWZn31gx9Tq1uCez7?Vbk(^klx zMXPE>r|nwG$jRu)vff#Y+^@b!`NPXLVMQ-B= zM>ALcgN|*Dr>#h>t?iQ`>U}{mh6lSC-!=i~sx*}KV1I#dvNzxIRN0_UP)J_DIxEMU zN0p*kHxv-;mSqM^IskE^o8hm8DQ^M*jRlsrxHhqiBTeVRxiyZBtcO-4y$eba~abzo3yY@2!@) zy+@eE{Jw(&x9`8bO5Gp&OE&k4II-IvrA`33EOs70817aFu`PNpC%0Ts|4fpnqxLvX zdOVUK%R#Hg3&jY;!cso~KxKR@NCiiAh;0KFc>_~&7Y#*^!<4Rvzowk*L2Ojl#YWw5 zBO8;M2}R__ELk_N(+Ha$uxTnlGYRz6b!7=<uWuCK|4Q<t2Y?nIO)#w zP-~}4*8-&@8}R z#u4JUtenVrU%fq<;tu(V{HGI{ezQ62$>0H}_mn~=K26XoD~{6+`vW5T%BLq*8|iRW z7ST1o;9MLqssRzh`tC#OygOamWzB33kef+TP1e7Qt_;DLsTl8!S zJ7?UTz2$pfZ?7<)>X7pXPyj+p-lcd3l|8f(noaQYnqHff>JWoPLT{51eNkPb^9g;# z-6$Pl*NL_oWh05yKb6>n0fs8M|5ALMJZzI$Z1eej5%Fcv^HRudX6h4cb@3H*pM~f_PO$BziJ{(Nw#G_5BzZ zv?vZGyO?}kC$~stX+J9QSzxvH2}Y&3oCvY1K;57inCy$JDq}CX&S`cV-&;=(y)=&N zdir*8jWBa2og@nDa}oepjHe0u!hG>x0Aytt^+tW(h2s?In4xX%fF}`CO<8vNSeH6= zf01m+?bnGAoqo7P+Qx6fGGCR?&ip%@hr0NRdLIvg+32Z4gW1B4U-x|%V!P>@okw93 z<0PH+{@lYLwwRyKPb|J%?y7vY&knucW!hMdBJaPl$%~9($6{nBQWXqu*>6`mx@cZZ z8y)Z+S^lx^c8Wn-xNw#cI_JSz+IS*lrnCU$BtA-m^@x#8eD%0+hu1ste|204Pz zrNlGNgXn5?kjB4239cj!1m&;Q!*0m)lpzLK(F>f`hjZU!oHV?AUu9Lb>2^kGRR!KE z3`f@%(a*X1(lxI+EITLcu|hGOI>~_(}Sbm<&F*XW|2`-uzTN?@Dr{Zpc(w6u;`NrFo2 zM8F;^)+sHyWgbikuv8`H`Pk0wuZwW3@Ehrk*_Z!JinPr`qc>pnIXREcm z32A>ZK@&wO=X6#-cYZ3Jy;XU+78@8XpRf!d3?%V^=p4GV^@2@eNy<0U7u4_s)yN;)N=Yy$oyat%;dSGfaP?OwN5Ok@mlpMzzncH9kLaNqy@0iuvCC_wk zn#l{{aQ+rI?L(4|@5I63VNpCPiqP0~aB!ml>oCza+ei1durfZcT5C?ewQv7PJ1DXq z`#M^Dn9u+j4%ayYiHyrF(=R}y-^#S0vN5G{El#LZOYI_n;_J^l{(&CA0moFiHSh{_ z=I5%$tZ6lsxHgr~1w^GuHg$YKrt7RX^ZbSnJHA}MRLvKcOPKl=1MGpFd~(4 zqgItORpi{TzV3}@;@dL^>DF-w*QOwe?XDdm)x%JyBO}tj;_jD6u5`^LDFW|&C&C@L z##TjHL@3cg8jahB+_0%?)5yIOUn*(o!ne=0O_c_$_?f0DIx+j?1z$3@og0p_F+No@j`U7NF9W)Vs7Y>3^2 zK@@eCPf>U6m?Jz3R%s@yqvXG1BurbAmMOa7c@?Fnk3$UAeKx$oVpC5l|5ht`?Tc-n z?c965ntQhRXB~mHt_~(o`qvKR6nw6&R>irxCbb>LY#Ts72RG9AY|3YfB|ScD7rxXH zUU}zbqCe%EZvBxALWjhHeZ&-lMD={)Z@pg5UU6mblnvkG%ceABtyTTIy zPDX;^{~DEsobrP2ZpueyKAy)V6hb|bn-Gguy_T{a`yZSlb>+^Z*4o0nNd|BB zhdU;g{U+vRKm!ELO}A%bPTOpA-Hq)~@rdG>jeK(qnGJ6tMxzK{wkra5=6}vTK_}N# zDC5D*oY-F-KlVLM@*=npaKv2pu`TKc`9_=^$eLJOoAg~KwjPGDTnjFO%UY#cTU{w$ zCT#lp6?~{TKe}VEbO=W_NcRF{xzylY4Z0iFm=d42@vCS%vWQm2^q+<7`)5FmDrBx( z@OhC$3I7G}Ltd%3+St1hB*3#y1*qyYa64Wz_fhHmW>A}M+Ws;)1w}*VgKyi z2wVZVx8wfTMDChQb35VZ&~-{?HamDQ6j{Z@jGC6CMB5X%S*R$<%_{u&Q2}MXPG!5@ zwVuCilrq*Uyh85AN)g_47~Gp0#q{5SBhz-DzlOdhF5U>!s@PDf+gk3T@&pvNJ?e{T zRmc-cCaJj8PM*Nxp${m7ftJTO>(xxqv`TdUE0f{IPCVe2keHy(V#9MujfT~rh*bh@ zbFXv9Ak9th#Sd%gpSuoPtw1kEkYq@q+%9J2jI5)cg5?De<=l(d6BYkqcVG$tmXJ^^ zW9v!a;Fy9dwYaGR)B%u_zzk=2TA)Qo7XjcXOd5(S2uQp@y+6=F?!uck7(I$@&wSn}LX^dgq)agr%TE;iYmFQC(ZB zrI}qgW3bgB`whq#^oJs*gmfC)cSa z^cFJS7D|&JXeqg|B9V;%<&7CnjK}%;{Lpg{ipy88UgAj3xc(Y(^*E_YD3SA)m>B+z zRf$dLxndOq2+%+4PX!0QW75^ZcO3-iyVhx(R6E7R}^8 zTWp;?1_b;b*$6X#UR(f#&#dP`(xg3qP0$A$n_jY&k@ry6dgT9owhm8kjT?YxO z=7q^^zf4`4%gO`S2AP|>S^3vMYtSgu2fC4=Q0gc8|!RkRR0e+(4pHeGNFDvJ*+K=V1=Hfk86hA-yt z6^~80sdAmBG2_>p3fh&f->5S>nEn^12XL^Wv7*PJM#JY*W*xF$kuUa9MX{V^Wxr_5 z;P}feYv|ljA3*8oXQ5Sr_3|!$&8OcEDOVXqqE5P2d@0;kNu;Qm!xdCdiZan$zAKRc z{E6nEeWTS;-!WqQ3V;t5iok;wkP5)*gL`^sO%Fh_&F;t@=y#ZmmmuA*jN%N1Z#px89A(=62?x%#yz zGVMs82%&z(J@#FnM0W|Flp;OB?PB6**3OA#%B8WiqkpRdCOautB^L06mW=k?K zNBP}@ezv4O>t3ep@o~6-w-?K|7`nwc*#hzg*4V=Ve$J^44SkP@o^aGZJG#2cK#kwk z@p5O2JLQ`qni9}pAKJpogRhWr_s5iSUoiE3p$~~(=T4UGlhbW!pknR61)M)q`go1^ z`)FMVQt3t&8HL7f2-mth5mU);glcR}HHW0B8Y_ps_5{=<4LlP)wpQjR$|Ym(d`%8^ zRHoz(kRWXF)+Y$B=@%dZ8SN*|(>6cIi;&N}*qt_Mu;xo`Er`3&k5zpTnGR!xlZqO5i?C;UW6)aY$CvS$z` zmZ2fA?99`OtU_bYMYpjZZ1pDGx$YdVsQR;jhV14@sOc z*F-{}LD+lYMruwXK?o{8$Y2Ue2D-7qwY^8269g`Uzb{FU3yePam$;|Zh)o&x?iTQ0?fU2q@m?x(`r|{3%HA&;M>&r}VL$zhy&A#Dp0Y``Z-o@Gbbw zOg}&a33;c0*_g?=&q~W34_TLB3 z0?7XmTXZ;NPmd;VulliCHJyf#&qxYV z2Oj>ryX!A2h*PcUCkNF{KY)hx-rRJka%!!~VAX_!a*4-pM<&?MQ>0EH^od< zx&T$_Hy|eyyv5GoCNq89AW|=cqXqD3a+&lPSZsqEINc|<8h<&U60#I2xF>`fO)uY3 zK*!0>ORKHFi`)OU`L7l<7eKQx`=TalI*HN%{4vioeOI|yspZCfM$yuE(V=a=1jNuo zMpq|&S5A?)ppCU8`uD&h24L3z%0P7l=3^hWbE1g#;7SNvLPG&QJh9&s8ddvb@1FDj zs24GnRyul@$Hcw)4VuHaEEv@wB))oX$`qs@Spv`!z+&AWd#yJMrkX0f2UX^w-J~|0 z0_d~_U2<}TXAHIv(O|x-UsTO&#|==~qu5ngCZh@KoufD{1UzUCAgyloep$sI0Yi)0 z@>|^RzqbX#rYH0~oFj%^7gQ&eQsxN23n3nS-U?mrZugH7O)V2{`;yq0Gd$5nnyUx+ zv#3`Cv$`rYBud*n`(bffnN);sq2K0oI`;$&ZAEc7bk1I=Xj@-sEDfb{rZmTFW{5X{XRd=DaJq}7Fa zkrpxynTW%vqU|?`%vuGhG7-w?`S2&s=0!iE?w>~$`Z$2w(~Z9Zc=Z^Y z%>ADCkG$4@cJ1x`Xbdht>u+c*NG$H9&RlG%=RFH{9<$rnCT4tQj=uP%i#J370e2Zf zT7#qn%(o5qDmpMwrPw(?85_&)YEmUyj%!2`&eOM3ZUd1!wl zmkHNLAVOvl57Ob7)5%@rufIs-s{L`+0%_1!(-R?`e-(!3+Xe;f{uU3e zSLbqk{(FpY0PouU_vXMb6m(&?ZIx~V)QPq2ALrDVmqQhs@8gp`w-jrEdZ|Y5*T_h` zHqYTrkf9-XlhAwh9h$Cy{&t5XA44Bl)&&}I`PHs90MC8J456g@;KwvI}$mJOAeksnwa^&_5HUb^PjEagAQ2H%SpVmsM{E#?Fb@x@lcOI-IqEa%F)3hQA0rWv(9)_4hX-~Gaa zzdkG0#&>Y$j~=~%uFVN@~I9Zx(XOJn2A7m+>RA+>0b7pUh2>d zlBUk&%&ERR1g)X*MliP2OY6$VSKJRTcCa*by4X}Gd2M5w%zD*ne#Q#_-o$WSFx|?E9_GZ2Fnhtt zN{>pOV`@zYrm7$D6?xQNf^8uDBJx|*V|gPlK}^<;ew|9_uU}jEq53EA9~&nnWR<}c z;)Y;{gTj}m>E^V58vnIdKZo`MW)+7?t(E2BRq6%heDc3{4a}*=il?=&_IVBOgWNSY zs#c-yjCs|eDk97sCL{`m7bNuZx71@VdGJFDX8uP)m=`6dA<@doz6aQR#46o!Y^LOr z_ocI7227ZtL?jy6Np3hxo+FAn_Wv8%s4PCKK&_+`;Zv$f*Zwgr;lLDorg{vc}M}SIm^F+uI z&ZX&{g)g!wmYyV$+Kt{SY}hM67lBtLO~*_G6X=iQZn$V6QeGmhJ#8FpEXUx9bjwXz}Tu*G$f`Ityv*M1Ubl`k#JjC0S3J}OT$>?=XOk=s9 zlY9eHPo(2$!}3`;pAg6{tBd^%hVi{mCiyZI>G?7gVPVm>@76mS?B7&sfmZ}1dE{%Y za9di^+Kj=d9+z%SP|~!CjcPR%@dc%H>D@Sb&;Vol_Y_3*%(Vu~G}%`0;22;n8LMzA{^%vFRHZNF;I;0ZsNRv}z#G|TvIq*Gz& z$U!kh#ZKw5RQ(k>=Dh4zYQmlPBDK+&Xv~dyJjB8~QS1$$yQ1c`PB|0XHv9l45~GXU zPNF0kDiwlRKp&;axDnBZdb~-1aM@LYFB{WT6H^$z*Py0o+WloqI|ZvBDvt;ZNbrG0 zKMK2^Qm@XZAf@~uCEi!*Vr#`GZN0-F0+|sZl52Sl zrBiua^k?74tZz8|;=|Av0V$fi;5L;osyF*S<8Kl%8{NMS7*z_c-}eG%?DTtM?iM4# z?95#Lqip6LubtKPSMS+sKjNzi+2_|R2(PDNBEkIYGZtfdK1oAyL3ZY(9phs3=sD@< zPPt6M_ytm{rxB@VGhL8fO9M32yx>UTAK{<)G=DV))e*%--?Mh^YdxvTmgLu{g-Ni8 za#s#)_eq3a@HjKBT-!49B1nQjii*6~2?#&(-$*+zn3@MA(NhB4Cq26C29qA;^>q)J zYL)naVhZKq_x2Dl>loUCabq^d)ry0&JfLD7d4+6PsRFEghJ!oG^}IXgxrxuLLB}Hc zBgpsmK`lMei?$Lry-eZKbzOD!4hmFU8{C{e=(kA-RtHmT4saY#B%o}siBwj2`?GiZ zc)yvtYg8~1-Zg(-Vm}nkRQ`5$H}}TMvPzc;wG=9$tgv@W3nhvIwzG3(wcAqe10{9v zsz94jy=jAe|I_cms25rVH|jsPgm3Cd5j=b>BJxAy*Jaie%x8-0T*nzRk@j|O9!2$n z1LjEm(xG0^vH@Ou*a;&G7>TW<|uY&j0BW!Jy8I(W~ptu}&s_g+K`ecRn~sX2}S1Zdf63nOCN z;)%2L!nt7jR~EcPp~Dy~&BVDl{SLFZL>-1s!UxX8-UTJ)`XFT$l*4c&!>7#qLU^x| znyMz5!(TOo$yfbr{S0b~6bpXUZqBAy-Ik$(2 z=T=H+o`o}MrZvCy^8>xHw(uyL(0Z;E4$Zn>l3V+^Oj@p4mGiX$=+g#~%q9gMKeNHx z5?^Zuz2HjVnYY3L2!+oCQ@MKcQFBtRi{8K;=a_~F7G(n($Mu2PbFFuqke+#83?5U- zZkYW6N!z{f|4&EJ|Nq7RztlhoBNh7SqgHj`fFGU&PjGA+kFXV?Kn@DHF{VR>H&zoZ zeAA1F?{^;#bX>{3!3UUJi>&B%YRB^VlBPe|*x_N@5Fwtdz#Q+mk2Eyp*&aC6_!eY^x1gC+J+B=w=0fTR33)AyA1%&A zu~`2U{iK27(upI)wTb45DU7vUGIXum>Ub&`)<^Ck!5@wE6doB}8d^)esG0ofT!8Dl@b(ygf{VR!XSOF?pIr;I zZ9F4d6tx*wS8g+}&8;>8pnoOI;r&3WT8;S zG;X`7^Rd4+kIB%k{D4!H1jnu~kJ;_oci~6#-%bV(j&jr%IgXU|=-5q5t+__Zaoc!Y zoYN<-AAlza?xS?X70f(%oBGkqom{h#$mQCi=iGu}u`aoR1G9m3Frh;7o`U~(Vy6Ly z@{K{h-lob5dFvPX0Am{D#QtE3fBoRgy{OmgF%M3%os5rcp1SHS8%~-2A-y4yR*-Fs3K^Bq}AIkD^;eGW5%@?qG|T>TwwI)3+}conLf z+I&5v5Dll(?@KSYI)>K25u<}7pE_#B1bsls1SI?c>VJd2xJ-CVprc<%C zchQUv`cm9f=*^Y#W5w}A%C}o{Eup?IP)f|SDkp~K7p6}oN1`P;HdyD{qZr3L>LuuI zN;=F2w<=@@v@I8nIG&+x-mK1C>}m2AAdQ3Cc$)^2e0&-5^hU&*o%d3G3;pVdmQybC zF8=E@B(`@=YYwaGBNJpa!s9&A&N6iY$`Vk4=0DONc~O7R7xxg_p+i8YLgwg0a8jjg z?tin-(HZ^>V*KKAh83zsXd11E{{emX@9J7ttcfM&p|Qk4&W4w}EWFT|FQ-vxq&iMu@izEM2%&B@ zw_=ohDF(W!$bTxN%H9Q^huC{jLZpYvB_ZKb?{e!w)>=V3$Zpn#ASI!9yXS5Mn)+{j&L0;z!4k0UK{XZzV9!7D0_6_M=2yZkW+ zsLu6)uY7fKuDqr${W=R5y9PA!`PR=vS=nLGuL|kyOA$b|K@t!q-vY+~*#$#BnoEX% zU!qeH@xEeYT<^`|p9q~t@o+`%40X^pv3Xy;yI`@GRrb)s1pddG~~u5u^YQ3-)|#O zVNEa=v1$@8s3*LrHe1UX+WKq&7SPdw=54eOo^WI#<|P@yrWKHovIwF93JJRk-5c%a zf@)RF_g*xFU4edFYrCJS2rqQne3h}wCC3_-T(3ZH+xhs&m zRv?YpFn-YSBDjR-AzJ9=W$IGgP%Rmgg^9Wf{ZMTE0gT6C_7b)vuyC$VV)I&U6m@6x zA>KLh*_)UC0snC#1PZx)8tPex)^1eHr8~?bnivk3s!d*+p|>pw`vaPqMPzPht6q&`sI@sQR5Y^!zaJO55J}I<_0v z61d7muxmhynqDl^>{lz?RgEq2f3yGYr|rvrVAr;w`LSp2Fe?}9L41(Xsoy5sYmNb) z{l6;zP21ek;_rCHbgOPgRG?LMzlT&%OX%nq#c z#2JWrarLu)(N^`fp`6g)af-d>h5u(4R&>oQexo^(rW*`27~(bma54WvsTaUIyHa3V|D1!KDLgMMAl1SWBuH`K zXsEyX@6nhn(wNn-KJVVD9TNSm@jq+ai78ZqVRS5U3_l)#pzs!G*sn&Ru3Zhh7|{1W zI{^|nc4LcS0Y6#!(WUolfWs zCn|PWS5V^s)zr?=hs-H2xO;-gVp{bU>5E@)dctA7?+4ZaeZu!++9Z%23;$LuA)ZX4 zUYv>(A!Rn+6>|>7#Fu9<->*UH%ruuFvt9|P=>akw+{s?;Ne{}(4ytMp9MH&VC@zC^ zK$m~C_CfN$OL&1e$T<1(1~@_F!3d=d2PsU!KR zkhG+;;7uB{r1F~ry4>MhW6a+{NYS7M`-Z@C#E0XFVS%#RXTk~6AduXmdJ2bg&@xR^p?=XOuq}y2 za7f4NB-8kulRilU&CrLeFj#8_g%{le_uFLEB+2$@*1IIFAw4ynXYj`#lYi#Muj+HZ zT8)AW{bB{qE>{BRv*{*wtzOJ0lYt4%xA2vhw)wXYuswBMZ$+*;7f7X!P6Y?^#%4yq zrF-?fq34ZlOC1gU9p`wDIH;cYALS8q;CS0PmEtxABW6}Xl%Un1W(c)(%^QlwpQbsQ zDQZLM#`+92X72TL%~KcqLvzP1a7>V$H<(YGQ*)U0shA68aRy$iV)#yqw|3?NZeoLCcUxJ*sot8UHx9G2Nu9(28-e?-HNF*wvbxKg zXp8N0i+91t-<$}^*I&ugFIuaE{Y+&eKMb!p4_&j(fZTEq9v22YVelRP9bA@3s3lkt zflnPepocEXLs59C91Om*_0NUG=hRe=V?(z1g@M)TS?VPQHlky4>lu%Cnb7v4a7JE=p)qdEFYQ&I>i$Ba;D?g?!I;k_yO;!H*UpPn@`HqQnkQ>6>Dstjo zB`51XKd#efApIT+4EqW^zC~}s2IRJ|H`MXDWd8#69``r& zL{eH*IB!%~ ze|69MrDo~O1H%D74sO+=XBsW<<5inEL3&pGQyNrrC06( zWJro0I(W@QAL1hTH6H5+W*okAFMmA+Ov(_Jk_yZP>&dhgvsaM%kEp?ThT5@je5P)LXmpzG2tE2^H-knlvGrm}jW z@v7K0>9Dbczk$kKY&=>@5WdB?`#Wk+1qkd^p6hx%589cV>fnv1MgqMeR^o_vsR4M! zzC@74qb#@og8nSzrf_twX=ZU-`OeHmI9WWeXYT$K~3TPIY{ew8GXp>+6$1)QOD)Ss^Hm! z05D{&p~XOIQ~-sB;ZKhnF(szkm+lqT%O84wKOJrTD1~}WF4BD3w$V^qmV@&%^X!AY zQe||58S&JK(pEz4}SOrMQ5RlA3Om+YSd1e7cR z2dLW50lD#xFNl!L-|D9Y&KJ0_Tgv^zY|OFa;MI?D=wtaN6zBZTn(Nua=UQs>5BlKOLh2bZt}Z%m>7^P`vw=E zJ0;&z#CNGX+LCnRzrupQr-kYn2!yrb7+91fz5}Hr->itB}xT2_W z8d8+DuP%cZd2BSl!$)$4Ji65FkS|_o!c{kTt>@05Pj{;UN1e?-RLAt-M2O?Qynk|8 zIL4;atrUWoPCUAITesdVW8314oBM;)kgI3x%xkzb^l6-Ca=5nIXLU%D-&x}f)M;1{ zFh+X0l=dKiFO??YlY7|13N8uT)}jjgTSzUxv7tTVCBF;Q89^}9FN175;*%<5K;gIu%OKbyHP)M zl(|Zh069R0lzhfUby8>Q#X9^T84imqA>Nk(fyZ7KlDW8)0g`WT%U|_FM8)T{$=t=z z*SD4+mw@b!7@I{~i`eWepyz{Dvz9>|`|h;_V%Z6q+kQ`inPbJaI)=8S-uS~TW*brP znrHvrH!JjU>-TW8C5r&gr5D5sp(Z72(4lP}qkz~-11&Q{pz;y=H3YetJ5;&bq-mXg zZiOwN?18(~dk2;ylhw!zj_j$>ME;PgatvJPJIEWg`e;?Ksxo84-2))6)Ae%(*1mHx zz_#;mYHigGNk6NzJ*`juiIuOS){Rx=YlsGoPo!bX{3AY1*!gBF zYIXOHX7YPKlf$fPko2ip>v$7Dmj7=$-6N#|im?$VGB%ZC|44t?%YZidM#b>Uwgfpb z>Pmz4Ju}>5ui>J{g0avwmlo9=(xj^W?K&vg7dx^@`~$ic4xNY2pP;!slGQ^qnHOJ| z7cZo6x0&FyuNy;^1lnV)0i2g#lKP%CFAT%roS2aYDm#MN?;!b1%t2eQ&DpV3`5@0YGNEH zG}eHh?3;(a8lQCvn)kN6Deo6q+|Jni`+sV?TZ)jdWBDB4!5tNk){mwja#|a1ru`O6 z4XUpg=7(n10dE|F9H>rvsm|^~V>I1oIQ2XJMzudce@+ zf;||VzUY)4YCOqF%Ujltqu%5HR)5YTe?T8A40qp72&>3?NED+bAQpnn%MYFYLa~7! z4x(3hC#+YFma<)H+?JA_GJ{Q)UjdsGF15#IHdp1P1dS=0g#TarC_D=xVZEYTApe`% z_Tiyb`9N@#?X?tk?w6HFyVJH1aMZlvlb^=H6bY9&&V(vYgs4^62<&2LKNKGvjd%RN zRF(xSS9+BL7#ohb6cIkLEO{DwLAb`@LN=T&u`1G+KllpsPCa%MHC;1DME_iu_wPY_ zezRN%Dj)qwxB}AarRutdQxgAMxH$J!f^=9Y+czvEZ2;t;ori}%B7(im$N2Bhq>32) z4EFY_A-WlC5*qgSk=ZRpTwByjhN4wb^(ebj`2SRKCGKpcYy6~IOEIOMNi9WN(`p|T z3`z+~Tc)GBOxu*$t3_ihi8PsLky52s!`x|WOOf&@MiaFJ5k=KtV$x7+5KA>l?Rzw$ z+%I#Vd+&44AMl>@z2E!$z3=b$opWBT+Y$vYlV*tnV`k^)L|CKH6Z74NcK&y7P>STT zV#4LHxOtH5I|F0hCzBwJCY?PZHJ-+zQH)C@m9a*HFB|Z@tn$-93futa*6bpup;zph zR(>HgA~+J^7`zgQ4=~XETcL*!X+w57ozDlNIj=?-BngtKv{}=>*A1w@W=a?nR~16r ztno`Oc{b^gzOCq+#kV8~N zscH|lLi3cY^qD&_9VMRAYx6}YN9z{PjbCL4yGMiGkAB}`kuUh;aMz%fJd8E8daun4 z7SQs)#qEde>@w*RpWU-PS+Fb(UnXuqGtYEt$SXp}tNbiJJU)cE{75snY11c!J6|!= z4j}rnxbJU;6sZN>NS(ZinXdiaUslQg77Cb!x;Bk-Q;q<~>~o2=u@-dbFHg>f5#k{G zDjiv*p5L-~e|^kwLOU{AoI1D`1ilyR=@CI)-K=b5;LZ(1`!??#M;rha%lQ+~@lR#= zlQ}dk*m%I}D{F?OeiR+D%1I{Dnq_Z(TKwU0GwTe)F7HxIXZ1hU< z8^vxP6c9m=vFF>{9Ti*U6W9fS5E#!!1*%9|x@^7r)jmcOGLL{d^bcFPTnA-IZ+HS} zyls0jEF?BSQlDJQ6$|3-{*e?oPL$QYB=TG{as;UdxAc+JC3XiRBOTRpD6u|Sc9 zF|Q)xHA6{K)11yJjBvawkOnFq{ezz(0mV@NeoL^Trn3E;926IJk>~lvs75$q=S{_c z0vJtiVd1)cY||i90UFYWrWcciA-$YVW=CEr>-Y%Q9zw9@EQ+dm0>hj8~B)b*RnuC_=;6-;|@P(%~W<40opbS6=BeZ-xFEKRW54 zk+OSG)3l;H-(uC%27e6f6r(A!uBaKTQ;~d&{bRILww!Z75?STJY(ZY5Z#L$24Id#G zDxBKuss;^zi1T#}3cp;FF5oE_Klx89g|#8w$xBqMbtWBQD7dshlHg1$SNb@lcL57Q z7-1DgI6I**XI2(iMHSA)Ftde0?Y@(FfLMK1A$_LkTHyPC?j%1_B8T7J7nVn-2zXEz zNq-Nt@Uys1=V*mYHxLg1+8g{8k{1+q9cvAQN-t-hZj=F|Ft->e!jDGnf;OaF6X`$* z#3*|M8(yjj5uA6xXx~J_4RqP%Fj~)E73f;(?B~H)ig`UC2jMbjsm%^{;Kcgr<<@5R zgrFMroRIjryk~#3Q|>+{f;mHeS3D|?xY>SK0|Yw+Hs5CY72o0S1CL$G`-eU>?qip0 zx&pOdMA1ZO2R)B>kXX=I_z00oPG_cGl%qj$>@YWjBFGFy$cxk(tb$|GW)~uIy>NTX57AR3)SJSZ3$h{S|4BiY`pPjwR!|xO`-H_J>U+Ej34!G*ETk*Hu*xP9ujX0qx#m?|eBGL4>w1!P&=1EW#jI&4s=lzPI3b45BV@VEd<-SS1=555rm z7j(*Fm9}Mo5lX%o%Z7$c!9mDTqH5U&%dmH>aKHt_)pOGoC!ItR=0cSmeu;`_;u_Gb zmoTx|l&RO5Jq>1Spn3_H!s0x!Kfgf|=*p+*(w~?MKafuA$$V0ur5j)+1GP5>(xQ4f zVY-XCKJg}>B6Vz_ONF?*Uj~h38^dCqGlKC1@;|7zMh5#s9!v$i&P7Wb#oA3z^~)z5 z`uiosejmS+_XP>D!`FoA`Uy^7(x!@hOKKh42CuAod{!wwaOP&@x zVrLV!mH`{%t<|(36S3f2>jVEQ5l>Vp%wL=d(_>~ira1TNV}!5h@$-DyUDKBU95&I+ zkJ|pc#6}Ck=jWXTXzA5fuL;DU6Wgbt!JMg(f7DgClO$=Nr&~ue*^(E@Y-d%kD-)Zz zE7Z()(<*PmtunV4ZS&)h1g`SM=o6oGhBVE7&Lt!hFzpUNl98E&cqPz7YlpIYz-HH%ZyD2+hq5~&c5ycY3 zCa=JP?H3=|If=0Uj*zSD9xeR@cUl=~sGf^u$h)tJ|Hw4SI7ZbQC4O#oZdA2Q1^;`zrQF!FE3;D-VXY4`6vU!F z4A%zTXus&n#63cj*lcOXAPj=2!VHz;+}}FWu7vqrQ~cI=wPtEQox1e|kMO1^&9?7V zIg^|8amSaD%?dOr2`+koEBwZ!_^IVZT=ef~5*58-Biz2l`mQQ}3XIyzrGW)OcSez; z+|3V2naMiRYP~S~B5jzXZn>LuPMR&|2iBW2y~_)Gb7HM{ikK?!Hw7&yMqLu`;C1_~ T_mO^e2wdmRUO2;W2}t-Cb$M~B literal 0 HcmV?d00001 From bdd5e334ad8b1c3cf9ed9d42c4c56e5f7a91ccaa Mon Sep 17 00:00:00 2001 From: Andrew Masiye Date: Mon, 2 Feb 2026 09:25:51 +0200 Subject: [PATCH 3/7] feat(exceptions): introduce DebuggingException for improved error handling in Debug class --- src/Debug/Debug.php | 17 +++++++++-------- src/Exceptions/DebuggingException.php | 13 +++++++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 src/Exceptions/DebuggingException.php diff --git a/src/Debug/Debug.php b/src/Debug/Debug.php index b869ecd..112f27c 100644 --- a/src/Debug/Debug.php +++ b/src/Debug/Debug.php @@ -4,6 +4,7 @@ use RuntimeException; use Sendama\Engine\Debug\Enumerations\LogLevel; +use Sendama\Engine\Exceptions\DebuggingException; use Sendama\Engine\Util\Path; /** @@ -11,7 +12,7 @@ * * @package Sendama\Engine\Debug */ -class Debug +final class Debug { /** * @var string|null $logDirectory The directory to write the log files to. @@ -86,18 +87,18 @@ public static function log( if (!file_exists($filename)) { if (!is_writeable(self::getLogDirectory())) { - throw new RuntimeException("The directory, " . self::getLogDirectory() . ", is not writable."); + throw new DebuggingException("The directory, " . self::getLogDirectory() . ", is not writable."); } if (false === $file = fopen($filename, 'w')) { - throw new RuntimeException("Failed to create the debug log file."); + throw new DebuggingException("Failed to create the debug log file."); } fclose($file); } $message = sprintf("[%s] %s - %s", date('Y-m-d H:i:s'), $prefix, $message) . PHP_EOL; if (false === error_log($message, 3, $filename)) { - throw new RuntimeException("Failed to write to the debug log."); + throw new DebuggingException("Failed to write to the debug log."); } } @@ -118,11 +119,11 @@ public static function error(string $message, string $prefix = '[ERROR]'): void if (!file_exists($filename)) { if (!is_writeable(self::getLogDirectory())) { - throw new RuntimeException("The directory, " . self::getLogDirectory() . ", is not writable."); + throw new DebuggingException("The directory, " . self::getLogDirectory() . ", is not writable."); } if (false === $file = fopen($filename, 'w')) { - throw new RuntimeException("Failed to create the error log file."); + throw new DebuggingException("Failed to create the error log file."); } fclose($file); @@ -130,7 +131,7 @@ public static function error(string $message, string $prefix = '[ERROR]'): void $message = sprintf("[%s] %s - %s", date('Y-m-d H:i:s'), $prefix, $message) . PHP_EOL; if (false === error_log($message, 3, $filename)) { - throw new RuntimeException("Failed to write to the debug log."); + throw new DebuggingException("Failed to write to the error log."); } } @@ -155,4 +156,4 @@ public static function info(string $message, ?string $prefix = null): void { self::log($message, $prefix ?? '[INFO]', LogLevel::INFO); } -} \ No newline at end of file +} diff --git a/src/Exceptions/DebuggingException.php b/src/Exceptions/DebuggingException.php new file mode 100644 index 0000000..5650ede --- /dev/null +++ b/src/Exceptions/DebuggingException.php @@ -0,0 +1,13 @@ + Date: Mon, 2 Feb 2026 09:26:22 +0200 Subject: [PATCH 4/7] feat(exceptions): add IOException class for improved error handling fix(InputManager): update enable/disable non-blocking mode to throw IOException refactor(InputManager): enhance isAnyKeyPressed and isKeyDown methods with ignoreCase parameter --- src/Exceptions/IOException.php | 7 ++++++ src/IO/Input.php | 6 ++--- src/IO/InputManager.php | 44 +++++++++++++++++++++------------- 3 files changed, 37 insertions(+), 20 deletions(-) create mode 100644 src/Exceptions/IOException.php diff --git a/src/Exceptions/IOException.php b/src/Exceptions/IOException.php new file mode 100644 index 0000000..dff0be7 --- /dev/null +++ b/src/Exceptions/IOException.php @@ -0,0 +1,7 @@ + $keyCodes * @return bool Returns true if any key is pressed, false otherwise. */ - public static function isAnyKeyPressed(array $keyCodes): bool + public static function isAnyKeyPressed(array $keyCodes, bool $ignoreCase = true): bool { - return InputManager::isAnyKeyPressed($keyCodes); + return InputManager::isAnyKeyPressed($keyCodes, $ignoreCase); } /** @@ -99,4 +99,4 @@ public static function isButtonDown(string $buttonName): bool { return InputManager::isButtonDown($buttonName); } -} \ No newline at end of file +} diff --git a/src/IO/InputManager.php b/src/IO/InputManager.php index 07955ae..e18bbb4 100644 --- a/src/IO/InputManager.php +++ b/src/IO/InputManager.php @@ -5,6 +5,7 @@ use RuntimeException; use Sendama\Engine\Events\EventManager; use Sendama\Engine\Events\KeyboardEvent; +use Sendama\Engine\Exceptions\IOException; use Sendama\Engine\IO\Enumerations\AxisName; use Sendama\Engine\IO\Enumerations\KeyCode; @@ -57,12 +58,12 @@ public static function init(): void * Enables non-blocking mode. * * @return void - * @throws RuntimeException Thrown if non-blocking mode could not be enabled. + * @throws IOException Thrown if non-blocking mode could not be enabled. */ public static function enableNonBlockingMode(): void { if (false === stream_set_blocking(STDIN, false)) { - throw new RuntimeException('Failed to enable non-blocking mode.'); + throw new IOException('Failed to enable non-blocking mode.'); } } @@ -70,12 +71,12 @@ public static function enableNonBlockingMode(): void * Disables non-blocking mode. * * @return void - * @throws RuntimeException Thrown if non-blocking mode could not be disabled. + * @throws IOException Thrown if non-blocking mode could not be disabled. */ public static function disableNonBlockingMode(): void { if (false === stream_set_blocking(STDIN, true)) { - throw new RuntimeException('Failed to disable non-blocking mode.'); + throw new IOException('Failed to disable non-blocking mode.'); } } @@ -169,21 +170,23 @@ public static function getAxis(AxisName|string $axisName): float return $axis->getValue(); } + $value = 0; + if ($axisName === AxisName::HORIZONTAL) { if (self::isAnyKeyPressed([KeyCode::LEFT, KeyCode::A, KeyCode::a])) { - return -1; - } else if (self::isAnyKeyPressed([KeyCode::RIGHT, KeyCode::D, KeyCode::d])) { - return 1; + $value = -1; + } elseif (self::isAnyKeyPressed([KeyCode::RIGHT, KeyCode::D, KeyCode::d])) { + $value = 1; } - } else if ($axisName === AxisName::VERTICAL) { + } elseif ($axisName === AxisName::VERTICAL) { if (self::isAnyKeyPressed([KeyCode::UP, KeyCode::W, KeyCode::w])) { - return -1; - } else if (self::isAnyKeyPressed([KeyCode::DOWN, KeyCode::S, KeyCode::s])) { - return 1; + $value = -1; + } elseif (self::isAnyKeyPressed([KeyCode::DOWN, KeyCode::S, KeyCode::s])) { + $value = 1; } } - return 0; + return $value; } /** @@ -192,10 +195,10 @@ public static function getAxis(AxisName|string $axisName): float * @param KeyCode[] $keyCodes The key codes to check. * @return bool Returns true if any key is pressed, false otherwise. */ - public static function isAnyKeyPressed(array $keyCodes): bool + public static function isAnyKeyPressed(array $keyCodes, bool $ignoreCase = true): bool { foreach ($keyCodes as $keyCode) { - if (self::isKeyDown($keyCode)) { + if (self::isKeyDown($keyCode, $ignoreCase)) { return true; } } @@ -209,12 +212,19 @@ public static function isAnyKeyPressed(array $keyCodes): bool * @param KeyCode $keyCode The key code to check. * @return bool Returns true if the key is pressed down, false otherwise. */ - public static function isKeyDown(KeyCode $keyCode): bool + public static function isKeyDown(KeyCode $keyCode, bool $ignoreCase = true): bool { $key = self::getKey(self::$keyPress); $previousKey = self::getKey(self::$previousKeyPress); + $keyCodeValue = $keyCode->value; + + if ($ignoreCase) { + $key = mb_strtolower($key); + $previousKey = mb_strtolower($previousKey); + $keyCodeValue = mb_strtolower($keyCode->value); + } - return $key === $keyCode->value && $previousKey !== $key; + return $key === $keyCodeValue && $previousKey !== $key; } /** @@ -316,4 +326,4 @@ private static function findAxis(string $axisName): ?VirtualAxis { return array_filter(self::$axes, fn($axis) => $axis->getName() === $axisName)[0] ?? null; } -} \ No newline at end of file +} From dcdfb4f6a51e17e8781f7c3b5eea82c2013143f1 Mon Sep 17 00:00:00 2001 From: Andrew Masiye Date: Wed, 4 Feb 2026 02:58:06 +0200 Subject: [PATCH 5/7] feat(splashscreen): implement SplashScreen class for displaying splash screen with error handling --- src/Core/Rendering/SplashScreen.php | 72 +++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/Core/Rendering/SplashScreen.php diff --git a/src/Core/Rendering/SplashScreen.php b/src/Core/Rendering/SplashScreen.php new file mode 100644 index 0000000..7db798a --- /dev/null +++ b/src/Core/Rendering/SplashScreen.php @@ -0,0 +1,72 @@ +getSettings('splash_texture'))) { + Debug::warn("Splash screen texture not found: {$this->settings[SettingsKey::SPLASH_TEXTURE->value]}"); + $this->settings[SettingsKey::SPLASH_TEXTURE->value] = Path::join(Path::getVendorAssetsDirectory(), DEFAULT_SPLASH_TEXTURE_PATH); + } + + Debug::info("Loading splash screen texture"); + $splashScreen = file_get_contents($this->getSettings('splash_texture')); + $splashScreenRows = explode("\n", $splashScreen); + $splashScreenWidth = 75; + $splashScreenHeight = 25; + $splashByLine = 'SendamaEngine â„¢'; + $splashScreenRows[] = sprintf("%s%s", str_repeat(' ', $splashScreenWidth - 12), "powered by"); + $splashScreenRows[] = sprintf("%s%s", str_repeat(' ', $splashScreenWidth - strlen($splashByLine)), $splashByLine); + + $leftMargin = (MAX_SCREEN_WIDTH / 2) - ($splashScreenWidth / 2); + $topMargin = (MAX_SCREEN_HEIGHT / 2) - ($splashScreenHeight / 2); + + Debug::info("Rendering splash screen texture"); + foreach ($splashScreenRows as $rowIndex => $row) { + $this->consoleCursor->moveTo((int)$leftMargin, (int)($topMargin + $rowIndex)); + Console::output()->write($row); + } + + $duration = (int)($this->getSettings('splash_screen_duration') * 1000000); + usleep($duration); + + Console::setSize($this->getSettings('screen_width'), $this->getSettings('screen_height')); + Console::clear(); + + Debug::info("Splash screen hidden"); + } catch (Exception $exception) { + $this->handleException($exception); + } + } + + private function getSettings(string $key) + { + return $this->settings[$key] ?? null; + } + + private function handleException(Exception $exception): void + { + Debug::error("An error occurred while displaying the splash screen: " . $exception->getMessage()); + } +} From a2796efc748c31333d849c53b4149eb225ed0b07 Mon Sep 17 00:00:00 2001 From: Andrew Masiye Date: Wed, 4 Feb 2026 02:59:00 +0200 Subject: [PATCH 6/7] chore: upgrade pestphp/pest to ^4.3 --- composer.json | 4 +- composer.lock | 1271 +++++++++++++++++++++++++++++-------------------- 2 files changed, 744 insertions(+), 531 deletions(-) diff --git a/composer.json b/composer.json index c3592c6..d9f5898 100644 --- a/composer.json +++ b/composer.json @@ -2,8 +2,8 @@ "name": "sendamaphp/engine", "description": "A simple game engine for making terminal/console games. Lovingly written in pure PHP.", "require-dev": { - "pestphp/pest": "^2.34", - "phpstan/phpstan": "^1.10" + "phpstan/phpstan": "^1.10", + "pestphp/pest": "^4.3" }, "license": "MIT", "autoload": { diff --git a/composer.lock b/composer.lock index d585879..c9deb01 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b73d2037d9a4ab2a693931c4fcd2119b", + "content-hash": "d321fecddb0ff981b106c127d06a3533", "packages": [ { "name": "amasiye/figlet", @@ -113,24 +113,24 @@ }, { "name": "graham-campbell/result-type", - "version": "v1.1.3", + "version": "v1.1.4", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", - "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.3" + "phpoption/phpoption": "^1.9.5" }, "require-dev": { - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7" }, "type": "library", "autoload": { @@ -159,7 +159,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4" }, "funding": [ { @@ -171,7 +171,7 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:45:45+00:00" + "time": "2025-12-27T19:43:20+00:00" }, { "name": "league/climate", @@ -241,16 +241,16 @@ }, { "name": "phpoption/phpoption", - "version": "1.9.4", + "version": "1.9.5", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", - "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", "shasum": "" }, "require": { @@ -300,7 +300,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.5" }, "funding": [ { @@ -312,7 +312,7 @@ "type": "tidelift" } ], - "time": "2025-08-21T11:53:16+00:00" + "time": "2025-12-27T19:41:33+00:00" }, { "name": "psr/container", @@ -474,16 +474,16 @@ }, { "name": "symfony/console", - "version": "v7.3.5", + "version": "v7.4.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7" + "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cdb80fa5869653c83cfe1a9084a673b6daf57ea7", - "reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7", + "url": "https://api.github.com/repos/symfony/console/zipball/41e38717ac1dd7a46b6bda7d6a82af2d98a78894", + "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894", "shasum": "" }, "require": { @@ -491,7 +491,7 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2" + "symfony/string": "^7.2|^8.0" }, "conflict": { "symfony/dependency-injection": "<6.4", @@ -505,16 +505,16 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -548,7 +548,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.5" + "source": "https://github.com/symfony/console/tree/v7.4.4" }, "funding": [ { @@ -568,7 +568,7 @@ "type": "tidelift" } ], - "time": "2025-10-14T15:46:26+00:00" + "time": "2026-01-13T11:36:38+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1058,16 +1058,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -1121,7 +1121,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -1132,43 +1132,47 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-25T09:37:31+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/string", - "version": "v7.3.4", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f96476035142921000338bad71e5247fbc138872" + "reference": "758b372d6882506821ed666032e43020c4f57194" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", - "reference": "f96476035142921000338bad71e5247fbc138872", + "url": "https://api.github.com/repos/symfony/string/zipball/758b372d6882506821ed666032e43020c4f57194", + "reference": "758b372d6882506821ed666032e43020c4f57194", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.1", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -1207,7 +1211,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.4" + "source": "https://github.com/symfony/string/tree/v8.0.4" }, "funding": [ { @@ -1227,30 +1231,30 @@ "type": "tidelift" } ], - "time": "2025-09-11T14:36:48+00:00" + "time": "2026-01-12T12:37:40+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v5.6.2", + "version": "v5.6.3", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af" + "reference": "955e7815d677a3eaa7075231212f2110983adecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af", - "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc", + "reference": "955e7815d677a3eaa7075231212f2110983adecc", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.1.3", + "graham-campbell/result-type": "^1.1.4", "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.3", - "symfony/polyfill-ctype": "^1.24", - "symfony/polyfill-mbstring": "^1.24", - "symfony/polyfill-php80": "^1.24" + "phpoption/phpoption": "^1.9.5", + "symfony/polyfill-ctype": "^1.26", + "symfony/polyfill-mbstring": "^1.26", + "symfony/polyfill-php80": "^1.26" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", @@ -1299,7 +1303,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3" }, "funding": [ { @@ -1311,22 +1315,22 @@ "type": "tidelift" } ], - "time": "2025-04-30T23:37:27+00:00" + "time": "2025-12-27T19:49:13+00:00" } ], "packages-dev": [ { "name": "brianium/paratest", - "version": "v7.4.8", + "version": "v7.16.1", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "cf16fcbb9b8107a7df6b97e497fc91e819774d8b" + "reference": "f0fdfd8e654e0d38bc2ba756a6cabe7be287390b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/cf16fcbb9b8107a7df6b97e497fc91e819774d8b", - "reference": "cf16fcbb9b8107a7df6b97e497fc91e819774d8b", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/f0fdfd8e654e0d38bc2ba756a6cabe7be287390b", + "reference": "f0fdfd8e654e0d38bc2ba756a6cabe7be287390b", "shasum": "" }, "require": { @@ -1334,27 +1338,27 @@ "ext-pcre": "*", "ext-reflection": "*", "ext-simplexml": "*", - "fidry/cpu-core-counter": "^1.2.0", - "jean85/pretty-package-versions": "^2.0.6", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "phpunit/php-code-coverage": "^10.1.16", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-timer": "^6.0.0", - "phpunit/phpunit": "^10.5.36", - "sebastian/environment": "^6.1.0", - "symfony/console": "^6.4.7 || ^7.1.5", - "symfony/process": "^6.4.7 || ^7.1.5" + "fidry/cpu-core-counter": "^1.3.0", + "jean85/pretty-package-versions": "^2.1.1", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "phpunit/php-code-coverage": "^12.5.2", + "phpunit/php-file-iterator": "^6", + "phpunit/php-timer": "^8", + "phpunit/phpunit": "^12.5.4", + "sebastian/environment": "^8.0.3", + "symfony/console": "^7.3.4 || ^8.0.0", + "symfony/process": "^7.3.4 || ^8.0.0" }, "require-dev": { - "doctrine/coding-standard": "^12.0.0", + "doctrine/coding-standard": "^14.0.0", + "ext-pcntl": "*", "ext-pcov": "*", "ext-posix": "*", - "phpstan/phpstan": "^1.12.6", - "phpstan/phpstan-deprecation-rules": "^1.2.1", - "phpstan/phpstan-phpunit": "^1.4.0", - "phpstan/phpstan-strict-rules": "^1.6.1", - "squizlabs/php_codesniffer": "^3.10.3", - "symfony/filesystem": "^6.4.3 || ^7.1.5" + "phpstan/phpstan": "^2.1.33", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.11", + "phpstan/phpstan-strict-rules": "^2.0.7", + "symfony/filesystem": "^7.3.2 || ^8.0.0" }, "bin": [ "bin/paratest", @@ -1394,7 +1398,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.4.8" + "source": "https://github.com/paratestphp/paratest/tree/v7.16.1" }, "funding": [ { @@ -1406,7 +1410,7 @@ "type": "paypal" } ], - "time": "2024-10-15T12:45:19+00:00" + "time": "2026-01-08T07:23:06+00:00" }, { "name": "doctrine/deprecations", @@ -1710,16 +1714,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.2", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -1762,44 +1766,45 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-10-21T19:32:17+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "nunomaduro/collision", - "version": "v8.5.0", + "version": "v8.8.3", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "f5c101b929c958e849a633283adff296ed5f38f5" + "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/f5c101b929c958e849a633283adff296ed5f38f5", - "reference": "f5c101b929c958e849a633283adff296ed5f38f5", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/1dc9e88d105699d0fee8bb18890f41b274f6b4c4", + "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4", "shasum": "" }, "require": { - "filp/whoops": "^2.16.0", - "nunomaduro/termwind": "^2.1.0", + "filp/whoops": "^2.18.1", + "nunomaduro/termwind": "^2.3.1", "php": "^8.2.0", - "symfony/console": "^7.1.5" + "symfony/console": "^7.3.0" }, "conflict": { - "laravel/framework": "<11.0.0 || >=12.0.0", - "phpunit/phpunit": "<10.5.1 || >=12.0.0" + "laravel/framework": "<11.44.2 || >=13.0.0", + "phpunit/phpunit": "<11.5.15 || >=13.0.0" }, "require-dev": { - "larastan/larastan": "^2.9.8", - "laravel/framework": "^11.28.0", - "laravel/pint": "^1.18.1", - "laravel/sail": "^1.36.0", - "laravel/sanctum": "^4.0.3", - "laravel/tinker": "^2.10.0", - "orchestra/testbench-core": "^9.5.3", - "pestphp/pest": "^2.36.0 || ^3.4.0", - "sebastian/environment": "^6.1.0 || ^7.2.0" + "brianium/paratest": "^7.8.3", + "larastan/larastan": "^3.4.2", + "laravel/framework": "^11.44.2 || ^12.18", + "laravel/pint": "^1.22.1", + "laravel/sail": "^1.43.1", + "laravel/sanctum": "^4.1.1", + "laravel/tinker": "^2.10.1", + "orchestra/testbench-core": "^9.12.0 || ^10.4", + "pestphp/pest": "^3.8.2 || ^4.0.0", + "sebastian/environment": "^7.2.1 || ^8.0" }, "type": "library", "extra": { @@ -1836,6 +1841,7 @@ "cli", "command-line", "console", + "dev", "error", "handling", "laravel", @@ -1861,35 +1867,35 @@ "type": "patreon" } ], - "time": "2024-10-15T16:06:32+00:00" + "time": "2025-11-20T02:55:25+00:00" }, { "name": "nunomaduro/termwind", - "version": "v2.3.2", + "version": "v2.3.3", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "eb61920a53057a7debd718a5b89c2178032b52c0" + "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/eb61920a53057a7debd718a5b89c2178032b52c0", - "reference": "eb61920a53057a7debd718a5b89c2178032b52c0", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/6fb2a640ff502caace8e05fd7be3b503a7e1c017", + "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017", "shasum": "" }, "require": { "ext-mbstring": "*", "php": "^8.2", - "symfony/console": "^7.3.4" + "symfony/console": "^7.3.6" }, "require-dev": { "illuminate/console": "^11.46.1", "laravel/pint": "^1.25.1", "mockery/mockery": "^1.6.12", - "pestphp/pest": "^2.36.0 || ^3.8.4", + "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.1.3", "phpstan/phpstan": "^1.12.32", "phpstan/phpstan-strict-rules": "^1.6.2", - "symfony/var-dumper": "^7.3.4", + "symfony/var-dumper": "^7.3.5", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -1932,7 +1938,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.3.2" + "source": "https://github.com/nunomaduro/termwind/tree/v2.3.3" }, "funding": [ { @@ -1948,41 +1954,45 @@ "type": "github" } ], - "time": "2025-10-18T11:10:27+00:00" + "time": "2025-11-20T02:34:59+00:00" }, { "name": "pestphp/pest", - "version": "v2.36.0", + "version": "v4.3.2", "source": { "type": "git", "url": "https://github.com/pestphp/pest.git", - "reference": "f8c88bd14dc1772bfaf02169afb601ecdf2724cd" + "reference": "3a4329ddc7a2b67c19fca8342a668b39be3ae398" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/f8c88bd14dc1772bfaf02169afb601ecdf2724cd", - "reference": "f8c88bd14dc1772bfaf02169afb601ecdf2724cd", + "url": "https://api.github.com/repos/pestphp/pest/zipball/3a4329ddc7a2b67c19fca8342a668b39be3ae398", + "reference": "3a4329ddc7a2b67c19fca8342a668b39be3ae398", "shasum": "" }, "require": { - "brianium/paratest": "^7.3.1", - "nunomaduro/collision": "^7.11.0|^8.4.0", - "nunomaduro/termwind": "^1.16.0|^2.1.0", - "pestphp/pest-plugin": "^2.1.1", - "pestphp/pest-plugin-arch": "^2.7.0", - "php": "^8.1.0", - "phpunit/phpunit": "^10.5.36" + "brianium/paratest": "^7.16.1", + "nunomaduro/collision": "^8.8.3", + "nunomaduro/termwind": "^2.3.3", + "pestphp/pest-plugin": "^4.0.0", + "pestphp/pest-plugin-arch": "^4.0.0", + "pestphp/pest-plugin-mutate": "^4.0.1", + "pestphp/pest-plugin-profanity": "^4.2.1", + "php": "^8.3.0", + "phpunit/phpunit": "^12.5.8", + "symfony/process": "^7.4.4|^8.0.0" }, "conflict": { - "filp/whoops": "<2.16.0", - "phpunit/phpunit": ">10.5.36", - "sebastian/exporter": "<5.1.0", + "filp/whoops": "<2.18.3", + "phpunit/phpunit": ">12.5.8", + "sebastian/exporter": "<7.0.0", "webmozart/assert": "<1.11.0" }, "require-dev": { - "pestphp/pest-dev-tools": "^2.17.0", - "pestphp/pest-plugin-type-coverage": "^2.8.7", - "symfony/process": "^6.4.0|^7.1.5" + "pestphp/pest-dev-tools": "^4.0.0", + "pestphp/pest-plugin-browser": "^4.2.1", + "pestphp/pest-plugin-type-coverage": "^4.0.3", + "psy/psysh": "^0.12.18" }, "bin": [ "bin/pest" @@ -1991,6 +2001,8 @@ "extra": { "pest": { "plugins": [ + "Pest\\Mutate\\Plugins\\Mutate", + "Pest\\Plugins\\Configuration", "Pest\\Plugins\\Bail", "Pest\\Plugins\\Cache", "Pest\\Plugins\\Coverage", @@ -2006,6 +2018,7 @@ "Pest\\Plugins\\Snapshot", "Pest\\Plugins\\Verbose", "Pest\\Plugins\\Version", + "Pest\\Plugins\\Shard", "Pest\\Plugins\\Parallel" ] }, @@ -2045,7 +2058,7 @@ ], "support": { "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v2.36.0" + "source": "https://github.com/pestphp/pest/tree/v4.3.2" }, "funding": [ { @@ -2057,34 +2070,34 @@ "type": "github" } ], - "time": "2024-10-15T15:30:56+00:00" + "time": "2026-01-28T01:01:19+00:00" }, { "name": "pestphp/pest-plugin", - "version": "v2.1.1", + "version": "v4.0.0", "source": { "type": "git", "url": "https://github.com/pestphp/pest-plugin.git", - "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b" + "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e05d2859e08c2567ee38ce8b005d044e72648c0b", - "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/9d4b93d7f73d3f9c3189bb22c220fef271cdf568", + "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568", "shasum": "" }, "require": { "composer-plugin-api": "^2.0.0", "composer-runtime-api": "^2.2.2", - "php": "^8.1" + "php": "^8.3" }, "conflict": { - "pestphp/pest": "<2.2.3" + "pestphp/pest": "<4.0.0" }, "require-dev": { - "composer/composer": "^2.5.8", - "pestphp/pest": "^2.16.0", - "pestphp/pest-dev-tools": "^2.16.0" + "composer/composer": "^2.8.10", + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" }, "type": "composer-plugin", "extra": { @@ -2111,7 +2124,7 @@ "unit" ], "support": { - "source": "https://github.com/pestphp/pest-plugin/tree/v2.1.1" + "source": "https://github.com/pestphp/pest-plugin/tree/v4.0.0" }, "funding": [ { @@ -2127,31 +2140,30 @@ "type": "patreon" } ], - "time": "2023-08-22T08:40:06+00:00" + "time": "2025-08-20T12:35:58+00:00" }, { "name": "pestphp/pest-plugin-arch", - "version": "v2.7.0", + "version": "v4.0.0", "source": { "type": "git", "url": "https://github.com/pestphp/pest-plugin-arch.git", - "reference": "d23b2d7498475354522c3818c42ef355dca3fcda" + "reference": "25bb17e37920ccc35cbbcda3b00d596aadf3e58d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/d23b2d7498475354522c3818c42ef355dca3fcda", - "reference": "d23b2d7498475354522c3818c42ef355dca3fcda", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/25bb17e37920ccc35cbbcda3b00d596aadf3e58d", + "reference": "25bb17e37920ccc35cbbcda3b00d596aadf3e58d", "shasum": "" }, "require": { - "nunomaduro/collision": "^7.10.0|^8.1.0", - "pestphp/pest-plugin": "^2.1.1", - "php": "^8.1", - "ta-tikoma/phpunit-architecture-test": "^0.8.4" + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "ta-tikoma/phpunit-architecture-test": "^0.8.5" }, "require-dev": { - "pestphp/pest": "^2.33.0", - "pestphp/pest-dev-tools": "^2.16.0" + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" }, "type": "library", "extra": { @@ -2186,19 +2198,151 @@ "unit" ], "support": { - "source": "https://github.com/pestphp/pest-plugin-arch/tree/v2.7.0" + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v4.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-08-20T13:10:51+00:00" + }, + { + "name": "pestphp/pest-plugin-mutate", + "version": "v4.0.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-mutate.git", + "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-mutate/zipball/d9b32b60b2385e1688a68cc227594738ec26d96c", + "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.6.1", + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "psr/simple-cache": "^3.0.0" + }, + "require-dev": { + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0", + "pestphp/pest-plugin-type-coverage": "^4.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Pest\\Mutate\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + }, + { + "name": "Sandro Gehri", + "email": "sandrogehri@gmail.com" + } + ], + "description": "Mutates your code to find untested cases", + "keywords": [ + "framework", + "mutate", + "mutation", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-mutate/tree/v4.0.1" }, "funding": [ { "url": "https://www.paypal.com/paypalme/enunomaduro", "type": "custom" }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, { "url": "https://github.com/nunomaduro", "type": "github" } ], - "time": "2024-01-26T09:46:42+00:00" + "time": "2025-08-21T20:19:25+00:00" + }, + { + "name": "pestphp/pest-plugin-profanity", + "version": "v4.2.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-profanity.git", + "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-profanity/zipball/343cfa6f3564b7e35df0ebb77b7fa97039f72b27", + "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3" + }, + "require-dev": { + "faissaloux/pest-plugin-inside": "^1.9", + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Profanity\\Plugin" + ] + } + }, + "autoload": { + "psr-4": { + "Pest\\Profanity\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest Profanity Plugin", + "keywords": [ + "framework", + "pest", + "php", + "plugin", + "profanity", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-profanity/tree/v4.2.1" + }, + "time": "2025-12-08T00:13:17+00:00" }, { "name": "phar-io/manifest", @@ -2373,16 +2517,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.6.3", + "version": "6.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9" + "reference": "2f5cbed597cb261d1ea458f3da3a9ad32e670b1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94f8051919d1b0369a6bcc7931d679a511c03fe9", - "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2f5cbed597cb261d1ea458f3da3a9ad32e670b1e", + "reference": "2f5cbed597cb261d1ea458f3da3a9ad32e670b1e", "shasum": "" }, "require": { @@ -2390,9 +2534,9 @@ "ext-filter": "*", "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.7", - "phpstan/phpdoc-parser": "^1.7|^2.0", - "webmozart/assert": "^1.9.1" + "phpdocumentor/type-resolver": "^2.0", + "phpstan/phpdoc-parser": "^2.0", + "webmozart/assert": "^1.9.1 || ^2" }, "require-dev": { "mockery/mockery": "~1.3.5 || ~1.6.0", @@ -2401,7 +2545,8 @@ "phpstan/phpstan-mockery": "^1.1", "phpstan/phpstan-webmozart-assert": "^1.2", "phpunit/phpunit": "^9.5", - "psalm/phar": "^5.26" + "psalm/phar": "^5.26", + "shipmonk/dead-code-detector": "^0.5.1" }, "type": "library", "extra": { @@ -2431,44 +2576,44 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.3" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.1" }, - "time": "2025-08-01T19:43:32+00:00" + "time": "2026-01-20T15:30:42+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.10.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", - "php": "^7.3 || ^8.0", + "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.18|^2.0" + "phpstan/phpdoc-parser": "^2.0" }, "require-dev": { "ext-tokenizer": "*", "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" + "psalm/phar": "^4" }, "type": "library", "extra": { "branch-alias": { - "dev-1.x": "1.x-dev" + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev" } }, "autoload": { @@ -2489,22 +2634,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" }, - "time": "2024-11-09T15:12:26+00:00" + "time": "2026-01-06T21:53:42+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "2.3.0", + "version": "2.3.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", "shasum": "" }, "require": { @@ -2536,9 +2681,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" }, - "time": "2025-08-30T15:50:23+00:00" + "time": "2026-01-25T14:56:51+00:00" }, { "name": "phpstan/phpstan", @@ -2595,35 +2740,34 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.16", + "version": "12.5.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + "reference": "4a9739b51cbcb355f6e95659612f92e282a7077b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4a9739b51cbcb355f6e95659612f92e282a7077b", + "reference": "4a9739b51cbcb355f6e95659612f92e282a7077b", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.19.1 || ^5.1.0", - "php": ">=8.1", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-text-template": "^3.0.1", - "sebastian/code-unit-reverse-lookup": "^3.0.0", - "sebastian/complexity": "^3.2.0", - "sebastian/environment": "^6.1.0", - "sebastian/lines-of-code": "^2.0.2", - "sebastian/version": "^4.0.1", - "theseer/tokenizer": "^1.2.3" + "nikic/php-parser": "^5.7.0", + "php": ">=8.3", + "phpunit/php-file-iterator": "^6.0", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.0.3", + "sebastian/lines-of-code": "^4.0", + "sebastian/version": "^6.0", + "theseer/tokenizer": "^2.0.1" }, "require-dev": { - "phpunit/phpunit": "^10.1" + "phpunit/phpunit": "^12.5.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -2632,7 +2776,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1.x-dev" + "dev-main": "12.5.x-dev" } }, "autoload": { @@ -2661,40 +2805,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" } ], - "time": "2024-08-22T04:31:57+00:00" + "time": "2025-12-24T07:03:04+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "4.1.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + "reference": "961bc913d42fe24a257bfff826a5068079ac7782" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/961bc913d42fe24a257bfff826a5068079ac7782", + "reference": "961bc913d42fe24a257bfff826a5068079ac7782", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2722,7 +2878,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.0" }, "funding": [ { @@ -2730,28 +2886,28 @@ "type": "github" } ], - "time": "2023-08-31T06:24:48+00:00" + "time": "2025-02-07T04:58:37+00:00" }, { "name": "phpunit/php-invoker", - "version": "4.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-pcntl": "*" @@ -2759,7 +2915,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2785,7 +2941,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" }, "funding": [ { @@ -2793,32 +2950,32 @@ "type": "github" } ], - "time": "2023-02-03T06:56:09+00:00" + "time": "2025-02-07T04:58:58+00:00" }, { "name": "phpunit/php-text-template", - "version": "3.0.1", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2845,7 +3002,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" }, "funding": [ { @@ -2853,32 +3010,32 @@ "type": "github" } ], - "time": "2023-08-31T14:07:24+00:00" + "time": "2025-02-07T04:59:16+00:00" }, { "name": "phpunit/php-timer", - "version": "6.0.0", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -2904,7 +3061,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" }, "funding": [ { @@ -2912,20 +3070,20 @@ "type": "github" } ], - "time": "2023-02-03T06:57:52+00:00" + "time": "2025-02-07T04:59:38+00:00" }, { "name": "phpunit/phpunit", - "version": "10.5.36", + "version": "12.5.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870" + "reference": "37ddb96c14bfee10304825edbb7e66d341ec6889" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870", - "reference": "aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/37ddb96c14bfee10304825edbb7e66d341ec6889", + "reference": "37ddb96c14bfee10304825edbb7e66d341ec6889", "shasum": "" }, "require": { @@ -2935,29 +3093,25 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.0", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", - "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.16", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-invoker": "^4.0.0", - "phpunit/php-text-template": "^3.0.1", - "phpunit/php-timer": "^6.0.0", - "sebastian/cli-parser": "^2.0.1", - "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.2", - "sebastian/diff": "^5.1.1", - "sebastian/environment": "^6.1.0", - "sebastian/exporter": "^5.1.2", - "sebastian/global-state": "^6.0.2", - "sebastian/object-enumerator": "^5.0.0", - "sebastian/recursion-context": "^5.0.0", - "sebastian/type": "^4.0.0", - "sebastian/version": "^4.0.1" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.5.2", + "phpunit/php-file-iterator": "^6.0.0", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.2.0", + "sebastian/comparator": "^7.1.4", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.0.3", + "sebastian/exporter": "^7.0.2", + "sebastian/global-state": "^8.0.2", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/type": "^6.0.3", + "sebastian/version": "^6.0.0", + "staabm/side-effects-detector": "^1.0.5" }, "bin": [ "phpunit" @@ -2965,7 +3119,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.5-dev" + "dev-main": "12.5-dev" } }, "autoload": { @@ -2997,7 +3151,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.36" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.8" }, "funding": [ { @@ -3008,94 +3162,96 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2024-10-08T15:36:51+00:00" + "time": "2026-01-27T06:12:29+00:00" }, { - "name": "sebastian/cli-parser", - "version": "2.0.1", + "name": "psr/simple-cache", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", "shasum": "" }, "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-03-02T07:12:49+00:00" + "time": "2021-10-29T13:26:27+00:00" }, { - "name": "sebastian/code-unit", - "version": "2.0.0", + "name": "sebastian/cli-parser", + "version": "4.2.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04", + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "4.2-dev" } }, "autoload": { @@ -3114,103 +3270,64 @@ "role": "lead" } ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - } - ], - "time": "2023-02-03T06:58:43+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ + }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" - }, - "funding": [ + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" } ], - "time": "2023-02-03T06:59:15+00:00" + "time": "2025-09-14T09:36:45+00:00" }, { "name": "sebastian/comparator", - "version": "5.0.4", + "version": "7.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e" + "reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e8e53097718d2b53cfb2aa859b06a41abf58c62e", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a7de5df2e094f9a80b40a522391a7e6022df5f6", + "reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6", "shasum": "" }, "require": { "ext-dom": "*", "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/diff": "^5.0", - "sebastian/exporter": "^5.0" + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^12.2" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "7.1-dev" } }, "autoload": { @@ -3250,7 +3367,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.4" + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.4" }, "funding": [ { @@ -3270,33 +3387,33 @@ "type": "tidelift" } ], - "time": "2025-09-07T05:25:07+00:00" + "time": "2026-01-24T09:28:48+00:00" }, { "name": "sebastian/complexity", - "version": "3.2.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68ff824baeae169ec9f2137158ee529584553799" + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", - "reference": "68ff824baeae169ec9f2137158ee529584553799", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.2-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -3320,7 +3437,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" }, "funding": [ { @@ -3328,33 +3445,33 @@ "type": "github" } ], - "time": "2023-12-21T08:37:17+00:00" + "time": "2025-02-07T04:55:25+00:00" }, { "name": "sebastian/diff", - "version": "5.1.1", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0", - "symfony/process": "^6.4" + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -3387,7 +3504,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" }, "funding": [ { @@ -3395,27 +3512,27 @@ "type": "github" } ], - "time": "2024-03-02T07:15:17+00:00" + "time": "2025-02-07T04:55:46+00:00" }, { "name": "sebastian/environment", - "version": "6.1.0", + "version": "8.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/24a711b5c916efc6d6e62aa65aa2ec98fef77f68", + "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-posix": "*" @@ -3423,7 +3540,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -3451,42 +3568,54 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + "source": "https://github.com/sebastianbergmann/environment/tree/8.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" } ], - "time": "2024-03-23T08:47:14+00:00" + "time": "2025-08-12T14:11:56+00:00" }, { "name": "sebastian/exporter", - "version": "5.1.4", + "version": "7.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "0735b90f4da94969541dac1da743446e276defa6" + "reference": "016951ae10980765e4e7aee491eb288c64e505b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", - "reference": "0735b90f4da94969541dac1da743446e276defa6", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7", + "reference": "016951ae10980765e4e7aee491eb288c64e505b7", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/recursion-context": "^5.0" + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -3529,7 +3658,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2" }, "funding": [ { @@ -3549,35 +3678,35 @@ "type": "tidelift" } ], - "time": "2025-09-24T06:09:11+00:00" + "time": "2025-09-24T06:16:11+00:00" }, { "name": "sebastian/global-state", - "version": "6.0.2", + "version": "8.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + "reference": "ef1377171613d09edd25b7816f05be8313f9115d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d", + "reference": "ef1377171613d09edd25b7816f05be8313f9115d", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -3603,41 +3732,53 @@ "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2024-03-02T07:19:19+00:00" + "time": "2025-08-29T11:29:25+00:00" }, { "name": "sebastian/lines-of-code", - "version": "2.0.2", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -3661,7 +3802,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" }, "funding": [ { @@ -3669,34 +3810,34 @@ "type": "github" } ], - "time": "2023-12-21T08:38:20+00:00" + "time": "2025-02-07T04:57:28+00:00" }, { "name": "sebastian/object-enumerator", - "version": "5.0.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -3718,7 +3859,8 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" }, "funding": [ { @@ -3726,32 +3868,32 @@ "type": "github" } ], - "time": "2023-02-03T07:08:32+00:00" + "time": "2025-02-07T04:57:48+00:00" }, { "name": "sebastian/object-reflector", - "version": "3.0.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -3773,7 +3915,8 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" }, "funding": [ { @@ -3781,32 +3924,32 @@ "type": "github" } ], - "time": "2023-02-03T07:06:18+00:00" + "time": "2025-02-07T04:58:17+00:00" }, { "name": "sebastian/recursion-context", - "version": "5.0.1", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -3837,7 +3980,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1" }, "funding": [ { @@ -3857,32 +4000,32 @@ "type": "tidelift" } ], - "time": "2025-08-10T07:50:56+00:00" + "time": "2025-08-13T04:44:59+00:00" }, { "name": "sebastian/type", - "version": "4.0.0", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d", + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -3905,37 +4048,50 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" } ], - "time": "2023-02-03T07:10:45+00:00" + "time": "2025-08-09T06:57:12+00:00" }, { "name": "sebastian/version", - "version": "4.0.1", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -3958,7 +4114,8 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" }, "funding": [ { @@ -3966,27 +4123,79 @@ "type": "github" } ], - "time": "2023-02-07T11:34:05+00:00" + "time": "2025-02-07T05:00:38+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" }, { "name": "symfony/finder", - "version": "v7.3.5", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "9f696d2f1e340484b4683f7853b273abff94421f" + "reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/9f696d2f1e340484b4683f7853b273abff94421f", - "reference": "9f696d2f1e340484b4683f7853b273abff94421f", + "url": "https://api.github.com/repos/symfony/finder/zipball/8bd576e97c67d45941365bf824e18dc8538e6eb0", + "reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0" + "symfony/filesystem": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4014,7 +4223,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.5" + "source": "https://github.com/symfony/finder/tree/v8.0.5" }, "funding": [ { @@ -4034,24 +4243,24 @@ "type": "tidelift" } ], - "time": "2025-10-15T18:45:57+00:00" + "time": "2026-01-26T15:08:38+00:00" }, { "name": "symfony/process", - "version": "v7.3.4", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" + "reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", + "url": "https://api.github.com/repos/symfony/process/zipball/b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674", + "reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "type": "library", "autoload": { @@ -4079,7 +4288,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.4" + "source": "https://github.com/symfony/process/tree/v8.0.5" }, "funding": [ { @@ -4099,28 +4308,28 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2026-01-26T15:08:38+00:00" }, { "name": "ta-tikoma/phpunit-architecture-test", - "version": "0.8.5", + "version": "0.8.6", "source": { "type": "git", "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", - "reference": "cf6fb197b676ba716837c886baca842e4db29005" + "reference": "ad48430b92901fd7d003fdaf2d7b139f96c0906e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/cf6fb197b676ba716837c886baca842e4db29005", - "reference": "cf6fb197b676ba716837c886baca842e4db29005", + "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/ad48430b92901fd7d003fdaf2d7b139f96c0906e", + "reference": "ad48430b92901fd7d003fdaf2d7b139f96c0906e", "shasum": "" }, "require": { "nikic/php-parser": "^4.18.0 || ^5.0.0", "php": "^8.1.0", - "phpdocumentor/reflection-docblock": "^5.3.0", - "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0", - "symfony/finder": "^6.4.0 || ^7.0.0" + "phpdocumentor/reflection-docblock": "^5.3.0 || ^6.0.0", + "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0", + "symfony/finder": "^6.4.0 || ^7.0.0 || ^8.0.0" }, "require-dev": { "laravel/pint": "^1.13.7", @@ -4156,29 +4365,29 @@ ], "support": { "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues", - "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.5" + "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.6" }, - "time": "2025-04-20T20:23:40+00:00" + "time": "2026-01-30T07:16:00+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.1" }, "type": "library", "autoload": { @@ -4200,7 +4409,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" }, "funding": [ { @@ -4208,27 +4417,27 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-12-08T11:19:18+00:00" }, { "name": "webmozart/assert", - "version": "1.12.1", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", + "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", "shasum": "" }, "require": { "ext-ctype": "*", "ext-date": "*", "ext-filter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.2" }, "suggest": { "ext-intl": "", @@ -4238,7 +4447,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10-dev" + "dev-feature/2-0": "2.0-dev" } }, "autoload": { @@ -4254,6 +4463,10 @@ { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" } ], "description": "Assertions to validate method input/output with nice error messages.", @@ -4264,9 +4477,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.12.1" + "source": "https://github.com/webmozarts/assert/tree/2.1.2" }, - "time": "2025-10-29T15:56:20+00:00" + "time": "2026-01-13T14:02:24+00:00" } ], "aliases": [], @@ -4279,5 +4492,5 @@ "ext-pcntl": "*" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } From 9c8c82af988b6e5837f4834b98e756a96d9e8856 Mon Sep 17 00:00:00 2001 From: Andrew Masiye Date: Wed, 4 Feb 2026 04:13:30 +0200 Subject: [PATCH 7/7] feat(functions): add dispatchEvent function for event handling --- src/Util/Functions.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Util/Functions.php b/src/Util/Functions.php index 820ba81..68debe3 100644 --- a/src/Util/Functions.php +++ b/src/Util/Functions.php @@ -25,6 +25,19 @@ function getGameName(): string return SceneManager::getInstance()->getSettings('game_name') ?? $_ENV['GAME_NAME']; } +if (! function_exists('dispatchEvent') ) { + /** + * Dispatches the given event. + * + * @param EventInterface $event The event to dispatch. + * @return bool True if the event was dispatched successfully, false otherwise. + */ + function dispatchEvent(EventInterface $event): bool + { + return EventManager::getInstance()->dispatchEvent($event); + } +} + /** * Quits the game with the given exit code. * @@ -463,4 +476,4 @@ function get_player_pref(string $key, mixed $default = null): mixed { function set_player_pref(string $key, mixed $value): void { ConfigStore::get(PlayerPreferences::class)->set($key, $value); } -} \ No newline at end of file +}