From a2a825cd5a36de27420c17d3bb56d746629f14d1 Mon Sep 17 00:00:00 2001 From: Julien Date: Tue, 15 Oct 2024 16:17:26 +0200 Subject: [PATCH] feat: add documentation --- README.md | 203 +---------------- docs/assets/images/banner.png | Bin 0 -> 103743 bytes docs/content/config.json | 21 +- docs/content/docs/client.md | 366 ++++++++++++++++++++++++++++++ docs/content/docs/db.json | 26 ++- docs/content/docs/inertia.md | 83 +++++++ docs/content/docs/installation.md | 136 +++++++++++ docs/content/docs/introduction.md | 343 +++++++++------------------- docs/content/docs/openapi.md | 109 +++++++++ docs/src/bootstrap.ts | 7 - 10 files changed, 839 insertions(+), 455 deletions(-) create mode 100644 docs/assets/images/banner.png create mode 100644 docs/content/docs/client.md create mode 100644 docs/content/docs/inertia.md create mode 100644 docs/content/docs/installation.md create mode 100644 docs/content/docs/openapi.md diff --git a/README.md b/README.md index 750a079..4dd12e5 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,12 @@ Set of tools to create typesafe APIs using AdonisJS. The monorepo includes the f The main goal of this project is to provide some utilities to have better typesafety when creating APIs with AdonisJS. Goals on the long term are : -- **Done (Experimental)** : Provide an RPC-like client that is fully e2e typesafe ( like tRPC, Elysia Eden, Hono etc. ) -- **Done (Experimental)** : Provide a [Ziggy](https://github.com/tighten/ziggy)-like helper to generate and use routes in the frontend. +- **Done** : Provide an RPC-like client that is fully e2e typesafe ( like tRPC, Elysia Eden, Hono etc. ) +- **Done** : Provide a [Ziggy](https://github.com/tighten/ziggy)-like helper to generate and use routes in the frontend. - **Done (Experimental)** : Having an automatic OpenAPI generation + Swagger/Scalar UI viewer based on Tuyau codegen. - **In Progress** : Provide some Inertia helpers to have better typesafety when using Inertia in your AdonisJS project. Things like typesafe `` and `useForm`. - **Not started** : Provide a specific Controller class that will allow to have better typesafety when creating your endpoints. +- **Not started**: Having a Tanstack-Query integration for the client package. Like [tRPC](https://trpc.io/docs/client/react) or [ts-rest](https://ts-rest.com/docs/vue-query) does. ## Installation @@ -455,201 +456,3 @@ export default defineConfig({ Only one of `only` or `except` can be used at the same time. Both accept either an array of strings, an array of regular expressions or a function that will receive the route name and must return a boolean. `definitions` will filter the generated types in the `ApiDefinition` interface. `routes` will the filter the route names that are generated in the `routes` object. - -## Inertia package - -Tuyau also provides a set of helpers for Inertia projects. The package is called `@tuyau/inertia`. First, make sure to have generated the API definition in your AdonisJS project using `@tuyau/core` and also have configured the client in your frontend project using `@tuyau/client`. - -Then, you can install the package in your frontend project : - -```bash -pnpm add @tuyau/inertia -``` -### React usage - -To use the Inertia helpers in your React x Inertia project, you must wrap your app with the `TuyauProvider` component : - -```tsx -// inertia/app/app.tsx -import { TuyauProvider } from '@tuyau/inertia/react' -import { tuyau } from './tuyau' - -createInertiaApp({ - // ... - - setup({ el, App, props }) { - hydrateRoot( - el, - <> - - - - - ) - }, -}) -``` - -As you can see, you must pass an instance of the Tuyau client to the `TuyauProvider` component. Also, if you are using SSR, make sure to also wrap your app with the `TuyauProvider` component in your `inertia/app/ssr.tsx` file. - -#### Link - -The `Link` component is a wrapper around the Inertia `Link` component with some additional typesafety. Tuyau `Link` component will accept the same props as the Inertia `Link` component except for the `href` and `method` props. They are replaced by the `route` and `params` props. - -```tsx -import { Link } from '@tuyau/inertia/react' - -Go to post -``` - -### Vue usage - -To use the Inertia helpers in your Vue x Inertia project, you must install the Tuyau plugin : - -```ts -// inertia/app/app.ts - -import { TuyauPlugin } from '@tuyau/inertia/vue' -import { tuyau } from './tuyau' - -createInertiaApp({ - // ... - - setup({ el, App, props, plugin }) { - createSSRApp({ render: () => h(App, props) }) - .use(plugin) - .use(TuyauPlugin, { client: tuyau }) - .mount(el) - }, -}) -``` - -As you can see, you must pass an instance of the Tuyau client to the `TuyauPlugin` plugin. Also, if you are using SSR, make sure to also install the `TuyauPlugin` plugin in your `inertia/app/ssr.ts` file. - -#### Link - -The `Link` component is a wrapper around the Inertia `Link` component with some additional typesafety. Tuyau `Link` component will accept the same props as the Inertia `Link` component except for the `href` and `method` props. They are replaced by the `route` and `params` props. - -```vue - - - -``` - -## OpenAPI package - -> [!WARNING] -> OpenAPI package is still experimental and may not work as expected. Please feel free to open an issue if you encounter any problem. - -The `@tuyau/openapi` package allows you to generate an OpenAPI specification for your API from the Tuyau codegen. The specification is generated from the types of routes and validation schemas, using the Typescript compiler and `ts-morph`. Therefore, it has some limitations compared to manually written specifications (whether via decorators, JS doc, YAML files, or Zod schemas describing the inputs/outputs of the routes). - -During development, the specification will be generated on the fly with each request, which can take a bit of time for large APIs with many routes. In production, the specification will be generated at build time into a `.yml` file and the same specification will be served for each request. - -### Installation - -To install the package, you will obviously need to have the `@tuyau/core` package already installed in your AdonisJS project. Then, you can install the `@tuyau/openapi` package: - -```bash -node ace add @tuyau/openapi -``` - -### Usage - -Once the package is configured, you can directly access your API's OpenAPI specification at the `/docs` address. The `/openapi` route is also available to directly access the OpenAPI specification. - -### Customizing the Specification - -To customize the specification, the package exposes `openapi` macros on the routes. These macros allow you to add additional information to the OpenAPI specification. For example, you can add tags, descriptions, responses, parameters, etc. - -```ts -router.group(() => { - router - .get("/random", [MiscController, "index"]) - .openapi({ summary: "Get a random thing" }); - - router - .get("/random/:id", [MiscController, "show"]) - .openapi({ summary: "Get a random thing by id" }); -}) - .prefix("/misc") - .openapi({ tags: ["misc"] }); -``` - -Feel free to use your editor's autocomplete to see all available options. - -Also, from the `config/tuyau.ts` file, you have the ability to customize the OpenAPI specification with the `openapi.documentation` property: - -```ts -const tuyauConfig = defineConfig({ - openapi: { - documentation: { - info: { title: 'My API!', version: '1.0.0', description: 'My super API' }, - tags: [ - { name: 'subscriptions', description: 'Operations about subscriptions' }, - ], - }, - }, -}); -``` - -### Configuration - -The package configuration is done via the `config/tuyau.ts` file, under the `openapi` key. The available options are as follows: - -#### `provider` - -The OpenAPI viewer to use. Two providers are available: `swagger-ui` and `scalar`. By default, the `scalar` provider is used. - -#### `exclude` - -Paths to exclude from the OpenAPI specification. By default, no paths are excluded. You can pass an array of strings or regex. - -Example: - -```ts -const tuyauConfig = defineConfig({ - openapi: { - exclude: ['/health', /admin/] - } -}); -``` - -#### `endpoints` - -The endpoints where the OpenAPI specification and documentation will be available. By default, the OpenAPI specification will be available at `/openapi` and the documentation at `/docs`. - -Example: - -```ts -const tuyauConfig = defineConfig({ - openapi: { - endpoints: { - spec: '/my-super-spec', - ui: '/my-super-doc' - } - } -}); -``` - -#### `scalar` - -The options to pass to the `scalar` provider when used. More details [here](https://github.com/scalar/scalar?tab=readme-ov-file#configuration). - -#### `swagger-ui` - -The options to pass to the `swagger-ui` provider when used. More details [here](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/). - -## Sponsors - -![](https://github.com/julien-r44/static/blob/main/sponsorkit/sponsors.png?raw=true) - -## Credits - -- Tuyau was inspired a lot by [Elysia](https://elysiajs.com/eden/treaty/overview), [tRPC](https://trpc.io/) and [Hono](https://hono.dev/) for the RPC client. -- Route helpers were inspired by [Ziggy](https://github.com/tighten/ziggy) - -Thanks to the authors of these projects for the inspiration ! diff --git a/docs/assets/images/banner.png b/docs/assets/images/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..3e7af42dab328a36c49beef0e021f99d1dcee6ed GIT binary patch literal 103743 zcmWJscRZAT9KY>wIQz&ro2)w#$vAuOy>do~hRDukZ8x@jS2R^}N2H=ly=(ubGu-DJe&u52Ms*4*w`Ypx!2bGuw<(j9YSVTn_ z-VN0A^V9Zl*Kl@JwYF3?F;djKxI|q}<(jOboHSNQP{`mXkoDq*;2=v=t8<#NSFG5F z0N|r12HG0dq0`H`pR-Fai1;hcYu!1d%(;V@@rhyi1`l_wlq%KIR_TwPqL0Vy7Wcsn zJxluuM?IubnXf{Mzm#7E1ckUc&b+S+c|d)gquJH>Guf-`zI}CB; zTNyf0gjK9yx?{z+-hv(VmQ2-3YvfAS2~#vUc2Vu`!No zQXGTr4YPjbIaf@*T%lxp4AUh#)6XPMr{;eTt9qBTMntA3G{2OHB|TcMsy&>XgFO8H z!R6%Ec1<37E@!#KQTT&%CNjr9E1>ID!w&PG2oP*jk8c`*Smm)#)xFPFcLNYr>9>6# zVIt&I#j*F9BWoWBn57=5UMhrQ;>(t?&(boI>~JRQ{q*z^j^8amv-sERGDv^4A*}qoB*E`SL#igweBT5dxvT_)6);Z{La| z3WZ1Lk8$lMmXfy`UQCHYPirLo554L-rH`18%Pj7sic@3Op0D3oR;o?p+!ecH)adg2 zTEuy7_-#2RPeuvl$EM|DHb$1hKTDg)?pupx6&aO?R0`cMA+cBDwR8YOo?`Ut#F|1} z7wM*4r`F0boO1HpD2D$$5DV6JfIYH@0J=K8ne_lAhEp89g1ydiu;dLdU2(oMbA~)+ zb_Yle$LW_7GeqA#-f{BtLoQVB+_b5p-kw?UQcSB5XTe8#g#;W1x$<&U(@mHj*ok<} zcPR1R5 z&L^TW4R>+@6f5EjL|Rz!Vy1`TA}|BEkEPAreDG(o$aPo+xYo2@c6(b8|1y>r7@X+@ zJcEm|-xsHRYhs2%XeTaV*B|)T5d&WNim1F{h4FzOeh>b|DMlNfC+a9-c_vEfACj15 zLd1hsPMJVGJ`-3Y+O=ziU1}pEk|y|(jL>x4uEzi3<1vs!KrTsff)c?NpKTjl#fVlfBX$+z)Q178C(Pop=ev_l6CK)^5lKd_G4;1wAuZK( z_RgZPUDSgk2bliK5xI_lLn5Po2~rq#5nDF)j%m@)s)+_fa#2kZE3Cz^BbHRgwSOQ3 zOHSE!!QXF%kKHD_N;T8e7{)F-Y62y%Zk)%GUX%1d8zP8$ZWwh?I`cu;ugEi*t;xHx z?lDSSmjRS3=mKvd3lEGX8PjdVnA{^Aq&fsqyCLMJ$WWoo^;DlfG+3ctlIX%NrRE5h zrzU}E?B;2{Z`FdXE;DJOiVFa)W9dZYEZu}V->*QBJUl_s>j?B(iW8p5bwrBnMV&u| zh~rz}$>T1sg8N2cj7s?5Km3FKE{g%1j%g|Js1wQE0N;}pDNJIQqQ}`}Otc!)IeiFl z5pgU8h$=N;%{zI1!bG`}=aV_i(S-`P*Tc;UtR=t>3yH!$id+z_v)*f}9~ljw>2-!O zAjezLG5XJL{5jL`=hs2jkKr^0>2>dYd-%{)tBz!s5$V;v>g(J=2VWPuxsh{@nI=mx zq|p{mH843^JwCDkAkUJD zTTzHdd*S?reQM9R5#tSya-F&X>G$A68L`M$hRj2x7B>l=JzO5zP0aII`7Mv;OM@g$ zurzJj%`UMSK%ey2Gk+esk6(xwX9nW~a2zPSQZ-eXEFN!0=Axw*NE4;0|M&*P)<_K_ z2d5M!;F*4}_3&!{OK&HDut7EE-=qKGbf43RZ28b5E`MM_d#=W@W2nnF&t!~ZUd(?Ry^H^B z^U3NBk#ybf29pP9OGE@i+a-r%F*i36_6MKuU3Z(cPvSjx36)%K;x0pep$v zW$E#$(b`&oR?c_q(`epP-5Gqg7NH8V@&IyY%;@_-Lr>o`z^*?rH0x$~5o{ye{`n1` zKVy`i=k-$hlTu+@mCP#X!ywkE_0-A{SclS?WP{`be_{xz@JYMb%DmHY zcx7I|gH)@q@;|v>^mNaAnKzZrGzJd3ki?DymjojCq-|QT<#FyLRWqIlUhMnP17H%e zfaHJroID3!QUoq$)icyw2R`d$&^25zxP@=UspFZNrVsg>Os-M>$)UZT%u8gU%yCWU zP)->@Hkc@_+r{z<@D%{~vLWh8`)|zsU-p0c)PrbmS7X=J%M4Z7@(6qjhz)-Hf&dDy$eS$VIqxt_0LKzX zqwMqUijUBM<3TrWK$5jiZ&6x6iNikb>@50B%QrwiMKZ;)j6)keWHU8fnzbfxHZC|| z;rrDtVz^k7q5C(0h%njP>lQREaFz2wLq8idC5IEoU!kEaJ|s`R%5)+|FQ(nT8Gb99 ze6AlSimBk#DsX|;j2UHpoc_1`?P@H!Z}`*z|1-iw?(qbHTtkaEDf$IbbcEJ)@Txe2 zUSBV2XVbNd;oXajf!@ygauZA7LY(lvsmt{ddVLF`K64cXOUy3JfvpH5KX}7dGGb8~ z>2!EJ9cyHf-x@}_ubJvYebD-zQ0PZ9i(96?@-eHUXR^J6=b z;cno0i0j@s#u7m?9(zvk+?R6;oI3IXq1Tc{Imc{>-jENJ$ zxu-?<#sdwJ*9bnO1BL?}KbTcOnJQF0X1%{E1Mb~#*z3c|4kn-91+>(}Mk!~q5Xkrb zvoCEqFekTA`+g@EKqY!z8d_)~6#=FVE&MBz5!ZBgmg?B^_#M(nW?`)OZ-9kMTL*W} z^mNnSTz40~Llea0od6P4b$P?INL%QL09o$1kEWtyqk2E6<<&3< zuKb;6nuRI#f1ZP!Z}hYr&euedI&NRzV{JeA&}-idu6u3q) zQ{|}_;v>;|h4JTDkO@GH2l4Ku7K-ZWAQ$D7dSK)iITmmMRoZ7GJ-w;|V0n{F{Z=qF z+aL3QekDL~fno2b6vi3ACS)a0!em()-LJUldK);)eqB=Y!##4Pf8v?jVmJc=ICOL3 zE@k?Ca2Z5YFqozC9xdiNo#nsrqy${)gDO0IBrVkVL7cTQh+Cu3tCqnzwjT#$--_g% zUVin%?__ZF0n<6l&H{>EVMAgPFvdrCyTMYlnYvld?T(gVz1K;NBUE%UAnRxrImT!{ zP69sOrV%cViSp7Jv@y8Ajj&6lvee2MjxHPMb?c$^GpBM+x`jSgT=fw^KkEb$15v{3 zsb>9+BZx{Rf?`9i)^WfkqVHqKr9hs2VyaRzF#6GHpL*xH>=)7VkWy|~1!I&9%!l*z z{ca^x<8vT7ekV9143xYu`@JRkC=3BPz0Hd9PNhuvgAf-_t3}q{!}P9nf5S&AfAN*N z6IY{gZMK|KxhwH#!L*SOEMM`Gwx>Da8gQtdk8yP#wEY%z&m%FEZnaJm#Rnn*{*#Qp z#~d5O_*@&U_n+)x*u30JY6w5; zJ=E$a%JwTNl;Ihg2l9^pRHZxWlknSm|j(j*tY zk%^n?p)5=)WwBObew)t$>_=>616HCaGER>Za3KP(D?p`)D+f>}$ZPBpdY$(!ZhGczBShj>0&A zVu$0SvEU*8gjNpxuuC#`4q2HnnSj~i3=gD}&4?+}q`O$Zcc!aR~mwoXaF5l4{ZH(-9XpTxOa zXnHR6q8(83g%1+BHOPg2odKcQXwLwKZzy_0FiZc1(NhmTo}=)4JS!q+w}ji8fPzBb zY1X;P-1QZJ<}1aQMN^byT5?2>i<0cRkRm#i=#Sj!JFk)Ub2s5Q{5_Fxe>4b>&2hM= zWXP!|23axrK8+UtE1E^#(L(su=wp!Y%*JNW@VXKCemyY1xAr;$L;ooDcxPrgO zRo|sl;JI}`mNgdi6{kbPt)1yT?Sa2O{Dq|~Cmw{Si6NTcSkvYnNS*KO;bR^HoOvvY zr{35PchU?2y%AWG?M(uF8v534J0yL$?)gy~o<>#D@4Mgc4x8F4XK>!y=0a*_@vw0h z^LLxULKBB#^nW|iE=dB<`vEsOI+9AW;)CpuvgJ(t@Wo!-jf3?>oH1dzUd?@qCG6Gh z#QR+MGjw)>dOzUR?ea^`<&C{!q_?-PQ=S~UCH--D`*Q#zwt;f?kTAc6#@l@dhx(po zU(f1XxTo>(`FJYmWX@{u#gn zhRRg`Z`)MuOzz07H2da6T@0;oI4s`U>%qyb7Fy7QNr2AcGrYcqwX-HbQ6KN{r2Cwx z{*`(;Z_7IWc0gr0NR(@l(p=A0QB}@Pco_|G1u0rV4;BXa8UHG+iWmBWSQ(Cmgk3T% zJ?QM$t#IFub#KbR&KK#FHjuwo6=OKps!Jb$)%-b8u)G7J2&G|AE%`cKb|D&pk&ZdF zf#2ms!Fl`>pFC}6pzmRj)d`kHHtpyBM}B1*d|BFqc*7Rk#8iF|Jrfkprpmd|?dSua z3>4BXr`H8*>k6CLdrqBg4J1uDpT*xgW6_n)Oap>QA`!@TZsXL^x+VTF!}A?pg{Z;# z5SEDRs1(&v1Ee5O=)<$ZyVs6AoD1T?djQR^%WTPtTBihr8TdNQ>!$$zypT}D1YWiW zbOwL6oT4fn&MCu+8KD%2K5TP)C-$LDDn3xr6AF5UR3>NK7PszdhCRp{DJ)Wg(5+s~ z&?5+SNxr@n?%0bZ-?WcJAKNvO3mJywcD zp|s{CGT}$4g<#^J@$oEl9rX(^{rzR74_(WU&b2hq`qze!w;dBMQ! zqx?4Z{^BR=@=8$}4Jo?mG8~F>i?xtj<`)r3Zh^)!Xy0p$H_61)1PuUEXJFzftTsub zkQb^{0X)aUYdD3m@zbV}y4YyG!}=X#cFfs(2EB=irJ#=Zr0II5 zd<lJ$SU$>wT<1gLJX;Syt>z@YWxVEuMOAe4dn*e9XUwEZ!TFeknJy*QZ$fn-m3(E-v z^J~tJY69T459*l@#tm9_`>;5`Pn@}1cBd+QBv?Kdbz{1qKtvjNcR{IlAhTK!uleEv&+yhHKLoL&~gmUs)m z+M3zpLwg*KTqo86UokQUxgSI4$JpW(OM@o1Up zbG@gXsqxt2tI1mJuVGi$Gg5O$x%hw%`!hek8Adp$fO+na9vD(;05@`N6ZU{%W8+oC zAO3y;dwyC!#^4-}+N6!Db&fx_E9D^Iv$nl1k?0rKg6l!(Sy@-GHa9%<6g@yuavpn| zel#uxEuJ9b`@2~Ibi>E>L(FGb5q8>3JdxTR*Y+1ayL`7T*f1F?J<;I>_V$a^I{}8w zVv$yR$M+RmT?fdj*hE*r>)(EoR(^GfK+)VHMoB!{95!Y5c&{N_j2vtR8%X}<5~;m?15Zk zK-fMMXVasRljD)Hr$-13Un_BoSnQo{D`R+*fFSXZoFy+Ug{qaK>Tvvw{`w1i~R!rmGt)H*ruD39TivXg1#pfNR zStSz$MF3q;@F}4N{o*n{QYxL?Kq+L%2NaYLpDt+b2_;WTMb*B|q1f$-?HCmc_~lDi z?rzzME@v!8C$FEmS4^|AlH2HGIdeJ5)VuU3INLDbME?RnX|WPSt1Tg(JJSBXGZB2+ zmPrm!5PMpA@tewetVa7n3Bhj^+V=BBHL~U_)#r^lt6xDV*J}-Z0{v&?ljI(zhWbjY zvrr3&ADT&`@Kw}J)Rs^MK$By(Dn+<&p`;|3oO`I_3fx^HuD|>KHuvd#npx;4Z|trj z={^@M9HgHuh#%Iz?JMGZEX1@CFPhhw3lX1vU_0O*5G&gBpGWdLx=<&_(JH%d$2oET z1YX{OJ$Q(&u4T#j!0j52(6$y#u|h9P;~9wPy$ss1GamhGufU-2A$K>hJRG$SYWsn3 zDZd5VPo9uOtv1v>`a*jbNd4Dswz6d9*F63+FbGLCTaMPZVMwLy7`x!T!LZdkq>mT4 zz6w7FT$lS=ZABPbq?@`zrebC#gUlTTtXhLlT0>cc-!sQJIl69N_n9A_?8+fC*p#ry z#M&MTD1Gpq$lFN$W^(1SmkHtiytbq0i(Y83@xh=-g%GCON&mU z19@`US*(+3dK-5=5V~2=b-F{(j49l_#;-#c(v_KfXHz}cKXYMlm0S>>O`0+S zG^Jy(u%{7QBCXP8CO69fImw+>Bqk>(3Hj^XZA{pqO;p7@S~qFK=|9{YWU4|>r;9wZ zc~+P1lRqf5Y4CNF_-D!wpn(sXFzHHvPZ-li1M`+Zk0k9LLi!VDw#<=?gtQxddt=4`<tk9(j%Tr ze@)MQHj>6!R2ig9PXeU@-0&dkWL%4OZAf?TpG)1?lXpX}Nl}-C{Xo_G+98mfb%l9w zdeW)%t8vZP%IIZsj`rNu#`?&U*?_XJufD%wa)Q2-z5I zfP6u_*iCcg zP>$c0#a2r0@S=+b|t@+;fjUKZ*@%MBv?jy3dz%N5C7{b}kPxX9n z;)k^c{PJV*D(3db26muZiJ?@Xcdr_go+xxiBV=SqNr3%ytFiInG$7$ zFb^VRSM=+`sQOvbxp;~o z3j=vFCpy8!DMZ(q>=Hu9X|+sM^eZCiD`z6G_!jGwkt8c(YoxM~&&RrA-hCe;Se>NC z68TqA8yw~1*Al93e3RW=%$KdB(6wj=cmjPjB^S*)Cr569Y!t$3xLF341pvrOCH&Sq z6dD>3Nf(bGH>Js5X9CS;&3JSbDevb_kcIQw8AYQnh&BWMSa{Z!UYHPs@bpjcLtH(U z3O^WF*^K`{igr#ii~fMqgSZIgq02RWu_N%9Z)*v^he2APn=D%B+RAwoW{e>5k1hx* ze_Y^gbXv6>aa(JxB#W=+ zw!<{ZjVwxf>JV~M|H2n;IkH+*-@vOM@sQ?q1&$&wQ1o9?#$nV*$EjcDu<#9grW!+t z9hIN4FR%A2ophF$qoCMN(^ajMGo>tD5aVT&OYO9pEGZ{etA!fsqmHI|Y(@N*x*6_^ zc;asIg^H|d+<^&$1LQ~aBEOap1`26o7c=+R@@0v2XmhYZ^Ld8#`Wg?|Q_hQ+W@ppz zER+0`qu7-l;$Zs#n3|o8WEI}qB&8L=Zv1oRMf)h>d(ZQ%%W4m*o|?lWWN_IG85 z3r0eyy*MD_HS{sQ*9l4`-1uhuk`CTHYU=Y}{7^Fa7CXGxeOskzI(qxf(qW(e21Fq3J-LwoM?kz6vb$D) z!Shl&k}VgD)4mJi(3cpxt8@?W-^U;W3#PGE$F9%Ku|k4ygeiPjn9F)+rjQv>KS1Wx z{O!ic@cVi$-{~jkX#F6_77#;#kj_QdkALcrGsZMxAolJRG6*bkK&Uj8sFVY+I=tns ze>R}s)|Qfh5`W+|KnnSLYr$+Z!)t(|OlAX5^j`7N6(Jzg?7pkM8EVHSE(8dae+wFQdJMe)h=I#l1GA$$Y?&U%^U4TNxOx(2Nw8hpP z%cDQ<=Rd13A$HvENTwyJ#*U@mrQg(?z+FSIk3Tb$2A8t&!S!(BNj6noc?o_+Q08l# z*&FfSY623s^+*pNs9RiQUN2@owW?v>v3KY{QRfb`DMZ^c%004-W!G8K91EP zpL?l)g2I$8$4%r7P@DwIq4`&Ev2m*9(Ol-LRlvT*!L6uEy}q4D4b#MN#l6M0O}m{n$%a?c*0ekwrXJ5d3%)xC^mh3RX@t`Bj< zgeR36$}1K>9V7Pv-Ci?r1NQWO;55BBR{0nJeoP82Oe=pKW&%i4+O_*#RLO?MIJA4$ zGkwnrf52bF?jjfZ$9!U8wslvMkQS5B+6{rs=c7pS@>OfGJDi0KHNX`V94gGburoHN z0W%Rv<5x@TsECc#4@3*QI^lrZQyXhuC`tyJaa__$^VqO5hy?5q#Vbzw-n0d%Brafl z-fE`V9;vi_-cTLtmmqHQo|*4e6Mi>w0@Z0llPF2=z3P>-mJ9UbMnD%vDu!|nF`7vJtW)OTvlaHvy{it+dPEm)OE5u z7N*&+DGG<|+t#ng!dDnfE28lIQ@J|FQ}{xIWcJ|g&M#3)>*6+!<6Hn*rG*dc{S|?51sUsuCGlAYo?FgCj_%iRXJXkGzb#f zONnFP=8<}J5AB@n4RJOWD;?|9A-)X4op2YuzZ%lrqKJL-$se}Cttprg{6inGl+ZCikuSUu(rIQ6I+*_)N~x1=3F(PAZA_FUb1i6!&Y z0E8R#sYQQe9ApH*ojhoudRq+p^8qqzS_^DoLT&ZJ>40p!d~nNdhF-vNYF8CG3?Qiw zf?obCK$xe)sfUc16=rimmH@c(eP7!K7v?aTmftCty52oM;?9qPjFvhM7?@e6(5}s9c3=)NgXgmgA=l%mv%&^|1tSl1I^4l<(vc7cp%7fj(_~Ffkru}+t;XnNS*G6n_FN^P2e>IlLEAzZF@tHIr2>T~LWhYWJ$fKq7$ShoTRt&c zm*))t3kyX2DjP|JW@~T#lEfFgf~H zr36EF;c78a>X0M5(cG_$1LCP=F`h*Hf0sDIX=t2U3=BJgaDQ=ZKx@v8uZn|Idp7zirl0h?WT#AMwys;3P!+6ZYz)U_fvul^ zi5$o90S4K}_3{adK=pjBE>G6i>TtcqGFGxn|8OiZszHip%+KN!V{TLbD072hi18&)`fUy3PHqm#!nK0?tD7IpD9DE`+Unq$Lrh z;hj9>ERBlHC;x_;Ea44iAqU>It>FiQP2}DS1NraPlN)m6deFwzDd@v|AQq#P{p`Wa z^lJ&{oX`0Xf%V1(7yb(^ReSbt-knS6hbVCFJ;4}?YkWN9Gh_?#dnpGmOL&_z@phRc0l#NcWb7a>qMpXDzmxZTra0 zw{t*VobJpP&fRB0s144!0lq|utNjmjM_F=Dk96#azD%Tn@{DyFLVlza z5g!2GUt*@3k4sViNm5SJsbNqRRbikuPL<0woI^>*tV=jHtAYenc|RLMy4&&NL+uPX zI|4QpSkBU_^r4@VD~v|lMH8@VRHV^)r^m>`e_fKVFTIxs_#@}7Ws8i{j-b~UfFe&$ zh-R@Uyf&_ovD8H6+^r6pwr!SfhQrIoU&4h%b3ht>_@G7JAZ}BC^fhSw;+vimTXHvz z-U&`^SLrRv%WgGyNVXHsc4}2-ZW9nwWV_hG^g{zMZrSs*9;C;PpMUnvx+^5Zh z_nTYdI5fG-Tv?Vk^zj z*@tm#pE-*a;1|!xf$C6A5`mv0>89vR;6%cTcU~y}d$;>3Pg4c_Y?+R^?fGi@|*JDLRq@fnzVhY~w zC|h*1__}c0Drjk3MiC_V;3Po@^F&93C5BzIjg=d;DjR2ksBxrM>>~gDTaTb0y6mcA z(8!v3e`eqK7x)Tlb^Hphxp&V?HXaFQd+cz|OXvE}iYlicEIvTOTQdhF0wT4Rj?VRME39s6XaNMWsIxMyyufAhl}-##~TMZcQVv$ z9N7rNCJ0|?wrOz5E-v&fVk>+#70kx0!Z}o#ny5Da^d~7!NXQPs#e_b7HhGl<6 zJzc4D70X7DxEe={(Cbzf?)jn&)EvBBIv{Gna{7J3ru(9~)xPNB7jX3p@D1JzWz&lu z-!J7At@Kz(p!$sH>`mmhHWDL|1zK`b-<`d#zDevKPYt`I8qH7|4pUN<1ZW!mBj3^vieQ#0xhsa-}JG!f0aYBqUQc24J5ZeU^KAbg6-dbI%Tc3&c_`Z_YgISP{)Ck4@6tcE6#MjOKrFEoCu# zKMBHSV|5GJRe8&)*PNUQ2SqLazKgWUAaxx1Hy@CI5iJj8w0oD?Jtco4G|~-GsAV{< z9lw+WxF&w?h=jM{IMrM&x%>cCSRIoA|Knm!ZgLWhB(vkR($u3b8Q%*P5+*M5KGpxB z1sIJ7-T+pvU=KYK4{Zk~rui%MNLFKL-I^#5;lm%mDZnRzH#QXbAVNau$We|XZjCAAkv89(?#p(pF@CL z6vSZ2G|$r8b^A-xZ!nmME_l$C0)}Y3%>(S5>WzTHc-ER89z8w)#$M$k5;57x-TX!E z&%fGO?!irh>b(O)IP$a`jium>1=@xIn(ZBz_+fAmi!K{xHGwLFVF`fnwA0sEBo2Ii z1UHTFNzLr=VaOC;+ClTcC|TswFMGqWe7bX&>eXxbp#mL=fDt_+=lg{f4PJ+`gkT1 zrWqT45tqsQHW=``P<~!2_;k@9vvC=+Eo^!s(NeKz1BQ8QflsAieBU?n%B}-xE8xry z%B)i3J?Lew#sE3NCDRY)GYs>DW1mAhxO51XVDCCX67kncz|;wO zpty;^Eh3~5e%)SMbT(JVK;;qA$2x28{$7d#a<~ANNcMSMRc?}bjRw~-^ENJK>o+z8 zR9vtxnjELx`y#v_*a~r2g}I9&hi#IIf{c?e9Tq6rka5dhH^AS&0i-SMj?;-6g1SSK_6ZwI=hJCcQ7Cc~w|?v&xB`AWF%w>$`+ zx`4wp)+u+IZW9ybOjM&=G9=U$U3sq0ReL4^AX>i%!}O2h0tvfIMXvvGY}id69JFjI6?j!?`r4URncmnvW=mMbrek@<ALh)f z1TF;<{ZbqA(`6O_|31nY9wh;8D`0aUYIAwqGH1%VXkT*cE8BK8ar^TKwGEQvCRQ?t z9Crfcy(W>=<6=e!$!J$HY^e$hX+r+FME^(Y!Q;-(Ae zQJ?zVF(J!N#u)x}P=;IY#FOoJH#CjG4G-TFeQWL|20SF65aT%0YfY4eupYHj+yepE@^#3JP>2KPF*Q$bhV_Bb!) z(p!z&$8nVpeVIp8QhHBmKF`@f^T)aK6TvI9w~+Nr`auAtnJA2sp^6|Pv#f&+@GI#R zvAU%maQ0XMH-A=7_3$sv*|;xS^m3}DOy#}rhh$lzjfP4-+~F&2vkf7f7tyrmAC&)r zF)a{smh1h8QUZ?xYN4@z5f@=D2r7*JPIQY2@dYsa zH2zffY#?Chtj7HXl{t>a1va{ECTvdRsK?ANFAzw;n2{NQX%gG_4?Zi!KmY7mwH za6DiIsjn4k47?9JM3s~fW`J>wG)AK#p{g7H(Yt{^PKk%#SLrE!j8h!(XNmP1bAvn! zdZpKiN#MLA{=Sxb;beth^uG<#;rJUrr zB>n=0Y27Wp9DO7E-RWXww5uwcU7v8w~}2gp*n$< zX-!l{ibQ+ne$u1Q{I&jg@b+XL&|Mi`d4hP<`>4~Hckeg3+T{w44zD7fR|mQyEVd;C zhZ1*c}b1$G0PM99%Bhy7T-Z9&xCo<9e*57CA2hg&IyjoiCOVsIdc&xL#7|_0_~682 z2eY9`aGX{s?TK44p6|u&NPduR+B{5Dl;DHTNPh3P{h1F3z^Wo*EdjqEBUS$3B^5|G zY%0Ct{`_gu)+L(ztjm@+sO7a-Qr&{RPMHpTzYoGXGsp9r7osckKV* zP0bp!S8>670I~Z+H-lEkAh6I0@b9^dcNe@NoE5}pbDZylRJkxe_8S3Syu12q2VeH( zDZHhQBJaFnHqdZiAJ^{lcinu-8{yu5iNd1W~Hd^>@)ohUF%}_s0;0SDn26` z$jB1OpC_C%u?i%z1phbV%~qom0S~sc3Y8iXnQ5o7r%5)=zSH z%RTB(ZNOa*M!3Vegg0wrW(87JT;zJQ(xqldMD3iA|${4nZ3m| zBoW-)#wqx5*)(n-S+RR)E$&?;&u{XTSk|ZfSNs=~=Ah|Jn61o=jT0j-H1Mu)G3= zU)%rg)PR`>34p>mmEaef{T;Vb%q8C6`pNF*WE4#G6nv8BxZJa&fFojD!924tj~9Bw zb+Nijq8N{MWrj)2-G_!TtgIpx##)T*WF9-AU$A(=_3@zSh4<;ur#?r*Evun$8O}`Ei@q2nHq283y!Q-Pr-bhYSkRNW z)*M!a5KO&uX@}1-B{CRI_Ow1FC9hcVa^L*iNXen>Hb&!tL)RqoZ{flh+$HmANVQah z=^6ArN%Z~si@LEU&_IxJ>fjp9gsiG7$3**H+~=2I)jAu< z+?8bsPy7|2#2p$O_$?Dq^|aTv>(Ca(GE{wyX-x?q$@1kz^21~nU4^acf+G=fgmTwh znVv>-3*APJdTLOV$m^~yf9IVP&I$Qzbg?yBNByP0-Iz-EVD(Rnh*+BVQ|x87kr&B< zdHwM^xXz2zHuM9{7R^!wg^$4g_eq~xLpTjp$LjpYGaH&X*nmC}E5&^5F({_Gc*BCc z0RFuVY{>_$!1bN-?xV8f3Ofv^xd-?$Ilh3E6 z`NBckd}fyu*Qe)X+vsRsr&YO59xC)`@Mn7z^sCz0IR$?i?ev6Re9q>RH{tk3cE`0N z2c?#R1kG6klKPIeJKPv5p0?Q|2J0Tn)4iMRiO6gT@3W^h_UsDzJN+;rOq@ZM_7z@- zET@U{gaNaBeO=QoF*^%Qy@+Xg$g->csFksWb&y;jogSmIhs``XUw%IKpNC>?g3IHL z|NPFC#!NHj&n+?_R7oFp+-<9ewK+imB;f6HE-}t(4qBF=0HNPHqB7(O6F~5&k*#es z*P+s2TARKvaPrOlg}UcHso{}KjJ-YY(yPcyQ9P%Eh!MfdUSNTkIu!Z}kA+CG>nIy{ zK#plw9@Z?E)Brnsuc#qM3!%GmYz1F?0poC+b}#x5N00?^b($Bz5FQMX{z~7-KWE5% zk3rpma^U#W=f%A83V2sG^-bVvdQc->euH_JS%-tTjdS;_u!RC)c#rjuCpX}g28Fg7 z<~L)gvOi#zFXDtK$u6|oM9h#K)g-Vg78cGEv?PDt5KQ$(jGd`{BIgfo;(?Ja^P^3-8x{<+h|#WmvE*Pd~WP>Fku>{S$HTwBV>N;2-XMON9$HOee1WyZaB zQua5A-0T&ySN#0`g!{RV_xt@i=e(Y;b7t+jOdYbjL2U!lRIRgsg1j~GHM&>G$XOHI zBH1@*z~j_AKRFQllI0=;0;@53AvN@$i00!Z=cYS!s45w5T6OE1aSP02=G<|-ie*YQ zk=7UI3hycHF^Qj(uKxj7Uh!T3Bm*gpB|<4F@d*r&Ro$DEc_YNgxpf7Ivez8b_x#8% z6u{}dnrMOpt*7CQ-HcsdC*#_#Yg^yJR_s(SclUn1c(=&A8w z>)4q&BZ_!BAqdQWBZfKSl&vBRQdruo@AZShQC_eP_Kg%t(ShhCsujxWp7qHNj`Q*R zXiGv$>J;7m2ex!VO2aYoG_I{pG=}-TZSC3HB~2Ej-n$7+lEzZQ+YN3k$GYN8+g6tZ z@`?6EQR8eE6evU$k}8bi?hUKI^QYJ|Uh^j^0}}&FeDD=G7*3vovwlr++MQ!@ia=bw z`N=p}8~duml3x7sFGz>E>X^eTA_J+=Nqb)P9%FE3xD3`5;#AnU^PMq1&UN;(2I3R@ zESm5cv~>rE@@>*U@mr*&b`5xsV=h22i=`W-$c^pdM=MWwdY0%e(B^Wu$xl8@`=-PM z+W}J&5aTQ>#Y0!P$G3lf#W5Du;JC+cg;lOB#~bEOK@X;SDb{2MJ?P5y!RV8i`bLH2 z8mKV3tOA+Z%qHDh;=NeLuN5I^I@s_3n+OhdVU!pC@(43`l>Mc}3!Q#Ay%K^JRn`MT zbWDvVk@IKb=5}_I`GoHLN8n}aJ1k|u;lU@UhBu=ZvlndO@*4d5vq;vV=rCxu!*!^* zu`PU!x9FrpIK6n1lNJ%LGJi|E{oi<&!u|7m?AyG7o!vs+lPENEG!;~gT#X&jz`lt_ zK&Ojj$hT*I64(d&Vzml%$L*eX`G5zad2CclK5N(5TP}_PO zQNNuqMsHqra|Wkgkcp}{dUuywRD^svWsrY-f^D&Ml(urAhA(2U?)SCp9#=K!>Xj=U zq`>cC`=O=eJX6GcYB`TS*_KRLei)JkpnXkHvmypP5<>(+Omtb!Hn?`o8+xK`2}V1# z8H>w9qCz^79$ox?#*pH)n&76h)i%&lQ|sa`>m-5gnn?Qbn^G~`?f~~IF**0(FkL`cCr&liv{hPIq$|5 z!cP74WVeO8t!Q}e{a`NN3GP(0Nk)UYhiIo3R^+@C$_GgtPJ`YRKh zk<7I8v@;Xqhju)Pq8W;a;YHk$=%|tZn1iXYZ5?ESY0_vaDe1$sDX{wh&jJ~mR&ejm_({UoyBgGRcZhx|_d=&P1$s|z`iDr@$EQr z38`NP?qg~Z4&26 z_gosG%n#xN4`HQ9^KJVSDy?_2H9NKJDtb?g(sV)kPtAU2$tV6uQq1GV*;N8+t=qNF|1WaVS-SV14$}k_Vuk!Uq(ZyEzI|ci z+J}yD2;ll9CDD5vd2U);x8v2E0`5owaj4TaI^_-O3(nzq{gQ3q(QEM0R8Xi8v-~Fu z5Ppf(U$*TeoAH+}bYGa=V!v{u|7&r>3m~0bkN)$Pa<`NJMeG}8NsQJlMcjMy3G@k| z4r`l#C~7O!Aj7%nKD?vXMp1no6>J>?$}POlfrn7K(YUNr1r zMq+TA-+_jCdzaCfb$g3ceUWe*!HQ6~KV*OgK?fpJVj*nNkcui$P;BTu$o$iRwP{IA zEOfi`g#%Mq>I-Y|&(dZn95Czu0ITmDn~*wfdMnoAeZs0g_{fdu-OayA(y--2xWuNq zflhjIUbZ0Y92SwJ?52ADy_fDoAoT~KzMuSFxFAR5D{??VM-|{TIT*#h;Kve)1WBi6Uidhz0} zjzG8iBX{61UZ?$OF?I z3OK3a%;Rwan1FW1fVWC|0V@kCrz4;3wGV0VWIS?%hX@B2Tki?q^kbYH@*vnL-5+qN)Q zl`G=rB3426im_PSN1)*KIb<}8MDH6CmU3rM22-)u2f$9^g$$2t5k)M~)+*Xyg}ii= z=BH9U68Eqq@{ixb*~Er-JZ^lfIAf0e6`$DX!W{7KPAc`f5I;kU?tcRDLOxIqjyZ(@ z_OpB9pUAg1+X6F%s`ujw&ttmGCl4S1>ylq7()Y6r;SkTOeu8~CR5t+GGq)90>IU#E za9pGY4`j7bxPHeqrTuF8LuTP+doo>BW}{~KC|}oto=;4CLT=Ql$+K7+SW0e5Zd$1q zC5RpSA=ZMaXUhf>PO}e_i3U#ZGGIEOBnyO;6j?0rCRAHJ>Lb7Qb|9e!rJdb$$YyPX zdGYXHd-(Eg;+N|!U_I%@2U`MUIo?XxhBFoy)+N{e;_lgb+IM-w<@JK&JJ)icAz7iE6Bz)OSqrmyfEIaGiJ|qMeXSEGWHfH4S^t&h{S}iZ1J5Gu{*EoNcew zXDX?LJ*{;m*g#fnv#7nypeDrpeGBGt`=pi67y_F1-5*z5MB2BY7q!C4vUME(p~RQ- zk}rdMzE8!qTOi7%FrROHV(a6vK0^^&K1f4E8sYXc(!RLJL{`eMlJ^ z*(qlNHP5Kx0~k>^A%aRR*>mpcuO3q}yNxZ(4+O)l;cm;x>lI;lp)~oSS`;Td*p^*< zc+OY;7Cafd%*c)$Px`B$2bSN zr-SZar;k?1bnkdt@?f<+WG|6c%Nrm2B(ZH%`I-sj zrBZloizPiPyzDIdK=matKY@Ntk9G%zLA|xC#B3Xud4IiQ&1nbwsaH)>7lLps8o`A7 zy(_>g+-!;(zpg++g`hPHS8%p{1NuD`hrXu-J^`p`MXXVc#C4j_2tQU!4q3NQc;2Wk zYUQI1&b|f1_NGlk2QvWq&nhw@0q~5f`e;&tr9eq#j0&x&X^{alMxncS5SI;fmYsApN->KLqXK`+EF*Fz8|liJ<^`ibDz}Nl@|~Y8 z(4jHm!EDhfL~_v8&K#Fe&`ZbC^gwSDkj+_$_}-QC^WK<{ed$t73lHO6mz<82#B9V#oqVDP zx41I&2-hzoU*9EMn+x-8>8uVJSo&8aXG!vEk(KZrxQ+kG0k2mdgcGzh*}x}vjcBow z8C>|a3=j`p)lbl&?V!Bka5|uf6`Xzi=!$4#;gI;ozMpM`n!bB4<`4TaK1TW-s z-Y)}E_)1EewNkbuovaa&95cM^`0yQDjV<`|9`Q=5*@Y1Of@Se$dUM zg;A3fzcBq#vZ^f3AmvyDyl-X&6E`Eo8>Oc}q`9^+|K+EEPL^K{>QLl$m2S3C|EN94 zfrK<83SY-=?`ffi`0Lo80g*8)0EkNxBj$+4cy`*`fg#~om5-3&gRGw8`mKwNKLF?x zkjD3?6y9PN;tqHA&pjZo67wK}8P%yc=t^(<6sP~fGy(eH%3k||Hc4$r_7QE<(!QgYzM|eA*hq8i$`M;xJeTAYmlN<@pu5><1vhV*dYhBcYx! zI*0S5pD^DRX+6r7Ry>CD;D?6&mTA2DuM( zNI*)^n*m=>H?0VLR^E}@*OW@)WFmpE5WZUu7rg~ObC~@5`?ZIMV`O~&!%b*M?j=Nt zJew{~kz-oVRJ>@m`l_Z^hM_TdqFlg%&2&FbMktl>zKd&|$I$MQ@k?SwIrET;E(%dq z2P!1qqEmQ!L~)zEgeFwaq>#Ro%}rizn>~b;6W$WW`1)D(@z_S{LMu@1sC@fvqIksB2ZW}?RIKmC6Cnp1HZ5$ zurGZ$lo#>-=T!9592_~pWDNiuM8%Jlgj9T>m6xtqn(WPGXQ0Z$!1q;#{0&6ULquuV zm!xX|>$Ui1E^Chpd|`EqfL5C7ayiq87B} zVKT6hR|xMnq;L!5;0gujh}fVTy$_e{G*@*dj?L^v4z|d1hQ@lz9B0=Xx5SV5Q*KMq zrdpYRKvuroqg#w?j%#R!m`hoyLgqxD_-Ho|D~`3Fuz7~JSe&I?Pv9u(U5Jsngo;ir zPb6%OHO-dgDv6RP@Lfx|o{iWVrB#E%qXtY|#?a@sS2(F0ksstaKldRSrDn&GiP1%oY}q5|A+}JttYRWDCF!GI=%c!FjOubRtr69l7VMm zc4B?m`_pb zo9jb^rEYEz33iTS-u=#Xh9E|M^=HuKv>Zcj>*@|yhI+fbPVmWpUp3Z}!(NGJzAk4- zHxgP)qCIzBi|;j|NjMUz2R(6>Q;43`;zfSCV`UJvi9nPOR>GN?>Fm*gqyFUl_A9J! zQR=>j15ZXHdfBp@&(kgVs9@(8JL%6sk!fqGwUsurSn7#+k^mcDGe)%o|Grq99!!$F zIQ)?(T__d%i>~AI2ZE#kLXyLLm}y_BXA6oP$x7o)7aV<$LAn#Kv4{<5!r}XT9Om2@ z=@K>Sl(7 z6&W1SU6`umJOqd9VS8{?3fn(g__$PRRi-U-kVAmcq7UMG6LBUC2f{0X!z%smdb9!y zOALlu+nFQCSZRGZ$jZ|x7^Fju?mTjX-X4TM$Yeeha?xiAd#J^C6R*+ZH35U@T+@>k zE_c=DFh*ww)?mcn>xg;?awD>>X|jiRzEo|t#g`hw6ZHB$my-z^+)B|nFTP2Y6gX4~ z`q9cU`|MHA&qr1*I&8W4W_}`Swuw2jHd{6k^GYiP_1|91!oSHYylR`g7FlHh{frza za)`Md5gdBVNXhl-1@BBsa-|>m6Xya^^KAf$azr5Z9*e*D{Oq~rtB&X>9*TO5W{dI_ zHW#7%a!doS;t$*?|8guiB6RHt!E9!@SAFNze-is^jKW{Gc zL`qp)jV*mFI6b!`!6S^%6piVShmhAcqx{dEoY!20IsYvjOhJmh=BBN(sXZ0`A=B=Q z`|{~IB$U3o{y}EE%7&FgG@$uDlCiNI9CeFJgOk{wUyc?YZr`O}g*sW8K=lB`kwzo~ zY3#DSui53U=;TXc(J`@i&jy43MWFCL6hWS1R!%Shor70X`^C2%J~t7cg%4{lCCaD0 zl-H<+$cujc;_LqYbTokNupCXBUzQrCg3xZ#^21>3T0Ic=9tHRy`;?n}`Rr6gGWL!- zT5*1R$F6^AHUjF!#NMSIP0!jf-0UHAem~2eQ>8m=z9K$?i!)mVdQ{m~PqlM=_tE0U zlMsO|cH!s~YN{puM}syE)#*2q*__mS|I&PUa=Q?lxW>3p{(Y?t%V>b-o0Tp~q6ABk zC=2fiY`xq0qihb|lSgXJdPE@Np_N_xg4vffVoRbTX0P#?+75ynE(^QV+{u3F6O(k~ zlg0|~V%?78=HV~w(tY{S&7^=+@~bsiV-7N_y+YJ|B_;%GQM>d^Ou1$X))!`RU%Y{> zuWoW_Dq(n_Z0mCWOSX5aR;Z;oiU8&=ZX@!;-#Z5`L^FRRs_BMth-+fsdE$X-hvnC` z3SMNw(ZC9k9wl%MXKSqkPEe%0@v421bXC)2o*bjE^O}A^^^SKH14*W~t!y&$`?sO!+PM)+Q)t7K?!wyT~Qn+5%<6>4*0bSsxbMb5& zyK|b)hbmj;@U=g5$pWlvciV^#fugZh zAz0IkH2vKG{%^t=gi9=|hM@9)TA)b_x{rNGFuIc^od@hlOjOnl+WN5Wq~_83cWga^ zrU+(xwwLy84>Ho+iUj{W^QdNjr@ombk{?AMr-B(coUVz;4ahrzTl`ebji~onoOrBJ z4^p`Zar;PZigBN7&m5e3#u` zPyUnXF@v!S z`$l^HaugAZJ9LzyvkhDNQcOwd13=A)rDI0#8bo-5O+aUvCv$@c96g|i4Agep1wKSA z>tRIb4O6wSh3Po;&G&P$Bu|RK%5*|oysb3+P=64?1n)KSKdf~dR$)BHo)+T!iWuF3 zX&bn%FcW)_08t4{z4F5F=2nbEAj0#BA);fs$VihokXAnJLGMe>nEkra_} zT86R`?kS?@D=?;!Y>;lMH8m=Lc?i}_`cb8}9R>u7&ngCXW^)t`WSv`uu;0x~eH91J zk$Dql3uTZ*rdm5=YEfVX`?)R;z1sXZqn2Kh32aKPQ5HL8aZiAU%ED+aeI4K z303A^c%iCYI$k6Eu3Ft|1Fnnked3Bk(U4}p!2x}Np5l%G8}~u|d5z!UXtFGk&d23& zqa)xNv9a-O%YAKKyp|L=ES-x>_i$_I_$*B6??1;*EskxGesp%S0VHA`dXzge$PI>^ z=qs^`>PGL;^D1Yg>+4erEFL;J9$oc^c>J$ViN zU8V2l8N3Z!M_OT0n*^CjcWM0Z>-qTRTiIq=T-j^nAk}I63|#8waP;!nU;P2SXS;Nn z7rY23AH-^2aC1UVwAK?rXIWTQA_+^huiGh!ql^TwlfK?|L0Yl(ZTR14<;v-zvII*L zH?9sv!D7Oc^{pxHH$pW&*%`l66ufgrMF>CeJPg*_1p25WT3Wz_cg}x!zBM(Zms@a_ zLw7*N3Z~SP9ow3667^!g!u}tH^P<5fv62=_*I|p+s!$|7%ALmMdw-lXIkz7(a2@x~ z5Z6uA6pH{wzfy>z59yql`EK;%Lgms~!@(I{j$M_;^hiAAHKU&ok8GdYP&nRMkH=67 zxY3ekl5=e}uxGw!ccVx2y#6);X8SSrjTC0#MD|U&X428*TH_s7>lCf)@EbPtAG_(8 zMdABPoaVD9zvbm0h)8~$(NF8~_K7zf+Ue9I8n z`K#ymcMt#lv^i<)5j!~fx3jZe%a_Te$_~wZ^8V@>Gd*_yJEfVeD;GW#EUO$vmW{0Z zXgwN6;2z|z31z?IkhqtY320)ljam!R@wSJGM8^H=@Zci(?Oimg>}Pne#!E1kx*!H$ zQ>t#81N%;~mG)kF zOn+Bt_n+05y7k+)m{VA~QXMBUwZ|jl6)A_gw$)EcjM_dl7p__E9+E^;5VJCmTP13I zIj&BTrO-co}bGM637DAnujYmK%*Py6uN4e|7>tWq+1FtoUe%FInV6Kql-RvamX> z^$sMwRAlYV>1QBM_hbD+V(EnsF!j8-y#85N)X0;U>l(q_$}q! z6TVcCLo9$eMM z=A5!(9**EspOW%tClI?AYG`MQIV1DTGw4V(!r~SSel+IG8XOrI<6+^<&d(8tQ|a%z zCpjQog-{(B!{|KFzhxdwLjr989eUBN7_6n7)LHY34#cL!AK)J=QQh9)%aMEg{>$JT z?L#5nfA802jLpYJL*gXMSYAB+agWSfoc2$tk0dC}LC)P+2=GWn$KogNBN z9RzWhA=MN&4T$v-ZIYGZ3zm(9pCrB9Kti_daF2ym0m3JlvQXa_ASZp#L_+3UpA9VH zUASV6?l|}s8wZv@`mKRvpN~1*ezG7Elv~Wy=SHbBhnc?4@S2ccZ%+w2nMH25et@1V z#$#d|-!g<_;ql_|MP4GOgt=pE#^Cu&vVZ8t2RIPN{YdL`S^72a!ROv@^5{X`0rR}F zUwuF^M^Vb>!c!T@DkqhjZt|t8uK}o7P~)yWPF^mmULmtsy2n%5f}8em{?F;QMc;W# zQ?9AIKQ*t<&mG>B!XyX_e*VJ}soV`3KJsPh1U_(Rxm@Z7TKmatsWfLmOw`S1e`kjq*LmOwgs1bNyoU5K+nf(I(=^7e$yYUOR+v zkw+zuXaTb^YgfcZvnX31|B9>YcmVfc?WG%-xVz3(lPp>;&b8vInpbrqjcoLhgFaQ4 zg+Lh&W}T<2pOaE1J*MR8T2lO*JwzBtN-kSh3V5L>Gy~PgucHSo2!w7Sj6 z{92}MER6Uj2b?^vz0SIGspjDWL(ol)R62f_uV2K;WvaSYxHo?E8zsDv=iPAMUN6ex zfFlapYzW~FKH;g7>tD*^+IdKzU$VVtgQWW@HfIjaxa_Y0ZLi@1Ry4KmILjsPK$Gp# z9XK>{tnNz?obuxe1v`8P9{k@`?=mnX0xCKgX9OL9!#8rGkZ)R)&l?F2om+9@iodq) zzy1Ip`qWp{{=Ulo7Q6hWZGMJ!63B_1_$Bsqrl_YX)}HS8KRMFg=^Yl7aSzU7kV5A~ zJiO&LUH8KH_`_)qHQB*W1`6+~%Hl4WjM|X`pFDSTw{MxMfokL}jtI~+VJEQl#2*|$ zk;!!^w&pct{o(oMMH|ZGo9+KN21zPRut_Ydy~WTS+%1!duOEOC~lZ z$E!m?V;(?(5boTx%c`-wV71S{9rm*}=Fk7E53vY_W1%w!W_N_?(q!Dv>}5xE1t$MY zP7bUeJ23`=UUW{J6*jNhZ2ailomAAbyE!C!hq6LDW8aw-K&r;UiUzCwbiWnQX&xxKAU0wLMI$yBdL(aB59Kz^gFcI!wYr=s89@-ke()pWuFNZ7{Sy*TFT$gOpKBPsAn4b_`;ET?FEE{$-`GE~L zc$r<%(NB!QF5cbpC2>32@uqn%rjg!*emO|!iJxdl|7^b(tG&xNZcK*br`xZBi@)@1 z27J$&%|2y)bP^W@`j+?~DF!6%x`_|p1A1~NZHuoP(Uuf@IyJsv)QjlJAtUxm z%925RAqxjK8O#7n`Z!>9F1iV`t&0WyJ0=!L@Pcs&h@~XR3Bi5vaf&&jab;myPF(B+ z6zU=x8HP9&b0^A2bY%F5unM}LkqOn^l$_SxIp@dV6TfdHT|xeIwVH+F_^22_K*xu3RnKy=INKxRp3)WWbP|>^WWhr3 z1Xt~6WR3xsCbuxTs*vLI+byn$5@hJ5ZvG7=JF0epw-6OD`jWB+rZi9{|GRRwQJf4WjHt_+q#n)m)> zKg7>pNo2qG*Z8Od(B4jLOWBjp=_K@G^ihkWm$(CZ-(qEx!j3GX{vKe)@u!NsWa{pO ze|xq&(Mq~~`rHHQNkQO>9R;ptvJQ@+d|tM9H~NkAt#K0^!b}W|Q{b?U<0Q}!_ZXn1vh#8r_`-*3>o#AV>*O!JlOn|ObyS0>2$3~v zUB;RXwFav)$GzI+Qo#Kgl;*{L83(S*u81O0P_G+H6`mZ!1#25&ehc}GE+=3#@b~=` zBQZ8S`<@eVPUgZe67_;$;Y4hazOjT{^)(N94_0CFt-AA*`97%!e*cH>r0pb`nsf8^ z>cYK?Oxbtm>bigr_GW{3!@Rc0t+=uk5Se)rbr=#eH6bWc+sfZ;Vh_AHcNG zp;{%MCs9&C;}*~IUBlV>R_%MrpNtppBdS<}u+KhGiiX1Ej-o_cxH>Q!K7YY8d!tvk z)FC5+4GTlRfiT6&ny!o1zo-!P55}FZ@gQ>>F~!A}0uX%9bDo>rjo(F{#Slqi(1b#{ z8_(#bmkN;ZNd6>Z?{;dVcoVvI2|Q3L=*eRnFFJ*qhsbMs^L7C8nH^UwDJdu;yEJCgsKt94Y)Im-p2fm zZb&}r3g|q6e8EFxVb-+zX1Wa|-aoE+?w*OB))1@s(c%Ks+R)-gDO9D+y;dy_c=I0N}ggb{ro%*xSRZw@1PInHs^u=Tt4i)Zq-m7 ztC5O7{s$n0IE)o3c?%KH$lGX46glASxq*z>WNvc6;Ue4Y8?LuCi((bkUYw+dHP_#i zJ?2BbPVgKpx+oIt$tA}Gz$Kx+Jb#|wgbVSY`|o`TOUjhqu0S6wWc(e^9FyE?HFyh# zOn~;L8_PPLpX7?vGz}$JhnW2Y{+igRB1jId-&k_)ly287E6Sn#pHqPSKYogNsAclpAB`-p8%_b?5IAf<03yLYpKV5nTnn#n16f}p z3o780cdh|@^Hx8P*hu+Iblcka>|fqz_}$~}dXA1Mz9f;FG4>}WF&OPdy@|dX)=q}O zT#-(CKgX5SwVc6aDLdfOZxq+pSR>fx^Z#N#cAfO6fN=y&>@#+k z8%xc!<2+ZQ&o>720SO|5k%&ZXH;}VA%Q07yKTTG+U^shsUV%(FV2&LKM4vO?y@l!* zJW8T)+>Zns%t({=Jrj9;$Vgh&i~$` z##25(OOYz=9!DIfKy_V>xw-l+?`~|*0H~`t?)NIVCeu5DaD6`Cm#G;C)#hx}pTG`@ zLjCy{Mty#b5|lf5W|>!gdifb#8By!#M)dN%FFvFf4FX%2C0^*NaQ%%Yc&I0}+t-kE zKkw3QJQWgo9Gvag2Nn==2t@?B6a0jZeF2x|EwN9V079@=${+3q?O?n0WA*$TSV0-) ze|!-@d{&~YFf|r1Z9KV%DIhUHqS((8eF}<6V;&Z))B@7&+X45WP-f5PXy?cPFTQK| zaSRIOc;OF>=223CW)}%FSH4gs62FJ^eks|Wve~Kvr+!@Bt9hw1dcLaqnEAVEm5OB%_0qnvxOYr`U9ym7x3F{la z7%hp(?NJt~XzYogi1Y#n6Hhn4AQke{m*obQHU+r&YLRUg3p z2TXaN?10_1+2TO0ztAoSG+#ONJWFIA)@Tu2TE7I|qJ|x@j;a6tq2xv?FEHX28D~Lj8VmADH-=HZ8q|tP$&VAU19BAy& zYD0J0F{_#i$de0;`&Rl-22|s>6^c%(MJ_g5QtAy0^j{7Uq^VHq@MRQbSab?WOyzyU z-r_K*#NIr|P0K@rrVMZ3BCfENbVt1i#pI*tRP|S!3h_sW-U{GL3}be(T+op&1>Ake zdrU$7^Q*l=GLND^ZAS854fxBLL*BYY4uAPm-bM|C{)(oVTK!cZr`^i<)XD&8L*}o- zvjd{kd!UO}_Q~8$!f2ZNdE(B&aRuw>%m>ZyrDhP?z5&q8xHw2ZcAu&af$nEi5#>0? zZ(iN$MUAn+plob{(X0bCES?RR^R#|}9TwHC8E<1cp!f1khhMDN8_G^31#s73_U^A$O!Hxk7#Z$>HtWx(T~*SWiHT#tm*cN%*+IH2}FFEsMhOOMky`IWuEPx z+=Z&;C zK?70w3QgW4;bs~K%XT=!cq^HHY4e>D`c5x}ti03c9jY#^2-q+I2o=~&6Y|xQlRI#0*GbHWwhUz= z<^_XVkWmA!FDUpIJVF)JYA!hH#uE%a^l~RsG%!;pJRDD}m^Mrr$>PS3L0x);9O6P@ z)z&)?J4vz~vJD+?_d9O>r!tU=YdsLUbNf9gHw@eJRyS^muClXc^WE`K@f$gM6g^|& zkdgoIh?1sTz*pEb3l)8;3q-pZ`>2`-CG%0oz5+iE-a~6zrduvmNJcp}Pc1(I)TWP{ ze!zoU6NvjD1}el6-Zu_R6+K3v1Q`~Mw6g}yZgA53-|1P?%lNARJAB=Y$r6)6H>b$j zx1G@7%-k4K@#XtL%qNen=iOpbVjh!tUjVz~MMqhfejXv3cR9t(C$4?tE+n)h|DzgM zo=_^1`;Lm#N1qIe8PWqEDr{Mvd*b0ZT5-rlF9?n{rJdWGeHpCHarB0fdHk>HR{#dG zGKzT%kQ7dx;qmlJ6ufQgwMOM_NS(@`c#z@Kf;Oi6AjolnB94x@3KfbLs`cNGn;%WP zv^?;l@`C+)$8_V&J8((3Opz>;m?We+OkA797v!nRa!IKt;Mm-&Z>Qo(p)!t!B8Y<`M2Kf{>0 zXU(@iNZBR?LbSCsVAG4~TZ4F6l^GJ-X!Bh`$++3++SU5V@mgch+qjkU{M@Z?@_k(_i-Hb9`!t@RPPT(r!mX?yZT( z%3scWN&Yl0#7D-qfzq!%TP^pAfM(zH(ctMr3H`MYHGlpbCUf`3Rw_(!50gy4&G!Kg z5vZP#U1ok$r-rvaTOb<_IgL_9w|^COey4)-fhosCFVNIDF7?H|Z!FsyE@H+Uke+LI zpvh~VILuqYq-!i8m)0`5F}KHU&5c*-l0Oq{yn0QPqcvHZOWA!bD7!kYC8mL@@*7bT z)Niaw%$SW2K=40rz{Q{ek76tnX%^g1e0|*pGY`Ee24Xm_Mv}%MptGh@T7RR$66{65 zRZR_sN*=fyH)enZyqfr$2!oJ++ux(R$E6sgFr*hwFru4(Y@G?8(3eBVc*9sZT;eF{ zjC|q@?0qQ3LH65gy~~q)0*9g4UAjAh2bAJvx+5h@_tPh1jFC&Np8+B5lk`h56hpnO z$|<%f7d`i5TVZ3C#hXL-(>#@lCTDxts7nT5@;l9ds%cgbr0*ggh`jH0OnOZ$O8rG8 z+@sTh=bKZ$rQ^p~CYChNWaZVM8WbD2XDspGx*w?5rqGfRiv0VMNfli-o#7n(yJ_E_ z*<-JYES)TuZ=M9dx?hwuM`;3Zt-tv}4- zCddVIZ%Cy46I)KjAu8T5p!NStP1B2IV=zn#D1hR`B3Qa8mguC4K^zs=My+B(abVqu zvT9&w%+m98Mje;M5ky(fgTLVfHJ!{)f$Yn1@g9StSO`G`==t7Rh#_>(5%jY|I`8-~ z{~5@@vhj-td`WxcH;qq@EU!14BrW1L}|@ZP17JSdTJQAO!hL+|Z6C z>z>hP%qktH*}Ae2kDB(F`dxFjR4q^p$Z&xXd`nchBj!8n8Bz_gyZ7tt#cKXKF;k?W zE=6~84m5}_2ztau$p#%R@1IIS`kL_YF<|a@b8bE(^q_r1yL6ci{1?j^sDlgWg-dEy zj+k9=hj1J#?JEg;EY>A}G?-w~$f5FBtmG{^A=ZREZpA_vMU)Kf~kT3*n+IyN8U zH0ZSS)C&VcW5gg?Z?b2%%6B`jS9x_3mr^=4eqB6wJmgFmWq#_MLcxT2B!ivyA8-={ z(!Qhg8)7bUA(|!68OQyArZ)|0+GXA&&0PEnxIZM%c@gU^zR_4H?s2rQXF{GwR0V9x zk3Z|d;0nhPaChKYnYt-_Fe3*-3J-!`m5WE3^LBi_?0@Cm(+_TtSrj+tya%zgUh-V# zOZAVKZ}2N`jS5n)y^2s*k}3!f3?Vbsn6u1JxeyJdr6KyZ1Dy z2aj<*$MU-oV(G&#N9l8Duek8KmU)T>u`knfB3iD9S&!2&L-lFWIJ0jQ<99c`6k)f?(7hULS>Ka6-wqA z*%?t}b)pE_+2gt2^A~*Pd%fm&B74?zoMjh$hW&i7Ss_kDm-RvC>*10=v*is3$l^%D z>Q{@_YCa%cy?H6xw7#T_8Je6_mfiru=;F2~LIe_Ioq{Gg658IGsy9 zJ?z`aHE(EcZfp|ydu=5nD{0_T12JtVCK&i8+7yV#=~LV?7zPLTX&a(xH0Za{!X>Nj zwF&-RSIVl&GQ|FdH>s8pk8zyn6A4_L4_vp<Hhi;l1Bl`iq zw%9bzKos*}i|b?DbR?%XM=RdwRu^8B-(fFpNbp_NUQ9C*sp zkQ<+4Ej`i#>2YEwBYsqrQc&sZ)+vK#Xv?+iQKBs4CSUNc)O#^+1?oCH(Z?tSY5D}I zto5ZE!|cuE>%Ws8e@xHF)I}cuYVoE`h#Ja`JPGTC%+hb23m!PS58w*=Rd#1C3tIr~8IvxmXI!`$AZASz^j!yaLJ_E`@LRB^zuP-Xc<*3sA9W#Z@| zl^6m(%7M;_mk5=vf|}-Qjqg3fRZTv5t=+8~`-oqqQ3X-cgxXQvG&`NQhbnDm+*bVX z=6OUYqYvdphD$?G2in$U( z=_gb;x(-7u-ZbtA0-pV zs;D8afy5??@m)1e$M#>6I>ByQM}q=&I^FX~c;J`}@sTCc{*EN2!T^{={Y&gr8e)J< z$|dg&6`t%xd&WMx;b|2+>P{XD$S4rdjG;+zckly-;W_NiYI)H^=jcFQ4IUcPcR4i~ zA-uTd*3{x64^qS%pv6DooBB-%cE+CpfKX?XopYMsJN0OhPCD(xY-8Dkvx_a}ue3ae@L znw5|=A$9pI^lRoKjUS?q>iJZ_@AnOi(aBwqh-{jp4GQBH4bM$bA6=<6YTSd}ofr1l zysX;oLC&3WuWv+vtSu>Ro(LiX;BYtOG06YEqb4;18D;KH^&~z9y7E8Fvo6 z3vz3^bMlXUUv)lfa(~UD@92&G3Ei6bqA+vE?&s6$9%AFc)R8=LexiHss<=O(vj~XX zlcV4Iz~4A1cJlDv8&6hp>~7b!d*uc-m(OSHlESK%Emu8oZXD2b6;Q~>*@hS(=wgPt zy8veU8-dR7x)O*v$7-K<>}@Q%VkEsa+cXEAJVF#T3kN&^Y9pHp~cn28`2m~hl7Uuv)ajk zju~p1lumh!{!sd`k3kCtfdBrVWs#zWLmW(>Z#xP^Rxw0PbgWpTAQ57mBpP|lG#AeopQ%f7w9Yb5LQv!+ zVkem9f{4Wx@G3}9Ej(hKhRP|4Qa=MRsn=Pa5kwq>>{c4*q`p*l)w;LZuJO&a3I1`6 z{ld;gqc{syG41GCV??>YzH?s$(?!gPtUkGWGB&&Z8Hg|qF|(teMb(`;<34&MH?7#9 z(mm{PPrepi>#`dG>i7aIDfgt|TP|S86`2rv2GT_kmQp-*pK8)i6gxp*^e(t;k&a^q za)GEKyGq6GViRaQ};*Yb-G} z;oFc0#k1?sTWEnDb)p?DfNa#aSDiBLKPflJP&)-gS#XU&|aRBdL@9jb( zB2QjQ!8K&`l^=qZDAjF{iza_tS!!AIp^^fF~G5&uf*|;C*}@oC-TpN zc>czY($R&peav`Ylk9`<6(QM4)eUA*Q$%M^vR0<_0vcYSJm#qr`J>Gyo?+Jhvo6R} znbge%W@wHKnz^$a|QE;M>lTY$s#^7CgT;KGGfPhGcvB$(5;C4tuaj=fPMyYo!P_L6zDf z7w(c6HAUy*NezzK1Qm9xg#x!9e|o3_-xc0P%HeqXE?ZCRcwy(M=}`~t_y7dqHS~Aj zg(fGdL4WN>!Z;!6rsM775z2_A{sHQsM1H~=?^I@}dGEo3myM|bd1Ikbw z$YZcrMW$FU6XwB=$I4HgAY4fJWaOFu3U%({9i6I=va_-du!=p79odvuWgfj#>4yzhU{eZVOZ=UNgP8~$$VQg9iI3tkJ1wz^FQ!T~gw z8Q&GiN7t45Jeu)e-M}kDqh>ZkYdT))ug4vJzz>Rg_iI5DVcB@s{{p)vvYfin2F$`w zOR=S=n$7xTsUgxDZwK5>*3rq$V{271^?=@{ZMLPCvT$b~|BrL#7JwwO)HWXpGI(5^i?s;P^E@=Vh}vCHSY zUElK!jzjxn9#6!ZT!<}_+sS=w{>Dz^c=fHnk>Lzn3xIoYU+skl2bIwiw6Ys+otr10 z9337iTWH{3ED#$mgYgabZ2AtaUca8_74X2_J2gheb`a>4oVdgRk0XJF&jC8t@VA#5bC!ch~Q-#5>|XS~r{D zBPVMO95o6Xu3I$Uf&=%xaYdRajMshW+U-J_%b-iv5RIhslPc`wOM0(Q|1x@{uJJ0+ zCq>=NqU&nMEXd*MkqObw#wK4)aY>HR@`_)s%{+93#jl1tPs4t1?(f{;7RaVXT)Oty zeFpTmhtGXzZEWcXch>wAEko!_AvmEG4s0& zH>%=rh6MO!s9N@Nc-A2Ok&Hi}30OmCb13WdPf#=_2JaZoyuQK>CY{H~LIQlZr7ZSu zrc6Slj$FexgS}~y+i;g(PLbDu_~>s-x6WAj4A=f&{lt>^D4l>%Ol9sN-c)Xp4^ z>0xKPw|je(E+y_Kcr*}C32wR#AkA?jEi=sJS&vU&06W;AwCOcVbSab+#5P~`0Yt!m z5RauV$iwM@wDhHoza)(w_FE-_9{=?x`_d zlCk3aD<8+~%?DIX*_4Ji3T{p8_-{29LHq!~ z$=aSYaYdDQvxXEXg_EXw)P$udRP6)sVCxg<+c&KmSAEf`;c zvQW)f|4!zj%4(IZ2kWQcy~U&W-@gBwxX{Ua{BC`-zMMvGF6wfkjYJWv8O{ahP*LMA z_j|X{QDMTjoexzP!ezkuJxNJza0MMf0`#>T&%-`y6^2mb>-~U@2@jlLo42tn@lgUW z5@5heSsK~9g!_3mE?bA1oZ@Ib1@~KvaIZ$P{dnd2yd#>v?t?Yp^Xd8HN+o)#z|YLxGCQ)zJ)eos65t6D9Z*!Q;X=e{8;zH4{Yb5% zA-tkOfz6SW$IXWNL8*y^a~sr;v+20w66j559*Cz#yr6OY-68T36|BJp(RKqVME6vA zfe@yc@nyyk?1jJf0d1NB(7ccAiEYCoq%K5Zoyfy_y%jeo{8xDre?KpNKa|F2ZUcf~ zKWq@mWz73}rY-@p$}{jgPERWcPAYnLuMr7;+?g(BYq>Ug$I-%&6yy^lC_ zz)gHz%g5EzAi0Uj@qAPKz)x_|Srik8{V2;O>wI#J!oWQ27xIPXP>nRk6jSl?S7;Tz z*~VjCTiepR!UHTtM1S#Ee44L6eBpz? z%K|7eDN*_j*zv2)h^pQX$!}oJ7Z)m$?vwyW&wr(IyI&Q;ZXyPNfE&}wLdD55WGH}E zx=sXImOy)rO*j=-_*k7#1b&Y>+WzjNkp&o9=a-_VbSo)b0_CQT{}Am(twe#A&y$+J zXU;Obd7sn?wQ`Bz0fV7%#SGxjXR>(dlLr8Jc>q6-@PWH?D;vKs6pC6S<){nM*H!4s zAGklE7LNLtSA2Jw40Wj_DUKeAR)FFv17!Jik0W-@n zeOmqG-v}hAEEg1|30DHJo04DKO;tP(Pq%g?rj3%1@PQxx33L0NXY@;3>6uM;2;yy< zKm0%^9O(z^2L>zty$#%sknx#IH}#Z?ZYsnn_^&$J8z7KWn6ZJ!OBuJ*6)+^XJ{r(v z+-j-_j$j2xdEn|m-4IJJa3XC?GM#LzG6Npi9$mCV z-||*hd{d2QBb6a`4n>~h7QZGwGg<-|4^y+D?kW-eMyZY^ScFgfU56+RgHWS1n_~Hm zqAV@05I?pVO2G$vaa%tfkO;v6iiyloXxY43oaZk=U3ss`|8mil{z;ZG684gI#3yj} zKmH?tAPcnx&YkqT(k>Re;aG9fDIg_ zRj}v8j(#{d(hm*y_a0|_;s0H#%OuX2v|{H2J5B@xhd*H(KXHu<^IYY#&f6{8Y}yj= zo1veq$w*C8qlHKmH*=Sa&=zu@@S=?C27UiT;VH#ow|tJc;Je)i;`HiYtq59?Z(y2uapTbZ>AD_`7xaau?8AD+3I&Y2U7YD#%4F%C$}rfSfs8~ zm7u1*C>Y}Vax=5t_J?-fH?V|O{tX|HBQaSn1z&e%Gxb;*04o1|n~$}4|JD_2TW<*; z8khjqrtU_c_4Ga-&t^w0a|qJ|wfmQ*J%y@^M>i5h48jtB;m~eR`QRb=3$f-?VZcb# zx4Px0&WS3kTOc5?*s?fCbuf(DsIsr_Kvt;|N64Hzg(STmcf1t5XypHoBdZn?F|Mgt zin;o{1-gGT7{6XOW@%oR2QdX5y(KW1A*N$qKl{hp!7x)yhW4n5l z$+=mOdLhLMUDGNc`zP_ZNdO`#3$@z&=>x?PC|ncxl_n|7@`~jLoI1NFKh9?Cpbk$@ z23sZ4sK30TMk)JW^sKi_1haOpcsc2xbR!8Cg_&!&GY|U zBS4bU#4c%~riPn`sV%y)`0cIecNzosFl1g)xTpA zDLX0D>)5-!4Z(=6>=Uya<6M&2;He&T4m_Rd@&f74$F(kMCP zl_;LkGMDHkh|*#F%5A|@CIFv`J(&QoSwuV-Gh)9)bBzHi$4t{F2U%KQ0V=O*o(AAW zzus&{?S~Q_Raz0_(bZ( zAU}D6avK4NeeX$C3$g!9{e+e+_CLE_VqEe&46Qw`KM(yh3FIUgIwwort10O$FS(|t zN7_AK2JT!QuD)6M7c4|gK2hHU>miVY)l`Ds*As%01fq`2Mf|5c>?0beq0e;IcrjL| zmDBk->4O8U9MfP$ZQ=UO9>{XqEb);cCF?D>28j?n&%WC0Rv*iE$4b!S$9|?^Q?=c67?yx{z_^mdVdg59Ji z(N%(JwhPaG#k5+t;I_T#WG z*U5J53s+JF@gkY8Tr|2QR*m{19U3EW+z%$5w3-kJ_fznIu5nnB^a@T5MP`6CzZ8FY z)nt-L(LE`C78v8CfDnuk#^J3J+ ziIy6)HHqsfT`*bVwmO(D+DmTNxqc(30n^A?JxLu>Fd1hXE(7SMJO3IZW zRc8ZiN8%SgSbBPcghlFFnf9A{0TV%g%BN#JiG{3d=18WpsKeJ>)AeL(MG79v0&{Qw zcmHo}GaJNn=c_9a?rhC9Lf*4ttsvZ?uSS0~R%fDe?-FSEuImoe2ce z=ggK>|I8Ir*U(V*x+r5CVn34~`uEyWfYg#eMml z`2B2dX3#&#RhO7}Fw=__0zNeDyu~3->Gs_VY4o020LUKZHV_iKKeH;9cb#wXPim&t zmX0fQU}*Kne*)n16Q&)ehJ|%@q0J*7yw`dyZPif>i1vSvycZg^`yPDy5$q)m?bAZV zVid%CGYyz;FXY3^E|YbZdXYc$Ur>J;s-K2CwHn7h%EcFO#1hEb*dOX$Cs5clfVE8>(2;@&4ygsj4+9o#8TO4Mufb z79k3_b{-Zai138a8sSDM@8sjsv7-6BCe@;dEeiJ$#`%Dqcf6HEY0?ZlS?KILLI?E8oui*ksw<`eEX2s4AK^Ck~axU6X1uXvURCFYp` zO_Dshf~vcdq_**gG$%G%@-Ts0k7i!T-X@{Cpz8L2Lc88auYA4atk z6Y&2s9|F1n&j3T<<3#p+c~KoL*63X%K#aL|_D11%#P2R$Z86@(Yo-;fWNGT%IB1K0 z7duV#tB9=8SJAT8(J4z3w)tkRA+$4CcpaDPVL!6Ff7zJcJG}Yyk^=O)jftu-qn)@&*G-T9=JH~l z<#hU)jmhflxW=lmOWT*~Ta$5Yl=UrTDaZVxZd_x)tcI^7PU{=+AF|SpMe)?zri9zK9Q(uO%>^Go}!q z7T}KhPlmOyH54NHhF78Y?gk~GW_73*u6?Z3EC=agBO`Ey z*<<6{<|IjK3J-+3lBY^=k^zd(XyW6Dt=IJkj?`$jsPU3^w+I%(} zCklYhLB?+ySysCi#Xb)(QeKdBG1CPi^=#9g zuJ$tabqyo09xSWp_Eb+w#V0nYHf-d*DA^rB90v?9G`}C`qN@?BL{|)XUT+d@ujqTp zJ#&(1oEw&dVhh!A`yK89ucCD&?}5o#L6u5R%ryl!k~M7-#qL+`l5}=O3gDk@R{zScFwS^swFh zNdR&Anqy8EdcWMuD{{V5lBDYmL@%ZPBTyj!H@G%UBN+BL6nR*5g)wBN zJX0xIQBStvdow<^)*wHZ?isNo+sCSzA27?CM?G>UEJpPYGp;|772Pr6{3%<_4o-cj z9&zknQIGSDP=a4ou!GL7BTVm_dS#r7`{GuCB-V^bAV>)WZ(0i5)(b^sD6m05Ly~s~ zc+cCxJb&<^bribOeV_2?_8k1fCtisj`0eGaD8qA0`%Fp^7CxbQc)}y}3(x7`@oibm zMiMvoFyKM56kz9f%E!fR^Y4Bl;=kC}jv(ODuH}5sd+E8!MD+p1F@Wp6i3edSmkUY2 zJ<6G`yju8U6#^wmPZ9NlJx(FZ^V#V}nf3a)OFe|+rACCk6daJ|T??M*p>oAOlZVJr z^kGzD(j~k#t-cIxJ4CWahvz`%X%J6k8jOl?{W)PhiPF+Q-dQHtvAH7Z_y@@y=ZDLttJCY!b|zf;S-`Y1?1BVH#H?Nuc* zY0^f(so?=%1Il7iwfT{b3sdyz6B?VLR;KvH2W1^nV z?<4ZQOKLs4`eV-R4ml`5yTv(0hN{C<+S8u)6KG^J+3x&hthRZ0p7>QGzf2ve;eZ3- zgH2U=u~(Jvy@^(6lR0k33+D4}8U*tA{tlp1=G0rB%ZeQmg7CES4F^Nx9dMKHYnpn- zU$aJ6V@WYx?9KJEUQw?trwrqP47w*?>W{*^esn8#B&BD)UC*IqYt$-&W$z$&LLf#j zMd^i=(HPCNcU*3F)E8(@bJ4_9?w+KSxyo;d!@rel>Z>P#AbcdQXYtbRBHTYZ+ZW&l zche74`*uZr?(2?1;G^-$KnBVwsF=A%2UlQPK)|8o;FIeM>>{4nzg*xIYoteu>Dnko z_rVHGcnQra@wo9-yB;sSmOkrP>${-Z;kTif8CTE8H>bqG1u1chIl%A;73|;Src=|0 zW4b~5#t&^!h4wqtFX+AcgitROGy@uGvro-_si|!#DV74pM?1chBVgV~lhjLjgB#`| zb@~OuawjT5b+_M#Q(Wmrnaq{_GQSNJa8H-h`P7Qn=`w2kF{;*{e`m6C z!g>A$?X)cQR1-n&Iv$J1{f2%s&RBubUrABv4GXJj17@%;c(flhV9O#Ut}S>7P;mGv zQfNXX5G!z$dY0o}Xyi86X;1!MdDY{2yHD}+*m} z&wi=R1h2dn+wROaw(%$Zz@!&mngtXwQiyugt(N8;I!6!M_;|<+M=kRf*xjY$$hish zF_O^Itoo6;$X_7DUc+k$qr|iSPTozKC+;apz|9rFH}^&N_1hx8@d?mLOUS%cO6R>L zxxfkz2JkNNwAcW5@T)~}*vGm_r)0d`D{ch~cRH?2`uaf6*F7m68!cS=SduW*m6zOt z(We6c_!3yjvHmyS2Yk5%5SM*0mY!1Zh-PR1U8z`IIP<3Cx$oB<8i-xKJsSdQ-rf3+ zv-kru%X0wPFE7pnmWoGlDa$o&u(*`fo(Lu3?jnI+Sqfmiucs;)2Q!6PmL;Fco%&3C z3eiNQA4yc;5tfI%Z6{hAzC?{*PdCDq{R`S(R8GLQtXX$A9jSxr>Cf0!(Y`^bcZJ3W$b%4d zme>{*u;8XwX0>w?e)`l`;^(LQ6gUcMp~g1ZZ}{E?pH$3s?ke}BQR4_%3q!u}1P@ol zO|1Qh;${=wj*I^-2nY^R&QsHyt^L4h+)3=bb0hQ+Sj2wtK%R_e@qQliF#XQqv*gE# z5?%2^I>2KpI0MV6mpyPlxpbE#Sm6S1cEEA^D@QGBwtM6BKg~w9gsaWV6nYWJM`Hk^ z6mm)pkJ2OKo2=SGWYFAq%1xB~S*~5v~IE@ImQ~*C^J;w*8Y8GoUo408Sd zUGW5(@t!<$y>qA17wmDi+uJtTsD;^ERV!XlrSHPJQKA|%puf!QKv*_jku!OW=#04L zrIN=rBBjbWZvu#6d;U+FUamTsVGKY?@uyjdmN)Ze)O!N3(1gCLBDhWX^2XOyR%7SM zMa4ig{*DM8iclLj+=3}XB#GjTU}9k@j5W>VKvSoz%$ad|WZ?G3Bn;^sde`uBE3&i~ zpyMwS@$;WzSJj#Ft!L21)hiFmosib9y|{h~On(qDI(+sOVu~JS*xQ4K24qk_{i9&` z77T!jkbn=ob{NtJGO3gl)X_fi_tG|Nha-62w0f!kmlR}awR|)H%XUs7iy? zmAx~jWj7jNQHUEQNeP|L;ePZ$mc|}QA2e`%*bS)JNEi85>pb#iw)hJeQoD6CDSUG2 zE$PES?$ybiG7r#-)&DwKr3M;>593~{IsYL7gxF`UIe+f)d3@UTI04^|n(4dE2Kg>s!mhBb~OGFYD|?CU`Pgsms?PA{okO=kZZ zlaQ(0*bfSAYmXPG5EKSJ`3f2YgrUY4DqK={qU{KE_XA zUC1MHk!-vnJ%0%eK8V4fNR)$#hPqS@*E+O-J!gmoL@qzu9s7d&JSy@;N8m2a;3+zzam7pcyjT=F(p2uhwPKX*mYJX7k?ER3F7V?ubic-r{(Gz>w(Z?R3nz ziV+n0m2*d*%WxGiPW3h*fa$j|;Dezzl&4H>go4RSif8&)c)kSKC=YDCP45-&g#-j< z?R-Ybr4;=NwMIYJ=JIF0kNwR%#}29Nu5~d8-4+7|Pzny7;Z67s3#Psn@k@$G;Vdi{ zcRek#6Mkg28lm#Th!)u~G!)Mvk{i+O+rME}fGf1?#{>t+<;k@&45q9E&vs5vDItq7 zoPoxd!tt{Ek0d{b!aaAPzmNc2iwnh|d8D2ZFqw;&NS}?T_9UqMoBO8>eVxgkuh-|s zzt9mF2QyNBr~`pt>t1uoU+cc)3O+?U8q(5V^-n%IQv)BU18&)v`5i(`Va>|%z;_gc zf-T8wQnK@O2m;aAEk8h5;JG94sIfnaXB~;0O9*c1LI!@qk<<_F!p1*gLO^V*UEh0W zi8M1uyNNQc#x7rxNq~yd5A>qcW5hcmx+&eUKsHm|;mE5Qkx28Sl7$J6>A&&Cp{=!L zG~)^y#*Vx8tSh zLdcro-0)(LD*?87#+`H8oRDR3A&nUq^Ikvm(8ht#1nZV?n8oBZ39Gk$x~ zN0Ly+fFPRQ8)TQk@Vg0^PAYzb`q#K_S^B!FJX<}R%`_x7RHq+k@ z?UGAOQ^A(XvOt+*dMIsR)w4nkbskvRSL8v(Pz=yYCrh#PLp_sX^4}cbcos?)qYWC0 zJSSc!m~l((QK_F8$wZs72?+2Q4lj`_9h7?^@j|>|!+IKT$la0-V?fS~_l2*t#0@7k z0jG!_>Eph$%UG2vS;)>^X>e8a1#pFMv5SKH_KrRQeHn5h{CxdI7x zPh(uCo-kBBr?J&xKY7bN9Qvk3;qn0|(WHg^-5*ZC-4Mv@DBJk)3!mN zF8$9Y#bE+~iN$!e;%n$^bMVTRz_TX|bS=qys&oPG#h4OJ>0EI=j&$o%YLM~8aoWOm-tpS`JqLoEP5k~ zi<@OH$@y0H6Dx$XFQhI|`!w0~{XxBQQ?PHt6t(3k>s& z>(dw(n3Mq@m_mOES(TG`I2_AN78Y_tB7-P_+$TNB<$Am4FZ~_=*)itVVT8iU!qM03 zT+&MEloDZ6e{60tGgLp+K-s?uuyhb|G(-HGX&9bLfffZzw1mKR&7C*$;yOhyUm4sJNNN>d^DafnAxyvZ4@iOiLJsfXlB958%jYtjB8F>F!t*2$GNLLVlfR^+F;i? zae6~0}+oT(jDmuKus+fhW{ni_H!>a@o zZ=zv1Oyy*=MHb@XA=WQ-xD{?Di=ygcAR!_8=l^E7f6m*z`J%}&1r^oGbHwSj;@P3g zzUa^S^>3yt^9uJ%_8HNZ6qRc?mngAPL(%mU_21j51rd?D=#fa=5%MyL!a(G9j~YQ~ za&>55yJ$^y>5yp?0a+W!j_XE84Tihegdn?>*CrU7()NukpnJ+Q1e+wrjt z9Bfsglq8p*Xc$qy&swoCF#t2nFE72#N0qUDjdh1$x5Gtu|Ak~}>nGR_8<++bvp+Er z(Rj_04`ysf9V)+BX`zEfo`|%gFogjGp0On6XgzBCb>KoIyV1ZT14bVKH+KlqbY*I;oBy3);xd`1Z=Z_&c%7?tdHt^atTe}q-=j0nR7t#| zq)ARq6L;1Dj*DwIKoyio6(m_|PG?QqlFRYO(IyHdJ{y;n*KiIzEsmPG{Ob4ld~-!S z#Sj=_t~Wx*Xa^YY%DWb2M|lydf`x=eWBU-c`vS|V{B}V;?p!*q7qk4bq z*a-1#tuT|zW^MRw@M7FiW5Oi~J$FAQM%BiWE18y*8aUCv(DF#mvyVmhB$g)OUW$ zys|#P@-FY{!j;<(1`ybLdd+?kXt2{}VE&Th-#Qh&@}Vzu&7Jq|v%{I9bc;$&BG3DP z&lH6*rmbJa?`J=yJ=VR1Ni+~Lset9CH;3Ywb0turvG{0}vTGq*anF)c^k+Dgl!RI% z*nw;QrjzXXZ}F!72kD;Ax>AAvRaYgOl68_q(jxUci3TE%ruBc)R0<&wnO@2_ld^As zS;>AYuXBIlOT5(nh-1dyR8D0B!)>hP1PV7e$|IlF3q4e@?zY9EG86oVDB9uRHyTZQ zwj*IH?%2mib^&;#O%MbwJJX#Di*!G^R&iG@{r)aNzyG?2ZC!Y^1G7M0%;8@atI3jh zzwl3soEc17>aEa0HWWPn0nrKPtW#wOEU%H#U}p0G12SK^;vii>oD*)yPdA<{D2U67+z~f&nETv@BhD0n-AP&yh?5{H36)u~tH4Irg{&sqo zq(GGKp*Dnbt<8`te95Jic3g)UC3!EFG@IGIy5^SD44oWMjo+)x#d{qJ0=H60dWfwg zO{MueVj-T%(cOUQ+QNvt@l5mg<`tsCLUbCHYU-L3M48aPEWf!?#~zVVA_!1E*6uM!*S{#!i)N~CX#Ii`FLpCRUm^U-`BN4sc9_`h~yN9o}cbTIR#roGq?X*m@&=7UBB ziIdUK&RVJ?4uAerkn{Kv+6ulv$qx9tPR)wLs-t`*USngXauxg~ZgMt`?mfRQcVj^ifDoqpr# z<6-Ud*O~7lRo zn&ZKnr6VTUT=L=_?83<|4DIdXgO~7d;hs8*4Nh?iWi54M>8%t*oeH3>Uc-T_amcDC zU$eksfK~7yi!BhZ{`;#_x4$=yGPU=k^+7KItG7Oh2G_p^@$Q^|Vb!Gg)%yEP|CPsS z@*9>_e%zmkGU)7-Ze~do$+S~=Pw2i5Geqv}Mxh-Zw@Gg}d8enC`*NOE9!6Vb{huX{ zp&Kh?bvSw$K1lJUllI_jH=TaG`P-eXr;NiN)c%1-%IUMK=RHmjX&2T{U;R5vpsne5 z!RgWX@<-s*lrLq;!O`&LX+E?ScnRh56Fb8B>(>kq`p5DP*RffAy8?+b|e?5(s) z>{ots-uF0L9=0j&J*cx-vaYgWyg-+ZHDjP_ir{l;FVpo7(^TCl@$v;_=$4}zEYbG! zgNFHh{Mz?Fyr_vc2a;JI?+aa0ftiju7pQ@;;`k?{z|WxMoz*QQh;IzQ39R$Zg(=YF ztWBFnO#iCXBlOv9Iu>kfR5B@^hd=GPSb{`viIsk-~P zL3S>eU543aI-x|S>QW^?L`3sI5*0$9+XRowf11CY8}SmwWTjmU2`#_5u5Cu>H46 zN#vJK`}1U&2+sMVD_H$#Uvs5oOK&GC7TO(WJ5-#NCEbm@Ffjq&R7 zfS0|qI9}aZJ?{5F3gEi|Z(xI@x|B7&WLcaEb@yheEot&tXgLXYKOa)LdlNr*2ZVJr=$D(6>w}Y`ZbsA0$)>fQ|$X_M|E&gNA9^?h>h0YZD*FMr?ku$h%%zU-g;-OjG*I=XbX z!R<=Ys-l-ij7KU^iONBqlPzS!1wnZI8)Whn+I(EiC*^V?-xhV`=zNDO*iq+C)5}z} zq4ZncUiv}%@pO`cL(hCig`%)e^OuA@DTX@VlC#v{8hAmJOd|I@vgYK zVbf(5zM&k!)@#zo;(sfswbMvw8E_U$UH>ILl>4wIkvYI6yk8N{mGk*y9<0^r0DM?q zpXhd3tB2Y6Un4Fu($>zhA=xGp#ZGGRE7WtB-JcTneDu~Drp5&Yfs6lB%i_~`Qq}|g zKZ?#fp6dUL<9E4(i)&x|8X?3rip*;(BO^%}7a2+R7iHahU3;snY>AdFqPW+Vt$`v} zW+gj&{O<41&wua7=ks~L&w0PjInUQ6Mq{;0hb1EUnk^T>kGsp=O%g&bbx5kB0UI)w z8T64`!)JAJfLkQ|fm?m^07+q#Y4w#}Ex57Kfv<~C>J4NgfV-~^(s6B?5Y*c)yax8K z&mMQO=W!{_X}cNf5Z0>|e3yMv_JNP|)ZrJXSCoCg)LH zx%dC@yMCqNzC8rc*T*i93!LVXd&Qw+MfoW9l(+aVdU{G`EIh+G-68mih&tk!uQ^Fx z6;|OFzoE42MnO+^PP=e)=hd1YC;R03U1Pq1DZd*Z0vNlgTveh2sorKVMF zzJ2C?Rugx`6GwzF5+HSRHP5fe`k*Xu-ZeJyQZpfyJsVmtmlN}K|5ft15vK2d1hgM; zT3ncsdl^%&X^NhnE|;De=5ou;GexY|y%$5I#!JD{Y#lKApH9D9LH;lY`xTrzuchw2 zdS4wB6-qHl3Da*^h#1WJktI7TYj`XQTrEJ|2bKXe! z_GG{ks&nmXO7dH`H^{=;qFN`i>P$U^&6yDKehs8!DpDU6cWR=v!S9;8%S4rq_t(56 zb$+E=K*-m~e>k*8gLh-R)miv?6=MG#$7CP@5#_z|pG3Y7TD%ZXM85?AV1iH2vF8;_ zBGa$<$-?yp@fhUZk^G+o%#O%fjE&GrO_pk@iuoxi4?sY5i2;Ps}P~0o)PWn%9_4So@xvYgh zs1=r3@AaQ^5(&aw@{tsGM?A%;0HzBc6t{R@cXyVE@hS{{#($|=2-*ROP5(n-0zIEZ za-rd$)ZdHB)DsTgwGPH!`>Yt?Kg8`wwiQsV4a7pF9&#X$B}V6^el}GcoKPZutDewAlG7IK(R*tfkw1U7CG9cr2E*N6qod6dEb^8MV2pifaa&6&EG;fTXV)s; z|JDL)D6D(ZiKJ~gw8nts{<$-vL#2 z{Z&v(@B>C%j|_WkZ4ky=6s`4U$V>Dw_g(!e+}?ObQQcrNaiuHD>rTvM(19q$gh*gC z=N#G_b@<+isea6PV;VbGBn8m`Yo(TE-MdKYpN@qx?oV9gfuHqN`+^w~!Z}}o=DQsf zH!}VyjDTk{(s;u3*Fvt(%1i8632eX2XRVJA+>eh4odyL%CxvC}zWixJ#o>%}``*JS zZKIx))T_Nd!w9$Q&bU9Ha0d^2TZU8k6(wUU!q`}@)$2Q`^QnQakMhg3z0BD7{?{on zS#>HM+W-B5;%eCL$iz5%h5D@GShK^&WAJAmj#>CLfpT(2Y z0PWsU>z?W&V8`T>I=LLsh7QvxERaWfjrlKTEz~|FsG}nHVfRJi`mw_# zj9enc(q9#wlQ;Bozx(l0A1>s3%c_?mK9o)T+{~|YsFCjt%eFpXg%W{K+|{m z$_nUc=gr;|8#DIE+D8{0m2ns&1Ff5#oW%A!ASJHh6#ULAV$I~i*DlahEC68icNz3s z0*m8#sAJ54KoWDP&+V%jd;kDJK>>}f5f0ROq#UQUD!}T$Rt@ zq4h~=$IAedD!uW9k~xDPlFnE?9Pk{Zr3ES1io*&2lb{JNBQS=NQK8LAQ|tfL*Htu* zu3H*>fWw#mgkR>^Y3QhDqu_yM%o?(HWO$2B`+w=|^H=YSey3Lv)D6wNPJMELUW)l- z-ED`?^-s6FuL7Nlm3Ya|MlDBGs`m}NCr(=OV-B2eM5=z3`@61440IRZpZKrK|7BaU zPnJUa9aa8G;m0!5yMopeLD_rc8tR{|qNJFb!)w5OwY6H5^1V3|D7=If1eFZ{}wVNlP5MNAm`r`KoMmk#^Pv=FB z#ypp2?AVxXhjietZOw^eTVU{UxX;A5C#-?v7e4(B4jc*WjCM<3@74O$X&ABC!>f2V z^^T7u$dl#K!sqnypQxK6t5^BE`iH<4$fRsKcqBstzuxF0+)N4`cFmgMW(uO$;ObK{Lbr68Vu#TpEv{%FdZbi+; zlO0)EiC8jI;jzeJ3-c{08|SvS=Q<}&+RB*cB)(of`ZfzCr1BFf4=k=Gt1LaYT^&g1 z6ZzXkDk&2m44bg4hQ?k#PJ1;`vTfuKh}RuO@2+bRau~7ohi@=a$CtBeaL?AmgQpIJb7^OOqx%Eh(2fi}>FCH~c+f zaKkoMThDq*^&c!nuOW)zFd2}EHuQx9P!cr`&wgBmPIfVYp4r#1!l|q)ynH62)7#hF zo3)IA(dxSadXj7JekZh{H*m5%!_U4Qo6qn4X3i5Q+U;*DED5M(z)3yYCGL#-slykR z-@SeuHO8P@y`}zMIL8yr4}!uWlg=;W7S{W0Sd)QX>(_AY(o@+&teya>INvU5%559v zX@E0-CywdzrqK{3mngnoU_k##@!KxI_m_?+9i|s>J+V*=-0PP0Mf1ZI#=-yxP?ZTO zTg$Sn3>i5V=baT>OSw)q~AI!--WdklFptU&qH??cfr`0#E0C@=Ge216(fA5VO11=nvEe?`H- zG$ubv#jnr}@9T+r0vOZTk{3LX4`tupgOL`L4|_bLQyQGl4L@|Pdw0H_e1R~WpMy#q zci0>(aZr%Ar-Fwi{AP8m#+2U# zZ1ft6Rq2bB&#nEbPugCqe+qnPH<<^{MGFZ9`K~aBe)5Np_SO*rlvM_zXwjoPC?6uI zL(=pV_9!PE?SO9a%H^@*sp@@^w2C@qe8mIO9C#ZkM#;U;8()~@Y!jU2cY!@1^#fe* z(t`r<%hX0E=D~k%JbjLSV0J0|dxYFF5wNTJWU)$h0gU6La_@LgrV<5j(G@YDB)5PRdFBSoK_iJ1y<>>|pK*=92a=FUA?;wcKKvmh;XG`9T$sbE=w@66mAX$%S;x|>uH0NNYK8MEj% z>!Z3m@95A|e3?fXnlmOpusZ|r6yFdrzVhTxQv36Az`KH)k&quR{4Y$kyZ$JT@t&y1 z<+!Va&Z2*`NI+|pgA;KWW}2AZBvF2Pi=UPj=rHl_o*HOR-(+DV?L-3a$4u<9^Rrkr1nQuhmTX{A9!}5DGoTojIi!C@GqjRXqMpRog=897aAtw_(lw@GV#n4-B zv`r|WQ~!wo^rN{|6ly#NDN5|(&|~#DC62>`UBA}80qNO&gPF8f&lR|wgFeYp@Y9$b zK0!{k4bJDE<4$Wk6%!LRxFLa)KmF1);zoSW!Jx2HAxbE7W#3(vzy>Mrwtd`-9Q~E? z(`iS%rfqOKGsxdCf-VFnQVN3ra8Y?}funHs9 z$_4h9khi&zu2)1X^Z!0@mSy?&T%F|u0hswVIRo;h14k>giQ&Hcu$sNhY2{$1!VZhJ z0J5$>nYi4fNlsk*(MlzL0-w|58m*;TK#s%cH^cOf0GyO-t~I# zx2Iu&IJna1cWtrwgNUUP8!h7=9CO$bOVey^qR9*EPQ}QJQotRX}bG z1DeF3eJYJS`g$c?#_sq9t=;mdlyjg2LuJy8{=f0To-HrXL}i}*%{DG+O{Yw=CE=*I zfW$WG>7|xj^K_2l&MGC`31+c{=?)xROg+LL8W=y^T}FAHtJe2O8g?=_KdxLL!9rI~ z{;P;{IAALf1`)e-qCZ$LMO{1l7_KUPSw>ZX!gq>$`&L24F<1Zpfp!&VJYA+>KSddx z3-Q5cRU*xb*{;=;|GYFN)8-LdXK6Hiov$%-wei!cZ&~btFSs!rj@BZ~9XlZqZ4)uQ z5i8-G$NQ(tlnd_($N$m8$X$%^2UXC5*@`F1Q(|d<6gt2ouLrJVk9rvx@EC44SVEJ!J;cH>byC$NB=-O79XPfo8U(s%A~;Kx6NtVJb09!$=2 zd&7&z5R*W~oF;;GBK|zp3*{(nrWo!_RheVzkqKk7hXYdz{*|8FO814bxKW0SN;L=k zFDDp07^94L#; z6QWKJpv46zI{xi(PX83>RicnLO=T}(-my{Y1^`RK2rtGG<>wKMy`giQXu5WtH=vbY z=P{6Fh!>NhW)i!thdbzNFeSIk?Z`)iE)5_IkBf!>eu1Qqcjy$iQILN>^+r9lYm-FN zir9otFq7MW<$KLuo*t=m%Po5yu?Nu&_vBETW&&$Y{*XAS9I?yECyyFb1b_xZMTig13H3||lkZ^R40>?VX@ooY=)1R(%L z@lmqP-hY7-^BkikY~IM4SjwTC*g-?Iqr`)hM^bFZ&5v8uqsUJ?r)Xdu))6;>VTqa! zw$I~0tF!Js6S?%JR+U_Vt%4Mn|BB}k#qYv3kJAXf3^9>T!&GRGwN0BS#O1Cb7Q(a` z)dQ4)lfN20G2=Fhu`It$q=?%n)?nt)=a7Cs0GEzKOcdjJ(%%Gu>!pK4w)fRJoTOJT zT2LBu*}M<#WmVkc*daW<*+7U{Ci`Wl0?$;leW8XctouqjX|#&nY8Yfo><%gaeD#Dq zMi!)LxTWnD2fV+=nh&Kw88Q^`O>mcDvi z@-hqXMafiwUtjM@c^-YzZS;-Ctv0gm6E{UgnDg;~?D|E?oaQG(w-!kjk9S{~&&h-x zJy4wjtpH zbBm;c_l;71A}v16PAYh;g`BfVP2yo|RTsp(t1>_P+&y*!^^SC8rgTgRyGx%-MyH4U z-dbcYl?geh3p{DP4rK@!hmp=> zaY0Fk^9W4qVI=ib6!`2rIGT*xyZoiCq7>?Pm%(UX<`E8gYW|G+i_59*a-KCZxJD{?>neGxL@W_ zwY_Z7JV!mMIyuP2u%1zG#>gOuLp(Pi&dB{yu{zw^fiQYZ|2<1kALy5>4Fi?2q~{P8 zc@S4D9Mck~SvHW4N=JF{2;VKcy0^YUh3sM}1d&HllM}Ge5ju0{U`zr9sE&Gw8HaV{Y_S2dlZT*nbZOebs%MF|; zQ|0g^eP`zAgOPJQ@D!(;?F|Hl=;LxJbn9PF@0wqt+1i+$C#O#J(Hy|*aOHtLZ-&oe z>##Z20r96eyd5hQRC+v3%kg5Z$FB|fUC(}IHXDl!Aa%Q~ygOswO217bm! z9{gJ482HxM@***f&PCAwG|*w)eh1(x1lZT7Hs4+j^IPJVc11}&{zccvQ|_OK-kgS- zg5oPsL#O}k!nnk-eQY2XmUom7aZI?$M`ziCtR~$!a|aEc;VT$tdHxPGCq(dItL?@| zT~Oaw`Q^Wg@(psLp@aJ*B9KYqGmdWh0 zonSxP`7P}~9jdx+$NX5U;nK(_mZRyZr}y!E)Z`@Y&M771JOir5vZQHeJnVas z2QdfBdSA-*P8554OM|X?ytPjpFO%%Sd2i>F?Wig@Vo=G0i0jaY$=)Kz2N+Nfon=%Z zjcw!?U!r^&PR~2K!fi|=5;Ru*HY{+#A;9chig)b8>l^V4(YQBk@g-tk8zNB6Y0Iu* zxo~%SFUtM3jk2?v?62HFErLWU7{NXTz40kz>l&8NqDFeFEiNg#@PmaJe)BE}hDD0@ z3)+7#rWwCmqdTo|L9`*^_4LwAVnq08&enGi7O#`ofKj7f!ZX4OG*So>K|G$RoFd{G z!VN*z=W`Ugqj6T$S$4B@{uRY208KGM>Rwc5nZ`#5HMUJW;KYhS94mHJjhCvyDxSKD zPJhay*`D&L)6D6DU#GMngQ~ zt_0VtZ%I!Y-JiumY7gaa?LP)tn4*kf@nNLP_(TKCE6@*8(vn||pZqn3{0WQRQ?6n5 z`R9yiVj(Of+;vWY0QSuC$@F6!@N}LZ_9FT%~SHM6u!tOM-SQkRMv3i2bHsst@}$(QWy=Z{8$Y z?c@$F-^aV;TjD~ZUo<+MINv`0hd z$=~AkHhI5hEd57eXQ*`P1{`td0u?fLuUwFICMfWVV=1TjamCTAKronm2P2>-%Mh5u zT6Y=P1!k_~X~wh;c)g^PTqtt~e-ALXjXKJblst}rDMu^s(-erH2prQUSe+q1AJihY z(CWsd{*yD#^J0MqdeC4cI{j2Z5Br&pl-Az6$FTVF} zD@QXJPVXCbLAv=C^Bcv3iJ z(0J@5!`7)%q)54Y!cE)jk+Z{Ggoz)Vz#LPY z0=-3_o+ask*A{iLZ6z>betd)ijI736V$dnIZO!6YGG=#U-48VHg3r_k<8B45hWc5- zEzqIoi|THAzWq*JmWD zGl%lDUDWXl5(_TTM_*-}{yx%FBp#Uu?KR)%6p+`mLQZ!&wZZ#MK}(@AV_A)&tLVM7+DVH?)X0{u29~j9Us#S~a5npq zwYFKou|V2CN67{58VWM<zH^CC-w15B$0v@!037|LsTsmeU9J`W@2|gwY3Fx zJvM-2;+^6N3gF3rx%jXg3?CA0B32Was_U&M)TauXmhd3Bs)2Bqw_GmyJ<-)j$)AJz zy-q;u;j=8Abnf^Rg^(*vB%V^BeE?=hE3br`OI@G5!tGL6eyX{C zT6{wsw0EgxiK=6>@d^z5%ZV~u{IYbYi+3h+K{NM&zz?F_w>C&Y3i35#7~wVdF+FYn zgksB0tAiX>qcQ9X17H$&1Sbb^36E=gzD{^4`M}J-T*2C0kjd|#95Tj^)3jnS+}ysW z>4Lpi*8`dAj_qg*IioN|3hWt?=FB5+#KkqQ0#EXBm+vb)Vfj_mf13-64!{9of?13? zl}~#wYy-0|iv+WvyL^mnLyR>L?@I=FiyJe6?re<4(@7}>YZ(wa>^q*7oPhZWIX!2s zNYsh`rr2Hy-uVsYCw<1W3uS{}%A)%X~z(wkLW{mT0qbpQ6xOV}p3 zEr}kKzSyr00IQzHynWicW|AEiX&5~7_Y?4cB>i(z{=xdEk28od-vIZray zjW^KSzLp=t;PLdUlqlD0qk3O&mxH8_I^P}v9rd(RhXGtwk68l5!8ve@vGB7_ z@P6x0a4rfuGlo*O)<4@O=rF;Ic_Wffpv=cTiPZS96yt*{Y0_deTpedN5` zadqJNfor+t*m&3AeKNOhu>iQ#v<+wZ-Id1o+R?M;Y{&KO*ORfJIH!z*=9JSr1j#Pf zTlqjMh4pG{U56T`6$JS4kD@vV=C<%8B95%>m4^(7g2(Dz;l%QF@Udz3tTOdLxHfng zsWjqGx7wY=AmiEOx~coWRwEcuQhp(d^7;!_jp%=GjrIcgVj#w3dV`rj}S7|>n#ZDoW`Yb|ZFKSwC-J6}ALEAIDE zOgnYsG=RohDtv?+JhLRtIwb&8d#oudr?gQ?qjo=;OiNq}lR%0d8COj$i)b;;K=w?Iq ze-no{PgU}l4NxvWj2Rm|(z)2g{P7c{Oz)l7~K|uj|We|h1 z3c9^z>&V`-nz=*#Gw?)~s7VJM6*){p`?;{mbv(X9Ha$i(1)N8ma?TIB^X$xH!g$XL zB{tTb3$1V-gvpHZ@Cgl(hxoo!5njs2vqL=TP5XkgyZ|*5A<$8h0_-u#XdX}8ce`4n zi_d&2D*W&Xv+l(tKdslQ#xBM<9CB5sWh_aIa(^1Gt6U}$4#O1}?W_CkTx;d=tfQeu zOUl~w;c7jArbuLbrDuws0;KCX(|qQ;>$YpPjot_M<0>NAPI~r(NcI_fCP<4KzrkI| zH+VbjDYjh=8EfX#MJ^Sk%8yQxOeR0H=ZwxEmqfpJxWo95o$m&i@I(vPBPVQD-Ck73 z&r9FxcpM+`N(dOdpeLZLpJ*7e7i*t2_1E#<>s$(G43b=e+(Z&YpgpDgX#^! zdz(bVYhu*s6&31Qh67owGRPQP^s$z$Kj zqI&*5e(j}jPE*$f`DU+S&`zI0xBJD}SgLUF6OdKsJ*lno?W(k~BIL#=dRj(KE&t1e z(AS$N3K}Yr?|*8EW5Man5z1wrH4x69+qRBDRi=1`LZTqlL~sBjYQ>=TxHM>hiHI8# zO=S)8F4~OoV@n0(!UwT*=)A3kqDgbn20xe*8>0^jz(0v7P5^LmmkH?ZMSp&>Y^z)6 zo`-Jjl=$z=*zB*jKlgEXq?(Ra6nmv{+*D;gmq|@EuiL_4&y@qG7~&u)>0C*#xY^ep zK%fKJPqU_uB1_gDNl@ou7}})po5nn4gWd%Mo^vLQHrNPaq+a$lT!X~Q=nX15Tktsx$LVIA^Nzq%CFVU4u2FPgQpOjO~jFoS%=v|XKcI2 zq08<)z4uUL(fsYj8V-&2IX+V<_EQiS&iF^%m~TShesuGa)J+2_o~7I};lW3`iy>}{ zVhU7n#?iCvSINXoROw6}v54*g1zL^Pr@mH)#uK!y)QQ@!g9}-OK&2gRb+{Y3m-fYe zQu-WLV72_Jc^J(7&v`$bowfCBWb(doXZ<^lZ5CKywB8e^V}%{&bX8@geZ zO8j{C;|&^mHH?s32 zn-uSrq9&qSdU!{P7B7vui6% zU^Yx;@omB{%;6<$0x}D?bKJd%df9U);hWFvfcv8llIVeG= zF8<_pTu0Ql-^ou@O>FxS@9Zk7Gu5?Chv)4I}*#(>!tXxjqF3DxVoe71McoQ`dkgP+bs`50#iV{!>}JhZkdbwd#>Jl!5xY z?ioi3_VeaVkFpLH%KXPP0PsE&F#XJjzh4-D+Lv;#DF)0?*tu4iFJZ6P<$9Tf&TB#D zNagZ5qEVE0Lbh5K?`S9JM9`cP&wd_R3a4Axb^LB#&tv63O!7h-!}p15vg@=-mzEU$ z*;GWBEJ6rS&%_J&pb=%GMxs)SmUmBe6E+Eq5kEYcoUS1K;ri-}jJ($(LN?T7@#M3J z!DHFf)8`Lnf@?`j5QQbN*S?8738u}LeO_&O|I6{!YIzt9SM}ec1bjhgZngzBFE3PU zyPRs7(O_z%3$;8$6D%Txv7zcKAIAub zf+Aet!B{L$Eb-w*c8S3&`3zPc*ilMvOFrvh3(|RyFT}HogFox(x@!4>zl#(%I)l~S z&vX9de%67BnP5;1%pS^FWZSQ`@#@j8+6{xWdl;<5T_%0$Wg6-$@V%_;_2@?t1<((i zN&)e)C6`<3S;c!_M~=$DYClm>%*NnJ92Sd<(;U|hy2lc6-D}H(muj?$z5aSO)x_Bh zN#ey-ZcJQi!xv7!e9fwKY0upkEZ6{DwQ6{w_|1K*b!-!F2FY)z%fj26ZPuhm@*eG! zo*!Cq~H-Dz1+pl1{*U^qa4ADYY zc}IRuV4){>61GUgY%l^piyjC-PCyI`>T7ML?@@uY>GyH%y!O5l0LBVxXVjb@czfvP zb>_=GT!+SS8LgA7Sj@LKc&V=iZjqOkcsW{DSN%4BK@Rd!s~_hn-+p&{L@413_0MYQ z1^Ur?nK|T97n8L!0j5f#F93a|#?e&*}oX9b0Ovku808X1NEn-tu>Qdt}ewyJT;ZE6dI zhe#H*+2dyJ-b|azMQk0ER_08dU-}jEvuX~%=dz!HD4va1R%^s`X>@8U8=fIJ-AlM$ z-$#xy#tM*TUp&q9DQ#gsF1hO!uNHlj-#rg}0)6<%=Qfh$yyIllY`7l{2UI;(z?qIN zPWw_zedJ-)ZUE45R{X$wm#cvRMxS4|0Oh`{{zGB!*g}nW!tyA#VySI1`~no&=&sCaF;th47y*&R6@NK)>H7oURxe+Mntmt#(ek+;u5=O6ImbBXQImq9!ohb$5SlCoNQunanuw>ThfC@ zE`d&GGLxJf z0zMB@6ruBRzp11p1!Kg2#~7n^3qSYu3dGPPx&2?V23|kUC1V1%lk7T4z!=<&;78#w zpB2o-Be3y9u8D7v4TC9FKVr*22r2@BM77w+h;6HF8PHC-sHjAS2WCevoVo@zl$m}H zJjUKUg$09l?at$}aD+lKN(wpppm<+bR~b_~@~tY41m^+=srcdA0kJR-Vq}!xuSmo!x_C~AnZj$4@EZ%|UxHp?HoioKSIzWCw~Kn#r25Sz zzWL8?snt~d!R7S3lH2L!|B2{+d`DA4F6p$<@_?}c1fvUBT((Lp5~1#mIJ{cVBmIN8 zpDPThFDt4FNLd*?`yH0~n8|%djOM@i&c92`jS!l>ijSYk>%Y8D8q)H4j|xqHo2rl6 zBccFL0MbezH}9LJhFaQ~=OwK=E=$uQ4y%jh%jV8RLfA7m3bG~@u;-UcNcx+t!AtXv zkJBi-O_T*|m^aSb@~AVSA)^&3sWeW+8`0J){w#8I%AQaCCG4Q*4{=E^D4HYDIPPO; zV^1HvFM7%?@BLPs+$X zvlwFpK-m%v(deiq{e&wyaWqJ)82iYeMxADU-7}(aQIraty&SUK?wZ`Q5^o7m74GV6 z5ZR6@&$@;?x)1-nmfcYT1VuA=9|+aWeNLk-@2E@nUr7 zEujp0y-Arq76PCXPE;)R%cs+HWM{_c8YKB|or! ztUA7h%Q}D8gmPQ84MxpXU$i7@j^%#eFFki6mt6krwIFj5e)sYN)_*`m+PP0~2>w6XJQMEs$|n<{Qk`-E4iRnvx-rmJMy20Nikk-Ajke*>^J29A)q7ti;@ z6&@HDOYT`^SAU_($vlaC^ohQE#w7gkgv;NV^ZqiSgAo;kd`6y)70JG$2ih9~1bmb# zz7&X@@nw{N2VfTlLKvRPsH*AuK%?R=ZrS_|_;BN8hf{~Pt}vUQAU77yrARcpDQ*J5 zVnphRwKHju7!`kl!>i-pKPGwW6a@o6eC6AUmAsw^wQE=!T{2+K2TQXG)USX`vONT7 zt|IA=K546@k0_Hc%YHlpmz`!&>?`zXsxrJgfs7N2ni5RTC=fvD0knr3<#Uu#)~N{@ zA~z`lJ)P&XbqTS&Go_%kQkRz5vNtE5-VAMXjeQU;>wLDViwvti_+oHdzp4%|;X|-; zWk%Tj7s#0xosLaOA*e*iMU8w=w{w_r#b4tfg3)_vZ(n)3W{34$T;s%C7W2 z8UYG!E3+;nE9FFpfB5GQJ}2HbBa3W-;+nv}piBIa4uH~N3teceZDZRv<ijthl(-_lg>EQs z>>>zh*(-2vsPRPwoeBcl?1%SV9jvP|ADWon8>JwSE`(Q!abh?w-su%V4fvU{yZ`lJ zxL4xI1Ep9^?`zioa=^71L=KWg{L(VNC}|!B`G(lpf-NQBjqHr`9GAExxEB|CAts=v$+OT zS|6y3*-=#vl6|fY4A1T1TlQ1AF`6w#*IbCploc)wD|dtPkvC#@z*{do^w50q+^3&d zU)A#l0D;a>o-Zcb&xolnJ*AP)9~}47)$v=$LZ-pPqcTYGLCcHUei8d|Yu~+Hpho@3 z(eoJF%L3_;bTk@`k0CCBZ^T5~XR}-sCr@6@!(((dg9@&*Q~bhOrl`z-?QzOp4%`U& zeiNP^N45m`B(*%|Q8R!?*}v_c;nyUuV}5y;ew}x4ukK7%HOix^PjB_tTE3@uyTJX! zUzR$3r7y+ayqpv|uV2NpXl6V9UI>|kH&6U@q`qzsR z$M-e8yIJa>M3u=Ak=o~StUvg{`jTC&=~0Rl`Sd*$bSPo{-Qqjn>7CW4e29G*sh2aq zU4%PUDl`^!%UO0C(ke;j;L!nubwEmzIC45`0h=}O2h>77mCsWA%uZ7Qk$sn-H&__H z+=lyFsXF(x0r8xoLe_c!w5#`DfgOi8dN>ROM1q(DE|i5ncl}v-InqW#DQZFz5b2o5P9#si!bTNy3mfnw+ZcDcp zei#=vT5hMR{8;vB9<4lrl0v?WL^E5m>lu3rwSLJDV+CCeX?s| ze{I9~yA3GCS&f&}{J^iSm?llk=gxgZ5k$>(YQEpx|1I>u%7ek= z-MuTuFIY~#ib*X%L$GD~LmYs1`@X|xWki;*bYE0QB4iN+dv@xs;$k?-F>CafiF$_< zt3qX!-M-e&z+XZ60*uEiC zv+G>u-J(|F0ZSE=itYFPK8&Z({R+->30W8C^g*KqZ!>?tTi>AUsuzzDF>1eAVzPKk z6=yB!XNgm4#Am&`;gY1(DAx=TR822~lV{n~Jss>6(%li8Hz&-~fe?iK^{qr$AZHiqP?+J$JfEI11nN~4P5{)yzX6dixmaYx1oH#cuCId*Z>A)rH zzYmhk_vm)xGgf^aK;ocX-U057pA)I@=@1fUGx_aGoVXBmasf$hEI6I3yaMX-DwDvEmvw)m*Ee;j>hXF;Phz&<9cCe@NP4RzCFTN8o?< z5#i3XNk9vLdLRGA8)z74t*kK&6Gu@Ug%=x%=j*mYai->=UbLo2L)26fee_XLWG1@u zV)EUUJf_|!SGC}u$*B2AiwYsY2~j0nFRo@Mp{oXr3GcG(JIWcZ{qaGTlU*{s@wz|X zN6*$f=@Nd;YA$bN?6sEA8T(p~5_j&d%!*IMyRU`NZ=kH{#(?>loDZ$n(7H3?vj|hu zF4HSi><9ksGIt-8EAfS_^5<`Cg9`6(l@rITITiJK#d<;yfzdV(a_+GyEXG`E`Aw;1 z?&D_;asBbE6V%oPIFsMK@G86j-*L>$-Q6MiEbPPK(L1Nxl3UeK^_(uYSsg3*kaKuS z)a$FDSXJXK*5U6A?=*oM&U>ycJY5^|5?%djc;V%yQT&NZ79=BZnearI42NIyi>k_p zsDTGpmTO@DDEtAU=vy~Jojqi!r%kX5NzzWNC}jpS8oAO6#}3gdrhc z|D&51yii19(#uTZp~=lgFPL2Js^W(Lff?6nj=apSbA&>Xe1qH{ISBH~eavGN<^Ibw z0Fga{0cz}J&Hv$kIsYWcRRD0lxlFU5d#v~`Q&z`sQht;BtWQ{2<4{Ai*ytO?X_j@f z(aEj`I2-__PmXd=Q%dvaqi!&1+b5f^CK=W^rB%_E(ViPdPh7k=fSaRT)azo07v0H| zHq_G4i0i}X!|%w5w=ogt**_s@Tbme#I~c(mtD25A>k18aHLP7}G#<9pOIW9SZ+WGP zHx*Wir~XB;QjXw)nsb{ECvQt^=7p+Fm&h_k+0iVc9unJeir}F6>}WgcO>!&t#ZA46 zy^s~#HN=h8`j?ex{Sa8Z5@o{A=Amr*x=OjQO>YP16fNbJc`^roPUi!bc%O6ZJvEWE zSqz~emH(jlh)}DE08bkq!y{uus89(V@oD5ubEL(%+zs+~^!Q1p+hTSs%sPwz^8JgIT9H%vIl)IA z*&LqfGe_BlF?slfe?CyZD{{ne%6u;2*3&{q_N;RY8FO}bfv1WHf$c{?tsfj6sqQ#Q z_{u4(tZL3?k{!l^x7fO46?rhw1Rzr(&$CXbxb`l?k|dEF+xPZL6J>mrK1TPd{eHIDT&;q-MvS?AW%gN z1oh1KZQ2;wyspD*m~fsmPhex$G$t$*o4nU~t1@6e?lP8w13Upir#Y(qf@1IhBp*5e z4Jzm*T^6}HI~iW88LE^cNxkJ3w@7v@G{qc??2L5@+y;wz6TJ*m`u&Ou^L1;RhSxpF|n@6t8n zE5Jr+Qm1e&{YM=Csd{;%-@!lF>A5)UDCSDJPCiF;leD!@7iH&1b^v6>0Ni>HU5;8= z!?ccfIVakt`S)Uj$_W3R2e}vcKh>7$P5GNGyM3Il`$-s-pZr#SGu6RqrqG|j`i61- zqVw;q{{fdkXunrSVb4@AF~AxJIS}gzj42#OYor?f`8{zQ#%_#-iMIrk0b#h51_=dw31-cc72ywcl}TTlAUFZR2U z&Co9Y(0e;0LB2gF0-GCwy<>SIf|EhO<_R5ygSh&O!?;laHd}~Wf+a=P90&a1JhTK; z3x*Iz1sew1?H~3N1vAI)FlG!U?#V7_k_m>bTpu|EGGFOefL2I&%}gNGZVgixas{)| zfA=E!l^>Hzi^%4lFb;oA`<&)$iI|smX_A7e-S7eUk;1E??b3hUV@TEFg`AL$aCkDn zpZA3xVV`Ti1j4B-(43tCQpDt`VPYVJ(yQ19@YDXh#Mt*|&Kr84Cd?oLda@bihdD}( zVE!*n3N>L2Vj$L68^JB1oNgs8>wE-kPb0!34wq{(Jukb9neEUNCICZfER6^Cu?kh86ici`ll8XID0S?!dK7#DCr zC3)M>3{nIg%@d|GzORB>d)O}hi5p)@9mUEEX%1>x7T_SRN*j8LXZd*t_@j9N1M~v{ zR5dtmBz@c*;vXiyI4LSuh9E>jl-4ZgB zA&bz*qc|?aTfKs)q-P`XO0VdWq4w@;EgAQIT_;{>?G+&85!GWg^loMbF?z`8;`g;Q zz`zT>a%|_tw^svr$H9986sD!rQw4roUO*>;Z5RQ>gb_d>7Gr_EI|IZt**-Y+)$PFO zT!aCJ5ph7jpi;2j21a*)cre@<2Ae4kxbfj~Km#cd`sRxU_a&2_RGzk5x4Tqxdh&7U zZvy@Hz9<9q^!c9qiIF5YXTkKW09bJI_NA6!?P%5rb7Zdjpw_y-0iFxEaw?cy{pI)8 z14!7{PNQLR-`Qk6Rp2+}1swKC1{i}NaS%(G8&#O$yfS>`3Ftu~B!ZDYG^Vy_=Q2+N z33(9AAG!%M98ho%6J^g42x;0KaFq=-+kJTg#=PUkeFEFt4`BkyjF1BW>cDC)Z;T$c zD?S9~LV3*Tn4zU($XoKVJR@Cwp*{y)s{x9c04%xuhdw@5K1z6CU+~TFKb^GHGZgOI z&@X0y=wHr&K%8?17~cZALv(BiR^=D_fB_2SBO8oc>Y^iC#Pd4CTgR=u zn4-RT?!cY?VF$b2J?wtKM)ib2wy59S5U{}Bn{tI!m=1CHXN;Z*fI zpQ28$<69foAkbpAWyH%(Hbxk`A9l$wAH%)H^x+LdqTZAW_72zT69B&x1i9fDM#aeh z24aOA)8bMXxxtNf#6~wY4h%Gp9g#q$m7eFAmAD*~ga37;CKp1C8-6&xw z`ngMtM+V0wleoog)Ut09HN5AzkeYFHEuH*HWJo0>9xy<8LF0X$_NCmqRqahJ57*0$ ziD9Ojr`VF}olBx=INdv#poam9IoxP6`nJ^*xxP6Xr1hDAU?YHSohyc<`me5(2z{pd zQVu<#u|Nz=A!L9;1mjY1QKdKvQBwnmy90_UJXs8Yep()^exzkXbcX&?gI(h#H8>Hckr+Z!paKSy z5J4i_2=qAhe#Qd7D>q^{+$ zhCDE2fMY|5R*U00V;FI3uz=v00z#TF3a_vLm%`L8dF{@D?S&3fx%Qlam5zc0z!kCq zA%{@JWGMt0SLZD))llfwR_76L6I)1C3P>DZQm24;7!zor&@u*`cst!-zCRqqA<(*o z!x`vt>&VCE4A46#j|ZMIgK*cnaloN1cnE>idLaZgLd+c|G=osU%5KR~Bl?GDlO2ZF zN@|0rvvG0s0i?k1g+3aO)VD|k){dvj&z7e)*zaXP6qh?tXReY`x_n)H{%iL#BU-ZPGf;_ z5I2ecAQ$BD>Lrp|O=StDGLU$_TM^&86;V z1=T^8nhAg1LQi!2=2XI;5Wr@CPmd$5xOT2Kbr|jJ1yd|0q%m?hE#jE1)qzb_t0K z7+};C0@-EE+yQz>oan)rfPycj#^UzC5~5BqJjHh0GCk4Cio=@VY~0S<842Jg9k}*W z@1h5TFS*XEIaA0F5i?7`K{|dMVx<_j3_8b0n;UXT0yhTfk+zWsw%0CcM?<7WbOtD$ z;u#D4qZfdbeC_gmj`IS>=x5F`U6KyI*Cqm=`9#Oo5GR1dLCo$B7!(OM1}Gd*wct=2 zzKL+-u>NSVQw*_=0?!V;p%nsg5ZwQj4E|TeFw)E(aQb7;RLzgy-o;(v>YYn-pHt|A zOxjn~%`O*nqXUX9#6!Fjw=FaUZ?+eF?M9-C5N3P^0T@Uvm{G&W#18`c34lKXf-_lR zuxMszj93Xa516BfU?YP-JdBAuK(hf;?0x7Ch$UozoRt7#f)F){3RY{dD8VYk%`X7$ zB2b{u{QxCjw4q-@f|3P?nj!U?D~_86S#}A73~+%w`gg83eyZ}4=LtySNE)!Tp|yP^ z#s)lwauyS}nbFo!5F`*fFg_!~!z>J|$Ue0oH_|}wJU$bm{)8DMC=zU)16wv@gk*bu zsKYzaug?>nfFG1NxhX_D)5CZ~h;zV>dzh>f{_q6p0X;FoW0Jup2W|_A14P^$Obql? zfr@_p!U|*4Rx*83mbL#xdyVjispUdwvUcVVM1!rvv5)W3nZ)eJ047jC<%XVzfWU@h zn1bCo$AOj`lE5CofkI-G1-N#_IIT-U z=r1iGkI`4;Lcq;n2!7bYrP^z$r=^-5*W$|R1H()i4Qi2KwK{S9!-Uz9-N{X0OI2uL zZy5bcXDY$_oSN1g_frM_v7`v&fvzDTL_W4DI|xPM9}cwZ7S>#-8e<~r?oOE&g2O+FvPN%LM)1~p9xwBlPa*7wEuoP;j3hI0sbNGbR{XL;`<6h}Z?_ zCpCkQuK&2iED()&N6|1)9b%X(5a{>t)7V%FE{?P3#Z|DX0<$0bm%$1plERYH$z}7wEx~ zB0r1@0q%7%Zkr4!(y5d%W_TkiYDknYQ><`P2KQ_j?mnAmBdU=8O@ zOKAx~7q7-ih=&)Du54eW{Gjnu*^WHRD zpQRN|sZ_$t<}0@Xc1TTS$)@$lEy7so3}es-Q9&1TZCp4D`?R4`SrStwJj#I(^e~+a z@S7WH-&A5$FO@F*Z=xyuL2@4Wyx2|Pj3!J5h^<*dFwhPDqM$fD+GUE-@J7LOV0H@) zo}++XF2sRQH;#Vp1C6svhrnJA39Vs9LRlnD+Oo9x;98di=f#=vKB=@SVIazSD!Ec3dvzxH^3oaa;;9~uwceM4f2;(aHot2Vob7fP~`anLq_D% z?4HsPDpXm&1LTijCpDhnHB^V(k6lFEJa%oEd@!X2KzO>r`~>jv3h;}C(t}bV5gNw# zfWrTpXxJA-#p%`=;9-@xm<3V}6=BGsKOk0GLLj4iwIpj{nm~?G@pRFJ1^v_?5c$~q zZrOK$$gPNLU<;zfy&rI}>9h)ei6BU+vBdTdeFIS7wy~NEvcRA1@H!}^w2iE z&}`deh58fzA3z~pb2lU982h}})J?*VU^&Qp??n2-#?wrv7P1giF1i>iwJOoobmAqC{X3&Q7= z%0WPGthR9+lOcDK&48TSS1#t&93WwjE^N40+y6Wo4x;ayGb8j*Kp-E#|Kl{ahHxLD zpvD463{WA^iX^ETJRch8CK2Kv*4Cmraopl0z6fm3oE|(j2V0?#eGS`YyRCI>yktm^ z%}NdZ=o*c6ec<2nz&niwhRTagqUExO}yJT6GG-AQzM(7_Y0enHgH#EN)cAg4e${<^DJw8E5*L^cFhm5i%U+7pL_O!G&5hOqQscoDp>W&M zRoCMfUuCrrYU6sTYC4I!L*ZE!?uVM!$c%d%m}ZN^jk97M!yp!jvmDS>-l$qSr7p*H z;z0RKA7*!7z*rBaJTiqqmbnQL{6X{(Id&L*a~-_SCgLpOdv`=tiFK<;Hg*w!Vp zj4*`z)WDAzX-N|d4>|>ldVp()hvRUouu@26fcVTR&>b+yjB1$?BuR8o zEv`*my=@}9V+_YuI{Jb8DfnUS@1)dY-HMk!6@8>|i?EvajCB{^cL?P_bZJ+bxF-kSp`vr23Evp-F8yZuh~! zVW;Sqbvqg0JNaLqb$G`_8Eu~b83Bw;AZ`feCiI1|fytR*$N)nt@X#A3sF@zQ1bWeQ zo-}DPJRyH zdR*!Hd@~p+!BBtc>Ozd94R{6OwUy?NoU3GyW|YW~0uu6!At8;7!-fOb$h{8~Fs5(5 zu-{K$$u%9@dQB;h^I1I@btT*Z>@o3s(iC!enA*>5FmH4P^byy|6z^yVQ%PYgTb6Yz zjnp@K5UatMBt5?006P3(igA$Ff3Bms2i$IdUoKk8Ye~y0aq5>~K zwMuAY0{w>u++&FQkZZr{7UM-2o>M>~oVv;^!l+*7f}nu$Wno;55{VHG|7cFxt=1d% zIklu;_!2(h?}pRTwcu?%8Q?pjLE_BN>_PxJVVMjCnKy(`bkK`2kbIBha~BpU5J)ks z5)#{j1qK7~lihI_G>qYoNV!{nEM6_joP>hT&7E`mvH9kds(LiJ!LS&RWX z1ylw|Gcfo3bfyE6%KWXN2;5Y9^I_sDaGR2Q_+eW!awHJ>qPV zFc+D)Ooqgf03KfiI`Z`i9|aKjF%XD|ahVW2n2q>BW=u#>GJVDt;sL=Nt-Bkb7=#a7 z&8a&)1M;w7peqh05Wto0F_4%hmD8j1+D{rPf>>Y!mQ_Tt1b{p$2ecQ>tyw7`4!lpn zAvoX++E&GBHFq=*=ad%tDu#Y+>$cOzyq$M|KiLBK3lc;V@dWR0FGv042zrrGHG4 zz(@}!?qP$)$zSiaok)&o3f3z)U20naL7?AbK%&vZ8!CeEDobT0KZHJ-frrjAb{ZLi z%|de6kE|hu2pBlsB*?A-W2t~Z*ZZ9Uc1uVq_w@w8w;-XoL&x`ko*sWHCU~Uy#+QJ6 za6k&k4`V9mwStZNxe^>-{f!ym_#zNukQhNYpff<70D6vodh=JeZD~>g3)Hv}?1TX( zz*^bFNpM5%KC(_K=X10sdekuQdggq!YYD7FjAZ0TKbJrgwe=Q9O!LV6Dm>h5vD~Fy*0Hu5|N})sTg|qW!&7($= zodN#X+&KhhV8MiA1}GwX>0XF4J^ZjCpg}BAyrh&ie(q34q%Zw0Fe^zEIAp845_|&S z8(jhIB5>Rl0*QN*S}+I2p#rSj&-|bTnj6;*&!bpR%wV{LumU7%4w{0!QX(xsdL_>*a1y_LB8TtwD^MtAE0`ezqt0^H4MD<^C!9FVR z@0wx$ZOUE4yG(Xy9xv_fVXFdw#&FC(nma@TyDzj7EuK04fdU$y$zt8NJ@0?AkK1T4 zyNr}|V}PyE9D?W&V}Lxnq?FD(z_&(&2=aygFtfU^!=P%tII^yHfILtpIKu#46ZTq} zloSM=1bTv0VBdVr;MgX73qTN)89qq<>@(0n;+-2qHsV`{Ka|T%x$FWulIVjZMy=2d z(3l@3%@#u?7^!A3AY)x6q4W_pRqo{e&;)LqsD~vz5Kx&NB6n3SCaL3)uUB)s3`|wO zm$x-tuFDC4?@J3~`=i@H!XDQq1^kZ_P81C}ygTc&o$)_B90Gh@c4fHB*u{;3!Et_Q zyATXYC7C7IF;H0`3?EB9^o?GS3kJN;?I2yn&G%=y-Dvj6Wo32*WcO3#yKy!Eyq19c z87@+OXqG%Qs1r1ESR!-dr!sNZWEDMg!O{`XHQgN=hyOtbzApW&i#%OJjkTwBTUSx+OTD0Qjcza2^py z41{o09*$=r1`_cm(jvGgv>_j9Vax*Q4x`-!VSuUxdtw-_ac(Ru6A7|3L9`J<8MOQ6 zWAcZUMvk5kTD=2n=YKl@)^$m!>VqHY+k{l8L_t}s!3{9LKu8K0FasNi0AXx6nIi>s zE3#pnNnFtvBq$(E_CiL5|6OjNQL!~FYfh<$hXgFSq)R$O;eHwdgBrn*tjGS(AqOP+ z;}oWcR@z5lzD}mozc405kuV~GCOQIgoxcqf{nt7a;EjflI5cDvRrubQvOa6WYqxdO zeaW~n+<5iZd|oQ-!x|ynX2@*daPv*>`d|hoa&R^jAa6~{~CfJ#AA z^8j=Z#uWJb?d#GD0Ou<2L4+M7p8)s^Sz+QTnO-iPRB$#m07F2xhZ^ zSZWy2g1I>ODdq(p(XIhwJ!XNl277iG^sf6R=)RK}xRy*%tLx*l?moEgA0e?pKf4l)0B|+PJ38#qz(on5B8&pz_!6*FK*zr!9{O8)3=YUYp~dU!A1(s= zVZ_H;_TPCC!@5(z699i4{3y&;$+XHyuUrEP22L?R4niOG;xYIU1T7*iQia7D;xHJ8 ziA;C|hV6!x1FK>nbzhuIi?rcNY)dIG<&o6Bfbr!eCn0|w;O;W(2$S`hfCf9R;oISv znrdrzFR>&Fd;^ccRUfx7a}l{h+D(as+PC?V0cgR9grJJE0F7Ze#eeG0PD} z4n4E zCg@S}X33yU|5fOd9kytYq%J1*fj^*3u^j}M2k9U~*0vzTjp#?8=%{HrtXhDA06d5! zf43Xd-MkaBz5&KL1ezOQfOhdYvf?Y75@N;=oe^R%90Y@+qar4SFOFvPg(W1VbP+V; zW}QQPOdVP8otm)t0;csurk@7BARHvl4fC{_2SExmS}-Rml_^wk$`xj0|DcFT86ZBK zsg&+OsGcy~hmD3{ZrE}4XZaC0&o&r<>E)91^UdFL!u*81pg(c%a{h(Ki8RJe0 z=vngTEFmOo?2M;qP-N84B_{%w$@U*3fRlz0 zih#d~6fqwLg=731)=3hR2(}J>#JfNb76wQRoM(Wre591u&%wc%1O{jB0&91q$+g`o z=7f5{)siKohGv952+ja+U-5!j$AKjEC`dxL?nJm!C=ycQxE2^O2(MgPmi*m+C9jsE z23xy6oM{XYx#S{t;B+}L>P_4aqKu$Eh?NPpigA%UH022h><{KSe?W7AaoUGr5y5RT z2pplG6%FodTqtR(sqv%ae)Y#QO^|>Ij%mJxuj$O z_zE6&GqCBsj(na^Sy8b$E8}x>tLaZ*Tj9zqRrj8GW;kr97r>y=z=Z7iX!qR-Prqmg z2_2#%_rJ+n!Ltm|&~M%yn(&8RP$>z(*ae|~L5@_h1!L=-@CP)E(*yFLJiPw;&f`Kb z%@II)MK{&uK<6i**EPy>l!Itw*?H`=5 za%rPB{30s8-crirObJnPnblLvAYMa1Rg=kx0vcfU)&NSdd5m~R#v7Ad748uD$Of2C z2KcL0lExjAVBLI7oTbNC1{j3~s~vbEHx>|1V}fza#SYDMjEkMB1Mh&P61IgJ>)whb_C`CsyV z7Em3BGzV>76~3HCkY6d4&M{poU3g$vBZ*KqPKXdE#}X8rs1?UtfbI2{ zz6gM+e#L*rfleu)z@NY;+M)AaH~;o;my!eO_cdCDujd`$FF3_`br6HTC=nJAF>GTq z2q~a(Ky(nmm;%N@amoV){A3jfobiW-Ea0ai4#KAJN2fqRKxc!Fex5nHv}-+-`q)R- znjz{l*i0a583y`^J}g#}og{6WzJ_PA)pRZM-gF1n2&8RZj316_Q68K@%nA3BNR43h z@4GQ@CxF)zmEHz@y4B%Yuo6K>#JRLEF#{afLa-ZU2b?dHsgkFL5eA4S(1P_~Gf0F1_T%|o*;JS;_DYhZR5N?3~jsez`7C)UfG*I9z zAYay(6#1~#gwEXoraYG{N;Ss5uR`Okk0!0>9pL+6a8?DT=Idd>*uHrC7sbSRYM7b) z&@gXK8YF^wuB>LTg|I^al^heTyKI@4!8At$Y&e34rg6 z2afb%?FNFFr)y*eny~aRIiOmsg>EB8qCvO-(E>Cs|V|-)79f)NLoB#zHdm7SOF6ekNP0zn8Sg z=go>#=5cSnNz$i&!=!oWT-VHthW`s>YR z5xsTZ76V><%3Y6TwpXW=YtlajI|PMf|1k>l+>eit=92-wAN!e5Xa#!CicQTTJSuv5SR2kbK7FL$hKd+5Kg z1?RiSVsUI6F|hxvLZ3sRO2`F+7lixi6q|v+#-v=YT^rhC?+I2RV<@4l8P;Fts0rua z(VQONP5^u}_Y<_^8gM`oK+%Os2tx{}w2$IZ=hDUJC2Aykkd*>LHVs&Ug0Z*ed=S7F z<%o9l`?D$h5%Js-GIok4_yPX3Y*NMLU)q|SJz-#%SuvdoF(vD24$di6r+Rn~Q>+Wu z2BP24KmiGRFSU}W{<>m(y*Ti`O8sa+a^reA0r0noU=R`)<%90CY^EzE_379WtfpWg zfLLdns=k2^+y)=aM*KWGY8L}Nn1aEdHZz6zB_JoP0K2^6QV>Vi)GZsd0Jot~NFNx1 z#<3~5Puz6al+W!Z5JxzE*$g}nJfwhf=;9dUNJaHUI0SwJ?}LOH>+Q4x|4uvzHW){p z3#lKrX!FVOl?jf!*9@Ml7AIKe$pN*?BoCyxS{x+33yiBJ-IgKzDFcLXez_$%!qu$KF##5n_7F}QZ%Brv)StchMQH+-;hcKG85 znhxQ++Ij&6oTiURfSSWsiW!5aDWBw!a6@_TCjh>k0%C{^Das9PF>w=V0o%|sLqP9@ zL#~h}^j_H?bqIkHY#?aOouS6o0}vLxJ{Qc7Wl0Y z7&AbJKJ97^nXi+n1dt=njNqULG@MI_z#WI6pJ&IKaSJ5o#nuoqzz$fM@Mlfo4hI;* z0m1&tr?8NcXbvvj2YU1J=@3BxlRzV}(Dh$-#PMQlxui`A)?5l=2L7&=F7rf<;cm-1 z^PA_m>WvGhVP%0fz@?WHm43}9no^Vzfe$0&bUPVN^& zADUkR9wkN)`1^Pzs?NH6UzssC1;)k;GHSTt5JvtF)kv+_}eO%vnbLpHFt@D;2o1#fxkIHv|IzC zn-9z!|5FT%HDM0B6PzG{mK?!L2CtbZ4-_A)uZG z3AZuq-3aVn(vZvy!UZdQfC~`g8`?#z%5kgIekT#o*s7a$fNhj9n?KAA`*cZXUEtp< zk>mqY92v(2qBqTl<%VDm?-qI29k?%jh3Cu4gwfO0 zLPqz1E8y>0D+c{b6^!tfaTz#l06F-;;EWPS@0?%+Cg^c^3PGnyH=e91E#Cuv42YY1 zGw<|o@t0#`aGB(Q{FYc<`9;5^%L#zLlpApJCJ>XEK-gjkoURkh?zUiSJc^NU7!qt0 zkQ13;(1>{yF$4#Dlx`LHixWk+ZdTzw)=c(C^aK4%D-FVZ8#MR~%GKZq{vfcoAI!@5 zv|~~S5T{Aklub|k7cHlhXG;^k8sX8)#kqqscE$irvcH*ql*^@@0QhF~b2bPmpnp~{ zb2c$ncn3Jn4QL=papopaBR{a=fu_geLh@%8VPk=I;#QCk)H*7JzaUYxA))yJ0{(c^ zmk0gJ4gUJ+A{T(x=mee330kqx=^t+H%?X+dLD3kHkRzIbdH65wAlEwQ`IqYJ8up&M z)L9(_Q+`b#{Bg~fR8Ii>HAOhC58^O5=7Dn{IIfZ&S4pDeZOS0xhT%i>&Sl3P+L#`v%(9y$Zm zw9+Gji18J#<~(8gy`=g@f{8@=4u3c^@Uv1Y&wah5%k6gJ(SK2P=ot7YaS=Kg*9gWC zY1l_wtW%YOjq(`+ng&eJ3*&;_3_QLITyp~~%Me3Fc&r8s0aWEx)Q`HE~U^oRbSLBtqfa0_U#hojgbhKs4c1E*+XfGRr@da)#Mz>TKg zeQTj{GmUz1LGFAaUmsgaaDE2-;jDxe)H_UW;`3(=u!|lh+k%GZH^wxG5w(PHLm<1R69!JLj z=xVTIfWQXQAs7TYU{0c7vug+Nca7MeU|1V=zCNNtNCDl_;Y1HB-l8$-(u_&}%K&)! z>u-qv_t#(d>F@i5w*39^Z+`Q0p#6^BHx$+&Y&PbkPqQ6c89@=lem!qwy9tK6(FD236Z0? z!S+!kO(4}@`a8Fie)#{~UCVOpDh%|(6g7Ta`?#G%8VCFOsteW@F%;atzR@HVnLwV4YwT4hNfFwI*=14*V?^>)fx) zKzE%8w9|d4Vf53V=2BsfG5s9?92lkq07Dc*d@@A4(;g{>@kbcoIgV>7#)Oyjj&K;m zd9@v2hCFY!6Jvkn3C4EL8+$=CyM)m1U5908mS^+R$v2t!7-N)Q;idD)eJ zo&ky$r>Qx8raElUT*8xjOJULs_J_1Tc7E;dHaZcAM$|2)=jZ)QJiQXY{0A8zBVUQ( z$=`+%eB$A@AAU^*yfIScLm^?j+MDESE<)6@nFa&{?4{#HywU83B{B_%VWmufyiW9^ zHw}oaI#pfxtJ&?8>cP+JM=djZzp1@Zpz(=0_3;p)U#Q>SvAVy<&Ndcu4FG3u>6{?! z778lGGwA0Tpay~c)nYyE&@lLvpFXp2s9Kl*K}#TLnm1x?3V&vNFCd#1wChA58g090 zzFK<*ILEl{e~&cVJ_j_h59|I?8>J0~ zii2d!mEkGyc?evH0CtmtA4H(!OxS5r&m*A)A(*Yk+aV}qHkp@4MGt{{XDQ4&6JSki zhx-`_M4^>)(IdOT=6#znAGJ2G#02n<%>iCv9F7#mkn=elxeUX84kgA^;~EFhC&qtNy2dEQjQow_z3DaK|iP0YdVwxuCH4^S)AvEn_roMjV2|d8w4eP z?WpgA4N@UL@qn$S4sd^$XjQhE{nhTyFk1CL3-R9&@0#oM-%^oJj0k|h6*k+&*p#WC zG4RhHo#|G?$h{u<{M7;mm{>B~A=p?kdT6bpnZR>B5})UbaaLaTH?skK9ZZSEYNo<7 zU}Q|)+@YZ^7^Kz)PnH?L{hvmG%OR=!CIh@Pr*I4VZ4HUFBKBBsV;KV9xyW6q1%SsG z^nrPdDRAE`I2Y<@Drff)k#c~9m!yFS<7nW!{4{)q3etGcAYgzDR0MZx;WSr5SQ%hO zAcNYZC-~xY*E>!1!2$$S26&R1wsk9)4}_T92S1C0_-Vz3hd|`+eh6&`SiIldJ2@EH zFu{)ZySiPm@bL$w4Mxdw&r)?zL6TiKggSPJpVcfb<_z-a&(Z9kC88n(LTTKJ1O+D4 z5G`mKGY+hqgs9IP~@vcI=jZ%kk+#CS%unC5o$MD^^kU}E|ZVqv|8_w^CY;93h1G>*>b-=3>x?|m}g|a8U{ZwKnTwFg$nPtfqfqSSnEv$?yEaHiGNb0 z8vQtSdu?O|)@^*h!k;IAos^MPL@fiW-r<2LoIi*v$+A3q13=DJ5FG<_E;2d1@O`Ja zIR94fQK7DfpH|1%dDlqhiViwuwfE)Jyt0IA1SDO#pZ z$2gW--bpSOwU;3gZF|3mmD(%-;yIv#`5r3`?}7U==GE9mq{m3_Xdglbf=a1wTO!68 zb>MRFDCP5a6dty4Enr$O6DWvxFpQla!bci2z*AJ#7eO|(UIKsz8PZ0U^6df-SE{`i zcQ_msqPjTn6A}p3alg*)fGGNtfPlfo-mg71y@P`Ed*uD@ltsXA6Z%J@xe}QX4PkdU znKLx$*C&7(MQ;Pv zoa7^r{nr`b5ytTtEVgU{MncTO@$*sO#QBm^(yBH zPq#9_i`L21Q~-J?0Ddq(0Y9xR^Lw;27Cd2BI27F2`(?#(z|OJ3?Kp_@eo>MD`sa^O zmqXx46qViR*-rw306tAw1t|z--IiG*`U(n{4(kX^EiOVqf!afoTI`)f{$3z|2L4=- zMa`b;p>6{=!Yy&~XQFhc1RxQ8ggpFSI=>y>H|E#!2W_PJ_~~_k(F0)Oj%dm;gdkq= z#SM83v8Dz9iYp85n$d#?4`ubW`OkWtd^ysEY?5^ zWZk9lL71x)vn)8CCR0jA_yUBz<)Syr( z`hVyo(8d6)<{jrBt3ho5`jU0yF0V1_NpZ$gy8{}46uJ? zQd%lv@xEZ+4FbfI@qn01()SD6*A(1)WW>dkl~7e06Ib)h0HG-Y*rkC`(G#)MH*8+@ zWE7>;TH;_d&4Q|#l>vvMExe38AD9%6gi)U6iL4o-_T->j-0av4Mz0DQVF0@|L!P_o zH{BWsxCl@Z)*V5_R{`M55CFR7fn^35UN0g6khRhz20-C+hIcnlvC-{A^_wt8c&!92H0@!AR4%rfx0M(F;LsW zeHb*eME6o;^~s-fek7S4#>77q0fdZ*!ur*c;ToBj%#LBGZ~$KmkdE!&ZIl2e%OEB_ zk`F{NKr}+{UoVGr2tbuC|w9F%7J1E6-)>B6Gbmlmu`avwxEq(T$r}k@153^g)0Za4m?$75l2Drr-Pl|b|&v~4o_K`LW4_ZrBfq8(m4r$Z*Y%b<>La+GH5050Zt|ZBtnL%696cjzusj9fPl_|d?2p)P%aKy`f36y z`dh@02rn>_cwvMumJE0KRTHb(1ZJ-0Q-10nDmdqkz~DY z3$7~u38No|JdUQvxEgNg%$y0?D-s4Z}NYS7wZVooy5<&SV2U<;T@e7fYs z7oT+m$e!q{b^z#o3vlKWAVmHSNeHhDF`X6z%p54VT|T-KCPIFxf#a1 z4+AqWTAdC-zg7B&JwH6Gs*L$21Kbt;qZt(!4gAwLX9EAM5-MpV!D+drXqXzNfP4^+ zXL(w_#VZ~3gB#d{NL0})jAHIL``9LNT5W-jHkn9fhu@gSI7af1jpPfF$j7?sV=Q34=N5Yw6#0gS`s_teF5wrHJU|O!6ei_UnR% z9WQX5*hoQ|z?t#yQC58BgAmO2IG}xJ5?5JzNdjpG^e^^K$D!>S!ZZld`LWyZHwVT( z*7AZCs-lo}jPO8_28Jd+NCP7ugyUAaJ@m*7mPU4cEZMnKyEBM)p4XTpwoz6Xg>lf# zA5z8g$K+b&`>Ue_G4|e{+)YiXZ2+=7(+SMr;rul_%i-w&1r|S*YnjmWd;tR_U+nar z7uaV2QC$l-->U(T-5?DBGvapu@9LDX{1KSO+eDKu{72Fk1N^`M#n+zKJ-~<)aHS^N zv?Iv-Rl0(C$9#`+IlErqrOdaWmqhV^;a6KSuL(gj9tp#;>u6{@97dlCR`;cY{`u{l z^e{RY5QWX)KK+C5W5ES;>cY5&e_0{mH9$IMng9Xh2x+tff^#6u@XXx#79X_j;rex< zlU&szXy^jC-x?*bA#dMe?(pd`PuT0|gl||~Zsg`D79{wyTCkGmp2`3ZrhuhNd3(t~ zFc6?WlyeV0olQSa>Z0HALsl00;1mBz+2^{H1AR!pkL^QtH}5{|SUAt*Eg9+_eAr06)nf~5 zNT&in<3z~#k&qoq=UlKgq-sFx zGCuSVK-(-ifCP#ifGimx0cFZOeg*)p?qI(`c=z>ZgcWB7Wo^TfUsJmlO`&fY}L2Qsg{kH^;>+=Z!h%aXQ7XwkvBJK<( zp(#8A5Lg~rkIqGzG{rhY;F#>f8{BmUqi}h-n2<}y1__zqiMi2UgwGRZs1A7mUN7^-l3zn$b1xoY_PP&ErC+u)NWXD&* zHC7y9hwVY{uC$7P7#$Y`HB^#VBt6drZh1Tdodk9x(K6F<_hn4~ZtCg1sd;i={KfKu zObh%>^g}}r>tk|Iqi#%xqKZn3TGjD0Av+rGnE`U&v4X24IDbJ42Uwu(55e`po{?{d zmcx99KOFPW!uUsztSHnWkU9~}sdi(vBglm%OkABiU%P73m>4?#`=zuDEYM!7xr z=*-#L{2*LJqdL+*T=?G(639~m>8h_Js2c~q7y$k2Yw#7!)?2#_2D03q(gt`)|3P_! za^B>NzZj^e7$62{^c8W?i(rBqC4f6Iv>0G#6Hqb05h}Q|LMl@}MFZ`Nf=MeWR@M#v zkd811d>ju$Au8(HBnf|81j^Zm}B9}+Kls7~yWA(A!OlLHP%X^CoqV*lho_7Lu*iUp09-pU$!64W-Bi>^~mOE!W z9gN`bBwYeHU;+yUcn&}yBOZ_G5gm%@5msDqWFK&sAgEoz-FQHvf6E*d{3*7lrbv;d za3;^LN)ebm;IB#|l#{|Mslg#P+1?D~d(;yfzLNHBJT`?0n~;P~PXhK>!4jhh0bs%! z3fCCc8FsIac@VeYVxHX?-VI?NIwp2HTVf;oK4IB*W(Dzsf_*;nbO`QS`QOrJqV+JD4#@_U+m$ZWPr*AEN{}xOas9H0e(=&OvvTN2m}>qmiPNH@>xeoKg1WWi8(5IGug`23 z2RMLD{EOmX2pUvqGCLqZb_t1TAi8(rV%DKt`b+?!2tJg=x+6}RcrN-C#Lofz43HWr z-p#IW@2aYbBDZquPc?-E7Kxc(*8DF$hXFo9wrwIO$5AjYxH{U*7ZD2cZG~x{t|k~# zg^msaRf*zzP`VJPogoZ6hQ;KK9H_Q#9o#_SZHa&Obja%zr{y} zMhpAUK-DkRyjUjKjk!Iz6B^b9*0b8&!E4Hp0RH!E6WH7NokImYKJ9Q;KumTH>F{lF zPYjh4!~{QycTmRppT@+a>l5Zq1Oj3(NCSJm5E}vWIxkCP-bWfOc4cD^2oEWGS!@Wd zGi+aRWe7WfJNO5vLxZOkY9f6AlVP1nd?d7kY&{w{&#s4jS`${Uj+dce6~YL=E?43g z1qFMQY1JO{Ts;pmWJg;dWI)p*@ML`9+q!}QkL*N)ca`vW9!f9xO z*N-gDb&r}*#bcu#R5imdv0%7TSTs!HH9wRmTm<43{vGCpNzSH^KNL_iK&q;sQD=c^ z3f<7bU=4vrBn+yN7)C*%CA*O#Q2BwOq4?_kP)9ItQzSLeFNX!>53t7` z;Y*IJy=VW*jX>&TOklXC4AdA1doyDl!gz=<=ub+zw6SgXf%g&iF~Ia24M@;XkraVx zsD*ZVZ&Fp;0@k74B&0{zB4WIA-T!P)xC=x8UVd=C<82aF@DB6$o6Bj(!dOK#zs*5< z0nz9s73>E^L*Yi{hsL-ky9$k_2L-j1yyEOfErJ26WHF{}7Y0aS%`{~6TjRDd^wC35 z$=C-C9u!-$z{5;xJ#w;|*|1?gF9O33DjHUL znFo1+{2)cqdwWozYgz|g^#;9#ei?aJWPvSA&r<pn$Zo$}o_#Z@g zf{W5Ck^=JczBlARi)WAlQl+B(z$dA;Fy2AO(9VukyuY3~A7LOyV%QT%tY^ze`>3QCeQ|OC`F^$CzQQUSQxk5ToG0XzT&`eOFlcWDsvb)t`+y=P47J8xA+egj zhbX9)FGS16E#-cpB!Bg6E1wUs2w*M-Fz3gT`+t2=2Z9UmvSS2AaX{htNEHVw5HghU zXs$XQR8dfu#rqJW87WP!~I~7cF4quy&R$^hT zNPQJlOdt;w1H`N)xqKU_UlbRHSf#oxwjxXp+5V;Z5m6y9jE*J{vJO)>#z2OwfImkH zPjFWtuYew4kHrMJFuQ;eub?w6OO=s@tI`k*NUCIfs%rSN|8T?dRSU`8#9#fv;V%H- zL?mw5uD#m8L?7$7{FuMgiFg=A{v_yWz=H;LJV8DhRJ+*u0?|W;0;6{*90ypp|B{_3 zAEO;T)@;eqF9d%JQ9llfhQiQCUAhoJJX^Zs-#$hGX#i&m!=05iRh8ev0b?=Xi+CYx zt81~2DP7-(1o>bf8Uv|Mtm~wrvPuF;C*p!;hYE2`2vqM{?`VnD{1t4_E%C_!7+|ug z3on$I>%>}?*Xbu38a*?>@gpHv8OjYPY+pPBhe8F? zk1$U{pnYpxCa41fmInD>Fn(Y1#RFn9KKU~d$Z7je)7yVSf-B+^zz_0F06hN;0AGKcz{3|Lp`2Q0_=7$Ig5CUBP@u{j z`TR%}>~lbA08<~Z{U;$14OE`1Q&6@P{5n@zN-52{$#WrW_@Z7*VQr24p~auMuoz=vb~6U%S-C$IH@kiw8Ew;|>X#LKay&{@Fr|Atp+BS#m6GilY&l^o z@S2M8=?u_!KjVJ@upbQg7y>N}WD1C0P4=$|IX=D#cs#l`X@Cpv3Ic1l`x5=9v%|>3 zm5E=j(6`LBma>LaUAJDJj$>8X!3DK~bbmzPpSV49&;!Cl4il7}HigAlWE`=+&yJ4qPuNKTO^acg#NRWvhk)w4F> z_66SG<Joq{}!rBr?c-|8$OzDENWpOR)?cF24{6~Vna@t0H zA#S4aH1YH~xyMK16cHScgprp8L+c)k6Ruzdf#av*#HE?J5I0PBF3ij~2?h5E@>;oR z%Nk<36TAXlNuLg8W!Df`AoJ=L#?5fM2#XmZfu8S;Ln|Q=on;4K#An=Hp>7M+lDMsHgGY zoXSVa2f}O9ib^`H?_4m_(NEo)>D4Bq@#X1)zw$L%i*L@!WC-QoSGv}tzPfkQ!*I*Z z2n?cO)U&N%RvGlv5XvYAq$k!LyDFhbL=&m2D+GBAd(@Oqq%04I)Zmea5z?hV=b8w$ zXb~wC766|-8mBaf_JGK=S&9KF6qL&BX6P3fL_-fc%>7XujEy1)Mp$X5<^9Hl&_7pALCgSVV0C{geGVL#K z|L5&$cbivXaDEtBNz^H-o9RuXnD_QOjX33#am5qhPqBxBeRt5*rgg6*`6)wVq zLDu1)gjMh%FzVe32-1mnU~ZH2MVO{UU!QTCsHiQ*LP(9U{{aGqAUHTpC9YLZ&r2n* zfT=$8I)ygvu;22Gk)UmbHl?YqYniB7H_U`bCiL?;D4KmLJnvS{8;r?{vuL*6cq{2XrDu6d^23EM!QrX{yE1;)-7W7y( z&H>5%o1V3YdGFmCKj@aC8g7_&r%mNbyk87EJZEKqi{PJv9;a`;VUh+;^?|aoR{63? zFHQu?qSs8sK%l2SmqK6t3&cwU)GJE4nB;Q|O8zx-p_< z<|Vt}Fp`O0uc9?>SV=GO&w4>yd+0Va>18| zVVu!<$7F3OBedIuBj({1@u!^2D{O9s0mt>d2NYMKbPn>>;Hw~bC$k| z+{au1c@7(!NGc##8p6?tDYk~I1wZ2yGinJw50d}-s{Xiik>TiAh2H+nz3L*Qa$((J zv}*BC`QI|Y|9sqNiagJNDEMb$MeCXO zan>JvJIncUDAMYXO(Adn<(Zhg{!9JKXsO7k@BrdMg?hcjl;NO$xC>%nUbgoo=;sAN z5IG<#LWLwS4XB(i$ov?$u|39|Ad6cdh9K&$YApo_huij>Bx&s${i@=gd@K(f2odPx zuwhE8D6Oxx@-p*WO9PGUcj$qG+ zOXUB;J>QCni%OQ0)wut8{^u* z)*@lw3ih<%drRBbyMdLG!@eIKm@d)=PE0=t{}l4LMxr`{eHS}{lnPO%WR49P+5)`8 zUX%mWG2TjlX|jiDIv2nZ5ft>^Fk0JZT%_?03OWdM22jfd#SiA=qVCXd5}tAt`lrfs z2B^vY~(Cwl0rz?bg+Id?2FVRW}HXgKF z4G+2lj_m__5afzsg$@kA<9{{^peEbq%cQPh{9ATzv3AJ(ai|TXu=yi?iIeqeFVGew zamFQogM5wwO4aQH5-5v?3yXTDFhFS6!sk|z1yLFwml-j>3Hvn0)oxJE7{|5nQ6}Ec z00GWz2^-ubL?Ld0THEBMtpM-?uR2~VDgyGaiKK^X9U!lDkzMX~DDnbE3-ECcD5Kn{ zh~mhXdIi{|qk5TN>!pl2-siB%U70=qXj<()^VF;zM9OI_CPLMUNkgk(+g6dTYyPLS zPpkg8xa*6vWnMO25X;A*pBIjWy2}Ylq%fCT{u&t6#c-O@pPfEt5k3nNv&@*TOdygy zsz$HPQ)@BNGKG~7OCr4bE%MpW2GRED4XW_mPt){1`7gY*_#$-aPAl@y>*Pl`#Q)po z$EIU1&#G4T%pIJysiyq=JY}g&pO$8dBwRNq8WdGB>zh8IS!;W~>q6kW8fo)h^}eDNnE@*CdA>OaSFs`PJDt= z7Q^uAWuebngY%*<&s~eR2xmhOS%Ek^@0hC)f(8q0(;EbGl<+79EK3QOpz}-zV(^Clv2^t_a zuF?Rv?}lwMTaS4hl*W0-2Hq4kXS=gtHOr-|%6f=U^_T(1$r`BNC9+p~0|6}(>Vzc_ z;~(b=qSQNukDZ)iS7}#0AGJN4;;^Ij!R8eAcc=j$rq8!o~AduZh@P}ui zbGghI^gnun`Aa##C85w8kgwI-ut*f1>D8rT`N`~2=y>s76ov>aj%gdUpTi{ z>cydz&MKfvy+qZ5Pc=YgI5{fTRAL`U4nCxvX@eyUxyJ%dK7?&EJ36&sFzf7H@Xjx$ za$E!Zz{3+?~s0$)kC~TG)22T-j5(fhqv34dXqyHO4GJ>mL0MXd< zIZ3s1T{IA9Vt~$dWh4B07);LMbkMYSM5OZD)n!Y7SuwgxUMB8l}&;h@VsN;{Fi$qsnpA&{dqh_gL$~ zK-K_(b_Jk<$Y>DlYTY|r*8ou$2?*H+J?n*j-3WFQeLQ~ymcVApY!BFjh#`Zi*}=aZ z>NXtkH|-x8I$(q1;l$kCT@tsXDs2jXn$TRlWzla#bEaThK0~iXTB1r zO_sO`pei03&{J6lP{t(3chf2|WzB!43Vww^rxxmUg-2t~(Yu+`rUD?jJ@**n5k$gv zHKi(0)RKYPddEj5lqRa!y_xeEz^K*QMtGh49wLBcQHS5w03S<%F(>>d*K=yPm-$a9 zz?p8yBT~>4>Z_0>T_$BzRKhZmg*sLM(YZZ19GcxBN}2s1N*kEXtW^R6;Q(H^ZZNo} z0W1O3!52v%PMOb5r-NOC^OMM&JE5OeFq51p68>T0=ngc9q#@};`Czb%H+h>jdMLvP zCS8pefpd$G~`V&M8 z7IrCtU}d&Q2$hiWkCi~o8yM+aGE*GDDhQ*5d@*$_T!IhIyA`lkn)0m!K}?eb&`|#T zUQ2i-70@{cumc00;E-?-+jJ>H^TA{^15NrS+nN~Z;0+I>Sk|lv{1NB3_wemn+!qK* z&_@$Hhw#f~6Zq<=K&J7wd%JwWw16%{rA{X6eJvfZSr?A$r8mz10DnL~;9=AM!7zxy zn86ri-jXcC1v{W!OLp&!PVY1L`*K48fJAM1qN8knH$ zLVj78$(&kHnprZhHWIQLBN#FTv4AySCGa?N^d9M;Dg)t()&mI7-fInG<0%#^fFn_~3`4AQNv;hKz zZ=j&_ZlYVlls>Qz`-F>n56;BP)O14Im_^er`CwWaASPk4UCFXSs0j#`Sz=*SHmJ+E zC#=F>!YhP)kXhnim@$DbNehTOYi|FsS#C4jv48#0XZ^tFJ#G+={*xs8e&4C68hV`G^pEE#HMToN<%~*}yJqfT-mbmUE33Lc5_O4CX3= z0_fxxoQiQQvh3c$fA2&BwhN`B0qZCRrh|5x$Pdzy7eQndZT|cDd6sB`K)M?TFpNIT zoFxz%U^5yp7V|erA-U;CiUbacskP{xPZs&!H-hu3wbjc6DC{dbASB=(=JsU}@K6Jj z=zm@1;x?VE(nkhx2=OngopMD3{PZ1of71pj8ugF-Ar(z9$MP|m>V*Iy-y!Gy+BhR5 zrLV8frfHH7hIS}y&^*8@TCT_SVHdG5)wMu2e=#@vgHTaMV*A0G3(Eh+2eGPu% ztH3U&dAVt0@Yf88rit9aeLA3lJ~{(z8^V}?hjIaKrE+mif~qGExk7>us|r*q$pGFD zY4HWOwr}Kv&j30KevvKh*jR)2^LP9s4mc>(;}ed~#pi{U z$jiDEP8Qy-3A}D^l?4GU;1 z-nuY*79LQigmDpP1hU+!TE<)yDX-s82b&q%#n%)I~ zS8tPnX1gCG7UrZa?w~pK-SS}MQ_)}^o~t!LK%n9Y01&wGx%uZG@o&+JZ%=6$ha_-U z{;iM07x1lWo2y>cCcHHzTJQZKSRL%AeTcyAiVo<;G9-H!WZ#c6nl=pP2@USMG>TM% zK%Kx+u@G?p&j#-x^aR70N&Duz z44!Ez_-qMUA7u{Y3d1r(0VaeIvlSPs z&&yCp@gPLn6*(Z5p;qYT|Z_trX3?-{rUnL$t_B3{pq92)n=nK`fD+ZbTSd@RwvFjSoyCK& zHzXkIks+}L&mM`j(fTzSBv8alP<>RbvOow-=mI|{&X}O`IeQ2eTT>LcL(wwN_OnDS z?A?t)OKCqc5T7>)Ts+G@2&@T^X6kQpz>NWVKIBCoZQCJcy;eg^p?{S6+n%XT0bSar ztSs6Wi0 z@q30oEglv=ohvV;*Zf5W$i0;$QrZaLBL(uUF=9Z#5h`F{z!nAq+pL=)g}@{x`nnsU zX{KiB(xtu%_9!kzppbz$B!fg8q1pRbTQOlMb4(xVEFQ!!gseHBg7_JI(=**Y5`?7G z)}tvBO$NaGWj?9|!WszcM@7ZrlPVsFa~fPBGPBwPhyjQ>0`UN>+!00t()QA?BQg^* zXTZS)!~z}!pHy<+8)m=Wjq!TDiesYR_TVpm6uqKdwod?e{PSWdb8P;Mi51jE?Y?LrC@y2QfPdqOAyk!N3mt{DnxV_mhS+$=t_ z#)c6z$V6llD^R+NstX2|0^1Eu#bwWMCbydtfZFZZ4_8x zm-W7&vj8KDb5tN!jR`t4cc287IS~_C1tU(dyP&C-po@_W{0^y*b0RnjRyo#r`Q;Rd z6F(C>2Dop)c&mMR%);rP1K(Iz0rTnHq@4w75+RHX5fp93NbRPe%TOorAhenCz)+zF z)~s$MB1DhG6z+YE#$eQ-QPUwTS_S0^6|$inVX$Ozmbb!81si*dFt`E<1{$(4P!jCQHt9=0rrLwVKEzEBN_PJMx{AJk#*&{O{9#0sxL%yPzdtRUyGGbh~U~snA7Y zl~P7v^-unWXz!b7c}s*a{SbpzAi|tXL%gJ3H6Q9MWd>nrYGVOe62=aVCy!*3Nss0p z&2UhACK#q&a7!aJaO4o+8mMo@nFK-90-|b!%*F-b7UGTonfvU=a52r=%0QM0CTP5~ zKc5pHKZ=nBz&`1pKX>CS&i-bp1aP9>>Cg~Y7W#KMG^ER#UhEIjCJx#HpxI9xa6d_D z+dA4Yun=9pcbdpKE#fU4zuZgoa);x{cJIt@d*60#6{{Tu0Vnw@f_@(J7U_a{n|3~2 z)NgbD2mtK9-vTO(*eno|{ukrP5FrMH`wAU=$NuE-Mz%?zYQGSTii8CH2C5<&LH73I zvH_%-4|NpwAappRXowBS5R1h>Bbfl0+GjN6aLgLMs?seIOcX(!*q))G@Js$&1009~ zgaau-W=0U}7vg|un8D1UAykCML;7W?0+3yW8ze>14&?CnKmga>5%d=z+mUf*xcuG* z30+nI)jVwzxRSs(`nSU1bP1{Z$lax_WpmHH3ADMM4-xiz)uz5kBuamFtdc+Ub~&7` z`J&g(01v6vH~SBl23@5osct0iD-3XlHLrY^-5LJJf}r8ElsK2l=3?OWxkA??q)EBr zWR0I1LgEP-Z0Frhi6LwGji( z@U;!gsMP|YYyc`xHWtaD0nI6}XbNG7%nVSOS*k%yj?}S<|3bPe2$(BeQcH;w44pEg zmJ|NY5V)m6WC3v64txImdMdb2TT{R}LHia;UJRDlH()g~(X1Qt*+ri6m1*hdIJK^N5s zQfPR$V!AW6!k2ugvw#PY0eKJzd?$dSm4h#U;E@z@5s*itqh<6YF#D(oksCr|h{^!b zKop8}NNfn0P5UJyD*$5Q2*`ne`XGo5*aksED4e990wuyVgcq`9O*+7}?2NS)r^Uef zdR^OTN$b>iGoKGkG6HD!#sIe|aK7o^_EF&2w}|l_63#eiT=C_fM<03M?s!MTZ7y7i zUgr37_}kALu8ZYq;04Js89qNz&`guvi5Y2SazyP{pmK@d6*(~xXirLFA$f9eKkhao4% zY?DbQ_j0|8R2`pAKoI4z*-~P+bth6iFQWHoT6~m>#0GBhQ;#maZ^C!DK0~1^x(-e6 zJ6X>#z}QVQCLJJNTst>XRb3QazfF_Rq-S&d6X)dNCTw}KO;XtM{fJa;NzQqP1E801lU5Z-Zrt6MwxS`FP z!yXasw;!tkqJs62MOj@f)8p$YvY`em0-3R_;^(4`X?KtxYe?;!*RpgN3}{FdQ7=gg zbm0v;>}e55j}>s}oL9W~&0oA9_Xd7WE@F-D8`!`W0iC3Kl)>Lsfd=U8zIiIZbZz(s zWi_aiMuv$>$I|S_#sD0_u-?(#R>)~(AEa#0>zvUHz1{dFzWx>f6q-KcWwB_GuSC2k zXO$(^2&77t=60+(o&zPdjLEIw%jCtJfUgEzA#MOS2#of|yO=zG91C+C^SmVZONMVW zs;bR27fg{5MJZXvZ0_A4L_wa3){J;csWk1rwpH5ywxv>)4tH3N9d*O75V` zHKg43kYZweF_3U{SgEKujdj3XdiAAz!; zjim*FEy#B0aDJi8xsBAnt#MHprh-e>`-FW!l9uF`6oN)TAymMfS1%A+mS2g$4l5|% z-x8b*jCdXll@SylzzPirg|5&Kp^K%8$VPAngsz4YueF;2z8(4j^!$sGRHti_hWQc= zRAFDOYYLb`$d8>S&CEXiK0vvIG7w0RrhjNDUz_BW1#1R5IT?5<>vs(X~mlR;mOE z*{%|ZnbHD>Qi6m~J4+3MG?~_+aX4gI{8MrW438{mm?8ZKF%YwMcS9^P*u_V<}Mh%OYE#xFX| z2mlwqV3r0wEL|2@MFeO^IfKjeb4h|+d8=Y1lbKM11+txG%jyIB=R{=VV9ifTpX2QU zzYYpwvg06(h97Z2f6iLA5pTkCxB4-V8~2?vF{6kE6fydcXk4MkTev&IlY9r`3Lml zYFV}`ye8OdTqJNvl^~l@Gk=Znde?*{UN)lzk-{}&=8T8#)r9&r0Q7~+AaLY30sRW> zc73Y4`Z^Kv(_;U7aRcE0vOF06Y!{d@P@NE2s|y0?gN|qQ6hbBRL-2PER1V8O5LZLU zOIM-bx(&wGfOczM1E5b&R37!o0@K`2d?~nCsl0>SH zebvn@EJ+Rti4Unk4CAbj|AWH+HsD{6n*qKS`!l>?GZX?0#0+K#4(Q5GsEDDjGaw!Y z!ocu1?Yspjm6M}?!(WgP+gh^+jER}w$*i{m+aQ6dApq=vUxNwYr^Yc+Lkzg-*f4Y| zpg{D77M`SQz{AIGLH!W)fG>@2R3}*{LVip-CNlS%0lu0r0^05976rTz4CXK}sGuT% zrYDRMIe;z&+(O{%l3EJ#OSTF^s4B!@HFY~R5QwaA{KzZAhFC92p}^+rDA{u3~;@hv0n)k0QOOko#94F z(p`JN6dutG5bN0p1qllNLjWu(D(d$w`2hs%v30M7Fsyfv;gf)Q_$Y`Pc(a z_K9Jv^p=(v67Fsu%vS`b+(c4t-hXhwsB zw4NM7^)JjkW-MS(7)U|j^fCN#O&HiHFOZrQ5FV>4;g<|YIJIFhrO}cYAg2XRrHVut zsA+&h=PfQm0@$29m}}q{Q)xda+qF<|XI$@w_uJ3?W`Hju59cJ@pN|7$;L@o2`E*Jk zk^w`A%j$q2W^g@BSm1*iq-gjHS(k)4W=vEll`v2#P;pU+NbEUE5Sd9aE($i{ft)jl zKH4UdyL*cWmfF%VaTx_0G`0%>SO9(^0KXqa!1|T!X0>Lo7yV{{FUd!#)<6q`0YaTf*MfxjTRZjwW4Yh~TW90| ze`_Hk{%s@dR#jv*-cGq8^3o&xaudFgg2xD)L|$9ugQx@I-q zd4R7a4<>;=1LR9X&g$0$VAw_(81jtmNQm}>v0kc@JQ!e!2NlyVnE`U{-|7Yhq@hO% z($-?R-4b%^S43wAvTb_6?K91Yj2H}XG{%gs@?g5XM0PtwR zQS_q~xq;(F(9h0<#zc_F$KpT%muWrDxnKl?VM-x%yfe`czOES$1_)xEf|L?g-X96o zhv~3UYM=@SLs6z#0Y-7Xyl7yihzP-&Q8QsRaKR=blqF*|DBLww)F8o97vd0WtRQ|D z0v^e>1&vTm8i7=!e|tU??BOAGY+Q>jVh2EZ5dfB|>b(GYH!=%!#besUuas}e$e zI0(ovh}%NI067|blsgTQ>P}(?e&G`n?5Qs?VaOtqbj83ab(0B#G1n{c5KRH$4@Z&u ze2KT1?4Sr#Ht2mps5%b@z*tAL1ERg&j``4Qbr{nR*0oV*=XY~NLoEvf zLn;RFt-&pzT16tC682qTyF=A$9UYyXH+)`5+;SPdxp~CmYfuMebozjjVKj<^+uDf7 zTLK#X?lhPe6u_Rwy-VpZPymU;>cJo89zvQhN8M}aLoiPu>^TOcJ%0uQX9vsE9rKzf zCR`T(Qw$L5Kx7GrpXdSIeCRarkl_LA)EEo%bTg2kX`LD190n$g48JI*)2qP>Ga(H> z40VQki?Pc@z^-3(f>ARiNUPB50e}6D0>2vak>T@I0=XFKRqm&!!vjWi#D~D$I2ku| zo%Ad*1t9=*LLmf+82)@bxJZzwA|!m7@Z1k{L9m&IHrhx6IBJ(24ZvRgvG4?=+ZKFAw8h7ObET0~G``YQs1n z7(;O2kDE@3o-3UNSSI;Pb?+f$7y7VJ@P~yaeR!o>4YKeL9Rv_SIBtLh2sV!g@^U2s zb_PK|EaLA9!U9`)dc4STDb6tq3dS;);bG8o-RM3BREALzUiA%ilL5|Fw+VlT?FNij zl7|n6c2o6H2?9sIaw4RMkk5#xx`lwo3E^e$lVcr8&l1uF`lq76yTJhGG_7EPbsPXO z5@h-qFzq#O3j-xMDAa}>!iF%Mh~lhIf~Z*yHs+BEcqC++J*wuabFEZ~R)CjMD| z4S1epZJTP-W4GO2j{Krc_)GP-elx(AzblRx?sgyrg_XE&u{s9Vu;yYhW++&bKa9O> zgZypF@eQ(h_oox-1095clA_f<;J`nTZd{?h23SS0wUXOWinLftTcpl9c`Gs z%zeIDhk?fu76$~00gkS8E*#zG0bni}jj40pthGL(szH_vw6T5RC{lrmz`*0t9jenx zNdP{Ib1YnmBIl3-RAqq0hBks@1zpSlsd^0|V26=92F5;SfAcT$c7flhL>54g25#s> z2nM}cF*pY-1r|_1%>dD$5)rJ&&R=TF;hl31(uV$d0RzP36pEP#fbE*mYl0h0nKBhz zFvyTNXcy;ASCI(H2Vi6fq_7CLQgTW$3Q3G9jXzwc3T`4pC7PuvA-+IU@AxJnXv_dH zC^Py%Ky72JQ)`?35sSVV;ERba56$^7Ygi2`b%lNFi%(|)<`6nxoCNq=2CuagkDWid zC#fL}?BO6t9#9Yyb_v=41^(H!5`cbOMjBFbia}(dAdoQSvaoOgO%by0)Ior>qvN64 zfoq<&IzTT8U{A?5V0ZqkiWiJ2iBzL>o@Ris5_yAFfh07-N&J|4wI&=1VegmS-R`l! zhIa1(zBK#O43H@xpzi_$ydc{wg~~;u;Q(W>PKESY>m|h0m+71rD6+ZOXCD*B1lZH7 zg@2SBO&F*tAMX!BYB^Y082v19*ase#0(z(*u)$QvX9Mt3lub+tTTH2xc0$WFDE^d0 z3UG)akl(Epzgq?ZHjJa|;Y0elj)Yie0M(inV1H^abv$<>^s6TW(wbaiYA5;!?1Rxs zivik6NCp8MGp5~YE9Eb2RV7H{MDP~ET)^&tA2UE;qCOS^{#O8W!~r!dVjimYiM8yt z>TS88Cf==+I3Do@csj6@qsCZLf^a@`>h?K1Vs(UPgo9Y~FT$6wPYwvH>eM{;_glGL z;J0#!r=oQ?8vIzp!n{k;LdYsHT1P-I87R08`${VNVM1SX#?9zgDIgI+<@v5Y@ei=b z;SYlWLRD*$ecx-XDW@%NfWrA)mu)!Isg$z*WG6@Ftj~&ojCPoyF*2GMNna5IBy~L6 zJRpp|Crsy7WJuT4?@ohx7Y?Z5kK-SHLJW`)Ha{T-S%4G+9<#zij)g4ri=`{%Lv(mZ z50YTGKmxfe%$Q+c{7-ncfc<^gZwC1C74nGzLi-dB zVx`2RggJZAX z1nRn|-eEZH_F_d0(4~PvHVT1YAcuIg74wxXwfTZ>io*>d@PUVZQ95gYx6#l1k6EBy zI2wNMQqUCgDIJBdENOUjZJ5)%%$OZVm>8fCc|>EnhHkuAGc+QJ7@VXQ#R1K>dXnFg zKY~B}b>2acwdpw@d2jn{pYTCEsFv!E( z3H0kl>oCdWf12+>0h7ZF9S<=;j3k5OG=+974I@l6>riL6kI=EtSZ$7_zPAqDLQ_jK zUj>s}>ZTJ1Bqv`7e)GrF4=Lui3;dSV5qt!k1CL;RwhxR_Flw)F2xh#Sj&EA`dz8M= z?W21_U^<2pBm?iJ=1bS3c8O{KZ#?Ma2&2Qs3=m&%*!WZk3*us68;xcPbv6eD(UM4U z>ndpA;Q$6StPMVRt@TPA5DEu=E8n=R(XZVu@cTtWxTE>8U?^U7gchWpF^^6IW2kvj zzaXhtMFJn-c+iF-A@*G%<7Y`AodJQZgZ~lUjn0i=ju_ysm7U@xn@H&_jVK|hn9Y;J z4VsC9NCdpx99X*84D&=l`or}4kMO7eg}93$yiF7YE9!1(1b`p&K0gcQjCt%4w^5V*9?%h3%N52!cs)i z4?q?v-)u`+9FSlS|K({#K8W`%4G?$-LEf=Ij8P(*8=nY<21vB3MCTB8vrfX8hTr?7 zejS|9Xmh~0a4t&dL+8qXj|+$5mX7_up_{~q`JeZl86I3RIYWc^72_az2+rhqpCeABaVTov$2c|h9xGo0cag2DeL z_>ELQiK%oWE84j=7--l}6wtUx_!<~alA&wGF!Djy4H#$?3?xT*j1&U#nV`)G!E?B_ z{u-bzK_XytLD1oZj@f+2znOLvCkG^UTayea=j~9VpyH6*1->gK%x<0rIZp-yw8J1< zkdJa;T3&m8;Q+q}6$-T5>=-5zW(mjz#EUC|kbc6sDna73QRXuy%xFrXp_3o1>m|&M zdtaJiR$xfQV{AuxwV{Ju88GBEHo4!>dvy8)v0(LW*|7E38wWcuoKdjdX)x~~?%g0z ztzhs}mI=l+0}JUO2&C7%9mH>eA86jty&8cU1ks~Nr9Hr*k?KQXlOR%w`z?irHF-b? zpLgjR46De7r6X+(Y-ccRMy%u`E|{5I*|_v&OuUS-x)kCrI?4;*ciI%d5&GQ>@GcUq zpl}U-p?u1JfhYq3U_TMRabqrrMRuCGJycm3=oKe|mmSG47Y~LM39>byA0r=wAH!g0 z^n-N>?GbJwWxl^44G&DTfIQs_Q=^B$vwBqh#ptf1sYzo1(X(C|j$azl7_r>St zuHPL6ep3#}@Q3ad$3iEga)<$d+mrVlRsEvGEjLPr0rT^&&7{lI7ZxUw1O`ax2hDYa zi-^Qd@W-`M=fW%$L-?nOA2YxvA}~Swim2cyG$w#dm5N9qR8wecHs`t$m_6R7C@5bK z3JwVJmm5Jokv`^scNF*yVGufXyI~*(NVTy5fUV{&$ZSzkuANxlS*K2o?+aOmAYnPd zI4MHd=S!9#o8A)yJfdU3teY{+2LnD|NznN6qBO${U?2j>5rUNnsCs8c=5sy|C~2jG z7L1_iPYzf%8FKtEST12c|Ko5MLwG;z!(uI&1)7nNsh$|1dc&^pwtz20NvSl&`V
<)NCeaY3NJdtNklfa;WtN^C@zA99|~Cw@Tcp*;N53=AZPrsG?&w&qP2Y~f^5Ae>2F-V}<_|D*|L6#Ac!bo%3 z!275^KG8woAIxUcSusgu+wlf+B_5naLgG>}cI|vh6~ts<;GCHOW|0iYgMBQnkU)kT zhDmTgzHUTB2+ROh*pMraVhs-)oAxI!U|t0Pl;EtZ=Z2hi3(m zVQt1Rl^mqB-MKz}OjyIRkLF_^BEp+U0@R(~5gM}K_*=qSp`Ff^=7d0Ch znGt9(Kyr!k3E(G!BC!$S8?(y+=kbpQ$m2VcC?>3%Q7?uW zVb}Jt9;e{2)NRP+?Lad+(l9V2%|yU612hOARs^jCfwUW`X&`0p;ghC~Eemq~h75z_ z>@T?i@D`vSP*8O%A6hAV5@Zh0Q4i4F1Z3ieK4)hu5N>Xs9B`UI0a@5s=J8(=1%4g# zGX5bG#)t|pRZThvMvFRPyevN@42eagz{&&=CQ6EdwHcW>#3V5inL5&|0g zeK!NVg#@mz5eiX@KOYT&ga=Jskl?g23qx?4-4vUSXgxsK5)ThieYoHZ-)vCd}57+tJ&4D4h{&bg2G{D4|rBTO@5`A zF?B2%^fW2tHadqznn+yO$LEHj;yo5u{ainnT4eKb3?TqKp8(qaFsB@1p6G{^KNcY1 zb=iBbfBkc#!uys8#L~w;Olmntstc2hJQyb|qB-Buu{0YHFf_9s7A#O(MEKW`ZZW_) z8zB%FRbvR81aLtoMLe(s^iffTZ|wW=&2;2CTZcy|sm39I3f>%1VGk*_o3iH_3~pD! zZ^%D40KSuG%)_AZ&o7sMj9eP^sg)po*iB)_SMfe*Q%7?+U~G6Z&7)}!xU?!FvF@I9 z4uo_}=kDC0#5mYQM3s=H2}`994YL1CIAjyqjY0wzayICke#d}_xfy**%e?W|8y?6w z!q~2Y-vYqDZ%}wMdC=0uK06AGNn>IQp+2s0K*l{-7z0^d2q<-bnC0=cp@>9j4)#%4 zn1nEo2K(xyB$7Gy!I#JFG=8CUg6{)wBHb0jKE^P1iAVPwazejUx}Wk`1euhs=zY|` z|N8r;fbS*b_F}M&D=5<|Sk06FXnlXYbhmf(9yf+U#it-u};{=Fcbm?8VZ&=f0VXZRq1 zXB%eP3MAWXOTF$7GrVNntKl%lZwhZ6Ugv(0$MBVX@PodO@$Y{D@TPz_4YUx@@`R6v z4~BpP<{#SQ*VnRAN0y{g4+rGax)Gv8!~E{C7=0?h7iba0K5_Kje{Xr6aK-7 zNjGpm#Q>+JhERv*2+)rZ?`sgaKkB^KL;1lZRiea-2+C)q>j#>ezy>w;L8Ura>;j(` zM*bf@)Bf?dm|z8rI6HOCS<@>|>O#LQGy3_GkrQo#A8h+{fF}U{{kQ?)Jt5G}iMH7v zCapiPL@G3KK$i*n;tr$3npU(5GNWaAg7>1|R$M zf}=oSiWA_14hAzudDeR z%amb`)ceM@%b2Gjni&K9cmpC|17AMb^+N%RjWJOBUy07*qoM6N<$ Ef}PNQu>b%7 literal 0 HcmV?d00001 diff --git a/docs/content/config.json b/docs/content/config.json index 55a2b5f..de5748d 100644 --- a/docs/content/config.json +++ b/docs/content/config.json @@ -1,26 +1,17 @@ { "links": { "home": { - "title": "Your project name", + "title": "Tuyau", "href": "/" }, "github": { - "title": "Your project on Github", - "href": "https://github.com/dimerapp" + "title": "Tuyau on GitHub", + "href": "https://github.com/Julien-R44/tuyau" } }, - "sponsors_sources": [ - ], + "sponsors_sources": ["julien-r44"], "search": { }, - "advertising_sponsors": [ - { - "link": "", - "logo": "", - "logo_dark": "", - "logo_styles": "" - } - ], - "fileEditBaseUrl": "https://github.com/dimerapp/docs-boilerplate/blob/develop", - "copyright": "Your project legal name" + "fileEditBaseUrl": "https://github.com/tuyau/tree/main/docs", + "copyright": "Tuyau" } diff --git a/docs/content/docs/client.md b/docs/content/docs/client.md new file mode 100644 index 0000000..2e184f4 --- /dev/null +++ b/docs/content/docs/client.md @@ -0,0 +1,366 @@ +--- +summary: How to use Tuyau RPC / E2E Client in your AdonisJS project +--- + +# Client + +As mentioned in the [installation](./installation.md), we used `createTuyau` to create our client instance. This instance will be used to make requests to our API. The following options are available: + +## Options + +### `api` + +The `api` object is the generated API definition from your AdonisJS project. This object contains everything needed for Tuyau to work. It contains the routes, the types, the definitions, etc. + +```ts +import { api } from '@your-monorepo/server/.adonisjs/api' + +export const tuyau = createTuyau({ + api, + baseUrl: 'http://localhost:3333', +}) +``` + +As you can see, the `api` is not a type, but a real runtime object. You might ask, why? The `api` is an object that contains two things: + - The definition of your API. This is just a type with no runtime code. + - The routes of your API. This is a "real" object that contains all the routes with their names and paths. Since we need to map the route names to paths, we need some runtime code for that. + +If you're not interested in using the route names in your frontend project, you can simply import the `ApiDefinition` type from the `@tuyau/client` package and ignore the `api` object: + +```ts +/// + +import { createTuyau } from '@tuyau/client' +import type { ApiDefinition } from '@your-monorepo/server/.adonisjs/api' + +export const tuyau = createTuyau<{ definition: ApiDefinition }>({ + baseUrl: 'http://localhost:3333', +}) +``` + +To clarify, if you don't need to use methods like `tuyau.$url('users.posts.show', { id: 1, postId: 2 })`, `$tuyau.route`, or the `Link` component from `@tuyau/inertia`, you can ignore the `api` object and only pass the `ApiDefinition` type to `createTuyau`. + +### `baseUrl` + +The `baseUrl` option is the base URL of your API. + +```ts +export const tuyau = createTuyau({ + api, + baseUrl: 'http://localhost:3333', +}) +``` + +### Other options + +The Tuyau client is built on top of [Ky](https://github.com/sindresorhus/ky). So you can pass any options supported by Ky. Here's an example with some options: + +```ts +const tuyau = createTuyau({ + api, + baseUrl: 'http://localhost:3333', + timeout: 10_000, + headers: { 'X-Custom-Header': 'foobar' }, + hooks: { + beforeRequest: [ + (request) => { + const token = getToken() + if (token) { + request.headers.set('Authorization', `Bearer ${token}`) + } + } + ] + } +}) +``` + +## Making requests + +Making requests with Tuyau is pretty straightforward. Essentially, you need to chain the different parts of the route you want to call using `.` instead of `/` and then call the `$method` you want to use. Let's look at some examples: + +```ts +import { tuyau } from './tuyau' + +// GET /users +await tuyau.users.$get() + +// POST /users { name: 'John Doe' } +await tuyau.users.$post({ name: 'John Doe' }) + +// PUT /users/1 { name: 'John Doe' } +await tuyau.users({ id: 1 }).$put({ name: 'John Doe' }) + +// GET /users/1/posts?limit=10&page=1 +await tuyau.users.$get({ query: { page: 1, limit: 10 } }) +``` + +## Making Requests using the route name + +If you prefer to use route names instead of paths, you can use the `$route` method: + +```ts +// Backend +router.get('/posts/:id/generate-invitation', '...') + .as('posts.generateInvitation') + +// Client +await tuyau + .$route('posts.generateInvitation', { id: 1 }) + .$get({ query: { limit: 10, page: 1 } }) +``` + +## Path parameters + +When calling a route with path parameters, pass an object to the related function. For example: + +```ts +// Backend +router.get('/users/:id/posts/:postId/comments/:commentId', '...') + +// Frontend +const result = await tuyau.users({ id: 1 }) + .posts({ postId: 2 }) + .comments({ commentId: 3 }) + .$get() +``` + +## Request Parameters + +You can pass specific `Ky` options to the request by providing them as a second argument to the request method: + +```ts +await tuyau.users.$post({ name: 'John Doe' }, { + headers: { + 'X-Custom-Header': 'foobar' + } +}) +``` + +When using the `$get` method, you can pass a `query` object to the request: + +```ts +await tuyau.users.$get({ + headers: { 'X-Custom-Header': 'foobar' }, + query: { page: 1, limit: 10 } +}) +``` + +Note that the `query` object will automatically be serialized into a query string with the following rules: + +- If the value is an array, it will be serialized using the `brackets` format. For example, `{ ids: [1, 2, 3] }` will be serialized as `ids[]=1&ids[]=2&ids[]=3`. +- If the value is null or undefined, it will be ignored and not added to the query string. + +## File uploads + +You can pass `File` instances to the request to upload files. Here's an example: + +```html + +``` + +```ts +const fileInput = document.getElementById('file') as HTMLInputElement +const file = fileInput.files[0] + +await tuyau.users.$post({ avatar: file }) +``` + +When a `File` instance is passed, Tuyau will automatically convert it to a `FormData` instance and set the appropriate headers. The payload will be serialized using the [`object-to-formdata`](https://www.npmjs.com/package/object-to-formdata) package. + +If you're using React Native, pass your file as follows: + +```ts +await tuyau.users.$post({ + avatar: { + uri: 'file://path/to/file', + type: 'image/jpeg', + name: 'avatar.jpg' + } +}) +``` + +## Responses + +For every request, Tuyau returns a promise with the following types: + +- `data`: The response data if the status is 2xx +- `error`: The error data if the status is 3xx +- `status`: The response's status code +- `response`: The full response object + +You must narrow the type of the response. That means you should check if the status is 2xx or 3xx and use the `data` or `error` property accordingly. + +Here's a simple example. A route returns a 401 if the password is incorrect; otherwise, it returns a secret token: + +```ts +// Backend +class MyController { + public async login({ request, response }) { + const { email, password } = request.validateUsing(schema) + if (password !== 'password') { + return response.unauthorized({ message: 'Invalid credentials' }) + } + + return { token: 'secret-token' } + } +} + +router.post('/login', [MyController, 'login']) + +// Frontend +const { data, error } = await tuyau.login.$post({ email: 'foo@ok.com', password: 'password' }) + +data +// ^? { token: string } | null + +if (error?.status === 401) { + console.error('Wrong password !!') + return +} + +console.log(data.token) +// ^? { token: string } +// data.token will be available and unwrapped here +``` + +Without narrowing the response type, `data` and `error` could be `undefined`, so you must check before using them. + +### Unwrapping the response + +If you prefer not to handle errors in your code, you can use the `unwrap` method to unwrap the response and throw an error if the status is not 2xx. + +```ts +const result = await tuyau.login.$post({ email: 'foo@ok.com' }).unwrap() +console.log(result.token) +``` + +## Inferring request and response types + +The client package provides helpers to infer the request and response types of a route. For example: + +```ts +import type { InferResponseType, InferErrorType, InferRequestType } from '@tuyau/client'; + +// InferRequestType +type LoginRequest = InferRequestType; + +// InferResponseType +type LoginResponse = InferResponseType; + +// InferErrorType +type LoginError = InferErrorType; +``` + +## Generating URL + +If you need to generate the URL of a route without making the request, you can use the `$url` method: + +```ts +const url = tuyau.users.$url() +console.log(url) // http://localhost:3333/users + +const url = tuyau.users({ id: 1 }).posts({ postId: 2 }).$url() +console.log(url) // http://localhost:3333/users/1/posts/2 +``` + +### Generating URL from route name + +To generate a + + URL using the route name, you can use the `$url` method. This is similar to how [Ziggy](https://github.com/tighten/ziggy) works: + +```ts +// http://localhost:3333/users/1/posts/2 +tuyau.$url('users.posts', { id: 1, postId: 2 }) + +// http://localhost:3333/venues/1/events/2 +tuyau.$url('venues.events.show', [1, 2]) + +// http://localhost:3333/users?page=1&limit=10 +tuyau.$url('users', { query: { page: 1, limit: 10 } }) +``` + +If you're familiar with Ziggy and prefer a `route` method instead of `$url`, you can easily define a custom method in your client file: + +```ts +export const tuyau = createTuyau({ + api, + baseUrl: 'http://localhost:3333' +}) + +window.route = tuyau.$url.bind(tuyau) +``` + +You can then use the `route` method in your frontend code: + +```tsx +export function MyComponent() { + return ( + + ) +} +``` + +## Checking the current route + +Tuyau has helpers to check the current route. You can use the `$current` method to get or verify the current route: + +```ts +// Current window location is http://localhost:3000/users/1/posts/2, route name is users.posts.show +tuyau.$current() // users.posts +tuyau.$current('users.posts.show') // true +tuyau.$current('users.*') // true +tuyau.$current('users.edit') // false +``` + +You can also specify route parameters or query parameters to check: + +```ts +tuyau.$current('users.posts.show', { params: { id: 1, postId: 2 } }) // true +tuyau.$current('users.posts.show', { params: { id: 12 } }) // false +tuyau.$current('users.posts.show', { query: { page: 1 } }) // false +``` + +## Checking if a route exists + +To check if a route name exists, you can use the `$has` method. You can also use wildcards in the route name: + +```ts +tuyau.$has('users') // true +tuyau.$has('users.posts') // true +tuyau.$has('users.*.comments') // true +tuyau.$has('users.*') // true +tuyau.$has('non-existent') // false +``` + +## Filtering generated routes + +If you need to filter the routes generated by Tuyau, you can use the `only` and `except` options in `config/tuyau.ts`: + +```ts +export default defineConfig({ + codegen: { + definitions: { + only: [/users/], + // OR + except: [/users/] + }, + + routes: { + only: [/users/], + // OR + except: [/users/] + } + } +}) +``` + +You can use only one of `only` or `except` at the same time. Both options accept an array of strings, an array of regular expressions, or a function that receives the route name and returns a boolean. + +`definitions` will filter the generated types in the `ApiDefinition` interface. `routes` will filter the route names generated in the `routes` object. + +--- + +Let me know if you need any adjustments! diff --git a/docs/content/docs/db.json b/docs/content/docs/db.json index 3771db2..b9b32ac 100644 --- a/docs/content/docs/db.json +++ b/docs/content/docs/db.json @@ -4,5 +4,29 @@ "title": "Introduction", "contentPath": "./introduction.md", "category": "Guides" + }, + { + "permalink": "installation", + "title": "Installation", + "contentPath": "./installation.md", + "category": "Guides" + }, + { + "permalink": "Client", + "title": "Client", + "contentPath": "./client.md", + "category": "Guides" + }, + { + "permalink": "Inertia", + "title": "Inertia", + "contentPath": "./inertia.md", + "category": "Guides" + }, + { + "permalink": "OpenAPI", + "title": "OpenAPI", + "contentPath": "./openapi.md", + "category": "Guides" } -] \ No newline at end of file +] diff --git a/docs/content/docs/inertia.md b/docs/content/docs/inertia.md new file mode 100644 index 0000000..dda5d9a --- /dev/null +++ b/docs/content/docs/inertia.md @@ -0,0 +1,83 @@ +# Inertia package + +Tuyau also provides a set of helpers for Inertia projects. The package is called `@tuyau/inertia`. First, make sure to have generated the API definition in your AdonisJS project using `@tuyau/core` and also have configured the client in your frontend project using `@tuyau/client`. + +Then, you can install the package in your frontend project : + +```bash +pnpm add @tuyau/inertia +``` +## React usage + +To use the Inertia helpers in your React x Inertia project, you must wrap your app with the `TuyauProvider` component : + +```tsx +// inertia/app/app.tsx +import { TuyauProvider } from '@tuyau/inertia/react' +import { tuyau } from './tuyau' + +createInertiaApp({ + // ... + + setup({ el, App, props }) { + hydrateRoot( + el, + <> + + + + + ) + }, +}) +``` + +As you can see, you must pass an instance of the Tuyau client to the `TuyauProvider` component. Also, if you are using SSR, make sure to also wrap your app with the `TuyauProvider` component in your `inertia/app/ssr.tsx` file. + +### Link + +The `Link` component is a wrapper around the Inertia `Link` component with some additional typesafety. Tuyau `Link` component will accept the same props as the Inertia `Link` component except for the `href` and `method` props. They are replaced by the `route` and `params` props. + +```tsx +import { Link } from '@tuyau/inertia/react' + +Go to post +``` + +## Vue usage + +To use the Inertia helpers in your Vue x Inertia project, you must install the Tuyau plugin : + +```ts +// inertia/app/app.ts + +import { TuyauPlugin } from '@tuyau/inertia/vue' +import { tuyau } from './tuyau' + +createInertiaApp({ + // ... + + setup({ el, App, props, plugin }) { + createSSRApp({ render: () => h(App, props) }) + .use(plugin) + .use(TuyauPlugin, { client: tuyau }) + .mount(el) + }, +}) +``` + +As you can see, you must pass an instance of the Tuyau client to the `TuyauPlugin` plugin. Also, if you are using SSR, make sure to also install the `TuyauPlugin` plugin in your `inertia/app/ssr.ts` file. + +### Link + +The `Link` component is a wrapper around the Inertia `Link` component with some additional typesafety. Tuyau `Link` component will accept the same props as the Inertia `Link` component except for the `href` and `method` props. They are replaced by the `route` and `params` props. + +```vue + + + +``` diff --git a/docs/content/docs/installation.md b/docs/content/docs/installation.md new file mode 100644 index 0000000..da44b52 --- /dev/null +++ b/docs/content/docs/installation.md @@ -0,0 +1,136 @@ +--- +summary: Setup Tuyau in your application +--- + +# Installation + +:::warning +- Tuyau is an ESM-only package. You will also need Node.js 18 or higher. +- This guide will assume you have a monorepo with your AdonisJS project, and your frontend project. As with most E2E client tools, it is highly recommended to have a monorepo to facilitate the generation and use of the client. +::: + +First make sure to install the code package in your AdonisJS project : + +```bash +node ace add @tuyau/core +``` + +Then, you can install the client package in your frontend project: + +```bash +pnpm add @tuyau/client +``` + +## Usage + +### Core package + +The core package expose a single command called `node ace tuyau:generate`. This command will generate the typescript types needed for the client package to work. + +**This command will NOT run automatically for now. You will need to run it manually after some specific changes in your AdonisJS project :** + +- After adding a new route/controller in your project +- After adding a `request.validateUsing` call in your controller method. + +Other than that, you will not need to run this command. Let's say you update the return type of a controller method, or you update the Vine schema : **you DON'T need to run the command.** + +Later on, we will use the [`onSourceFileChanged` hook](https://docs.adonisjs.com/guides/experimental-assembler-hooks#onsourcefilechanged) to run the command automatically when needed. + +To run the command manually, you must run : + +```bash +node ace tuyau:generate +``` + +And an appropriate `.adonisjs/api.ts` file will be generated in your project. + +### Sharing the API definition + +The command will generate a file called `.adonisjs/api.ts` in your project. This file will contain the definition of your API. You must export this file in your project to use the client package. + +So, let's say your monorepo structure is like this : + +``` +apps + frontend + server +``` + +You must export the `.adonisjs/api.ts` file from your server workspace using [subpath exports](https://nodejs.org/api/packages.html#subpath-exports) : + +```jsonc +// package.json +{ + "name": "@acme/server", + "type": "module", + "version": "0.0.0", + "private": true, + "exports": { + "./api": "./.adonisjs/api.ts" + }, +} +``` + +Once done, make sure to include `@acme/server` as a dependency in your frontend workspace : + +```jsonc +// package.json +{ + "name": "@acme/frontend", + "type": "module", + "version": "0.0.0", + "private": true, + "dependencies": { + "@acme/server": "workspace:*" + } +} +``` + +:::warning +Make sure you package manager or monorepo tool is able to resolve the `workspace:*` syntax. If not, you will need to use whatever syntax your tool is using. +::: + +Then you should be able to import the API definition in your frontend project : + +```ts +import { api } from '@acme/server/api' +``` + +#### Initializing the client + +Once installed, you must create the tuyau client in your frontend project : + +```ts +/// + +import { createTuyau } from '@tuyau/client' +import { api } from '@your-monorepo/server/.adonisjs/api' + +export const tuyau = createTuyau({ + api, + baseUrl: 'http://localhost:3333', +}) +``` + +Multiple things to note here : + +- We must reference the `adonisrc.ts` file at the top of the file. By doing that, the frontend project will be aware of some types defined in the AdonisJS project. +- We must import `api` from the `.adonisjs/api` file in your AdonisJS project. You should change the path to match your project structure. +- As you can see, the `api` is not a type, but a real object. You may ask why ? `api` is an object that contains two things : + - The definition of you API. This is just a type. No runtime code for that. + - The routes of your API. This is a "real" object that contains all the routes with their names and paths. Since we need to map the route names to the paths, we need to have some runtime code for that. + +If you are not interested in using the route names in your frontend project, you can just import the `ApiDefinition` type from the `@tuyau/client` package and ignore the `api` object : + +```ts +/// + +import { createTuyau } from '@tuyau/client' +import type { ApiDefinition } from '@your-monorepo/server/.adonisjs/api' + +export const tuyau = createTuyau<{ definition: ApiDefinition }>({ + baseUrl: 'http://localhost:3333', +}) +``` + +By doing that, you will not have additional runtime code in your frontend project but you will lose the ability to use the route names in your frontend project ( `$has`, `$current`, `$route` and other route helpers ). However, you will still benefit from the typesafety of the API definition when calling your routes by their path ( e.g. `tuyau.users.$get()` ). diff --git a/docs/content/docs/introduction.md b/docs/content/docs/introduction.md index 723e4f1..349e219 100644 --- a/docs/content/docs/introduction.md +++ b/docs/content/docs/introduction.md @@ -1,277 +1,156 @@ -# Docs boilerplate +# Introduction -The boilerplate repo we use across AdonisJS projects to create a documentation website. The boilerplate allows for maximum customization without getting verbose. +![](assets/images/banner.png) -## Why not use something like VitePress? -I have never been a big fan of a frontend first tooling when rendering markdown files to static HTML. I still remember the Gridsome and Gatsby days, when it was considered normal to use GraphQL to build a static website πŸ˜‡. +A set of tools to create typesafe APIs with AdonisJS, including an E2E client like tRPC, Inertia helpers, Ziggy-like and more. -With that said, the [feature set around rendering markdown](https://vitepress.dev/guide/markdown) feels modern and refreshing with frontend tooling. But, the underlying libraries are not limited to the frontend ecosystem, and you can use them within any JavaScript project. +- **E2E typesafe client**: Generate a client to consume your AdonisJS API with 100% typesafety. +- **Ziggy-like helper**: Generate and use routes in the frontend with typesafety. +- **OpenAPI generation**: Generate an OpenAPI definition from your AdonisJS project based on Tuyau codegen. +- **Inertia helpers**: A set of components and helpers for AdonisJS + Inertia projects. -So, if I have all the tools at my disposal, why not build and use something simple that does not change with the new wave of innovation in the frontend ecosystem? +## Project Goals -## Workflow -The docs boilerplate is built around the following workflow requirements. +The main goal of this project is to provide utilities for better typesafety when creating APIs with AdonisJS. Long-term goals include: -- Create a highly customizable markdown rendering pipeline. I need control over rendering every markdown element and tweaking its HTML output per my requirements. This is powered by [@dimerapp/markdown](https://github.com/dimerapp/markdown) and [@dimerapp/edge](https://github.com/dimerapp/edge) packages. +- **Done** : Provide an RPC-like client that is fully e2e typesafe ( like tRPC, Elysia Eden, Hono etc. ) +- **Done** : Provide a [Ziggy](https://github.com/tighten/ziggy)-like helper to generate and use routes in the frontend. +- **Done (Experimental)** : Having an automatic OpenAPI generation + Swagger/Scalar UI viewer based on Tuyau codegen. +- **In Progress** : Provide some Inertia helpers to have better typesafety when using Inertia in your AdonisJS project. Things like typesafe `` and `useForm`. +- **Not started** : Provide a specific Controller class that will allow to have better typesafety when creating your endpoints. +- **Not started**: Having a Tanstack-Query integration for the client package. Like [tRPC](https://trpc.io/docs/client/react) or [ts-rest](https://ts-rest.com/docs/vue-query) does. -- Use [Shiki](https://github.com/shikijs/shiki) for styling codeblocks. Shiki uses VSCode themes and grammar for syntax highlighting and requires zero frontend code. +## E2E/RPC-like Client? What does that mean? -- Use a [base HTML and CSS theme](https://github.com/dimerapp/docs-theme) to avoid re-building documentation websites from scratch every time. But still allow customizations to add personality to each website. +Imagine you have an AdonisJS API ready to use, and you want to consume it in your frontend React/Vue or another framework. Typically, you'd create a class or file that contains methods to call the various routes of your API: -- Use a dumb JSON file to render the docs sidebar (JSON database file). Scanning files & folders and sorting them by some convention makes refactoring a lot harder. - -- Allow linking to markdown files and auto-resolve their URLs when rendering to HTML. - -- Allow keeping images and videos next to markdown content and auto-resolve their URLs when rendering to HTML. - -## Folder structure +```typescript +export class MyAPI { + async getPosts(options) { + return fetch( + `/posts?page=${options.page}&limit=${options.limit}` + ) + } + async getPost(id: number) { + return fetch(`/posts/${id}`) + } +} ``` -. -β”œβ”€β”€ assets -β”‚ β”œβ”€β”€ app.css -β”‚ └── app.js -β”œβ”€β”€ bin -β”‚ β”œβ”€β”€ build.ts -β”‚ └── serve.ts -β”œβ”€β”€ content -β”‚ β”œβ”€β”€ docs -β”‚ └── config.json -β”œβ”€β”€ src -β”‚ β”œβ”€β”€ bootstrap.ts -β”‚ └── collections.ts -β”œβ”€β”€ templates -β”‚ β”œβ”€β”€ elements -β”‚ β”œβ”€β”€ layouts -β”‚ β”œβ”€β”€ partials -β”‚ └── docs.edge -β”œβ”€β”€ vscode_grammars -β”‚ β”œβ”€β”€ dotenv.tmLanguage.json -β”‚ └── main.ts -β”œβ”€β”€ package-lock.json -β”œβ”€β”€ package.json -β”œβ”€β”€ README.md -β”œβ”€β”€ tsconfig.json -└── vite.config.js -``` - -### The assets directory - -The `assets` directory has the CSS and frontend JavaScript entry point files. Mainly, we import additional packages and the [base theme](https://github.com/dimerapp/docs-theme) inside these files. However, feel free to tweak these files to create a more personalized website. - -### The bin directory - -The `bin` directory has two script files to start the development server and export the docs to static HTML files. These scripts boot the AdonisJS framework under the hood. - -### The content directory -The `content` directory contains the markdown and database JSON files. We organize markdown files into collections, each with its database file. - -You can think of collections as different documentation areas on the website. For example: You can create a **collection for docs**, a **collection for API** reference, and a **collection for config reference**. - -See also: [Creating new collections](#creating-new-collections) -### The src directory -The `src` directory has a `bootstrap` file to wire everything together. We do not hide the bootstrap process inside some packages. This is because we want the final projects to have complete control over configuring, pulling in extra packages, or removing unused features. +This works, but there’s no type safety. There’s no information about the data being sent or received. Also boring code to write. The next step is usually to create types for the data: -The `collections.ts` file is used to define one or more collections. +```typescript +interface Post { + id: number + title: string +} -### The templates directory -The `templates` directory contains the Edge templates used for rendering HTML. +interface GetPostsOptions { + page?: number + limit?: number +} -- The `docs` template renders a conventional documentation layout with the header, sidebar, content, and table of contents. You may use the same template across multiple collections. -- The logos are kept as SVG inside the `partials/logo.edge` and `partials/logo_mobile.edge` files. -- The base HTML fragment is part of the `layouts/main.edge` file. Feel free to add custom meta tags or scripts/fonts inside this file. +export class MyAPI { + async getPosts(options: GetPostsOptions): Promise { + return fetch( + `/posts?page=${options.page}&limit=${options.limit}` + ) + } -### The vscode_grammars directory -The `vscode_grammars` directory contains a collection of custom VSCode languages you want to use inside your project. + async getPost(id: number): Promise { + return fetch(`/posts/${id}`) + } +} +``` -See also: [Using custom VSCode grammars](#using-custom-vscode-grammars) +While this is better, we still have the problem of keeping types in sync. If a field name changes in your API, you’ll need to update it in the client too, leading to a high risk of desynchronization between the backend and frontend. It can also be tedious to create these types repeatedly. -## Usage -Clone the repo from Github. We recommend using [degit](https://www.npmjs.com/package/degit), which downloads the repo without git history. +This is a naive approach, but you get the idea. -```sh -npx degit dimerapp/docs-boilerplate -``` +Tuyau offers an alternative method: a frontend client generated automatically from your AdonisJS API, which will be 100% typesafe without maintaining any types or runtime code yourself. Tuyau uses codegen to detect input and output types for your routes. Taking the same API example, here’s how you could use it with Tuyau: -Install dependencies +```typescript +// In your frontend +import { createTuyau } from '@tuyau/client' +import { api } from '@your-monorepo/my-adonisjs-app/.adonisjs/api' -```sh -cd -npm i -``` - -Run the development server. +export const tuyau = createTuyau({ + api, + baseUrl: 'http://localhost:3333', +}) -```sh -npm run dev +const posts = tuyau.posts.$get({ page: 1, limit: 10 }) +const post = tuyau.posts({ id: 1 }).$get() ``` -And visit [http://localhost:3333/docs/introduction](http://localhost:3333/docs/introduction) URL to view the website in the browser. +Everything in this example is fully typesafe: parameters (like `/posts/:id`), payloads, query params, and responses. You can leverage TypeScript's power to avoid a lot of errors: missing response properties, forgotten request parameters, or typos in payload fields. TypeScript will notify you, and your code won’t compile until it’s corrected. -## Adding content -By default, we create a `docs` collection with an `introduction.md` file inside it. +If you’re familiar with [tRPC](https://trpc.io/docs/client/react), [Elysia Eden](https://elysia.dev/), or [Hono](https://hono.dev/), it’s the same concept. -As a first step, you should open the `content/docs/db.json` file and add all the entries for your documentation. Defining entries by hand may feel tedious at first, but it will allow easier customization in the future. +## How does it work? -A typical database entry has the following properties. +Tuyau uses codegen to generate input and output types for your AdonisJS routes. You’ll need to run the command `node ace tuyau:generate` whenever you add a new route to your project. This will generate a `.adonisjs/api.ts` file containing the following information: -```json -{ - "permalink": "introduction", - "title": "Introduction", - "contentPath": "./introduction.md", - "category": "Guides" -} -``` +- Input types for your routes’ payloads, defined using [VineJS](https://vinejs.dev/). +- Output types for your routes, inferred automatically from your controller methods' return types. +- The route names of your project, allowing you to access them without explicitly defining URLs. -- `permalink`: The unique URL for the doc. The collection prefix will be applied to the permalink automatically. See the `src/collection.ts` file for the collection prefix. -- `title`: The title to display in the sidebar. -- `contentPath`: A relative path to the markdown file. -- `category`: The grouping category for the doc. +For example, in a project like this: -Once you have defined all the entries, create markdown files and write some real content. +```typescript +import { HttpContext } from '@adonisjs/http-server' +import { vine } from 'vinejs' -## Changing website config +export const getPostsValidator = vine.compile( + vine.object({ + page: vine.number().optional(), + limit: vine.number().optional(), + }) +) -We use a very minimal configuration file to update certain website sections. The config is stored inside the `content/config.json` file. +export class PostsController { + public async index({ request }: HttpContext) { + const payload = await request.validateUsing(getPostsValidator) -```json -{ - "links": { - "home": { - "title": "Your project name", - "href": "/" - }, - "github": { - "title": "Your project on Github", - "href": "https://github.com/dimerapp" - } - }, - "fileEditBaseUrl": "https://github.com/dimerapp/docs-boilerplate/blob/develop", - "copyright": "Your project legal name" -} -``` - -- `links`: The object has two fixed links. The homepage and the Github project URL. - -- `fileEditBaseUrl`: The base URL for the file on Github. This is used inside the content footer to display the **Edit on Github** link. - -- `copyright`: The name of display in the Copyright footer. - -- `menu`: Optionally, you can define a header menu as an array of objects. - ```json - { - "menu": [ - { - "href": "/docs/introduction", - "title": "Docs", - }, - { - "href": "https://blog.project.com", - "title": "Blog", - }, - { - "href": "https://github.com/project/releases", - "title": "Releases", - } + return [ + { id: 1, title: 'Hello World' }, + { id: 2, title: 'Hello World 2' }, ] } - ``` - -- `search`: Optionally, you can define config for the Algolia search. - ```json - { - "search": { - "appId": "", - "indexName": "", - "apiKey": "" - } - } - ``` - -## Creating new collections -You may create multiple collections by defining them inside the `src/collections.ts` file. - -A collection is defined using the `Collection` class. The class accepts the URL to the database file. Also, call `collection.boot` once you have configured the collection. - -```ts -// Docs -const docs = new Collection() - .db(new URL('../content/docs/db.json', import.meta.url)) - .useRenderer(renderer) - .urlPrefix('/docs') - -await docs.boot() - -// API reference -const apiReference = new Collection() - .db(new URL('../content/api_reference/db.json', import.meta.url)) - .useRenderer(renderer) - .urlPrefix('/api') - -await apiReference.boot() +} -export const collections = [docs, apiReference] +router.get('/posts', [PostsController, 'index']).as('posts.index') ``` -## Using custom VSCode grammar -You may add custom VSCode languages support by defining them inside the `vscode_grammars/main.ts` file. Each entry must adhere to the `ILanguageRegistration` interface from [Shiki](https://github.com/shikijs/shiki/blob/main/docs/languages.md). - -## Changing the markdown code blocks theme +This would generate a `.adonisjs/api.ts` file that looks like that. This one is highly simplified just to give you an idea : -The code blocks theme is defined using the Markdown renderer instance created inside the `src/bootstrap.ts` file. You can either use one of the [pre-defined themes or a custom theme](https://github.com/dimerapp/shiki/tree/next#using-different-themes). - -```ts -export const renderer = new Renderer(view, pipeline) - .codeBlocksTheme('material-theme-palenight') +```typescript +type Api = { + posts: { + $get: { + input: { + page?: number + limit?: number + } + output: { + id: number + title: string + }[] + } + } +} ``` -## Customizing CSS - -The [base docs theme](https://github.com/dimerapp/docs-theme) makes extensive use of CSS variables, therefore you can tweak most of the styling by defining a new set of variables. - -If you want to change colors, we recommend looking at [Radix Colors](https://www.radix-ui.com/docs/colors/getting-started/installation), because this is what we have used for the default styling. - -## Customizing HTML - -The HTML output is not 100% customizable since we are not creating a generic docs generator for the rest of the world. The boilerplate is meant to be used under constraints. - -However, you can still control the layout, because all sections of the page are exported as Edge component and you can place them anywhere in the DOM. Do check the `templates/docs.edge` file to see how everything is used. - -### Header slots - -You may pass the following component slots to the website header. - -- `logo (required)`: Content for the logo to display on Desktop viewport. +That’s the general idea. We’ll cover more details in the following pages. -- `logoMobile (required)`: Content for the logo to display on Mobile viewport. +## Sponsor -- `popupMenu (optional)`: Define custom markup for the popup menu trigger. The -trigger is displayed in mobile view only. - ```edge - @component('docs::header', contentConfig) - @slots('popMenu') - Open popup menu - @end - @end - ``` +If you like this project, [please consider supporting it by sponsoring](https://github.com/sponsors/Julien-R44/). Your support will help maintain and improve it. Thanks a lot! -- `themeSwitcher (optional)`: Define custom markup for the theme switcher button. - ```edge - @component('docs::header', contentConfig) - @slots('themeSwitcher') - Dark - Light - @end - @end - ``` +## Prior art and inspirations -- `github (optional)`: Define custom markup for the github link in the header. - ```edge - @component('docs::header', contentConfig) - @slots('github') - Github (11K+ Stars) - @end - @end - ``` +- [tRPC](https://trpc.io/docs/client/react) +- [Elysia Eden](https://elysia.dev/) +- [Hono](https://hono.dev/) +- [Ziggy](https://github.com/tighten/ziggy) diff --git a/docs/content/docs/openapi.md b/docs/content/docs/openapi.md new file mode 100644 index 0000000..206509f --- /dev/null +++ b/docs/content/docs/openapi.md @@ -0,0 +1,109 @@ +--- +summary: Generate an OpenAPI specification for your API from the Tuyau codegen. +--- + +# OpenAPI package + +:::warning +OpenAPI package is still experimental and may not work as expected. Please feel free to open an issue if you encounter any problem. + +In fact, I am not sure if this package will be kept in the future. For now, this is just a test to see how far we can go with Tuyau's codegen. +::: + +The `@tuyau/openapi` package allows you to generate an OpenAPI specification for your API from the Tuyau codegen. The specification is generated from the types of routes and validation schemas, using the Typescript compiler and `ts-morph`. Therefore, it has some limitations compared to manually written specifications (whether via decorators, JS doc, YAML files, or Zod schemas describing the inputs/outputs of the routes). + +During development, the specification will be generated on the fly with each request, which can take a bit of time for large APIs with many routes. In production, the specification will be generated at build time into a `.yml` file and the same specification will be served for each request. + +### Installation + +To install the package, you will obviously need to have the `@tuyau/core` package already installed in your AdonisJS project. Then, you can install the `@tuyau/openapi` package: + +```bash +node ace add @tuyau/openapi +``` + +### Usage + +Once the package is configured, you can directly access your API's OpenAPI specification at the `/docs` address. The `/openapi` route is also available to directly access the OpenAPI specification. + +### Customizing the Specification + +To customize the specification, the package exposes `openapi` macros on the routes. These macros allow you to add additional information to the OpenAPI specification. For example, you can add tags, descriptions, responses, parameters, etc. + +```ts +router.group(() => { + router + .get("/random", [MiscController, "index"]) + .openapi({ summary: "Get a random thing" }); + + router + .get("/random/:id", [MiscController, "show"]) + .openapi({ summary: "Get a random thing by id" }); +}) + .prefix("/misc") + .openapi({ tags: ["misc"] }); +``` + +Feel free to use your editor's autocomplete to see all available options. + +Also, from the `config/tuyau.ts` file, you have the ability to customize the OpenAPI specification with the `openapi.documentation` property: + +```ts +const tuyauConfig = defineConfig({ + openapi: { + documentation: { + info: { title: 'My API!', version: '1.0.0', description: 'My super API' }, + tags: [ + { name: 'subscriptions', description: 'Operations about subscriptions' }, + ], + }, + }, +}); +``` + +### Configuration + +The package configuration is done via the `config/tuyau.ts` file, under the `openapi` key. The available options are as follows: + +#### `provider` + +The OpenAPI viewer to use. Two providers are available: `swagger-ui` and `scalar`. By default, the `scalar` provider is used. + +#### `exclude` + +Paths to exclude from the OpenAPI specification. By default, no paths are excluded. You can pass an array of strings or regex. + +Example: + +```ts +const tuyauConfig = defineConfig({ + openapi: { + exclude: ['/health', /admin/] + } +}); +``` + +#### `endpoints` + +The endpoints where the OpenAPI specification and documentation will be available. By default, the OpenAPI specification will be available at `/openapi` and the documentation at `/docs`. + +Example: + +```ts +const tuyauConfig = defineConfig({ + openapi: { + endpoints: { + spec: '/my-super-spec', + ui: '/my-super-doc' + } + } +}); +``` + +#### `scalar` + +The options to pass to the `scalar` provider when used. More details [here](https://github.com/scalar/scalar?tab=readme-ov-file#configuration). + +#### `swagger-ui` + +The options to pass to the `swagger-ui` provider when used. More details [here](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/). diff --git a/docs/src/bootstrap.ts b/docs/src/bootstrap.ts index 4b1b6de..e119fcd 100644 --- a/docs/src/bootstrap.ts +++ b/docs/src/bootstrap.ts @@ -17,8 +17,6 @@ import type { Collection} from '@dimerapp/content'; import { dimer , RenderingPipeline } from '@dimerapp/edge' import { docsHook, docsTheme } from '@dimerapp/docs-theme' -import grammars from '../vscode_grammars/main.js' - type CollectionEntry = Exclude, undefined> edge.use(dimer) @@ -86,8 +84,3 @@ pipeline.use(docsHook).use((node) => { export const renderer = new Renderer(edge, pipeline) .codeBlocksTheme('material-theme-palenight') .useTemplate('docs') - -/** - * Adding grammars - */ -grammars.forEach((grammar) => renderer.registerLanguage(grammar))