From a86c07fbf2b0dda930b662e36a5c2e1bcb8fb708 Mon Sep 17 00:00:00 2001 From: Elliot Braem Date: Thu, 30 Jan 2025 20:09:01 -0700 Subject: [PATCH] local mock testing and loading module --- .../__tests__/mocks/twitter-service.mock.ts | 127 ++++++++++-- backend/src/index.ts | 52 ++--- backend/src/routes/test.ts | 59 ++++++ backend/src/services/config/config.service.ts | 11 +- backend/src/services/twitter/client.ts | 6 +- bun.lockb | Bin 331872 -> 352664 bytes curate.config.json | 4 + curate.config.test.json | 84 ++++++++ frontend/package.json | 2 + frontend/src/routeTree.gen.ts | 135 +++++++----- frontend/src/routes/test.tsx | 194 ++++++++++++++++++ package.json | 1 + 12 files changed, 570 insertions(+), 105 deletions(-) create mode 100644 backend/src/routes/test.ts create mode 100644 curate.config.test.json create mode 100644 frontend/src/routes/test.tsx diff --git a/backend/src/__tests__/mocks/twitter-service.mock.ts b/backend/src/__tests__/mocks/twitter-service.mock.ts index fa69dfe..5bf3544 100644 --- a/backend/src/__tests__/mocks/twitter-service.mock.ts +++ b/backend/src/__tests__/mocks/twitter-service.mock.ts @@ -1,12 +1,52 @@ import { Tweet } from "agent-twitter-client"; +import { TwitterService } from "../../services/twitter/client"; +import { logger } from "../../utils/logger"; -export class MockTwitterService { +export class MockTwitterService extends TwitterService { private mockTweets: Tweet[] = []; private mockUserIds: Map = new Map(); - private lastCheckedTweetId: string | null = null; + private tweetIdCounter: bigint = BigInt(Date.now()); - public addMockTweet(tweet: Tweet) { - this.mockTweets.push(tweet); + constructor() { + // Pass empty config since we're mocking + super({ + username: "mock_user", + password: "mock_pass", + email: "mock@example.com", + }); + // Override the client with a basic mock + (this as any).client = { + isLoggedIn: async () => true, + login: async () => {}, + logout: async () => {}, + getCookies: async () => [], + setCookies: async () => {}, + }; + } + + private getNextTweetId(): string { + this.tweetIdCounter = this.tweetIdCounter + BigInt(1); + return this.tweetIdCounter.toString(); + } + + public addMockTweet(tweet: Partial & { inReplyToStatusId?: string }) { + const fullTweet: Tweet = { + id: this.getNextTweetId(), + text: tweet.text || "", + username: tweet.username || "test_user", + userId: tweet.userId || `mock-user-id-${tweet.username || "test_user"}`, + timeParsed: tweet.timeParsed || new Date(), + hashtags: tweet.hashtags || [], + mentions: tweet.mentions || [], + photos: tweet.photos || [], + urls: tweet.urls || [], + videos: tweet.videos || [], + thread: [], + inReplyToStatusId: tweet.inReplyToStatusId, + }; + this.mockTweets.push(fullTweet); + logger.info(`Mock: Added tweet "${fullTweet.text}" from @${fullTweet.username}${tweet.inReplyToStatusId ? ` as reply to ${tweet.inReplyToStatusId}` : ''}`); + return fullTweet; } public addMockUserId(username: string, userId: string) { @@ -15,14 +55,15 @@ export class MockTwitterService { public clearMockTweets() { this.mockTweets = []; + logger.info("Mock: Cleared all tweets"); } async initialize(): Promise { - // No-op for mock + logger.info("Mock Twitter service initialized"); } async stop(): Promise { - // No-op for mock + logger.info("Mock Twitter service stopped"); } async getUserIdByScreenName(screenName: string): Promise { @@ -30,7 +71,29 @@ export class MockTwitterService { } async fetchAllNewMentions(): Promise { - return this.mockTweets; + // Get the last tweet ID we processed + const lastCheckedId = this.getLastCheckedTweetId(); + + // If we have tweets and no last checked ID, set it to the newest tweet + if (this.mockTweets.length > 0 && !lastCheckedId) { + const newestTweet = this.mockTweets[this.mockTweets.length - 1]; + await this.setLastCheckedTweetId(newestTweet.id); + return [newestTweet]; + } + + // Filter tweets newer than last checked ID + const newTweets = this.mockTweets.filter(tweet => { + if (!lastCheckedId) return true; + return BigInt(tweet.id) > BigInt(lastCheckedId); + }); + + // Update last checked ID if we found new tweets + if (newTweets.length > 0) { + const newestTweet = newTweets[newTweets.length - 1]; + await this.setLastCheckedTweetId(newestTweet.id); + } + + return newTweets; } async getTweet(tweetId: string): Promise { @@ -38,18 +101,54 @@ export class MockTwitterService { } async replyToTweet(tweetId: string, message: string): Promise { - return `mock-reply-${Date.now()}`; + const replyTweet = await this.addMockTweet({ + text: message, + username: "curatedotfun", + inReplyToStatusId: tweetId, + }); + logger.info(`Mock: Replied to tweet ${tweetId} with "${message}"`); + return replyTweet.id; } - async setLastCheckedTweetId(tweetId: string): Promise { - this.lastCheckedTweetId = tweetId; + async likeTweet(tweetId: string): Promise { + logger.info(`Mock: Liked tweet ${tweetId}`); } - async likeTweet(tweetId: string): Promise { - return; + // Helper methods for test scenarios + async simulateSubmission(contentUrl: string) { + // First create the content tweet + const contentTweet = await this.addMockTweet({ + text: "Original content", + username: "content_creator", + }); + + // Then create the curator's submission as a reply + return this.addMockTweet({ + text: `@curatedotfun !submit ${contentUrl}`, + username: "curator", + userId: "mock-user-id-curator", + timeParsed: new Date(), + inReplyToStatusId: contentTweet.id, + }); + } + + async simulateApprove(submissionTweetId: string, projectId: string) { + return this.addMockTweet({ + text: `@curatedotfun !approve ${projectId}`, + username: "moderator", + userId: "mock-user-id-moderator", + timeParsed: new Date(), + inReplyToStatusId: submissionTweetId, + }); } - getLastCheckedTweetId(): string | null { - return this.lastCheckedTweetId; + async simulateReject(submissionTweetId: string, projectId: string, reason: string) { + return this.addMockTweet({ + text: `@curatedotfun !reject ${projectId} ${reason}`, + username: "moderator", + userId: "mock-user-id-moderator", + timeParsed: new Date(), + inReplyToStatusId: submissionTweetId, + }); } } diff --git a/backend/src/index.ts b/backend/src/index.ts index e9c9ab4..0fc5ac6 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -11,6 +11,7 @@ import { db } from "./services/db"; import { DistributionService } from "./services/distribution/distribution.service"; import { SubmissionService } from "./services/submissions/submission.service"; import { TwitterService } from "./services/twitter/client"; +import { testRoutes, mockTwitterService } from "./routes/test"; import { cleanup, failSpinner, @@ -50,19 +51,25 @@ export async function main() { await distributionService.initialize(config.plugins); succeedSpinner("distribution-init", "distribution service initialized"); - // Try to initialize Twitter service, but continue if it fails + // Use mock service in development, real service in production try { startSpinner("twitter-init", "Initializing Twitter service..."); - twitterService = new TwitterService({ - username: process.env.TWITTER_USERNAME!, - password: process.env.TWITTER_PASSWORD!, - email: process.env.TWITTER_EMAIL!, - twoFactorSecret: process.env.TWITTER_2FA_SECRET, - }); - await twitterService.initialize(); + if (process.env.NODE_ENV === "development") { + logger.info("Using mock Twitter service"); + twitterService = mockTwitterService; + await twitterService.initialize(); + } else { + twitterService = new TwitterService({ + username: process.env.TWITTER_USERNAME!, + password: process.env.TWITTER_PASSWORD!, + email: process.env.TWITTER_EMAIL!, + twoFactorSecret: process.env.TWITTER_2FA_SECRET, + }); + await twitterService.initialize(); + } succeedSpinner("twitter-init", "Twitter service initialized"); - // Only initialize submission service if Twitter is available + // Initialize submission service startSpinner("submission-init", "Initializing submission service..."); submissionService = new SubmissionService( twitterService, @@ -86,12 +93,12 @@ export async function main() { contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], - imgSrc: ["'self'", "data:", "https:"], // Allow images from HTTPS sources - fontSrc: ["'self'", "data:", "https:"], // Allow fonts + imgSrc: ["'self'", "data:", "https:"], + fontSrc: ["'self'", "data:", "https:"], }, }, - crossOriginEmbedderPolicy: false, // Required for some static assets - crossOriginResourcePolicy: { policy: "cross-origin" }, // Allow resources to be shared + crossOriginEmbedderPolicy: false, + crossOriginResourcePolicy: { policy: "cross-origin" }, xFrameOptions: { action: "sameorigin" }, }), ) @@ -102,6 +109,8 @@ export async function main() { }), ) .use(swagger()) + // Include test routes in development + .use(process.env.NODE_ENV === "development" ? testRoutes : new Elysia()) .get("/health", () => new Response("OK", { status: 200 })) // API Routes .get("/api/twitter/last-tweet-id", () => { @@ -180,23 +189,6 @@ export async function main() { const rawConfig = await configService.getRawConfig(); return rawConfig.feeds; }) - // .post("/api/twitter/cookies", async ({ body }: { body: TwitterCookie[] }) => { - // if (!twitterService) { - // throw new Error("Twitter service not available"); - // } - // if (!Array.isArray(body)) { - // throw new Error("Expected array of cookies"); - // } - // await twitterService.setCookies(body); - // return { success: true }; - // }) - // .get("/api/twitter/cookies", () => { - // if (!twitterService) { - // throw new Error("Twitter service not available"); - // } - // const cookies = twitterService.getCookies(); - // return cookies || []; - // }) .get( "/api/config/:feedId", ({ params: { feedId } }: { params: { feedId: string } }) => { diff --git a/backend/src/routes/test.ts b/backend/src/routes/test.ts new file mode 100644 index 0000000..518b384 --- /dev/null +++ b/backend/src/routes/test.ts @@ -0,0 +1,59 @@ +import { Elysia } from "elysia"; +import { MockTwitterService } from "../__tests__/mocks/twitter-service.mock"; +import { Tweet } from "agent-twitter-client"; + +// Create a single mock instance to maintain state +const mockTwitterService = new MockTwitterService(); + +// Helper to create a tweet object +const createTweet = (text: string, username: string): Tweet => ({ + id: Date.now().toString(), + text, + username, + userId: `mock-user-id-${username}`, + timeParsed: new Date(), + hashtags: [], + mentions: [], + photos: [], + urls: [], + videos: [], + thread: [], +}); + +export const testRoutes = new Elysia({ prefix: "/api/test" }) + .guard({ + beforeHandle: ({ request }) => { + // Only allow in development and test environments + if (process.env.NODE_ENV === "production") { + return new Response("Not found", { status: 404 }); + } + }, + }) + .get("/tweets", () => { + return mockTwitterService.fetchAllNewMentions(); + }) + .post("/tweets", async ({ body }) => { + const { text, username } = body as { text: string; username: string }; + const tweet = createTweet(text, username); + mockTwitterService.addMockTweet(tweet); + return tweet; + }) + .post("/reset", () => { + mockTwitterService.clearMockTweets(); + return { success: true }; + }) + .post("/scenario/approve", async ({ body }) => { + const { projectId } = body as { projectId: string }; + const tweet = createTweet(`@curatedotfun approve ${projectId}`, "curator"); + mockTwitterService.addMockTweet(tweet); + return { success: true, tweet }; + }) + .post("/scenario/reject", async ({ body }) => { + const { projectId, reason } = body as { projectId: string; reason: string }; + const tweet = createTweet(`@curatedotfun reject ${projectId} ${reason}`, "curator"); + mockTwitterService.addMockTweet(tweet); + return { success: true, tweet }; + }); + +// Export for use in tests and for replacing the real service +export { mockTwitterService }; diff --git a/backend/src/services/config/config.service.ts b/backend/src/services/config/config.service.ts index f999f07..ea34e6a 100644 --- a/backend/src/services/config/config.service.ts +++ b/backend/src/services/config/config.service.ts @@ -2,6 +2,7 @@ import fs from "fs/promises"; import path from "path"; import { AppConfig } from "../../types/config"; import { hydrateConfigValues } from "../../utils/config"; +import { logger } from "../../utils/logger"; export class ConfigService { private static instance: ConfigService; @@ -9,8 +10,14 @@ export class ConfigService { private configPath: string; private constructor() { - // Always look for config relative to the distribution directory - this.configPath = path.resolve(__dirname, "../../../../curate.config.json"); + // Use test config in development mode + if (process.env.NODE_ENV === "development") { + this.configPath = path.resolve(__dirname, "../../../../curate.config.test.json"); + logger.info("Using test configuration"); + } else { + this.configPath = path.resolve(__dirname, "../../../../curate.config.json"); + logger.info("Using production configuration"); + } } public static getInstance(): ConfigService { diff --git a/backend/src/services/twitter/client.ts b/backend/src/services/twitter/client.ts index a31b4ff..0243dd4 100644 --- a/backend/src/services/twitter/client.ts +++ b/backend/src/services/twitter/client.ts @@ -59,11 +59,11 @@ export class TwitterService { if (await this.client.isLoggedIn()) { // Cache the new cookies const cookies = await this.client.getCookies(); - const formattedCookies = cookies.map((cookie) => ({ + const formattedCookies: TwitterCookie[] = cookies.map((cookie) => ({ name: cookie.key, value: cookie.value, - domain: cookie.domain, - path: cookie.path, + domain: cookie.domain || ".twitter.com", // Provide default if null + path: cookie.path || "/", // Provide default if null secure: cookie.secure, httpOnly: cookie.httpOnly, sameSite: cookie.sameSite as "Strict" | "Lax" | "None" | undefined, diff --git a/bun.lockb b/bun.lockb index 6333c54daaed89c69fe2834b8f97d92b7e6ddd48..3bd94612b10c30dbd08a2cef2f47ac565fdd61d2 100755 GIT binary patch delta 81831 zcmeFa2UHZ<+AdsO&`OC36$KO(Gb(0nVk;_2GL52$q7qs_$w|=B#(+6t>1=65#WClE zaSWK#m~{|E#hev$_C9Y_HN!c}bMCqK`@a9K^)Gv!?&p1;o!|ZLUAw9n&#fg@<{z@0 z-OS#VlKPU`Hk32cIVRRz*IQ zisQ-v3wVyR1U^E8Re);<^)RNI${Ef9D}g7*w`oTeTtWsaunFBQ4_pbPhMSk-ICIJe zEDIc=G`J7>slmAljt9~}5|iSCV^Ck18OK!y_XAQpoq>AF&;~|DV0~Z(U^O5$7#puk zOpMUQqZ3la2hb`toRAa}AC#cu`oSGElGup&phR5)7d4W)&2jBeSn6Q7E;?dpDpynC z_CWGr7yMAWVUaP-gQ>trv`RgH1f-5^#}8GoRzWx9rjE_A5DluD`Wvt*+35MGi0Fve zpoE0W(8)nr7$s$Y1*CDDR`hA;ELA@SNc|Xrfl+>MAk9!6^ntJ%I5~DJcs1ZEAhmZ5 zI#8ds5Gqyh1V{z`cNNvOmOJuK6{WeM66!z$AT_kyhU1VmZ7Q%jupE9!PXb5VY4N&* zn8;+kjvG>g<7z?Z3#1Ns0%@e-K?yML&-J21P|uAWT%+H)|mGCk32}Z33rau@p}DECi=! zLy%50w-cjz%68 z5v6ko4d&iJr$uxOI=L(;AvHQ=ctm0k=+y8MR7@kT(MHZ^0i=$ofuuieEu*fjytWZ{ zL6JeBI)88qwa&mURbWg)DGK^HAQoC$j7A=L2#_lB15yWu$)U@20H?)p(@}2txTE+< zT~j|0#Yvv-EQf{%cr|e266oh5>s^7=e-|KC-v;?CfVzapi0DL)YtUZy!xSLpi_|4Y zlf(6j9;sk5ko*>^)5SVOBW~!6HR&YlwH1tYh>Qurz6y+pib&*Ycb3!tLV4=gW^kIYp^-s}i8_5KH(Zx01y>*z zC^c{#8L30MgrvlX$OKN85SOHjPv!i($OUx?*x=ED9OR=#ei1s2Xf-%_W?xsC&jY84 z`sWKAf-!P9JX7eYgc5ci2lOT^XqX)28@xCUJ6Dq^a@Wm!Yr-ooV8IOwT zLX-@okO)>9>I|$2u2J-szW@9l9;BEGnXWkHKjQq7p{Hu^g9*5zuOF z+fNSC20)rp8z6NwBr+l?g7UT0J4#V{qpw`SNg%oW4`6xV79hFmcOX?D0?A`x@jn6B&us$h{mYPvvO^R|D%J-y~oi zU>LAA&=XjT4h8nIkyb{*r>KsC<&1)x6cm9p=gA5N0BeA26|AFRX&`m9nk4lc?uc=sey%J zVVP?B@kqstrF|bQJH9D+1#llAg`x|P)`qR38zUzgoQ5?JNVQb}mIsFEqUqYm4NHiL zZi@)oG*X^0hTb$l`gKGY)prhvVd>LakCPiZ3IYF1+Ye4L{?7;djYzBpz2gKqVj2M{ zq({ae6!1w*fMf8PlrH=0B9Iy~Mu>4DjR7D}G$O?~v(n6=fN_G16K9MhV+0u^#TX&R zNH9i#aiWbAEQjRZ5oe4bDL^PvjInDBRbxmRL(3Q;Nzt*9NnsJu17^sc2#pwu)x~kf zOHVm?q&n&|MvQSPjZ|gOvI^x zZc!{1?lVVT_4&ZYNIw9iDTxy0&~yc-SaO^v=d)7q<6JrB^5>5}z8I2t6_;G$XHtyXivXdsc?8wBwdR+U35@zq%M@(uuL9FqAn%TAzYUtbp+{0m&>tGX@%TyD3BWZ z=exj|_#os)pknwGJ5N^1v1GjKTL_K;=+iP_Pz9rv3`UFrmplNZ3i<=dwef3Y9V4TG zai0{fOThsScW+MXoD^X@d=6R6dt%y&L66amGWmR`uMftg$i2d@u1}4t3aw2t2QDQSFb-6eo)E&n}Rl5 z<#9!A6}>Cg)CX*nTaCk{)8`yGRnsR&_Frcp4W~YkhI0@|k@k4I91u5w=HOv5a__Ey zQ+JLj={1m__=6pCy^EmJwweP(;OWzLA_MukHIV!_9sML;d$(M$nu1Zf#PFEV1TH8f zL>HTghMc6N@zUQHHJ=}stu%pbU!Gk zcLoxF52W((;srH;r2M*wO23P?;<~S z^rKSl8IYzBn`Z(}m)xpja{de;xqcL|Do_X1lhG3fRpbh!o;3zihirl5%1S`;&=GWm zMj9KG7>)?zGEU0r)~Dq1=0NJ`$Z*^@BRw1s>1cJ2M?TX3Ie*R~_@9ES_MggJMq-tr z;7Vo0c=9n!mypOwKhT$*kw@loR_>7TmLhg|m;>(9i|^+C)m>byL!uNUUZ{r#G~zt` zPaFP_^RjDPF31~yZ*Us9ac;YUlS@M`%CTTT1vHmuF3HEjX+Z2QX9e}lgHbAQ2H5#CDXMpvA zTY=SqGZp^kvOI-BN{5uEXY!K@+%aE9I(1Zk8U_VV{w;ZqZYu@XqM!xzlZA3c`+?*# zQs!7Ruk1J7l~Yr2E~7s36XGR~U9r@?fX53CKt>FbYT= zd5;RJ0n_ix72N};V7v;XDUhBNQH(s0%Po8$23XeA2O*jIYkYbcjsBMh|KpKdd^3>x zE8Q;xe|aodR|);5W2_SujGb1gOf)zK&m+TwOo-d z17z7goCrL7(M3aV2Aw*t!*h^CJi@4>@D*?6cD?|mN9q3W&FsKB~tY=~S=HCpj|AfaLMBKs_bA`zQ;K zfK>6S&+>>lrC`4=^8V%vr2M77$_;!2rv~0C7^(}!(?GgpkY6~iBXn|k+BfBx7#|-K z@1RRDo<;hc;krs0wtSa+UY=7)G3=yNNc)!q9v8fTP7TM#$3)RJY%E?+P{qR|bx9O$ z)z63{t!nCx53vL#Hs@hGq=7CsQAy((15VW(G?kmFqn6R=kbk}fG2UbThj$+O;+xn1 z)>{vB_k|BLf7O)Fep_1izE5J8%Q@a{BLif{)3Si@1YVGiNQ8~_1>Ky+0`?T zOJ7_+t!a~VU9juIz$Eh#6NTxfyHoEyOtsqaer}v)%TqBH)<>AtImau@8sC_ffKXvi23lSO317!9!pd_m! zo^`yun#Y_yZ}^y9`Iv3AN$#bjHbX}r*!b*Jn`_MmTqu>i#QTc3U6$+fHGXMz*K}>} zp6)(&O7ff7X@~qSKCL%0regmlvlejc>&!b;b#DK%36l~}I}crxAT}H{%-GRkJ#U|_ zcj;-{gi!@omY!*#6<0NB(P*FB$X)GvG&B30<d!ROaqZfh$T?PRwsr|y^B=FIH;)qUUBs^98(?B95C zX4y68&N-Vd4FCAaf5GH_e(zmMwVC@h=w7pD{r1%;)3Lot{hr4jHrVrO^y7}%n-6-V zt4y|@Yj{re`zm{0%xe<9)w5}e>DF^ChN!jCb=TGendvt4_E@p#sckmbZet%m=GB=7cRO*qaA z8j`pcVs@J*;yI0(@D(cMF4=emT!vsIE}eqFua3xDDnDW+E24 zXoO~J`S8Mt**=9n4$D`vs$ie@maIXrJSA%dtS*xE6jo=l_zq=6gX0+fvbkvEq~%|f6$1dn%ZUcS zALYb+z^ihijkA__FE0iFrj-{BfZXz8KHz(K(Z&U76~q9*f(oJmP@$rj@1hk#u(5ff z&+wVEMx6_$6|>s72;a$qURo@4(g?n|0?KnieqIdLQ7S{M5X@0#@Oo=p8d`zzVqsg2 zpets`$+JqLjaJLQtRx0#wL+81a=FrCb~_CpQd!K0xDKM+5T;N~=YM#ht&7kZmb27I z5W;3b6*0fPRwz(JGpVg?OF2tvDHiU7QDl@7?Q$K>sv5%twesOrMH_dma1vr)x!)K? zZJhFGyk=7AK?*Y^uL!HbXuKE`iodNU26WU4Ev@9{(D+$LGZ1PGV`-xiRw@i}3U&vq z1JcwqPIXIb8d4U9lmv@f6hu3Wf2*}<(^;!7g6J&Ty14L7Y(zt6Ex*r3%m>t~F4}l# zg=CznDc($|F5v(eIZGwlb=L3}HN<>m(bvFxBw2*Z@6`|kx@dVjThY)(D-5;exHd?W zCub!X4GYEJIGTZwH_&JFvRh4~;}^i9A|{ew&XO+YYOCQZ*AfFfwL-sI#r1U72$^8y z3e<{lxC15^&qi9c+E@cfgIiJD(oQsBeH8()zC7!Q`QBRLw>lixQ)-WVeZP(v;DgcE z6%Bykx?(;cTLR%$UAZq(Lwvn@qQO@y45=r#ZYBo7smtq$Hr=#B9>f;LW(BkQI7Lb< z2oc&2tf|x6yH+?2kw%1GBa3B&62Tz!1M@&y1!<~xgDGQ2NWBB2 zX5noFQG4XiyFhK}XB zzo}^Br&Yg#*iH=eaS<9h$Wx#aX$ty+DPBWR&jE8pQ4?&(8g(8q+5?4(m@^tA+<`@< z1Cuvi^6f^j;xfX6zZq(;)m&~1&H?KNMq4xn1U3_lhK|ceq@x)~YmiEkzs)go^62dR zHR@0>EX#NoVG%4BV=n%73o)R-mbYyw8v0{(wlqd0b#XTs#gY^@e7RO)K!BF_ZY3H5 zw8B_yk+Qe4duoKsU^FyDGQ!3houI*RB3kBqwH6HnwEUXZV*UW^nFs+Yhie+bJFblw zFi^{HY$F-~@7jp@1GU1awzzqcD?@1HfhnFD?x<=f8UnR^?{;E7U|BoSW{_6>7GdEd z+75D2r)l6D$;yS*LCnHTsZdz%S|R49I~Z;5*nTj;G%!!OqnLxLPVy|G+hD(d(RP9% zVGygF#DF2#uOU(pVrt?1%5aXnJP^ZtCuh+nNUL50v8!ks8N~s(GxdXb8rI&Q;6@OmP!!LbQB~_F_PYR>(vIQceURE@6dW)Ep-U z_0|X$m|KcD$;m=Dg`v0DNT+lV4WU}$5Jc)N3Ut&6AHkIP_SXpQ5kvAmLGzI6E*f;W zpg@%Cq?V1j zW5D1=bd1*CF(u7Z47%@ViVpXbM%@b*g_tzG!gw&+op~u@HWsJB>%t2#42Q0{Z8hrF zUhogqzz_El1ERFTaR>vYqU4l%*erU2$+MpbMiYRxF#)T<$hFc&%ir=A4bfVm0SuTMcoBH@D^?RxbU-kiunmz;VOh*kYDyhmtMvW z;gqGG3)WXG2zF5?APY{Xu-?NOBw2pFvCl}>YFIe22D=C^U{P1GuWL1eE4Cr>a%nNh zTceHzlWRK)3u_s*RrNEL90-dRl(b){mx47DZJk~C2Yp4uE3MF_pK-f~r-a#Hh%;K) zXyytSO^&5>Ev)7*#}AHW$ms<}iyqs3PffX0Fq%_S4v#U#UbxTbc}6gWh|n{*2&Vnz z8N-DT^XCahQ7(^V0vI(ZU+hkTA^IrFacfa2K%TCOsJ4t$ZNce2P=;TWoKS18F!P;ud^#!A*bY6qN?b zXATh!`>{+RVkbb8m`inqAnDc!VLbqrqm-HEQ3NBeV8+3&fl*-LD)>qxR0x)HRuJvF zY51~>_g>%fFFD`{Nhm2kfK#r3BzCgiCHNw>e0p4 zeOO^)L5izsI3BA?iCbU=Nmk_ujvFjyjdC@W4U{}zY_%B1ae-2{8O7G)Vyo+LjvFAQ zE-kjc!s;(+eIt$5#$wAXisSl8se@tpN!B)4y~*M$MT>^fTD5&Fd@E*+c2P&c!koj} z3d>WnJ{PC9k27kMVEIa^7h%aItmBQf1j3TbE{7%OD=OBU60lE5WhcQ>YJsKHl4vX& z1WWGK@?z~7EV+bJlCgvymbX;j30QKw7RknZfv}Xm!0Idp>RnaIV*Yq7-*kj%GeOIr z86gG$Y*R!7U~Y<-KS8Vhl)`cTV!;F#{+CoSAYIEZNfiz0S~Wis>srj3;G)*TiboDt z)kra5qE>xq6kc^x33b0;rCknI4lImnf{XAO7Ofy$%MgNXN6XhnY3mk-gS9~#q6%l$ z#b86kz=SGXvQXi7Wh=$2pK4*-WKUu5(2)(1!2Nyjp8S3RIy=a)C6}Cc9_BJf_ zk6^ftAns|QdZc05QpNoEG|@0sD;$8(N=|mys9%9$hefYF$8ub>RO|M!qG6hr|1eg} z2Xr1M+Dz99Q^v`i$JRDP!yg|f8m4P`lks9cz-_!}GefH$GoItRNoT>h2^5laK0FDl zH(9)Gx)?B1%loH`hM8JHOgC-<+1|M01EZxX9cK9^6UBg8T6OS53YfrIF6vFNd?o8k zvE@0*sLd_5ZWUWiCsS}x*<@H)#<2DkTcxHLweH2%Z^hP)VynSaW4`EOYdfqia$kzA zj?;|V|MunxtHpC#|9a$09F*W$D^FuM9nM@IDyupv@B zRGW>fJXt~nEHC81b*znszdl>cpRZNBXOM4jm{3oFg~bu%A{>DwhZZK4f1e@dXX265 z9MQ(0Rhx>mU*eLf9uLb)vQERo%>%S@b16y-GF{aE^P~>I+F5K_{U$9fXvxLaC0GNc z5-pg~nhpy~2l?*9!a|0noi80ZGF|vN^F>3JRyYB{Um8uJ&e1H>xK`-Az8s7kgBZsN zy)aWWEYb>f4R|mI5%bYSBMb#2FT)qOKUf0hD5V9h(+KtpI!#5*pCrVHhzg~yw?&N_pU6D4L5sic{5(t8=s5@&^m^r*AkQlvOhjYz2} zoghek1XT;QG*lbhRmqP$owGQuhg7Fso<^7gMl&uw@31%k){)YbkFBuk{PS!vKU*s}E|vE?9AAPp z>giy3;D@5uVA0^Q-QvNK_3y^hKoAZ&y};Td4Q|DWWVVtfZ8*Yluy#_DbnO2IMiVEU z8+gZMqRld`Fl-qbkwiK%?F3WyP@GX7gHds5!{uu)mmc*ETrRgRw=frs3g9Lw)X@y2 z0|=55@DOo@Tr& z9So;rxT41@dAK;!;ns5|SQoigv~dZH+K^X#rPanV!DK|VNJ|5woYJvLI1EP4kT)!|wZ=4@!Fb=bqG63z@Leb0F<^`l8npoo*Ks^I zxC_e@Md|iQBQ#wv7e##Fv0ChUF@LRA$br~V%7P0j|9-s~uujW6ZV(OYv_iy&Vz*-U zSA)?YaTkci_yCL+DqM~WajlKUG`d1YfZ=kA^W$n*wAsoNd37T_KoqKNGOqX_xEn-? z6wJgVFnOtFFVgV4Hi?Ffc*F&`5KK;@lW!Inc1_$=UfUwtY{4_ntzy6ytvYopjR;40^)6VBV%8!T zp$HcB6>A22TitD9z*eoG+a@~#i^5sM|GrJkhj<;L+y)(k!*itYXzQXr3rpJZ`D)w6 z{B2r(;C9g_M=N~XE(aGT3ah964!KI1jRsQ&jr;BOV9t`KU&Eq+lvan@X{V$+Ix0-QBaH^55P;usG2a8$T`q?w>unE6 zS2N7P3|KwIz>Y3L0W9(WR^%=X-!NA+?8Oqul~<5-+b?VeQ+C*O8udFcS9**i3_d8o zj>7E|w!$@Ff%TNqaQqc+f>A?A>!acAkBR|@wCa$faJqC2 zSq{s|Sdsb$SQ|0$h^yJL;yQL~go?+F+e$XrATa7IF56&hN;363Fg(#X>}qzx*d@v| z8LV@0k<(yun{@e5pOhyOlZA!f2~1fcV2i+LX<_QHw9cIrZH{S$W~Yirgq9zOG`@-!TQTNvB5tD z>j8#qA{^{@0r#mAqYLaBFp3 z);Sk-Hmn%3_=?xWfb+Q9xyEs0rKIn$CWwLOakRaTKe>^#`>=u~&9i{+tLV;dJFHl# z_69dZn~Petctg5NN1cyvh=z+=!S5#Spo+V=6zuN~-vgsiz~sZRb#KXzm0vsz1e0@; zn-_z*6xZ?)jCKLZ32KKzdRAU=+0_h&PjRN}V3Y~F3A}H2TW$(7ig_9g)>BHu#fjf| zTg<dHn0r^W$yj@bX}7B`2eh=#AuT2pUY#AFAou5)E7J$MY-i* zH0jdLC0qk@EG}31MG2RwhlAly9&oMO1-@3^ya7D(^;*uk{ zg7uZ!qX?b@)*TE!!&CBGVA6Ab!RnQ9i$@6X17C@TyINrrMA`W?pD&dxf^?_W=CwRT zX|C02V6M{N7W@H=9EkX&r~~_Zr8a*fN2~lyI0CGd)D?2l0x)+l$<_SzH=+SgOD*4) z5QQCJe}_=Tck)6gBLwO99?|^CYzC;D;STH;Q!F7KptiN}O z&tUQvOIL=@A5eo>aK_aPMsWv)TVUR35K-ff>(58|)F$l`>R2%O{pK22)GaxFnt$TB z-o=TY=pGyXs4(x9qZU>{)fl~>*_6h9o28Mqrxpi#^`#Y`b z*Wxas+(0neH}H%N>^Cr%;*OmMYXgR%9oGm}-{ey}-f3Wm=?q4}EAO>gV8LJ*HqyR> zQShQ1ZY(t4TlfyYy9ygwTZqi)0p%&K~qp`;13na?+7p-jmcnBotxST zf0&7fHN9O-#;MUKq?`p-fA6L)T}s9E5N+SPsQbdgb3a(W7h5ljEw|Ff)TyxW))@IN z!$PgFYML3XuwrXdvGuXo>L?iVO)Iu86z~it~{2jVZPc z6YT0$qf#wL5D}3ZV}8>ZZ0XM*|Q1>Y@&Z)lIT;iY>mpQR`7` zErjJG<$D0jOR`#2Fj}d_*1=-SyrMC+PqCF%Y~3%mnp@DosV~XJ*4|>vw30E^2bPDF zZ+5YDx!AI)j8qn=Qn{LvCE9*-RaIsICMsurDGq<+hOY^}0={Z|%i~L*{{&UYS3$}x zBM;j`;e=FoHGC__=e(3pOQ$P3&WQ>60TqbkUl>`>R61F3Z&8U zteaZZketFL;Y;--6Tyd&XQS1sYVIVb;!8LZU-}S|AATi*42rwS^Y4)I&BK?v!|2LQvRC?2kIr^76fYWwvtd1slXk43GXU+4@e(E zij0T&QaX;y62}Qt`Vdn33w$XZ=RfH~NW6&l2>RfFCw)p{Y4){_sv0X>S7oNBIrxGv zRrX!V`C}w+;(918W?UDf&%Z;e7n`>9AtV!Z(Fw`yr|=&kCHX7qgfuk+R2-{ZUuB`fo@b6;MVg~S(77SV zKn(-~sZ0p{C`fY4r;oRG{E zg_lH^Z>#slr|){u5IEUlhG0Qqc8yAHoy(ArGDgQa$HLRFKM@mpI$kL{&;U=3N0NDNn(CAmzJ8qJnfuxU2C0M$}6k zeV{b>P-);tNCSC_bh=!C?b6x6%Gpo(3U z41~np6i!HU-vvmLm!kg&sbU``{of(g?~8oMujjhMAfbm+kgyhbw4%o-`hS8HBMHb~ z4w$BtBc%GrDx8oO(L{-36O@hr45^|?N=5$((yE=Q#~B_kn?WU;~t z$+g)EC#3vKfi#kp3a$fExlKU&5S9V%R``!l+Jyf=2C8tcl97Y;ucZG7Y2=@gPRmgRLIdJ~ zR76dQKSbpsNEs-jnNm;yQU}bHfPSPSK0@k%y}}8J>zgP-Nu&-nRWdYF(g~?R3m~PpRCGe(t$=iHaR*Yv zU4WF|Q%Uy%(ua`pd&}v1&Q}q-DcD`X9zgmK(wzD!oRBK$t6)C`{gre=O7E|5LR;`; zAay83(Fy54sOP4`sECAxN`^%WE>Uo)g3AC4aqVSM-5EsxXj51!??)6;4RygMp-nDtf3| z*|$ib28SsLC6R7r;}xBdo+nRM^3McP!?P5e4WthtH7EjUU`){oNjE6^B8nU8(GnoJ zZZ(i9Tmz&}Nu*oR{m`kSM}SoBn39i>_z57DKdI=1blATQq>kJNRsnue()C7zkhJgk zp$eo{6r_Tt)T)A%u2wi9d8izaMph9>`Ku`DgcN$Ufpisb2&9hM%jtTf@efE1G)4xh zs2Pwr{o{#*Efj32q_n9B!k0dTRG_wkc0l?NQU!JKrSy6V)>p6rkQ#1^ zFMa+cO8);B8R(&KS9~dI|EFGHsbf(2&lgzqB8%p6fr39@V38w!zQFqV0t;~uw7g*9!^5+YzpD(a}zQDqUL>I^(y||*Sigda^`}qRvM=!3(L!{G1wB(B` z(uvb<|MLYFy||(eAw6vP`2vexX#ISFB?ZOL7g#@EVEuf7_45Umv>E)vi!3^p{Ct7+ z^99z=7g+!BzKUL8{Dbhn;dBQ5|IrJqZ#zOXs&OX1CU!PmsyhDee9YG_zu(DuJvFcW zI`&?X`k_OE<6|c`ZP9qOsgP^1x?1<+!}br~tecg7e@WQ9)o+7*DyQAisiL;&u)CebW5Z1>w~Z6B>J8nnSW&#lq! zrX9z>9AD{d47rN>2JD>AJbt+ODEJmfBDs# zbtmJundY2voiOH0wBytyvvmDm&mDKpjk{iN%@*;AIeQy7F1o2^?H^}8&&^r6chnS< zBPQ?G{85$uQw(FjjW6w}a?>jfE~lp3Bv{U$mbWeL+n_P_BO@-1?q4I*bmzu#E?SlM zphLDEH!I(M&ff|*oqW}Pa;M4<%{&im+SIzyhqCqk%lD$csy9|_e7h%AygA$H`KZ-L zyKlUntartY^^v5d3k7?fQhBw=DUDw=ca(Kn7sprl0p8BX0`tnOZ=Tt7RUSxKu zQ9-prn-;s~AGUZk==QYNvj*)tbY$Yo@WZES&3nDzm2Uw3cLK%^8(%+C<))gX>02Jp zd$PLs%?@h^UO7CgW~s_T_@PmsHtcI4ocgq4*Uf`fwq4m(`*^<)*K__Ot~`0VyI;S- z6AXfF+h0>3=Y3)=a)ME~eNCoq0YU8i3i+RJ`L znMc`fv&Zf_)ca1^Gj~U-H~s#b|C`7&Z{`iFIsMp-dSz`o#@7hjzh%V0{$?*v#*e!c z@#o0nzvmQB32*H|^Z zy_siW#O(?zclZq3_9nW-2Swm!P6{gfhsD;c z^Lbsaj89s7Z26Z;7S^6U)3b(@ZTMEk?@cN+inngv50OE=J$s1Ckefo zb$1A!eIShP4#AI|AmIZE4m}|BW2rsxv&s)b0SWz?eNPDfeId-~31I-cN`hHG2yVR~ z1hOf;AnYXJ83}`#(=QOh{UK!i0wIV!BEhOZgl@edgs{xs5RQ}Zl>{C0qBWNi0AYO} z2w_as7eb=}5OjSZM6k^yVFVjH0K%#v5blwX%32PD;2#8G z-arVWSRn~!!4NzGA&h1jfe?0*@RkHUa~}jDJOsk>K@i5WA`+}ZA@~i3FrH-(hH#t& z^$-Z@tk)0-DLM!_BurweAgr{>tUtjNwi&?kh9cQA7|GLEP%wnaVGxdxFoRiyKyVC) zkQ4%87Rx2!Aqn+EA!M+)PzduQAY3LvWOh0Tp2Hxd>mbZy=OL(mWA;M0kOZ?72p$O#_Ogrw2s=r5OTvEUo(Lg4 z6~gjF2)V3?1gnt{{E{FXV%bR$j+3BHhH!-SN`{a!3PKJE$CzpaghszY(2am_f^8-t zj|9sU2&Y(33WUj{Asiv$PiBz{!Ep?Pq*MrJSuP0=NvJ;(!g&@q62g2vgv%scWOkz< zc&0%}9|hquJ5Ry~659R>;V(A!R|u=dLbyl5Rn~Ge1pjdm=8cAMjTMq$HXee<7zhO{ zV+@3yB)lczCUe(A2%i99xgJ6xDgNSO#BhlB@A zH4Z|fNf317AUtB5NysC?ay*15ENDE0$&(=*A>kRbm;k|X3WTHy5MHoc5+0IJKOI65 zi%W+ve=3B_B)n#J6Crp`gOENE!drHpgb&kHqZEP&7!qb01VVr8_zS{N%~Z56{msn=I8{a5hHNz8QXy2~4@Z?WHgU~_Ggd*a2? z`yLmLv-LO?+HFhn7w6?ue{KEwWTRT)wja}6`jnqFyx?Su{avz8Sniu15;pu;`$rpI zl{i7wfo=Sl{&Bo+<)Vodi`0uOqGQ$s-0D9?UD3~Rw`c7c`s_a%{Mw}5=O&)InltZr z{I)rGQ;gZ(Y1??!0lzY@&7U_gz3f%q*Rn*#bxT$pY!h49zT@M{kJ8?!T|2ez;?^l6 zSEN>MQ#oVD%iYzhjPKg+=C<(p0j9q58>}2|bA9lKE2U3W&fXQj(&~`Y{gkdV=asxm zsaLY%kM_wuymOdFp$`?z+eGB)u-DitDypW;+{Y+N7;2Do9>);*Iy<5*0TnS+QM1 z^Qb$I=Dhyn&RVkrPrCa$KF_IKHED!b<_O9vHcuMRNSy+#k})G`%OoC_VKo>IP2ZHsguV}I=ORe{@BBjuc}x2 ztCB+#$KW=DLT^`aS1%H0?VjlBoTu;UZF%fO_R>GX=l9t$zvR?4Dp~K$;40R`4(O+u z??^JlExI0@(mZGJw}CgppVWzH;8d?&n`Y7NS6aDDObgZgl3C5{u0!|~r#163CYitZ zbH3r^(o$#dmgum3$%=2S8a;eTa#ZO$e6`P(O{RNTSLy#iNL#RX;m4wzO&@K3``6bq ziJdz(UY6`={^tVy!0(A`wmchj)#OOK@Xh0Q{333eTcYB|B`fZ|vG?_9eQUmYX;Xes zL$CLB*RKpHbe`Jl-U99G)?SaAR*7+|Hfq%G{)X}?``$#g&&;g8%X;z6wHviYvmeX99KEE2f zJU1dSV9n<;>3L7fl^T}5`}um8#?87v9kT4$>yi%#ni?yndbzR|F$?~(JnGoIFeYrv z)!hf4hxr^H)n(|b$zhjzJAWN>pkSx|gJJbDRc=PWlY@B+E1fint-&>D6>Iil)LGld z>uQwfF~x`RV>&q8@!prh!=rb+jGuX@>V=K_H>sPe_m^LKw928~uL|xD%YL-Q%dBUG z+#W++T?fQ(i_%yBuxCzbGxyI=tvByYT|xilyK<|o=bDwQxYBX)>4xelX9jD;){ome zKXmW#{>j%71~)(N{2Mm6Ubl0!N*(8O^wVG2o!9ROzxQ-%c7TIgD1G{1&6E{Q8?{+4 z{{s>7M%cV$y>-()YYgnOYsP>r77GIB7L^H)jmU1g<68UQ^!-PS^se`5M*nLYul8IS z_u65=^8t;juX(nq;>(I*w@tI=jA?vl$%JVu{_Zi?qGZM2zKjd7sC>aEVE@OtDK=9) zCcHGX>{0vo^FezLI+k@=b+=D5v(?u1%jds(pWmm<9EVGXTTOXc{kg^Ndc&rVGg(o8 z0R6ZB#-|P~OI9409@)Ls@}m9e8&a}nHaHY;g4>@{#_4^Fh`xa&@urU%KbZ3rTZEoXGsVjH~W` zWK`)z)Azk_y|U3muN%CrpY3MznU?+~DsEG<;-ybAN6ac%5W4AZ#F|qpjvl|*ZB)Se zw>CqMzMtm#{A9d&<*e@K13$O;{O);1(5lNTZk4LbP5IOL)sFf@5AC#zC>d;A+miLB zEx&V@XB{2*E_^!`o9cjzp{8Vdkos#<01bJfCzot;0;b%&!kR5STxl^H`z4G``; z8{MEFVA*=wosCoLq+*TVs`~QIDmISLMaA+7T~(~rJD?i|24p*z;n?!*oywD=G8q->6rkXzQ*8 zwzK|n`Yn1;pKfDp{M`09hqA!scc)5mt4}mK*x2xU zT)2vx^n~xCy%k!yUdFkWci;E>T>EU)WnT}!BA4z4pA)==AN{)<#__nb^EYtHvRZ+s zO|NF{8eRER+nv`ELwn7i|9(<-;U!Z+9~OFJS^Ik#13tXH+c|H}lH7?sS4XDpcye^a zf)_(K_gOeO*m3u*Zu=Jgm3OK{wVg^1`lRQsLGc^(`x04JGixW^j@4yqZ+FZpH>k}7 zo9*d`OE*YO3UAnW#RKa#p-)V^yWhHTZ{0!fPLb>VkBctD+U(D6@ty5mfy3N95r;qz zR*1tKOIZo`dd!8;m1WF@&}bEewNM*SMBboIIz$g|+@GCn(Fq+w|1dL&+1bTL!AdT6t0*qy23C6Lj1mjuD z)qn|X3PCz61hAC7G>dC6X_HvS8k)s@G>armVeV@oFoR{U zgWz}of_goKS*+K32oFifAt8gQHb9u43qiL5g2*&Y#5-evUY3ix9F7Ksd%8kIS;kqu1naQ_{oN&U`pRvy^}Iy)9{fZ1j&*2WreB8BuA!f= ziMv}kb>7(brFVIL)`Rz(M#r^Y6sjI`#=6VUmDWzT$98YyH?zx*4guvJ#gBOtc*%R^ zvDNM7K47*t(Y=#f&V8@n$1=6`_{}CA58Y1b@$u#}i+Rh%txr`O-tKsGdgioV{Z3v+^BPj?iv5pBs!j(&Ts1eTketIS{*+mMNICp zaAb_SqV?%PYPa#tTF*bX&nwotO|7?6^6ag?u3cVbkvgr>9;+hDs4pwW_ei!``c52n zpPjy`vexg9FtK<(YTkp&yZer>bEkKEhx4ZiQXCittP2=W0K7OcpSH|nZ9qiKg-l#G}py^ zN>{h28l3j*H-|T;+Pz<2rAbHMn_H|qxuu%Fe|oEIwac}0}`1r*=CM%$cjR_ll z3(n*29KYV)P;~jhjEzeV9%*cC_2g33t9H*nUpZ1ROk6)?=7=k%YgRN*-kY+~{^a&y zr}mc(2xR?gR^c;G)Hs>8 zGkY?xzd87=;=(Tb{uo(n&mikvPagMOvcc`p$?sopl;3@)W3}wSfurM7OO0R-h3H;ZfHRqmt?woV=f4x1aME72l?4El37&PC@uU!dnvFGxyUFR^5fL{4|7*tcV2vdl3Bogz%YV{|UkDJ_Pj{ z2wz#RGZ1!?kVC?EraB8D`~d{rSv&&7zmT`(EY4U~5209|gJQz7;B!!plX8RH&BwULum;AkkaTal==lw?BSmRD0!q@ zCZ!4da|6occTm!AKylz%!3GWUTU;S<76Jljp!86J6n*Lxl; z;sGx0U05zbS7!ZC)o)%S95d=6kMN$BHbuf>|X9M@SgWEWTns$FK+jJz05++xJ;K4(f!7_LVj#d!fk}!+8n?QI-!g3P`8LWte`PLBpOd*IY+Z2MQ4Ft6s z!aUYX4dDX`IV3QqDg|LxbqKmr5Hi_j68vjGuq+K>0ShV(!ORxI5fZYPg&BmMBqW(Z zSj=)s2(JmDz5pSc#R(9sYC*V6!tczk420t(q?duPoSi2jrS|_~@6E$>F8}}UYp&}e zyCKV9$i5phV~l;@vM<>QV+@8d!_3%ahKN$KpINd+30W$MWJ{$;WJxJYsH{;G62Hgu zy)@GJe7@iN_}zcp$8pc$;XL23^L6g~`CcvrRkI>^!%WDEplmS&=OuX4gk?i;T!OjT z5Ueq0B$!$pL6htV-ZC?@BdA^i!A%L)n+7=${4BxB90)d=>k_zP!Q{LMJ~oFW7*zp5)qDs( zH52k7C|eQ1c?muU z5#%k5;Dm`Pj384L1RqQAy~$Ms!TS;nFM{Bd*(Jfi2n3~`KybzkdjdgFRRl*RIBSX( zMX*NdO3<|qg1luB{Ar@fBFI!1!N(H(Wpb56@P1uy4l|;hw*~8v zT@nneheoOLUe+N#lUUx%JX;^hQAvD0Q@jF_y^>6>fFy&@d?m@K21u$_MDm!=Ost5c zY(pgHCCThFKXArrj!V)c1W8t(nH9pVz-&HqQD%0ZX;>LEhtDjKnbT)}l^N(WEkZGK z`OI>exqar2%*TDELl|ZrpII$4ug`eHG4uIMcbWNp=53ilK9i*iW&xk+E0b0HR+)u- zCU*p8u+I#VS=eWG%PitE1*>8{LC$3s^_kCQ79-!0-nKGK1 z3o@FS22C)YH1lONH`isfFwL7{v^2|Pv@*A4v^MRVVYD%C%4ll>p2TQpy2)s7Hp=K= zGB?L~%0$WNXtv1cWOB8@=xkzTbTPYRbTtK9VstaZWOO&5$>?EoNwJ z=It>Cn`JWM%xxJ%O#2QP@#ak#2`1nvjG?BRjA3S@jKs~EJ9_g6%cXsvyxvftRrk21A+dp1DIOReA&W0Df$M#OkB2Ah-J zyoQ`HoV@@~!0q&iV-9+tpk1KDGe;$q{I`H(ihzmnaKMMV`Cp#Sq&1!m#~cr$rV zwcdOn$s2D;vM0q184;7n*PlA#%SvqaAxR^x9rwIWMEqbt^SXPhTcx-t)|;t7k$&Wc zbn1cg#DuuG;Y0Z>7qXo4l48eKe=x=U7=__v+bSe;aERBR;mT;f{cI-0dySRrv~-y9 z@!qT6eOWihCwQ~i!OR#+Fs&w&9F@9$(baGMYtU%+u!2rZ>N`9(uAk??4CVXRfD8v_ zxb-LZ?8Ar0l59Tg$j52^Rd9Ck#iH2JuT(mDB3UjqX7S#opES{4&MA<=dIT^s*5BT26X{(m=o`c8C~p|SQG$z4a9!&Q6@ zc&6jtefv)xp?K#wv@Y5ZR zOkYHakW9~%Tyuo_VqaAW`MZvcf7aLjYFHA!l<&y&;kKrZ?4~2rCv)}ja{c{*sk1(P zv$>jL;_sFt(~B+*l}i3@Bh#mxtuGHZa)fsg^3VF(n?8%GKljVidN-u1BhyExrKk5g z-0!{y5SSv?D;e3)lilmcm{UEyoYL|+GG2{H@pR-ua=#;F(DcMez+VPOrjIdpaAX-V zWz~n;8alGfn2JoFo-5(VvN$q*hEAoabgNpWr#Bi#D)lP=YzS3K1!0sWwCh!h`qI)k zNA@^+vg(_^&p5KYj!fTbwO;DR%Q-GWTh`V#pM9X-IJ&OjEHboHC%t zeAF0AImdHp8k%^{aCrW zbw7PdS%3OsIsf#Fbe;i@tiB_wg=~-`YvjmkBOC0ie( zne*4y(QATTA7j;DJ6o1wjbzUENj>cyMSX}dFNzv))bADXlc3k-HQ+qu$eLrHiCu$> z#u4eYfGOBDj&yQlEwPWpu5qNZBU7Vn?tV(Yiz8$)=4nc~X@KbJ$l73Usj}p+8!}nj zLJLRM!_jMptdS$@>B!n6Yl2MSJ?+RkVAl_3NY+c^uTuRK>~=i&c4Qrq=~omq;Pi21 zov`cEJ^G7sWSz0=S5%5)_C=;qunXv}ZAr}0j$T*namY%^&5@;a<6@{%z~5L$*d6;0 zM>Y-_|MWY;o?Xb)@0Dn!p(jjo^z?xbS@rEqJw2(viH=?`>@OhG_%O-Q>x~`gfBa2$ zgnh81v}g#BAb_PQe*jI5~R z_YGu|aWMmT@+1rk6II_{mG+4dk$VOnFOe7kt-bJR| zjfAmI#P2zJqmWH?WSf1|zakzD@dT(5?0rXZ40e5Yy&>inM>ZDwT=W`a>N_g(I}TeukjZ%xeB#JXJF>~ho`W8$|1*wIf+Z-bTm9h3reN2P zTd7-}b!1bq&vj(y9NF{8)WCYfc}MmF_BhbrkB)2__D)?v(S)j_eKW%AI6c9N8-D>Uv6i zR!8ohzHP}-DzbT%aj&Lo43823~$Maj*Rd-7~Tl79{aPPF!DIE4cI3+vb>ILBiAYgg{xn=2+$g6>PWi?&P_3k8~93_zzCn;>}lW& zOra(5YdidhLI8Nd2Y$!^8R0R=1eqZVXp5m8MRv#mIYB$eT#y^Ii^v1oIaDO*N)Q5- zArv&*ghLe-UxeA!#FxLS#?;zS2kJsSs1FUGAvA)<&;*)787OZKb?`l&k`t?%!ecO; ztZ0u|9YGC<1nm#CA8Z7Tp#g*=(+A(fu~&fz(6%oUszG(A0X5+r!c~o^22|oIVO6=R zm?n~96}77BZBRQ3!ISojC7>jf0yTkLp!T63BT@DLLiuRy(6FF!MdOXejmx0%=n818 z(jb;s_}41s={-Unl82U?Q#hV~ExS{wF(IM7m0tEuh~3r~XI!m|$IzP~P zfsO}$1#SOtfVTX!Q{j0?1pR25_Mdv(?iW*xmYJgWrL=?8b4Ge&NH6#3JsiC=qqk7> zVu#+r(CZRL=SJgpxT84S_T4f;VZh=X{Dfg#WzxmPa1MQDv zpeN{OX!=4DM1!`v+S(2UZD&)G`L7>n%c?D^wxrsEYRlOJw4>|@PeFUo&aoM2x2WCW zBG8Ud8^Fgg-zV3hxR-@8pn+QhdN34%qEHM9LlHma{{)tTPyn=A(CR>|0nPrJ?KLNA z4%D2dX-YqHspo6;6ze(Ii_T{-17^cKm7+ay zvkF8&K_~=a5CWCqFY5D8&@aI0ob3pF1?NFOPuCopfp(kPU1~S^B-96O4)s%c`gy(0 zpiSWS#P2x{$KV8f0~>L;NzU|*6m9H=K@tptScrjW=mSxZh@T`F20gic8oI$#&<5JV zUffz@HiBl*5Sl8Lj5Dc z*Vq!YQ)~*_9cnYEjh;4k+Q=2fUJOox_G<^=n10Yko3d}v1Kh{M0nnyv71wXT zT6hn(f%aJ1Q%!`CFa^D7FdDR9(*CGF@)*$XD|Ldd@FWx`1KQebAv0T5|K$*t2Q6c@ zY}GRL4ruYF#oCV)>re0tx(kpmhu2^Qtc2I06|{z`5D7Z))$c2Q4c~!&Q}HzX0N=w& zI14Ff`0o_xw-DPxJJ5b=7Lk1nCzU|1bHZbg5&Vz|GQi`I2eLsROd$Gw zpbKylF2NNDK<aya$^>d+a~KgI*iV>hLYa-5x$c`~v8gtPf)zg|Uz_hW`e@pLqHaDv?TU zJ@f-c`eCbrFdeoMSWWm8-6Nn~f*S7xxQIXfq|;37mlQc@_mGY2&iLsF*Ga?_xK08p zLI`Zq{Qo|dCsFJUPsjriN8!0U^oDlO7V;w3iDE9Oz;#h{XAEQq@@52@dg~|{CHK00FgKvrKHv$_6V;~OVffw$qNG@XP zY)xlm`_a`du_)*$Oo`C(S2(l<9edS;8c;_&l<&#SK_^G2P~L}3KSZTdpihu5#neg9 zYWNUzaFYayFd6q$7zO>fj)y_8gX_s8KtEHbgfz!2Mq+gKqO+C?*d^~)2?rIS%V2Wb zA6i2z=m2rh4V05Pc(P2c^Ft66fPzp6f}t=JF|mz(B~r>@EeqwL0#t-b5CRHDjDSz! z6ZjaW5XEz_1p7>w3(^~m>`Bty9-2ctXbc^o1=NR@&;*`>P8n%lt*~^2*3bZ&LK|oZ zZJ`-7f}#X2bhM(AlQ{0Vfpkrfoim*~ixrs-wA zBkV_xRj|8o-0CQ_a7-!dG@}|7(FL8(@HF&*?$8xf0k6O^SPjeJO?Va7z-urcR={Fd z3+uHpSP5^z>#zWpz&dyX7Q!l63X4Fe9Xj1m6-;(;t7#P1LT7?C(UrU?cIllXBj@2K zxC)oyI;2S9j#C1%i&i*PCbzZ8t(!|Xhbkcj>?S}56se$+x&Vq;znRt=doBFC`ZLgR z+l>-54W6Z@Jd>~}LJ8eK(qYwgap0SJWrpbdaF0@8Ii0?ys(`;xg7!(J3L;uQg{Zi3-HoFyr&a#O5d z-dM!ND=-h#5wv*w45g-+TEJ=jrtOguszu&MunXRUO|TYTgDB9bvljd5|o89P#P*gMJNa5 zo$H#Ib)gnihft`jNW&ovBB3f&fe5Gu@>BzALmj9O^`JF0hL+F}T0jG629itG$T6E@ zHUUjQ&7mF0zx<}OaxB^mwt>#j5uSohj=ei(H|PpIpeOW(UZ4_DN%aGjT3;9eDpAS% zLoy^mA`F9}kO1*2^dVT{U@*ALngQ6|29*_iUKj{&f)t2)sUjE$V?foSYfbnQAQdLU zvoHzNFP?)L@FGlu7gXu00y)ftmp}^B;boWw3QX<`U_QvBf$=a8UV&5x$ri&BSPIJ> zyDMA6^-5R)t6`NQe;wX{H(>*;hjs84s7RGV5~+QFiRw9%X^d;DP%X z_ux;s1GnK8`~knhFDm?SELY$%T!f$DCpZu1;4GYk<1iJzhGU>AISNYXS8xbE2eq36 zmRTnCYuR17 z{7FxdKNz0e)jpJn2jva*$X|Vg)gi*{;LI;tzlWeHH@E{<6YbV8=8EsFUByp3C9 zD1yB(=oGC1*9{$c1+FVWc_;-6Q(?LGQ0zMP)6gPx#K(1tb>3*45)i!2fKBqFCiN#f# zf&E2z9;ShI7cawfm;w_3Pu97NM!ILQPlU-JKT|;wD-wk%nIe11xmIEoz8m&Z^?9`{ zdD8IcCZq%QryjUgLLQVU5H~mTu4?W@?_ zBK#QpPS^o+31~a!Hdq3S;Z;!6cmI}hT^v2RyKb*xUj}L|%P~KIt*`~&ht2RFY=V?` z`R^Tg8#clQSP$#qEm#X{U^To6tKbcI9ah2$(CGIergE#Cy18@Pfm;R2vD{SxDiumK z#VU5!!7XB4yT$98XOMjlpTT$V1?+>*VLxa_{K~mLfT82N8tz@hSP8o6v!#eha>>B(6SK$&Umt`<7!jDiA&O>oH2L<5*{0u+AWw?^U ze~CW3;po=PJg>dXpNrX_05zn}!9EkFzzEPyu|CindV#hS{PXnWzb8N^Pk$2bZ*T{0 z!VS0$x8M)Z1ob;+iX1ZHL5@1!(ovT@crpLNlu`h8>D>jnNv>n zaCgtGF}VnCjX<01s!#^BjSPn{=nmR!X`9&qB4rYePB}X2IHWcf?asAZ*TJ81QVUZ% zuj-)Pd}$~JC7}cqhhk6^q_4mfh5}atRJ?A2bhP3oM#n9RTt_ZeoZ7D{QuoL}o>Z)g zM2b2-r~(S020WO01t7m}?(1WB^{j}vt_O9%&73k-7o@B3Qe4N^iMJtw#?S_ufsRF+ zLKEl!l0OM@ll);>J7n!adaa=+VYb3-2`!)p*Ud58g51#yx6WVXK}s^^NdYM$T`RKA z&pC&>~a!ArZ1w=LE!#-Z4gL5qiHFvq|+7z-nvYb9_rjDq~=X_}DUb1)eu!Lu+C zCO|5vb&QAU@I3hOGZjXoZKlXot!6d zofmr%&{+52-#qj*{Dh*n3j0zpAe{wD*()#~7Q!OXqWM+K#jpfe(px3+I`$Q?9G1ar zj=2)^4e%3zbU(v>9+W`x=V=IvRHeEXvnYB6pel(?$%{n~FJyv0a9jf!uz!eo3U0yY zuo`Z{9=HN;f^r~zxhdkym@0)`n2KEUm?BoBqp|PARJaOH3u=}RDOP|VA=n1))V>z` z2e1{k!27Tn-h)l>F1!P8!$#Nu>tP+d1&Y|6@BXt%`TS>v|Gm3PLM7nlb_e$DpsJIb zE8i_0)xTS3sw=m+K0&UcVrut%jQQyUdS%da>-NDSR&}W=z6Y)L_F-!ArN(*;`%yRy z%J6>7uOLN@?ht$dYJ^|H0XPU+A}WIMAkRnOYxs%)b^RTD3m!-I4d!u>{Di|XoKIrE z01L3Y30FpGkSU%Yv8aKZ0j2&lXvL-0DrK2xF@J#T*i~9;-Km&PAeR51ko^o7;S$L2 zPS^u(`?`uu?QzfvU)FU@9W@j^9CD&#f{AVER`7iiP0 zYY%ZKEcsXB)i!*{8giYYls?8qMo>;vWNLVlXGZp54mDsX)k@@pxzvMB(hG9JP?f1l zHMTqs8Y9#=l~Gl3Ze+P2P!ECXsi_BZ?FRN>u|D`z)q@F=uB)eT9wUK@SUGjw^u!er zS%Z@j;#N%;bk3>%4<8V``h^4GYIP@ro?x|!zriAH^s7AdSn6)uAr0On&TK7D3 zG`}n0?p$|+uAryGmXV-(m<^ylxYgVRyKZoHQvK^L=Tp!D+Cw{N3%V7nTl2a_-x73l zvN=2n&7dhXfyU4X8iK}#r-^hJ_Mwmf@t|ko;$Scgf>;;`4F~XFe~5v85Dk4H3i?2A z&_i{4F0Lo^fL!F*dYAyCPjhoD)>pnXeLnSK!(|(HH<|1!nz3(G^x&BIe%9cfPjz;y z=DE&mt28s^2l?t2s(?R9TQt5>Siu<1!{v1xUUJv(1$i{G%28r2B(6I}S0`gz83AGQpgaKm!J zbEM~B7V{#0gTKRBxtjXH>t6+pJXBE{)yZ*~=W-UaGtL(ttYj5HqjU1r(cSLn{Rj=G zmlViqat!f>N4nnx2ng@>&DY-^JCBCiD*Z?kjN*5DSlNK_RcWd;?})6X=Mds=Lg-5S z%@dx1S8{y+OmXGADrMs7nbpidBXSrT>Ty3c-e0I{o`sdsaB?@rN!cI225lRhFS&l2 z%hIgoy5bq23hGa9TGibEcvYF5X1_Wi~sHmxkLqHt z)fvZeQR5w#@#%$iUR)WEi_^2Bvzh()^~Ps2KPFIuqa4l9gfHjM%>PVPG-(xK6ybAD z=!M=)IkoPmO&%w7y2gxbrp-`t@S5Yd+nN#=vrite4Gl_IrN1$onK+b`mdPMT&nMTQ~olHJ4&LtZ3@xhk_<4zpBdWDc`+n6G@66cSJ1skVD~@P0amN^@ty zQW-z!-RHEGA8W8Wn$y%yBrPY=P-#vM>=_p~DPWQEq46xtb0Mc0ibmvZG*p|bvmLv4 zztPbMOT!v-*U${*x83--ZQpHtXDu$YA?4$}K(jrO^w!B`vL%teS-DKs#889Q)y*EuV_0VR-F{VuQBFr3h1 zxmJDm>-N#^#W|N|@CI~5!oSp!c}=fmLZ8Eh;t$Jm{Zjd+dHmjhR3w@;PGBncZ7T0x zUf}bO@({0D5xLKw*Q~{_w?$r4|7XnN`ON9G>%33U6jo89@_e~p_fFr}-r3m67M|b9 zXF^C?BBK%w;sHb0M`Gb|`nTV0Tg5G*X zOuLad(sKrl^tB3(cY0sa^H1J;cV)?ORs?F!o)%A-uSVjXQ6S?e-@D#c#mu%*6jtYA zc2UH(9GtiEtmjkHeDo=1uAvbbkA~V&lOO7Qylv6ht(Hb;NHuoUj?4I$-`W2}j)KM0 zT&5Q@bw}fOv7_-=o$RMxSW!FF(V%s%!$s}rlWf18y;Eq%Gq`B1A*H*EnHTXJdy_L~vkIq{B`A+IdN)huG720zQWtXY6nSmL+ zsI;B-mCr9NQNGRcx@bCWe?w_=a*Qw48&THu8B6}xl(kE@&Tl(6ejZqrg%o3iRUg-J zQN!|g4e$6x#?>F=qFIXkH!5e=;n#bsoY^~;Sl=se?s6Uc30Y7*?5Mc#+ghE1wC+)H zkUov@Ak?xy*DQB(Jv^w<=;QnzcU^FyzOnI!_ z;?}Rd{e~4FQx)gK73V$UTcDX^(K9H1Udc{t{;qBay2YJ69 zmTDS=S8$R;7(GCKe_r)M&SS?i(p_632}JUZ^m>x1X?UEdi$bk8abfA=e!BZM0H z=uq2I)(WFgao>{mOCuOaH1;#ydwPeN+9}k)z%bJ}h1}ghQ){fYBg-$TdT9MUOVgS$ zec@&W8iPKiMXNURj2d64{lg;hKm$h(Ta%B<)y zyIx&N54c5LYS7Dti&}59oxRN1ycH5HKULJQgCk7+R7x}5er~m%MWs!0D*66*=+&ru zh29ubJ?~PjsHo4v+h~8G1#q=0o~~8RcZBZcgG|2@Mo7A`SoQvJt^xzTtZF(>@OAXA z&1%+8AcYUFg4nA#tDyST%;^b~YA5oeGW$Fr*SBrg{CvkshqZK#u4Y0el8%IG=E;e) zYd6fjtWnfM-)UT=%&MJlyHlm=v`=d@LtCs<$F!S7N}om}7h(N-9E`_BWxKtO zSxi`w-#KBOnR+65W1G)2St-@_({mFS<>6$p0=cu)$>GI?c2t8x&0N=9Ryy+5wfjlT z_zNQwyPeT6K?hT*kIG_-PA2qHXyhi0q*jAF^=UM5vK59sGU1{&*L_N{F*P>*u2GKA zBPpWBbxpkd_C-VS-}tsf-eqr|t!HU4C317q@msf-ciWL)GM&OjeTve3wyxPWnf2A0 zK$GP;+Q;0wrtWj3^i6!I#um;mxxMx5H*Vmgx}DNZbI?@(e?B7%sO4$?m@vR_3ZXmeO{$G#XidYnYGikTD7b3 zD&x4upKd#@W>|FcN?EHU!ZaQ@>%_>N4eT&Jdud#b{NAIZG^<&|fv0FAyS;U8zpqVH z;JcsVS51-HtI)`df1Xk-+Su%8t?Av^*!;+K@Y79fA4AF&>AQMP-?rWWXJYnrZED)Q zK+iqg)E5oT2;QN}>KoXcnF$1^cZw5i*hnS+#ja94b2!8N$foT{o6O;~@2O~C|C_m%fnXm4syr?X#d zZ+cCqV18?Fhj<|GlMPnf81%Chmz7GlIl03@`dwHxdB43mO<3OG4ras4n6)~X_*oQK z#g~1#Yy9^y;^A>7FrY^VPQgv38NR~a4?36w+C)ASw)b!cb9tt(eDHq?-jtuggy3#N z-7VLnbNoNq-UHh2wp@EKgN*?$H7lMO3_b^U4Wd9$J7)xZOSbJHd_5`c1FlK_5S9xmewDBrY1- zFV)?-q|Ep&_RdHf7SAMHG+Z5++~{&j;O0+!mM6L=_s|ogh7KGS=PA@U@lJH{omP$_ zLv*q@8&83Fsyi|E%A%->nupZ27*k$zf-C*zcXv8JK8)2TrC`lJZ{wm-<-KzL9>u?% zcFA&y(295)E}H+c_8KuD<>;waXePj~O5vGZ{=gVKD8p(XPv1+$cG=i*+n%mCb#+yIy+@=1Y$bwz$ z?p|lW!G7bW4ARgNuH9>xr!+1qwDqxTj_!Yadp^04f7WVMyUe}n4+X>+q$$b!Ef=c?{e+8_f)!p~=y|L7+>dZkl~Z}|(G%pdy+iCr-g}2}nP=g82R~T_)=rlVxdWsH4?BM?ESxu{S8ajPYHz_jrwg->xb;fLoY#U;) zAg%TpjEnL7w2f@R&SGIFT;7pKBnwmUwA}Pa#Zs0q?n{Lg?8VVG&n$`tusPKCnOAxjp1Q{ z!z<2(J#{666^6Q_#~hjEt60|)Z9STs`XZ(Z^xfM$ZID#?-%T4Q2h0Xp=abN!{PiES~L)&+~9= z%M;M8pD8-WSJOMPpXoV=<-oK3%#1ngtVh%}73Xq&q@U?CmsRkUe)b$NvE`D%m)^|M z${)Zc(YoRC7`fwMBK3nj`Eoz~^8ATuF2!TaI{B@OMlc%3!aunG(yv_t(loj|E;)ZV z6jt%>zPo8IBVx>5{07fPqcDC8k7*lle%#U2G>r{0CUhR5A3#GhUB{Cun}ZYFU@5@ zf3qLI!BfyEPQ24z{%g_Z61gs>X}sBgo+0r4D3nCu>Cc8WY24zOuhJB*IWDs%1a})h z;%3=2m*(-Njr{hAw@3QDkF_ℜ%^i&vH53n!Xco$w#Oi>K?2RTsRlEL|m82@n#Wz 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
    @vKE|L2@L# z6;V;Vhl%iMy!LqhvQ1eW3sXPNUF;S-7YVz@e{`aGQxn36@OqJev8Gp>Er~5IJzM7B zDQm)*j+>Xb+n$eve@E=F|EAF}*nuZhTMdRHm3i@s!T1bZ9YNlJ`;F+gNJ7SK)B! zd;Jk-zCF(7ey^MKQY1VPvAI_rG72rmYwaB%UY6x0)qUX?JUewbU$$Hxn~o|x9nY^O zx6$xgyi5Ibgne`-jEBZL_rcA3DpXuF&r{wewVQ=DTFqqn8?L1V{D8ky}b@d9OSSR-(xA@J7b9Akn z^j5^Fx6Unq`mJ-fy%h{MjoZ^rbv~kn1#B?-tw?317dyWK~kHXYnrg+Ny=?Jnkml%-QO3x=Ls4@G`94 zUS4d(p~Id&^W%>3PxxC8#WU#_)-1bkD&)ZKT`Z=zu$bX51wD9?p|~8v|t!(mdJ~E{^Zw!skg;nxGW|X?v-2I;)9X! zdPH>~jb`DV+~O8|5DB;6>c?cdbL>vv>K1^h~Ohw(Js+3cBcU*c)MV2k`CF@Ae&_Nhh#@e&BC>&}{#5ITX^#_jZV$M|RbYI76( zC{d2G_CDebRotN|rO$A##L^u1J{At$Bv0SYgr32QC`PClE7AMLrtPzST&hr<7?0!E zM^#vcr|zQ))V2O|JelM9A9v;B_4kTa*B-(f?0LEewSV59I$k;_=XtmI(}?rb^KR0i zNZ8rw4_7tst0fDgBShBCqL|2ymZ2tti2M0FT2H` zMMC>uMsQDmmh23E#Vz(ep$}i-EPtskx=zLfxs*;$Y9NJ zJAIKHs{JZoVSCyQSYtiyQ>-c8>8jsr{EKxj_h(;oiw{Ra&0g1$S$W?0x?AvNBzz2^ zuUB8yKIx6K>pK%qW5j^drQ7k;rT&M)KbOQ*2PeGgFGIhbMR?jgyiN@5!s~8!#05K+ zdP^Uz&OFjF9zzkBi1hhZJQd@Q&aZe{p#IZT_qW|`NBQjkZGUjR*%ID@m*VBGI@jOp z@0fh!#fsi(J^$9yd|%1q<3^S!oT_OR3PL>PX@pWLLQO5O1Mm8sj+0=y(* zasZ)(SMaX(JUwBweb+CYn$bsd@U${ncBFgBdfsDJxYU0Cj&P3mg+PBimEhm944%5c ze_Gs+XCDAk5}b1Hxk()&;gt7EZYVs@O~cdr_P$OEZ^u(|o|3tbox=0GU)?kQecxk2 zJ)97F1h2C@^F+sZjLS*lKSU-S@ayeA>J{K=Q}su46JC2S^PNv8gulV_2P=j4HaX~5 ziWNxDjKlNqhkE2(fp;nCXcKp&kMNWy{rf>exW)&u#-uQNy5qGgDero`z9rrsJS}UU z=_#SjhyEt(*Rv3>D`89{dV43Hf0NSa20!xa;(rg?2~TbCx2_z##FBK6;FWY&DDGoQ zaA&^QF&?86k^D}$9q-JPYyS`b80CKhwgj&uG1<5W^Sps~HJ>e zWu$$14&G&We!XA8OTuFcW+%pf7F$L1SLhPF6mKH@|jzFDiUt< z`PuD$2d|{7!p*<%r?B@ycW4e?XMJcMdJ~JSdPs8k+{0&2r0#ensYAEn_0U-{{4SPX zV{IihzC63%$KYw&_^r7IFQvr$9Zze3CB!#uSA69^Df)ZLZFp+8*VS<^e-$vl#RVg! zj_7ll9n5|V%`y6()&2{f=CpSQ4PScn?D;nf?@V{@!@HcA3@1HU{cAN@cQyhqpWyZODyEK3`HovbiMIsL?=a=L53hq4rY)rL_h&UI zZo>BglUyp;Df|$!D#h)L>^{fSLZxf?{J7c=Zov(aaQYAa5c&JVlXzDU#eZTwiKoG= z=B?}w$Nl8&N37mSc$(6Dqr%>C51x9R<<>9JIZpARV5d;cAHC;_0m-4BSc%^EsI#!t z3U-UR+_9|3)7{pDxD!7H%yp%y_p?9wLs~{348#S0W^N$IU5Al;4jF&=vp%EYSvBsW z69KawDQ+6ZZwgy?f@cSF`kY{L=p<%e-|Y5FiFwx<^OG}X^Iz$CKkl`dJ$>`}GiLZ? zi8}C%dCwX1i!)}6QzdbypD~}q?C$4Vx>)ASa@=t}%d76)XUq>VyZKSi|ErhueJT%U+s)<*Mzv)ztdUe>5%Qn{Y|IxbV!-g>IKgn;+dzbP=hn#@iS)R z5A}x^bv|ZK-`sb`to>(+ns&x~?2P%#8MAXB7|UVa8FMdYcR$~nL76*L8_)8}eejHV z46~abwXIWPUVFxT=8PGPi$!p*h%-r5gGq&e&%%p|k+i0ee^A9Kc5DU1zf@+!v?<{Vex~b^R5qVuQUOa{8d(zE1n zoz03j6$H(KHo=R7apsLS!5ZeHHoO|M2`In$fq=QEda#n|*ftnnSPEqUs08i<%5eqIbv8?136#_QK-YhTmCtG~ zwa4bx8r0;r8o`SC6JMz8*VtgOs<#d(*~38Bf5b}vs29s;rF#sB*IRxZ)^#>3{0Z+4 z;Tv9p4cJm_8(*ySZD2`lt^OD6cZ32I=Mw%XVLO}PKVrp8u<^t)6D@bJoFtJ; zEHjxuO4rHior07vJ7R^uVg+@!;bM(YSL=&qUdA6~+|Bw|NaPaB>~4Lr;`Ow8uXAlc zFSwK`S36i^n3Voj{#RJ}4Y2XgW=R9BE)JW=$X>fo;2K;d8>L_~GJzfQ-P*y5-ex(@ zCOMlGH)?h9x%ksy&FXwO49|iUZ#Jw;EOU=9*C#=a=8b|9f3@gL( z)~}>UTwX&HDeA8Gi{1tHB@Dp{DiElgK5OUfb$*tzXZEixplU)*MK%x>yzL zXnnCN-WgW>F48ssWGLZK3IEIN&Hw+&g4KrMwjqDT8q!gOE6Ny$_G?Th;Hj%7!|Iw$ zg<6)G#UJrB{-_IcVP%vr(X!Ibu)bLN-2h7}vV0?~c(WyXO#8Ky-DZXVNmfPgum#>} z^Z!3sH)FNrZny^g2&{y9nU*fGcEYEu|F3Xa;y-8edBO57SS^3q<`d(QF+;2fuhGT#)NkrDggwa*5N)!`q)%I+Ul|0`C|r#AdQVpUr&B~#ST`6K@e zn-2EgBoK(BGzFAWssAEtVB(2i4z6a?iIq=v>x(tjYFq#RWN(T6M-luC>(~tb71o-$ z(8m8O*3G&V;i_nB?-nluK`pw}CKRie+FM_&1PQRWZ&^-(m987COI!x-Z~ebwiWPsf4gV`v!D9%oqb>G&8&Rx;H^7>NH^XY_ zZ8rR`SS`Pka4o{curfBVk}T7o{|lGW7Jr|OC{_hlTK{ZT#;ed3^ZRiAr8NJZv=PM0@F~krTm5WS2Ak2x8Y*Ne-l>tTUHm#e;d|g$0u?0zY6{WK?%OJ5x#x-5CXUiun|6;>`iKG3B@T(QXb@8h*vs9t#nqijpIZ#vlig1z* z?`Sz=U+V9lLOs~5^pz!WR|6>F?!60V`T(ell(Dn8$)6Dyxvtp8W6^mo{B%Y~kxmfS^v z#%_s?a5k%;WmXrfpnG8H%dIZf*1FDyKMX5qz4afL!zETZn>?TKm!S-ww!GO3V3}Ee zi{-6W7po=D!m9XASY7(64L_Te?`u{U%YWVaVsHJwZ3VGv_8zRyOTLFyfL>^(ORR!_ zwR$nE41crXV)e*r>x-3MiKIJ9{7+m<1;*QevpL?(Y{uh(;#VTLI(#v#=q+HC+tP9? zSeIDkwS`rM_Er~5?_l*#u$>#m0wR-*I8WXoh(NYpn}t2B^+-fish%n${@q) zVm-6Zf>n{ZuvYI<8-6xRx{p6fzru!#m2P#&YrhOX!UHyfSf58f3ae$0!%DExhKn^9 zpM&){@G`85?zZ7)v%=r9`v1X&1gL<0Hp1Dg8oz_Cg5QVbAFzDT@&`8jAJ+fG`k%w9 z&|%A8Sw0GDsQ-j@6~>JJkGQa;CjXfVsAWMWjGozRYU2EtneqScW$@oiuaCpLOT!^( zF8ptP5#p79_7@9*j%zX(}LPiT4F4|M%k zSR2)9FZJJFg!sL=N4@d)7a@Ot5%TvJA^w6Gr9I;BFGBoH^4Zv9!2iTID}R3xqTAQs zUxfVqMaW-$0ip#bU1zGlzX&P$Lgeo+LNqb{`U??lC!gxi|Alo&`}>QKv%e7e`->3G zi7}jL|NbK6?=M2U&v*X*BINHcLjL|DnH>@PsnMe=nQ z{riiMzxskikEQ?r_#$NU^4`IDapv@(VDDgA({pfeNbp>t9A5|uXtu7)a@b)t%f zKSTKU^}`@@U;vRP&va7E=^??g!73&^6m{M}(q;}N!)j)$gv6^5>JCGwVKRmx9Fg#b zgj%N7aD*j;5M~ZXsBQKj1nZavBcQrwhNzy|E2?jrTmzkNW{Db@gOC}jnsypV(hJS} zkqGfa5spb{XgZ8S*dk&1D1?j6Q3>h85c-ZrXkwO*MyNR);k1Nirso)h-4Y%fgV4g9 zl8`?FVPq;oE3+;Yq46~c<;EhkF~i0p9FVX>!X+j=4q@I%gv@aW?aWpQiK7terXeJl zj5LHJ65f!|!PJT(EE$b3Gm4OG_DJY42BF1xgidD0c!ZM@K9~HWgvP1cVfG zP{Po$2%XXqE;IAf5#q-o9Fx$^bjU#1B4K$3LU(giLV6lP--!r4&C-boHKPcpCG<8u zCn4;X@Yp1TzUGvK{P74QCnNMX>n0;Ko`6s;6JdZEmWgmc!VU>nneY^ZdFcq5QxFE5 ztr8M55RMH*7-BkH#h)V*X7)!IW-4VNESZRqmxVCG?3B=B5<m2w{OaE+PMVgu$~A?l7xnAvB(e5V{dz zp&4)^!T|}JB)BFx8)4oJ2+`RHi_8Xu;9?V=0~wPFnInY+@0vsKQd4Oz!jd9{ytxSX zn4J=O%tB~*6T)&c?Iwhi681}2VH(_wu=YlTxi=%MGJ7QqosH0L9>QufYaT-U9E8IX z)|fW)5w=KJG#}w1b4Wt^T!e17AgnVBZ$YSe6T%4zkC>EO5q3*hb1TAQ=D39Xn-K=z zhVZyqbsIwCc?h8e2pi0R1qcTuY?83i1aC)}Hy@d?7BAk@4U&3>y!CeSz7a+{N3t^|(D`DvE z2<=>i7tJgeA^r}8!xDCxHg_Xzk+A4)gjdWV3F&ttbX$b5$1GfgP;()|2??*6l*I_U zC9GMD@P;`qA^$Ff!3NWkY3w3|mLTjk1C}5hkg!R@J0`dkVcy*c(WMCc%?1gH zix8?ULwMh$E<-pXVV8u1rqVqKOBN&K-GlI<*(sriL1=g{!pCOXy$B~I?3eI~X|Nn& z?Gl8!%MlKly%L5lMQC>)!slkzeF*W(5DrT?Y}%|q*dk%k3WTrBAqnaCAaq-aaMUbZ ziBR)igcB0JF)6DMc1u{Z3gMVJE+Kz8!r=Q6zBjAxM`(N>LTEL@aWh~w!T|}J5S*U^ z#(5xkwVAg9CHepgKk|K2O5#dx)YaDndpW-b%-A(3M^+MN*BauS3Yf|dqAXd3lJ_7A zKahPvN{{X75_6GxPz3cIy!0%&c_? z@oNwcO9+`Z45Uat)f<@(koDFlObwjc8c1X zTDzf3%rw!ZW{;?yX|M-sZ)S)R%wAEVY4R%6!ORjRnS-Ka)8;j(qnR)2WDY^*=n&0 zO+JMR%q-D$=Ah_$)8-H~)65s$U=E22O^45*BC}95%N!NmXi`3hW}Bs=Ip(-%uIc%O zhH)>W_ShGU+Rf&ag!Fv~BM&3YH|q`~)O-h_+?NQqnqgle?3S=Y!U7Zi3L*bpgv_rH z?l4;=G~SO;_Xxs5lW_#$fP^T(Pnx5mjV9$3^psgD+GLK4o;E#;q0MHMXp1=|+G+;;MmgJp=HcIh zS3BE-#`#^_^A|Kc`a7HPj-Yu`%1J5JPNO^*G-FR|dp?Y^OUllmsr(1Z&@WN){y=## zXkL&K{}oEZKT&oCP4=HCTcqrl@=9>g1H@LJH^ z7)04E<*<}Df~Kv5lK(ZzA_wKIp!rNn<8M&9#i8sCn!Dmq4oEp6<(;7ETFSZFc{ga5 ziT4N1kK*@&rdJ4lKWOe39|)Ra@xh=OSQ`F-P7!}dr<_BMJ-#Q$=sD#0F`Xjiq?Bs$ zD4)-%ySU>o`+D)EIkh)F%IFh zgc_!2ZGLQ$!utP$96RwA_wlqR! zJ%k2ktAwHFAk?joaG}Ylj}RY^@P>qjrq=lgTO`apAK_xNM?!iSq3>oER1?Q6+sqQH zSq9~_lxB|UwFPCjl*hK9v~WzZl>D+NEgFzbD>I`3*)%>E;bRGHOp^-`4oFyV0m3Eb zpoDql5IS9m(9X=i5FxQV!Z8U6ro%-DMHDq%?lguV@#0LhNI>nh6aQPHVj z9%|_H5A(LK}jCM64qn@TxQ-qq;5b~NL^fo&s?3U258A4w(trH3Qlr46Tc>Ny0c2yaXY> z9zygIgs9mdVT*)nmm*9ssh1+8*GJeTA;VN^hfwo;guHeLlgv&DyCpPikC17mwMWQr zfUsXemT8cH(D(v`xd{l<%w7oxB=En!(nkri5)tNIh;Ud!o@vtoA@L%FMI8{XHHRb| zkwp_d?RlCatYdm_YNiV*FIu*PhVuth?(UI-7F)Lsbb?GScJSZ6BrMyS~y zA+I;WBW9w}QbJN3+d36GlweG&K{snH*O5jL2;5)Mdc*AHQ%nbi+r zUI&E35;mDO{SgwA5Ek`E*lZ3-I3l6jl?YqS!YdJ$BqN-Vu-&8#K!e*)L_UV=fqqazM)5p(yV-W}lRKm!q^BhO*x=Hx5Hd?1pk!%KMIKI~?VRltsf) z4pO?5C0C$y8-em6o9GCX9^Fw+Ncq??U9UkoDP_$yD4#gyM=5K2pbQ>~a>z0Fk3<>T z6D2eX<#Wdj9EB3!3uTj(!}Qo_lr2)Cqfx%1$E2k9MyWOi(Ll*au~_DeZV|BOR9AZ6}2l%MDyDf9ZHv`a%dLI0$o zBwmSfSjw;TPZZ^dltodLQ?&F^MrX+Yl);ZNI=|7<$8?h!h!R?la+;Q|M>#prDQ})$ z&xaI$(yPj6?N!9-HlFDdGz-UT`V2xiAtBDBOhAYqjId?`LdYDKutmb)bcA!vs&s_( zs}VvO2w^iI1EJ;+giR94n&3n>rgP0OQ8}|gRNjOqK^07@sG`{_s$?onhANv3Q5CaO zRMphVgsPcoqUvUksD^1U1*&Ogh-#U=qVr6XEU31bC8}c%it3s+Q=xihzNo%ABs$-8 zm+ z3Fbk~%`j05vq99-gr`HTOsc50*(z#dDqRb;H5sBy%udmzrdB@G&P)@vH+w`0rojv- z(aaEaFndKwrbz*mY-T~GN4isP^Wg%gY|w3;T&i4dUiR25z81Th@5aJr?+wm%lFRVF zJll0<_*r`GV}O1OZ>dL=&o}&PbqZn zazc6hTB@wFam$U%;s0Ykj;#4@Oc_a6DQENhjZS>%^7D8XhB-9TsckCW;FNMp-P7EC zJ|W(mKietA_*J@rmpMP5&{zo;&msQd`nGmn9-GtVIF|-PZ(T%Lo2M1>psk2bk7k>f z-RwBQG8OoZ6BR4^yrpW{Re`nIwemIopQkM+=J@}rdVOnt-DQ^F>^!0JFzM7|QQY3_}%Gdx5Xt?Znsm zZXng7bbZ;Foi%xK-cD4{H%saTj&@s zv$yZ0r2PJ7{Zi+WI~+s`y7?HTXaPDS}}g_4e0 z=|zrpOwvYY{W-D0Gv98bN6yY7_W!J{{yqOsSvB@GrEU<_m~ef<3m7$_)OizACq+v% zHFbibmW%!0XUAqtX*teJo8xr$%Kx`LU|TBDdH|<{p&}%Y`ei#2XdtajMO)9?J7TJ0+vuO3=Us~xc#TXA5iGT~AWVHO6? z$IXx7$=KgvYdu%BT1RaC15aJCH`R2)-fD-W zx{Vh>+hnyGR@3lyv|7#BTPwOCcCcbC8!-hf_K&2_vszcQjkH92hF*cA7GDPR9uMsq zdaI4J%RwV-?GJUW*3E`%FQ{j=D>VPk#nEO^--_L_e^4%5=Uc4@_AxZA`vz9)iT$IE zcLAEZx)(TZwTo=L-e_N2t)bQWpnZ#0s0>w8h4%$|k(soMt=12FqH^YHY&9NT^inHt zBWYr_E74-VglTHE0cd)+U46J2nl>mNaRRAm4dAXCf9$|j0>%aKWj663>>&!|y4-4m zu_sxro7JvH%d^@QXsYWFpttxngnQU{L$Ngkx_Vk|nAX33xv%xy3sEiCZ=&C2jJ0;h z*oY&rKSt9HD%EP&V1I+AYpm5qVt;G3aaPkyV?VK48k#CN8XS_Q@gHv^j=}yAQH4yf zS}OKuHgUSu#-bHiEyHT#(DJP|(Q0XEdbNhGNoXoK3bJUhu1qxV#eCy&@~k+;Mx20l zgVmU;=ZvmR__cMXsMXA-Y#^!eew%%u{ z)$*X#RCeL@R(!~cMTlCrS}tp?HVa$pR@XYK-H81Y-Ktgau+?T`>s2dS1&>&5j%+lo zf=8`37duGpM{E2av*JyNvxul`JsST7ZU%a<&scZ^f_h;dm}%o}v}xy~-A;y@$WK}A z7VKNpMO>S#b}M!&nkKj21?4^e-G-xA-Dq}iwhNAFo7L{be%or>t+o&?_DYgx(A1E-z+{bqCe*VwqKmCpkZ7VjXSKVr^}adHljp6r z2>TC)PV;0ZnmS}LC`CNYlU+8R!PYA!rM+ymCD>ztuEJLkRhy+?Ho0gR_SlHau;*Is zRjb{Db{m?8<~6I`i>(*9=+YajC@-)ae2uMMd&6q?VQaroZ@lSih29IIwPAQ&WFxM` z*WwrzX7F%;cz4n2Pw+>tLMTLE6wTH3QV&(CX)gHlCgQb1k(29@Z zsJoQ$KdknA3IiN=xr{waLxm@K^;&R)C2WF18@Pj5L^Tr zf=1wC&=@oUO@Z#L%|QiF8I%W=Kt)gmXeyNkb*O}ne}@o0(;I|7!T405`G>$~K|9U#(F={@M<_Ud>S&XD9stI7_AU3SDcpcbeJbVOVP76Y9O&jC6OhJj9idb6}n zedR#~P!UuDl|fZd9n=6dgG}9880Uf7pbn@D_}AqHaWx7r!fXf{fr~*L&?zwpeq$Vd z2dBYF@ICkjd<%|&@4%PfD{ufD1RsD8!AIa@@HW^B_JNzgJn$-b9kkG~t~Ev*(ALz= zr#>AqJAnx33{pT>P!80kUv%Kq>!$QOgfgHCC<`is(x56h7dW6CI0sY%H9&b#9q0qj zI1mpif>NLo2$yDkhcI+n)#4xPr8}CQ0iFQ5 z)#?_kyQc1vy3Ojgx&!ETt=ss`Ku<~Y!7bocunOD{Rs%ioECcs|<=|ee*O6cp(6-zd zGy_e+#h?Z_59q)-0$c;ygR-C<_>>{~5PSsmJn#y<8|(p(gC~GqKdTS2UIN#H0gz(gWsfl**INB}QU$a7!^cmX^QhN0g}{4qeMr=egt7yzyW zeLydu*R}TmLzM;$(f)oJ!sVa~NCJ9EeS7dY5!%8{K}(>w;x`A)KqkOr$W`Y|4vUi;B244ZXgC3wK_>y_j znHC>rt{eeh10BhZ0v*G?06KcW5J}1?kk252gW~ zc2+y_>G_lVyLP5_CZ14shN z;1Io~kM~{$`mpl`PzdJe1;Vp2=7C$lTyPV(70d@WgFC=4jO(vJhm(8?(u2z`@HYO_ zU_E#YJOUmEkAiJrJJ<*|gJ-}FuoXN7o&Xzk6xxEZ2|Nj|0V6;bnd^TlFdQBMI$|e- z1aJws6ughV1#AZEKoPhKTnYMvL0};01^NKJr0)n=%uu*MKNK}OX#5OL0=)q8QJ_Z= z9ZGcIcoFOduYf(E0|l=oT<=7j2kr(saNGt~f>l6o#MIj_)3KWay}+?4elyS@HxYy536qkC#l?H zq?rozDx_`jGawa=0e!(z%9sOQ1A2U44fN_xUej6Vy_&K=nZ8A4>xsM_i~~_H0n8(! zUR!xB_I&JbiMtk*#s7vhM?ovFgu<(UYM>=yT|hl_eNy@-@lS%&;5%>(JcYhdzhQrz zOmz^^p`j668`J^kf{Gv>d_qPWfIb;J3cdwLfIh_f5=24`g`C*@G1BN z=q>*{DQ6ix4y5X15q%&v80i00aRtx^N*jqZ2dGl|+(^1UE7I?5j{$u~bO5{$^ij@r zUVfJ&ZyP-q#DkIelgR7@tyiXB z!Ox@X0||Wqp${D11qyqOF!fptm1%uB@H3VE37i5|@B=^%DZmtW)Lx)BBs~Y72Rp$F z;6?Bf*acn&uYlcP4|o;423`kmfH%QgU@zDQ-U07|{Xm5XBf(7|A6yTl4WJUo^(;_K zD}Ds0z%lR}_z4^ZKZEbU@8Az`0-OfFfUm*#;8*YsI0=3L-vafDpbsweLB%8BF%Tq7 zy?Pp3+rYj0vlHwBdPV8&;3kj?)}bkrOv_Et5`na`wu?s-Hq?e6VF>gYfj$$+B+g_I ztB8*JI@;?fe;Sm+c0gf}KRVfJY`TD+U?AuP27qqhD$pCGfIi>~FbE6*eZgSR4|E0H z!PTHYxC~qgdVtG8Y&b?yoG_B|z4tmPKs8m@+(>I?gSp@~a0|EtD151Hy=>uYTCA2T zT?Wu;SM~wo?WMq2D`O4Rq*PgBi8CG?i5tTWhs+NlK2D-<&dTs@BF7R;CvvPyRlqb5 zD^Qh?7ORNzNFTr-uGcfX6LnZOr^@)1Ox<~LtqLn*RsiKeIdCp03yuTb&lZ9x7z0KD z&EE<@dt-UH95@%~p-LO>CZHW-EqDm1Z*=U`ZRt+nf;&JJaJxEv0mjYXCNLLV2ec}6 zqt!;MjaO~duCDu{?rF+Q?NqyUpVR$L_c%Q(RRt-a3#bd~fEqw2dY$ag1GPa-P>b?C zp$Se4pp*K=pgyPv8i4abLvRte09*(f0i|gSnt|q^70_<4Bh4is3A6znKx@z*NSD^u zay!cRKH*J(6G3O743s$8as=)Kx`E3;S8%ztd%}7$><)CN?+yBazCb;qp1KOC*9L%e zpdOVz7!+#mML`-E2gZU_Fb0eUqX3J;`^<9$tOsZN_4%I~^Q*xSFdW1xrF=C1Rmc=D z8E9zaXM!A%4RSU9c^I10Gl0hYMsNd|39bhkd+D=*dQ0I&U>2AIl$pYB1GfUDn-8Xf zdEgeH!USn|f`#BNaJO=l6BCzX8?YGM3zmT;U@5o<+z(cPm0$%J2-LzWf%FH#8n7BX z02Hn?Pl6|a%5lM3@ECYhEqVkz4Ay~IgvYVhpXE#60G_hiR(K2844wv6Ky2L01ldA? z&h*>Shk`%BAK-WJ8~70%2ZcZI=NR}Fd;`7)N5L215NH5C0sjCWfe*kz@IH7C><4dy zS3v>T19k%q$tyq=eF?k>o&lOW&%-Z(of^^SFrEcF!1F-9co%36UIwp&*T5V9Pj6oy zS9A8pf4lb!OT7V7e?N^1?GhZPrw7H3GfoW2A!Izafq*v$HqV-pdnBX;BO6pVEI|$ z;glC->;ZOP7o;ITXF%cUgy&G8Ezlll2Xq8F06dT8g&fPY1P}}2RW0z)9B2iwP#b_V z=1f>Af8)$}BCce@87RuNsX!ty1@cpnP6GM^{eVb795?#Nqem1z zSb~um6k-gXqXBL@1CUMxCII7sUx5T59vBCV1$Y8I1{e*D0(esW3owGBW^1a(a|oD> zS1t>eL$O_{X~oH09;S2oxXkQ0cC7NfKAzdJ{I0YHCEbW`Yk|4I8Xy^%56t6pwgq@t z3Be`O2NfaZ;x9tF5LgWGGpDP8RX{4R5=a470Ly`8z*1lXunu4y>yZ}8P*q1GRc0}y zoz$plV?6NGn1_1q(L5dIDKt;5c{@!9@M(iBpln6sQTzwfen$qs0lR@Mz%F1bkOu4o zb^zOfJn(Nr%HOj9rX2z@xu#ek14sv$=>WitdjO{KcYfaw>;+mu**>HPfo$Lia2PlS z90g7R#{oa^oj`gLI1MBMSAfgFCEy|upM(FM2hIQi5IBeQto+OZ7l5n4HNX+{>qx%< zkAVWy8_qhJKB!2>Lfj7V(Kpt=h z$OUc#w}6|#4S@Ms87pDsY(O3iQw(~Ecg2`zc;?KX0^emS=5Zh29&=A;w{j-T_z-vj zu!2J1d+S*NEdEydzwoT^aYkI)kHC9CE}dAY4|r!@)~C=G!r;$%|H}1W0&Y|p%$D#sHq8m(qAA7WZ#4kSKjOu6qMz_m9jGRK6wi`8@XT9FobvgSC*TWk zsOH^Z>|dw?fx3X7{LaP)0RBL2pbuAy{D=8@EzE1=#y}&WAxDe`cnAjS1NDG5Knp;N zjCdiA|HOrMOFZzxnit!=IBx~;Vx1S`_)lDjbE-uIQUN@}T8j@|*a)OO@m>tipYW_0 z^)+68>V%IRt_qPx1HAy=-g2Mh#G0P(;|U_X!q!~p5Q zWMC^W0N?^JKhrqlG^Ff>2}qH5yf{>G#++$=5XK{A#jG$J0`2jQ6;1-i0kiQw3y8(@ zSRf7<1B?bn0V9E5fDypYz;Iv~FccU93zRJ_$f%I0t%ls_~2fVp9`kQaCLE2tN=RVd;APM z1cRAd>q0yeumE6>%tSgL;6^tOm<4bnoCC}T<^mz$;|v-DESn500(dRJ@5_Ou0LNwi zwgk_M;c;+P+E8Y!c!tz~6E8!a($rT=)0_^gG zzyTl_nR1uAgERy0Oe?_3SPA!Vc4$TPsVlH&Oiz`VBrnLl2v*WdBs8o+^qTPT+{2Dl1b0WJgQ zf$vMMC{r?KkK_OsfQ!KQMlr9#$9j0-%IzT-?N79diPr(nh$9pm!lmQ-YmAhYu>eOs zHt;5J17KG(AGepHo?w2iMlRer;55Kh`UjvC@-n+oDb-y(-vM%gqNP(jkOvyq>v(`= z?*ZStm|OiDfDJAHI9}u<{XPaf#QSZ44U1<*Tq;(^F6TA=1Eg#OmyZ9i7q~QRH5>a3 zP`vXL&s_Q^NIBET0DF_$0tYDeDwm!;%6U8jxG-{|Q<;3MFHckV&_-3W7nLok93;9Ec(4K;in zp&A}I{F>nT8iYDA@XiJ}<6C9G3E;D^XecOwv?O2(DD9yVo*e-Pz#gyz_#BN-h4?x{ z1;7U2>kZZbUvIDi;>+THWdKWn58TWFGoUoUhj1K7T##vPJoE7~A2aiDGaoziy$HS& z;RARBUVtay0r1hZJ5U3t4pal&fS&+Ypej&BgIzoqJn*7R6^#e{nO2-`tkk%<_3GJs zK;PIts*i&z%>4A|;~qy7tQ#GwB zE`p{K7*d@rHM`vw#rms_!adx*+)*E?+iwg>F&uTQQ3o19C6lf#z#WpIAGeK9&N9 zZ8;aqUn{pi!yJCF-qWjvF?p=h*r>Z2Q}cBw?I2?s$M1>8v=Hw?7WA`SD_jz)SDVzg zm#fh@Uw56m7h;-m@_LxXN4T8%+ncKX&&_U+bz%?u!gTSJvK%Niwr;G{woYOYC=i8f zajTC)AS>_*UIlQxdu@|3zib)P$QZD_e?WBwW;PjBp-dv1kZbF^yE=FwvwKjBPlg3AVyck(* zM%FWmOa58&=g2DJu;&_@QXc2l3LIP;lPtQ$#7s0=D(2>i2CC`=3U`hQ zWy-{aRyMyZ%fgQSrsTc_t-uz7EZb&WOvbJ4J2!&^ZI>-|DM`JypawUWq;23-UJC7b@(OJw_ z?8h3YWQ#maUp;ecY^7Bjy#%inzBC7gJrq*&NNR=C*;bI@W&{sSHm5dQ;Thy>;jXn2 z=75tMTobxM?J@aV!_Eq z^SfZc9)ARBIC^eb%3z)M;RSZy-7ja;TzyI}gk7izuFeluRKU8d%28e(IOD*^C5UL& zyQI_9Fs?Lis&JLWY3=8uO1G9OXI1owpGSS^Y6l+Z(t+t1DD1uo+xnh&^KZRR%*VsM z7N(unl(Zdvt%MC_ahhgBNtwtOz0_<6ny4HI)cY#Xh#jDBH=&I?G`rLXDpJHwG^S(N zTw%>oEe4dUId^J;sG0kR>O3giR9>IBQ9{?LRZW=!i*Hw?G?sl14%BPHxLG^TIGEWw z>8<)KQ#y@#_n>V2mJFE!+0p`if~;VN`4Tsw#}owJvz#i`8~=9^}1EMk)DFFaU5jZM1X@^(wv%IW>nc-*Wbd( z4PBD|tO12PPl-Wwjz4X?_yF38&fOE$s4bSD_T>P-D#44^A~kh8KI#0CK8Oopk8_rz8g+Ulh-^Dh&pQdU#cumabVW zti=$(&RahDK!f$4&DO$2a(pTK6V>BU2BS@vy~tzPPc)O?$5y5S7i~EQ6vbfiJ-8oA zLzEZ(ngg=fhx%PzO)Bi;_0!C+UNu2Tn2#^qo}8*nly>*l4w&C-YX=k(MdT4tmZcH< zPy_OddSP|i428maS!~v#CjDPm+{g`9jz-(7Q{Fy!uWk(~`nLYXZH4cJtzW>wV~-z> zf@+ZaesEa0OO4)YYSS;f);Pz>`NM5!MPE4oe`-(QP6KT- zJgDUX|?IUa;gS04&EsBsf&p?SDM*FUYO&@~F6fE}-Oj*@fqs<5!F949-N@9q1+ImMETKCKeqw8s$*6@< zIw%}Giy?m4BOR6zwkb_*{z}YUoaejvQ=3y7D-#|q0{yAmA^5Z%xY?%_OgcJObR97t z+#c@!T#9ginhg$J(doF_2(xjXQLADhi!m@^Sjz!n5e>%kyn&}2sE-7H%4OLZvMm9g zEkbR7y~FbvZrf;mn?PY772c>^Zsmr1L82^Ah*gLDsd^Sle8!&|XCa!{mZbSv8ZTY0 zEHQKCj;W8{)fuh33*gTl4D^HluY;C`E3WLbCzvN74Hw;ywqGNTl5sFm4WiMo=mQ#0bs%B!!8 zn@J&YQ>9L+A18fv7Ar&y(yyA)mlJ5}@y(@z zJ}c!|q2zcMFL227#WQH0+)YkC@!9v&TErq&>65T*VMmrxgYM2Inb_PYD-N7q=0bLedU-&PmVCj&kx$R2%uZ zW3>n`^=(CF99Sl{qUEPFZo2;tC<75#DnM7{??F4lYWO{{Fk0P;zMMj^(uR=ZY3Ta9 zP8+olLb0bcZVvyUn~Y9t?CA1ojnBU~cF=e_0c2RdnuZW%N^fa=W4lP&a8$kyp?{+qb91S2T;&2+q2z2wkh&OKl>kSlh^< ziKqH#jo?YKeH{eF7P*dZv0}ip4W=#ilxyv2`BffJz=0`o!tSs6uT5=FyXZO0LM6&o zuLECayzXeEr?`buK4gW);NW(hICM$X+lkFe>p6OdlH)aKPXGrGeN;Q8{lw&tY5*H)@Bw{F6cQv$dsKf+U_2YCzaJxY{Do5 zvO=ve3?`7RRdtJ>*Izk}^&B0;sDQPP0te>f3Gc%fjq_Q0`m3H}DJYnSCv-nGq*24> zV=w9{Sz#1-9kLa=OLdu>duZ7vuY>+t1aU7h5>x}l3bH4QwV}q32OiN=g1S=@WQ8!9 z!{*~h&vvJEef1ns-6@N;j{`?p=sxaX!^aCdJbPu*s_?2)UQ|P?5nS*vz@YgGY{yd=P z7y=5HH&MH}e-F!mk$TF+aH@U-mMxa^omV@*a`cSFiF%H-aEbtjE=%S(`DcPd-0HA- zdXCGo_TrX-)%F_AKclBS3a2#4s)HlRA`cTWUQUNZQoTF!bJs}v#JLZNl*(53cyZVE z#ZT;38@XY+$A6|olE+PO1cQU8Jl16&wscKwdRXk+;&@cGAB}s=c_0O+l9war^~-a!YqhT}rnFUy{oV+ReR zuouV45(CI77dn1y;rSREoco6FiS;elGZo&!`m|`~KiDw!MW-r9IZyP>!-R{VG(~=fNt<~S>0dSQ zHj|vEwY`Vx$3^{nb5aKxQ?I-5xMqm7m^qkK*x2gjC@i6rc0OweZM%#1bO{_>ud9Ar zIBG@o#V9O}{dh~=LsfLl0>ulHDld1xH3EHstU5vVB|1+2w8&>T;wnqeML$& zj5?4tJRXW17}I96c7r3A$YF3i5-B|Du&lw6L*y_xVu%!s!_^8EMGD5WG{~wkrk;F++U*}lpEx(e{lZ%slRPkn{2(GS zhZ6a`(f*2zIJE6C&R!%2YppM$N-_e?{}2a#Ip~BufrbAN{?{n}bqRbY1PLy7s=g5zTXbZr$B41R`M?F0@- zaGaZ$(e~65HKLq&ZU_!{*@)jdB%i%_d3+Dvca~^pf2B;8eFRxV&4gDz&Hn1vrSA~E z?C9~*EMjWnt?WUEXSnMrvaIl9j!qM(UOu!Boglf%-{I_$h4E)=X}{S%6BIk-dv#&V z*=FX8qV$wa6KE#Oo&twGIBxxX>y`P$tq1iSk3ivhc3Ass@AD6zKBT9ZOr%?o64_U!{ znk4JgrhMV&Om#RoFbwk2z-}5jzCu}Cr%}Kw%uMP`qr_JzW0Pr=@k$e^4xL8Mukk%| z8kxNTU)|}H^crc4>9m%=cb`tV{9UFCe)Ls&kDQNe-!@460rT zou85@qEK%I%`L<{Cw&H`;iHgdBNaZ?`)bIFH6LmrBYBsu#7z2w#UITg>$f-~DTx~3 zJ?&mYoa@#rY3?U-`tuIp*V$4(-*3Efwf5Ay?IMNemZ~mg=*(N>(qfLhvuf4DYRr@1 zt>8ehx!}v@B>xH9z-ap?GMhV>Lh!CzI#*J;I&AgEMZb0F4FNA~szP8rC|uKxiXh>Si6B!Ma%@4V;`Ju3Ryd+ zyOpy_IPsYsANPP#-jeL!LA&!jX+0IXb;X717I}}sA%{JjK(>2_u(t>K^Llc~+S{rT zRV(le2pgrWW77gk0f!n#(WNe;)ZWSIFWzaQjno;*3n3EOfx@#;|4gmaHoQ9oXJdFvP+J-sD%f~3(PR1E=ZyqPnxt92V$5PiCgOo{GVv&+FtTI|fRxfn9>x7|=U%eu)NOX8{sDJBr+m9QhXbS^XN?Bw#sc$5w_xejCh<-~zVgGlp$R$ettO=1V^HQ%~ zOcOt2XGW=nAG=ugEyaA%clXYe>~!6aP>}_-{S24Ug)_mbZ`U_Z3pL_v*av*KH4Hq4V)MCz_P1 zb;^QnsP-i()W#I)>XZeuF=OA6B6)el`PnO5w!SQ0W{~txf?OoER?@cO z+H%I|S&EAKD=9~VPZ*~qZE8j4e_9ExQ#k2mHMc)Uh42vTF7ZsDxCpch+HMPa>rJad6zvKPuFWyG{_qNC2xtNMlrFM zD+b6J|BIO93%$v*Lz35!y;h5BTx+Do)uT-h+Q;r#WW|jcXFI5tA5^8ck1BC(dW=@9 zHd60gM}v)_r0Do(^=%E8u}-RWck|~{uHL^-;+eU4WP|lzCd-}x2lv09qtY%lY1H_& zUbg6{Ce3?rqs8O$Ix1xXxBdZHj-;>NR^K>TyYd5b)Pm`_+(1NEhvvYsr8LHi1DVB|_@X0Dps zrpopql6Ed#Iw-u?KJCyxi<`IZO=1ei5M1P4PvOPTtQO)TQZa2y^@RU zOF{18JNV$J*7KD51o6{ZHw4vM}8%p|unCLYzID zYPN9S&od6xlRF!{q2#5cQZMqN!izXmy0Aqm%yM1t*4vNv8!n3Za$LUw3U9Q1y4GCX zDD)f-QpB+z#`jF4@JooGgVJc^B@CPc;mtDOtaW($p5z91&x2EJ?6^_7m6oHJx~bq` zrxr@iT^>R-v756&B<6jhme)sgm2=U6fP`1zxsGvd{0Qj@`14 z+BFb!6K{2F+(lX7&}D$b3iixs^mWMexrNVpwd}*Q4OI>(Z0SIw5?80p@13HjJlI8M zr6KzlIC%FsSvPFQG2!@5J%{CPX#n`8a;n9hq_`M8#dSAzgRG$0E0tl^#s@d+TAZ^J zE%SHx^-@{xrP<)XKgP%xYUxO6t&cEW=04Q##**{H)GgM|8kR=SSU0$rPfh60Sf28GP>686CXx@m1WRpaah~V68T02N*o>9{ua{HV%G_9 za4Q6dbQYeV^d|MCOsZ1`m3}8v%5B-mF_E3d#)U|h>d*o6Gifk5)S5%|$rYR@4oTZ@ zpO@8&uin09HT|Y0I5JrmI{QDvu2(H=3B1gr*JZQ`a`oBBH>$qfX;jNz`DWd(hsmNg z@=ii(g(gIYrE=xihfJL7@FGXDl6!S3DD208$c@A1RSk~@h0i`cab^23C3rx`{=?*< zgLTJc&K$eK-l|n4M(K52JWQLBi~7!C+Exc?{$a}F_Ya3D#}28<5jw}3td5X-Ii$xA zP@D2dD<7d!+$g<{kXe1C4UQz&LK1p}wt-C-c|;oXrX-p~?Vb1spK{BMz)%&7zs*cg zOpsNRhqX3D-6xw8tf5$H{Nm}wV1K+TUX#6L`0zx$zdkUV1h!Zykx~Nrm|Z9tElnw& zMURR5x2Q+kfkps@C*uI&z|jCwOuq+lB3qe?kOfjA-2 zod8EQa8$gsuveY+^V{oXU(2$0dArxmUTOA48ZWi9)7l!kRU=2bJDD`z4zak1m@xF5G+JD) z`*sVqbl56%M~-wdCkggipKqb5Nas>_JWnb1@VVTDgo@}gJX|#2{WUMQTV9ydM!1l@ z&eIF#%=M?@4j99fZYy0Q{bBe07G~tWFMP<6r&TV8>NLDivoCVsi5EJha*k+vfgFR7 zlXNq>oH4E$$^)e^2_6+LQaGm=ZRS?O0RQzO%Y)aG~(YFz6|bV0pKS9$eeTH%xRQ->;O$DMzdW(qsj z9ta!MrWlWGJllgAmwuo;L3oLI+ z6Noj*%kzyLBkGH}hcEN$+lCR{Wzv4*DTI(Pf zJe;xMh6)_q8W82ApR=^&@Dq+7zI>};9lpBIFd8@GN52?2t zE>rt$kCt!3epB31jX_?0e_L-3d)yH8uvl1FCpuzm!f+|iP9eE^^ zJJ9xf)CL^tBljrK8|nYtLcc4X0XNDsTfyeOv@%_F<)rJI9T#hI#;AEz*6{lj3-<`w z;NZB?G=JBsZco=?pMt&OhlK$gXGpV>aZ#_yB@ljm{Zt9l-wl36m4!Z}IG)hUZlO{> zXveabsh$s})=F?ylqpkxxe9c*9?IuvgRUO9Fr>62R~8C{@caxE4hxsBq;5{C?HVLj zg1DiBimvV}jr)d5tYoD`rvLkOit%F_hZW7Y34_v3l-mD$t?SA>mUjiW9@uy8!1eoF z2C2b0%amk`g!7k~qYqFfQu z>_ZrN|{(C2|r#tDxO!YR4QLtB`N(*x-4y@9`=-G2Ec0BIg}NE=7es3D*&-l z$yVvSawSl=+S>7I%V(6w>sh6|w$G&K;WFvOnnRA~`8t_A02J*9%Cu8y%JOPfX~o~B zqm`kNcP+A)T)i-P-BaZ0RPlu}&8?I#4c1F{Oi%JGWo(H( z58lCp-*Kry9f+CTQgHBrdF^Sbx4bHS;46FlD=l!yKU!5Un?uoomsTO#y!*G?o``yG7cSN`Z>FgX{Nj~=3V`~N2Jvzg!DQfkm$zy>$Te6S z{q2_txEG%mthK52D*7C(E#Ic-7;9k^ z`bG+Z(S3&vXV(5RS2w@XdVWm{qexJ&ZI+OQuN;H)zdTwE3Sargu3n!8TCZC53Z-#3 z%C5O?=k>pWQGRc11EZ8}K(PX6kESVSCYH~}u|F?Icwp>PsG_qCwB_yQBU|36DreTL zOYi>IaP0;Y2612dC|HadYCVO2hGS?!l=D;-iK~L4%gKBK;-TshPD>Gig|u*~yoVpm zSzoq%lXtw`D{_P2#{MwAGEZ4V_1S zk|MjIi-h=XET+V`NLZ=~bQ!uxC~@E-VLfmdyhtc<7`jL(QE-uPCd(SSNGNd_tUJ*% zTqL~3mKnNOFUjH}p+!?@H*}Fu;xJfuqIO&)3};zG7YQX0YZa3w$2Y^qtnRqUu|hE3 zIVAjAKFsW^l}~O6-YxKc%)dwbpb-J_*M<+P5j&_)U)97;x=72Dx#5=)jd*swV2|B4 z>nYn@?%uPLc_@xw5^(MDkydMsvAxRtqxS7_ZF{ERx&_{Ok`NNy z@!ZfO{<9opx?c(Z)YMIl+}&jQw%fmyt=}-X;wgW8;G@U7pUj)y_4=KMG!RIR4@Jp(7|E zPHRVTiCR<24$*q2ZyBha>P@43j4P&}-lF~8v3R}VqXzdKR`VBHc}#1Ud`@dZpW13I z=*l^5BxRk`8q>kH+A>sSy;h)z@gPKW(3+&TIj=3SPPclhO)hOVxaaWxHHHtWF|=>L zz9R>xZ+xfSuOaI#TEFx@e`))f{WR$3e*J6o9yDk`RNvvhL=7L_cW8~CgQIGU@ao^U zXP>@9ht(K7C}vb-R7_0z^%BOvYtrv+)TS7vZ!c}^t);kj+D7U3EsY-(D>bU;(0<@$ z?+r~qRl)eVajBlM!=h^R9oD-i3Xs0X!T7Etwi=9$Y2OB|BaKVdI#b~WZS8cbXKYuR zQd6}y>Bm#Gu|`xUQClMYU@PM@mZk}2#-cxG!=Dkxl`ATL4Uh7d?p&AYe&)tBpqbW| gp7$`GVA|hYk{W5wQdMk=N@dCP9CO+qVVwJa0Q&6LY5)KL diff --git a/curate.config.json b/curate.config.json index 4a37072..2852cfb 100644 --- a/curate.config.json +++ b/curate.config.json @@ -19,6 +19,10 @@ "@curatedotfun/gpt-transform": { "type": "transformer", "url": "./external/gpt-transform" + }, + "@curatedotfun/supabase": { + "type": "distributor", + "url": "https://unpkg.com/@curatedotfun/supabase@latest/dist/index.js" } }, "feeds": [ diff --git a/curate.config.test.json b/curate.config.test.json new file mode 100644 index 0000000..81b7d58 --- /dev/null +++ b/curate.config.test.json @@ -0,0 +1,84 @@ +{ + "global": { + "botId": "curatedotfun_test", + "defaultStatus": "pending", + "maxSubmissionsPerUser": 100, + "blacklist": { + "twitter": [] + } + }, + "plugins": { + "@curatedotfun/supabase": { + "type": "distributor", + "url": "@curatedotfun/supabase" + } + }, + "feeds": [ + { + "id": "test-feed", + "name": "Test Feed", + "description": "Main feed for testing basic functionality", + "moderation": { + "approvers": { + "twitter": ["test_curator", "test_admin"] + } + }, + "outputs": { + "stream": { + "enabled": true, + "distribute": [ + { + "plugin": "@curatedotfun/supabase", + "config": { + + } + } + ] + } + } + }, + { + "id": "multi-approver", + "name": "Multi-Approver Test", + "description": "Testing multiple approver scenarios", + "moderation": { + "approvers": { + "twitter": ["curator1", "curator2", "curator3"] + } + }, + "outputs": { + "stream": { + "enabled": true + } + } + }, + { + "id": "edge-cases", + "name": "Edge Cases", + "description": "Testing edge cases and error scenarios", + "moderation": { + "approvers": { + "twitter": ["edge_curator"] + } + }, + "outputs": { + "stream": { + "enabled": true + }, + "recap": { + "enabled": true, + "schedule": "*/5 * * * *", + "distribute": [ + { + "plugin": "@curatedotfun/rss", + "config": { + "title": "Edge Cases Recap", + "path": "./public/edge-cases.xml" + } + } + ] + } + } + } + ] +} diff --git a/frontend/package.json b/frontend/package.json index f4dbc7a..427c81d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,7 @@ }, "devDependencies": { "@eslint/js": "^9.15.0", + "@mswjs/data": "^0.16.2", "@rsbuild/core": "1.1.13", "@rsbuild/plugin-react": "1.1.0", "@tanstack/router-plugin": "^1.97.0", @@ -35,6 +36,7 @@ "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.14", "globals": "^15.12.0", + "msw": "^2.7.0", "typescript": "~5.6.2", "typescript-eslint": "^8.15.0" } diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index dc00964..a50ac82 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -10,104 +10,123 @@ // Import Routes -import { Route as rootRoute } from "./routes/__root"; -import { Route as SettingsImport } from "./routes/settings"; -import { Route as IndexImport } from "./routes/index"; -import { Route as FeedFeedIdImport } from "./routes/feed.$feedId"; +import { Route as rootRoute } from './routes/__root' +import { Route as TestImport } from './routes/test' +import { Route as SettingsImport } from './routes/settings' +import { Route as IndexImport } from './routes/index' +import { Route as FeedFeedIdImport } from './routes/feed.$feedId' // Create/Update Routes +const TestRoute = TestImport.update({ + id: '/test', + path: '/test', + getParentRoute: () => rootRoute, +} as any) + const SettingsRoute = SettingsImport.update({ - id: "/settings", - path: "/settings", + id: '/settings', + path: '/settings', getParentRoute: () => rootRoute, -} as any); +} as any) const IndexRoute = IndexImport.update({ - id: "/", - path: "/", + id: '/', + path: '/', getParentRoute: () => rootRoute, -} as any); +} as any) const FeedFeedIdRoute = FeedFeedIdImport.update({ - id: "/feed/$feedId", - path: "/feed/$feedId", + id: '/feed/$feedId', + path: '/feed/$feedId', getParentRoute: () => rootRoute, -} as any); +} as any) // Populate the FileRoutesByPath interface -declare module "@tanstack/react-router" { +declare module '@tanstack/react-router' { interface FileRoutesByPath { - "/": { - id: "/"; - path: "/"; - fullPath: "/"; - preLoaderRoute: typeof IndexImport; - parentRoute: typeof rootRoute; - }; - "/settings": { - id: "/settings"; - path: "/settings"; - fullPath: "/settings"; - preLoaderRoute: typeof SettingsImport; - parentRoute: typeof rootRoute; - }; - "/feed/$feedId": { - id: "/feed/$feedId"; - path: "/feed/$feedId"; - fullPath: "/feed/$feedId"; - preLoaderRoute: typeof FeedFeedIdImport; - parentRoute: typeof rootRoute; - }; + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexImport + parentRoute: typeof rootRoute + } + '/settings': { + id: '/settings' + path: '/settings' + fullPath: '/settings' + preLoaderRoute: typeof SettingsImport + parentRoute: typeof rootRoute + } + '/test': { + id: '/test' + path: '/test' + fullPath: '/test' + preLoaderRoute: typeof TestImport + parentRoute: typeof rootRoute + } + '/feed/$feedId': { + id: '/feed/$feedId' + path: '/feed/$feedId' + fullPath: '/feed/$feedId' + preLoaderRoute: typeof FeedFeedIdImport + parentRoute: typeof rootRoute + } } } // Create and export the route tree export interface FileRoutesByFullPath { - "/": typeof IndexRoute; - "/settings": typeof SettingsRoute; - "/feed/$feedId": typeof FeedFeedIdRoute; + '/': typeof IndexRoute + '/settings': typeof SettingsRoute + '/test': typeof TestRoute + '/feed/$feedId': typeof FeedFeedIdRoute } export interface FileRoutesByTo { - "/": typeof IndexRoute; - "/settings": typeof SettingsRoute; - "/feed/$feedId": typeof FeedFeedIdRoute; + '/': typeof IndexRoute + '/settings': typeof SettingsRoute + '/test': typeof TestRoute + '/feed/$feedId': typeof FeedFeedIdRoute } export interface FileRoutesById { - __root__: typeof rootRoute; - "/": typeof IndexRoute; - "/settings": typeof SettingsRoute; - "/feed/$feedId": typeof FeedFeedIdRoute; + __root__: typeof rootRoute + '/': typeof IndexRoute + '/settings': typeof SettingsRoute + '/test': typeof TestRoute + '/feed/$feedId': typeof FeedFeedIdRoute } export interface FileRouteTypes { - fileRoutesByFullPath: FileRoutesByFullPath; - fullPaths: "/" | "/settings" | "/feed/$feedId"; - fileRoutesByTo: FileRoutesByTo; - to: "/" | "/settings" | "/feed/$feedId"; - id: "__root__" | "/" | "/settings" | "/feed/$feedId"; - fileRoutesById: FileRoutesById; + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/settings' | '/test' | '/feed/$feedId' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/settings' | '/test' | '/feed/$feedId' + id: '__root__' | '/' | '/settings' | '/test' | '/feed/$feedId' + fileRoutesById: FileRoutesById } export interface RootRouteChildren { - IndexRoute: typeof IndexRoute; - SettingsRoute: typeof SettingsRoute; - FeedFeedIdRoute: typeof FeedFeedIdRoute; + IndexRoute: typeof IndexRoute + SettingsRoute: typeof SettingsRoute + TestRoute: typeof TestRoute + FeedFeedIdRoute: typeof FeedFeedIdRoute } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, SettingsRoute: SettingsRoute, + TestRoute: TestRoute, FeedFeedIdRoute: FeedFeedIdRoute, -}; +} export const routeTree = rootRoute ._addFileChildren(rootRouteChildren) - ._addFileTypes(); + ._addFileTypes() /* ROUTE_MANIFEST_START { @@ -117,6 +136,7 @@ export const routeTree = rootRoute "children": [ "/", "/settings", + "/test", "/feed/$feedId" ] }, @@ -126,6 +146,9 @@ export const routeTree = rootRoute "/settings": { "filePath": "settings.tsx" }, + "/test": { + "filePath": "test.tsx" + }, "/feed/$feedId": { "filePath": "feed.$feedId.tsx" } diff --git a/frontend/src/routes/test.tsx b/frontend/src/routes/test.tsx new file mode 100644 index 0000000..03a7a8e --- /dev/null +++ b/frontend/src/routes/test.tsx @@ -0,0 +1,194 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { useEffect, useState } from "react"; +import Layout from "../components/Layout"; + +interface Tweet { + id: string; + text: string; + username: string; + userId: string; + timeParsed: Date; + inReplyToStatusId?: string; +} + +export const Route = createFileRoute("/test")({ + component: TestPage, +}); + +function TestPage() { + const [tweets, setTweets] = useState([]); + const [contentUrl, setContentUrl] = useState("https://example.com/content"); + const [selectedFeed, setSelectedFeed] = useState("test-feed"); + const [submissionTweetId, setSubmissionTweetId] = useState(null); + + const fetchTweets = async () => { + const response = await fetch("/api/test/tweets"); + const data = await response.json(); + setTweets(data); + }; + + useEffect(() => { + if (process.env.NODE_ENV === "development") { + fetchTweets(); + } + }, []); + + const handleSubmit = async () => { + await fetch("/api/test/tweets", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + text: "Original content", + username: "content_creator", + }), + }); + + const submissionResponse = await fetch("/api/test/tweets", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + text: `@curatedotfun !submit ${contentUrl} #${selectedFeed}`, + username: "curator", + inReplyToStatusId: tweets[tweets.length - 1]?.id, + }), + }); + + const submissionTweet = await submissionResponse.json(); + setSubmissionTweetId(submissionTweet.id); + fetchTweets(); + }; + + const handleApprove = async () => { + if (!submissionTweetId) { + alert("Please submit content first"); + return; + } + + await fetch("/api/test/tweets", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + text: `@curatedotfun !approve ${selectedFeed}`, + username: "moderator", + inReplyToStatusId: submissionTweetId, + }), + }); + fetchTweets(); + }; + + const handleReject = async () => { + if (!submissionTweetId) { + alert("Please submit content first"); + return; + } + + await fetch("/api/test/tweets", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + text: `@curatedotfun !reject ${selectedFeed} spam`, + username: "moderator", + inReplyToStatusId: submissionTweetId, + }), + }); + fetchTweets(); + }; + + const handleReset = async () => { + await fetch("/api/test/reset", { method: "POST" }); + setSubmissionTweetId(null); + fetchTweets(); + }; + + if (process.env.NODE_ENV === "production") { + return null; + } + + return ( + +
    +

    Test Control Panel

    + + {/* Feed Selection */} +
    +

    Test Feed Selection

    + +
    + + {/* Content URL */} +
    +

    Content URL

    + setContentUrl(e.target.value)} + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" + placeholder="https://example.com/content" + /> +
    + + {/* Quick Actions */} +
    +

    Quick Actions

    +
    + + + + +
    +
    + + {/* Current Tweets */} +
    +

    Current Tweets

    +
    + {tweets.map((tweet) => ( +
    +
    @{tweet.username}
    +
    {tweet.text}
    +
    + {new Date(tweet.timeParsed).toLocaleString()} + {tweet.inReplyToStatusId && ( + + (Reply to: {tweet.inReplyToStatusId}) + + )} +
    +
    + ))} +
    +
    +
    +
    + ); +} diff --git a/package.json b/package.json index a2f1242..205a494 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "prettier": "^3.3.3" }, "dependencies": { + "@curatedotfun/supabase": "^0.0.5", "elysia-rate-limit": "^4.1.0" } }