From 59c0ea03cfbf0d1cab0d21881206b3ee7534a98f Mon Sep 17 00:00:00 2001 From: Baptiste Arnaud Date: Mon, 7 Oct 2024 11:44:28 +0200 Subject: [PATCH] :sparkles: Add Webhook block (#1815) Closes #1531 --- .github/workflows/deploy-partykit.yml | 44 ++ .gitignore | 2 + apps/builder/package.json | 1 + .../api/getResultExample.ts | 6 +- .../api/listHttpRequestBlocks.ts} | 24 +- .../integrations/httpRequest/api/router.ts | 12 + .../api/subscribeHttpRequest.ts} | 32 +- .../api/unsubscribeHttpRequest.ts} | 32 +- .../HttpRequestAdvancedConfigForm.tsx | 48 +- .../components/HttpRequestIcon.tsx | 0 .../components/HttpRequestNodeContent.tsx} | 4 +- .../components/HttpRequestSettings.tsx | 6 +- .../components/KeyValueInputs.tsx | 2 +- .../components/ResponseMappingInputs.tsx | 2 +- .../components/VariableForTestInputs.tsx | 2 +- .../convertVariablesForTestToVariables.ts | 2 +- .../helpers/getDeepKeys.ts | 0 .../httpRequest.spec.ts} | 4 +- .../queries/executeHttpRequestQuery.ts} | 6 +- .../makeCom/components/MakeComSettings.tsx | 8 +- .../components/PabblyConnectSettings.tsx | 8 +- .../blocks/integrations/webhook/api/router.ts | 12 - .../zapier/components/ZapierSettings.tsx | 8 +- .../typebotLink/api/getLinkedTypebots.ts | 2 +- .../logic/webhook/components/WebhookIcon.tsx | 15 + .../webhook/components/WebhookNodeContent.tsx | 29 ++ .../webhook/components/WebhookSettings.tsx | 209 ++++++++ .../blocks/logic/webhook/webhook.spec.ts | 61 +++ .../collaboration/api/getCollaborators.ts | 2 +- .../features/editor/components/BlockIcon.tsx | 5 +- .../features/editor/components/BlockLabel.tsx | 11 +- .../editor/components/BlocksSideBar.tsx | 18 +- .../editor/providers/typebotActions/blocks.ts | 2 +- .../nodes/block/BlockNodeContent.tsx | 9 +- .../nodes/block/SettingsPopoverContent.tsx | 13 +- .../features/graph/helpers/getHelpDocUrl.ts | 6 +- .../src/features/results/api/getResult.ts | 2 +- .../src/features/results/api/getResultLogs.ts | 2 +- .../src/features/results/api/getResults.ts | 2 +- .../typebot/api/getPublishedTypebot.ts | 2 +- .../src/features/typebot/api/getTypebot.ts | 2 +- .../whatsapp/receiveMessagePreview.ts | 51 +- .../features/whatsapp/startWhatsAppPreview.ts | 3 +- .../helpers/server/routers/publicRouter.ts | 4 +- apps/builder/src/lib/theme.ts | 32 ++ .../{testWebhook.ts => testHttpRequest.ts} | 14 +- .../results/[resultId]/[fileName].ts | 2 +- .../{webhook.json => httpRequest.json} | 0 .../test/assets/typebots/logic/webhook.json | 92 ++++ apps/docs/editor/blocks/inputs/email.mdx | 2 +- apps/docs/editor/blocks/inputs/website.mdx | 2 +- .../{webhook.mdx => http-request.mdx} | 10 +- apps/docs/editor/blocks/logic/wait.mdx | 4 +- apps/docs/editor/blocks/logic/webhook.mdx | 40 ++ .../{webhook => http-request}/preview.png | Bin .../save-in-variable.png | Bin .../{webhook => http-request}/simple-post.png | Bin .../variable-test-value.png | Bin .../variable-url.png | Bin apps/docs/mint.json | 13 +- apps/docs/openapi/builder.json | 113 ++++- apps/docs/openapi/viewer.json | 168 ++++++- apps/viewer/package.json | 1 + .../features/chat/api/legacy/sendMessageV1.ts | 4 +- .../features/chat/api/legacy/sendMessageV2.ts | 4 +- .../features/whatsapp/api/receiveMessage.ts | 41 +- .../blocks/[blockId]/executeWebhook.ts | 15 +- .../api/typebots/[typebotId]/webhookBlocks.ts | 6 +- .../api/typebots/[typebotId]/webhookSteps.ts | 4 +- .../results/[resultId]/executeWebhook.ts | 132 +++++ .../[blockId]/web/executeTestWebhook.ts | 82 ++++ .../whatsapp/[phone]/executeTestWebhook.ts | 79 +++ .../{webhook.spec.ts => httpRequest.spec.ts} | 0 bun.lockb | Bin 996800 -> 1021832 bytes package.json | 2 +- packages/blocks/core/src/helpers.ts | 6 +- .../src/migrations/migrateWebhookBlock.ts | 12 +- packages/blocks/integrations/src/constants.ts | 2 +- .../src/{webhook => httpRequest}/constants.ts | 4 +- .../src/{webhook => httpRequest}/schema.ts | 2 +- .../blocks/integrations/src/makeCom/schema.ts | 2 +- .../integrations/src/pabblyConnect/schema.ts | 2 +- packages/blocks/integrations/src/schema.ts | 2 +- .../blocks/integrations/src/zapier/schema.ts | 2 +- packages/blocks/logic/src/constants.ts | 1 + packages/blocks/logic/src/schema.ts | 2 + packages/blocks/logic/src/webhook/schema.ts | 29 ++ .../src/apiHandlers/continueChat.ts | 2 +- .../bot-engine/src/apiHandlers/startChat.ts | 2 +- .../src/apiHandlers/startChatPreview.ts | 2 +- .../executeHttpRequestBlock.ts} | 35 +- .../parseSampleResult.ts | 0 .../saveDataInResponseVariableMapping.ts} | 55 ++- .../logic/webhook/executeWebhookBlock.ts | 14 + packages/bot-engine/src/continueBotFlow.ts | 31 +- packages/bot-engine/src/executeIntegration.ts | 8 +- packages/bot-engine/src/executeLogic.ts | 3 + .../logs/filterPotentiallySensitiveLogs.ts | 8 +- .../bot-engine/src/saveStateToDatabase.ts | 21 +- packages/bot-engine/src/schemas/api.ts | 2 +- .../src/schemas/clientSideAction.ts | 20 +- .../webhook/utils/executeWebhookBlock.ts | 2 +- .../src/utils/executeIntegration.ts | 2 +- packages/embeds/js/package.json | 5 +- .../ConversationContainer.tsx | 1 + .../executeHttpRequest.ts} | 8 +- .../blocks/logic/script/executeScript.ts | 1 - .../blocks/logic/webhook/listenForWebhook.ts | 45 ++ .../embeds/js/src/queries/startChatQuery.ts | 157 +++--- packages/embeds/js/src/types.ts | 1 + .../js/src/utils/executeClientSideActions.ts | 15 +- packages/embeds/nextjs/package.json | 2 +- packages/embeds/react/package.json | 2 +- packages/env/src/index.ts | 11 + packages/lib/src/api/utils.ts | 6 + packages/partykit/package.json | 11 + packages/partykit/partykit.json | 6 + packages/partykit/src/webhookServer.ts | 36 ++ packages/partykit/tsconfig.json | 4 + packages/tsconfig/base.json | 6 - packages/tsconfig/nextjs.json | 8 +- .../src}/helpers/isReadTypebotForbidden.ts | 0 .../migrations/migrateTypebotFromV3ToV4.ts | 4 +- packages/variables/src/codeRunners.ts | 8 +- packages/whatsapp/src/receiveMessage.ts | 35 -- .../whatsapp/src/receiveMessagePreview.ts | 31 -- packages/whatsapp/src/resumeWhatsAppFlow.ts | 251 ++++++---- packages/whatsapp/src/schemas.ts | 8 + .../whatsapp/src/sendChatReplyToWhatsApp.ts | 129 +++-- yarn.lock | 457 +++++++++++++++++- 130 files changed, 2454 insertions(+), 644 deletions(-) create mode 100644 .github/workflows/deploy-partykit.yml rename apps/builder/src/features/blocks/integrations/{webhook => httpRequest}/api/getResultExample.ts (91%) rename apps/builder/src/features/blocks/integrations/{webhook/api/listWebhookBlocks.ts => httpRequest/api/listHttpRequestBlocks.ts} (78%) create mode 100644 apps/builder/src/features/blocks/integrations/httpRequest/api/router.ts rename apps/builder/src/features/blocks/integrations/{webhook/api/subscribeWebhook.ts => httpRequest/api/subscribeHttpRequest.ts} (73%) rename apps/builder/src/features/blocks/integrations/{webhook/api/unsubscribeWebhook.ts => httpRequest/api/unsubscribeHttpRequest.ts} (73%) rename apps/builder/src/features/blocks/integrations/{webhook => httpRequest}/components/HttpRequestAdvancedConfigForm.tsx (85%) rename apps/builder/src/features/blocks/integrations/{webhook => httpRequest}/components/HttpRequestIcon.tsx (100%) rename apps/builder/src/features/blocks/integrations/{webhook/components/HttpRequestContent.tsx => httpRequest/components/HttpRequestNodeContent.tsx} (89%) rename apps/builder/src/features/blocks/integrations/{webhook => httpRequest}/components/HttpRequestSettings.tsx (87%) rename apps/builder/src/features/blocks/integrations/{webhook => httpRequest}/components/KeyValueInputs.tsx (94%) rename apps/builder/src/features/blocks/integrations/{webhook => httpRequest}/components/ResponseMappingInputs.tsx (97%) rename apps/builder/src/features/blocks/integrations/{webhook => httpRequest}/components/VariableForTestInputs.tsx (97%) rename apps/builder/src/features/blocks/integrations/{webhook => httpRequest}/helpers/convertVariablesForTestToVariables.ts (97%) rename apps/builder/src/features/blocks/integrations/{webhook => httpRequest}/helpers/getDeepKeys.ts (100%) rename apps/builder/src/features/blocks/integrations/{webhook/webhook.spec.ts => httpRequest/httpRequest.spec.ts} (97%) rename apps/builder/src/features/blocks/integrations/{webhook/queries/executeWebhookQuery.ts => httpRequest/queries/executeHttpRequestQuery.ts} (73%) delete mode 100644 apps/builder/src/features/blocks/integrations/webhook/api/router.ts create mode 100644 apps/builder/src/features/blocks/logic/webhook/components/WebhookIcon.tsx create mode 100644 apps/builder/src/features/blocks/logic/webhook/components/WebhookNodeContent.tsx create mode 100644 apps/builder/src/features/blocks/logic/webhook/components/WebhookSettings.tsx create mode 100644 apps/builder/src/features/blocks/logic/webhook/webhook.spec.ts rename apps/builder/src/pages/api/typebots/[typebotId]/blocks/[blockId]/{testWebhook.ts => testHttpRequest.ts} (91%) rename apps/builder/src/test/assets/typebots/integrations/{webhook.json => httpRequest.json} (100%) create mode 100644 apps/builder/src/test/assets/typebots/logic/webhook.json rename apps/docs/editor/blocks/integrations/{webhook.mdx => http-request.mdx} (91%) create mode 100644 apps/docs/editor/blocks/logic/webhook.mdx rename apps/docs/images/blocks/integrations/{webhook => http-request}/preview.png (100%) rename apps/docs/images/blocks/integrations/{webhook => http-request}/save-in-variable.png (100%) rename apps/docs/images/blocks/integrations/{webhook => http-request}/simple-post.png (100%) rename apps/docs/images/blocks/integrations/{webhook => http-request}/variable-test-value.png (100%) rename apps/docs/images/blocks/integrations/{webhook => http-request}/variable-url.png (100%) create mode 100644 apps/viewer/src/pages/api/v1/typebots/[typebotId]/blocks/[blockId]/results/[resultId]/executeWebhook.ts create mode 100644 apps/viewer/src/pages/api/v1/typebots/[typebotId]/blocks/[blockId]/web/executeTestWebhook.ts create mode 100644 apps/viewer/src/pages/api/v1/typebots/[typebotId]/blocks/[blockId]/whatsapp/[phone]/executeTestWebhook.ts rename apps/viewer/src/test/{webhook.spec.ts => httpRequest.spec.ts} (100%) rename packages/blocks/integrations/src/{webhook => httpRequest}/constants.ts (83%) rename packages/blocks/integrations/src/{webhook => httpRequest}/schema.ts (98%) create mode 100644 packages/blocks/logic/src/webhook/schema.ts rename packages/bot-engine/src/blocks/integrations/{webhook/executeWebhookBlock.ts => httpRequest/executeHttpRequestBlock.ts} (92%) rename packages/bot-engine/src/blocks/integrations/{webhook => httpRequest}/parseSampleResult.ts (100%) rename packages/bot-engine/src/blocks/integrations/{webhook/resumeWebhookExecution.ts => httpRequest/saveDataInResponseVariableMapping.ts} (61%) create mode 100644 packages/bot-engine/src/blocks/logic/webhook/executeWebhookBlock.ts rename packages/embeds/js/src/features/blocks/integrations/{webhook/executeWebhook.ts => httpRequest/executeHttpRequest.ts} (75%) create mode 100644 packages/embeds/js/src/features/blocks/logic/webhook/listenForWebhook.ts create mode 100644 packages/partykit/package.json create mode 100644 packages/partykit/partykit.json create mode 100644 packages/partykit/src/webhookServer.ts create mode 100644 packages/partykit/tsconfig.json rename {apps/builder/src/features/typebot => packages/typebot/src}/helpers/isReadTypebotForbidden.ts (100%) delete mode 100644 packages/whatsapp/src/receiveMessage.ts delete mode 100644 packages/whatsapp/src/receiveMessagePreview.ts diff --git a/.github/workflows/deploy-partykit.yml b/.github/workflows/deploy-partykit.yml new file mode 100644 index 0000000000..c1e094844d --- /dev/null +++ b/.github/workflows/deploy-partykit.yml @@ -0,0 +1,44 @@ +name: Deploy Partykit server + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + # https://github.com/vercel/turborepo/issues/6348 + - name: Run Turbo ignore + id: check_changes + run: | + echo "Running script..." + + # Enable error handling + set +e + + bunx turbo-ignore @typebot.io/partykit -task=build exit_code=$? + + # Disable error handling + set -e + + echo "Exit code: $exit_code" + + if I $exit_code -eq 0 ]; then + echo "hasClientApiHostChanged=0" >> $GITHUB_OUTPUT + # echo "::save-state name=project_changed: :0" + # # echo "::set-output name=project_changed:: 0" + else + echo "hasClientApiHostChanged=1" ยป $GITHUB_OUTPUT + # echo "::set-output name=project_changed: : 1" + fi + - run: bun install + - run: bunx partykit deploy --domain ${PARTYKIT_DOMAIN} + working-directory: ./packages/partykit + env: + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + PARTYKIT_DOMAIN: ${{ secrets.PARTYKIT_DOMAIN }} diff --git a/.gitignore b/.gitignore index f2fc961a71..97b388f012 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,5 @@ snapshots .tsup .env.docker + +.partykit diff --git a/apps/builder/package.json b/apps/builder/package.json index 1739867abe..c94cb8cc1b 100644 --- a/apps/builder/package.json +++ b/apps/builder/package.json @@ -9,6 +9,7 @@ "test:ui": "dotenv -e ./.env -e ../../.env -- playwright test --ui" }, "dependencies": { + "partysocket": "1.0.2", "@braintree/sanitize-url": "7.0.1", "@chakra-ui/anatomy": "2.2.2", "@chakra-ui/react": "2.8.2", diff --git a/apps/builder/src/features/blocks/integrations/webhook/api/getResultExample.ts b/apps/builder/src/features/blocks/integrations/httpRequest/api/getResultExample.ts similarity index 91% rename from apps/builder/src/features/blocks/integrations/webhook/api/getResultExample.ts rename to apps/builder/src/features/blocks/integrations/httpRequest/api/getResultExample.ts index cc6fe1dd05..ddbd3f6b3d 100644 --- a/apps/builder/src/features/blocks/integrations/webhook/api/getResultExample.ts +++ b/apps/builder/src/features/blocks/integrations/httpRequest/api/getResultExample.ts @@ -2,7 +2,7 @@ import { fetchLinkedTypebots } from "@/features/blocks/logic/typebotLink/helpers import { canReadTypebots } from "@/helpers/databaseRules"; import { authenticatedProcedure } from "@/helpers/server/trpc"; import { TRPCError } from "@trpc/server"; -import { parseSampleResult } from "@typebot.io/bot-engine/blocks/integrations/webhook/parseSampleResult"; +import { parseSampleResult } from "@typebot.io/bot-engine/blocks/integrations/httpRequest/parseSampleResult"; import { getBlockById } from "@typebot.io/groups/helpers"; import prisma from "@typebot.io/prisma"; import type { Typebot } from "@typebot.io/typebot/schemas/typebot"; @@ -16,8 +16,8 @@ export const getResultExample = authenticatedProcedure protect: true, summary: "Get result example", description: - 'Returns "fake" result for webhook block to help you anticipate how the webhook will behave.', - tags: ["Webhook"], + 'Returns "fake" result for http request block to help you anticipate how the webhook will behave.', + tags: ["HTTP request"], }, }) .input( diff --git a/apps/builder/src/features/blocks/integrations/webhook/api/listWebhookBlocks.ts b/apps/builder/src/features/blocks/integrations/httpRequest/api/listHttpRequestBlocks.ts similarity index 78% rename from apps/builder/src/features/blocks/integrations/webhook/api/listWebhookBlocks.ts rename to apps/builder/src/features/blocks/integrations/httpRequest/api/listHttpRequestBlocks.ts index 473cd6f3a1..f360e0b397 100644 --- a/apps/builder/src/features/blocks/integrations/webhook/api/listWebhookBlocks.ts +++ b/apps/builder/src/features/blocks/integrations/httpRequest/api/listHttpRequestBlocks.ts @@ -1,7 +1,7 @@ import { canReadTypebots } from "@/helpers/databaseRules"; import { authenticatedProcedure } from "@/helpers/server/trpc"; import { TRPCError } from "@trpc/server"; -import { isWebhookBlock } from "@typebot.io/blocks-core/helpers"; +import { isHttpRequestBlock } from "@typebot.io/blocks-core/helpers"; import type { Block } from "@typebot.io/blocks-core/schemas/schema"; import { IntegrationBlockType } from "@typebot.io/blocks-integrations/constants"; import { parseGroups } from "@typebot.io/groups/schemas"; @@ -9,16 +9,16 @@ import { byId } from "@typebot.io/lib/utils"; import prisma from "@typebot.io/prisma"; import { z } from "@typebot.io/zod"; -export const listWebhookBlocks = authenticatedProcedure +export const listHttpRequestBlocks = authenticatedProcedure .meta({ openapi: { method: "GET", path: "/v1/typebots/{typebotId}/webhookBlocks", protect: true, - summary: "List webhook blocks", + summary: "List HTTP request blocks", description: - "Returns a list of all the webhook blocks that you can subscribe to.", - tags: ["Webhook"], + "Returns a list of all the HTTP request blocks that you can subscribe to.", + tags: ["HTTP request"], }, }) .input( @@ -32,7 +32,7 @@ export const listWebhookBlocks = authenticatedProcedure z.object({ id: z.string(), type: z.enum([ - IntegrationBlockType.WEBHOOK, + IntegrationBlockType.HTTP_REQUEST, IntegrationBlockType.ZAPIER, IntegrationBlockType.MAKE_COM, IntegrationBlockType.PABBLY_CONNECT, @@ -59,21 +59,21 @@ export const listWebhookBlocks = authenticatedProcedure typebotVersion: typebot.version, }); - const webhookBlocks = groups.reduce< + const httpRequestBlocks = groups.reduce< { id: string; label: string; url: string | undefined; type: - | IntegrationBlockType.WEBHOOK + | IntegrationBlockType.HTTP_REQUEST | IntegrationBlockType.ZAPIER | IntegrationBlockType.MAKE_COM | IntegrationBlockType.PABBLY_CONNECT; }[] - >((webhookBlocks, group) => { - const blocks = (group.blocks as Block[]).filter(isWebhookBlock); + >((httpRequestBlock, group) => { + const blocks = (group.blocks as Block[]).filter(isHttpRequestBlock); return [ - ...webhookBlocks, + ...httpRequestBlock, ...blocks.map((block) => ({ id: block.id, type: block.type, @@ -87,5 +87,5 @@ export const listWebhookBlocks = authenticatedProcedure ]; }, []); - return { webhookBlocks }; + return { webhookBlocks: httpRequestBlocks }; }); diff --git a/apps/builder/src/features/blocks/integrations/httpRequest/api/router.ts b/apps/builder/src/features/blocks/integrations/httpRequest/api/router.ts new file mode 100644 index 0000000000..62dbfedfcc --- /dev/null +++ b/apps/builder/src/features/blocks/integrations/httpRequest/api/router.ts @@ -0,0 +1,12 @@ +import { router } from "@/helpers/server/trpc"; +import { getResultExample } from "./getResultExample"; +import { listHttpRequestBlocks } from "./listHttpRequestBlocks"; +import { subscribeHttpRequest } from "./subscribeHttpRequest"; +import { unsubscribeHttpRequest } from "./unsubscribeHttpRequest"; + +export const httpRequestRouter = router({ + listHttpRequestBlocks, + getResultExample, + subscribeHttpRequest, + unsubscribeHttpRequest, +}); diff --git a/apps/builder/src/features/blocks/integrations/webhook/api/subscribeWebhook.ts b/apps/builder/src/features/blocks/integrations/httpRequest/api/subscribeHttpRequest.ts similarity index 73% rename from apps/builder/src/features/blocks/integrations/webhook/api/subscribeWebhook.ts rename to apps/builder/src/features/blocks/integrations/httpRequest/api/subscribeHttpRequest.ts index c746bdfd29..c7c6c6d7a2 100644 --- a/apps/builder/src/features/blocks/integrations/webhook/api/subscribeWebhook.ts +++ b/apps/builder/src/features/blocks/integrations/httpRequest/api/subscribeHttpRequest.ts @@ -1,22 +1,22 @@ import { canWriteTypebots } from "@/helpers/databaseRules"; import { authenticatedProcedure } from "@/helpers/server/trpc"; import { TRPCError } from "@trpc/server"; -import { isWebhookBlock } from "@typebot.io/blocks-core/helpers"; +import { isHttpRequestBlock } from "@typebot.io/blocks-core/helpers"; import type { Block } from "@typebot.io/blocks-core/schemas/schema"; -import type { HttpRequestBlock } from "@typebot.io/blocks-integrations/webhook/schema"; +import type { HttpRequestBlock } from "@typebot.io/blocks-integrations/httpRequest/schema"; import { parseGroups } from "@typebot.io/groups/schemas"; import { byId } from "@typebot.io/lib/utils"; import prisma from "@typebot.io/prisma"; import { z } from "@typebot.io/zod"; -export const subscribeWebhook = authenticatedProcedure +export const subscribeHttpRequest = authenticatedProcedure .meta({ openapi: { method: "POST", path: "/v1/typebots/{typebotId}/webhookBlocks/{blockId}/subscribe", protect: true, - summary: "Subscribe to webhook block", - tags: ["Webhook"], + summary: "Subscribe to HTTP request block", + tags: ["HTTP request"], }, }) .input( @@ -48,30 +48,30 @@ export const subscribeWebhook = authenticatedProcedure typebotVersion: typebot.version, }); - const webhookBlock = groups + const httpRequestBlock = groups .flatMap((g) => g.blocks) .find(byId(blockId)) as HttpRequestBlock | null; - if (!webhookBlock || !isWebhookBlock(webhookBlock)) + if (!httpRequestBlock || !isHttpRequestBlock(httpRequestBlock)) throw new TRPCError({ code: "NOT_FOUND", - message: "Webhook block not found", + message: "HttpRequest block not found", }); - if (webhookBlock.options?.webhook || typebot.version === "6") { + if (httpRequestBlock.options?.webhook || typebot.version === "6") { const updatedGroups = groups.map((group) => - group.blocks.some((b) => b.id === webhookBlock.id) + group.blocks.some((b) => b.id === httpRequestBlock.id) ? { ...group, blocks: group.blocks.map((block) => - block.id !== webhookBlock.id + block.id !== httpRequestBlock.id ? block : { ...block, options: { - ...webhookBlock.options, + ...httpRequestBlock.options, webhook: { - ...webhookBlock.options?.webhook, + ...httpRequestBlock.options?.webhook, url, }, }, @@ -87,15 +87,15 @@ export const subscribeWebhook = authenticatedProcedure }, }); } else { - if ("webhookId" in webhookBlock) + if ("webhookId" in httpRequestBlock) await prisma.webhook.update({ - where: { id: webhookBlock.webhookId }, + where: { id: httpRequestBlock.webhookId }, data: { url }, }); else throw new TRPCError({ code: "NOT_FOUND", - message: "Webhook block not found", + message: "HttpRequest block not found", }); } diff --git a/apps/builder/src/features/blocks/integrations/webhook/api/unsubscribeWebhook.ts b/apps/builder/src/features/blocks/integrations/httpRequest/api/unsubscribeHttpRequest.ts similarity index 73% rename from apps/builder/src/features/blocks/integrations/webhook/api/unsubscribeWebhook.ts rename to apps/builder/src/features/blocks/integrations/httpRequest/api/unsubscribeHttpRequest.ts index bb70debc83..dfc54a8057 100644 --- a/apps/builder/src/features/blocks/integrations/webhook/api/unsubscribeWebhook.ts +++ b/apps/builder/src/features/blocks/integrations/httpRequest/api/unsubscribeHttpRequest.ts @@ -1,22 +1,22 @@ import { canWriteTypebots } from "@/helpers/databaseRules"; import { authenticatedProcedure } from "@/helpers/server/trpc"; import { TRPCError } from "@trpc/server"; -import { isWebhookBlock } from "@typebot.io/blocks-core/helpers"; +import { isHttpRequestBlock } from "@typebot.io/blocks-core/helpers"; import type { Block } from "@typebot.io/blocks-core/schemas/schema"; -import type { HttpRequestBlock } from "@typebot.io/blocks-integrations/webhook/schema"; +import type { HttpRequestBlock } from "@typebot.io/blocks-integrations/httpRequest/schema"; import { parseGroups } from "@typebot.io/groups/schemas"; import { byId } from "@typebot.io/lib/utils"; import prisma from "@typebot.io/prisma"; import { z } from "@typebot.io/zod"; -export const unsubscribeWebhook = authenticatedProcedure +export const unsubscribeHttpRequest = authenticatedProcedure .meta({ openapi: { method: "POST", path: "/v1/typebots/{typebotId}/webhookBlocks/{blockId}/unsubscribe", protect: true, - summary: "Unsubscribe from webhook block", - tags: ["Webhook"], + summary: "Unsubscribe from HTTP request block", + tags: ["HTTP request"], }, }) .input( @@ -47,30 +47,30 @@ export const unsubscribeWebhook = authenticatedProcedure typebotVersion: typebot.version, }); - const webhookBlock = groups + const httpRequestBlock = groups .flatMap((g) => g.blocks) .find(byId(blockId)) as HttpRequestBlock | null; - if (!webhookBlock || !isWebhookBlock(webhookBlock)) + if (!httpRequestBlock || !isHttpRequestBlock(httpRequestBlock)) throw new TRPCError({ code: "NOT_FOUND", - message: "Webhook block not found", + message: "HTTP request block not found", }); - if (webhookBlock.options?.webhook || typebot.version === "6") { + if (httpRequestBlock.options?.webhook || typebot.version === "6") { const updatedGroups = groups.map((group) => - group.blocks.some((b) => b.id === webhookBlock.id) + group.blocks.some((b) => b.id === httpRequestBlock.id) ? { ...group, blocks: group.blocks.map((block) => - block.id !== webhookBlock.id + block.id !== httpRequestBlock.id ? block : { ...block, options: { - ...webhookBlock.options, + ...httpRequestBlock.options, webhook: { - ...webhookBlock.options?.webhook, + ...httpRequestBlock.options?.webhook, url: undefined, }, }, @@ -86,15 +86,15 @@ export const unsubscribeWebhook = authenticatedProcedure }, }); } else { - if ("webhookId" in webhookBlock) + if ("webhookId" in httpRequestBlock) await prisma.webhook.update({ - where: { id: webhookBlock.webhookId }, + where: { id: httpRequestBlock.webhookId }, data: { url: null }, }); else throw new TRPCError({ code: "NOT_FOUND", - message: "Webhook block not found", + message: "HTTP request block not found", }); } diff --git a/apps/builder/src/features/blocks/integrations/webhook/components/HttpRequestAdvancedConfigForm.tsx b/apps/builder/src/features/blocks/integrations/httpRequest/components/HttpRequestAdvancedConfigForm.tsx similarity index 85% rename from apps/builder/src/features/blocks/integrations/webhook/components/HttpRequestAdvancedConfigForm.tsx rename to apps/builder/src/features/blocks/integrations/httpRequest/components/HttpRequestAdvancedConfigForm.tsx index 18cc9c7929..943835d74f 100644 --- a/apps/builder/src/features/blocks/integrations/webhook/components/HttpRequestAdvancedConfigForm.tsx +++ b/apps/builder/src/features/blocks/integrations/httpRequest/components/HttpRequestAdvancedConfigForm.tsx @@ -19,39 +19,39 @@ import { } from "@chakra-ui/react"; import { HttpMethod, + defaultHttpRequestAttributes, + defaultHttpRequestBlockOptions, defaultTimeout, - defaultWebhookAttributes, - defaultWebhookBlockOptions, maxTimeout, -} from "@typebot.io/blocks-integrations/webhook/constants"; +} from "@typebot.io/blocks-integrations/httpRequest/constants"; import type { HttpRequest, HttpRequestBlock, KeyValue, ResponseVariableMapping, VariableForTest, -} from "@typebot.io/blocks-integrations/webhook/schema"; +} from "@typebot.io/blocks-integrations/httpRequest/schema"; import { useMemo, useState } from "react"; import { convertVariablesForTestToVariables } from "../helpers/convertVariablesForTestToVariables"; import { getDeepKeys } from "../helpers/getDeepKeys"; -import { executeWebhook } from "../queries/executeWebhookQuery"; +import { executeHttpRequest } from "../queries/executeHttpRequestQuery"; import { HeadersInputs, QueryParamsInputs } from "./KeyValueInputs"; import { DataVariableInputs } from "./ResponseMappingInputs"; import { VariableForTestInputs } from "./VariableForTestInputs"; type Props = { blockId: string; - webhook: HttpRequest | undefined; + httpRequest: HttpRequest | undefined; options: HttpRequestBlock["options"]; - onWebhookChange: (webhook: HttpRequest) => void; + onHttpRequestChange: (httpRequest: HttpRequest) => void; onOptionsChange: (options: HttpRequestBlock["options"]) => void; }; export const HttpRequestAdvancedConfigForm = ({ blockId, - webhook, + httpRequest, options, - onWebhookChange, + onHttpRequestChange, onOptionsChange, }: Props) => { const { typebot, save } = useTypebot(); @@ -61,15 +61,16 @@ export const HttpRequestAdvancedConfigForm = ({ const { showToast } = useToast(); const updateMethod = (method: HttpMethod) => - onWebhookChange({ ...webhook, method }); + onHttpRequestChange({ ...httpRequest, method }); const updateQueryParams = (queryParams: KeyValue[]) => - onWebhookChange({ ...webhook, queryParams }); + onHttpRequestChange({ ...httpRequest, queryParams }); const updateHeaders = (headers: KeyValue[]) => - onWebhookChange({ ...webhook, headers }); + onHttpRequestChange({ ...httpRequest, headers }); - const updateBody = (body: string) => onWebhookChange({ ...webhook, body }); + const updateBody = (body: string) => + onHttpRequestChange({ ...httpRequest, body }); const updateVariablesForTest = (variablesForTest: VariableForTest[]) => onOptionsChange({ ...options, variablesForTest }); @@ -92,7 +93,7 @@ export const HttpRequestAdvancedConfigForm = ({ setIsTestResponseLoading(true); if (!options?.webhook) await save(); else await save(); - const { data, error } = await executeWebhook( + const { data, error } = await executeHttpRequest( typebot.id, convertVariablesForTestToVariables( options?.variablesForTest ?? [], @@ -119,7 +120,7 @@ export const HttpRequestAdvancedConfigForm = ({ ); const isCustomBody = - options?.isCustomBody ?? defaultWebhookBlockOptions.isCustomBody; + options?.isCustomBody ?? defaultHttpRequestBlockOptions.isCustomBody; return ( <> @@ -127,16 +128,16 @@ export const HttpRequestAdvancedConfigForm = ({ label="Advanced configuration" initialValue={ options?.isAdvancedConfig ?? - defaultWebhookBlockOptions.isAdvancedConfig + defaultHttpRequestBlockOptions.isAdvancedConfig } onCheckChange={updateAdvancedConfig} > @@ -144,7 +145,8 @@ export const HttpRequestAdvancedConfigForm = ({ Method: - initialItems={webhook?.queryParams} + initialItems={httpRequest?.queryParams} onItemsChange={updateQueryParams} addLabel="Add a param" > @@ -173,7 +175,7 @@ export const HttpRequestAdvancedConfigForm = ({ - initialItems={webhook?.headers} + initialItems={httpRequest?.headers} onItemsChange={updateHeaders} addLabel="Add a value" > @@ -194,7 +196,7 @@ export const HttpRequestAdvancedConfigForm = ({ /> {isCustomBody && ( - {webhook?.url && ( + {httpRequest?.url && ( + {websocketStatus === "opened" && ( + + + Waiting for an{" "} + + authenticated + {" "} + POST request to the Test URL... + + + )} + {receivedData && ( + + )} + {(receivedData || + (options?.responseVariableMapping && + options.responseVariableMapping.length > 0)) && ( + + + + Save in variables + + + + + initialItems={options?.responseVariableMapping} + onItemsChange={updateResponseVariableMapping} + addLabel="Add an entry" + > + {(props) => } + + + + + )} + + + + {typebot && ( + + + + + + + + + You can easily get the Result ID{" "} + + with a Set variable block + + . + + + )} + + + + ); +}; diff --git a/apps/builder/src/features/blocks/logic/webhook/webhook.spec.ts b/apps/builder/src/features/blocks/logic/webhook/webhook.spec.ts new file mode 100644 index 0000000000..d1a7a08600 --- /dev/null +++ b/apps/builder/src/features/blocks/logic/webhook/webhook.spec.ts @@ -0,0 +1,61 @@ +import { getTestAsset } from "@/test/utils/playwright"; +import { createId } from "@paralleldrive/cuid2"; +import test, { expect } from "@playwright/test"; +import { env } from "@typebot.io/env"; +import { importTypebotInDatabase } from "@typebot.io/playwright/databaseActions"; +import { apiToken } from "@typebot.io/playwright/databaseSetup"; + +const typebotId = createId(); + +test.describe("Wait block", () => { + test("wait should trigger", async ({ page, request }) => { + await importTypebotInDatabase(getTestAsset("typebots/logic/webhook.json"), { + id: typebotId, + }); + + await page.goto(`/typebots/${typebotId}/edit`); + await page.getByText("Listen for webhook").click(); + await page.getByRole("button", { name: "Listen for test event" }).click(); + await page.waitForTimeout(1000); + await request.post( + `${env.NEXT_PUBLIC_VIEWER_URL[0]}/api/v1/typebots/${typebotId}/blocks/webhook-block-id/web/executeTestWebhook`, + { + data: { + name: "John", + }, + headers: { + Authorization: `Bearer ${apiToken}`, + }, + }, + ); + await expect(page.getByText('{ "data": { "name": "John')).toBeVisible(); + await page.getByRole("button", { name: "Save in variables" }).click(); + await page.getByRole("button", { name: "Add an entry" }).click(); + await page.getByPlaceholder("Select the data").click(); + await page.getByRole("menuitem", { name: "data.name" }).click(); + await page.getByTestId("variables-input").click(); + await page + .getByRole("menuitem", { name: "Name Rename variable Remove" }) + .click(); + await page.getByRole("button", { name: "Test", exact: true }).click(); + await expect( + page.locator("span").filter({ hasText: "Ok let's look into that..." }), + ).toBeVisible(); + await page.waitForTimeout(1000); + await request.post( + `${env.NEXT_PUBLIC_VIEWER_URL[0]}/api/v1/typebots/${typebotId}/blocks/webhook-block-id/web/executeTestWebhook`, + { + data: { + name: "John", + }, + headers: { + Authorization: `Bearer ${apiToken}`, + }, + }, + ); + await expect( + page.getByText(", nice to see you again!", { exact: true }), + ).toBeVisible(); + await expect(page.getByText("John")).toBeVisible(); + }); +}); diff --git a/apps/builder/src/features/collaboration/api/getCollaborators.ts b/apps/builder/src/features/collaboration/api/getCollaborators.ts index cae07e8028..429f806e21 100644 --- a/apps/builder/src/features/collaboration/api/getCollaborators.ts +++ b/apps/builder/src/features/collaboration/api/getCollaborators.ts @@ -1,8 +1,8 @@ -import { isReadTypebotForbidden } from "@/features/typebot/helpers/isReadTypebotForbidden"; import { authenticatedProcedure } from "@/helpers/server/trpc"; import { TRPCError } from "@trpc/server"; import prisma from "@typebot.io/prisma"; import { collaboratorSchema } from "@typebot.io/schemas/features/collaborators"; +import { isReadTypebotForbidden } from "@typebot.io/typebot/helpers/isReadTypebotForbidden"; import { z } from "@typebot.io/zod"; export const getCollaborators = authenticatedProcedure diff --git a/apps/builder/src/features/editor/components/BlockIcon.tsx b/apps/builder/src/features/editor/components/BlockIcon.tsx index 15b104b53d..dd0e96ceca 100644 --- a/apps/builder/src/features/editor/components/BlockIcon.tsx +++ b/apps/builder/src/features/editor/components/BlockIcon.tsx @@ -32,6 +32,7 @@ import { ScriptIcon } from "@/features/blocks/logic/script/components/ScriptIcon import { SetVariableIcon } from "@/features/blocks/logic/setVariable/components/SetVariableIcon"; import { TypebotLinkIcon } from "@/features/blocks/logic/typebotLink/components/TypebotLinkIcon"; import { WaitIcon } from "@/features/blocks/logic/wait/components/WaitIcon"; +import { WebhookIcon } from "@/features/blocks/logic/webhook/components/WebhookIcon"; import { ForgedBlockIcon } from "@/features/forge/ForgedBlockIcon"; import { type IconProps, useColorModeValue } from "@chakra-ui/react"; import { BubbleBlockType } from "@typebot.io/blocks-bubbles/constants"; @@ -98,11 +99,13 @@ export const BlockIcon = ({ type, ...props }: BlockIconProps): JSX.Element => { return ; case LogicBlockType.AB_TEST: return ; + case LogicBlockType.WEBHOOK: + return ; case IntegrationBlockType.GOOGLE_SHEETS: return ; case IntegrationBlockType.GOOGLE_ANALYTICS: return ; - case IntegrationBlockType.WEBHOOK: + case IntegrationBlockType.HTTP_REQUEST: return ; case IntegrationBlockType.ZAPIER: return ; diff --git a/apps/builder/src/features/editor/components/BlockLabel.tsx b/apps/builder/src/features/editor/components/BlockLabel.tsx index 326684e352..651c108d1b 100644 --- a/apps/builder/src/features/editor/components/BlockLabel.tsx +++ b/apps/builder/src/features/editor/components/BlockLabel.tsx @@ -42,7 +42,7 @@ export const BlockLabel = ({ type, ...props }: Props): JSX.Element => { export const getBubbleBlockLabel = ( t: TFnType, -): Record => ({ +): { [key in BubbleBlockType]: string } => ({ [BubbleBlockType.TEXT]: t("editor.sidebarBlock.text.label"), [BubbleBlockType.IMAGE]: t("editor.sidebarBlock.image.label"), [BubbleBlockType.VIDEO]: t("editor.sidebarBlock.video.label"), @@ -52,7 +52,7 @@ export const getBubbleBlockLabel = ( export const getInputBlockLabel = ( t: TFnType, -): Record => ({ +): { [key in InputBlockType]: string } => ({ [InputBlockType.NUMBER]: t("editor.sidebarBlock.number.label"), [InputBlockType.EMAIL]: t("editor.sidebarBlock.email.label"), [InputBlockType.TEXT]: t("editor.sidebarBlock.text.label"), @@ -68,7 +68,7 @@ export const getInputBlockLabel = ( export const getLogicBlockLabel = ( t: TFnType, -): Record => ({ +): { [key in LogicBlockType]: string } => ({ [LogicBlockType.SET_VARIABLE]: t("editor.sidebarBlock.setVariable.label"), [LogicBlockType.CONDITION]: t("editor.sidebarBlock.condition.label"), [LogicBlockType.REDIRECT]: t("editor.sidebarBlock.redirect.label"), @@ -77,16 +77,17 @@ export const getLogicBlockLabel = ( [LogicBlockType.WAIT]: t("editor.sidebarBlock.wait.label"), [LogicBlockType.JUMP]: t("editor.sidebarBlock.jump.label"), [LogicBlockType.AB_TEST]: t("editor.sidebarBlock.abTest.label"), + [LogicBlockType.WEBHOOK]: "Webhook", }); export const getIntegrationBlockLabel = ( t: TFnType, -): Record => ({ +): { [key in IntegrationBlockType]: string } => ({ [IntegrationBlockType.GOOGLE_SHEETS]: t("editor.sidebarBlock.sheets.label"), [IntegrationBlockType.GOOGLE_ANALYTICS]: t( "editor.sidebarBlock.analytics.label", ), - [IntegrationBlockType.WEBHOOK]: "HTTP request", + [IntegrationBlockType.HTTP_REQUEST]: "HTTP request", [IntegrationBlockType.ZAPIER]: t("editor.sidebarBlock.zapier.label"), [IntegrationBlockType.MAKE_COM]: t("editor.sidebarBlock.makecom.label"), [IntegrationBlockType.PABBLY_CONNECT]: t("editor.sidebarBlock.pabbly.label"), diff --git a/apps/builder/src/features/editor/components/BlocksSideBar.tsx b/apps/builder/src/features/editor/components/BlocksSideBar.tsx index d9d0a53655..44451531f2 100644 --- a/apps/builder/src/features/editor/components/BlocksSideBar.tsx +++ b/apps/builder/src/features/editor/components/BlocksSideBar.tsx @@ -20,7 +20,9 @@ import type { BlockV6 } from "@typebot.io/blocks-core/schemas/schema"; import { InputBlockType } from "@typebot.io/blocks-inputs/constants"; import { IntegrationBlockType } from "@typebot.io/blocks-integrations/constants"; import { LogicBlockType } from "@typebot.io/blocks-logic/constants"; +import { env } from "@typebot.io/env"; import { forgedBlocks } from "@typebot.io/forge-repository/definitions"; +import { isDefined } from "@typebot.io/lib/utils"; import type React from "react"; import { useState } from "react"; import { useDebouncedCallback } from "use-debounce"; @@ -129,11 +131,17 @@ export const BlocksSideBar = () => { .includes(searchInput.toLowerCase()), ); - const filteredLogicBlockTypes = Object.values(LogicBlockType).filter((type) => - getLogicBlockLabel(t) - [type].toLowerCase() - .includes(searchInput.toLowerCase()), - ); + const filteredLogicBlockTypes = Object.values(LogicBlockType) + .filter((type) => + type === LogicBlockType.WEBHOOK + ? isDefined(env.NEXT_PUBLIC_PARTYKIT_HOST) + : true, + ) + .filter((type) => + getLogicBlockLabel(t) + [type].toLowerCase() + .includes(searchInput.toLowerCase()), + ); const filteredIntegrationBlockTypes = Object.values( IntegrationBlockType, diff --git a/apps/builder/src/features/editor/providers/typebotActions/blocks.ts b/apps/builder/src/features/editor/providers/typebotActions/blocks.ts index 6080a5c595..e1017f3bd4 100644 --- a/apps/builder/src/features/editor/providers/typebotActions/blocks.ts +++ b/apps/builder/src/features/editor/providers/typebotActions/blocks.ts @@ -6,7 +6,7 @@ import type { BlockIndices, BlockV6, } from "@typebot.io/blocks-core/schemas/schema"; -import type { HttpRequest } from "@typebot.io/blocks-integrations/webhook/schema"; +import type { HttpRequest } from "@typebot.io/blocks-integrations/httpRequest/schema"; import { byId } from "@typebot.io/lib/utils"; import type { Typebot, TypebotV6 } from "@typebot.io/typebot/schemas/typebot"; import { type Draft, produce } from "immer"; diff --git a/apps/builder/src/features/graph/components/nodes/block/BlockNodeContent.tsx b/apps/builder/src/features/graph/components/nodes/block/BlockNodeContent.tsx index 75443de59c..3e839acf01 100644 --- a/apps/builder/src/features/graph/components/nodes/block/BlockNodeContent.tsx +++ b/apps/builder/src/features/graph/components/nodes/block/BlockNodeContent.tsx @@ -17,12 +17,12 @@ import { UrlNodeContent } from "@/features/blocks/inputs/url/components/UrlNodeC import { ChatwootNodeBody } from "@/features/blocks/integrations/chatwoot/components/ChatwootNodeBody"; import { GoogleAnalyticsNodeBody } from "@/features/blocks/integrations/googleAnalytics/components/GoogleAnalyticsNodeBody"; import { GoogleSheetsNodeContent } from "@/features/blocks/integrations/googleSheets/components/GoogleSheetsNodeContent"; +import { HttpRequestNodeContent } from "@/features/blocks/integrations/httpRequest/components/HttpRequestNodeContent"; import { MakeComContent } from "@/features/blocks/integrations/makeCom/components/MakeComContent"; import { OpenAINodeBody } from "@/features/blocks/integrations/openai/components/OpenAINodeBody"; import { PabblyConnectContent } from "@/features/blocks/integrations/pabbly/components/PabblyConnectContent"; import { PixelNodeBody } from "@/features/blocks/integrations/pixel/components/PixelNodeBody"; import { SendEmailContent } from "@/features/blocks/integrations/sendEmail/components/SendEmailContent"; -import { WebhookContent } from "@/features/blocks/integrations/webhook/components/HttpRequestContent"; import { ZapierContent } from "@/features/blocks/integrations/zapier/components/ZapierContent"; import { AbTestNodeBody } from "@/features/blocks/logic/abTest/components/AbTestNodeBody"; import { JumpNodeBody } from "@/features/blocks/logic/jump/components/JumpNodeBody"; @@ -31,6 +31,7 @@ import { ScriptNodeContent } from "@/features/blocks/logic/script/components/Scr import { SetVariableContent } from "@/features/blocks/logic/setVariable/components/SetVariableContent"; import { TypebotLinkNode } from "@/features/blocks/logic/typebotLink/components/TypebotLinkNode"; import { WaitNodeContent } from "@/features/blocks/logic/wait/components/WaitNodeContent"; +import { WebhookNodeContent } from "@/features/blocks/logic/webhook/components/WebhookNodeContent"; import { ForgedBlockNodeContent } from "@/features/forge/components/ForgedBlockNodeContent"; import { BubbleBlockType } from "@typebot.io/blocks-bubbles/constants"; import type { @@ -123,14 +124,16 @@ export const BlockNodeContent = ({ return ; case LogicBlockType.CONDITION: return ; + case LogicBlockType.WEBHOOK: + return ; case IntegrationBlockType.GOOGLE_SHEETS: { return ; } case IntegrationBlockType.GOOGLE_ANALYTICS: { return ; } - case IntegrationBlockType.WEBHOOK: { - return ; + case IntegrationBlockType.HTTP_REQUEST: { + return ; } case IntegrationBlockType.ZAPIER: { return ; diff --git a/apps/builder/src/features/graph/components/nodes/block/SettingsPopoverContent.tsx b/apps/builder/src/features/graph/components/nodes/block/SettingsPopoverContent.tsx index a6e03a28ce..d86afc429c 100644 --- a/apps/builder/src/features/graph/components/nodes/block/SettingsPopoverContent.tsx +++ b/apps/builder/src/features/graph/components/nodes/block/SettingsPopoverContent.tsx @@ -12,12 +12,12 @@ import { UrlInputSettings } from "@/features/blocks/inputs/url/components/UrlInp import { ChatwootSettings } from "@/features/blocks/integrations/chatwoot/components/ChatwootSettings"; import { GoogleAnalyticsSettings } from "@/features/blocks/integrations/googleAnalytics/components/GoogleAnalyticsSettings"; import { GoogleSheetsSettings } from "@/features/blocks/integrations/googleSheets/components/GoogleSheetsSettings"; +import { HttpRequestSettings } from "@/features/blocks/integrations/httpRequest/components/HttpRequestSettings"; import { MakeComSettings } from "@/features/blocks/integrations/makeCom/components/MakeComSettings"; import { OpenAISettings } from "@/features/blocks/integrations/openai/components/OpenAISettings"; import { PabblyConnectSettings } from "@/features/blocks/integrations/pabbly/components/PabblyConnectSettings"; import { PixelSettings } from "@/features/blocks/integrations/pixel/components/PixelSettings"; import { SendEmailSettings } from "@/features/blocks/integrations/sendEmail/components/SendEmailSettings"; -import { HttpRequestSettings } from "@/features/blocks/integrations/webhook/components/HttpRequestSettings"; import { ZapierSettings } from "@/features/blocks/integrations/zapier/components/ZapierSettings"; import { AbTestSettings } from "@/features/blocks/logic/abTest/components/AbTestSettings"; import { JumpSettings } from "@/features/blocks/logic/jump/components/JumpSettings"; @@ -26,6 +26,7 @@ import { ScriptSettings } from "@/features/blocks/logic/script/components/Script import { SetVariableSettings } from "@/features/blocks/logic/setVariable/components/SetVariableSettings"; import { TypebotLinkForm } from "@/features/blocks/logic/typebotLink/components/TypebotLinkForm"; import { WaitSettings } from "@/features/blocks/logic/wait/components/WaitSettings"; +import { WebhookSettings } from "@/features/blocks/logic/webhook/components/WebhookSettings"; import { useForgedBlock } from "@/features/forge/hooks/useForgedBlock"; import { VideoOnboardingPopover } from "@/features/onboarding/components/VideoOnboardingPopover"; import { hasOnboardingVideo } from "@/features/onboarding/helpers/hasOnboardingVideo"; @@ -309,7 +310,7 @@ export const BlockSettings = ({ ); } - case IntegrationBlockType.WEBHOOK: { + case IntegrationBlockType.HTTP_REQUEST: { return ( ); @@ -343,6 +344,14 @@ export const BlockSettings = ({ } case LogicBlockType.CONDITION: return null; + case LogicBlockType.WEBHOOK: + return ( + + ); default: { return ( diff --git a/apps/builder/src/features/graph/helpers/getHelpDocUrl.ts b/apps/builder/src/features/graph/helpers/getHelpDocUrl.ts index d3b8af7049..6fe3d994b0 100644 --- a/apps/builder/src/features/graph/helpers/getHelpDocUrl.ts +++ b/apps/builder/src/features/graph/helpers/getHelpDocUrl.ts @@ -51,8 +51,8 @@ export const getHelpDocUrl = ( return "https://docs.typebot.io/editor/blocks/integrations/zapier"; case IntegrationBlockType.PABBLY_CONNECT: return "https://docs.typebot.io/editor/blocks/integrations/pabbly"; - case IntegrationBlockType.WEBHOOK: - return "https://docs.typebot.io/editor/blocks/integrations/webhook"; + case IntegrationBlockType.HTTP_REQUEST: + return "https://docs.typebot.io/editor/blocks/integrations/http-request"; case InputBlockType.PICTURE_CHOICE: return "https://docs.typebot.io/editor/blocks/inputs/picture-choice"; case IntegrationBlockType.OPEN_AI: @@ -67,6 +67,8 @@ export const getHelpDocUrl = ( return "https://docs.typebot.io/editor/blocks/integrations/pixel"; case LogicBlockType.CONDITION: return "https://docs.typebot.io/editor/blocks/logic/condition"; + case LogicBlockType.WEBHOOK: + return "https://docs.typebot.io/editor/blocks/logic/webhook"; default: return blockDef?.docsUrl; } diff --git a/apps/builder/src/features/results/api/getResult.ts b/apps/builder/src/features/results/api/getResult.ts index 2d087f5f2f..f3a9817436 100644 --- a/apps/builder/src/features/results/api/getResult.ts +++ b/apps/builder/src/features/results/api/getResult.ts @@ -1,8 +1,8 @@ -import { isReadTypebotForbidden } from "@/features/typebot/helpers/isReadTypebotForbidden"; import { authenticatedProcedure } from "@/helpers/server/trpc"; import { TRPCError } from "@trpc/server"; import prisma from "@typebot.io/prisma"; import { resultWithAnswersSchema } from "@typebot.io/results/schemas/results"; +import { isReadTypebotForbidden } from "@typebot.io/typebot/helpers/isReadTypebotForbidden"; import { z } from "@typebot.io/zod"; export const getResult = authenticatedProcedure diff --git a/apps/builder/src/features/results/api/getResultLogs.ts b/apps/builder/src/features/results/api/getResultLogs.ts index e1b77c2c12..ca13a25fb8 100644 --- a/apps/builder/src/features/results/api/getResultLogs.ts +++ b/apps/builder/src/features/results/api/getResultLogs.ts @@ -1,7 +1,7 @@ -import { isReadTypebotForbidden } from "@/features/typebot/helpers/isReadTypebotForbidden"; import { authenticatedProcedure } from "@/helpers/server/trpc"; import prisma from "@typebot.io/prisma"; import { logSchema } from "@typebot.io/results/schemas/results"; +import { isReadTypebotForbidden } from "@typebot.io/typebot/helpers/isReadTypebotForbidden"; import { z } from "@typebot.io/zod"; export const getResultLogs = authenticatedProcedure diff --git a/apps/builder/src/features/results/api/getResults.ts b/apps/builder/src/features/results/api/getResults.ts index d3dae4c471..70533cb443 100644 --- a/apps/builder/src/features/results/api/getResults.ts +++ b/apps/builder/src/features/results/api/getResults.ts @@ -6,11 +6,11 @@ import { parseFromDateFromTimeFilter, parseToDateFromTimeFilter, } from "@/features/analytics/helpers/parseDateFromTimeFilter"; -import { isReadTypebotForbidden } from "@/features/typebot/helpers/isReadTypebotForbidden"; import { authenticatedProcedure } from "@/helpers/server/trpc"; import { TRPCError } from "@trpc/server"; import prisma from "@typebot.io/prisma"; import { resultWithAnswersSchema } from "@typebot.io/results/schemas/results"; +import { isReadTypebotForbidden } from "@typebot.io/typebot/helpers/isReadTypebotForbidden"; import { z } from "@typebot.io/zod"; const maxLimit = 100; diff --git a/apps/builder/src/features/typebot/api/getPublishedTypebot.ts b/apps/builder/src/features/typebot/api/getPublishedTypebot.ts index e30cb2dda5..0fb1735e22 100644 --- a/apps/builder/src/features/typebot/api/getPublishedTypebot.ts +++ b/apps/builder/src/features/typebot/api/getPublishedTypebot.ts @@ -1,6 +1,7 @@ import { authenticatedProcedure } from "@/helpers/server/trpc"; import { TRPCError } from "@trpc/server"; import prisma from "@typebot.io/prisma"; +import { isReadTypebotForbidden } from "@typebot.io/typebot/helpers/isReadTypebotForbidden"; import { migratePublicTypebot } from "@typebot.io/typebot/migrations/migrateTypebot"; import { publicTypebotSchema, @@ -9,7 +10,6 @@ import { } from "@typebot.io/typebot/schemas/publicTypebot"; import type { Typebot } from "@typebot.io/typebot/schemas/typebot"; import { z } from "@typebot.io/zod"; -import { isReadTypebotForbidden } from "../helpers/isReadTypebotForbidden"; export const getPublishedTypebot = authenticatedProcedure .meta({ diff --git a/apps/builder/src/features/typebot/api/getTypebot.ts b/apps/builder/src/features/typebot/api/getTypebot.ts index 8a70db5a2a..348641407f 100644 --- a/apps/builder/src/features/typebot/api/getTypebot.ts +++ b/apps/builder/src/features/typebot/api/getTypebot.ts @@ -3,10 +3,10 @@ import { TRPCError } from "@trpc/server"; import { env } from "@typebot.io/env"; import prisma from "@typebot.io/prisma"; import type { CollaborationType } from "@typebot.io/prisma/enum"; +import { isReadTypebotForbidden } from "@typebot.io/typebot/helpers/isReadTypebotForbidden"; import { migrateTypebot } from "@typebot.io/typebot/migrations/migrateTypebot"; import { typebotSchema } from "@typebot.io/typebot/schemas/typebot"; import { z } from "@typebot.io/zod"; -import { isReadTypebotForbidden } from "../helpers/isReadTypebotForbidden"; export const getTypebot = publicProcedure .meta({ diff --git a/apps/builder/src/features/whatsapp/receiveMessagePreview.ts b/apps/builder/src/features/whatsapp/receiveMessagePreview.ts index bb3f3e1bd7..38f076fba6 100644 --- a/apps/builder/src/features/whatsapp/receiveMessagePreview.ts +++ b/apps/builder/src/features/whatsapp/receiveMessagePreview.ts @@ -1,11 +1,15 @@ import { publicProcedure } from "@/helpers/server/trpc"; import { TRPCError } from "@trpc/server"; import { env } from "@typebot.io/env"; -import { isNotDefined } from "@typebot.io/lib/utils"; import { resumeWhatsAppFlow } from "@typebot.io/whatsapp/resumeWhatsAppFlow"; -import { whatsAppWebhookRequestBodySchema } from "@typebot.io/whatsapp/schemas"; +import { + type WhatsAppWebhookRequestBody, + whatsAppWebhookRequestBodySchema, +} from "@typebot.io/whatsapp/schemas"; import { z } from "@typebot.io/zod"; +const whatsAppPreviewSessionIdPrefix = "wa-preview-"; + export const receiveMessagePreview = publicProcedure .meta({ openapi: { @@ -22,23 +26,40 @@ export const receiveMessagePreview = publicProcedure }), ) .mutation(async ({ input: { entry } }) => { - if (!env.WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID) - throw new TRPCError({ - code: "INTERNAL_SERVER_ERROR", - message: "WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID is not defined", - }); - const receivedMessage = entry.at(0)?.changes.at(0)?.value.messages?.at(0); - if (isNotDefined(receivedMessage)) return { message: "No message found" }; - const contactName = - entry.at(0)?.changes.at(0)?.value?.contacts?.at(0)?.profile?.name ?? ""; - const contactPhoneNumber = - entry.at(0)?.changes.at(0)?.value?.messages?.at(0)?.from ?? ""; - return resumeWhatsAppFlow({ + assertEnv(); + + const { receivedMessage, contactName, contactPhoneNumber } = + extractMessageData(entry); + if (!receivedMessage) return { message: "No message found" }; + + await resumeWhatsAppFlow({ receivedMessage, - sessionId: `wa-preview-${receivedMessage.from}`, + sessionId: `${whatsAppPreviewSessionIdPrefix}${receivedMessage.from}`, contact: { name: contactName, phoneNumber: contactPhoneNumber, }, }); + + return { + message: "Message received", + }; }); + +const assertEnv = () => { + if (!env.WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID) + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID is not defined", + }); +}; + +const extractMessageData = (entry: WhatsAppWebhookRequestBody["entry"]) => { + const receivedMessage = entry.at(0)?.changes.at(0)?.value.messages?.at(0); + const contactName = + entry.at(0)?.changes.at(0)?.value?.contacts?.at(0)?.profile?.name ?? ""; + const contactPhoneNumber = + entry.at(0)?.changes.at(0)?.value?.messages?.at(0)?.from ?? ""; + + return { receivedMessage, contactName, contactPhoneNumber }; +}; diff --git a/apps/builder/src/features/whatsapp/startWhatsAppPreview.ts b/apps/builder/src/features/whatsapp/startWhatsAppPreview.ts index 87ee7d5772..6dc752b03b 100644 --- a/apps/builder/src/features/whatsapp/startWhatsAppPreview.ts +++ b/apps/builder/src/features/whatsapp/startWhatsAppPreview.ts @@ -7,11 +7,11 @@ import type { SessionState } from "@typebot.io/bot-engine/schemas/chatSession"; import { startSession } from "@typebot.io/bot-engine/startSession"; import { env } from "@typebot.io/env"; import prisma from "@typebot.io/prisma"; +import { isReadTypebotForbidden } from "@typebot.io/typebot/helpers/isReadTypebotForbidden"; import { sendChatReplyToWhatsApp } from "@typebot.io/whatsapp/sendChatReplyToWhatsApp"; import { sendWhatsAppMessage } from "@typebot.io/whatsapp/sendWhatsAppMessage"; import { z } from "@typebot.io/zod"; import { HTTPError } from "ky"; -import { isReadTypebotForbidden } from "../typebot/helpers/isReadTypebotForbidden"; export const startWhatsAppPreview = authenticatedProcedure .meta({ @@ -127,7 +127,6 @@ export const startWhatsAppPreview = authenticatedProcedure if (canSendDirectMessagesToUser) { await sendChatReplyToWhatsApp({ to, - typingEmulation: newSessionState.typingEmulation, messages, input, clientSideActions, diff --git a/apps/builder/src/helpers/server/routers/publicRouter.ts b/apps/builder/src/helpers/server/routers/publicRouter.ts index a17205351a..64e08e4aa1 100644 --- a/apps/builder/src/helpers/server/routers/publicRouter.ts +++ b/apps/builder/src/helpers/server/routers/publicRouter.ts @@ -1,6 +1,6 @@ import { analyticsRouter } from "@/features/analytics/api/router"; import { billingRouter } from "@/features/billing/api/router"; -import { webhookRouter } from "@/features/blocks/integrations/webhook/api/router"; +import { httpRequestRouter } from "@/features/blocks/integrations/httpRequest/api/router"; import { getLinkedTypebots } from "@/features/blocks/logic/typebotLink/api/getLinkedTypebots"; import { collaboratorsRouter } from "@/features/collaboration/api/router"; import { credentialsRouter } from "@/features/credentials/api/router"; @@ -18,7 +18,7 @@ export const publicRouter = router({ analytics: analyticsRouter, workspace: workspaceRouter, typebot: typebotRouter, - webhook: webhookRouter, + httpRequest: httpRequestRouter, results: resultsRouter, billing: billingRouter, credentials: credentialsRouter, diff --git a/apps/builder/src/lib/theme.ts b/apps/builder/src/lib/theme.ts index 1526be70e2..ec95bae482 100644 --- a/apps/builder/src/lib/theme.ts +++ b/apps/builder/src/lib/theme.ts @@ -5,6 +5,7 @@ import { modalAnatomy, popoverAnatomy, switchAnatomy, + tabsAnatomy, } from "@chakra-ui/anatomy"; import { type StyleFunctionProps, @@ -188,6 +189,36 @@ const Switch = createMultiStyleConfigHelpers( }), }); +const Tabs = createMultiStyleConfigHelpers( + tabsAnatomy.keys, +).defineMultiStyleConfig({ + baseStyle: ({ colorMode }) => ({ + tablist: { + gap: 2, + }, + tab: { + px: "3", + borderRadius: "md", + fontWeight: "semibold", + _selected: { + bg: colorMode === "dark" ? "gray.800" : "gray.100", + }, + _hover: { + bg: colorMode === "dark" ? "gray.800" : "gray.100", + }, + _active: { + bg: colorMode === "dark" ? "gray.700" : "gray.200", + }, + }, + tabpanel: { + px: 0, + }, + }), + defaultProps: { + variant: "unstyled", + }, +}); + const components = { Modal, Popover, @@ -226,6 +257,7 @@ const components = { rounded: "md", }, }, + Tabs, }; const styles = { diff --git a/apps/builder/src/pages/api/typebots/[typebotId]/blocks/[blockId]/testWebhook.ts b/apps/builder/src/pages/api/typebots/[typebotId]/blocks/[blockId]/testHttpRequest.ts similarity index 91% rename from apps/builder/src/pages/api/typebots/[typebotId]/blocks/[blockId]/testWebhook.ts rename to apps/builder/src/pages/api/typebots/[typebotId]/blocks/[blockId]/testHttpRequest.ts index 22eb09b2b8..81b7a4b02d 100644 --- a/apps/builder/src/pages/api/typebots/[typebotId]/blocks/[blockId]/testWebhook.ts +++ b/apps/builder/src/pages/api/typebots/[typebotId]/blocks/[blockId]/testHttpRequest.ts @@ -1,12 +1,12 @@ import { getAuthenticatedUser } from "@/features/auth/helpers/getAuthenticatedUser"; -import { isWebhookBlock } from "@typebot.io/blocks-core/helpers"; +import { isHttpRequestBlock } from "@typebot.io/blocks-core/helpers"; import type { Block } from "@typebot.io/blocks-core/schemas/schema"; -import type { HttpRequest } from "@typebot.io/blocks-integrations/webhook/schema"; +import type { HttpRequest } from "@typebot.io/blocks-integrations/httpRequest/schema"; import { - executeWebhook, + executeHttpRequest, parseWebhookAttributes, -} from "@typebot.io/bot-engine/blocks/integrations/webhook/executeWebhookBlock"; -import { parseSampleResult } from "@typebot.io/bot-engine/blocks/integrations/webhook/parseSampleResult"; +} from "@typebot.io/bot-engine/blocks/integrations/httpRequest/executeHttpRequestBlock"; +import { parseSampleResult } from "@typebot.io/bot-engine/blocks/integrations/httpRequest/parseSampleResult"; import { fetchLinkedChildTypebots } from "@typebot.io/bot-engine/blocks/logic/typebotLink/fetchLinkedChildTypebots"; import { saveLog } from "@typebot.io/bot-engine/logs/saveLog"; import { getBlockById } from "@typebot.io/groups/helpers"; @@ -38,7 +38,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { const block = typebot.groups .flatMap((g) => g.blocks) .find(byId(blockId)); - if (!block || !isWebhookBlock(block)) + if (!block || !isHttpRequestBlock(block)) return notFound(res, "Webhook block not found"); const webhookId = "webhookId" in block ? block.webhookId : undefined; const webhook = @@ -82,7 +82,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { data: { message: `Couldn't parse webhook attributes` }, }); - const { response, logs } = await executeWebhook(parsedWebhook, { + const { response, logs } = await executeHttpRequest(parsedWebhook, { timeout: block.options?.timeout, }); diff --git a/apps/builder/src/pages/api/typebots/[typebotId]/results/[resultId]/[fileName].ts b/apps/builder/src/pages/api/typebots/[typebotId]/results/[resultId]/[fileName].ts index 2a697210a1..81da93c63a 100644 --- a/apps/builder/src/pages/api/typebots/[typebotId]/results/[resultId]/[fileName].ts +++ b/apps/builder/src/pages/api/typebots/[typebotId]/results/[resultId]/[fileName].ts @@ -1,5 +1,4 @@ import { getAuthenticatedUser } from "@/features/auth/helpers/getAuthenticatedUser"; -import { isReadTypebotForbidden } from "@/features/typebot/helpers/isReadTypebotForbidden"; import { badRequest, methodNotAllowed, @@ -8,6 +7,7 @@ import { } from "@typebot.io/lib/api/utils"; import { getFileTempUrl } from "@typebot.io/lib/s3/getFileTempUrl"; import prisma from "@typebot.io/prisma"; +import { isReadTypebotForbidden } from "@typebot.io/typebot/helpers/isReadTypebotForbidden"; import type { NextApiRequest, NextApiResponse } from "next"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { diff --git a/apps/builder/src/test/assets/typebots/integrations/webhook.json b/apps/builder/src/test/assets/typebots/integrations/httpRequest.json similarity index 100% rename from apps/builder/src/test/assets/typebots/integrations/webhook.json rename to apps/builder/src/test/assets/typebots/integrations/httpRequest.json diff --git a/apps/builder/src/test/assets/typebots/logic/webhook.json b/apps/builder/src/test/assets/typebots/logic/webhook.json new file mode 100644 index 0000000000..3d26e8ceab --- /dev/null +++ b/apps/builder/src/test/assets/typebots/logic/webhook.json @@ -0,0 +1,92 @@ +{ + "version": "6", + "id": "cm1qmfbmd0001otew1vegx05q", + "name": "My typebotโ€โ€โ€Œโ€Œโ€Œโ€Œโ€โ€Œโ€Œโ€โ€Œโ€Œโ€Œโ€Œโ€โ€Œโ€Œโ€Œ", + "events": [ + { + "id": "aevmtgzc9rxzvxs2q5hvpwct", + "graphCoordinates": { "x": 0, "y": 0 }, + "type": "start", + "outgoingEdgeId": "l4t0scp44brhsaev2shrz1ww" + } + ], + "groups": [ + { + "id": "ad35ipsnb48e6ezkgdir18ti", + "graphCoordinates": { "x": 214.53, "y": 90.88 }, + "title": "Group #1", + "blocks": [ + { + "id": "ti3emvkuw16pno8hfoqhwohy", + "type": "text", + "content": { + "richText": [ + { + "type": "p", + "children": [{ "text": "Ok let's look into that..." }] + } + ] + } + }, + { + "id": "webhook-block-id", + "type": "webhook", + "outgoingEdgeId": "zmc9shyqackqkxqyjok5adip" + } + ] + }, + { + "id": "wt0gw2gk1bw8x7ajch6ppkkm", + "graphCoordinates": { "x": 607.12, "y": 92 }, + "title": "Group #2", + "blocks": [ + { + "id": "ysc1dd8tydgn810jzn5xkhep", + "type": "text", + "content": { + "richText": [ + { + "type": "p", + "children": [{ "text": "Hi {{Name}}, nice to see you again!" }] + } + ] + } + } + ] + } + ], + "edges": [ + { + "from": { "eventId": "aevmtgzc9rxzvxs2q5hvpwct" }, + "to": { "groupId": "ad35ipsnb48e6ezkgdir18ti" }, + "id": "l4t0scp44brhsaev2shrz1ww" + }, + { + "from": { "blockId": "n0i5queut7m0wa5am2gqa0ra" }, + "to": { "groupId": "wt0gw2gk1bw8x7ajch6ppkkm" }, + "id": "zmc9shyqackqkxqyjok5adip" + } + ], + "variables": [ + { + "id": "vzq187kjoyn9nt7283dkbs59n", + "name": "Name", + "isSessionVariable": true + } + ], + "theme": {}, + "selectedThemeTemplateId": null, + "settings": { "general": { "isBrandingEnabled": true } }, + "createdAt": "2024-10-01T15:56:19.477Z", + "updatedAt": "2024-10-01T15:56:19.477Z", + "icon": null, + "folderId": null, + "publicId": null, + "customDomain": null, + "workspaceId": "freeWorkspace", + "resultsTablePreferences": null, + "isArchived": false, + "isClosed": false, + "whatsAppCredentialsId": null, + "riskLevel": null +} diff --git a/apps/docs/editor/blocks/inputs/email.mdx b/apps/docs/editor/blocks/inputs/email.mdx index d93c355811..ddca0f6d48 100644 --- a/apps/docs/editor/blocks/inputs/email.mdx +++ b/apps/docs/editor/blocks/inputs/email.mdx @@ -28,4 +28,4 @@ The Email input block allows you to ask your user for an email. It will check if The retry message will be displayed whenever Typebot detected that the email is not properly formatted. -It won't check if the email address is **valid**. To do that, you will have to trigger a [Webhook block](/editor/blocks/integrations/webhook) and call an email validation service API. +It won't check if the email address is **valid**. To do that, you will have to trigger a [HTTP request block](/editor/blocks/integrations/http-request) and call an email validation service API. diff --git a/apps/docs/editor/blocks/inputs/website.mdx b/apps/docs/editor/blocks/inputs/website.mdx index f74375fef7..dacbf819c1 100644 --- a/apps/docs/editor/blocks/inputs/website.mdx +++ b/apps/docs/editor/blocks/inputs/website.mdx @@ -28,4 +28,4 @@ The Website input block allows you to ask your user for a URL. It will check if The retry message will be displayed whenever Typebot detected that the email is not properly formatted. -It won't check if the email address is **valid**. To do that, you will have to trigger a [Webhook block](/editor/blocks/integrations/webhook) and call an email validation service API. +It won't check if the email address is **valid**. To do that, you will have to trigger a [HTTP request block](/editor/blocks/integrations/http-request) and call an email validation service API. diff --git a/apps/docs/editor/blocks/integrations/webhook.mdx b/apps/docs/editor/blocks/integrations/http-request.mdx similarity index 91% rename from apps/docs/editor/blocks/integrations/webhook.mdx rename to apps/docs/editor/blocks/integrations/http-request.mdx index 253ccee6bb..dc59fd55fa 100644 --- a/apps/docs/editor/blocks/integrations/webhook.mdx +++ b/apps/docs/editor/blocks/integrations/http-request.mdx @@ -41,7 +41,7 @@ What I need in my case is instead of inserting "Star Wars", I'd like to insert a Variable in URL @@ -50,7 +50,7 @@ Then, we can set a test value for our variable (it will replace the variable wit Variable test values @@ -59,7 +59,7 @@ Hit the "Test the request" button and then we can save the result in multiple va Test request @@ -67,7 +67,7 @@ Hit the "Test the request" button and then we can save the result in multiple va Then we can use these variables to display dynamic content in the next bubbles: - Preview + Preview Possibilities are endless when it comes to API calls, you can litteraly call any API and fetch any data you want. @@ -82,7 +82,7 @@ You only have to paste this URL in the Webhook block and click on "Test the requ Simple Webhook POST diff --git a/apps/docs/editor/blocks/logic/wait.mdx b/apps/docs/editor/blocks/logic/wait.mdx index 9c66bdb06d..e5a00afade 100644 --- a/apps/docs/editor/blocks/logic/wait.mdx +++ b/apps/docs/editor/blocks/logic/wait.mdx @@ -7,11 +7,11 @@ The "Wait" block allows you to pause the conversation for a certain amount of se This can be useful if you want the bot to emphasize on what's been said or to wait before a redirection for example to make sure the user has read everything. - + This should be used wisely. If you want the bot to write slower or faster in a more general sense, you need to check the [Typing emulation settings](/settings/overview#typing-emulation) - + ## Pause the flow diff --git a/apps/docs/editor/blocks/logic/webhook.mdx b/apps/docs/editor/blocks/logic/webhook.mdx new file mode 100644 index 0000000000..b4c7a5e566 --- /dev/null +++ b/apps/docs/editor/blocks/logic/webhook.mdx @@ -0,0 +1,40 @@ +--- +title: Webhook +icon: webhook +--- + +The Webhook block allows you to pause the conversation until a provided webhook URL is called. + +This can be useful if you want to execute a long-running action that can take several minutes to complete. + +## URLs + +### Test + +- Web: + +``` +https://typebot.io/api/v1/typebots/{typebotId}/blocks/{blockId}/web/executeTestWebhook +``` + +- WhatsApp: + +``` +https://typebot.io/api/v1/typebots/{typebotId}/blocks/{blockId}/whatsapp/{phone}/executeTestWebhook +``` + +Where `{phone}` is your phone number on which you test the bot. For example, if you provided `+33 601 020304`, `{phone}` should be `33601020304`, no spaces, no `+`. + +### Production + +No matter the environment, the webhook URL will be the same: + +``` +https://typebot.io/api/v1/typebots/{typebotId}/blocks/{blockId}/results/{resultId}/executeWebhook +``` + +Where `{resultId}` is the current user result ID. You can get this ID directly in your typebot flow using a [Set variable](/editor/blocks/logic/set-variable) block with the `Result ID` value. + +## Authentication + +The Webhook URL needs to be authenticated in order to work. See [API authentication](/api-reference/authentication) for more information. diff --git a/apps/docs/images/blocks/integrations/webhook/preview.png b/apps/docs/images/blocks/integrations/http-request/preview.png similarity index 100% rename from apps/docs/images/blocks/integrations/webhook/preview.png rename to apps/docs/images/blocks/integrations/http-request/preview.png diff --git a/apps/docs/images/blocks/integrations/webhook/save-in-variable.png b/apps/docs/images/blocks/integrations/http-request/save-in-variable.png similarity index 100% rename from apps/docs/images/blocks/integrations/webhook/save-in-variable.png rename to apps/docs/images/blocks/integrations/http-request/save-in-variable.png diff --git a/apps/docs/images/blocks/integrations/webhook/simple-post.png b/apps/docs/images/blocks/integrations/http-request/simple-post.png similarity index 100% rename from apps/docs/images/blocks/integrations/webhook/simple-post.png rename to apps/docs/images/blocks/integrations/http-request/simple-post.png diff --git a/apps/docs/images/blocks/integrations/webhook/variable-test-value.png b/apps/docs/images/blocks/integrations/http-request/variable-test-value.png similarity index 100% rename from apps/docs/images/blocks/integrations/webhook/variable-test-value.png rename to apps/docs/images/blocks/integrations/http-request/variable-test-value.png diff --git a/apps/docs/images/blocks/integrations/webhook/variable-url.png b/apps/docs/images/blocks/integrations/http-request/variable-url.png similarity index 100% rename from apps/docs/images/blocks/integrations/webhook/variable-url.png rename to apps/docs/images/blocks/integrations/http-request/variable-url.png diff --git a/apps/docs/mint.json b/apps/docs/mint.json index e05c830061..9cf64c15d9 100644 --- a/apps/docs/mint.json +++ b/apps/docs/mint.json @@ -106,7 +106,8 @@ "editor/blocks/logic/typebot-link", "editor/blocks/logic/wait", "editor/blocks/logic/jump", - "editor/blocks/logic/ab-test" + "editor/blocks/logic/ab-test", + "editor/blocks/logic/webhook" ] }, { @@ -114,7 +115,7 @@ "pages": [ "editor/blocks/integrations/google-sheets", "editor/blocks/integrations/google-analytics", - "editor/blocks/integrations/webhook", + "editor/blocks/integrations/http-request", "editor/blocks/integrations/send-email", "editor/blocks/integrations/zapier", "editor/blocks/integrations/make-com", @@ -341,5 +342,11 @@ "discord": "https://typebot.io/discord", "twitter": "https://twitter.com/Typebot_io", "github": "https://github.com/baptisteArno/typebot.io" - } + }, + "redirects": [ + { + "source": "/editor/blocks/integrations/webhook", + "destination": "/editor/blocks/integrations/http-request" + } + ] } diff --git a/apps/docs/openapi/builder.json b/apps/docs/openapi/builder.json index b951f47ddf..633cb5c159 100644 --- a/apps/docs/openapi/builder.json +++ b/apps/docs/openapi/builder.json @@ -7952,11 +7952,11 @@ }, "/v1/typebots/{typebotId}/webhookBlocks": { "get": { - "operationId": "webhook-listWebhookBlocks", - "summary": "List webhook blocks", - "description": "Returns a list of all the webhook blocks that you can subscribe to.", + "operationId": "httpRequest-listHttpRequestBlocks", + "summary": "List HTTP request blocks", + "description": "Returns a list of all the HTTP request blocks that you can subscribe to.", "tags": [ - "Webhook" + "HTTP request" ], "security": [ { @@ -8075,11 +8075,11 @@ }, "/v1/typebots/{typebotId}/webhookBlocks/{blockId}/getResultExample": { "get": { - "operationId": "webhook-getResultExample", + "operationId": "httpRequest-getResultExample", "summary": "Get result example", - "description": "Returns \"fake\" result for webhook block to help you anticipate how the webhook will behave.", + "description": "Returns \"fake\" result for http request block to help you anticipate how the webhook will behave.", "tags": [ - "Webhook" + "HTTP request" ], "security": [ { @@ -8180,10 +8180,10 @@ }, "/v1/typebots/{typebotId}/webhookBlocks/{blockId}/subscribe": { "post": { - "operationId": "webhook-subscribeWebhook", - "summary": "Subscribe to webhook block", + "operationId": "httpRequest-subscribeHttpRequest", + "summary": "Subscribe to HTTP request block", "tags": [ - "Webhook" + "HTTP request" ], "security": [ { @@ -8295,10 +8295,10 @@ }, "/v1/typebots/{typebotId}/webhookBlocks/{blockId}/unsubscribe": { "post": { - "operationId": "webhook-unsubscribeWebhook", - "summary": "Unsubscribe from webhook block", + "operationId": "httpRequest-unsubscribeHttpRequest", + "summary": "Unsubscribe from HTTP request block", "tags": [ - "Webhook" + "HTTP request" ], "security": [ { @@ -13310,6 +13310,40 @@ "location", "timestamp" ] + }, + { + "type": "object", + "properties": { + "from": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "webhook" + ] + }, + "webhook": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + }, + "required": [ + "data" + ] + }, + "timestamp": { + "type": "string" + } + }, + "required": [ + "from", + "type", + "webhook", + "timestamp" + ] } ] } @@ -16026,6 +16060,9 @@ }, { "$ref": "#/components/schemas/abTestLogic" + }, + { + "$ref": "#/components/schemas/webhookLogic" } ], "discriminator": { @@ -16038,7 +16075,8 @@ "Wait": "#/components/schemas/waitLogic", "Jump": "#/components/schemas/jumpLogic", "Condition": "#/components/schemas/conditionLogic", - "AB test": "#/components/schemas/abTestLogic" + "AB test": "#/components/schemas/abTestLogic", + "webhook": "#/components/schemas/webhookLogic" } } }, @@ -17042,6 +17080,53 @@ ], "title": "AB Test" }, + "webhookLogic": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "outgoingEdgeId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "webhook" + ] + }, + "options": { + "type": "object", + "properties": { + "responseVariableMapping": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "variableId": { + "type": "string" + }, + "bodyPath": { + "type": "string" + } + }, + "required": [ + "id" + ] + } + } + } + } + }, + "required": [ + "id", + "type" + ], + "title": "Webhook" + }, "googleSheetsBlock": { "type": "object", "properties": { diff --git a/apps/docs/openapi/viewer.json b/apps/docs/openapi/viewer.json index 49e430f4c0..230b5e7c47 100644 --- a/apps/docs/openapi/viewer.json +++ b/apps/docs/openapi/viewer.json @@ -385,7 +385,7 @@ "$ref": "#/components/schemas/csaStreamOpenAiChatCompletion" }, { - "$ref": "#/components/schemas/csaExecWebhook" + "$ref": "#/components/schemas/csaHttpRequestToExecute" }, { "$ref": "#/components/schemas/csaInjectStartProps" @@ -398,6 +398,9 @@ }, { "$ref": "#/components/schemas/csaCodeToExecute" + }, + { + "$ref": "#/components/schemas/csaListenForWebhook" } ], "discriminator": { @@ -410,11 +413,12 @@ "wait": "#/components/schemas/csaWait", "setVariable": "#/components/schemas/csaSetVariable", "streamOpenAiChatCompletion": "#/components/schemas/csaStreamOpenAiChatCompletion", - "webhookToExecute": "#/components/schemas/csaExecWebhook", + "httpRequestToExecute": "#/components/schemas/csaHttpRequestToExecute", "startPropsToInject": "#/components/schemas/csaInjectStartProps", "pixel": "#/components/schemas/csaPixel", "stream": "#/components/schemas/csaStream", - "codeToExecute": "#/components/schemas/csaCodeToExecute" + "codeToExecute": "#/components/schemas/csaCodeToExecute", + "listenForWebhook": "#/components/schemas/csaListenForWebhook" } } } @@ -895,7 +899,7 @@ "$ref": "#/components/schemas/csaStreamOpenAiChatCompletion" }, { - "$ref": "#/components/schemas/csaExecWebhook" + "$ref": "#/components/schemas/csaHttpRequestToExecute" }, { "$ref": "#/components/schemas/csaInjectStartProps" @@ -908,6 +912,9 @@ }, { "$ref": "#/components/schemas/csaCodeToExecute" + }, + { + "$ref": "#/components/schemas/csaListenForWebhook" } ], "discriminator": { @@ -920,11 +927,12 @@ "wait": "#/components/schemas/csaWait", "setVariable": "#/components/schemas/csaSetVariable", "streamOpenAiChatCompletion": "#/components/schemas/csaStreamOpenAiChatCompletion", - "webhookToExecute": "#/components/schemas/csaExecWebhook", + "httpRequestToExecute": "#/components/schemas/csaHttpRequestToExecute", "startPropsToInject": "#/components/schemas/csaInjectStartProps", "pixel": "#/components/schemas/csaPixel", "stream": "#/components/schemas/csaStream", - "codeToExecute": "#/components/schemas/csaCodeToExecute" + "codeToExecute": "#/components/schemas/csaCodeToExecute", + "listenForWebhook": "#/components/schemas/csaListenForWebhook" } } } @@ -1337,7 +1345,7 @@ "$ref": "#/components/schemas/csaStreamOpenAiChatCompletion" }, { - "$ref": "#/components/schemas/csaExecWebhook" + "$ref": "#/components/schemas/csaHttpRequestToExecute" }, { "$ref": "#/components/schemas/csaInjectStartProps" @@ -1350,6 +1358,9 @@ }, { "$ref": "#/components/schemas/csaCodeToExecute" + }, + { + "$ref": "#/components/schemas/csaListenForWebhook" } ], "discriminator": { @@ -1362,11 +1373,12 @@ "wait": "#/components/schemas/csaWait", "setVariable": "#/components/schemas/csaSetVariable", "streamOpenAiChatCompletion": "#/components/schemas/csaStreamOpenAiChatCompletion", - "webhookToExecute": "#/components/schemas/csaExecWebhook", + "httpRequestToExecute": "#/components/schemas/csaHttpRequestToExecute", "startPropsToInject": "#/components/schemas/csaInjectStartProps", "pixel": "#/components/schemas/csaPixel", "stream": "#/components/schemas/csaStream", - "codeToExecute": "#/components/schemas/csaCodeToExecute" + "codeToExecute": "#/components/schemas/csaCodeToExecute", + "listenForWebhook": "#/components/schemas/csaListenForWebhook" } } }, @@ -1692,7 +1704,7 @@ "$ref": "#/components/schemas/csaStreamOpenAiChatCompletion" }, { - "$ref": "#/components/schemas/csaExecWebhook" + "$ref": "#/components/schemas/csaHttpRequestToExecute" }, { "$ref": "#/components/schemas/csaInjectStartProps" @@ -1705,6 +1717,9 @@ }, { "$ref": "#/components/schemas/csaCodeToExecute" + }, + { + "$ref": "#/components/schemas/csaListenForWebhook" } ], "discriminator": { @@ -1717,11 +1732,12 @@ "wait": "#/components/schemas/csaWait", "setVariable": "#/components/schemas/csaSetVariable", "streamOpenAiChatCompletion": "#/components/schemas/csaStreamOpenAiChatCompletion", - "webhookToExecute": "#/components/schemas/csaExecWebhook", + "httpRequestToExecute": "#/components/schemas/csaHttpRequestToExecute", "startPropsToInject": "#/components/schemas/csaInjectStartProps", "pixel": "#/components/schemas/csaPixel", "stream": "#/components/schemas/csaStream", - "codeToExecute": "#/components/schemas/csaCodeToExecute" + "codeToExecute": "#/components/schemas/csaCodeToExecute", + "listenForWebhook": "#/components/schemas/csaListenForWebhook" } } }, @@ -2154,7 +2170,7 @@ "$ref": "#/components/schemas/csaStreamOpenAiChatCompletion" }, { - "$ref": "#/components/schemas/csaExecWebhook" + "$ref": "#/components/schemas/csaHttpRequestToExecute" }, { "$ref": "#/components/schemas/csaInjectStartProps" @@ -2167,6 +2183,9 @@ }, { "$ref": "#/components/schemas/csaCodeToExecute" + }, + { + "$ref": "#/components/schemas/csaListenForWebhook" } ], "discriminator": { @@ -2179,11 +2198,12 @@ "wait": "#/components/schemas/csaWait", "setVariable": "#/components/schemas/csaSetVariable", "streamOpenAiChatCompletion": "#/components/schemas/csaStreamOpenAiChatCompletion", - "webhookToExecute": "#/components/schemas/csaExecWebhook", + "httpRequestToExecute": "#/components/schemas/csaHttpRequestToExecute", "startPropsToInject": "#/components/schemas/csaInjectStartProps", "pixel": "#/components/schemas/csaPixel", "stream": "#/components/schemas/csaStream", - "codeToExecute": "#/components/schemas/csaCodeToExecute" + "codeToExecute": "#/components/schemas/csaCodeToExecute", + "listenForWebhook": "#/components/schemas/csaListenForWebhook" } } }, @@ -3128,6 +3148,40 @@ "location", "timestamp" ] + }, + { + "type": "object", + "properties": { + "from": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "webhook" + ] + }, + "webhook": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + }, + "required": [ + "data" + ] + }, + "timestamp": { + "type": "string" + } + }, + "required": [ + "from", + "type", + "webhook", + "timestamp" + ] } ] } @@ -3606,6 +3660,9 @@ }, { "$ref": "#/components/schemas/abTestLogic" + }, + { + "$ref": "#/components/schemas/webhookLogic" } ], "discriminator": { @@ -3618,7 +3675,8 @@ "Wait": "#/components/schemas/waitLogic", "Jump": "#/components/schemas/jumpLogic", "Condition": "#/components/schemas/conditionLogic", - "AB test": "#/components/schemas/abTestLogic" + "AB test": "#/components/schemas/abTestLogic", + "webhook": "#/components/schemas/webhookLogic" } } }, @@ -5754,6 +5812,53 @@ ], "title": "AB Test" }, + "webhookLogic": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "outgoingEdgeId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "webhook" + ] + }, + "options": { + "type": "object", + "properties": { + "responseVariableMapping": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "variableId": { + "type": "string" + }, + "bodyPath": { + "type": "string" + } + }, + "required": [ + "id" + ] + } + } + } + } + }, + "required": [ + "id", + "type" + ], + "title": "Webhook" + }, "chatwootBlock": { "type": "object", "properties": { @@ -13359,16 +13464,16 @@ ], "title": "Stream OpenAI" }, - "csaExecWebhook": { + "csaHttpRequestToExecute": { "type": "object", "properties": { "type": { "type": "string", "enum": [ - "webhookToExecute" + "httpRequestToExecute" ] }, - "webhookToExecute": { + "httpRequestToExecute": { "type": "object", "properties": { "url": { @@ -13409,9 +13514,9 @@ }, "required": [ "type", - "webhookToExecute" + "httpRequestToExecute" ], - "title": "Execute webhook" + "title": "Execute HTTP request" }, "csaInjectStartProps": { "type": "object", @@ -13671,6 +13776,27 @@ ], "title": "Execute code" }, + "csaListenForWebhook": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "listenForWebhook" + ] + }, + "lastBubbleBlockId": { + "type": "string" + }, + "expectsDedicatedReply": { + "type": "boolean" + } + }, + "required": [ + "type" + ], + "title": "Listen to webhook" + }, "error.BAD_REQUEST": { "type": "object", "properties": { diff --git a/apps/viewer/package.json b/apps/viewer/package.json index e83b7a8d39..007ec214d1 100644 --- a/apps/viewer/package.json +++ b/apps/viewer/package.json @@ -10,6 +10,7 @@ "test:ui": "dotenv -e ./.env -e ../../.env -- playwright test --ui" }, "dependencies": { + "partysocket": "1.0.2", "@typebot.io/trpc-openapi": "workspace:*", "@planetscale/database": "1.8.0", "@sentry/nextjs": "7.77.0", diff --git a/apps/viewer/src/features/chat/api/legacy/sendMessageV1.ts b/apps/viewer/src/features/chat/api/legacy/sendMessageV1.ts index fe3df4ab89..ab060a5df2 100644 --- a/apps/viewer/src/features/chat/api/legacy/sendMessageV1.ts +++ b/apps/viewer/src/features/chat/api/legacy/sendMessageV1.ts @@ -140,7 +140,7 @@ export const sendMessageV1 = publicProcedure logs: allLogs, clientSideActions, visitedEdges, - hasEmbedBubbleWithWaitEvent: messages.some( + isWaitingForExternalEvent: messages.some( (message) => message.type === "custom-embed" || (message.type === BubbleBlockType.EMBED && @@ -209,7 +209,7 @@ export const sendMessageV1 = publicProcedure logs: allLogs, clientSideActions, visitedEdges, - hasEmbedBubbleWithWaitEvent: messages.some( + isWaitingForExternalEvent: messages.some( (message) => message.type === "custom-embed" || (message.type === BubbleBlockType.EMBED && diff --git a/apps/viewer/src/features/chat/api/legacy/sendMessageV2.ts b/apps/viewer/src/features/chat/api/legacy/sendMessageV2.ts index f8d412ffb7..52e3f93c3b 100644 --- a/apps/viewer/src/features/chat/api/legacy/sendMessageV2.ts +++ b/apps/viewer/src/features/chat/api/legacy/sendMessageV2.ts @@ -140,7 +140,7 @@ export const sendMessageV2 = publicProcedure logs: allLogs, clientSideActions, visitedEdges, - hasEmbedBubbleWithWaitEvent: messages.some( + isWaitingForExternalEvent: messages.some( (message) => message.type === "custom-embed" || (message.type === BubbleBlockType.EMBED && @@ -208,7 +208,7 @@ export const sendMessageV2 = publicProcedure logs: allLogs, clientSideActions, visitedEdges, - hasEmbedBubbleWithWaitEvent: messages.some( + isWaitingForExternalEvent: messages.some( (message) => message.type === "custom-embed" || (message.type === BubbleBlockType.EMBED && diff --git a/apps/viewer/src/features/whatsapp/api/receiveMessage.ts b/apps/viewer/src/features/whatsapp/api/receiveMessage.ts index ef3ff9b68e..59991c3c9a 100644 --- a/apps/viewer/src/features/whatsapp/api/receiveMessage.ts +++ b/apps/viewer/src/features/whatsapp/api/receiveMessage.ts @@ -1,9 +1,13 @@ import { publicProcedure } from "@/helpers/server/trpc"; -import { isNotDefined } from "@typebot.io/lib/utils"; import { resumeWhatsAppFlow } from "@typebot.io/whatsapp/resumeWhatsAppFlow"; -import { whatsAppWebhookRequestBodySchema } from "@typebot.io/whatsapp/schemas"; +import { + type WhatsAppWebhookRequestBody, + whatsAppWebhookRequestBodySchema, +} from "@typebot.io/whatsapp/schemas"; import { z } from "@typebot.io/zod"; +const whatsAppSessionIdPrefix = "wa-"; + export const receiveMessage = publicProcedure .meta({ openapi: { @@ -24,18 +28,14 @@ export const receiveMessage = publicProcedure }), ) .mutation(async ({ input: { entry, credentialsId, workspaceId } }) => { - const receivedMessage = entry.at(0)?.changes.at(0)?.value.messages?.at(0); - if (isNotDefined(receivedMessage)) return { message: "No message found" }; - const contactName = - entry.at(0)?.changes.at(0)?.value?.contacts?.at(0)?.profile?.name ?? ""; - const contactPhoneNumber = - entry.at(0)?.changes.at(0)?.value?.messages?.at(0)?.from ?? ""; - const phoneNumberId = entry.at(0)?.changes.at(0)?.value - .metadata.phone_number_id; - if (!phoneNumberId) return { message: "No phone number id found" }; - return resumeWhatsAppFlow({ + const { receivedMessage, contactName, contactPhoneNumber, phoneNumberId } = + extractMessageDetails(entry); + if (!receivedMessage) return { message: "No message found" }; + if (!phoneNumberId) return { message: "No phone number found" }; + + await resumeWhatsAppFlow({ receivedMessage, - sessionId: `wa-${phoneNumberId}-${receivedMessage.from}`, + sessionId: `${whatsAppSessionIdPrefix}${phoneNumberId}-${receivedMessage.from}`, phoneNumberId, credentialsId, workspaceId, @@ -44,4 +44,19 @@ export const receiveMessage = publicProcedure phoneNumber: contactPhoneNumber, }, }); + + return { + message: "Message received", + }; }); + +const extractMessageDetails = (entry: WhatsAppWebhookRequestBody["entry"]) => { + const receivedMessage = entry.at(0)?.changes.at(0)?.value.messages?.at(0); + const contactName = + entry.at(0)?.changes.at(0)?.value?.contacts?.at(0)?.profile?.name ?? ""; + const contactPhoneNumber = + entry.at(0)?.changes.at(0)?.value?.messages?.at(0)?.from ?? ""; + const phoneNumberId = entry.at(0)?.changes.at(0)?.value + .metadata.phone_number_id; + return { receivedMessage, contactName, contactPhoneNumber, phoneNumberId }; +}; diff --git a/apps/viewer/src/pages/api/typebots/[typebotId]/blocks/[blockId]/executeWebhook.ts b/apps/viewer/src/pages/api/typebots/[typebotId]/blocks/[blockId]/executeWebhook.ts index 6946b68af9..861f1d4fd8 100644 --- a/apps/viewer/src/pages/api/typebots/[typebotId]/blocks/[blockId]/executeWebhook.ts +++ b/apps/viewer/src/pages/api/typebots/[typebotId]/blocks/[blockId]/executeWebhook.ts @@ -1,12 +1,12 @@ import { authenticateUser } from "@/helpers/authenticateUser"; -import { isWebhookBlock } from "@typebot.io/blocks-core/helpers"; +import { isHttpRequestBlock } from "@typebot.io/blocks-core/helpers"; import type { Block } from "@typebot.io/blocks-core/schemas/schema"; -import type { HttpRequest } from "@typebot.io/blocks-integrations/webhook/schema"; +import type { HttpRequest } from "@typebot.io/blocks-integrations/httpRequest/schema"; import { - executeWebhook, + executeHttpRequest, parseWebhookAttributes, -} from "@typebot.io/bot-engine/blocks/integrations/webhook/executeWebhookBlock"; -import { parseSampleResult } from "@typebot.io/bot-engine/blocks/integrations/webhook/parseSampleResult"; +} from "@typebot.io/bot-engine/blocks/integrations/httpRequest/executeHttpRequestBlock"; +import { parseSampleResult } from "@typebot.io/bot-engine/blocks/integrations/httpRequest/parseSampleResult"; import { fetchLinkedChildTypebots } from "@typebot.io/bot-engine/blocks/logic/typebotLink/fetchLinkedChildTypebots"; import { fetchLinkedParentTypebots } from "@typebot.io/bot-engine/blocks/logic/typebotLink/fetchLinkedParentTypebots"; import { saveLog } from "@typebot.io/bot-engine/logs/saveLog"; @@ -24,7 +24,6 @@ import type { PublicTypebot } from "@typebot.io/typebot/schemas/publicTypebot"; import type { Typebot } from "@typebot.io/typebot/schemas/typebot"; import type { Variable } from "@typebot.io/variables/schemas"; import Cors from "cors"; -/* eslint-disable @typescript-eslint/no-explicit-any */ import type { NextApiRequest, NextApiResponse } from "next"; const cors = initMiddleware(Cors()); @@ -51,7 +50,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { const block = typebot.groups .flatMap((g) => g.blocks) .find(byId(blockId)); - if (!block || !isWebhookBlock(block)) + if (!block || !isHttpRequestBlock(block)) return notFound(res, "Webhook block not found"); const webhookId = "webhookId" in block ? block.webhookId : undefined; const webhook = @@ -117,7 +116,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { data: { message: `Couldn't parse webhook attributes` }, }); - const { response, logs } = await executeWebhook(parsedWebhook, { + const { response, logs } = await executeHttpRequest(parsedWebhook, { timeout: block.options?.timeout, }); diff --git a/apps/viewer/src/pages/api/typebots/[typebotId]/webhookBlocks.ts b/apps/viewer/src/pages/api/typebots/[typebotId]/webhookBlocks.ts index 7de9f20ae5..590ba93367 100644 --- a/apps/viewer/src/pages/api/typebots/[typebotId]/webhookBlocks.ts +++ b/apps/viewer/src/pages/api/typebots/[typebotId]/webhookBlocks.ts @@ -1,6 +1,6 @@ import { authenticateUser } from "@/helpers/authenticateUser"; -import { isWebhookBlock } from "@typebot.io/blocks-core/helpers"; -import type { HttpRequestBlock } from "@typebot.io/blocks-integrations/webhook/schema"; +import { isHttpRequestBlock } from "@typebot.io/blocks-core/helpers"; +import type { HttpRequestBlock } from "@typebot.io/blocks-integrations/httpRequest/schema"; import type { Group } from "@typebot.io/groups/schemas"; import { methodNotAllowed } from "@typebot.io/lib/api/utils"; import prisma from "@typebot.io/prisma"; @@ -22,7 +22,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { { blockId: string; name: string; url: string | undefined }[] >((emptyWebhookBlocks, group) => { const blocks = group.blocks.filter((block) => - isWebhookBlock(block), + isHttpRequestBlock(block), ) as HttpRequestBlock[]; return [ ...emptyWebhookBlocks, diff --git a/apps/viewer/src/pages/api/typebots/[typebotId]/webhookSteps.ts b/apps/viewer/src/pages/api/typebots/[typebotId]/webhookSteps.ts index 00b3627408..56291caf32 100644 --- a/apps/viewer/src/pages/api/typebots/[typebotId]/webhookSteps.ts +++ b/apps/viewer/src/pages/api/typebots/[typebotId]/webhookSteps.ts @@ -1,5 +1,5 @@ import { authenticateUser } from "@/helpers/authenticateUser"; -import { isWebhookBlock } from "@typebot.io/blocks-core/helpers"; +import { isHttpRequestBlock } from "@typebot.io/blocks-core/helpers"; import type { Group } from "@typebot.io/groups/schemas"; import { methodNotAllowed } from "@typebot.io/lib/api/utils"; import { isNotDefined } from "@typebot.io/lib/utils"; @@ -23,7 +23,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { >((emptyWebhookBlocks, group) => { const blocks = group.blocks.filter( (block) => - isWebhookBlock(block) && + isHttpRequestBlock(block) && isNotDefined( typebot?.webhooks.find((w) => { if ("id" in w && "webhookId" in block) diff --git a/apps/viewer/src/pages/api/v1/typebots/[typebotId]/blocks/[blockId]/results/[resultId]/executeWebhook.ts b/apps/viewer/src/pages/api/v1/typebots/[typebotId]/blocks/[blockId]/results/[resultId]/executeWebhook.ts new file mode 100644 index 0000000000..8f39bfec18 --- /dev/null +++ b/apps/viewer/src/pages/api/v1/typebots/[typebotId]/blocks/[blockId]/results/[resultId]/executeWebhook.ts @@ -0,0 +1,132 @@ +import { authenticateUser } from "@/helpers/authenticateUser"; +import { LogicBlockType } from "@typebot.io/blocks-logic/constants"; +import { getSession } from "@typebot.io/bot-engine/queries/getSession"; +import { env } from "@typebot.io/env"; +import { parseGroups } from "@typebot.io/groups/schemas"; +import { + forbidden, + internalServerError, + methodNotAllowed, + notFound, +} from "@typebot.io/lib/api/utils"; +import { byId } from "@typebot.io/lib/utils"; +import prisma from "@typebot.io/prisma"; +import { isReadTypebotForbidden } from "@typebot.io/typebot/helpers/isReadTypebotForbidden"; +import { resumeWhatsAppFlow } from "@typebot.io/whatsapp/resumeWhatsAppFlow"; +import type { NextApiRequest, NextApiResponse } from "next"; +import PartySocket from "partysocket"; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + if (req.method === "POST") { + if (!env.NEXT_PUBLIC_PARTYKIT_HOST) return notFound(res); + const user = await authenticateUser(req); + if (!user) return forbidden(res, "User not authenticated"); + const typebotId = req.query.typebotId as string; + const blockId = req.query.blockId as string; + const resultId = req.query.resultId as string; + const typebot = await prisma.typebot.findUnique({ + where: { id: typebotId }, + select: { + version: true, + groups: true, + settings: true, + whatsAppCredentialsId: true, + workspace: { + select: { + id: true, + isSuspended: true, + isPastDue: true, + members: { + select: { + userId: true, + }, + }, + }, + }, + collaborators: { + select: { + userId: true, + }, + }, + }, + }); + if (!typebot || (await isReadTypebotForbidden(typebot, user))) + return notFound(res, "Typebot not found"); + if (!typebot) return notFound(res); + if (typebot.version !== "6") return internalServerError(res); + const block = parseGroups(typebot.groups, { + typebotVersion: typebot.version, + }) + .flatMap((g) => g.blocks) + .find(byId(blockId)); + if (!block || block.type !== LogicBlockType.WEBHOOK) + return notFound(res, "Webhook block not found"); + + const result = await prisma.result.findUnique({ + where: { + id: resultId, + }, + select: { + lastChatSessionId: true, + }, + }); + + if (!result?.lastChatSessionId) + return notFound(res, "No chat session found"); + + const chatSession = await getSession(result.lastChatSessionId); + + if (chatSession?.state.whatsApp) { + if (!typebot.whatsAppCredentialsId) + return internalServerError( + res, + "Found WA session but no credentialsId in typebot", + ); + const from = chatSession.id.split("-").at(-1); + if (!from) + return internalServerError( + res, + "Expected session ID to be in format: wa-{phoneNumberId}-{receivedMessage.from}", + ); + await resumeWhatsAppFlow({ + receivedMessage: { + from, + timestamp: new Date().toISOString(), + type: "webhook", + webhook: { + data: + typeof req.body === "string" + ? JSON.stringify({ data: JSON.parse(req.body) }) + : JSON.stringify({ data: req.body }, null, 2), + }, + }, + workspaceId: typebot.workspace.id, + sessionId: chatSession.id, + credentialsId: typebot.whatsAppCredentialsId, + origin: "webhook", + }); + return res.status(200).send("OK"); + } + + try { + await PartySocket.fetch( + { host: env.NEXT_PUBLIC_PARTYKIT_HOST, room: `${resultId}/webhooks` }, + { + method: "POST", + body: + typeof req.body === "string" + ? req.body + : JSON.stringify(req.body, null, 2), + }, + ); + } catch (error) { + console.error("PartySocket.fetch error:", error); + return internalServerError(res, "PartySocket.fetch error"); + } + + return res.status(200).send("OK"); + } + return methodNotAllowed(res); +}; + +export default handler; diff --git a/apps/viewer/src/pages/api/v1/typebots/[typebotId]/blocks/[blockId]/web/executeTestWebhook.ts b/apps/viewer/src/pages/api/v1/typebots/[typebotId]/blocks/[blockId]/web/executeTestWebhook.ts new file mode 100644 index 0000000000..4d8b942ffb --- /dev/null +++ b/apps/viewer/src/pages/api/v1/typebots/[typebotId]/blocks/[blockId]/web/executeTestWebhook.ts @@ -0,0 +1,82 @@ +import { authenticateUser } from "@/helpers/authenticateUser"; +import { LogicBlockType } from "@typebot.io/blocks-logic/constants"; +import { env } from "@typebot.io/env"; +import { parseGroups } from "@typebot.io/groups/schemas"; +import { + badRequest, + forbidden, + methodNotAllowed, + notFound, +} from "@typebot.io/lib/api/utils"; +import { byId } from "@typebot.io/lib/utils"; +import prisma from "@typebot.io/prisma"; +import { isReadTypebotForbidden } from "@typebot.io/typebot/helpers/isReadTypebotForbidden"; +import type { NextApiRequest, NextApiResponse } from "next"; +import PartySocket from "partysocket"; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + if (req.method === "POST") { + if (!env.NEXT_PUBLIC_PARTYKIT_HOST) return notFound(res); + const user = await authenticateUser(req); + if (!user) return forbidden(res, "User not authenticated"); + const typebotId = req.query.typebotId as string; + const blockId = req.query.blockId as string; + const typebot = await prisma.typebot.findUnique({ + where: { id: typebotId }, + select: { + version: true, + groups: true, + workspace: { + select: { + isSuspended: true, + isPastDue: true, + members: { + select: { + userId: true, + }, + }, + }, + }, + collaborators: { + select: { + userId: true, + }, + }, + }, + }); + if (!typebot || (await isReadTypebotForbidden(typebot, user))) + return notFound(res, "Typebot not found"); + if (typebot.version !== "6") return badRequest(res); + const block = parseGroups(typebot.groups, { + typebotVersion: typebot.version, + }) + .flatMap((g) => g.blocks) + .find(byId(blockId)); + if (!block || block.type !== LogicBlockType.WEBHOOK) + return notFound(res, "Webhook block not found"); + + try { + await PartySocket.fetch( + { + host: env.NEXT_PUBLIC_PARTYKIT_HOST, + room: `${user.id}/${typebotId}/webhooks`, + }, + { + method: "POST", + body: + typeof req.body === "string" + ? req.body + : JSON.stringify(req.body, null, 2), + }, + ); + } catch (error) { + console.error("PartySocket.fetch error:", error); + return res.status(500).send("PartySocket.fetch error"); + } + + return res.status(200).send("OK"); + } + return methodNotAllowed(res); +}; + +export default handler; diff --git a/apps/viewer/src/pages/api/v1/typebots/[typebotId]/blocks/[blockId]/whatsapp/[phone]/executeTestWebhook.ts b/apps/viewer/src/pages/api/v1/typebots/[typebotId]/blocks/[blockId]/whatsapp/[phone]/executeTestWebhook.ts new file mode 100644 index 0000000000..86fa7afdf4 --- /dev/null +++ b/apps/viewer/src/pages/api/v1/typebots/[typebotId]/blocks/[blockId]/whatsapp/[phone]/executeTestWebhook.ts @@ -0,0 +1,79 @@ +import { authenticateUser } from "@/helpers/authenticateUser"; +import { LogicBlockType } from "@typebot.io/blocks-logic/constants"; +import { getSession } from "@typebot.io/bot-engine/queries/getSession"; +import { env } from "@typebot.io/env"; +import { parseGroups } from "@typebot.io/groups/schemas"; +import { badRequest, forbidden, notFound } from "@typebot.io/lib/api/utils"; +import { byId } from "@typebot.io/lib/utils"; +import prisma from "@typebot.io/prisma"; +import { isReadTypebotForbidden } from "@typebot.io/typebot/helpers/isReadTypebotForbidden"; +import { resumeWhatsAppFlow } from "@typebot.io/whatsapp/resumeWhatsAppFlow"; +import type { NextApiRequest, NextApiResponse } from "next"; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + if (req.method === "POST") { + if (!env.NEXT_PUBLIC_PARTYKIT_HOST) return notFound(res); + const user = await authenticateUser(req); + if (!user) return forbidden(res, "User not authenticated"); + const typebotId = req.query.typebotId as string; + const blockId = req.query.blockId as string; + const phone = req.query.phone as string; + const typebot = await prisma.typebot.findUnique({ + where: { id: typebotId }, + select: { + version: true, + groups: true, + workspace: { + select: { + isSuspended: true, + isPastDue: true, + members: { + select: { + userId: true, + }, + }, + }, + }, + collaborators: { + select: { + userId: true, + }, + }, + }, + }); + if (!typebot || (await isReadTypebotForbidden(typebot, user))) + return notFound(res, "Typebot not found"); + if (typebot.version !== "6") return badRequest(res); + const block = parseGroups(typebot.groups, { + typebotVersion: typebot.version, + }) + .flatMap((g) => g.blocks) + .find(byId(blockId)); + if (!block || block.type !== LogicBlockType.WEBHOOK) + return notFound(res, "Webhook block not found"); + + const chatSession = await getSession(`wa-preview-${phone}`); + + if (!chatSession?.state.whatsApp) + return badRequest(res, "Expected whatsapp chat session"); + + await resumeWhatsAppFlow({ + receivedMessage: { + from: chatSession.id.split("-").at(-1)!, + timestamp: new Date().toISOString(), + type: "webhook", + webhook: { + data: + typeof req.body === "string" + ? JSON.stringify({ data: JSON.parse(req.body) }) + : JSON.stringify({ data: req.body }, null, 2), + }, + }, + sessionId: chatSession.id, + origin: "webhook", + }); + return res.status(200).send("OK"); + } +}; + +export default handler; diff --git a/apps/viewer/src/test/webhook.spec.ts b/apps/viewer/src/test/httpRequest.spec.ts similarity index 100% rename from apps/viewer/src/test/webhook.spec.ts rename to apps/viewer/src/test/httpRequest.spec.ts diff --git a/bun.lockb b/bun.lockb index 21e90c254783f255d1ff9215bf105022d4fd7338..f6318c3190c9096dd471fa3ad80606a71241059f 100755 GIT binary patch delta 226091 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≠t$x zg13C~&Y=rZ1~;3F4(U*|`%-&c21>c$#`6TYXf6cG(3BlwJ0TgSP*56fepoZi2Bl}i zh!?>!83HNL)s9^%C}aM`*J{o6;4)>gvH~ zTU76OABD@9PW)Cg4g+P(dxK{BfFf|tcdChv7UL0hWXztjh5TiM$=|Dr2Z18!S)i0F zC_S_HRCa=kj%a-q^2Z|N2Sq3Rpb^0bn&2B2?--npRaAB*AQ?`zm>Zu{G^a~h>HK1zeBqZL z{Y97KRiGR<7J#xWUkJ*1CKD9DP+pW*kXzt5pZ`}?zp%L6{Hi|pSDlvkfue!nNXg&$ z;I(i?Vg7>ObgXB~<7RoD`TxUX1FTJrE8!!NDVsNm`jCrmhh z>$paHJGGhJeo~W%7mRK+WZZ8hlQX;Z+4f%bXFrci|6<9y>z>};X#aTNvy>evRK`@fuiWq!NwH}4;O%f3&CEYEv#VQDOB!I!t+ z(&pHYy}S+M+g7|?`t-4PC-ikY9r@_H(q`X3{@&Q3cYS?xP47u(-#g{T#vy-O&frt( z`|A#WvfEiaX3bg4TJ)&N*X8^1qj!(yk^y)IIOsyRRxc zsq=#iCXBlFgxfY;o<4u=%xLa6KacbJ?K$=RLANc>ed4W@(YcFDraYJt9{=TtI$0ZD z|1^Ed6(zera|RWxN$In=Q^l*(GNV1C89NTlT-5rJxhsdgw|hdye+Hg({|l+>UYmFE z)1PL3dgzG}cfJ;T=K9B04XSx#>b#oAzwdC%cVDCyWL(qkp25?$W#pc4(rNd5%Wl{e zJ?pOPXP^7+$J_nXSCaet%hrZnZ{PCF$XqO`r%2Am_Zx2?{~h7ASe+TV)63$&-*{_QXS%I%6KB&z9lvH; zIL&LZCNuQ5m$fD{+~7p7YE6ba5~J@+da{@LVpSszOZ8)J4NQtAd+|?Wk(PMk z-d@h?boU%W8IsGdIW3$Pg`EjY@;0uCxv#;by0BNXCKh&^dR1#PLKk}b)@Fuo@>;CR z3_b5;3f5x8A;6Gb5upY7F!a z+?pQQPH38h+%}vdq`r_>vnm$75GH;02OLN;?S)o)EjDDj{~=y%AmpX4kA((%Yc^y? zm!7O``SDWC>+T{(q^;v^^kd=rt-S1w8SW(fp*E1ZD&}s4sk&$`@;)rhYvrehk8kZ| zZ_0?yz=?`t679Qp6Ejd-W=wvBiF|cLTDJ?XS6EoubEm`7VGT@AHo>G^!fM@v#E5iB z9)g6<@Urg6jGWG)E6dBdBRz5jp@}B+0iknDsAD_F8D&BxgvOiDbA-m2P$Q1~CSPcZ zmvv`mqC>-Lg?3ccd(yED^0fmEO!o%j`ViMx1eGDYD>Izj z!ArS2BQ)I0x;rzp*jvMY8MC`HBfWV0!kDd353TaD?#Xmt7EWP|<<+z(cVyBf2*x)O zChAajWrD?br+I6(WV)?R(@7fgQ@dceunSD?_&u@ccd)-q&Yf=bbo~@( zW8rn3z3iM;zShNBrQ@M8dpCd-5hU0C#FBbg`W?YXLuRm@^$c=Va86$qYA=-gS zd8YM-nEN5j>KQ{z>A|ntNDGT0xkF(xAW44e%urgWhgbDrM)Y?g|K|O|LwkBD4`qZa zdwSUqWkg@-sguGV&?eH|e=|SqMtkW1Fn4yP386`m6*sc1mzVu;hWi?kA_Tq#SA~nF zj>_Y_jmWSQbCR)C)|3fW%Zgyw2mL27fwr-c$mNZhKO<7(Ju5~ z^k^oF|7QKL8ye_kKb_&OCqW8Q?}}LTV;Iw_c1}eb4ze-JJ6o?lk(kNkVX>OdVg=KQ zfnRW&6GWk++coF`)*JD9dCTsMMl$e+t<@h2eYuv(I`-%g9hInPDl(4s?`-eDmh|v- zL%r;0Gu#&>f{K%j$8d&eW`y1p3r`y6r977r+U{jNm+97H7E4E&sCUQQ{xF@IHGiZf zK{C8n+tb}wZKgURrW+oigNM-6Hg1Gh{d`9Bc_L*hB*rSzoWWq5v?<-4M@Y;Rn_-+D zu`F38XQbsQW8wFtN5@D=Y6vtJtsq7g_S%@Yi9zL+X3f-jk1;ZvWJP1Dj~?x1znBr; zJld;zF~j}DrX~CF57GP>Rf#K0NhA&%YE0o#LNX$B=g&kY+{lacZLF8FC&OJaR!gF5 z-0%6Av8dMUYusfp8Ns?{&-0#TYJ*Lf@nWs$c6BUT20I_7vM<{jc9V%Q(|7SSyE}=F z4>TpCIutfgWQ;5&)Yq&Z&l8f?qkd{@`XuqNWIuHS41c4th@R)cq;=*J^Y(d|^wRND z)5v+Qw&Qv=TX4aa)iwKoD`9FnXf(9b+xJ?gJD#DX0$Cb2#KO0q=Via1;kpxb!eJF& zS`=b8VK|vJ6TOr-GTaY|G~$_oKb77a*Wt(CWQBmKTak&d5hlaPR^-1XNk$pteJB<_ zeUex8W=3Q}7Jp35TmNQy2BA9>p~z&u^qSPE zCX}-`Go<|F9R8e*NtvGry-g^~#GP?b5W14kBonuv(76(FyG}_+zcuDggW*YMM)gPMjIUWhX)#2n{y*4kkjKW(9HbM?pED?eCiuM7jSV zB%MYfEJ>#q296|kdn~#LCex*MbN;N2F}jX4n=L&$@LAs|0poxCnyW%-Ny5BVtI|Vn zd22q&j5r8JA)9U5!Su;u9miNb)zn%FqQ&EF#)%uFV}BN20S6F;*b=USgyJ?7w#47+_Lc zSbV$NqD1Wvh9Qh9@v;wQxNj2443w$Lig8-0uIp^j*To_$U=zLdYtkc!2=()F4yK3u zlzCO3XN2dKdDWk1xX+jA#_=Sxr;7MsxbUe;Hc(Mgy8BR+ilce(Z!CFC#mk; zgv1#e58!?ZQ;U_wuK5x#``_$HmgvfBy6FB2GxlhPtmhS8%C{Ne@+-XTZ!;nKWqF=>2CAlKk5vufZzDp0+yV^_nJ|i;!YW^^$ z*XsN9$hU-wB^1uN#;Ydjm1`Vlu}K=e)N!u#4v^_hLb)cc*R^uOmbjIKii`^CT*tE_ zQ+w%kUeym7kxz)2Wa^Av@1^{h5!rM-O7dHMfog7G{W0h5^9ZqE+u_{^s~zlvFqs^z zc(}uMH*)(9?RR|3%f7f?Xb*o1|+Jz5jNRW%WQSOf=z(cHNOA) z<-w9mTIgpl>u9E%y~56Q@$lhgE4=EX8SY*p`%=stTcgK&_FxuzTdtqjDNZ6rO$a@> z%VFn}+w8pE-(k`uyC>F!vA3wJ;q38XTCyc(PwX#Sfu|uxDl#ioIJ(kH`7I-wwNhI* zM-bEAN@BW`jr|?&>k}BeM_CM6lsc`l4oNS^kZu|=Vy_$q@gKJeGbe%f2=zAQ*mFhd z`P69?cs8Mt#OucPW|)k)c*83n6>F$Jyi-{T?hu8eY z6x@EWGfAuCr?v^FCBc}h_oTZI5|R#?ODXqjn5f&FgWZl*ftabx$h@kR*`W;gHX@~f z*#Nk2*erOpgE6q~=;@eUm88zo@dexCCP%y{|4ASSK*OV`}Vu(75A*^q95 z^@C{-55a0J?B?})mNk2V&>=soPAJno;{lPw1hqH67NMG&b2td^=1O#IWJ^_rhu? zanp@zW#+8nj)V0iC(UsjUIH87w~B`PMhW#No+AZ!APqM8*~y`d@UTsO73fxO`l}Aw z-TqhIT?!L}tY=oZy|7GEKb?=Xxi!HiCdlJ<-||=G zFNO^!&8XYm4Xdp?^WMLD#Oq*cH8psZ4`8)9)9wqF6>)2eVB$7#Yp^F^s-M)y)BF~V zFvqjE>Y zXtm~vSfm_wwwLp4dT6KLqA42rnTUyGW>A@OlOE9?ag8Tib(kzu%xHFwU&F*r;#?To zHjnD$<-p0U+a#ED;Y71&O@1uc3CT2n9yWor#Qrwg^l=@u#Qw*fMvTl^X6eCLWG9U6 z4qrHq5gILJgI%RN@(GPGN8;!zm?+Dn`^#AWl8KQympB7O&)Tk|oMs7CvhGA z6fpyd;oAbMU37mGr1g)t$Yj78f#r8U;}YYH(Lq_ZLn#G}E)GV99+jc7GYhBqN^q~5C{+c$S%+T-t zJ|NoTX}iae8!K}^6(z>zW}2b3q`}`52 z%y4A4pK>ZihVE9ap^K+u;U&BMDqbUHO2gvo-z-e!Bu zJpz+yqw~4ji&XBlI@mW#Kx`2Ax11>p6I%)S@f=3~rGVA+V}*uEbqQbX{)?D_Cilk1 z=;lvoXQ;XJ3ZJpZukJ)QrtDEuz~C_2l`t7@9sJ!eX^tD3Mxj{5eHjtWl}|rHVlie7 zch|s7J7&4w4-+?>xVdy=uV^*8VqOaC?5$s$9(jn+SaX@=yo#kroSR0-*uTHA4Kh!I zivAowenB`b^s1lT8JQ0dJDk{%zi~U;qSvUVR^_Eo(U`Gq1}&=#Zuu0K=LEHR!mn$! zW`hy#|GJ;jm9e`u!XNCb&2&AP`cP5CPi=`PZ$QOeEA9078;*0SP{%w};J^Cs2I9{k z{``iEpIvd&8rR#kyt&)+O~)CLDC=GdO;6CsW0LA0=*k{~P`^Y%c=%g>c6a)F#aq^~ z`0;k3SadH`x?eleE#KDhz_9Snd9XpGv!D1h7QX9kzp6)Qk^7U))6h%Z6LWj))!B+C zV{X0#6P>A-KJFd6gY-8p#>!x-Pgz!%!ep$~kGu@)4`Uf$7js*`tG#smjVE(+|E{0X zn}zWMKdU#BY503ujc&JYf{AOu8ElP(-tt@YVUUi0pBXNDi|FNqCX-y-!+pdI(UzWz zMOuHrrouaLG~Jy`$VlL)%Er+$?N!%(dJMvN_AozSBc;y7UDZp(h05M+9s&u!U&}dVqY+;^(>G-kM zM3O&3D*wP>q$Ok~imb(vD_{dm>E|V6+L1}$*|UcKV<;s|4xnWFZjfAPdD=+qJVbWYP)X^ z2n~;%_Nn+3KBuU}^FuAYAdKpFIqR z^@NFGag2UXNK8yOIE}vK4K|!~J^vkoNp4;6TO3xSV^J9#iiHaf`za&n z=N*UrY|#CfWEm-r!!@zUz^~0!%$D@Xql9Aq`oW>}$TtML%OV}_^o?IN63OoRhUx0B zA4%fz|7O3d!QljL?2QD6Y3w@$xs$plJ<{~sAU%BHw|@3$T3zetNhBnijGtzNSqWDw+gT@E1`D!t@d1M3cB7_sg>Fp;{hpE@|4=IRm&!o9u%(@KRU|3|mW$BSE!&V@~&(pm@h95FJD zo0u=NNk386Oz-}L#HLsRd2nJ$qF8hnF+EL1_S>TS12Jb4!wHw}cm7%X!fL=5-N`VM zJ8@xr)6ag@d7)#W4<27LG4ymO-07FcvnPcXg(4TC)W2+gE<381qxIz?#eD@PLfMJ% zUpWQIv1TwK(N)-wug3#grdqu(NHfa$1SXR3Q3&O<`Be>^TjDKvci2?YYIpca#B?{e zC5?VlHR&$-TA29Qggzqsh`B(VPY<>jrr7#9t(~!axb(Q zHrh0MgwSx6LKaSA^L|x`rR^EsG_$Jb5U)Ryx01H zn1n6(Vo%9o$_IJ;H*W};n3+M0+=E5y)d}hMK2vj}m)s;GvQ48GC58UlD>CmSCQ;mf z^AakI{4>u|V$T0t6CeHGVtUsN>Bt1#39qW_SI5~I?DJbp#b-854(Wg-4sSWcoJF;4 zFEN}|und^akaukE)@D!YW=BJ2Rc~!h1*f=vRW813k?U83?oKykY|a#o9)k@PY0VQ2 zQChT~{wp1(k2z6Qj#!Ai4raQeCh!|n=7P0|F(>^k z^?B%dwW-aUYE7Csd~H)dyNEs=CVH62XC4p?Xy#W-?9Kdvyi8)*-mQ(fufxoCz)z(Q zk^0RsOOewZK}hz5>OB|3WFlH|4nSq2X6b1ytgz-o@}({O>Nzy@5V3y|vTn*>X*fH@ zubRu6l~y;g`I7H;x25IBpDljrP8eCNPjV=idcc-4Bwu|(*sz+d* zMGSvqXIwr^ra9X;gzDBlAvddP0ZeQramH{r+8E<*+@D}tmbEU@<5b4MUti4GL_$(7 z@TTr|V$}L*C3*yA^c$pyyLIqWE~f`GI{4XwcXsfrE@yxLc?X@Kb^My6oYp$}*^B7k z_Ku9LpR`+?dXK0u5(RU8f%qQAoW8>c#~i^l7TVlgb8T=k6O zdcg2n-Bi=&j=)_A6TL?KjV&2j znCRTx*@eFI*IdbZ(XqP@8HUb~<-+*7&%OLz5-+cgqMs2;7{B>4mD(eiD&h#o!=$6; z)l2yL9)9-K80>$Dokpy=%5<}Ps#r#;?yWGXQQrZ52ou#geoYEH`9{6`s%yA&9Mns_ zZDO+#+U2)cip0N>U{)(XwGZRar?+;1s&W2vVdB_f+$lV0(?WjD^Px09>spF;>Z8S( zF}y073KNU1^)q+d7&9p&2VvG)+MJ0eRco0{FjIqTYlGDU&(hcvVu=hB2-;#d6V%GW zFUS1qicn@!TEYS^AUH}(-%jvC4IU$CEql;eA!oeC-clRHhlb`8rIK%L=?D7P%J2%^@$eb`#3* z4>Sn(jb^I3>UG^{m{SXiW)@&o+5P>h zTaalL(dI_R-*_c5?e|-(6q6bdGS{nYZMb_XALv)DB=2Jb6Z-(1=pBEJMAjb?%t-N5V2q!^kxa?^nkqeB4NTI<5@gZACCWMVj1Z`#eYlA1}C&v6j6I_2UvxJaXvTi!Qf{8D} z%Gs(mn-&xZmrpZVXC2Ran63Zsq*&#jH9Zl$j-ZZoWN&S3qZx?|V`_uf5yWpve|Ogg zoq|McdTnqXLG1DYRIVW@^(#T`uA4bC`i=6^{bx5CH9|d9ZYb0=FTBTJ+yq$+Xgc(#ZO(zEtX~Kgym;|$tC~T zRYgmQNo*qhMk6?H5fh{3h#&9GN_@;;^DkD!@5@6@p@~gB8;3nld(C%-#>!-f%)07! zny>dd%9g?|3ex=5PlpD}3uSwVxpjdaZdI|r!m<--=Pk6%-M<_oGBk=7xLbWoiPP_&D;ege6pD z_SqlV7;{VEo_u+bJC%CpB$&J(dL3pDZAegW(O+|3WEqPqTjGacVpgh)#uX~7z9~5u zrfG3{wK>70SHHl-P>_|hA(d)!Fw$;>iAhEL_J@&~OKe4Ny27spBQ36^XXYS2i;#>C>(16#c&4uyOX;XUm@oW9;*O;h3 z5hF^hJ&Tq#d-6duX)`ZNhjz zurn6EXqlh#7CJ8HQ^E8=biZ4pHcB2JnEQd)o3~_s6*|9AEBN zlNx#0b)06kTg+$4WXwkPSCIoNLe3n2{T}RajAsp7kBqlLt&4mu=6(kg+0~hDxh1eM z_D8%Z+xHIcz4c0z>K}NAC1W8>yg=p9n6h z4Zc_#Y_vL&A*(jHsW$kv3Ff><3u|QMHq~BQ8+^7l=vE~%j3daNLqu3v8?3Gkwpklw z2-_BfOva~(k%^*bp2+&(c8>9hOof@}`z{-Kvu@~S z;7u5NN9x;oYsgt-LVa%wIhUEx6NJob?a0X+FhKvn=k$=!U@^sTuY3KJkC17>z1;Bo ztzO}PcwfkwjRZ`%<*~?m7;kQPbNCsdT<-wC<{i4#&;B@+(dntJA@ga>;a`aVV{7K$ zJ;a|t4VT`LIr?lbZ_%nRLoJf-4~+kPLJQ36@e8+E2WM^-+CM-JN|}3 z6|B&xD*Q;GzDh!K{8mR$6(Lf2Z$A0akW*&zy-8@MpCgarAG32cvYHSxnL%jscrcSI z3C)l=j^|mQWAWpjpbEd06eM)Df8bL>+iA+Ug&l;<&$c6NcbKMV^cF%oqD`KpQPaR> zgm5G@^#dUlY1&gEXQ`j_74;F)TF(1d$hpqs`-spC|G>AT?iAY-*YgA+BV45I(}9b+ zlaQ9^zKbt4#(}LMWZLC%SJq)VG2@w_&o2;CBOCZ^$SF5(=^rOlX&NnlF668*q0sYz zMv4hdH~HQpbgL1hYlK6L@1 zLQ~&CLaMTgtTQdudtG`c!*n;Hr%YVl8zJWw z35AaO`;M@@UH7JLWw}t1S6r4Ery0q8i`nlVh%qM#>G3<2ZNgWup0vvh<&vk*+bm}O z`Vkx$2uVB#akdXn!DJC*S?4zTPs>8)wOG<#{D*lhSxl%uxe`lVbQ>|!R&C57V*a)q zMpNI>#Witck6uBH$&=U-|BD#e65yk_eW=F^pr2Vv5+UE-}U z^Ig$5S3U1TWhc(oYf~)T@B=^Pzc`9tK2ZPb`0v z_q)6;Y*}rNK2@xa7Za5Jvv~9Nep9ty{VThg&-@m@G3+PqS2?MP0X+|HW`Zp#RSs~0^&%aGbdZ^1< zqmR`sV~p3L)^ng*NbdA+hViuJ>vZ=;LSoN#{CEfE222Jz?AO$XePa9N$3LZt1(q6P zb031qV452#_sf9kGr~?aS`S(I!9yNQ4icOzx5wPous*Pc<_9OQ!KAb103JQ=Q`^~u zg%2l2%ChO_NycR`k=C9(_qj4%+f$0fbEb={=43LpH(&Z4%$oul}^h17iayY}igBa<$*_lS(gH1KN z{dz_W)Wa$}zKf~40(OB`PcIL{ zq#8E%=%e%3dS=(5UICNSFM9#X?SaW>XTIoD?zC^%Ou`!Ysh9C(8FrDG2`&GvyI5*? zBQ2UB6p1_yyTD{>_HANsOI6chaw@`E(9DxCQF|S3L%BNpNwS)q$&l}Y-J3jJ@L*=5 z`l+X4v9CbIoYe}Fzt_EeJ#XW)X;FyuS0~X1SXV!1IWFaOg0+(|a!gW;c%6^v0I09O z2qxJO4`FYD$-W6c1p6I!u`u7<1pL5ypGf-xW;$ZZwf#|#_gd~Um~>g$n}18|{2!L> zMA|CY`3d$7EL+(tX-PlH3NG&FPC~Us8vSgAG9u1_nF{<=eryyCm`OYN7kxmYVl0A< zqjD^YuDuR34#JpFkE21uGHs&&4a&(y&r(>P5o#lL>HeDV>f}5Zrf(G_=PH<4CCsl$ z-+)av<)qFte#3;6y_6P(%n;(&B!^>eiJTw9L};!%cBVz0 zuzl}AmC+|)GB~xXX_HXcn4Zc#6DE!52lyX^nXgG_hQs!|QVF4vChiqN`WFit*9jZF z;L#%7Tfp?2((Qz-DWLT~VA35%5|KwGDZ_Q~dzNMq>LN{V1l&1L$;u9ZWp^j+JoBik zN!_qL{-dqPRAFY`Jw!+bhE7m*qh#_Mwvp0#FkXGGN)O+k>{qwOdHqC;E*8A(bkB%} z&1DV3|Hvu>6Jw}veopotOg)d7TXQ#TE@Z=g{2IQQyMA_C-WqKu)_g_wH#X(lXp7V^ z56;;*{^coLhk9Y_O%X3VjfY05;f(N_dVWf~a0buXv)Y9-!wc&BRZ#bX`eA225~#tS z(;#e4hpb)nWi^aHV01X$bsM5&axwqeYxQf$4;d)CkXSJ^Q#SfAOeTx2PmSJkqsOT& zum_>z=fKpw#7(S+$%>V1X5BY1Q3KU;&(iI9WmLb1MdL4L?Ia}XVY4J3&zweK zJ$sqzGhs53ko*poYnVKrsjt7(cn&IuUcNTo7mGA#EH%o{QHK#4MZ7w~6|jE(fyX&& z-+WS-&kyT6voj_nU+frbu1HFos@P%UibLP}`#Q0<^lKJ2D|c{9pVs}tZLXKpVsEX^i?Pwi0p@rGQY9v&;o;!Tz-5Iy`BS=p0OD0W|e`B zkbSn>IwkDL#V|9tX01$;H1oSb_jEfg z{M25w4>L9R@mp(|s`n^qbB(0&m+1W&iQ4%b{EVNKhW~2NnMgk;EzDoXBdCMB5fwvf zEu>x-ZPHX5p47#!IxCzJSwSqnGhh?ES3-$yoY*yB8(E{a!HiY<8$$@uE$rk6IU}X8 zVP>lBAY|sco(F1~*hTlh%JLQLoS=qq8jolCBFo|)VJFMa>5A7Nq~?IVMb7F;y`t*K z0u3SU;}QyLaDPrP*`4<3rE^?OtHd(?jL%qPhfPeBYuh`K6YI~ZWo$01gqc>~*AT6C z>=P`5vL)XLn_%h^9YxMm=R~V)OfoIg1Jy#9m?>#b@Od64K8ShzYFbhZ;#SO$}H9ICuq4w%_`cRDL@31a`(!c0!%sGf(3 zyT=%q(0{;m=q2Z+XKP1z;zin{FgbFu4_+4w9ro7@#$(S)*9ppD1lM~HOpKh*DM#@R zeRUmUKf^>D2a{7ylAoH&=KE6jZ%gf+q>3h3bsl2E_S2GP+`<$3`Bg(%cuQRW+bipy zDR+$FYk6xkjJOXK-P<#Cjb}kX)Z;U?JZ^(O|1||>?ky6(g5OMxoSxa(@%rv9n7EbN zv-C;*!)D>fm$L>=gvk=CxjmS;mc)0<=mBC(`NVlMbwIFFVFwtQ1WWS|EM{N{^)$!q zX9!81=!UCB;Ne)g&9*b6EHSH zCpwEPYsd{SsfE{il=}=OIXR-T(>-IP&J2u@Yl3MoBV9s*TZs{wxPoBQ|F+F-ey|s9 zI!f!+XX=eo`M^tz7z&#iT*?N^BC7cxq3SEo9<7@fb@GKUX_O5d{_74{H&_Ex$Q?aK z*CS5veyq;zW5Qlbzn}XxvAVBGy%x7NR<|1U{Eh$Omf``ZTnHrGM)Z4PjAbS6Eqb4$ zhGc$j9-Rj>=?QDzL5!SIxvG=9wV$9eu66wQM8;uUFv)9}cvry00nq1zCP|d5PaxZk zR|O&b_Lw^vCVM+PAExp!Oz)M1{g}u}mC^r;z59>Pa{mASzjN!Xb1F4Vt0coTzYWt! zFD7Gt8>SUu81p09i@nUx8Lz#}k1(0Xk}#Q;N``4kYP4DilWCP?n3i9eOv}&j?S353 z)yb!?&+GO6yx*VCKRYh!emt(v=kfDAj^jMev+Z!;S%2Ds=<$eNLjQc6P8IfT4!;Aj z3h@t}uHTf|?QH)F(8F5F`S*G%Eqm)>7yT;YosE_q@Z0_S@2+PXoqUd)-uq_fK`Ol} zeZ5}`SV*PI>CToN&F9HWGb1Cep!AOz4~Py<|DUW6 zIIrt$;ATDYqVsGueS&%8SaVx@QRIl=0!H~(T%)}&Ulu3k0^yMO){25u@jMIG?dI-Q2UeDe>@1#vHz~6$f72HF&yczQa<|3+bHZ`6L^Ci4PdULpqF}KohGkTV{gy`Kb^$#*G*StFCQ+bE0|CQ#m zlsaSh)XRzfEtQTXevXYtIRtWS(B)hqm)n@jxk8S)tn02#r}rW%O~_}OrNg6pT;BCj z!}}%bqbM~WUSQr?%c*p*aE|f~!>3gK2cG!v_~eo6ua5VbGWtp?y};h%38SBJRlwr) z=t3%;$pOngERYg;MdYM4lSf9MeuaN@^Rn`xaUqp9&i@v3hby~QDqD~HqtZHg7ofLYTcNEgS^jG{w39)+c{MF{KQspd8nk)VeS7IKs%Lx9_z)}|7yN;;91#$ z5s}4L+o16rON$_W%%6YA++n;uibNehKGM#+GB7gw_VNC-Y}eno2vKR9{qNC!q|*7% zJqd^ZVb}OQ`NYC4&3G#R^PKl9Ez`P6?{mUNDs3Kb`~1_wffH=?1UB!^306PZ|J?>%$FkqP&fops9b9nN>p73&N96RN9N?)qt|mI^+dk$KMUh2P_$^>`6nGd<@Egr ztLc4(b2ekz#ZeZ25$i?e-^S`?U4BDk#P!~K=>SMAh;-i=>MAmmitm{Daml_nM(Q6l z`DKto#&W&>y6UW_D(IRzc;QW2e?2Z~pPQLdZY@``##Co|^Z9}?p3lck_D(_N`mLXyO!INTvPgf6IUDZCyQiyNP~=%6*s6nG&M#-|%&2 z_bK$hpW<}o(ZCeC8|KlwI&Dry-a8l)mil3 z`UVtgMf6&^hh)9XkB}Al?-g6$xT9}ChSP`l-C7QLs;w7<;D9^WBJY0h62>$q|NW

s{AoD{36^xFE@^@INDf3xTvPtpIP(zi|i_e&p8>CMhR zo%l(wyRY@vC``I4?5qYK6U%iZX9N%S}*%bO$jG@%+Qy)xUT2;Akb zn{_?gG&B;?6kBh=qstJjBR3Pg7N1dRo4wnaU~&mtYHM%iA6*$!FYovWEv3@_+q$;V zF?ajR=9>T>1kj7>obdM0Qbb48)+s^<-t*^q+<<7mdbc&f52^T~bKZ!^eeShE|BU=w zX!5=OI&7Us^m;^F?A;|tr`_jQ{)KE7l|H!mXZH70N9sK)djE<3e)oSA|7I%PW<^;= zG3N=@u{Jfp_TKMb>iqNnc&e?J?&un-v0AFd-@&o;fd4xA%PF8b&ejg%!#88P)_JdX z=qD=g_4PlJ%!&>@=&!8*zVj@VcBp@w{R5R=1pgE}q0~Q^y=!pfBc(RzZZ0+Np}N#? zU%4kg;H``EAeTyCZm|ix5zcoNpQ!a!$0Pn{ zX#Rz|YIx*?M{MUCNtlw~{g}sB8I0-Umj4xEE0uPr_h}^h;3@uV&e6*i<`OC$XWNdW=}!D#tJ?K3 z|2~7S!#N|yQ0W^{Zdm!ayMRjPfp?Bs|6x2M8%l zWe4)&j-=A1i`CdLJo*DFy;*Rgz0RY7r`gytW^mCo|KiE1#Baw$KjlhpcX&=Hg-%9p zs@e1-pYFOT(7bP;(t>-z(e1;|mHxjvuud=X^(K|>$2fqw zt-O;;mtp=fvC|2z`b)m&!XGz0b)Hx;AdrShaVk^xbDqs~FFRoC9X~ zTfy?#rVFX`;ltnFC#iI3@`VH6;eSr$T|z9olw*DGXL)Dzp38VHW8O-8Cx7rEN=`{m zf@a42OH=lf++fYL)sJy|lFv`SP344pZzgAw4VlV)Q@>gMG0vA0+&y1GrDL3`YItC= z4Sby2?daKCH;I3388zD;eVlo`kLKCb$C<~d=lmm&t>pc264m8iL$LoG@82!x6U=6m zPYD`*jnN!`%12)C?>)R*+0c(v-aFm4$7sFiU&YuB&km2QdC>+{@G)QuzaYbUo%51^ z^WeXJcT?% ^y{Z?2>CcA#bV=Gt82&za?Lpk-6(u;q2&7aDs}X`8owZM6H#{@vqN zi5`#W9PR1O-%jNnw*GJ2P`Q_pFLc2!UFeyb-mFRW{D4ApS&7VyEX&f9);i=0{bw{CH-_!mCjcFMTa^1QZZ zI5%ga{t9V|EmXSwVxQ?}Oy;|d)YDF)@1@e!*1J?jze%-iW&6MC=56suGaZ%Rrg_e! z+O`&-cD4Cmb1c}}#x3iWRL8Ld|Hh|=O8=6$ZKt;({<`YiJ>s>lou}K~tElu6_`7Q^ zm0nN|Th64u3;pxjn{QEDtc_q}4<=yTgfH*FhDY3S-3 zeJi87u5**mMesSQ(OawFvheDBD*bDM-?RD;RnIs5+X}yJ%pa;*R2f^R`;|%`{`{V& zzUBXGomT~oD%pm-%xxKCTB{wrUl@+=|F(aT~Eg7x9|rK2CF)H%#;S|gWBDtF(-Ey)opyj$jFJcd(a^osBw3-1~)(avqN<`+ygA(hw%fXH2KLwPI2{$et=Q!b>5QsC9PF9xsDTg|ZfFdhBX{$9h%hNh)pi*3V(l8xbvoixsbB+8Tc!`uCstRC*b> z;b(hmsB~ESpCNDi$Uhx8y6)l|>PAZaBkR_We9>`h{hh@jGcP4Xp^LfyW$(jOf9$*- z(OUSQa*{soS}yNsp*$*0yY(}4W7I#CykDWNw=oTodYS{P~^uwO?_;V!DT@^w0C0R@`znQR(nvCp^yn+T|N}#CUrs zlS*CvxA~{3vQr{?eo2B?>Y!W*CSdjch0Vg4grS({=ZIr(ltTX zhriH4oBWm9cKx^((Zz=Ymw)|zp31wg+4}EUZHSIOe~x?q@P`G?;`wb|ThaADae5f} z!z}tWBRYVh!gi7OI$hl1pV<-K$C|$LD7{a}^yAQ*zdxO#LqEE=J@2E{DOB2E?}&{q zc9r)9QuIP9Etr$;h2i}oH~X)8;7<$f*F?i5n#G?RQab%FE&i8=$uwNJy(v0r7`MHK z%q{L;PX4rvzR%O3J8_P#h~2mjX%j=yKcLe7Z!2yUYCp28M$%+LP$q7~OLTnDF}d0`;BKP|eDcD3x+b=XF4phY{&`#P#`{4f3u zyQb#<5%Q$BT79$7uGeFmK7)F<??=#!gueYm* z2DUec0`&O*WxlU&S0f_-)Yp4>UY|d!w`L`OR(r51^7umTQFWi{4N5Mry3BjSf0CMx z~Xt*$|-85Z(K z6Wqxk4cp5o8@Wf+0KQg@h`5VC0UJl@ZPq<92Bqe9p9%)0mNroZ!@rZf)kc(gz5lJ$ zif_LoZ+8)I@kcB0w#((D2BlW?UH+*3J^pA=s&3?uc1II`G$^%SSMx{hYxtvKjkn{x z5w8ww@F(~L)^Yg-e>CBGmu)ULkb0;3+T}*)-??me`Mt|cq=v1@yJduS+0Rmj@YELm z>ZzYvkk&#g63|~Za!+1CX{6&QF@sX0JzNHzD>d6*t`1L8$hGg_vXASxz0`bm46p}d z8QIHq{O?RHAi>Rdd#Uze0efL}7ELSTvgE?fth9tNhSnZmX>i(ju!&CGUe<^ERyHfq~8lFCev;MWmYQ^p{Kd zVpo&?TY*4U?{bOjpwtA*7>9=eI7sL8;M| zt`5(g@D0vM{{;^NZwCMOYJd6RYvf%e`QcOOFh;t?X6-EqopzKSB16-}t8&(o2 zI*h+Gqr+V$J0Gq_2Bq3lTpsE2DA%sk$#kr%m4OJm;FHLnF`Di;ujUEjYPa#9L=H&R z*a?nLRKcJeLS9OWE_1Hbwdf|YKY0(?&z3VA`{dM#R-h!99>69Af zn`kfJa=F|kU)y@#=yF9Q=NaP>d{gP=N|&o#u5r26ieDbB;P(Ayzr(xA4pUB`1=N2L~=hM&*Cg)1se||}7#R|V;L^J;0bx^AQfz*T@&XsyI>dRVcMPu}rOI^wmTpgZzDfYuP zpZ#6CQhV+&lJ*NCQe1~4T^>bh1&$`Q>|;p&ix{Q9TbFpjQUTXa=aqUX&;HybZH^I45{jPPjak z5K?K+014zCw+E%0U6uGECJu2yRLd9GG!bb+gtdXX2qIy^O9 zz5a)^8tUmFSVHQsd&|{tlR8?OUA>CrzlgQ`rGB51TESMKOU>sCS1VPocm2L3wEYO3mndS1UF8qpSZaHQi>{@6TBn4qD((bkqbty9t!qQvD?lkZKBg z=%>7=tCgBhG%4S~)qP#PBdI~jd@hUF$w7GTK!@F&D|e*ckJJqJcOIVd190uygIv2( zqlYPN?V(WvlBgn%qEz=0YICXjXy+qI&E^=AGv~0VVfOZyk^Q|dZjN_tCy*NcDs>S! z!}U{Y8Ty9{4gX1Mx{RRv?81mQDD@&_lG@o>q*i(?sjK6~uKhCCeub;YyZTzPH~nrV zbpv^?Yk$DyBvSJ$BQ^i>Am@n|_?UwyNDbksHGR^xPb2jr&UXEjJ5w(rMfJ|ZQ`0YT zu2j9$)k_N*(G_7isTFA?wT3H59UU#ChVax3KE;)vx%`~e7Oi*vm7326S1UF8t*e#l zZ)YY)`4@|J*AkxU@dK`=EpCcmNG=;i;o-oO7k7ztGj&ORez5v@3I5znsFZ5mH-l8L0(c z>GB#c82wcaqxTpGgf$P4^3_>3(ymt?RFgP=9SEgHjXjOlpP$Tn=_Q)V1$P zYKD3sg$AYSy#x)T&n9&#J&#oTMWo)WBQA9vE+;i8bp^^Nb&@&T8nqz0whCy|<; zxtv03#UCT}YCPp!KQyO-^v^$m)^G-?HLP|WlsfufAw_krev?$c_g%e))E0e0YCfNl zTA@}_gHkKpsTJGB)kc*=5DUz5v11qNY`;B8L;u+^A)33{213QmDKQ`q-J|O(`o%ranpyV=6@=# zJk90lt{++Gy~||-!{|^#YQyev9hBPh50IL{gU*#&@FeFCJ6Foh)k;lQ?sBrrM_v1) zAzmR}X)6Kk!5O5Er8%zS^W-4vH(mQXq!!Rf>f~ADe4WeBNDWHOX9KAX_?Fa)Y;^5g zvk=gNH@OZ!xDMf|J@TV-rDpgusRaadoVe6{g05C-MMI<(9PR2puI@`}DC`;uPra7A z&_N5{mDGZEBej4%+=St&{==Myrxu)uEBAHn;fe1Uu^$au&;jaT-~15ieMXPSqtpk9 zTitX@UFYs`wNe{6k<|R}clm(J2VMIlQiD?c9(MI#r2i4C%ym?1LOq#HLwM>|@EKe? zV7BY`oXa_+Ua6N{T|;V6YDdl^wcYcbE48ERqW!BDBYwwvQY)~;byRA?2ItFMzUkVP z+B3_YzvEn~6EA~mDYiK34;4ermu#MC~|Go1~qz0vC+)1k6PtKK^ z;cu=zN)t2uRcd*KJzU4XN_7mneo8I47pZ0UC-ssHAonImxb~5x2BrERAZs^gA~XofqJ+C{OXu7N{H zrimCvYQc#v_a!ymA*7~DcJK)){*Mu_A=YJ_%fT*pCpCkiuHKW>pwx`^aqYuMz2XPBOd_?w!$}h?lZD{z@RfAT{9z zmtT`wfp17{!6s4*`i;~r_b$5V{m04c|6iN%zh}CDj=xyeT)Sj|%Ykl7l=8u(cI8k~ zdteXOuGDKgjMSdmk5vByNbR9RU41xt4s{8s>F;)aFX?^%P|An~rDpUnsRfq1e9ZY1 zuAW9}0W(MqN-cP%tHVtH};5rO)^=_^n>gsq` z?@j8E*q_t_4|aYisRbSG@(9;{l*_-7T3#xt4LhNb5lxWp8cuQbX{2`b>CQ7qEhy8q zk9Il6wVzL_|2R_pvR(Tnqz0uHnCtvXQvLg0tZW^8VT|4wQLOrbwVMMRaGPN@~0;cBIJ$aAjV zmbz4Iw_%%WMswVRf0a5!UUvP$lS^sD>&}&0;U%tK(sf8QIM`n5C3(}eho`paU0f^p zo@?J;>YbSE}Dhq~?2aKX)XY?BEoar;-|!n&1plujqNC7JMP88C*na*j}nV z+x5?J{VyZ+P3sNLZ$8{L+~V?9Qfpl3vWV0N?^05OGRh{N&N;00cv2-fjnwj~7zn@H`z-(7ovjZ(iTZQP$y3+nAWJk`Ii^S=G}*JR<~FK8#%L8*0!Cj<8D8BqhQ z&zVsn?=SCrz`a~YrFP*yt`1N6zPNVvfv#OCKg88aU4=$EKi0WYC&>w<);!%gS?IyZ zfHw6kQm@f@r1tIkuA@?SWtY49-$||5mGsvxpWvobs=nUU*Y|e^{WhT0bx_9}UB~d$ zirs|sD?AY|k=mk{NgXVUNbRA;r25s98kDM+xLT=t=`IeIl3L?8Roky2cTj)mn!{7e zYj&y0h18(bXilY8avsO;Am0#tTR- z@HH~h7M$(wT^3VnISr(C#CxPpwhvu?^v^}}QzlIs5vseWr+eoU(0C!~h$CC5f##Cq3p zgUhd6e&Z%o>g?O>>c2|uk)P?OE&Ywu3jOZV``03Br97aYkzo1Whz_nkuA#5j!0Q;6 zI*NC3?Xk|cmzwdeuKnLh{rqf1oSWdUQoC{x{gk`8+}%y5)Pje)Iy|*u`{CL%2a%fo z5Kr}b*&59a&q?%80%}PyvDyfrTjJlEMlUnWt zq)xW$$q2hOD{4T1U#{rSPyKw9#l15aaU)Hd#ZBtsQuWQQR;s?m)!R$8-|G6^MrzTw zyS&5soume(&eQvLc2}YY0bRn%Nlo~u>!_4JMQWQWUHf#`u2laSt`1MF(6hLvf5G*8 z$@Noeesg!$k;q7`>+p)pS4q8ii%1QBmFl;cewt6cn|`UQ8(c0U)o;10-yt=;Q^<(c zq>A zd*UqT=aL$f@+>mSPCGAZsQ2mNDoV}&YS&Gv^_f6ww%57(AFf>~zlqfBZXq@OtuAkK zSxD+MzK7JHR9!}DH6PnWS22IYo6+MmXoIJbn&H!=HuPCiLwIThp2M{Q&y!l=7hPTB z>RM9sTR>`luaTPHVp2Q!?Oj;DX7G;dxPsJ-J|wk()h<6KHG_4e`nS0J%=zb}X85(M zzacfB??}z(2j?9we{}wnc9A;%>L8$>n`ThTdyraikko>Expt+tq%Wxj#JKjIT|L0n z16>X#H7M18sH+P#qBS4pK&b^LlInPX^Z!a}0}f_-U8hpW>&Tg;=0D3%>vcg?talL1 z0knnHuEPtieu>ne)SciWQm<(}sTsdRYKHHU8p2Z_{62Q=>s-51d#;t#hJO>wL8t|9 z1hjykU55Zye9bsgSALhOqo~zC=v?W&G_LMN>K-!I`2gqPsXaT``QTmslOPOe{X>WU zyw>TpCkxPm_HheR>LuCN)m!uUFwm|!m|9zUh|45WGfE~kgA`JOQuPScHf{hn<=PQ{ zbInT4<`|d9x=eNLO1*+7lUm;lQj5tXHQl+SWiHe8JCf&%Uw<(b?7`vYJ%x5pCdKH=SeNNn&iKTrLJxuwO`&LHNQqzuXOclQY-%P zu2=aTKX)CzB(=beuH*Nl7W9LwH@o&ZPNdkpXw)Qi-L?nr97{-ov?ONMMgoZI1h zQ)<12x#k00eK4uXl3aZ_sqHw*)gwu*cPgoVqg?w*q>kXxqz0wh$GBRlQ!94>J6!8G z!8L@Z=wHtN=hB@z{}T(~G7vGvZNUF;7XI%#Y^U8j1aa-sr$`;DGhIKW4#8Qj{W(%A z@`m%p&X>5laH$(Ly+81tu^)B07ZgBY}se|u3QVZyC`4g!@sTKX1)O9(EUj$O!gVYN2bagNNf)GPk z`e*$Pu0g2@dy`sVjPvl+Wn(wzN}WAJNi8Vexl(6Gf~%F9?>;Vvk?Oy%ezUHJP27#M zQ5$xs>;G4&`5xi=g{M9)9_w7G^%_NLzUicP%$cMPnK2|^OhsHlYCW!s(}P6)k^gdP z1@f7p2BlV@Knt*`L)boT|IL)jTU_4i@-~-+q=xX+`W4~Y?qX8ga}O!M&-MSS)bb|A zakbIN{cZxKX7He^l{%QFlj>LH+MjjxOqa8qKj-Q>uC69^z|18zgs1kyD{)+HG~@ZM zgHrXYE*Cgg>J?hz>hM(mrOvmPYHx7uO5KrsK+2o6OSQ!-U5D+Z+E>x8UHq}@_wS?@ zu+H^YYKuQ5HNVeY7JlJ6C^f-)SBIxwg5R9~H)rAh)QtZyUv065H20@#T)$fJfAb+a zTCVr-9UTMX_!FtUdLe%_{2{v^qSO5l9q)u(;~yN|57FsrJAo$iO|@NH7}Lv*~W zf*z)$clGXv=yX3s=ihnAjy`O3KSam>$HMN1=x{J}KSW0#_4T=Dpsu3~-4D@mJEHp` zI^7S^Ifid{^aal-{pC_0UArHm)BO;g?uY0^bU#FguO#*6+5He5c0%_cJKSZbdAv)a;(FuQOPxnJ~ybB~x z)A4>{qWd8_{?CDSKSZbdAv*fS5QgrD=yX3s$G*Y1btbU#F=`yo2+Su@=a(dmAOPWMA}wmof!_Xmc5r`A1b_d|5t zvuU~?qT_#F?S6>PfAX-M?uY2;ezf}`I^7S^;n^MC57Fsub_W$@HIv4-pVK=%L z`fq+pPsDF?S6vqMbwc5+!0Bf`wa=Av%6gtw^z)@>_pJZtYhzZ=8nxx)8{#WcH&vc@ z^yukJ)@+{ja^mTyFFQLm?XGiM7AGFMWm3cV!LQFfz2MMYxO4_0e(U!eIYH~`h92L~ zeyh5?@#Z7yFTZ5@PoMoZ>XplfjM^vS>rc5(L)r$Kq+K2}%Tk)XHQ!Mm>eN7;r9Rj5 zRGT=aM<1I!BdWKZ`drUcTU!;?JMf03&Y*AlvrJq$gNf^{UC=4Wd=^-070&`yGlAet zV3}pi1hQrUHG;P+FbfFH26AQr%dJ{aD~O#9yldIBf!ybSdO@SbJO{+h0rH;%R#=^& zL69&9XtKOHK*94slVGLAKMy2U1I5n+tE^GbEJ&^f)>u(BQ1SxMDp+etF90bo0%b1% z>#Rl4CP;e`XtB~4f%2Dt4#8)Z`Vx>{15~~Qv|78MQ;=B$thb69pz38H_%g7;GF}F< z<^nZ>uPiVZ2-N~PbAfNHT2L#9tpzq(b}f)Q52zQkTg*Hl?iC<^9SojTl{<=@l~LBKCs0a1g)D}J{YB9i}) z$x*LGog2wB$R8pDk+$JATB9s=A<@GoD}vUp=xHY{B6?YcB4k?>(Uwt1>|iq#y)E!M z(Z|Lp`dYPOM~i-g=x5o4fd06Vk~Ac5LXZ6F9vqDIzfXVp&p2}yn3Ku3D6`M zVDU?U#HB#-5@4V;3YrDUOM$^wv=k_509pk@EUAH|?{0T1hFXha4?A)h5pSi6J#B+x zFH3!s*xM#660BXZkDc@uG0Z9yiMB}N9+`&(c+ae$3c9B9>wgDmvs5mxlR zHgttHR4~$#Rsbm<0A(wHW2{BcCP@1LNVU=rfbu4wLomuxn}GBWfyyQz&DsT>g3J$r z6RqMyplT%$TnU_H87qOT=BOR*<&{w<2Trj-GZ0!ukDO+DoMzR6T0!h8;0()N1>~*< z>IE4Vvl@t71LUs;&bB&1gCJoIkZE~qfP#;JCc$Wn{|HE23lx6@jIl;RvmkjbaGn*d z1xh{!S_R`Q>0=;e9Z>c$aG|vb+5~CqfNU#W2b6yTbOI0jfR)f}a9cSjMM7)@MMC;3^Az282Ebay|pbTeYB85c@eW!LmOGa$A9VL7v65 z0&!me`K`cpRwrl>BzytnTizEy!Fr%caD&CK2NK(W;`P9d)+lHeB)0)KTTvTOvH@rn z+-gZ1fRrzRvJF6?wFue-X>-vSe@T2L#9-3UBj*&Bh}?|^zisl|K;#I*zY z-vN`XPS7AoXa~wHuN^4(9%vGn#eWYZZUTzG2PRvipjnW-37BFW=p0G_ZGL7O101E{dl4xs!;phGatQhx-}Hv^SF0+rS-=oDmb2BurZW}vDQ2zCN9 zETa?1+5*%FW?Eni5c&zo*#gYAYC)|a_9tMDW&Z@^{tVO$sx9VcAnq3+|7YMus}nQ` z5`F<{EbkYf;8&nYFxTRL1rmP)ihl*>S)-s?ko+4k--><%N`41g1q&?c_o&fjDLvR7 zzf&)?7S(M%`j(~jI3-Y5R@#Go5uog#e8W-$)aj9QuM7b7)-LE2WJUr@ts)YriUNXB zz%t8-0xf<}w!3B>gR@_Pa+tWMA% zNazJLSza%oAOtiCR$6=rNQ?%GL%=F)6f_Hxqk%P66b+Q@0JI9$TG9?cN^hWS2VkAG z2-*Z`y@3`h?G2Ro0XhVqS!y33y)RJN2WYi+L8lK@D4VJMZkkt>U z5qxEVen6-{kkb$N#;OIig4q7RM$7IGBddu|`3&AbA(yXDiwTD2WAH1;1KSEReD*P!8U)k` zdRt%+5E=~R3%RcLR2|IzfXVVF(awc|(AL z-GL^-0E^!pNE`|j?+y&KMnSV6c_=X0iiQFudjPG1A(pfUkP;7+?Ewt67D1aJEgp!s z(s-bJPoP7vm!<9rr0)e(?g=DVyP#8$xfd|ZD)s`Z_6CA`1N&OW-au9YP$Ssi0trB9 zA0Q_IIMAvEwSw4vfP*c2A0T%aP%lWbm|;L%B9K1}ILzt<4T6M3AldQ~fr5R3Cc$uv z-xo;S4=COjIMNyg&4T3pfDu--A5gMC&?*>dN&5pS2LNUJ1IJj4piPi=0FY{>2LR;< z0v&=;mUVpjnWd44h|0$w0{wK&xP!B^?2z3N^i~vGM134pr@m4LU z6~rD5Ot9>uf!vXN7+idGk5hH;F%pRT8}Qfxz;#w9X!skGB>as@^8FoDa178SxWVF& zp-1AeK=Coajn*h=79<}F+-yb10wt+HtKe2kN(EAm1Iki?LTeGU3DS-OimdcFpnMe2 zA-L0DZ2Iv)N)aFMh~2jWfw^3#DyRwrl>B%B14S>8!N!O1|Az%2e`An_ES z_+((RH42&q$)^BQtmqV=|2%l#B*i1q=Kh zDOo_-XkekWjHX9h7IQs5i@DZWX%Nr(90^W1*-CZU>*={8F@g~-+>xIZwve#2wex{{2l0P)q>jVSfO36>v5{}zrM$% zcD2S9Y3%yzdz?D=+Wa0@2YgJt0sQD6VDk-K$Spwjbda4kTON?La{h&?Fdc@kK!59YApr zaHKT~ngz*s03)pE4xr>tpj9x^lI{dj{t1-b2^?cBf;K_gKY>&${U=af40H%aS!ywm z{x6`i7)Y~rL8l<|U%-i0@h_n2E+BXpaFS)*1!R=~HG)$tPy&SR269S()2v!hD~P=t zIK#5<26FEK>IE4Va}N-AFOYu^aJJP68UzXV0-2U~FHmqF&?Fdb@%I6V6M^FUfHBr6 zXci<-1kSUfi9pHyK&xP!CEX9CJOGs44_s(1f;K_g130)h_#S6IeFK-MInMsSq{CIO*`ft*Rec&irF3Su7yCRp~v zKyDdOFUYf)G9c~|AioT_&gujWf`mtae9L0IVm42w?GC9rdkLiFePPzqnh8~^MnIv;M zlT5dY=|EK#5Uc`bSVk3)H3O&-%(TD^AoMJdGXt1y)q+|`(RWDd|O zSYSzWfRyKfvN^y)YZ0^w(w+zEtn_)Hyc*~bykV);K>7dK1)3~xE>KVlGznH(d@YbT4=An$R#~H4|JdIdmI&|;@4#8)Z`YMpV0H}NwXtj1hryz3yu-+;b09CI6!PkHd zmhl>pwGgNgd}V=!Kxh$=vk>^kss*)z*hRob%U%TJ)&cc`c8jS4;$8>x>wryGCuk5P zybg3&-s?cY8$gp_v&FvwBrXPu-vG8)qo7%kycqb|iWUPU^*wg9FBbPWSvL*!K*|z& zl-1MYcWV)}3DTD6JBS|k&=TD&EJZpH-7GAnOL_xPxfBRmyP#8$*#PviiUy!+84z3s zL|eu(AnQ$_M$p>=ZvvsWfSfmhzE&-$6~w*;^t0@@fZVr%dO?iEybZ)H2lC$rcD6b} zgCJo!5NmnMfr58{CcyxUe+Ni>7bt!Q7-)@xWin3AGe2hH6RqN7plTfuTnC(F z8S8+oPkwV+lI+X9?n*)2fsr$D_R!(u)K;ywfNKLyUVIzfXV z;WHr9@;(C!J_nivqb>e(Ah8uF{u~%%je=%Daw~A26}18-UjVIwahCK2kg^^q`vSPo zS_Ex^wDmx?m97WM+kg(iC6?L-q;CK!+khNv7jz0THvpGg#Rj12OCb0qaD`=j31oc* z)CjJ!z*j)%Yar(LB-Imb-Wc>)#2=2AO zk3eWMknQ%XQ1R4pj9x{l70bFeg(>Y0iLiHL7O1$ zSD?a5e+9~a13CoLEcG`a{db`9H=xqm1)YM--+}2?@q3TaWmUnxWx*i#9c39oc1D1* zhH|C_0@R_%Ap0T!%(iMltspiMm}A+IKyDOJFQ~SdC?Ku}kRJuSXmx@HK|&9p#`1aq z1wo)mFxTRPKw?jzSZguQ8U@XQ1mgMu`8xtDtWMA%NazPNSzbS&pg+(gSZVS7 zfy5Y~xIeJU8U@XQ-gO;#sp5F`u&IxKGxP%s#15^T2k!9e0}K=EK; zi!}jmK*+VxToD2HC#D zh{0B**v(cchFDS}!Ou!4hFXh)pOx5`h__M&KP#cw%To6v_*n@>g0&OYxj!qHxj!p6 z%qsQ=sty2x2LSt8#sNUqfk2I5e+wK4gbo674g?OgYC)|a_8{P3%RUImJs7ALBw5VC zK-?if{=vXuRwrl>Bpd=HTizi+K@!j;7;f=NK;of5aT0K(H42&q$%g_Xtmsgn337lcsM*_J=0ri3mi#ZC28v*1W z1)Oblf(Ajt2q4q)MgRpz15JX_7JoF5I1(s68W>}Zf@VSTNZ>px8VQvA4QLgNv!uTP zDaQb1e*-SG7D1aJ?HC~2N{<1`j|DmemssktKzb@rc`T4)?Sf80W-4&GRipw{#{t3P zfGaHHI3Q~jP$Rg?0;7P?@j%WfV7yfeY6Y>!0~0L!cpx_os2Ai}Od1e(0+62uTxWHH z20_9JK)&Uj02G`EGzo67_!EJ|bfEY|;6`f{Gz*f`ft#%;9Vj^oXcgRQNhbj*Cj(_C z0fp8gXcMHJ3=~=E$w2ujK!@N?OFachKNYAv1t_+5L8l<|RNyYFI2EWm4G5kF+-(`B z0a>R5HG+FBa5@k=1IRfYm}u34T0!g?zyp?j29SFuP%kL8m@|R63?TnZV3O4d8UzU$ zK$+#~ZMxtrph;jBe-@BbXGrXrS_3pwij}or28Kz;vq^4OC?T!7N~gWn=+aV}KgL zObd(wLSuoPF~DrA7Ssx2#{zRKdn}N99#Ai+wwUvPxbuPh^MDtvPS7AoI3K97yz_yA zaX^z`uEmc75-$LX#{u)KQP3<%z5tkSMHc`i7Xqz<1(tLnka7`Fb|J9PS_Ex^w2Oc` zE4>IP&jvaKZ&+$JkbW^xnGMuiyP#8$c`>lmDlP`9E&+m<0Lv`n5+LhRphobP1ug|b zIY7>(z;de=)CyvAfOjoB2gtn)s24O^%w<5_d+CPGPkI~Z} z`bUuOF>XLQkU*5B-awc10-*8+AZYD^PC;e?(90?cfT|mT;Eh1EW!wm4-2~JKdRyQo zAapa3a}&_lss*)z*qecVmVGmjdkau6h_RSkfVf+M{9AyXtxnJ&NVpY2U3cFvfF{7)*@&Vq!j`2R$2s< z-vM+8_OjGFfb=_o$~%ArYZr71GVcV2S;d_|)jxsYKY@KMs(&3u*Ne=l5_MInt8u1yb$<9=i`X###hz_c2M@eN2*S zrS}2l6M+uFC`+9Pq~Fhx`^-d++%#(!bly*o%=_taqE*}vR6PI$9{^6Wj0b?M2Z0*F zDHeDT2$cdk4+5uIwV<|?neJN3OwX|FQXuysdeoQFBg0}I0^%kC`40hSTb-amkT40z zw7f|`!NWk4V6??Q3?!BT#Sa5xtWnS`NG=1;v!XJfd>#R=DAV_!|$hW-5 zfr2N1CczCB{{)cuBvAYWaHBN}ngz*E0ykUHlR!xY&?>mqk}80dr+~5wpwL3MiijbO`RW)M-Ha(?I1kpxD|4or27#fxE2YX`reS2v!1jTSg_2^$buWxYq*D z0HNtX&NIM7s}|G>Vy6QSSoU-vw+g5ilv+#`5H|zJuL34nouEOGFas#Fycs~jvp|!; zEdE&_aVAjwEHK#`18~{Qq*tffGW;42F(1wsu#&QjnTs}|G>VjF;smfZm4 zE(7WX?H02Ph7>K#icc1>OfjD}bE$fxcEPs1?Mn0Qy<>3Ly6b zpk5GTF&_YNO+fw!z|K}DXb>bc0kM|X1QdJ-GzkV+{D(l|N}%{dV4yV$ngz)#fx%X^ z5-4c~S_MNasToLF1(Y=dL#;*7CP-Ta#9QeqpnNsZA=t}OR|Dy5fXdZCg0%}e1(|Dr zVOFsQsQL&9egy1m86N>zYk?ZU{uWpZggypx)&d7wwV+lI`!R5^Wq%Cht^@u*=H5K6 zrmlb7Kc!A3Aw8XF_DI6d6xR<|$;G=g2%Ov$&C2h|KeynGl)3*Sl8F z=RD8%xj*06>-X31-1pU5=Q?Yxz4zK{uf03_y>nWGi)Q-}VgCpb@(@u&OAwJL!s!vh zRSSHC2z`u57ExPse2j2@f{1*KsG}u`ND<-s1W`{5e}b6)6!Ba{1I^_rU+Of}W=P_$ zJ(i@AR_7UshZZGCW9_9RO*D_^Bu%xYl6Y!ql6Yy&|0Zdst(C-E`zA?q%_o(lg|=Ce zmYT^6l2%$bNm^^WC26Bsy(DR?`I2bR+Swt7j@eRWHEh6#_qMMc^B1MGjTSN~n{4HYoJH&Gly)>712)Fl$=y!-d z+G7zfM0mbO_-aw_5lcQG(na*wJU$@2(-1Kq5Pn*khzt>((h!5RwP}dhj|lURh{2lA zM?}|8i2WjlY9^l$mY)%RpAf^f-6G;e*nLKf)OMqMFeWL=?METh>&!|C@n!m zq6nuih!8FC3nKI@B3Z;3&G9S3IRg>-6){#z5|JXpH3KnT3(r7I|Au%jVxs2q4dM13 z5&aEe&>o9;A;R-JB3z65j#%;okuD-a^Z0@A{)veBfr!-7L}ZBQ^b;{nTl*6c`wLAzOwp&EJ2)jRsd7AGZ^Dfc;db&g2M@8G}=?xRT zO>~H!>x<|F-X`)^b28z5v=(TB2sK3{i_kPjQ-pI4M5HNVsg@)nMTBb(#Bwb>2V%Mz z;<<>Gnu{62%^VSJhFGmV7V$!ar#WJ+7G;iDqDQ2QSg(2L5#APv7(F6JOB0bHqLT$; zleX3Z5o?Jsw?u5#d@K=Ntq}V~Y}HJx5SBR+epZO>+HMi?BJ6S^c51#k5&qVQ(;{|j zw$=#yT!;{B#9l2yM4||%T!{TzU@kA4aLtW4tcB-B zOwWUOF5;->k_X|I7ZIHYaa?;W;)Mv$yodxXDlcM*Eh1gSDa}K6lXpHuj4k4{mL?)Y zM5la+v)bBxh}irH^ZbZ&nooX2*8+(BA}(kq1rU~Y2)_b|OWJM`@gnT(5LYx`JA{8h z#Ay*pnr%UZeIZ0hLBw?}K}4bmr$UHiEwB(G)E<#6;+E!Uk8m!Gh_pxC){;b|h;S{8 zxT}R1Moce)crN0;=28UVRumCk1o2RNEaHU-&!ULOT2xWQ5(h-Oh^Lx|1H!u)BE|vn zTuT#?A)-?;M5?y77$UYf!n`=*rRGx{(X|9(zlhhGNeP5yNrYbs#9M8*i1?CvVi-&6 zTgl}`Nrb;6qUFCH`AySo9kKSMs2Ac$y-!+#h(r-ir4Z>_U@1gsX+*M!ubN|NgmW20 zWNE}VElEU*2-h-*A6j@B#B?Xba}mEZ7bk>USwyrG;*a)N#0wFgW%XQRSZWK(>bb@! zhe^lCHAXp#csnCv$|20PG!YphIyoaOw6)HN*zyST@(3%AXwp&EJ2)l}iJeqGsgnuQ(X%V)XZ6$<#Wkg6NM1CznM4||%$_P6xureaF3L;rV zAbws*|(wawggm(=@Om&2lmL?)YM5h{va@yJ&h}fD4^O}hAnomtcS69S-5fwEP zSA=CPgr6&-vbI}9ya>Bmh^m@zErfq<#Ay*Onr&@_y&EE=Hll`>ARBBlYtQ%e((A)-@5L^Ew|Lqx1Q!rUFvT=Q{9bZvy# zFQTPp(gMai|C>`HbpplA|jh2x@k!wQbf3VB6?`yo`~sQi02}DX)ay}w`PcFFGL^h zv4|HUJewhWwWwx@CEkd15&bm}Z-jSqM2t7WPfHV#A)-@r#2{^Lb3|+lW&pDmObLTE zpB9L&EfKQ^B8F-vEfJQjsN&a>D#NwiBH~5ZwL*;4d|M&>TO&@32-Iv_>${i)X@eyh zr6rJPiEa4UsSO{8Xn}3;qqTFAjL{t1l7wnwB^j$FNit3=-;QLw7B0yI?Y1NnH5VU} zN!koa4BBH!!n8W=Ny4=#NhWJAC5g~HI*?4!mP!(-rAabXYu=G$nzmMw>Do6*W@tX0 zNcbBTlFZUfI+O4>EF_tu?UrP&X4Qpcp5`kFf5SqOD9yGj$pUS#Bn!0!N%$KU-AJOf zKoTvqI~$zbjSbc`$L%XkuGAr=FuDB-3JlV8xf!a;ol!|TEuS6wm-st03xJ6Vy~7U zB2k3X0K|SRZ~!9I50NY)PIL4_I1fZb`XLT#Ng`52xDG@d*1`uOrVm0q7jaZ`8H8~2 zM??=o9M>L;cp<{mACaI%`6HGLMx={4rFjfScn?9u3`U&R(nMs4=rjazR$Ds+5jzxN zJ`{0I^BIchIt;O2#0AY{7{YQm!fzPjlD1n!ya>DDh%1`!aD@K|#Ay*pn(YXL{YXT} z2*hj6qn2BK*c6-fFu=#EY;CMZDL1LlORC5vN6@X|`h# z_TvyCV-cUU1QCfMoW>#2wZL(R(D8_55nnaO@d)P$h{*AXZ(5Rw6cMfy5I?l=35e+v z5zj^Z(p)AY+$JHSCnEl6k43x?;WiGCmF6He8Ty}GJ>LhlPPMW?VgN?7hxBH$fNm2ApECL z_pb=5*lM;@m>u$E)->doe;FX*;NKK1B=&RvSN&OM;`@q{v~rq%F&mcvc{!KduMwN%i!;lKOG zxK=J3IkDdLyE^+$;Rt-s9`R9SHX^bIi@6l<~ zO4a;b_g6E|PXo^n_PTDg!i9|6o%`p=oS4bPUqqK2;Of+6jG=Zyj^-~b`kb-ux4r8X z&qKaGYX%&BVsmL&$yuEPmN>+}d^F@{%EsPf%UoQq^@^+WcTuB;?Ts5g)UHfw`vyLp zs+Y~>cl>(YMcpgk?*DcC;RzMpCplJ$c(T98&>lWtn#{>)btwJM{$^9^2R^7%W!ku* zhRF_PW)HeJ$o-;G!-b6-_Bq|B)0df#%egJz|23p(Z1I^j8ue{z_vqvgSO4o}2 z6tgz3^HrRZV_-m8p#-GkC_;J21wJLs%UEIICSDhpGA0}0Ct#z>DilC$I%$6@0G;Pn+^~X9r z4SaTUTHD4=nw%Ro(5Lu@?eI;GAoVNq^O#Xd$27td~3+BF$dc;wbkmv=X6*Jgk5 zQK8Egyc+X0U+Em}=3ejItl5r+BlcXYVAy?cM9Dg%r`z1^l}A5tn#GZnU3GuBZCW~_ zfVp4q21X5+Fm8C`iu?ykb=%tH_>jsq-j{nf`s(1WGhJ$2oboF0N%Ew6p&dut_K({9 z<=u<&R~FqZ9s1zN{b!$JM@;+qb9MTM702qNuKb^N4Z4!X4M%QhdVbmAGfnE{i+fbM zWX&s&`ad$6x#+Kz(I&O#e)NA;ba5V^V)HlJWsJQ3WZb^K9;1($UH{l(c7ZLf@ne6b zw#aDyKMm^~joa-MW3^{~pGk%b)k<|*bK#oF?QH=o79?+7|J<-VX4RyQYp2<#PVCry zP(Y;JhJb#X3b!4zJnCA{KP}ydZmWA_tNw=b38RKf88=)d*!J;(i6wQXJj++zxL$wk z;oH2?E3`UJwaj)sJv}zBJ}k8MxY(hW(q0@|xGea<&-TrFr+4<WM}D{!Hu>w=K9vpU|lfu6^GhcJ#rO z;ujj6E;ajN>mqx84NeTd<23V_`GFNCM(vg{ZueO14j0uKa-7j2! zurtTpHVYTztGCtc=lS$G1<#ZS+qTO7h+#s`&b`w<_K!Dxm$>ul*LoGxxnEt*s9`7L zhFitv8((3b`Lmy`@}&){-o`mSX6w)DqbK_muU6eA#bspfiM6*Cbw4>GU`JSo9=EK1 z97uk$cANJm!~4UPdcNv0@nwzrMh%xWZrCxgwP921CrK}=wJ-N)@01Vwm)^QrckA!N z`a$211xDrjW7~XJr;*pLl(ThMo%3G8q;;n&-kCncd5L+|!&CE}9+w7OV*)Oxjp6L$J9}>@&-)bi?-E1UNCC6f@)XhADvm~jVp_E-`^CT_ptq`k~hb6*nO?gxv_8S zx9Q*iZ?l!drq18Fae_yo`+s+Jtlu|aQ12@hW_UFpl^hY+uekN-0}f%ij0bT=;}!0? zt&6Vm*^WPc?A(!Y@t2)-aECtb$IHa_xsWGxalO-@TJ2e3*byC7=T-jDi}4SO-cPn$ zHpuP1+s8Am@#X84wHQHF~Q)|Fe`SC@F+@4~Wm_Xq78A73d)!rGj!TR*)EYOun4 z>dwdBvoF;;wJhvI!*0VKZSB1-bA`=x{A)+LD#quEbuXuP z?R&MmGR#mS{!nU%*s8DgHSFuL}OVMa4CJIv3fPa8=jQ%>3NtlDo?F4qsNY ztY?|g-A#YjuhPBqNnPowDZwXljoNbVm6PF;YS@g1s~I<}KXp5>EpRqA1ijKWmCf*kL0gsr!GEw>EgHTl@8VkIrIB_@Vk=f+v<6TjjQ8q zc}}$}J>g>9Zl%@F9#*Nhp-}0e#fp!gY&iCE(a;v>7e}YNMenbld(w`-Z%qDL=|kzM z=B@#+%l9j1(enP=0$)!}t+2J@p#feGy$(gU&ulpB&tq0MZutEGPn~OI?B+!y{Rb}l z)F#)uubWbOTCClkQe%Ggimu*`x_8+aU(HZyP0>#g@5fqAZ|t1bd-Q9=Xs3BST)v;u zu6}u7)Nl>shL03%`{?$l2`_tZH%lG9qsZoExu&$e^Lcc=Vnt%zzD#Ltk+1RdDW%J{ zau}3+cw2)LL%k|$f8|*gwYTrvvF+BoP1?81sNI^z?QVAL(%#Ve#(;beC+iB{zt$q6 zQL7O?2S-#`Q0P?cN3H9wpTA)D@@8L$9xc9k{cVRc(*vHop7wbBrQc7IYzq8}u-H0o zfKkJ)#tm0kvi?ha*taHq-`06KHTLZL4|{`dxUV<$a@{>(LbILYr-mLb@GWEU=8n6& z1Q{-Db^J8uiPiOP3!DG?eW}`Tvk&t>wKZzEmT|*yxY`Mda(Re2lzKAiLF^<8Zm zA1#yDrB&xGoApl`dT#XCP^4MT=V|MY7M-)@yTR-8;SX2Ko*cjD!qyGH>|Qsj@x!Ry z+F9*p&ObV{n(jkvs^5E=vTNS+2hYzo3h#Kga+fY6?sagoOPbpCNcGuO2559I$mz>%@+u%~U*Oldi?^Yi0>s*Xc z!*z@s4&E}!db-wiN7YVK2l|!^cDDUiC?ll!RG-~@CZw3RX!`a_zIoP9+O_}v@k3;f z!KbX31Wm5gY2JRP$x(NAR$o1@=D*GzGIiE9ZuouI%`ugBZCzaS{f|!l9hc9yFFCxe z#e;V~S5~bqvAwi?*NPt>e>ihxnfInG%X&=jQlsYb<|gk8lrCRoOM|b$BM16#Hd^6& z#_cY%xm^ErXyk-lCzs_o^Y&`UjzG%|rM`bKEF8Eq-{;kTZS?j?nQ*@Mj(jycH+}fD z#n+&&r?1$#l-vBh!y@mhNgX$aZ_S)Cb(u3;edC55V=pgxKeNpCxDgFU2X~xfd7(yf zj`ZB?>y9$LaXe&ylU%zOmMS>*?43zLbyBtW-b1ETw7l4>{fns6W6zu1e16n$Zbar9 zXEofwxZ#vBU2>hBXrGwB$ljYPR$NIjy|&K7{p!1Bk*-y{PCa#hTK{TotxkTPydo)l zWzEOVz9wHgES*;O+>>>dXWCxV4~xiU)NVuLcH_sS>-+0JuC03M&L>ktY%TBbg}C&&|E8(2afJ5AH|rTe?m6 z{#g@~Q`Xx~79MQ)Rl8QlQhlzTNi5;`L^UiEeIw(BN43<_+Z8a+eZN(;^;_Q*OYHaJ z#M$A^(qgUs>Nw7Df777Zjm4(dwieHGeE-eTdF~8oS^Gv$%fEWgu43wB;M z7I(7St&T-K((}FkYHPW6SHGgIP3F~)QVoaEaAV_!2YKgi{oq^Y^jGJ1r0(f6=8Ar8 zV9hp@x4ttulcW9dopt|IDW1Q5`TTo2JlJnoVBI)Rm!r>S&MRLgXNkieZO#p9Ilj?{ z%!YNAC26>cal;OKTc3IuxaVic9UqSlIrD$;Y`nW-Qcc&uT1#%f-JAM#?%j{3Z7)5( znV9Tb@XV_1m&)C*SJW+HWYb^E&(#`RY17{IS?!w2NvUaOyIJ#(&g}B*I(r>nna%$? zc0$0h8X@PbY{$*FiLh<3ILG2U>r$5;FE#(Kms7nT^?uu|S?d;$+V}lfY(U;dCt_^-t$)yZRBJ#vS>u@2b2FFJ9G{jq13 zhHpEcnjEiEd~)s&R&mFF7ueSJ*~nZbhxc9@Zu-6D?Etfy5o*WFw7@buok}+_bf+LzA*&H?*s^%)|ZugyfrjzI3xv4a*?*Hf}f1fq9;uHpTJ= zlyFG%`aU6b%hPtjo;?g5S2i*`-*e*HSer5?i_@oUUU$9!X5WG#tk?3J6)*V ztlM_2U1El1y-8VHVtlN?T?YAzoxt3^o zZCmp`Np3T4{wdKnyC-P3rE$C6ryp>caBKEy>%f!M4Aq0~%_`gMNx<~#(eq6&xqV(+ zy!xjVHFe2n<`giSl4oN1-4+3h%cl;Socm2Tt>uxp8ZmDxS5Yf0W1^LD!~HUD*0Xl>l^?)T?r_pw>rFS_Uqn^JEUyY%^Td-1AmCAJK9KIn4i z*8T@k*UwemJ7jY0uzg?rcWi9gWaiz+A8UF}>ZvbKz~S_w{v%bpVYJ)ExZV90219b{ zxx+P1zSe+f}xuxS9eCV8Gg0I_&KaCf4n)q?T%$?O+bo}HPT<7V< z_+A}K7kcy~uP)<5Y1MEe8g6Uc@aq?)mvrxQp;5CAi(YQLU1i#p9jC@_+L2M|+l_f1 z!MTr**)U?}{gV@}cl0u3YzeCBai-AL*cuDVq^@1vYrx2eC;F`wRKwE6?Tj0~d7xOE z6+5qo?g{8MN$>Fbl4;}M*I{*UZYkzEV}R4nnbjjw9&|a}^litg?mxQR>Aoq?my(UU zzFfboh{e}w%|7)F%=$*fG;^Ntk#^Pmqca=!<(qEw@ZnFrW|xW{l&p^(=KR64%Hxx- zUMDnomU~Qvo{bt5UUY8Ompp5O4>qp*Jan7=lk_S1zb&o%Vr9JZ53kb!nJXNY`7hcu{e^{Bqs+87Q}veSPxvP;bH?GWn`X+ZGms05>2$fZq>K8)HvI;V9PURq zlq#vySxLEw%yQ;`D(G~%wD7h19#SpNe~vz{sdJ#5X<3hUq*9Np z#)mCL?rx&f71q+G=sT!YT&OQ#Ue=4|rMYGzmzXzd&hPh3)xew3*;zp{m;dA|N^n=EQ;)}mrp>Z=u;#$V^RrkecggeCzx9slrH zm|Z~JyOnyqsd?E6tgJ?j^;)|Y7G~yqC+T!{vd9v$NSh&}{6^8H&WizHCB^26F1R9` z?WURj!v_>{S^0YQGj!GryRcJ7U1S?^LW<1Qc7 z4>b9oeKXWV3RnjQ^&dHaJze*cF0O4}i|EX1CO_g@pVhz5VOq{1uHglJLle__CACQx z^DLr*$H6V@TBT}MFU#%=88mG0z_CL{?W+-YIZ40D z)aFiElgxeFm#?MrWNx_4@Sy_+2eF%ba1iJG*DkXe8Q?dM@mwL;>s~2T$r*wkfsD2@XhYisEYp@^AT!+k+%{nAxP-J#)&frn~M~)cS ze`tr5ak<~fkTXGK}$zg8ow-)sVWgM!Bn9UT1N zy(x#N9JPh>f21u|%6N!mcCSo<$1?|IwGUa{aq)9@Et@eTgNFJA4bXl4B3sW=EizZx zad^n6VBh}!e)oS+RQ25dHB@q###O3ovAT>5`+nN4V;23gM)Lokf6BUkk7ITY${|1B zo6AhSP_jEr<>xTVOd(tI*ED{_?K)<;vUc2>E|vkgtW6Tt7Yzmcv@TyP8yIxI|75)w z3A*R}8+tM%=IZ5L)_WO+LcP3Vbh=bkCI|WY%Duoz8My+BluL77uT(j6@?{v#@_MbD zG+#E;aUOYtl*UMOL2AM8c_R%+23;;Ciy+fMUN*`VCI2=&!pb?}nkiRAxw5!b1{o#1iYi%-{A%SKlykqN#!b$-_3rJ4l_6+WxrL1y=qjuse)B-d(>LjQLZYktvX8TDpw6B zV?|!|lyf0pl|d@6`pQ)&Uro6NSXVn`F$}1Gf|GH*ij+40{ z3?aw1HzcUB5P_4GZVo4vo1)6Kz@1WVnsP01XOx?XlLfbeYq|JEPF8ZRD%hHQvMLyb zlZCW_o60Rvt}X7aatoDfhkK~pBISH=TV-jyqH)qQ?V*xncxjgGe_2QesI24?Rj?zj zigL?TgEG7IR}C&#t}||cax0YUg7Z^urE0G$&R@CJs$4f2e}k1=qhxp75are?*8>*} z@>-`{Px8OyAm+6mC%dT^Sjrah+NjF)CO=EHw@JA^xGm&m`MOvo`;y;AUS6A(^Ch2O z^@3E8w);T=<+dup;becx zJ|CgnSye6sx0(GelU1T}qsf1h6F;wW%8em^LQOvBl?%lsD0cxTH&S$CVUMbOQ577A z^Hl{eDK{S1UG>6c<@kMF7p*3wE6Pp8%~BK1Rh;Z%evjA9QSO>@Y`j4?SIHYlIm3m) zLgj9&2E%cSa5974QROC+k5=v>PI@Z>7AyAzCkvhe_Dp8-dZt_?`68K)Rrnhzy)YFV zRKZjn|Ldkfapm5t%F}TrabBcp%FQ6}sN5%sEMpdwR_%QiC;NXklu-pU zRKYnoC*{7W2Iu0+D)$p7TQ?6rb3`;J{f(2^bUvgjC-H=`;3&8uXE0t83MebM01P*k zl&ClMn*zRZaq*#Az^9xso_J*p|Tb zthxrB1g%KHr7%Yolz4v5-TEZImlp=mxOtUTLF`8#OQPqq9JZ2w3apHCD9e) zR)HroVS7>uj4+ksZ#8dPAmt^&2U2GZv{FuP!%OA0&|bQOm)t>@>^kV6oZO_Aa_ga^ za&muJ+y>~RoZK3gWyC;dgOYNWR0?hc{>`?`m)zi#>?Y`;oZLf{aVi=FK~TH z<=b#6w;v9Y@gtS*o2A?V@si;s-xW)_I5^70CFg(+s$4wzQ}*nCksXyhNdB;z;W{aI z2=_{@OlQ^LVVpm+qs)?BRkVlsk$mr(AcOOq9pKSvg+=QhM_^e6`~TFF#fJ z1o;xm4OA`xS53J=I9acgP#Y)H?ns<;|0!sbpC7z}aMBBZL3`z7*GMNBPV=KHQVyvQ zRrw703rwT(3RMNqlE19nSk+)6E(u2`>c-<_2%LlKs$3XOR^~k1P~{?&yMRm1M^&jj z6)9_Y5w=n=jC49q*6iTQ$=6mcM!7pUH{~|sWFdFqEL|jXd#oyVk9?wXa*ZH8cpp-j zMHi56QFR}XKhKATg=Ds=f)B~Brl8FC+m(AnK1R76%00#%#L3*gQ@JPPk1MxJxu^0u zPNw$V$~`0Ri<7B+j}?1gw&XcTPsrT9kAl+ue}gv#1q^@lBUrK%?-v=2>dRnn-v&yC8 ztdvVs?h7u4m6rMboN`~u>-GHLbsm>_{>k9Y4H?S3E~$%b*^aS zm8=?+P~E3$8oi~;nc`}z@sXlj4%`@3?zVDfxH`C_(*Jjm(sSm>x|xzgMitcK%Hxjl z`2*!Fa21q$h-0HNf90yg6nujGQ&moG>Hp2lDz9hC<;1;I?zuSG|JKO2$OQ6#tAY}S z^Fb9%#mNfV;L?K4SA6hQFGhm(~qgfq=@23-Lq z?U6ZD!Gg*a#+fT;kK?Vb2tV}76;@6j@32s=2u@bo0cWXPG1Xo%oE0vC&r4)F`oB0b zr;?7UUbfdd9(Px{TFP-FFY{28 zQ?Q}7k`UTjlEGT2<08%|EuBb2L+>yU^2FX!cvNDcs<8?qAx#f7SZb#UEPgJYGei)(fzi~x$!s|gc6Pq$e(ytUj-qI9VAlTyC74 zXl5(d43}4xo2#5RE}!fhUh`FZ&2iQ^Ir&5>*8=xPzW^E_#^YOob~ z52jn0o;6juHTh>8p7L6vTpL`fa!YZt@ojN0aWWOJ#K|(+k$ zs~T*NOHpo}avgA&aWXxx$4Sq0B!5-87*(zlE=jqKs$6H>HPv1$PFALieE+f)DRcW) zRk5Xiu7LtgQHSB|HrQ8Kot}m{&a+g#&UtAmIuBvkVaBXq2;A=P;7X8Wp zUqQK6fcdqVwa&2j%W5HxMTijg-5OlYKmh{8^lwq#omBL;T5K z!pZBYDmNIn6DKFGRGc-R4UzNjZdLFjQo4001rDf!UsZ#{a3k3gIm3O!k?4l=BS5+D z%8kGUs$TeklZ_jR3sU8NtM&qL{c#0l|I4aK63CAMsDWr(6!4G&l-3P%4w^ zRk>jDC2?|UwN&kekT0d`TB&lQaivu`Yh32_-x#DVQcl4(s&Xj#e5!J8Re3BfzjAqT z(p%$j1yo%-Rc<^^?o7%lxR7!a$jeErBxzxs6q#tk{+F|u$RbKkB0o=^DvPSh(o1sj zD5u+Es$3ZPAJmmIW^v`h$)~dG2v$yv_{ zCx=rcc{!ZKRZ$I2RpsPNSXGsqrpk$PQ{|?Umz9yTV;$vYke8JaR~MHCmXUE_XgLF)DWx+ZsE;RWM|S4Rd6f$WvcQR<+kBY;JT2GRc<@^ z12{QxjaTjMAU_i)C$0%N*^r&&=O|~$DreB`Le5t*OcmUXTY!^SglcdPZlNkSMY+8= z8C-HEjKs;{+DBfFoSvk!aB}4ACoe~ixJ9b&0rEWyu>a*$7_DR+`CcqU++v(`S3LRN zYGsz-WK12zb;ZdUbA_sVh7wKYRrwgM zJ+3e5W>xt(`HsqM!O7O0z;#k?ud17XYlrhCjaPL~lJ`;WpekoLg_Pr6&eg|M!N17M z@h}>$l{=SJ&Y%-{P8B>) z{wkH_{CyE83%Nl47;XsZWmWkidHE`BIO!Ff^ui_bqu9C;q$#T0W%9ww-Nwl-xq=H( z?twM?Ulwu|S(OGyl0KrKxFqt!C@81pC#u0~TdI+ymEh3xjVQDI63#{V6u{O zcljYJIGWT9CmVN27gf$hx%arQ;#hv>H(R{dec(riDp*4mOv8P{$=qHO zC;RRr`42dm+iNNJiTo$!#VrWChJ}vJi3oaJ<#&Ro#=g0jjQrsw>V<)wNW0 z#raEJ+5bf8W)4D`c893SIhB*?bf|LH%E{C@Ou1Y***cjKhvQ`HY;dx5;zp}-xpA^_ zG9yaB8i_6s&LCSSG88FWmlr8pCvH5Bw>n#X$Q&S3=S1c5;iN&C5e=$Ai4&3b#7U%_ zv?p;Q(w?{oRo4#Z&G=xJH0UB#<$}n@NSP+5;$%w-;hHEnPnEOB)yLhW+M#rPq=a6H5hcQ z%E_FuUb(V3*-bKM#3)xzIhivyDd&unNsq%%XV{D+)sh~J}}L{mv!8K8SrU5TcWax(A^W|cGOswydi=a8ygO*z?Lhm~_7 zzg_j#5#_4mOz9bU9aXLdE{Af*l&gu8o{^qBjw8{zX1YpQT`7@mB5Uy@MlIx&s@%v= zh9Z|!OSIx0tx9S0zgU{ZIh3->WnyTGu0*d?28mgA;kT~3AhF64n>>JTOeM5%APj;D zFcBt!0m5K1NI=~bh=i#ip=A;_whC6m8rT5sIU{#~j!-Bk!5;Q}pCDg?%a`6o!2yc# zuZLNc>*P$fJX8SrhFHE4mM?qVpbpdpJKC{_!cY_>{8_@CCA@hZoBxwT;5Yn%R(#f4 zt5e3Rs^K8nLvR=tP;?Xw!XO+b!xV^wsW1(u!wi@Sb07*N_<9j6 z2F;E+W;vNPuol+A28e-;unA&eGf0H?G``c2pzO!+9A3d|NQW;V!Pydw{R<=jdpz4e z0VaY0mM|vecGVo13-feZ^|Ds^`0IwAWv!|f?ulv%t)Mlufws_2n^V@RkZB8@wxg_7 z!O9Yzy%+Yu0f>WmI0%QJ0i(MixC0Nz(6XT@3wMBG(2gQY*^?4WS%ddA!4+yjZE%A+ zP#5Y!WvBwvDL(@~u}0}2w=m^f;|!2Hl5z*~G^1n%?XQGYFp1Ga7=^aFoK-QyG~P($ zfJ6%9f>hCv`_`@tY5csZeWhAz+*+JPr4=LO!-9OSyG3F$3u zjkQ>#4^xGNpYMbHAg7Vs_`INlHcTTDcHRzrz?}88086j} zdBCC)R0ats>}|mZIzlIqd#Ilnpr7F@e1r>T+K7r) zMLbuNUju6)iVa@?Z|H*pY~)c!{&7fvgRlb%wWNwxdkve(Zh@_^ z4R*jam`YIzRPP9#KmydeKv(DnZNPyt5~OYmc2EfHp$JG!dODl&1uV#$!4LAI>EE$1 z9wt~2crStKlOP->!!@{Wp%t%e)!h(KkmCe`U=##HH)sM)!4tfoIW&a&P!EbiaVQCn zU|?&w^W#Zw(&M1X_SMG=ys40@YzA192A2hB+Xy`<8fflS*_N z8M%Xe4X(otXv!9Ng2Yut!EBhtKjdE3sYM@i?Xt^E4?8RXYn`E7OyEQb}a5>`PpEC%_7Lw>=SF5f=P zATt>vU?z=9X!<-@1dCw<737!sjUc~+$HHcK!pbJ``^`1D4kM`F54uAS=mkfqe+-U; z{3hEPE-){j1^H@hn|%AUoy-o{3A;d^AZx~zdP7g?Udlje(3-R)8rF=Un-$pKj z<*)!|fP85zUkJ;Wz4Gm|eA_HvllOu8&$xp($JF2~9wL zLvSZ;1Rl^BDnVta0Ex^17vLgXg3E9Pu0j%A1Bu6vhl3!$5NxAO!*+h`023N9g&bf8 z?U_|2F25smg3iztx3H6yx8h{5h0Z;IPUT}rali(U$hZ~R#Hz5U11M&R5bqOq7 zei=Un-bgQK3=-IX6(pEng7+m@e*?roENq4$Fcc(ae>g~-zQopCACm$j(sR)q}7 zWN*SPkT@WTh?0n^1dxC=2~gY5XBX+`u1r9Uzyq3q7c>KJXbvr)CA5OpPz{`+JXC;E zU;?IK0X!05&^@>1bi|A%asQ{_67OfiZjiA5IEaTsaDdO_U^RIIOoo1Buj61_{nT4;SDPgu+;mVEl;?29qHIrho+B zOR)V6n5DV6S{16Zfb2q81ktdR$!HlYhjq{k*Bd0velzd4z$8XuO)cEjYNt(q?y3y{ zKNzSrsAV;@mjv2Jz!ZptQcxPofD<@CF?i2G{sGdU6;q@Hq^MaL%0Pga2w=XKQGojn8wC|JREZ!ZffP-ta^Bz zC7TH6z@OssRaXX8uEQl*4f3t@TzXl)B9pJg_VfM#?1kREKVhm}cC#92_?tOD7340( zOE?2^f9DWPU^}*eL@7O`AF8sA)xZU+!y}H*$M67d!yUK__uxKA)chgX#WHt-e7z#y zrucFUKcL^9=$VQBCX)&;;3d3*H}DQVzzHh8WJ4v~{w*YP5U0Rx*vvt-mGm3~HJ$ty zcq6c~Z{Z_+f^>KXAK*6J1Bnk!lvwYhWF$IyFI1v(9>@#%AU`B9&DlXAkXZf^FcJbF z5G0mgV)s|FRTWv8OH3Y(K%Tq##0()J{2A~7By_(dI6^5XEng*+0VgO6636cf5~nY5 z`Da)G3A>jtdkLXG1o5yJ_Q5vT4m&_1-ODiF7J`x>LFp2Nevux$LJzHlGY|`bFaiuC z`Oy!qQbVa5C}ma(asXxYylF{+=M!fU=s@;39Df(M8jgx zU>@wJ>}>YL1PFwYV9J=v0cK#XXa7s+vqU=IgnMuwOz4eA6n+NJ;WY0{GCm~IxePc# zS+IrtAkT8ivs)73Jecz9C?5}U!{P|+gX$c0az9D#AC&+*XlP(zHNX{~Fo-02S)!Fo zF_=st2bhB%tRN?RW+C$6**mzyfZM`;uf&$7vBeKT;<^jr?7@LSJA|>&h=on0?GP9Z z68&5c45 zA%JWk1i>h%1};z?YCuhJg<8-Y3PK^Ufjp2GazSDE$zJ;f_H^2A(w?kJZ&=Ts+5j=I z5jH_A)R(_C>`E`!g4)o6ir$bPoaxm%>?H@rt3=#bfE7p(j0C;h1BqW*4s&4vNF+xG z7zAZN?#Rmx_;MgO*yV=$eO6TNTg!cFEkC zGi$vCO0$A;dsuG$%B^0xv-=T-!V~ITfQulvl;jqY+%j4Ua+}BjioqY+{cWJaA4oym zhC6TtuEHfa4O?J4?1Bv-H$~(o$PMzza0K?iUXc468$r+K`$#vzBF1Yp`&XU$AuAq~w0dBjWZBfiAR zy~U+4pZ7~3j?a41fZS5{0tqiU0b5}Zjk>TQsjS2ccnPoIHN1hh@DAR?2S}4&U_O%h z1fL-tzQ9+=fN$^}e!x%o1#z&C1Zz^IWQOI!F-5<1+Wkn zL9{lpu~kb$IN8Y%0aG9ndO&aJ3Y*#Ht+0^3n!{qouv7YhAC%;MC&&v6S&c$;d||MI z`E#l(!3he09oR!*kVhTm$wqmSu>?GqlMTqzi4sC344X6xx!FW0B@*M1Q zt+=OE3qxM^nJwgl{7{SDI73%Tq(UOdlZNL>FMvEuxE|yI!X0oH4nRD}1B6FG9vV!5 z?H~^e%EN(jrd&+hpV?K}8!6B8eU%&bmTd8C-rNCs)@}(Tf;?ll9M-@h=mtHZCdgxT zzTgMHX>=oP#K1aO0ZTxhc1wY~P>Nl6pAE}ixIBWNm1ors(?A?scM#69PtU*+_&_1~ zg8(DwW)B8dZJZm(lV)|HJ~RM#kcZ0TA+m~4335H9c^XNcKI#F+`>P!9zcN<7f$<>99z!?rx8ODy*UR48g7nfxK9fL`HKcv$ zo!qRTJRjsL4+Kfjusj4LvG|U>e@H41_B;l8ZbzQek>_o;Lvv`SCA8)D^3G(tKv!r9 z@{v46BTvZ4aFOL@4;{(Nkda|woc8CvA4D)jra&aDWvKjQr^tHBgD{my^FtA^fw#B@ zGIZR@q=6Kwr_%AHp&$>p$WXB4D3^y--Y^VOp*%xD8vena<$;yVeD=!LW-m*$)pPcs z8!He>S{bT>3&?XTt{|?8y*rmqWaI831pQ$k@PvUO z^YH;&-bmDcUdRKvzy@-|A1X`qfA+|#!2628Eb^}thsLP-ZzD5%1Xf`L+d@9D0txib z8j(f$<(fD5)UZsY(HHO%US*Cl8{<*dEmJxDo1)~u)#CmC(!CN~FJu2dyVtmDpV7qg z|I%y!(api$ayx9Ob|$*OfP5wUeFA@!2=pY6Oe;M;^POy zG3k4uQW4s8MGQK6$-3%J!VtQ-HfNsVZmAH0G$O#gD zF5%|c`?Nd@f5X0h3imPb5ix7`&a23umTJA;H&w+9KCmJsQ7-~<1($BYMtY)f;2fp*4L3Sq0g4u8o4#8nK0>>b`pJW-WVHE2n!O9YxT$T57HkQwZ@qRe? z!(bQ!#-CT`de3A$zKi;y_ynGTX^P_mcK9>pMpEg4ExZ%G|E&p~b$-rs~vb7S@+ZQB^w;#x7|2gPi z;rhZqeI~vgw1*CPwDWDPa{k-tUd~kit<8UG@1KUmfBOG?pM6j<0h#{e8S@|3CHuI@ z?)-mRx9rb7=_@a21`^KN5Zs{=ctB%lB4^B|WZa+*)P;IbA8J5NaD`gn0+pc(RE26F z=ZowG{=Zfz`|Nq1gEs+A!ZP{(Vg;F%unLxd?Dc;dLjUCdw>t8fwK^u&v(okq`E*hp z=}!Zh?5@pDjSrUWt&_1YRYGQ<5-b(z=yV()K- z9k3JD!DJ{7()rmtC_BwwrR+3&TUm{vy2(`d&uP@C!2kNWlsn2o%^4zQkiCHHG?vf8 zmN2Gxh5h`W$XmlFuCwLaLkYl=>lwMeiNh5mb$}wU0SbZ4+57qIFxTyNVF)Wwg|Z&x_do|&Po12Um$(P{UV=L$x&)g3z*hvfs4H;`@;j*| zoWys8mb$pQovb>W7$itSf+nOv9cc)O{HpT@j^QRl7)bQ)J{Gi%d>-<7AvgR}uSswH zXAep5NUunb$i}n5jWf4UwobOnc#C8!PJ?X8I@kg+Am7%^gc%^WX9Bd=U5HT|Nj4Iu zKsd;)XSstD45J_jMuTkMc#vDNV;~gd*6c)Jg42aT1WbnRFcswQO3PVXu8d_>WEIxH zN>~BQC@By+s@`ut#^*Xxlcj*$EjNoZ{-~yWN3+vvnN}&)mFNgrDhTPCxKo8unfX~5a;8PG> zopNs{Y?~YY2lOKN3RDDN0RK+@>;SvL0G}^tkAWjVPww}EJzziB2M&P);A`*= zI0z*39nh2iqu>NM4$gs}z*+DE_!XQ~{hz_12nGJ=($mmW;Aij)xB#R;!oRunJoI<) z7q|re1eaaw@txMm!{H^45`MWS-g2HM8Rd5XcjnF(a z2A#F#hU&7QE&)!*A8ejlPF)a2Tsx?4G1dd{_-{49p}QGsAq_zzplgk~-q;xE+G88= z2njv})g6xJKzBM?xU?0tHBj9=3~dW^|3Y#G=;^PFEJVEOO$JEcG!0Fc@SFpOz$tJT z90c1yd!Xa#2|$-MbcQ+&JPLF# z6#%A!CxH~c2F8Pz!Aqb67zbW7QJuUA{vkM}YcNPem&E#jUf?P41n2>}0j1Lgs9Sc1 zs@6I|9|x6jKL)irtej-T-yJHq!o?#u@Tc7U?@53XQpD4sH;_^#ECUs8jSw9K27>;e zFOVVqz?;ZC2YnU{0M7u88{*R#a0v{>EuBLHF9Zf0HsJp+ZR00#RcWC5ps#?5Kyp zMKu%X+CxJy56l9&z#K3e%mpfbC7}A1>|0e4Ww?zO{>OIrmRG9;2oUPP!;uA z=&#@x&>0@dsa>Qk8OeV@xN>_K90aQ3KY(;9(=y;wKYx^)i{L!?9i+{zEs+bvsh(|> z{E0ib$kneq$F1C714{HNutonSZWXg~uM$=oe}h1-q001l)xVTUSb{cZO4uf%#8q4w$k{FE z44@o!1sZHTgolCs6DDAzK` z5nzMn^4lU6uioF*d)<1mAqRMdFfHq}hRFu>TI@;u>R}3>PP`sgA6Na`+&@P|J_1!l zYPm`@68|HF=Z8iCU4PZ1C%x2L6X;s39-WI%cVl$HtP;?MBmt_cvUxD&x_GN*=2M!0A|fUK+rY5*myf16-Xqp3@nE(tUQy-`vhYIivDF8~c(o(G^0 zf+irCuchm60c{SN0Y?Y*FUAfisRPx%S38>lpfzX%`h$L;FL(+(2Ks=fL09l3=my$? zhrv>GJq{J0;@g2vpd;u2mXWDPT-shcphtm(H2ZZ9V)&S_F8C#=xE?O;4t)aj2EBlc z>IpW&qf9&to^h%C$zV8m9t;G7z)&y@_=oUkup6L2>sJ*NLg5I)azJIMHUgudBY_fK z0aRK_WI0$07Q?duIu?w_Jq9`-%ma(?&xXzdGr=I#25zQD5HW>-m%&S59C!h|2vk%P zz<8jddlUKwcpXdxlR-D+UxhvieFfSTItlt35RbydBRU;S15<&~4p<4y1#`d>uo$R? zzYBc_ybTtCh2SmMy$q^cZ-s9qR7JiDsMO@PR6HMoPrw@RK3EMN0%~J*RII^`P68j; zNH;Ep8NZYT1fljgEchr)vLAu8I(39*kI3xNn2y5yI{OMf_X@%P7Yeq3kAY2WGj8Q# zEzpe!)nzhJqg#((4LJBPLK)crtnuoao4`h(flj)%dia#P7==Hh*G#(5+Bk`|XZ6st;-CRLcIjk8jU|B7&F zl@|FAL-&AE;5%>#90UizH{ffKlOov<-3Ru9!T4Ey_ye1CMgEB)qLxvD--6kM+c+g4 zB~pA090lKlU}H|E@yoy&==gzf4R69vDLgcFF`Odc4EPzG2fu^UKyjKgf5oj_{03yu zFVM3<{E`zCCf+Eh_{C@A#CHy8&!uq5D(;k;`U9@#BJ_gmR;1i&1nE`&W#TRYRh6ny z{?9>hHA+@Gu7F!$DttGgW1-ie{;T}?+l{#4Qbj22I;nxch0IW0UDw2(1sVpjA)6Is!ad*N@rQ)t`2i*|1|R$c^@l(X)ZBwhL;hf+)F*SJ zOWlP&6gYqtPjD%TUvbjo=%fCvlC%lfK9)97>lf990wrj5*o1=<6`w{WjY4+dc+R?2 z|2Fa=8+ey69kK`Wjlw??B!dw^m$iq3=YifN=m+`$T^DW$N`qK%4=4fr#rUIh)}qij zpz9LFU3W>BmU3wWs4m`@0a7BrWGVpZsSkA1EFS0;hjKtS#lg5`onrW5jFyqLP=44O4s>-KkWBEVK#M#a#y| z(b}LEu!+~i%`K{wgo6_mk5z6BPR}m`1N?p*N<3J&9)4T28m$`Re+t?H+8pTmx9VA! z``UpnpfhL>9tCYddU8#PlfGs^ZhsTkp+HSv4@294)^7Mi(3T)rVJrONwFXF5hRGP~ zwsA5lSk`8ulf&;1Tu&5(a@dHL-;oFny9$%vx+M^tsB-=9ZGSDKmqD+|n`F zg=V2%^!;>meK%v&W$qULZ^ubdaDUQ3t)--vj#|a90i%HiYdhxF$E^gEyD{)h2h+e* zP=c^oK<6c&L#sdwLJe_KKp};JH*miW{IBt+Don3HCxG$bCGa8`2OcG{m!Wy!aoW_a zTZ)?qf>#rh@xKa`u;L~G8S^Goel>f$@(57E`sakZZtgvEcrAKRFuy8F z45P;%5Vi?PFr0|((5*m^61PA%fz4nW_zZjsv@iOe_|I|oLgqW@w?Kw{4R(V4K&#US z!9Lu(z!yM<>;=0uXMc%fj~n=vOT{3!;^dAb0a>kTI|L2_RpkMoFvY8SRsE`V$tYYh z!EWXH2f|K*6JU|Xjicaua0DC!$AM07i2)4=)ctCPibWTjS(6Z z)kbV1RLL9xGA6hos!gd^W~Ddj*fSiemv6lpssE>l)H@JiAR`C?w_uJTJOgeI&>Oqr zOD0;0WWrw-|2w!d<8Fmp-Su9D1C5@Ap=Ce`PzV$R#XwOI10q2#KYuhFtLu&j`SI%| zN^Sb|LZzJ9G#ol3^y z_sdFaov3ve!5s&J7JJn~v%T!fO{5628L3g#|0cT$!3DD|5cTTGTz4%xasN||Z zE35wXCS+j*YeO}e=rmXtmz0olcRy|!qhZ*FZFPARFJ2ojzv38i>07sl+wK-BOM>al8Ru2d{z2U=nBzUInj!iC{c<89WSL1Y^Kx zFba$WBfxMod^i`jTjCr927;%-bD%eP77PIWK_Acy^aM|W$3SP$5p)18Kyx7br(mY8 zW6GqqKqj;St$@-}xadO;^#?NC9*^GVdK9D;wu0$}ce;y=;<|z^Agy6f5caT|l}$j* z-NEBP*2#Js*DZ~|hwIl`SGfr`&QY8yD}5@RU>=o<7kNE4RT6Hdi*$Wa3p_%AsVG^I-nqoJwEXxL`w4N=*uF z0xzTy8jIf+w|d4)Y2vMcQfv(yhg<$(G|aRH*mQ!8yJP)NK!I9#umYQyRie_6B9%s3 z!>rN4Mg{A#@r3zr8z`*uJ2j$o6a>Z%4JsOZ((+h+X?s{|X59Lp;^w3-^NrRW-{W5e zR)D2o33wOG2D5;c4KtwA!8C<2>ggT2x4|N?5WEEzfcao9m;)wKE%RI|_lti1*uWQY zYf~n_@D7ZNfwq!bCg@)#{L8>{uoC3L5XUf!d(cC~nBXIP5vWs*`!P(5Kdn4Q|rqZHb^xp_DT{dsdES5N?1TKWR^9Qk>vo zz?2&2ZK)d`s$c1`)F!MF&_@;ZaYeNq{S%Mcjr|grzRIPqb6I({8MzfND!aaTZ32Qlwex74NH%z=cdNd)2uZuUB!x2gSO$Kc6=CyTk*D`rYdsy zpC>Ljf#EK{bZFSH{3^*dq&Gs1(gp^*lY_!-d~lBKz@;81C&2c7;= zZlJAODYQz0bD~CQ!!3Qv4O9JEL)2hxyrsd0rwQ|?6tl(;Ypsgg8l+;A^6o)~sb{DP zq%6IvunDP44t9-_o*}rUU-4aa?aU5bN?eTUe!-<6#^6?Kja0bJwN30{#`4$hs7|7--5~3)Khd zKLYQ9x4|N?5WEEzfcan^5WjRuhjhz;WWrp7{DHGJYt1SmmH0{!oTw660wkm}86_gV zW#D}vd7Frgw3)b%4B6aoaQPIbbd=doG}BhYVRN^h2r-JuhHu8d3Fx5cQ|L~x0~Eu* z9a<3jIrJz+y$AXg*bROq{7dLAptnZst0!OM-w*UP%l)eV-C@oYuc`e@+*%uGD%Zi! zF8s;3Z(@+ne{|-fUyI;7>VXgP>btr1f!@lqYlyn=YPp~#gg%*mAJFI1$D{WU>Dfu| zAa4Hw{^)F?F+#e=Vk1uyejFSD-vS$c825Le4l<%g!7-pO`pGTGeF7*GlG7L3?XJY zt^?_lE@kR3P#nH%s{gCt3ium{Nd`zzZYdMJAF63L6N>d!Me&3}^_4~O%3vj;FE1(+ zAyAv3bSOdb+c5d%_RAtgh)SubKEvVzO2`ZJX%?GcX59LkqfP8i35nMxWQ|S7?++-p zk+LdSSVgLDWn>51OGH5f0OQQ`O}P@KFB} zwhU^iiWHMzfa`!9I7#ug((&k2GPJa ztU|c;fkQPeH7Yg67&l%$L&n>dE~N-P18QGXzp@pvi%ec4XkQMsxB ztb!`ID+4K$um%quNIMI4=Wh-1AlrmEjilKb(Cmn)(J%vXIsvZ(p9i-#Q?>MM((x#2 z2Wk^h2_emFX?I;ZDz8g?J#ar52)~}`XnK7Je=E=k=sW2Rfkr(I&5Cae4d!_Oe@W5_ z@WsL?X01}h{4kMifD|?d&CIGdxiLvh3w&B|>sL7LM?x7iKjHW^$;5g}5o1LnnQxS?eZJSb?R52<+3CJ(ERjfEwzYJFH zW&mYGJgUav1l3feV-{3~ibuSD2}+TaNSOrHDT0M(<5zDL&s-p7;+qPn!xUay5;D}5 z=zQWOCw>{Agr!?0NJgCc4^C`25lZYGAR#3v1rmH4ECR}@m9dEicLo`y#1v-T8bZ>_ zELZPbN`#c#+^a-n{Su&r)%tCbOX)KFqU!Q1fcWH3TmQC4slh4Xr-7>5>I#&E0+fgb zI~(ynZV8A%!ZuvhWOJYdKXiG9Lsb>Zz#7mU*x^O8wuFMK#Tw_A!r;UMRuN_sorPOD zQ&lKV#s(WPlsSDJVKPFat_%^M_{FPUV)=s&*+}?$)xY|3+WxMv4KREH>=@9SFa&Pv z`?kncv??hjYzjJ1sY%A{1fV#7I`>Os`-x8L!YIN3G3E14qZHp{8-YT;V zR=Jg8=~H7><6Q)#N9lYHR83Y#HPyeA+wNi`f)lhkYU}cjhra>t_CP19GnmQu;ob}O zfUm%GavXfmPY0Y@0i?a>=fBfMzklbRpH?|vlh6TB1tYxpzX@_{<9P_bE|h%>{UI~E zByb70eg)<-^n&VNTi@UDoCT-AcR-6s-KSC95BN`jvm|mHdJG%|N5En5J-7*<_V$ms zG73KlVhAe$e#HF~n1%ai=oxSt{G$3-z^`B$37mue=0bjLo6h5x)qj9R_%A}AC800C zpSa(4!<55uK)EAlA!VS-fDXgwk|AZVG-1l*JL4+Xd2Q+SBn?mzkU-Fk&d&aZ&Xc&u;;N_0C#AGOa2 za)6>hAH3HG?&Z%0^r8E3kQHPH(d4oSGy)U?c|k!C1M+~}pa-Rq6PgP|3%b);07oRq z5Ap#ikZ=@;1%-hW7X!szcS&doPzwAf3G0{U^lj&=K)*a!B{TJ}z{(&AR08_>IsFu! zev+;n&`;8>BzFl=g^#6lM4Qs35~1R;G76Jldcn zfaLl@El+RUR!U>RSY1*&{pSR)n2aX~p!#ok_%u8n;ui}0M zq&*jzM3{~)3ZXa}$SosZ2a=rzoeB(?0^S5~fDG{3Baz#6QvY|3a161`p(yZx%s>X( z@QnC%Ad?BIGcp}v**JwOOeHxNBj$kFU?!LW0vXUiZ*ww>KzqodoY`#jvBiySqQ~zop68aG6pyoZORrmpJDUtB|xDiNc zNaB(5H7<`5QJ4*voWdldL}m15umP+C%G`SOe>s%1PuxJc#k>(nSZ*b>32K!pOeluz z7N{;^m%v>TC|o)fCmoVe+*aJur#ShQsYo5e$YLF$Dd)Lit^z6pxz%^K6E4N_OX&{$ zpSy|_CWT+%S6dKH0NW+M!>t2)mGW-fUjn7K54s-szv7STdoOek5To3xPtmWzeh|!K z-3JK&#*LHgA#e~#R`?dTy0Q{H4wdfjp@-ct1|WaP5j+|wRM#pVF-!Ou?xO;3wQw6Q zdXjMQ7f0?K^fz!8{0dHipTLjcBJo<`{DS*ua0Z+PTJS0USJl6U%yFPP&|Dil&q{%; z?hefetqIjjl6nh7??HqTrjtN@Ku|AqWWZks9xa0(0$B*(3=MHb4|)Rq@{B%Pu3w?iLcc!Pje8f+?}{x&HYc*5 z;NAv416#mGunDXOI>NNCzvx1*UpIR7O-1{Pik|eWMv?u@p`Q3GfM+CV1iApdj5PpM zNA5mQ73jAE;z0$l8C&%um-+?b!k`e)qltphY8btk_=SL9BJl@4D4-Y5bsicGgSIg_ zpf5vT0@-nA1A0fiAJEleAN~mVTLEo}w1vG&_5BU5fOh!*f?fufz#rfuxB#9|23)y6 zRsTAi{lN{WK<=$V+rL9d>=uI8L1~1qL1UrnKI+G+KFNr`0eR4LLa%->=vwjR&KZ=|ggnzo4w2u+h(87YC)mJ)i`L zb*aJ?ub<17yBM%PnQg;MWug8{x{+cOq$I7d1oWHQHe7D~gsXmjMn)z;D}h9y7OxD6 zS4LFQPzQ7Yoj?cB8ngn< z!IPjPco;kZdI0}}{CN~K0u4be&;Zm2H9-wv4N^&|gk+4uWKeA&Lu8l|SMf`Stp?>( ze3Fr@_^lpv_ydemAR#HS%=hD#KwXzgNQqjiIGc!MEU#4_Y^3yx_n&dpzl{`qz-5xV z2e8Hb816ct3y`5IUKOQ^Sf!_GP_f$LwpF5HSG6<-D(T>~0!I4@RIZx1)LIyvGh1}c z;88AZ2}n)~ZJ69LQU+Q*;*l=#Sbim}c=7w?QSpo6A(z?&#Ly;9nE0)6l4+YJUIs`m zkUQujZoCqgUdaaW_yY~2y~`wpVzidZFq;!8R7ps=Rbt~Te`n&X4#kPLs~aZ2_^k1c zfzah-wgOzG<6Q^4LpvR5X%5H=X-5|^$) z%|}l11-3F9g)v5F0HHvqLocKJr7Y&uD(}N_b79h5@HYwLE4zW?FkS9=6TCs#>sd^v z_q>J7nD@L9tqv1t2-J&k-vWJB^#nK$I+1l*u7kVj@cclSa;1a2X;9^82C#>7YjJ;^ z#hiT4Tf+Y~?nPiBcniz}bHN-i8_WV4UIopl)-5{Ujav}JW8*i%yO{8I+&B%-@8Vto zGGXsB;(6yXf65P;FDGId&!AeP5HzK69 z*C)^oKno5#+lfbUn+YF}JFU!m!X>BhtuFl(y4`i7-yir07x1r`G>jlO_kCpZmG zfv><%;A^l4>;rqjZtx{g0ty$OCqXmX!+j*s1lkyAD*ci0^d3|z{vkh%t;7r-1u9zQM$2R| z=#2a!V0mPK9++D`o%dWI>^%4#oCDf4{07~EoN|2@{Hk#9GdKfmRr%$Rl0U&A6zkdU z4H&M1zri2iBCrBiaQ_AV1ed|3Ajsfr_^$)$l+4W_xAp-F4}~h6czz{<0L!f3wa{sl z8l^^@z^(+EgYfJ?yN_&89m4CdUKHa)8lm4>y$=)z`9NL}0d%)I1{wwOgGf*SM1!J0zul$ZZq?%q zeU>>2{=)bRfr3E4?OF`f(l%O)dTp005fBf`f-*oq-l<=HDyev=egL*4xQs$omxe)= zTt&hvfbyUmxECaVNQ$@`G!aw*NuV;=fiYE~t$@EJe_DX%pc!Zint;aOLC^?102+b@ zpgyPv?gw>29Z(z80yRMmP#vfWwc%WO9S{FjzK}IQebw8?Cv|+TO3m2#q}XSAewgD! zZ@~=yt_^VX>rc7Q2%?(}r ze%S&CCl0s}8#bGe9E9jzUL;jv*0_0&e{ej+LgnqhYD zFJjJ2@yCYg`5JdBlXK9eRX$xmeXHiA@W}UK!)_9zoFoz>BfSfsd%tW_&GE5eMG27! z?V#d#`-wLe?AmvsRBYITgk&c~N1W0caj3?W?w^(nkM)Q3Bq)|3ZLeg)<5lu)diw0^ z%VWcIFsBO8sG>s6w5U5G9?5b|&L~snln8?usFWW5YazDCx>jmE5ro z=I@_w)TV?iN$b}Q#IbZ#i3fUycj^%vrlC=} zS9eisdFjW9xE6b+xBro%BZy|2+yUx?CVe??!{)8i2giohG%MD7qx?NwhHiZye@4xD z(N{CCJu>Rjz}T<>#PC0PqDz0iGA?0Y)z+tC!-f%}e7y~o@*Agr{$Rk7ve&Vm5asKc zXZsH5+5MT2_C*_=?-zeMG&XECG5Lu(1C=4g+I4Sxc}nYy8DqnCn#P}aqr4Z1370nW zG+s}*&y4%T+ryL3XU>1(jZ9pCl&bpM9M!+tay-+Q8AIdCRV){e$*YJ_w#S#4KIf}> zXJ)6yY%(=Ac%!2Cz#yv%G z5o~JQW@W#hA77HnU`!ntLg$C*78=AD;w95@ixk8~U=eVvks7JXrc&zQoO!o9$KesTyeh&l96zY;V%)x*K<2?35bw zhM6ecOJLA|@yXH$YQ?mzGAEVcQ?nU{(C^G)NX$j}$Vl?q>D?n6zVX!7RK9FQOwMiI z$exRfI#sq|;p)#GSoZz{r7)sWqO3?esg+8Z)Y6WnrBcSywP;g^U+Znqtj=?5*Oj6H zR4G?6f#Xs$c^h`^F*T0U;OV3y7tFD36xa=u?L>H#Cv!1Vp;~xkbpB#akBkTzQfu9Z zzDjYS3FQ(3eYTY8x*a9e%`lOcX4y%UbT+F#@=KvG>~iK3>KODj2&bJ~ z;!-A;py?nm8^kdin4Ay#V)LY(4dR#$8t?QL3GHQ`-brDNFc(gRN13@hX|9vZ=F=41 z4Aa$6aOViRcOhl2ZgC}g#_IeNzRVC8npm!qdSA$LVpOF)Unr5M)x*nL#D!Kgp+&Oh zHcbYo#?KkZq(Z@?Tb3^?hb*Hb}Tt6GVhXF%llgsy< z$@L|Cqe?o47mT^#pS<|=jsPFy9h*J#{Vq&%_(HFkGO*i3+(0)i-#yMifBwU9lcr_) zsY*acVC?B-mfcC}+LtKyoASHS=qh$pVg2x+z@28*ZpNI;r5zhv{jjLl8;`Ac82uH> z#j8p4k?@MX2$Sb41~11Qt0D{e=O{IzjMGnpxiXh^I!ey{r3yb?tZi=eCNY-LclVl+ zUopab=61))wPg zJaN?~y_b<%WBd!uoH_1wQXT)$n-r zFVWPFc9JTWXgclpMkY02Hr2?#!tXzm+xz>}ROZfZ%o7uz+dpCQlgX(ugA>hCWIeAX znr-{N@iA$~{0Q}}N?Lg>O*A=Uvc}V`8-DFg@WfX&&wq_ziE3ub*WS{ZDpsSCP4gzc zyr#;;|K^}3likv_cH_c&$`KXqFmI zvr4}qB-b#zzah<6YM3+Mc#~s>)O4Cm+g39}ZvK9CCrzfJGaZboX>wi)kMxYMX=Wdw zXbhYhKI?j~6mAr;F*?8*Xk70SBRl%!FJJxdk1i#-F;rK0EpzsOH=fzN@IeN!dup3% z2WfHd);4_(dLu&v{U~21mMQPmHuDvet&TZx(3?z5+e0)CM?TVX-~DFOA*_BEn;!5* zri5p&XR?3mjm)2RCaU1fL}ug*zC5PIw}Fwg{kJrR^yk)b^~@wxIg@5o%;b9P*2&ZH z*%yYqo40O>Qp}Q!d$gsM^~_NiqqoALJ;d7X5&MedIDfNLXeGvUt;UXC*3Je6(beN}z&9w2-uWo4Bx@44}Ukz^2$6dp)6HglLR0E8h&ySB;@CB*x`Qcy?XH-h3thMhWwQl$0Pw z1Apo1%O|}xwD+(;7hr|WiA)3Y$}yU5!v^M^W3+I0ac#~Y^A=)Hkb|Xk;tnj6bek?` zZ(W_#^)e$yLhSR;$9AgpN<*jd%*gxUF9pV~?-fWfFp;b<9gj0j1{UK@JqsI}WLT3hYT~Yg(*~Dmm zi^`q5ck7}#zIJ7qdzUvdH<9&pdC=6Z#-L76uLn*1Nk)l_ru#|y&1YuyNpG2Ab^g=g{bnBAwO@&-)GLIvYHDIuGb}=WKx0BPb37(%q^EW>(*i&Hy;rNS)%d-c zW6G7Q!=BvI?u~_>&_jf%N8U2Ce(*;1EY#dt3w3*B>z3I$=4SGQdI`x3Ls@8`UiP0Y zHneZfM@ZUPbcHk`Mzc*t--TaGH;BmS2~8v~44>$O0j&@3#ZK;m{L%u4wTD5S>_iVRE+k9VrQ@ud?f!gdyjIyw_Z^id}pL={Z zG4A@|rdjtR3%8~WO;W$N-Qqos{+#m@%R=o4>2%x8?_$_RA*i-*^bTFwFRtEq!Rf15 zD83p$L7YO0xq~TerqmeLv@#QZ@Ykz_ zv2YkD+PSS+EZV!RIeLoa(b%@mrfgkAzT6#Ooi-{X<4}COaz2+BodAAt>+D5eft|%u zW7eB8rzw*|Flb00H(*A|@5eRDmdbF+bcP`*cROcv3VU{6x8A$|@T4+SaAR5@-8k>X zKSvfwjcL}-EI+NT35RBsD(8na&z|!|_M_IW@ovoO;u$+uI5Rd+YRuwx<~p*`JK$ii znOwQ#vMTY{_B2T4_}P>{gJI#1aOe!fl}^(}RGPW-Rw_d|Vpu06zcw=Fkr4wgl}e2% z-^GkLF4(asGNDmz|+3U-jF@dKWX5`PbhI3{HB<5-tr};0OGW%?% z*e-Rb9co&&lew$e^Ru^!r}g8e&@be=$K%ebq36fd->DS#^r?)YfweiUVz3)?ddjmG z=heErE;Z)m$4zf!V`jP>NlkXGnfKcJLsL1HKW^s2!GdUHWY#>HS?r_mYg5D2PR_nH^nW z?f#!1e>9b21u>dPTh-iCCZ=Gnw^L(2GvQ}xriWdIB@KFZ98jy|%v6R8rWOn_nY%k0 z;&Tmu_{7>)kEJpc>~5Yt%d&4UGmb{?*)OcS|D7Az7X@~WfwBFu-X`a7bk=^oP3hn0 zR$)(@PWYLs=7sxMSM%&Z2U$^KSSrSPeayDsm~wZTd(Y8A_M83}y$tI#h9jn{LQa`s zkeG{ooMHB}N#U>7diYFs5AA<2YXJWBH7n0ioCW$ieg2;N7Pc$V;nM3&gG}wxlt_%s zegE%5-%U!M{ZT|{F+y??QoFCYc@8s~R^xwX(%S0kXxHqO3HM%n?K8DZEe|S&z?u9z z8Tr!mFX4;I^CyfN!Dr9UJ#fV*J@Z9`errDa-CHEOTR$hQc~4dTVEf-gZw1Dtz{u0V zgq~+X7uDZkc(~;k&AR4%e>)8BKB-uL)8ag!Q0 z@lLnd&e~`<<15KA`R_|p7no0O$DkGsPIQGb@M~^N$G$U14y@BR@ zB%Cb=nxm5JG|==b?2C-)g+z8FT7+=!rtli{rw2el+IftEqbK&@5;uI^?r9xtdM6OWtOj(+rd2_B>H}us$ z&g99uf%OM{Bqz!L`qZ!&yw#HCU~1)Zl{N9YdGbUJHk-1NY?;Ak&tu?l@`salEoEZ;^ zDLBlT&pvs1TA}B^S?kylNF}kai7ie!3>jv+G-uYL@B*jTQ6BGLll5=Xe`T17$&Tg~ z!%S`b(Yw*5=G5?H@A{=Tf9O~oua*80v$?e|e})PiB2>Ii1z$1q?g`JIAwDEfmmH=( zZ;t%UsYTTDChjVy7Nv)qPFLwP?(ug0;m)jACt^v@0xKTYc0fna4}`nTYFbJl)JWNpRdy zIN7u)Mfu#9Y?801#(0L|KnauJ0y={?f4xItNufU2x#jx^^d z`qcU>hH#`X`vwOW|2(~UWRx=$^ngZCBfs9)wd;=fW?l-eVmZ!AdE;c1iM@&57e|?D zH`y!%PDb)oWj>lY(iwRAPW>v!h;dC!&d>{J(YpO#6+@(_z*tiuEIcAit(4rIR|@Hb z)x1Sq5YNBz2lQ&2dl#AimZ#r!+If)Pbo0QfEFp;RU)h{iZ_aRP5cvuoU%VabZXdzS zN)H3ycJqaYQEk!yb4r}IvoNtX%|LT;LQQf8UnO_e$wa-T9V|DP$wg8J$WZ6`Q6Ov<#O?E!G-2nRhTpqP8tzaY^TZ@)M$J}yWA%D_%Xg>MY0t(O>-6(T z&)`XBLng}H={|W2SFFOS#hIwb)cQL5!9AZh4D|898u-4+CTkeX4JVtWnW2wNHns65 z78vVD3}63H@_n0M{2mE+Pt$L*Gq}FpFYn>Y*@uS)ntfor_})wm^Chr`+AI<{tB5k$ zGgE{+%>9{t3921(9g!)Xq4YRYQy;MSR^)KoLuM_Y52 zDK-Ju=k8YanX2Kml)%z8%1+Bc3!C%dzWA6E)0|c3dyCe!>DwZ+Rx}ij#_IOdP0g$% z5m?gN_G_0CX{TLtF)NkgvaU0AvQZIkChxM)X*APJ%0{l-1)$lO%~#n~Vx_IU?sRWD zW%oU4)4A)C%@)~Rk}oyO8_)!Z(Yk1lqH#h5vf(^tfkyMgI)g()5b9lgLRw{4NAx9da^+ICW$W~R`V zn);7Pe|_!NqFw7|m~ZMu_Nd%p=2%2(leCS>_AYmc4s+MMaJg&Uy9}MS zUAWyb`uKcjnVRRDxN>i;nz2EJSGAn>IOoj`sSH^#r6BDm*8->gym^>JC9m3PWv0)!P2B>%WNXWOu-C_7Y-_yPSj#;I zXHDIH#k!AouRElBJG3yhoc(Uhl$^ty^ zt_f)m9()Zao%M_-+Y)mQwK3^$#cgkP*I{XP(RK=riCp476Ir%*&~FE$5-~`punekY zmY5Cfw|R`B@-ycPGb(ZT6w7+UZLW8zMyDg239-IWp8QKqPTId)1TnEooux?evD+7J zjQvWZggg4*-rPdnRiN!Y_T<3s!R>j#J?P6pf8RRez}^==Uba|8>dYp0Y(sX792jeZ z8u~v5V^7ItCbFoniOsFur^hT<=5)QdITx=Es8(~mQ->Pn-d|?=X!q%^6#p?H<#hJT zw3-tpv>1&tFfZjb6^i*X#$;XY9N1-jt$yoVV-B&Fw1+-%%T0@7zR2h*aA@8vIQhXo zkI#LbhyIqsJzWUQp|0xSpd)54sd>h%FlW)@aX0!}Hfpo%>>b^+iNJO+B6HxJ!e)o` z#}#KXab}Tpj@Q%)-3s({UukNVK(TXP7mt5%sVr6|NZ zJkN0#;LbA_ttSFH1E=62&atF9UW#Nln`=rcPz-t5E~cI*I!n%&#_v0;hsF_Gec#5F z>aEnN6Rm7C^J$A&F(lUlGxZ*yZt&z6e`2kA^ivXGm+JUlA!}u@2LCj!fnX%c#M;`koBfl8H!~Q3_3gQ{a43KJx31V zQ5DJq&qG#yVCG8sCvfOd(DO~t_L-9T?;~(vtdieBj85ndmQOzW;zK`a%ykRwfVohX z^7zRND-Ze86v5}=DSq{`j7%BVIK8BQwTGrxE41{JfWkmg@X)3o9G<*uOznH2CD)h@ z%c0ewWm#%FYCRo3G+Bv`dGABVvcI#%eUP{I-1DB$`GjbOcNN5x`q)XTOSNlZGiE)@ z!5L-;{yA3(;?1IaslJiXFtL46Yx1&{Mzl z?s3FW0jj+g>rAI|nAmBZd1VFE&4O86j=9`=j>@z26LYm3g9g*W1Let#TR2WVnC0bt zm13@LbZYC#XO`c(nqx^%j<~hcq}^rQWNsqlo(x8Wskk28WSUf<82WB@mgo6y#?{}r zV*e|FYDm;wgW;P^G91qBrYO(2%_cUU8&98C@D<|8{k(Wi>=|GKx0<4KZzQH7N%Y-p zT0mpgZE?o`tc&;Tsg`l34hk_vTc3_w&FqTg)#^Jx&X=!d@2$LpKoNah%qhfSk@sEg z`C{uHIYVts!lzE|rZ!*c8}-Hqx(LG7K)G`iaW)Buiuq`^lNsB*nZJD?$pntBLmCm2 ziK$ z>IQE&L**Z{-7JT42y-Hlo_g9`gXjWHz9bSOc0>}~Luwk|qrQ7HQ{)LDsMwW+=#ZUL zDr3lmoz5Vz>xY=itur*v8>BxMKxeuk4Fu^e$M% zmk@m?2B-R+%c^Ef6|&ZMx0zc712*k;N_F7!BF{a#rowI>wwKd^5GDDQ=~vYk*&_NY z=h!g2|HH^JrH3&1T8{E=jK9aiqFX0k&X5}O95I?vt1iF){+yl(JOj9&5Jrz=SF6XR z<^D4rW=&OJHP4{E=3-TB-@4ZsuL>nJ=z3=8n{lay=R9PM_q5$-o~`C<@{hJzD&;n9 z`lWSbpQC-v)qX#eFR{(X^rmS@f6`+PGzkMB3-sx#^@E9Z39 z^4#n0^hMV{18tSNUx^{T_d6}Vb+AgClPbfY z>+3Z0(fiFxlqbCkhbE3DD~8W$xqQ_*S2=BFCNVjPsnKiU=#iEBYOd3xV=8mWesi)1HNxej~c%C#HrsnN#^`xbk6)G`f9DJeG9$)En@Pb@UP4}26p*iX*)Nj zGH+m-FKf``x0}5+efbJevqcX$%dEcB#+-Sp(54fqtbt3iO+8f)nAlpJE5IAL-x?LV z&CKHVZ1m#?ouj*TgMVpxsKE_q%N3Z>0@|Zo$;g<^j7mCetJUe^z^nJqS?VT%LPvLG z(uG4#xzEfn;`NyJYvKbOfpf!a#As|7F>pyjqo#i^bY&?t@3*E;Ehd5j-#YF5`;oH~ zjwcN}2#01Vw9}F2*P`&s9yVo{b7))nh&hjh=Yb=}SKHSlsU5Gis@R8Ry;Y-7^N+L& z(9=0|KS4}xVqy+lKU}%(2Rg^pE`XRJN6duUq&gN3Rd;le!+$p|)b7myM}QNuix)0*!aJhe$rFe^>c5*a{rkffjO}8|1wu9-~ZHz>W}$i&A1P}IZf@2zG7y2 zJ>RGY{#OO_VYm7|U#LIr%YShxFaM=|y)G{0^}4h##Kon&5101!vA8?EK9=&n09~ap zZ8n{1BPQuMcmKj8O|gayZ;g(d+6{fvQ!ccobJ6uK6K3EgkElxjO|6=h6|?3w8z1mx ziYa@->1s`O_Zv07$W!m6ZZE5!Fvq!a>uy<0xsJX{N$F=U{g~*J=J`gx9_9WsL-qDW zAJ3?hCiy`Ivxz57i*^i1Q%^dB%9<*B+D;o>AXx(gb3s*JvOj5d!x{aa%Q3Fs&0qE{ z_->y#0&Ay`jV81)lfswA-ZeSF7P?o@>%FJUQJ8g^Zd1$fKv3&bW>*V>x}Gv+o1>>M zdNdk$ej;Z0=|Qtsq|O4)1NW4Z`%D~za*c0AajSmf6hR-LfZ%-w!S=Pc=(Bs;DmwXTYc?$Iw z{p!z7hKd%OTgNl*Iqj-6X;J-Y3OBiB;OJhB#4Yf8zA8UBrOMs?Lai`A+plJ3@vM=i zMJr$KOvQfXnxHP9=k-ke)r@Gx!Z(e|eXV?DeKBX9dG+D5=6D;G^jXtMbii3t{UJ(x z6!8%h#kBKn+N`+nBbT%iC^Pm-AydwpesDzRW9P5U|D581zMt%@reDnTTB*bL9X^aI_o|(n{GE>UlAK&S&)tIT`ZsT82hVirHkuP1 zd9h?dN1rcU4%@5-B&I(nrd__3N_qQyhk2rtZ{!_XS0R(s*;gsuRI#gIx{=nTns?`@ z(8bs8?i}=%f1v{o-(8wn{=ZD^uDn?)G@Ul z^HqM}KU=?~Z=O+>{-@evpGx~Jinx?-QT(s0b_U;Gf0^WN?DB{IP)wuAnY4qlR zxX=VWdE*?wxG^Wjb!|KQ%YIc-V^;lTb|V|T6Ao?LC&u)yH?(=#(E*OY`%}kEXm=)q zOC}l;ljn+aNIA33FVD|vII?Z3rV>|7i|$xa6%I|4Iltc#f8^@8xKxg%MFW#heP z`c*z2lN!_gikXe9r{5LxQFq__p4C^(`5ug7tPbnyJ>DKH&fKt=+*h3y$(S~;K6xqg zJzE%rbY{y)XF0mUq4x$Vjha;CzOOcy_y>ol9v+GWgh)6DHnRi&SR%jg>HdHts8(v$R@ z;56xF$QBZ6cJ%aBye)qjq6n?{6n8D-anyL)m*3+io1qZ_eqt(j4han@LIy8yThqK( zk()ZZa5I>`WyuH^?(NI(<~_rk_(Oa7@+Yo9nXdCb-D~KDfvekok*aJbF$IY6ReP#v z$p(3jrpDY}a3;BzuRw}*X>w`3HWMj!+^!=<+}4K8b&5Z@5k#-e5E^J;%^s{%I=R^w zdV9?+frF+9Rhv?HG3VgSPbNzI@~OEN7;Srr*;vL2H>UUQdyXW&;t?bu)$r zUXOXJf492X{Cn90T2ZI{q?BRwzgto^En6sV%S*BLzl;x5KsvJN1jpDQGMht@Uy)34~bM%8vX4N2el>3Petm7~LeCx5?M`wF9Oa_L!)8-2pLa&)3 zL+O|~yi*4M8wPm#dQI#zY&`c2V0>^qJSy{s1`g>iXU+A~hv%afPjj&GlXLC1r9D1 zGoKu^?;&IpoV#-I;pY4^0pnMV3(p&-CD=RRrrom~Nv0_kv*B6HwuR~z)$M_4BzS0{ zVluBh$385*>M=P7qSjUC>66_w7-*jvsWN%yb^hM0ffoPFWHrf3+Nu=NsiY9Vp?z zbPXi6lZ)GyZT9TH7d(QbUPxwU_|KY`IX{#KrcN=NxM98) z(K%e3nwFVlw7|DM9q&Ypp*EHSMW(R{iYgp;`Rr{Bs zcNs@s&ujW5Q`L*}nsLehmwdGTmsTX*sR*3WtVh1kz!TYbn-|bKM&kgU!e(ynZw~+RQ{@ z%KMD3M4LVM6K5i*+3=AiMSsaQcA&0$(e5;!E+$4Np)m=$s}$c;Y!)$maz`72KTJU` zCxxyx^Dgn(ucjuvAhSg;-B)^Vye7RaX7_mdO&~tPl%L?MYQLZ5e&ORsv-dQ2g#x>a zE9S!qzLB1Rg-x}I2;KfE5XTqOp-5=pa!a^}`)&fzYY{;$wcZzFd(#Jc^fQCVO0{c@MD zTcp2VP5*ISI^VD$AL+DCtyk`Ja$xp!;X%~Rj{2n4(eMIy$&>R{Esy;|$nBrWa=wrC zA>YT!_}_iVP1cO*IC0gENbSy&WiT-&t08;-B%zn z?ZurqrgHA>BvcM*Rn+Mx8;Tw5Ix|D_`Ej91v@g!rLxR4IW%|6~tK?Z+)V%cur;0Jf zOx8C^uS79Z8yfv>F{cy0xTD?mqD8C!K^t{9KBvv*H(6W9z@TB^)`9X5dCp9$8R(0F zWv|0P{}Z39IMQUFLTW7cHqGFgRJ$p@LU*b6+dr!2JZNKmb8HIhoBYL0grQdka+2UF zRnkl`zIw%~lyo*~{~X|KjFWbx$v2e&;f0c>!ffWSS4)~g%UJ!*KwKkB!vmg<&;C`2 zeI5(Az=6t=k|ud7oUV8ZZCJ|eCOoN4DQDeS>e>Sl9j|venJU?d7~TKt-XiC&gkRp9 zmm1Tvl!=)}ssmk)zd}A;J+kbde67l+I+dE{RlerF`0 zE}wi~cHijKn7O6QY-FR~heNxcl2g`y6B)L$dn(80X74nHmILNjNX+l><$~{}iDMHo z4?o4mNdGyYVLLd0mc_~_X9Zfl>;#ElheU&(t+A7nih z?=j=1)3B=FW7gx3c>oEO%C}t>yi{*O!TzZd58Y$VE45B=D9^jz`|i?)jmvr7WpzI3 zrgo;)pPBC8`e=pJnEv;enln;rDL$os-BjILOAWV;p~OsI?qbzSJ0{h4>F%6=tUY;u z*95oCvp8;1+H{zS`i`Z|^;w)Ay)u)=?6w`xlcmj3MW$^j^-G(wFmt84{;a@@FSb7n zUf{deY@X#SoYI&ay{YE8D$P^NXsfYh%?P#CvSrQG*_33JvS!z8ZkANL*O^X+m0q*H z>kpNj^mX)@$aALi79+6wxB%>QfeJ;0(&mj2P15fzycP(i|s63hxn9E^xLE9PAz zh>1l-0dqokb!?sVIAbe?rvqgkCW_tXRRVu1M}LO0 zl%5VRrrm;ANn0ikkI?wY`489h5CP*9`4j-H6%!Ou@bD| z;hkJ_!gge_Kv@bICK>MQ*H+@g`de3R!%`i4O2a7zek&!cg~@5Z05F{V_1K+_5#^L| z2ltdy9Y+pRvd{Z_Bt6~Zi7-Em6;V>fJ;OH|DnxMcvTV$w&c$O&GWRrLeHRYdhKZH= zIlkUE$NVPgp|?cs5CQOV-Mm5ov zbU{T-+ZREY5YvZyURb^5D?FU}EnkzAMgcg7xSLM)c|>CxvcbX4;c9iY5?Z1`4Vnjs z1DCEfcyr4-KblhG{CilVTGgPV8?a**=i4F>p<#U_KT}%Vsl$+k4HB41Q5^A(uSq=_ zwZ({OEU7Pl%dZ#}^4)~hYC|oG*krq~AqVa&GI6#qf5lfF8cb!!0L-}%6tk$#@ToTM ziRUjaZHf@ZoXM}hUa~dm=sP!a4cF^ZyeKl)uW+Xp~Css<7Uz+M_m!QwBs%o>M)E&msTme zpAb!rdUSg$#8vZpRukVheG~{JAHKIy8(5=xmI1hNN8|8Jn+30rWGXWtmM(=R&%UQ88rmzx8vPz&@ zYVnpY1(Y2_Wz+BHx<~6tLROm$hg;`fx${3XN&}lDXKP6^vhoC zZQYyDLqKXeHl+&tFiAdAk9}Ai?VE9~>~&~gr&f-6>nN^@xW0fxZRD^Hrk;zgyRd#? zEGuwgRC0Wp(awEX9RdWzm%TYl2iC7LU>$idFOX6FiIZXzHFir}vrg9+D8X zk&_RNbXrv9#@uW3e9SpNG^fibtBGq)8OPAdKmlo7wxmq24I{JQJFYqT{SJsUK(KR{ zqp9w@SE#SIHrKVbIYk2k=SB-oqGx{!K%X(^)}`Gj)zu7mlh*tWPIwIfrbZ-9zJBmi zj(xB=N__L+@hO&*&t3b*jf&!Gqs%e1ppOiTvH~Lh_MsD3kJm~vLsV-)fd@d17Jy)) zqE@YUx?8HjpJ~h zNYCR5{WqF(wv+k<4()Cpz-pWknhGY=bZANGhwv3lsflk%y$<8IU>`&OmV8L}djI}p zz~+AOin62FbVs+O01Ss57E=v;m^}2YxF|b3#xd8d7Mbu6kn|DbNXA1rJKRd~hhS;g zjKe23uSN|x^C2u{57vIMDyhDMpvDr7{}w)1Yf3r)eL{TIjx4vdHR;ZRqqft&BbW-` zA?xZklyclmUR$;5x3}Tk6_;MSX0?3{H<+o1Sb3K~FfW_rl_O|D7(0O9k105E3@=vFNAeSQ`4_5j`e_mZtc-Z@d`a z#!PU5x#+iMOb6Qg2b3S%W6CK50_8pRUl7RF0Li7nfS`|x=#=a)&a*s9zXIVGQdubeINNP+epg@ zutgM!w%d>NOVLB`tPlIp4UJ)Em8MmN4*AS3PKdid2!E+2$ zb7&1LhK8zHJlzDO-paKk=oM_2_KAXSW1V#9K@D!Bs=|01l(}!lAE4E(!83#*p@{Z_1Cs9^2vj^S1 z4aCEG(!4u30lGcT!I_+KPR|bOz0|_-VAvhpgAyPjja_M0oC_Li`t&02yQl-+2+i+e z=W4^kiu0zWy=muN^yyx2ZiKx0s^}h%LB|<+gq5XRB0U9!3`}@4M(d82P;%;86sHZyslLC{n4Shup3m` z0B!3e(`8mR8W7A>&Hm{U}$$;ssR06kbP zAdWZsJ0PlB;%jr+z0~0WHi+i|!0a(W<5y=s9}vm-Lsb4;g)WKl*YcM}+9ema~ z^V33OVc^S5n!p!ekxxOdul;5u&wm}r9Ay>1nQYC6sDG!6Pr-Lz^~F3(-D%ik96}1z zlx|LW1}mrCFuM8-6DWI81PII4LPN{GWdX=a6$=VTYKqbDpyMwbM&Qh^@kEh4 zoq9X{Q`|7@0Wn5mox|)9{zso^?n_{ZbH|4-Aw=M);r$9<5s{JNUdfRWZcrgIi(T04 z`7=Fzg|QIPrwm>x{6&WL=br{NO7}!OZa{foc{c1F05DPdZqUc zdfg2*31NMmhP-0}5CHiw#LXTQG`1WN_X+^YTRwQ~mmfOO(RXl1P>`|q7m;r4MsVqGgbbS!W8Yu;c%Mi_HQiXcYW{r5Q9lEe^P zr2+4xE&)41E!n^d{pd{R8Jr83bAh zhAcv1i;&?gidQ*PK<6go>8d;0Nf=KlCA0ai%N&a6PhJVBR&+>g_U(@@@ zwtd+CK>!Z6c*{qAeNB)2+BlIQpDJ$_Va+df{+G8Bb1` zaaa4TTfqF>N3(niMX9y3gDel4d`$;UHhcj2n`h?NBR#e>)oAV1Mk~%Z{9O!{ji_Bv zYDCm7gZ1FL?5ezYDTa$_x58nn4^y~~*YjQZw+(-4st4zXLQh6z{r)0HJB0J+aS!3* z8m8i29xiw+M1;F{p#(c^P99j^RRPI8l_sN9Siz}W3e8CyK6B=S^(+RB@W!0a@kfM608>sha<6b%UDF9Jd}_R1e0R*b|M03dKN9gnjkhiRp;mrD-r z+TgM?a>Vgl$xfudb{&*C36LtK0pXbEYO}(Rci_BIN<<#A z-W(Dh1J@~+R)kC|Kr3@;z1cKBo(sLHBU}YiH`iZeAO8Cr)B-Xv&xi2osks4AW4c;Z z9L*ft;8m~6Pj#8gfe^euO{dV@+R~b-(B=6ggQR`Qc@s(F6zhtrzM{%N?}Ard3X? zAC|wVnpT@%FImV6b5Vj;o4_=mt1Q^R#hPgsu5zD80Xh(({5;NmiOnCr{q>Wvg~Arf z$`y+oe{_OYrId2nQU0wtXYxE6jIxaP7or2oXnRGQiyd`uIxN&sfP(% zA}y=qZ%`D3IxJuOkQH!Mz}x=;-?Q z%Ka;I0#&sa&^)|op`Q*2yh)dGecPbzuR1PuF?aV*I_;?SFa|GFD;ILNm7Y=P$=2!X zHEJiM6hRT&&#Q<>RE)MCQHd3G44yi>sqc}qEV7t*fMwJ|>d78p$p8eKxUC*d8J(lq z@>yoY{trIDqPP73%URlk7g~&;fChU9rfN{XxzkU1H8-PCbP*?vpTgkY-RIrja<-&-pe`jt$p$1v`Z1o1o;_@%|s5= zkd(8_@9f%gV)8(9Ek|jN3z~TW07js42WvO2v+`Js8KB?_I)%!Ol~$-#et>wg>-dcx z-8yF3K6FD48`H3uNh3R+O>;Mw9kqfAyRu#aq7)!@59qRS!^-OC%n*N36IZOFZz&!h zgV##l*{k7Y#@5>sJJt->a3!Uqrm#dnFnSrg6`B3+;GYdR1nc4^?h@^-H=|t3{r9w`)09K zRH-1=87W-3vBD)M3ln{vF1n#wOa0JUZNHkgJ8=+RjzWW>gD1>>YbLIy;%-ojZCAuw(g2x>ZeV8HfL!*o_fU)+y^t{5z_wpx;dTUS<*xOz(mnlJ z?o_oJU*@ejdROIMm*QsCl0y+JcY=&+%A-_rMQgl{-n(;ZvUKB|b==<4_ha{DO!|zTP8yx0Np0MXtPAe#=*dvZ(}F@-uXJw$pDuTmc4+G>f+Rp}tJqLmx zyRrv^EVJZqIaYjM??TZfuz}*wkC|ox&r|dz#ZK@ItXRHy#LQrO!){rDc>T=OphS*u z{h1*m++>K5dC+$J*vn}`7@A&Ev#eXbv}TzuUt+UNxBQ)D%oXOA?elllTI77Lz$v@jA0`WtMkh*?^& zd;^U2)?iDJv8Ok=0Fz^!r6y%wW#I)mpR*1Ce`AhC%n$!+zR7uJsiX_*7Uvufx?WtX z*sx^v3W;p%$ysNum30D?gcEC^>KhX{EB?0lP9wG^hI2=_VtB@FdNIS2m7ZUZ73U;G zM(ci*uNteOxAd8C`0t?{l$|<;?vu{^`msheHaB{AAd|A{Gfr^ zekr}^pA_hiD)EkCJ%4Q%M5oB~*M=B+oZ;RX{`F%=C0{zw1U15)>Kt##@95tT*PJR)Q@IAB+=fNKt6qS>s_11zbsf*q$xsx; z4Vm3YHuew&TV8t{x~5hD)DU6eQuRj;yIG=-elg{jHU*=Uhr|^Z^Iy)9E(mo@K1UfL z_@1Q6ZZQY{!uwV?)TVWzZ=%dZb~ z3SawD;0&e81{ru4y0F^?3=Iklg~+MjfEt8q8(6jh+HC>tjOr{`6SC2?|2b-IL^<=# z1(t0HyULLo^zv-^VXJ!mz&fumi7NIldTPYz*|-c55~?bEjf#eAQ>;ftP|jjF8&-f~)xFe?{dpcAq>th|mQUc0|XZBSJI;yUN9oEJXS?2&OI18gip zDIBLs48ZoA^2g~~Tbx_8x=EMIfmOQRq_^cTC-L$)!ey zeSE(ns0qJT03}W(jvK`F?5mXT0$x_BeTzP#Vl{tGK$E*D1y+DPl;0`PMBSpH6+rL~ zs5C$Lr0=4x9gL-#RP#l6T|OkeZ_(syKy3IeIu1z9q+8^79gOk0g0`E#ZTr8KgA1Bg zQR`!vhnCo!=rDfl>n_Vmy<(3@@RnBHqEi)t+E&U@Ny{SLR6d3`RS!{&}vy?$cEF=30|q1~uT!@uqD5;49PR zSENIocB+C7KBIjR+7L~rd*qE$BGu669^c0nbUX3$*1zhVS9+lAPR8D&jMk`j`8{f1 z1@gj*TMk&6iQa8Ag*9-TR^g+!8U$H^yFxyD0MP3#KlOR~yj9DY3#Tw61ol!4;vN-< z#K62Jbz?2gCz@(=nmXrtpN2LESDf9Wbo44zi)Ro9p;Fv8z$7 z@Yt|`v?LUDs;cc~NnIYEFhrw!nA!qFK;*KhY7n??4>=om?daU`_?>|}815Kv;NBNG z4j5q%ry1>aKaFLkCPAL;RcJ&{G${y0{3Gz5hJx(KxSrW3#wx%_=g3!b8cY9-qdl40tl1#Pj=2xev96+ z5Q@>wK=j#1A){rYq^68S$f73J1eR8!CJF-d{77qSp&1q<5s-)zxZuFoND6kPOEtA= zvIC-j8c~Bx7EOzvrf7#sfWzU9@WwDEAd7K0{5LI+amZ-Ig7BTLgO{ZdgjkQC%$$QpkFbY3>8OG>Ax8sUC(vGK?ffn2#MdMje}VK(kK{m0) zEOdWMgBwEpj)T~32%F$^Kr+XMoJB}i??GW^A{BJmj2w3VFIe~CM)N{vCz*3j)2D`7 zk3tUsz^q79@64UNW~LW(Mp@LvlXA+63&4`4O>C_B=^b5eib<4xigTF>2Oz#CVD)X> z7{e~F5sNtsHGVFVEy{BG$kjxw4<=c`uP!U$<(1&^+4$C?8Ci#b5$z~3D6>vQja*vB ze4xqb3*B!F`66+(UWpt(Q~$Fr60)eM@9PGw6HVzj)o6yDjQ$g!@WLOL3JKXZOsR!ZM4^Y0(?p`eh4fuAXm ztyfJDOP0y~<31jdg%0+>Wkd-fYhr^hbn2PxCf4Lb+MN1!A9$?a5B)Y~BHBM=BcQggmaLr+>lCQA&b@%XkmWQJj_TBA&^$>)x? znN;Xm33FMo8t9JzA|D7^HF#B#O*>r~T4Jm&(6d%x`{M zcupfZ>oQIG5m-N`)2FegvCqk|&XeA^=a;k*>{N{TO=G3wibSxX9Uz!@Mb6y4C-11d z5{D|vmJfQn1*#O?8ms{~!of_xllaR?iC_muTWcHWEmc*#p9i}ITkQ5yP#cVLtvuAA z4H$XiYVI#Gp~}wVb>{BB2h11~VJC^V?NR{o4F@J3(upV;PZ1gimj6(!-q_DYc!?DR`@Uqml+R+;9G`-tt-K~dAFC3=x4zRsdvF56zghbPL zI?&E`+6G43Z%$f`xPgZQ%QKIjz!<2>kWUNuwgj1 zRLq$0m(Kyee(x$Sz%zuXm6Ev_P+fj8x^Nhkn2hGwEYA zGz5+STN3KVU>kFhh5!Y9AbivrsV;9`lwazI%jIT1A$_VC!};3Ra6e!6t7-w7 z$jUYaM1C~6c%OI2bA1byHka)ra&~&GxW6*kwIjKrj1A?szU;gT}H@DloAME#Iy+BFEDsb~?*9Ek= zB>=iPtv9||)2kROs+^a?ZFLF&G>|^4(T1V03vZ)z`^2Ix@;^d2D%r;%oKOM6SQ8Lz z5}o{WX5}}}k}q&9SX)1eoMO2>j)#|D6VJ=CoE|O|&=qBe1A-m3?`z~96;kX#Uk1VX zGgaiAK5@TC@xBKUmp~5IDi<1zviiM%U`}7PhR$$5TPeAa8R9Ih>#7WA@BZkC#+By9 zK@7RO(r1*@lyyxVjE>cE)hI!@Bg;6wc(`z4GhUn#ubnG3=mx+(u5=2JG_)Hg?>0h2 zZVwIYOp%GmgGK8kgMmeB@&J4>;DJ3MPQ40hlmXBC#&ccK{y(o`gy+*Fs31i#G-?&3 z05GMY9UvL{GuGZa+`ikx{H%Ql03aX{mUsv@wrDdrEE6>_`)qiJgvU{(@*@-h3k=D6 zupK$fB$aMQ)3?qmyH*3+F=wtr)Z-`2_kRGu7R99}YZdu8=Y%7Ry~pINN+qJYX!2@l z=TF)OFrl#mxP(!)J3joEMq(zWpvcv)@!*|5TjGxY=__>?vo7(@@1Z^Mrdn9>m+`Q& z+J@)NF~GLnK{G)Cn%Eu6O>aF#_rS31?5zzo*q&f~m^HHvls*J9U>B)>#`uZC>MNAl z6F*gjD5@_S@G3;767UTxL>b8#oQ8#v1^UEY?z#QZrkr7{Cw%@0xhW77!!8IQxq#Qc ziGM<1CuO2m)@^D_v~5vVljcqHeg?uELy=4KdZFi{BGITuOd`!x9~wUrC9LC=m0TtS z1t{m{U_<93T)3Cq<6LuKi1rHBqF@Apfg;hT2xSZhUnlId@^-Rf9YF}sOKe7n2*tUW zm$FY$a!%5^*C|(&*Bibo_m&-o#)nWZbLa(&)kAL+-ZKmkwQ;O2mByF(k*O2pzYN&S-wi+n%c)Zrz_30tbBRIMvnT zW|8wiv!Ir}l#@kHd?5$q#22Fk$A&8@z*>j}Sy+e#i_S=yLPr?~tS-YjpmmuA52lyi z)}1`NFC_9(3<$)eBod2{MbrAY=G*~4W$vSYv?=X=F}KE*7ZbZp z_M`SGU_T-Mq|}kh4WHK*GQ%|Zg20j~>-y8~p;=g2;7AU`Ae02HB%;}6luVVQAl6`^ zX&G7saN4)Xru> zEk-n(P&9Q=lEq?K&EwtkQpIB2 zB|{2MObDc&zhoi3SRLd%ROXeYZ&%9oi#FJ3TOqg*#FdQ~^*@B&YE6?FnF5tJ5P1W( zrn|+`C1jW!RiVnr1xO-g$r5txgkoy~6Waq>n;cpxC?(IxnVXH$!3NtK5?PUCHZEM` zP_GN2kH3PV)^!*|RVc=6lTfDX#mX0sB)K?)Qp&{}l@N>;b0a&2WM3@1YB5Pf*S;@1 zL1Y1pyc|I+g;7aVQk|}&x!(77X@mTa$&_Zuptrj9!wj^7YgC~{= z*+Jd|(|nQ=xlb5Hjz@Fj!>ILmY@5p!rHS|*@INf~at+62J*Np;kARP1T-$9PG%fS& zg~QA(pJ|uSQEV>qdE|2lUq~I~;KI)qIcom2CrO+0KhO`dFN2cfC67vKNnRC-s~D8&zFsd%C_2%hkia!gsQa|Wt}mY-7BqLoBYt_xiXlF%`LnfHJAkL z)V9(g@sW#*Tx=x1Vl?%(1#foFlFNx0_H1gXXe&EM1ZxD6dfQ0ymFgS9U#cXsHxhdx z^fh8pFxVFUl50i(^|{Jxl!Ki`pEo>vUfSy@-OukJ7H#PMGCVk=xq065?tBm zyeWB;mflX&Hn3t+i>MsLcJ?axLqt!<^x*gXT)mKJ`=(!LMOq7BeT|BIf1Q4Md#}@u zJtu-6gbj8(U7N1;aCwLToNV^jdwZmN&L@kSJF9=FNRBhKXTRa-^8d-_KfV!Tb!H_> zClKpKCBCm1>{uw*_I`gbp9eT@v6&|>-oK$oct}M57;wyJ(R=B?s=g&WaJ?IM0hE8) z;DqN3^_+>l=5Lkxu6o6ld+Q%>bASz$jYBZvFBgoU-7_&UgCaD_?!WQMrS%f858uqD zJ%^|sL4i9l^35VhHw%!R0m<6`Ja%rKgAcnbkaY#C21HQgEQrnt5tPinGb2)WVYjmVXg_P5xbWku9bLDFb6pM3(dWe$3uS+0E{^_1M_Yk z>+9SH0Kz5iHcgogmG?QV!bh*F!Yze8e{0>~&5Gg!6v8Q6-p6!hwzjM0mqRqvh)qZI z94OgJng^_mGv>hWB)%S{yKpDi6P>OOueK*qZ^2-rY&jp(Nqd}H&F1*47Jt=Lj zHWW>so(nYaqIj-(Fj*)+Jv3raG$FNU{Ax4l+4H?1u)C&uEjl|-J6jM~69usONbXRJ zmd=Ou^$X44t#zZP^R)&|w_4|1{0~TJ$zud)E+Lhc8SU4$pZ|c3KAn!5C^p z5DaW(E7qZp3s7bKI&|C(uUZEz1Q)&E!`}q%mbCWEd#6=#ID27i1W{nOmxeAxEBd;$ zexY`wemi!;Z0NS34u&0h@j_Bpz;OsV+M zVz9=Q2K+!E(tA016-VUJ|LeZfO|{(mSV_rHRMCywL(OrGf5xWsi$zyD%g;E zEdkdT2Lw~D=DFlebo_M`<_B{R3Wg%;?N1rYL8<{qQ^RqeBOHLtF6iX(C?ITRD|Qes zf@x0^jRfYHkUXV(3?WCco2gX88Tq=D^|)XEvUf?^!o%L*a_5#v@bKe zv@mwGx8W?gu_zwNsw1 z9#N-qcNAoN2Dae^MF4ih4O6O-??;YDF~;EsOP(x|uhq{??6K1b6cY`6A0ttf2_w-J z*-~~y-FdPL9@fAb_vT05dajB&-c*>owK-a6LKmXwJyl+fnaU~3L}SXo=&TTp287#H z)~yd2bN2eYKAXzdt)TEAwi@kStxd@yB-^f0gd-%bhm?mJ_yhmGM%zW>cai+oLV`sd zq-cB%)7o$|^NJnO_ZN-m(wE)K4pA=juWUnW*J8%(q4-STb(yZM#peTkz(?cOmI`MA zy|A_vxDH~YSzC<~IO*^8-6rpzb1%Y9ajOP%sS?`KsSH3)X-h)^sm}x?TUH;B9(~Ea zcJC491`kl?I<#<+j^Sf?Cg6tup()YqXUz~ZT+Vi>sL<%uj=M7$^zX*q3UNAX1_(zE z8{w<_TtEMEuG>v>PMvnt2xSfJ0Kt%`-%nky$%{+QW{4*3X+#F-Bhb|d^uN(TK>|q< zK_N*mLA}qk<0iayqdHMK$QITR^Nr~Qr+-*^s_x7Aw*(h}2h%$7sg~QNgUg_M)z%@0 ziC^%?5_*Jjuv{~#!g{TrW=v=5wjQAmW^|^p>w*2U&a@j}oP|DHk1~0>kZuFI?c0SD z#;NY%=)9NqF4w5tkir(QkS-Lt0V{1<7rMkCmx=UO9X9-)+NKY?zzjEjHc8fZA?J-) zsq!N1`bISIlBR4#6YC=>WwX}R3oJeYcgESF$J5`3^h*2ZI6H{O88LH~=--vDZUisL zClP+_%JI3R?_2rf@Lo#*$$T8pS=1h|39Yzvqrsa{qHs4#-GtVT(2-5hBc{jEXO=9G zpn993@hF%S%*k+6kZ}*&5XYCrjaw2bTz21vqaJ0dV|f znqr42VjD)cbWcjeS6{j(Z*A&C&#E;t=P_Op0`NTT*@mfflg@9`HZ*oh;BY^mZCU(A zpBn6xUD)`0AcyhcfD?xv797+hMafY#oqh?_c01ZXPkpv)8)`Zw(C^zJKKk`0za8J> zr05+`@eb0O9atGGSG{JE02_{=$VP1z2m-C&fJq=JL>ai;de z@#c6G;v#Wn-sp}31yI$blkX>$cpA`HPzKCZ2082o^oblx<7UEQ-5jFyb z)_`F0r`G9z+(S2gfN4SMGeU!<*}E{DaAi6LaAQ2c+3FhhAa&uu->>%)6@n19-6E%c zqQbjTb}>BUm>^qm;$OEMsv#-`O{jh8`i!52+~DA%+1!EI%qwi|#zX z`r=+{(GMUtE{PtadHoDPF!9m3z=05#Wn~yGgx5FsHrJ#lQKdZqTqF8u3^|`xA9rLe*{d8euvU*QKNKaE^XT%+0x0Gt~S;dwXDa|`Pk%otGDg- z%{`$lYLc$1*8TPsJx}@&E*eM?2f>{C2XY;#(u+T9+)oeAX(sA<>U|LG^oqveqt7#l zE6R>3L))*r(w-^GV&F_}sHyNqWiF`}JaP^t*%Z$+<5m8Ov+Dnel45@*aN%yFrcz&3 zlRZKB@N*|q>H8|2%MG=La7DkeU%vy3KJ8#_3Hc-^31V#g4SuwoQzF}-9m*B*gKvHq z@??ALC381@hSJBwSo%a$mY&S1N5CIVK4iyL!wZ=%aTsi0K$$U&LXTq7o2y_qK*;+c zrZ5D-(=OO{{*=Nsw||rR?_F0a!p=;|p~zty(p#VIw0co=zcX^gPD7McI{egJSJt`* z_k!JI-Z4n!?2e?yx5N1wv|v!^$9TUq=GrHA3pqz{>8O3sq1m9yx@*W0bl$+dcJ;QM zO}TTaMtG?TCsW#aT-!*rZ&-rbnSY3`(4hXow|6qr3kw6rl$#M0c>=uu32vQ)cBd&BSRzFqsb>Rx>6o1m3XMRm#x0w zFI*Kbvm*A^>pqJX0a?M`*}Q0F)7c7(h6N2wt!+J~bz`cW82>OU>#?BrvXOiop0?FK zesoWL0F8@rKS1?Qp~G_cL9molS%#nSKVt{1DJQL^OV$I0p8jbj1bV?(Qg&Wb@E4kQ z+C)Iv4_R1zQy*wub19ayzo`75Fa_m*4<#4l`%8;S-CpG`wI5gun};kZ(5BaCKzD&d zmQgA|!~WFf)?kz({>p-!EH(Bo6EB&rB?tx(F2`2=kZB@XGqxMUC4k?cbuHF6ZNv8a zg4njvmbEf;VQ$I?34?19WtgCm10a2!#c%zNv0PFOs_c0;-`ji4946!`mr!c{F?1Nm z_1D$cHauU@bu7~|5Ws-V1)+x+Z0jN25c20N7Tv9IqGiE7kH$XjrWw=okWz=@z%5md zRQ~!a;nlhdWpTjWB9Z!weoh~ss zP3h>ea<@yub(lb>7+m}Wx^JaCMV&(%5;(_18g&k0S5`h^B0WZ9hO~(qCFaez=)KQg zUbr>Tj47H^E-A`y{3L359%zcW&-Ee}S|d;;Fg03HT7^TR6@^y}6R*x-Wo`<9Wg{u_ z0_KL$?u4w7lU$-BvHU58E&;m&oiC{Iz2i1+VZl8hi=#_(C%-!H9c_$^_~V_u&$z%mey#2~*|^x&4a` zt^m~VD%~y3LGqA-j{l1#i*H$^d7PWYO_m>hJ6A587Zguu(PYfc(Vp(E1uUKFOXiw~95#*Axp;)ofP67|DvYXFUNTob z#7t%Tl?|y}d~Iu$TV3;Ns|fbgn&R%GGG3*pSD^770R-EjRU5hSQ=f^AXO&bgYhZ7L z?e~;}xR%UcLf2ma97BCfw}8ffehO{Q;2;SBca3^q#c8J~LlDqgPGx~Ya|zv*pg z0!TJs>RE?Rmv80M$ya$DkM*$=QS^0CYY-q9wVFQrrN@8~%?A0Z`ArlRJn>=z zl%L@>YN0u&17&W-%Ur29w5NvIh$4$}J8?6Gg$|1?S8#8Shtt;j=30srM)>>q;{HHN z&NXHCJUjWdr{Sn81~ak>MzEShW)j-D?bN?Dw;hgE;Lf-1aGS3%#G_%40>Zm{wx3?~ix4 znu`{AE3QzqxIcJq?cuG=IUgw=MGc0TT$1hZYqx)3!Fi|~qXISxRh64b>9@dVKLUca zz93}iV8^pZ+L|GHB8PENk71v`)T}f6j4${fk34`kQfAU)R<79_uK6L zzPTch!t5hcQI?T)eRZ{a=#v4FjC&O^saiFYD&5AIZwCY`J1O=>Ov`s?S8y(7Jo-B& z+=kT&{yYd7;I59R9!J4uL+Keyt$URs?_gVfhLY~Uly(`lFr&|#uz-nO8hj6vuEZ>E4%sthr2YA!X$O3jdk#TBC3qHP074Tni;mvY zb}{(O)+m$CuE{X3p1NjilLpflqb#mEHMkGRybma?7)~UknmXinDEffYv_?IeeIH%l zMknq=l$e!EwtnazV6=0j@romZgCZ_PpKl{pCroFzhZOYyRJXSO;>+Vb`uPFY9--RM zsRvk=p3wuACY4g`5hJ?7N+v3!?hlnM6W^oB%%y2`_92|ztnA!{8fp8wJ~k^6kGKNG zlywCOlL9Gk(n)Nv?z2sy+((h_vDQ;R7BLkV|E=9yEg;-GG0IGVqg3@VsD_(UqK-H={dAJ?EKdP2s{7z z&w-UBp6JteuCTIw3%mN?4ZUX{IcNeZ@g*p{ad$qhQHWR*51Ar*~Pv*Wue~P7QWERx~;>RLUdE1=*7e z-4~Gk3vCT7$qip<#~AxBXmm%!rlI?UMG}bMp_9%<0E5C<$`h%vt!sjaW zX1|`(ID9m@meEzfXgrtE-uL*5-x7GwjEXuB&9G&Z@&*+tHMr2E*V>#0yX9QK9?-4p z<2!H1c{B@w$q>rBoHAZZXb;1lrF@i*PI>RPrDIbT_6vTQ7$_C0{R}`&ffW?#h&y{l zR*U%Tbh0L{r}P2X&|aWv4k6K~KBQI!d8rN*JeO z(bl)x(hi64o2?j4SJT6{n8=$~^Zmjxk8=9A=K_iXZ#K@1uxrTs9Uv@MDy3(NHHKeK zaLLJ36;8B@${EIU(1NJ}p;cd(Z*Yt{DZnLc| z&1L6gP&6RIG62EUlHb;DTplwks)8BffXK;zeNUHLboTeF5POhzbb}YwYQ_jvdJ|dZ2896@1Rlw?BKT zMS%TqrCCMVfKAd+md(|xY_4MBox<<0X1TM8wqUS;96BaE3Vp$Lpy>v&ZJ6yn@zSn| z!2pEX$mF(aQ^NpIBVsssf>uh4@PU0yOLdIh@?? zjQjZyueS<>GWD_uxD00?F6W9A4_O7Brs;+my}$!SKn5lf{_ZmHu%$oh#BvBx)GII8!?q|AqiwJ)lDH6YMN~`G1)7UmnJ4}yt&5Mce%KbF zZ8GU0_~^T`4AI@hbjl`dmD+`{)d*3iw=Hx}(t=#*@PbU*37IGbzhrm$I}69KNnx}t zJ^F0nZ2fZj^V4INXBJVIR>4Uysr4zR%nu88{SRi6q6A3o0UkJQ?71$Z%v{L@a(2kC zzP z1C`js@`R9NtK%`6_?HU?WUA`S(=g6TcRV!T+9p;S_0l^ zgAmZCt+HHsww9sny!_voWr0jL$g*k~TI}Iky*kGPFUN+R*m%75-fZ)hIN4e^&R@0q(QcvpGmUWxq_ zZrF5*O?Ah*(O&@ENlkvfpvvnVW&o=%Cn-hKVyO|z>er%d0rWqx+UyQnayer;Iv~Q< z0fHH!n?{@}V)HTpd4W(Y5 znCWvuY2u$4dvTv5Cx$2^ly)Oa|Mk0|JK-M53@3lnGfyz(J#zXhPeJDK`2`tlfAr6Y zf|+^#^0R~YFKn=E|6CBa4fP-unxK=xn?KK!ndY1h zVKfnC4gCSZ%;t4Qsp|PH`vIhqwNDMB{d!%nVPz3+d*8ie!+|<$Hr8RmvI0WcQOW8s zayDREZKLW2U74ce8t|%y*X2gwy`IH#?dTAF6>|ui3x@F6x|DGZlL$h4T3uRgK>PCm z?2Pt3^5^f~yoeJb^x(ix(Z53)(N#cbA{$ZEjXds@zmTpGyv}0^fjVF4nj@YW{d(d{ ze_4U>4u2}%J(*d{>aL*;RI>(2|Qwy&KT;-JXcn`caq%F~X_=Q-t| zIsZc^3hO-dcLl(qwdsBB?EC#_F8YqtUJzRjC&}OiEc7S2*?C2H?c&vA%H(?UUcHMb!#2!Y7h?X!5*@yv4t5wk;uJmg)|E!C-bdFkJ5W&x)UGtQ-z_RU>UZy? zGrr1LGWX}8!hh0UAFxa5Kk0&xuJjMG#MjnBnR5vz$?Uk*CK{e06 zI7({gv~eX?ze8Xn=AX>C63?Ty^+m_e7v*8Yem(NT$09{4!SEoRICj${UtL$ja#X?g zl^ddZ-f_9V=%P}EvKHgb+x$gzHL`c`fBoepJ7eQ0mwZM$6r$NR96TwbsLnIU#sL}- zwHC<@9zrbAMj^~_W;81l5coHa16g#E$+h2X9CEmDva{)+umjC64v}wpObN>YMu+LW zYyaV#*l6aD(MxZ;e_$&-Kfn~HF;>Fz$qUT?P|OgAGN${?ye-Ypb5yvvuEBqfox$=n z(~SOjqBF&HrA6em!^X>yEW?43R_WR>eL9lXk{s#r$!{w85bkqn3xn3QI7v9o8ps}UPrlK>j6PCDYWgrWuXsP zvTwGCiL5oNgUUJlKgNRxOT;0ph?B@0@1iN^N0))F{^SilyH@4Q>(~CGv#TOr6%*z^ zln-t{!u!W?4RB1oc-;Qv6+_9TcLB%FdyI%~6i(5BK-Ttrd4{t2QFhnzu$&vm9LVC4 zoZ}{~Wo6xO@(p&`j;RA51qOd)?2VZWl`2r=_#7|3>(TGHLd$aCy^qLGH|b-buC!MS zAedeG>-9;=VRF}%+6U|GsbdgE(%77a20^#1eTR+*VVpYL;cN5lecfCVUso-O2E+>u z$#+tNF`z^5kY}*2f${75rNmI*<^8L9s&%tB6@O%orHn&Cn*w(!4RsiN0l_q<2F1eN zCib~I1Q1M?KzTr1$WyG9~_pM3S9^iZ8qKl2e6O}?W?)$>)K7^d_)goSS7cYAC{ zF^#g}B8C3hSUiza`Y~U|kLD`1tA60{mnGRaVvJR)(8n|}4Dx{cDrsszru|_$Tvz88 z@zvaUhe{fCjlL26)zY8K*W}M->9l2z@oL*U#zyp4FZ9F4>W-WikI5w*L|#I*!?B7T zr+g*h)w4DnGL=(Qd^GKz(C2VWy#-JB)LV3H=79M>{TYY*-Vl^bDLz0!0KPF4TA zi>7;hei71IX-LUR=-D|ru19B8s|u?+OvtBZ7rn(P+lolOzdGBf*0Kkau7#S*oQvjX z<|Ut9ev;(&bPIAshr|op`uKYMPW|qtaldIk{{^bb9n*ChelL$loyVwx5xH-x;6f7y zH#!Ocaf|*vau{)!q|SSo(>uDVlB1xSJBAw#dbRlDhdZqj*_;fay?!`;3#E^!GzZ3q z0!ll07G}oBfeF|^e{RP2W`Ob22s>L2RDvVl{b9%qN*M@%QI|vNjnoA+8~Fabn<$|4nHRzxM9xe zj~uodWkzV4+^w4<-JFw(940}wp7v}IJ3O(2IcF1cypdD+NU?va&z}-&r}mKqzx9rb z{O$k#G_>%>-ZRh-K1*o93$1=wk3LDsF|pk`cUcz^Tfnsx;&Xxv6>`SD=O-I$5Bj8? z=o`I9DHz23&0>=LlVdt|@7%HPwJlGJPk(|OHi<%vs?{jN*@N6vYUHB%kWv1Y2{ zIbS_{J`9Y^v#eR~c#bve9nZ1$5<1VZX3yg}+22MV7TcOx<9L=e6Ccm9X5!;H7lmyv z>vQQm$C}lT=M?J9=j`bP&czG;Uh-aJGYU4AKzg<{PXXH7&cQ*e`W31bA zlXjKUwb6JxkY{;aVc#(|xnMo|f+`Q)H{eMceluzHCVcYJQcFETmei!y3@)%Xm+BW= z3|ic3mA6Ml{ANOYxLXUyFD{{%YuBdP<#iFP-V1>5=s%Sk-42gk|M9n|CQB>hH=98> zl1IcjcdC1_Hhvet?8Iu zwmvR)c-k}jxQ@H1bp>7FVb}BXa`ZW*TKA0l`7I+gS>}%_!(WM;nL2fRIe;}sPn22RD;W#L;EY0s~nhj!2}U}sCnI+q5aBi+-aHbJeH6CrSV(-tf))L zsTYb-Wb%MsojWEZ2gD_mmCDkVI#qS$y|l{Dq=eY`&dFPbN9+7^7lJOuf0y;=!2Fj# zla9gEGhWw{dbiG7Y|EOix&t}LVZ6@p>-=~f{+D;l{CLMq)Cb?F|H*g3X)T(6D^F^Qc93`po2 z1R($IZ>|nc66t)r zvb6>1Y`^^jDaymKh?^NTK$w4kD7s?)LY+g7_{(2er&vFe1|%hS?%^Mk7~3r_xpQoC zpG0c%L1!rQb$VwMw~`73hR5iN&@nGZXHUhb&8mfJ8P_u@u2X0K*o2yU9Uuxb$ zrM^n;oTwmW0o*x}_SMxDqc2{L1xY<#SIn`0=hzrPc4lAya+LW~-XfF`qsvJH(*XD4 zrLHLbmZmFA*H`1OqmGUSbr>~SgTH(Isw+T=t?`%Z3P*RfD0+3UPN}#keVMDvuQpP? z@VrIII8Rp~zoKQcE_6&tNKQ&ljOj%|^>u~gO-ukZOn>`lWfN8i2L>iq=-eMH)h!_* zJ}Eh&S1-1zckQ0g(LXuCKe2Py&i#8u#wPVjNRCbD)VXX@LZ8Ig&i*}Odif{y=|v~k z>b&!qa%tI3T^Y)}o!6IA_UF}+!(^Q=`3}%|)6-KryF%s)fm!wzFg6*vor<`a5CDYy zn=%6&i@Ta|RER6^rNB12;>CqFi43V&Vt%mQH=|8lPrs+D}&w_Rtb z`qi8@wXXnW>6SoCNzT$o%53IXB*Jp$XTu~*CA{BO>!|c~9p=tvsluFSiA9C4YcSC= zO9iGh%kt&EE-%L^OL3W$wY2(xW0B%)+X6VgfALo?;DjpU-z6p~Ij+lqvK{**bxa`j zBb~u5GF#dJ6yupyAL)wwD0?F$DPPPp_J80brc%daR>pQ#>Y#B&oE&^vQFF!sNH-|c z47zLQWPfFDDMHP|^pnXl^AG=EdhFy>GHae{8oH;F3)0CV2~2{VKV0B9rdcl9kFrgIQ&4E{Uyzf@J}@Z$MG`Ck0M&EI|Vy zpRfm7;Opp1lhbkFWd7TcBGYxbsmcPKOSaUvpBz0k!Dcvns`A;<+mw=VI&VqS4^(MS zyO*GvtDha+%*ki+I#TH`js?vr46w%+M?=;WN7`ut<49u{>D&Z5Ga{XeW(8Leu_wb~ zovTC`$T*YZnY=D0IHVMyb&DaPWe?8e^)=-x_+B}a*WAHgOLRp{prSW=nUJD!GIu<3 zsm@1cpdLqZ&*79`)(l+pQFIO`Cz(J%BoEEa;pFDav|%=Ciav|~^lO}0KPf7vy88rZ zRQPvz0+lP{f6dy87(Fkq|r0JdA9%)sDlRHg9|%;hq}%J%~I)oyrVj^~#H&^4h1oZ@&+fa%#~C!en$pV^NXMh ltmDZ|`!q|gS)lpFv6 delta 215068 zcmcG%2Xqxx_y2uo;KH3i(4c^V(m_-@=nVuCkRFH-nu>^oBtRe}F$qnDM65(m;;17{ zL@5?P6crna-9%Ai?-je)d;Nd**=H{3yMF6k>-YX&)_VNz@19fl*=L_JbMstwd-~ol z2dwIT{Ka>_{!;cU-?sYo!WFM}@45AreP_INLRIe#o91Rm2TgK6-qZ7>!y@u8rTVls z6ej(|T1HU)13Q^2+0;ou680xOq-O~A6* z`K1LjBasftk;swo6j1X0-B`;x080M1z@`-l&mc%av*(x1T}*|a(11qp#X0lOErgfk z7v%wwmLwu+2oPnD!c2RfYPH~pgf1wkptQ08l-A|W&MBN5iJTC5pq1KTJ1EVcom19rW?{~(8{yLIH;z&Jbn!26 zTU0cncxh(5wc-h&Snml?igkG|SY@rY1eE8cwi~bWP-^+Ctu}ujNJW)*gJiAT0Ah*C zOF(*5Sqx&4%G30DMde@wNwAs17ms2)763JOqP%H^w9iaAn8WcMYC*2X?cLNnCq-(A3!6pAoptQP6hU(7&;X^8C z*%E(8A%>nbNSmFPKV$wZD*6t+G#fLT*j7tH!WZWh78aD1M;;mIXEta(>cUghyhnr5 zp2^QK@L@1Pyul zptyhkiK-t5XKARMBI8hj@ITG3lKDl8bBpuxr%%!zmKGNl%1|yiJrcnOD<^==)yiIA zdoTrT3x0oEB+>?a$>LoWuLTL;m5VG+0V_l?5P{BDwzXIvWHqS#Y_j4$P=fbep!8&B zS^k3jqVlq>a4~(jNTkstnHdYu)}feFO#6!>5j-F_2Ok(aU7w!X9qPqEko zq#L=F$EZ;0S^SxxBmLVC%1nRJ;$Dk8EUvRi=gW!P<&@q#S3T!ei>DU) zQ<7Sb?}pi$P~3`n)|1P+&B3OrPeRU|viveeCej?e*zXrYm-zjKOsZD0gwH|{KYo=5?Z7a^ zgaLJbr8eOC3)F!qE~hXjFaJ@vRI~#WM|zTUGEeRTW!xX3JaM#qup>C*BDLtr@E-8P z!OqlO`7z=6MDQVyeMRMUAlr$`*ioADnqrl3!`LJMjD ziVuaG$Ff4RG)7J#y$sO{q!%2?P`3p4Ua6M3cNO-Rmd+{bwm4^AVI;q-q_kjOP9&#v zR!L52S$@h@>O|eEbu2r9(x8p2)Jc2zTF{kn>CuIt_{x7*h2Ux}CtQ|}x<<=OzQ+GB zx$}(MX^-^h*=w~QyJ)YtN=cx_=7Z9|(8pWR4{1hjLAQdUnZ=PQ_W6u5ao|Yg_|;n7 zLN?W+xoNdOr%CII!NgVRlvpkkAPyfhu7)2nWX?8 z_I?|BNk1=VE?d2V(zS5u#^vDQ;7ix5HI~5XPDN!2f)u=YgSIb!5v}0gYFlAw-a-l7 z=*!THImcQYy;1v}PwUI`OBc_w`ZI3Sif;#HCN#Q98!!bFyLsr%Zh53lm$_4q6C0o>^%RmV-`JfC~Q_7Kn8A^VsxZBO@AFV)h zT>KEhv`7>r_;agP{J{ji`bWlt@36y;2+@=kg4N664Tg=PPW2s{`C>DE0 zA~P_bH0;l9T2JNnNMv3M6mt+{m6~YrOA;8@+o2vkhKi(MiRRsM3k$a2t`(P*7SAgw zFN1dnN6qM3SO7qLh3-U`J z7^Ann`&N`~t|=|@RfA<60OU7F!*P=+Pk z8--_%{piK~Z-G)ye%bu;0@jEhA5{H|clrIB4zJir_a*z+4Ry4ydRPPOM)cC(%dL4{ z*sIyX`1k}|@^NY?U(B&`H(Z)m?@>E}$tV5U43~0d!6oqC_?X(O8pKW&l@}vO!t+3B zZ+IMy$JFq@JgyZDdO{6)0F?CQIi=lZGVJr7)O^o^Qc;hmv?1**?nN)@cY$KZ8HE^+ ze>3LKoS9$hFFm4Z#S;`MZD;qfun3R(2rf-|xkj@*0?G_Hr&iOI=4uh)%#e-vJkA|} zdGHPC#5P52NxS9DD0s;>{Z3HIxe1h}UI&t{qB4mA5=F*7&3JBco+;?Es2YnEp^POV z5_?Y5?Ez&bEd-AQCxg(5Oyr_<9HJ;0AcItp9n%9HbUR zBI)(Dg1#@R(@4*XDc}dVG>|)lqWSY8k^Ppx21>=xfMVqFFKdIu)uiIXUQzGA0F?YQ zuJ8%(Mvhq-&1{4+*^F1 z5A%-v^(wz;;J0tydDO?~k49fjI%&t8g|wP~a~3lG{L7#`sqa~aP@Z4(zVdQV>RU=a zDL2orZr*0_oDVg)4F{$6lR(Mc43yNbe4yq!-9G=Zucqr{pEvZsYu=*w?vFL~Eua{C z9Vnx|3N&r7crGY|Q1Y?gzeVSY-zi*@z4n=A54XvC;o`jq?DO|6mMtum9!4g8q0eh= zVJ#}S5*Fj(2567gVN4-K#3Sz!FJ%epmJ=R?`r1o7HrvvBb#j^b}vIyatq(HU*_2 z7d3Q@AF<;vmvR0*=9q$ZgAy?c=5gN-jl594sW6s$Su@mpp|_d1CJp8#fLd2D9i^BhZlfi zfzv>#cusM-T)Rf5!z)B_?)oF(l@E*sU+%VPmEwzFBK^da(UiDSW2OaBZgf#b$kw{m#Smqg?OZnlOkw@S% zOH%=t9F}s2*&qKDG&z{Eg6yGygh7JMsqD2L1vlBX}08i`2K( zQNyfhukD>}0u=3sOT9%M)CFb~7Z=XRDa+3*o@aKCdFXjwQQ4!TcCejA3}W6gTuA}a zkUZu>x0yv{7bW;&6O;aN)zaS+6J+G(&Ed$)zaKhlc^`q|I`?%^U%bfTbLgc5JCBzR zGaDP6pcSn|Ar-zx07zU>^);JrpH25kUwyt4>xtF2 zfRcYLD6Lv$QLNvsG-u&r#@N4$82#1|`TI4odU?JWj3inZ@@& z$^Qx{71w}b^@l)dz#X7;bc^NdL5~Zd$}171K+`f7xDx9R?LoOTnbvvl#&SS`m5WwskmvZKAk>JIEU4Zg#2cZxRbYxI(Cz1256QwO>j zlyPw_Z!=XIOnCc|jAEB6GKfbkw3vC87L;ds3>3TVr-y>sR$n(w^F0iTKHciO+Vp>( zthl2}SM8;@F(-C*%bixr@ls@US$7ROr5us9@FE9eDE1A16}8;gg7;=+!_ z5A)Q*^Uo=z|NQ%2$D;D)d>zBD5T|kinZc2zot{0imyv_4)i)laRSweU3S7H z2(c0u#fr+9 zReW2fzWchxy%uk_xQhlz^K**}i%XfjtE~Q9Q0(#IdD`Gs=PTw}{izlkFVT*rTHfB^ z1U}qA5Ce``sulc7MZ!O__yQ<%W-llP-3prEwP04Uc-F8=%@=NVs{DS(9api|rW%Mn zrFp#>b{XD#mZ{ywT&T5VfYPn-l}UTjNiC}`(oyr^Vy_=h)%08vn=5g1|B6285-q=M z9$&ujZ{x)#Uq$6fm#Q7!C8IRpF;E63^)d}ww}Rq#CxGH-?LY~J;VO1J=_SSlpv08b zRZgS}xDad)esH-H=?(4%PXKdPsskJbc9ocuhR~S@t4Sap@*&Ho^z1>4*MP>uqPls! zlg*?wbe`QcUu@I8d!-Is`0{hcDs6aKd2wk0FV4S(%Xs`$?U;8q?}N=G<~)laJ-FB6 zf&z1(;+4pZ`2~e}k?=L={~xb6E2QH8;r-_S`t@e`9{NsShhhaN7Onurhc?-cR#y{q z#AVCNcoRA^cdqO}O7j;*mXbj9udjC^-NDyp>W;=bW`o{OcBV()(|z(1L#4?wI#*!44gxVYllTbp)lrg*zQ{oF4<82ycj9 z3jB!l5`}&RrJ@6%_)7TZ@fs@X1fK;;M+Sk%fvv3m`-+>L$nhwi13Q8{L2<&1z^>pl zP)wHw;%AjTcWcX=!X@APTh+Vo1Et^q6t6D=<@q2`Dn163^1t7r9j*c8dBwE|eGtk) z$v6n?3mywfg+Fc9F?pw2TG4W-lQG57nGrUZlhMb9Nq(7 z@u(9?l|4r;3hCi(B#;U^!PCKiZc_bQpcH%$CWf58N~<2TDi}Z^1}RrbMwQG9182yY0U>PvgU_xozq{`$QHhRK6$$C-|d$$ zi*w27QNbk?EFt>Qmz3WP%Cs8svO3>+JQpW?9xe^~?IBw*qByH3B6bhMV8NUen3;EhyFQ2hBQTzqfqd@>@Y^Mm{Ivg3_|`|7lYlZl+g| zp6>Y#yZ5TFa^4(0s7IGBa2cykzSE;c#qA#{+W?9wm+?qU*79S`br>k7ihwd=(NEN*g`ku&4Qv7~ zm|0LrBO>8#q*?{#T-J^DXfi~irf1+AzIuRV{$0MfqczvyX)d4vk2pd+z>G{CVquD)j242r%#C_~*Dlp$_v@t1m9;0K^Q|JGV4{B$8l zKEy4W71@Jcwyi(xR~P&QY$CpxV?L&gM6QVWb9(d11ZQ_iH^Xy{g-hp`&CbF1!>=x1 zBcIHb8p#h{mgJfhvr!}0QgC5Ci97)wEr*rJZ!*UhiUp)Obv1$4VCnJj&zNyEN`mif0v@= zv}x{|JA%W&!^I_B1SvSY8UL-Bwmkg0bu$IYkT*U`GiJiY@^!YrhDR&^iRaP*b0XB;6IDTuLkk#ZvdRbj?CI3vND{xPk&D!FATu*Bs-TUC(h~JM>FHX}~N{3LFVa zG>L-Z>>svr&62**ma~Q)OGEaz(Q>C-eP7$~H`}TU-F<``F&op?_Cd_z4?K{TzH4z$ zd-cKXpqO@52iHWIH$WMxN>KC#pmgMHP$ol-&0pO~8@v%NzMS7#EikULTVX0%X&)@M zI1`jfFu~#=i)*^5Wx82D%#LkK%Nts3eu8T@BR}$7%J~s49SU!1KC~8k{lp4wSzlXF zbyw}t3pT++7Pnb^wVMvfouKTN)`L>;BAb7f#oax$-0MM^?cp^-C0ycE`(8SflPhe7 z4i=kQbSz$Ao$LdPzw~y^xjM)4M=ai9aihhleYNK=gVMmEC#h4XS=@tOhN`04W;n-U z^~pM2z5-=T$D^0|UlG@_8)A81i>Icl6Lzw^1t>nT6=xPFyb_d1d@(3KQ3OiGeTSOk zUhl+9j}`s3g7ZPCcs98t8@`H6n7c#Bbu2I{0OE|p*viG)u_rfYM_zT4$E=3y|{C z9Q!$q`R$7A8D6pFoKV6=T$0-U6l0@W&8Ea9P%l1I3JkKrv8wNfLg$GBSGsVKaUD(RL}w3vcRx|J2nJC8}@b)4@8 zrGQ&O8NqP5xsJ|@+s@KoO+_w;OS(%Zy5@Lu3Ml-ZN!q~8V2Z2;%@Cw#8z$=vy%H`# zcg$&8K?Pha^Dl{soJ7dpv>F~P%2t|mhBN-R)aUrbj{W$3ocut-EdhA`oSg0u7%4DTnD&xU4$(7lA$w#w7elG%gpyvtWSV4bItljL$Ax#A=(d$#VQGOG6W}(PI~xF zo(|=C`D&TpK}lynnu&Y^m-Nri)DD&v=T^*R2N9V$OADR~io=HAsLm;yQN-iOq(S<{ z!d^1U82>R_(--BJ&&w&B8yQic1wRRjFNMGJ2tS&em0#X%erZAEtvTAz7ePt)_H4Tv zgeS%QwuQqBwSw>`Zx6#IL)+=vknm%%(s`<%kyD!Ajct}ez9Q>dqzz(gmY*|kQKX&a z4};?5Gx}-}ySoB~^mrR66@*`(&7mRU1KHMu50|L-HaSO4D_@I>W!}N$V*2xjsgu?# z(|kXbs_8!i#p~Yz#q_<()t5Gc(vXU|yjWdCLE)>v_B;?1zeEOU*lY9E2fkV0n#22Z zplng@0L5okfStfXuq`+gl-q(`wgK7aYPx}-I9=QGv|;Vw_#WS?A&8TGxmca=it}~a zJVF7|vw|h6pP4hiP`b<~U-S6&S!4ow@$%Aqd7FbJmZ}Bj@f#vZcO&`5f=@EUGONOq z*`07{h#s7n|J62O)&*MOAW%%!YMEyI9WGwH0xq}=6w6GzP)!+wOB^_0^O>)#PvS&N zxn}y=gT;T3ir-vBea!#=M}gDHC$Cq85-RUhGPlC6m`gs(m71IpOW#06x+cZ5raMw4E!2PgsW2#eoV;|y{)wwMaU66Ri*uWK&3P7690 zl!}T$nLY)pwW55u_(1rH*+jTF8^35Rl8&p)oujmvA{IKQozngAU**@G5}sXJSI zeWNyr&zW;_8GHFbgE-AI=moppq^1nNFbO|K+q=>4Ke%sgv_ z!o)9|HNg*ow)ag?e02V~vh|5Xo`%ba|9z`gaF69{Zqxq%4ax|P+@=+77EW1>1rT-Jy5`DB)lXD89VrPObQ(+qK}UcB$nq1;y1W z&`Xb(*?hBXzCNIIWX#>#fR>hjz7vZ|IB9;57PtqLf}Xbp4cV;~@Q>X|MB;4|LgZd>ndjGXAl1ue-Las6#lL^{E{X7 zDQI{Y3lCitiBr}fojxBj6yh{Jp3|{A3KZ^wGIj^{sSmtvvG#eL72y{ekE0ih?zFhh z;&M=YrL<1Vv5~%_awZD1=HwUV<(KB?o7K25fAP03suL9w)@1(AfXg&pU<=B%I3ARM zGuUEJi)}16vG~`sn(hE7f#@BB%>U;Q#QWKznx6I{4oHD7U#mU+`Bm-FvFN3t$Ac0v zpL|Wz9S)c2HT89M;s-%V_c!?^V0~`Ox#A5i=P|gH-}y~Y=KuWp1$olL3s6YLZ6pwr zZ+=S~uo^C=n*^5#R|-mluL4DX0VoA_ct`W++UJDw@&bNTI`m!57hc-4J(~ke%=Uwo=HlQa3%UI9)KpXs@i^IzQUQCXmVG<`+eWVd_(MMb^Nr0G+LU1%F z0j3`)raK;#v1$W~$(w^xK=>`6eCN=OR)#m5Pky39G3Qh5@q?h`3vWEngp1`yfl~g1 zpRk>f1Y5}{6;J=H0(kZypbqAscL;_XbTw1*HQi zR{#E2Iz)e5qxs$*8r9Drt9X?z4OrDc?_Sp*(1cflGUn%lX8M5A;LTsFB|aD2)ok>6 z^>*EG>fys;{kC2{Z^GB_-4~?1+i=iT8)8mF-@7)$8RL(-HY550IZySou1$;fAli;G z(s@XkMtTBiq)1*eUO1deTtEAz)cTO2qK*YfeNhM1S2yfQ*|9h#7=m z!+!(cTbmJm2RqaJEon|CfBD)BZy{4c8W#yty1S{qw=To!=#S#R7x>Hh?^Axwx(xTX zrhd};bZ53dYJG;Y-Cw>w!~OIKzjl4P*NNdDK=y_~$~8`E490C(Wi&{+R9Sz0HpPiY zf97Fdf91L~?*xW0U6T3P*T%isu#;i+{krvW=VpKTh75OqGrx92x^uGc-H_oF_@nsm z27mbt8PRVEu7muQYtp<797(M2_;u^z&c*(yjTy1W(MiEUyR{9(^jQkoa_tJlPx zb-s6FM)VVOGyIk7)4Y)^s;Q=kl=boG<*;;r-`X^%#`kW@h#to_=oCNsrnKnQNHaz9 zen*n>oS^OnC$%B_9qC|@@=JZG&MEOnZOZVTM=d6G{OpZ!r;T5;DI=CkOqQyGUMo#f zZ!HRGYCXShZQT9j7{6+By4RbNrzzggUK97uhpAyw*2bMX{ZU&o+%JyxtG1-ahOpxr z6xJM6PIbK)np3pWEphKTn6#{(PgXD1Mwu&hd8feAc-B~+#g@P%U+Asg!ziR-^`#9? z1AqC=8PO;^!881oH>X9XAx$#UE~K$Wa@$8Dqm7h>lx?Kzk+O{RC6YYTw(I;l9?%&)k;o9OWXkHT?G>-`k!Mt;hR##_Gm2XQsb=dxp10IGGs&JnU_lbO~2kANN{vj2GJ|o0wqL zTjI_Ye$9>y_dpjv>GpJQ*71?Z=_XZ#6nWe{xM7TjJ3)*f@XX+BENS zB(ZN(@j34;Sf7wNP5hdj8O}7{yCcI}eWJDl-&_}WUpmoGx-;E9zpI}KJloZ;x-;Er z?$_Lz;pKKy-#OCH-V~3mg&pd2?$_P?s$J>MiGIzl3~xsFa8_imkH_wT$$T_}>sNm7 z#ZZVFM*O;+@mTL3HiU#uc{z$q9y2j$Fd|Mg1J;#iuAlNxY78RQ zu5WsBpr@aCPkL-9Hog)z^cZ)To)OQ&EV4aRMs8 z8Ah?v()Dro_1=Ecz3I_rSTfzj-kXs~QQ5?=!^jPA6d5UH)BeToflW?4Ys`c?$Y#JM zCZ65%FP0RKM8+qcO@)n1ux&7Ds;STYHSQ-pknUvoqaMicF2TGq`VljQAAw0TnR#EP zI;norgG};%iI}t@9$O6)-wd3e%xdAihJuD@*-oCM zU%MyWYesDn3LKdy?xZw7^PzO_DwK9aHpIP`VPZ?24fTmI(s)K|L)@J{z|VX*-Mw~z zUj=+Jz^{Ec-Rm(>M_$V;MsC?Z$gi7T2 zi&-V(5fd9luNx+PO1?eKeQ}td^i;angk?=SQ$I*K!cC3AByocv9NM6 zbPkvp5e)i_r34KrCDV>nA(E%QSxNr1tf9=xXfC7G+h4goEq0Aaqza7P26cD6hG=AY zHX1q|YdKn7GO+?qMIo_>YVgCWNBc?7rn?75`KGB`+ac^*zrXqYiOXsru+I{KXn^uXXNpk(EnuN{D6t*WYbufuHt#NH( z8v3Td#7zl(>*BEoVUuBD(_>8u1!9ySyRtz~fA2moW);jSM)V5UDbmE~3rGXZs?a!F z>y8B}Ef`do)Yi~gb(v)i40{MBbu+WJr^X0D(#MEdOvb^aUe8atJ?>p$Sp&1IKMONn zBj%KK`$d+R2^zy#>NdsQFDLj(FQt3gL_C>h7>c!VEzEAC2p=Cz^lM4danhm9n+L}hmQMCFUrCRyIi0`N4j3U)=J>|ch7d7-VqH4^Y~7qU2(leU4kl(+ z>)j8N0A_sH`x@38M(iW9&zNpE^)e@9+V4amR$|R$1^pf-^{~$(Q1;FVYqg2xZE(6N zxo$_?+X)kYX%O%QLTU^qvs(Ah?Pr92kp&=shM)OPdi4A}bB1|`V1UFK2B~>|D9uPn zI}+0G32FFD9K_FhZvdOAs`oPZZ!Q0wAKDdrILhnmyuS!KcIf7@&?QeLm5-jlQ$z8JnHtuV^6|l zmV^`3ODa@_n)+;5CXZd2o!%|5MAqmB!u+f?Y0lC9sE;#ZBj?$o?6$;RJ=IH9U_a>~DJPYdnAg&baP}U1l_Hagk$vB#(UE7rCib4jD*~FT6 zVoAstyx2mRNo)@kvAaWsZB6tC6sbX0H)h{C-6Z5B@BU|{B@dp;SxXTn{@YqC4S~N>CwZNGHjxcjzQwUK#E|gpY&~dv<5}G zdDO6ipYr<22hyU~Bb{zefIlNmG19ooNF-05dfy^RBrvnxJK=)RJt7(7Vk7?19FX5+TBDtF{@vDAHj~;O;7iQ+s z)kqilD}PFh9(5T%cr&{5kT?pXdk3k=bbs`6zD+QtKd{`-{3SiwrAp$blvz>b*P{3W z#dLq=4{6cN%UNH{!FU}KYposF&tV6Lx$6q`Y7B+P&w=$LWz!&==}{jx#JI*MND_pN zYeajj$Y?#a*| z1BG22Z-_@f5oWgbt*_OYY4%&u@vxr8ddrX`yw(q@J;EPMietxu^_m4bLdW{mPp3LT zQ0qGB-cu+uNTTL$aGeGf_Gla8(M(uckler-5JNhdRIIdn9o+1Fsr4Z}{goTi zygsY7r{+NG6~p?Hg^qD(+_pMMic;ODXr=SAyvMy3Ycvg8ug~M&XxK@Krc}ZbUL4&A zJ2}`_6t#Fk!@#uRn?#8yKd2KdmD$gu< zxv*1ArLqaW9VWh^yXG%pVjJ9x_}=P&N57yl}T#%LUa@H#Mk{L>HnH8zt7jJt#5~%{}F2cAOz|uemv>O~D`s z(59osAu;-iw;bYh-XhqLFo#zI8w$hd52eO#J+$acVFx4Oi!k*XiBfHEJ2Yh_Y&eCu zLCR6MCrn3GqFZF!A@*_7f|?_7$Ym(Il7Z9D9(KpDgh8Dq!SZHKh8t`TYMU{}pKK43 znmg%E-(Xa8CnI{n4!H_srH_7ubeaj^{cqO_+3?`rO14nq&sA@N+^gP0MCM@nrCv4wgD#1Khn(hO?TTZkU0f3zDGlUxJ1RKp^%{q7cp-K z3Ta|QqF1!RZkh4&(qTB#7)fY%j^0&PVa~eo*t<~i_3(MD%e^}J%rPg}`!XkXG&0U% zU9pEyn99TJ!TR@UAmLQb{lU2Vf~q#Sz%};;wV?MjPY02oamKNZyB5UkQ#%?vZ8y2zI)W3=DNx&fk{G+b41EpVLde^ z`{*}eGK%I9;WQ0Kb#yYEp}}$>w)i2twXoMu-fbvsV!HK_eN52a6nB~h%R4cHraT3A?=2TJ#Cg%f`-Y{-_=VaGk$6Cq5cfb)lHI(aIoGIM)X41Mhe| z{jSF}B(b2SIB~}bmZNpE9uI1dC(9L&tIgOSJsx*!9}hB5pdX!LyrK{L;ki$Q&B0Z? zaWIMW4TEeVXACCeia7``{hrjh;s)6TOm5f!o~b{)1(U`x;LM|ePpPly%5}j(#`*aP znDG_4Xbkoq&&A!->S|7q-5mRjfND%5?73x62UXqZ%2u@E3@ico>^m?Sa2@{S8m%r8 z)E&v|t86H*uejj97)jb_7IE(b*eRw6dK&9ct8rL2XnSv?9caYE&AJ(V4A#}(N4WZ3 zQkqMmkc1PO011e)`4CQ|HOpw$|4E-#|4E9}tiy)nvF!=*8F_v*` z#?0$YK>j<%555vfGWThLwB?0VXJ1g;oAMS%`8!{UBrlOfHbl{&t_{6z@f-mmr-bFqVYDEESpIt7`Wfm1(_#1 zOTB?FYNBMnZeQG64C`&OWHT!de<}2Q3CojVeR*a!iP0)~W;U2FAj$CRVW8>D+9q7^ zhPXEqCcR|yv^VbF{&G;8%2L=i@cJ=_R=uLdtGB)ilYl_dJLAsLK@CZwr@hL=_V?{b zi#>rfmB)#_M!VN^pTjxh>3DP|Y=Xb?uQcyIBryu5vd;fz83CCNEqGl|JWTN0xf6k9 zN|AacI~`Aa!_J?e`f+9&OzdY)GO<-KaW&I`U{IWc1sWSP>r!KHYV+CR7c+rjVk;BR zyrr;#utsL%_5iHAdCU7bl2{vOdy1{)Th_JBDm)4%^Nz5~QhES34rXi|8~C=J%68d_ zE)j)!=eq}Kj26js_1*~wP}YvIFlm$y)P=B-Ftf#qJ_8#pB}5y)ixeaecL&6f#3z~h z+>KuIZjd<`hxr2SP_)=^cic%2yi*t_mq~QEnIZn(TVv5qMC>L9=^G8g0DjL9C+0+- zCiSuAvA#ffPrJh#4P<@UJKD5x3gR5<;DQNxI@*SsH84P!xl^ZlR}bdCwU z;nY5q>e^~~mBXEp-Z>v=BqKPty!i8i{XxALbMY$S}%up{g7xhBTp_NT@m;%`on zGRld&cYGeyX3^2FJ`a*krHbReV2py~U%9wKnk=toqJJU9gRD_bTD0$%*g;z0mV6mx zjwQo4Xodw@V|jSmS8Q)pT!m=0uON<4?J);vq#QG%(~$JJd((j+DI3GQiAsE!j{+Wz zN1J@jW?FNdfjC6P2N4Gw(f#Y|AalI4G1>O@b*jtBQRkN=|AodiA>gCl;` zMi|GBPJs0@_pB?B`Ud+3aNZS(YE8uTezZ*Yp!QqlPwZ4Fo`Pb0LR-l-AXo}BJ%sEss-gWfVT=isJsAtCK(B%47)Li*W5Z#? zO-t<;6f023L`n*(xrcJRpsIjY)eCA0*pFPDq_#9+HX3W>;269I@TMc3N+x0s3&IZA zP#F6QX6E;>{;)_;eL6da#>$8-d)NfPB)%SPc3j>L=m6+Z=CbGu%k0C>$(ojzC#-=* z|6;W;=>;|7WkZ^%v02=@QzJ|!D*Ywg)WD=hV;e8!FwLhdfQfh353(1L52mrdn)*&W zT&>1Yls?UcNuTi7ds3algQQ{%`?^(f`NRnINO8>Wg!r(Dt0)*3?|gFh0MZcCoa~P& zwyBPsqX&%hpfYFOpss`#z@!D{3MCpnf&%69Brgrg^g)OFTBsPG?Z)GAZ$C^-VSQo9 zkvbhLy#{*7)5gtoI@zbwp;D4kx2{=`R7S^o^A|rypvJl!*_SsDszm!5+6+@|9lOZp zE%fh|VDn~d`6oa*G1Ay-q*Rh=)U1KYWV97_I!Y~W={Zmd+pUA@M(j$C3aaL_@cfQ; zVvuzX!SCopo9o_qbdb4#1V=Oow^p)Re0p?Hi`F?I@D{R^Eas1SatU~6ns;<7bqoD$ zV=+tuX?WE4wxf{F*AMCnu{}(@h7rc7Cm!RN5UFn;3Sr{krc3V5V}hi`*yB^Q!_XdP zVp!&}>dJ}D_$4T`KyjygU=m~r&THba2Ca2AC)zU9DyRW}UIvqCVa|%tXJGnaKyn-O zW@j-9seh1sGB0}C1hwbUnupPJCl5Op7OMl6(f;jmr&X~0e71@++6J}flk}Ok>J#Sr zHk#5-?6vRFG;b`@nc7CK3~p@~)Gi^6eAg~WS_)>j4>G~%m{_=T4fZZ&XL)OTbxxYj z#s8nMuEy`XbkI()Cu15egqaBxq@;040}~%f80&MZ(Cys7jy7wM9b;r*VtqGAIh^2Z zS-4txPN&e{WGU$flQ^jdn92f&=RxfSw0~!(AZZ!+b*CV68T+4Zotc;Z$}MT``p!Yp zh4johjsXplFJwnH9C4J2PjtZ*RBUj3LL7m}8wb+54<8>SRXZ8=PY^pMbJQOzk~CO) z9$g93{NC{=>I`De9L`DoYN&V!?^kH~<1pi&dh{B|(F={(jD2vN#j`5`DcE-^hY%#u z>*1gVCP$Ja6DI3-(^Z48#UvdFla<~a2))Z-;#O4uEM|q7xNq_u*-jCMUK^pWT+L^j*vTAL6vRQ%|m*#2b3O|@qt zW&~MRbCN)^xn4t5e~UJa6P-2b83)B15J#)_OT;r(9G;qR>B|wbRr~fqv0J}Hxw8>% zZJQCT&wqkwo7<^BGe%38jcB!75pBypM6?s=xU{f8-gHD=)x;%t!(K%xr>Jkxv$Hw{TVu0c?DK4-ogGlI<3 zwCi2e68TxgmvhtEA*fj`PBqvu-%K2(w@G^j2bpU~{5NVjXXth${S@{3n2Cx-FmZeR zBIIVv@E+#bhcH>W3E|wajtFYjQpZC>R7pt4!6!$pLpNb)I3eqpdzZsT1^ZUxY40JX zsn~K@LY#!y&uG0X5M?abn-vp}_CaL{;he@m)gP{HV~fXkg6XgkCYO8h@Syg3I`)E1 zz&@`VI{+>e!rCrH1cP}3kWNsqPyo}bw z?(mD%SeEr%GtB*AGR$T{h?QFwMh$mIR#3H>4NOu{vl;&$9(Y@vis%83#M<*}cuk5=QhHsI<#72k3ub(hM`Av3}Xwlf*(= zfI=4walgl4x(5`NG(OBKuhoy9q+42+hX>;B)JZ|@Eez)eq7C-lg6DOdoDi2FW~sIo z5r32f(bE_@vy;g}%24GB#IzvmPMie^&ybYKr-zwhBN0tB50{WC6f$i&4dbE@!ti`k z{22*xI^ruR#UWhL>9TGp8>U5Z&UFO8gZs^c`5Hw#B43(^@G~JQzhiCyp5zt z1NHfXh)fj8Rqw2XI1P~s?jh+x<=2R|X?><8#6^g6VFa0C_%Idb|2IpHa(%8 zjA+YUYs9P_)QelknAoWyaG>GUZ~Vmtlg-Cnm~`GazBnJe8-j&ULP0OC;dTyCEJfk|q!x$?d& z)QD#G7_sB$+53_3+Tclz%&@ifvEpE%EA~ND@SqY)++V@3JK&UY6gQIVCO^jdNn_wxh=1W4ykp_&19Zb2!GR}Cs zdHEeCPNl{p?eQ;!&o|XtPRu zC&*gPR2Yvai!lcp8d(Jsw_qyZrBA}dhj^REv&aSEvYib}g~|09r^cmh-Ojln$b6R7 zb2Do3N(vy+D=?{*-5B2nG+U;9W-T4S8Dd$GxsT20tIL8aFq(5AJ(DHN+l3^9W4;4- zf4VTpe2(X1FCx5|%dt&J@`jIPiLbB^z$U=VFXz0|F4n8Mh`Il9U%5D_dY+}fZBX+( z&O7fCwF#Zw6Zh_hnY~6(w-H;y#!(LY8{VeXzm#zel8@w$1WA^B_K?(83p)*VnEBLe z`ej-hD?TUD`(V7EoR(u~#*)9K*@M0VlQH5{$l1PAm9YT7 zZF~f&)O=Rj=W>FG`R?_I6;9+5^R?@0qy^?v&m}9F8h+L{Y0=k^&NVr*uW;m}QA&9j zX`1;E^w=x49?q$RhJ2W8MCj3@ad*p=LG3HpcO3hnBg{_FYz#NV{zcDfaOjoLXXJSm zvvvwgn{rg&KNV;CQUac((ro5R=6$?MYGE4Bqm z`5b5Lo)L<`aaURMmOc2a5g`UG+T5W(7i8k{OfjyMKr#b&=6#ONTo7Y)zpE`pqX(iHvVBcp*4@kOw-x}dEQ~AjchJ~y{sxrFHdu%hJS0I&| z;1hW$)Xhe^Bv|@U|3TuD zdit>YQ73Yhkw!fh+G97;ER(MF<4)vy(~t*|H1?hFL}=Bkk<{jqCylRA-XbLZSpIb+ zb>&l^GBKAA==UNOn)13n9a`*Gqy<6N0XkMAbx6#89*OsONXOMW@)^OCLzF_kiFkv<^UdK*AfEitOExr zT&5yiR%1J|h}4sp`L|%NdJ1FJwko#xs6_b^(l!+xY5nH_{+VNbEI#K_S&8 z6g^%#WLabv6)Qj?YixLAk3AEnkR7ppIrg${3d}ym9nWVyzq7zrqdjy#7JCpBRLAmv_Gx|L9|G{+h4m0p95lrQ?D zZQd|3m6z$cNSyPoXO+AeQT9aUmc;$yjiBlu*54C@ntvGisc&jK^$KhqOl(5vV>9_C z%#5{c5o1l?vi6coaPu2U6f#(5rKq>+Eyo|dWnk=Sw6f6ZEx$1g8tJ?lY_Ip*+CL|# zlNWn1jesfN#@#h<2UV_{9(xvT*MFCRMoM+ew_{$HcfywO^;K*-Oe#oRm#jgdEyMMq zZwiy|JG_?fY8RX!dm-)$vsY|aVAB9qrylIhRG=F2J#Y^ z=~|HTDMjqH)Hs^=J51I`>_f($?}z<1p8}RZr5x5eezmX-CZ`A%0ydZL!%l)Fn;(oc z`#}3j{d|u&8D{z#p7&OvkgR6yiaib!dxp2_-p^LSKAfY&@DJ4hdgXc%Oxj>d_V&W$ zWd-j$|KanzKcJ?f@J8pPk5pkk7WRr^G9Y|}jYr=LlSY^&laG)zB{t&aN$35#53{d* zmg;Y*6JN-B9X}2&A$^|?6Av`oVs9%TNM2^6``(gA`zT{rVFg6X|Ij}5PRLid z^KTEGj?v*zz7eEty7(WzcL{lY>ay^ z!1BzqI(#zww?sUr`)gtLvjkGU3)7EHj^xvm77)r&`!Vjpz~>zujdM+x)N4y0TDR4%2BZj`J`~+(^Sk^541u z>Lwu@rapkpHW5*O(|=y}c)OWC(b|;tKi|6F>zphgXAe(T_TzB2&(5| zyLzta4(@m~&9aOb!4JsKu1{H%pjQFjy-?{nah+eZHHf-= zLL>P>nAbYyMkI9Ue3}mHE-8a*zU+2yhy_WV+@+r5xd&e!^E;lJPPpjb4G#U#t%xMD z)YS{>_;6UhE9&f~yB~N#E$AE>c*nUJ?%t#z^EjHrXK`W8hXi?>(NiLf;DQ^+fkMw5)ai|iy|1A~U3(v4*EKv%pj&9o*4jbQ%(Oq%YR zKRel4vO!VjdH}d!E6s{ z+8>E$@*;Y8TWOp8w5%Fwlw7xYKOxB#AoHHUHL{&+-X3dETM5$^i3wi)7i-hr8pN1q z=D%1SOx{6j`mSiNq#o`^C?9_Xe;1H{3meZL^_5yfbHrmNOv%5yh#4KmZ+^yr&tO-ip-H|q^S^`p3L z$jol-7ueZ}+9!8+Bl#hVJ^&jb6D#WWpax@$!6JnzyjNjra~Zi_JzaA(Yx?X~@{P=3 z+EIh{j9}ljw6j;}19(~V0+=Z?y4Oh9-1|qQMCs|hEeopoHa7;7*^H%2i4~TyqsO0) z=;NB-Fsij@!%RxEcx*n%Xyb<=3+h_2D)uF%`CBi=ND_AO9`@g#!*uznBTv>z8i^>K zOZZDI!*|)wJOUH{Boyu8cSR>lc{1-OilpVSJ-rde`g~_v^iL#nN{tPTYh*|)`aX(- z6XtdMnC{+0=TkK?&7(mjFbxFaPIZZA!W#F}08hZ-s51}2>hr*|-F6!Cdif1R0}JP5Sk!Nj|{5&4UmnWifjM*wEnbugKy^@F-jY_unHS$*&$ za{l6j={^TL{vt-^0L`e5v1UM!IU2{vB8L}VZ@YDTT7%Z<7}SiW9bXRAWu2OdNT;Q1 z0W3hA{k{ z$mR}@mIWweeK6x4-2gK`)QG-@)Y}}Wy}?={cHytn<-^1-iOZVpD25)Q@cuy|C7Q3| zqbHrhW3yws1W8sy)@Fw8X_&0kd;~wA$TGy$p9Oe>k>uo}ud^#2AGdD4*o= zSGWEd5+sdh^D%L#29(C;v&#S`yCsg;H1!F~!au0s{xCF1n!q6R2}Vs|5bhfm4uW{y z_b{`dn-f>h;bMrDd$=Dp(!Og4G!%it&DGJh@A3D+Q1JZ{ziMebLN`WX4>r87VcmGhTQ7Fptwvf$Gt;jEHi>6=ElbDCFx@0bz7t1jN)DnNC(2;5 z^kPn~s&>G%arRFUeS$*vYUUNU*D_NR>o4tM)1XqW{*JivA1=1gh@)_o#g6nd%c5%b zw9z(FddrYxqoT3>E|^rxhK*S96|4uWvB~6JJw}%y%HZ?87);!T`Mw>N722a3%P)b+ zX(1`dew0gzU!mg9VdRLVo@zZzZYktj{um1JB=h6&*hAst(52n6uDQ0r_;Qan5Gn(! zN1F3svOlfQzKw>!Bm$U&ir0ReHqw6YI0q_L;+C!_@dPH7YK-~=rWZ-#J89WArMPx% zKJ3sAcy}irr*K#D4NPp9n3L_shu*{A2l4mb@5N9^1fT=AIQ<%Ey@?yk`V;JY3{sxq z+W?r^Z-;-8@hTLWS^ls|3?_q;tRMV0oTz?qM3B;i)8jU10Z;WWjC7c!Q__4~>D9vY z+`~c;Z8=#&{678;Y#x&M9e>V@&~-OV#;z$heN5xiWb?&!O!PFQbhGqagCvU{;ecuX zF-#&hH4RtOWZy<1`8k?OMEC(tuYF)}g6ouS1LF}Yi=hD|a*?)v16 zpf-=NFkp&$roNdjhv{5kSMJ_CC8)|LK;Ji&rUxtY*>*(FVtG_?0^)ELZ$_jKT(o|I z#QlpT?LUousniu+k94{!e?pvPlvB@66nZn_nMPS}I;OP6Bc5%PcOF!_IiWHKX-tqc zi`@j0>^+Ff+*HP9xbg?ps5KvnKf#9dB$93Pk-04BrUGv!qIeh4njoW;t--GwP(4frn9ry;5)>_?3YPK{X6qEz^XL?q zq}2P(YhmKxx;JWApk?Zf<#?Djt@?awfJw{Dhtux!1wm2?zL7LXTSyM-od}b_WJ+__ z%?XmmxfwB(;?)hz&!%E?!`UrEQ3jh5@U3Q=yKin#dk&ryD-=iJR>B=$7-W{Z>Gf6= zx$r3Plt(DvKPVm&x1)q*Yy8xm^HEd4*tuK%1iHPyol<8 zeU*HVRTNZ})1_xbV=~Jfg;$J8#BbeE#X;tLo-afr1~iMjw+m+5#0)-QpApje)4|6F zqZY6wUtA)y;Qz39_wiZJ|NsAYY#nv1Q^RVhWHK#@CexBIj3r^1=G-vm?9DJOXERJo z!ek!H`D9uWCexBmR!yrUlW9pZ+1nB_nUfdtI#i>v4TP zU#G|Gcpb0f*s+&xH-(N2|B%sBm^Sbsc63(YBG^4EZ>1bcW8mES5|wtF|2ii0`z%{I zo`oNHdtiV2FWJ18Q*k}OhjK47rWuD>;nO@;RQ|`xBH}rb+`)58SDK--@31o`@WI8m zci1Cjc%SUR%KQAkI_W^=zyC}4vy5pI18edl)rC|XcWh$h_|T9${OhGhd=k|S{z~VL z?Noe%{KiOcYECG>uVgHIIb)fD{+w%$o)8+6(>Aqt1{J^2=7XVs-Nlr9=kqbux&HYa zb+>xxGwItHyVBo7Yw-z{*2#a)3Xi=fwC!_b+GbK+P8*kyy!>{$H?SW$5p(McifaRt z@wwEGR9u$vruLBg_MX(3JONyh7uXbD z_s?TZsPvErUT=mz5LgsXM1J}I3{~Jl@k@>QSBRI(@IxQm`{d`ua3@uoe_6VS{D?|# zYXYAi^?fMt%H+TB3?E0OrDb=8Uq(M1STFzWMD0xeb^7WV$I6C%1HlW?#2H}eUEy;h~;yw5g z|6#gdWaz&hv6Th<9lZ4Wdjrqq;~(XML9a#Ow=<@l z9r#-PQY!6Aeo1jA7fw?`Lv+>;Tr*xsr88ULdHN{T-ZQj!H`T@d=hnE#0*_8$Ig6?M ze}NSq`gmYo{)gCi3YGss^?&VqUAT{0j}fmgRVnYLMze8?+GH?w0pT@olDs7zq?U4W8 zIDjs={1^A$i>I=4wMD)%#{AD3|GiH9zcNJ<=;_}R-l0$FOs4bVos8-9%+rDc{w*qf zcEz6HlYp?IwokJ3)VPpJZ07-?RdyXaGM`Ep(VTOC93I~H<-jWg@7b4gC*GH>^E1q#4Ase! zXA$2g3jfZPbmB9Ik<$Yk9yovGQ3YQAHSL zDf|+uVYIQsD*4!wYA6+lE<1A-m0nO;Va~LjO51+npu_NZD!qBs=e4m~dU&vn=pdfeWQoTNK+)f*S12M;!di-M(XQPYpL|!*MEi#-+zA4 z8ic1&xq0)ve7UX7YOmxdr3&l-&GU%&+osd1pvcl}-}wB85uNgT4EF=z_ib z;0k6-rOQH|CQLeqO0SlIy|sr*k7nS+kh(DNl=jaybml_q{1yjmUZ;?dp8k(C?fygf z7-mu6>FR$03q7A(qtPk+ai<-ua3vp4Q29@#?k1exev3K4Z1r2ABSTXc+sblH0qLB; z??3r?WTiS#wY`pbLT%F9yd^olisQ+qyv=*?NsRJ6UX6bC=jebXd-c{oM;9>4_jups zx5!Jm3=T|{^5^I?e~xbcbM(k%>brUlQ~AgI%Uby7Xs7DE^E#JNzQ?PTu|G$@{Btzs zgS{PYVwCUks&Dz9qrd(+I&%5m4)^aJ4XoIUuV6C_lsJ<6S zrL(pFHwEEOQXNIbRU;oxE~e5GX76Fy;lsemHgHLJGL@SBRp>(>+RFDiDz5&B#iAjW zlE>_+^d2T8@U@2>lzPbt@&9#!@1!+>mxH|@nLh;R$xgd|&$)q8&ox$u=Tpqbf!9DT zu(%Mqi0ZVxv+_Q#a_U$<=xX?nhVYo$wpSIsDWlMJIIE)@u6;on_*%lDpMAV%Fm82D$MAKGYNzuu z%pX}r+vpE?7U$P<_Oh`b@C>M7l)Elz)U#fPy+*@NXH<_Ye^$=l+PqAq$I)N;ny9qe zZEv~4hkxF74%H($g-X{ifxpXNK&8uFF7eso-BkL!icaRgo$7|bQ-NcPYcpT(F9KJ; zfse!e|2GXpzu)p&;UEq^DxJmyzf<`lxVQes=je@rwFq3uPoUz{mlsF+<}s$p{2yfd zzdq{OXq{JaB0cX*?apqiI0+ViX^%jmpMGg8Nly<8|B#pE3pNFgwh*f=4;}6;qU4T# z1K2^IG8#CN{D1u1&B9lQ>YkRP3GWkM@idvbx^ZOqdd9Q`oZT04z0?qReshx^&V^3;2JH=l?r%z7QB#O5iQ$ zW~x6gocsN|?KH#Htalt0U*O~yl4}{$dl6oB^n~p6EzcbPJINfzPNO04F7N{?Z2)hy z)h)D%GVpD;6B%{CZRbglPo>vJ&Sv~cU2T(f{*=w=z9q03oH%$PzJ*H1X5b{Yf=VxR zfln-Zf7kXdC51bMQ|RK55Bhkee}^jY3AoNhEmV3md7$F{%Zv6K7oSAIVPX3Z*X<4p*a|M$NBKcDgpw9)H1Sa10;@SdKF z#us?s{Kk*Aay^Ii&p%q{&qGhNg6~34$mKE6T^V8k8Peur;(-uF4zq4};Rf z4`0#n=S`#rrPktK{HXmFel#fMd~L$-w~-oxQ;Q$ZPX~VP=0`(N=K3eRC*UDU&G0^c zG{FP>XxLYV;rag7Kx)MvcC}LVM3)85mGVihp2Yw9*1y2X4wUNfsH^`@`sn+V>;I3_ zzyI9~xMB%e!H-s8rOQ>M2Br4QYJSxI5kDG~s@L$NJ+_t~4NC3$dVbWto*xbWow>oF z3BKei zxH>pv@$q;k@;>rF@=4cEsrfzSYNbYt_(%Pp^8}X92$dfK3x3gce92{rYgg)-GQ-tM zEnudrm0I!FNzrWQN{znZ>b;o@Xa;Y&hTs&H^N;dv*RE9mcU-O13cW{a!3#(=E!00Q zZt}IRU8&JeT^*eJ zq+#BYY0ve-{kiX`${$OZGQitjAE?2 zcIQ7y|Nj3c6a3Q!{6F;1{i`+B%Rx^!1En^$m#dYkqe#&~&XpSN>uO~;>Ornn>Y+G_ zd7?9)k^h0(d9`>8`2Wm>v}XKk&^#H zPIb+vsbEm{BhyLI)y|bVJB=szBPWtQ$fsTVzB0^av$$NHtX^{6lzJvkC$)C3kU9&O zxpt*SU*jL`9zKfo*X1tx5Y_K|wCJCH9;rd8`79tc|HaP9z*8#-)IYd`e*sIFK+`O9 z6Dl>zCrJLRak;=VzfTpYaghllzmm%l_oOu0MIh>d&uS z^48B^e?w}Ec)#S|BHpCrdI-8x zD-R$wX{2jcYC%0+y|2`C8GT%b;1nI~X3(G1jH6vYrH_+A*VCeIlvZob!+V_Pl3IcLUAt1-`+%#J8qIe;(fP!%z@Z)@&;qBphTzn>tO(Zv zo_6iQ*%N==P5-v*H^=2WuD?>FbNNSmc)s)bT62vrbPY>Lt@$!i$Kq;I3;u}Ipwwus ztAkVhK5?$p73{a9HtYvd^WEm!m8x@_8PSA4y9T9Rk2;Jg2q1=Ug|XMqhGuaB91!J6CGDS6m&O z@>1u)snwZ*D`&dtU+dtW(~S5BrH;V2NG))VbEPJ%aJ5nkde_xTjlSnt3M<8FXVInQNK;3R`6?~OHH@g)k@XhxPIS~+JG&t zU8#C&u7j;XsTu$1YNckh&DBbcZg=%xrKbDE_4}VPR}EU=4pI}ex(Srp(%sJgAk`Eq zbgBMfu2yP39Z7j7S9f-G7gB>#^XU<&bK6Gz*_npkuA{O$^*~ZHjCCHI@vM03HXwByYO0D3@uHQdNEhmxbLhar?p#yVu&N$1> zMyckpuKp*f>CSf31*cZ?JY4%dmDK8wBXusl!pn0@9jELbb}F{uOPOH#w$%mp-qO)kH3*+6QGz9BUzHKTvKTB*^ku2!o5kFMTV zs=b+Zb@|ox&)vz0R^T^MYy5|+_mJ8WFaPKm>8`@1R%AbFx`aeJSIT>lTF^nxm8$!Z zntwE@*wxRHn(if+r7mA1by+`;)bxu<4NCv@VlZfp zt6T@A7QEDDHL3OwT)R?Rw$jx~y&`?)YNh(GBh_z%tG{sVWZ=W}jer)kiHsnBA~nG; zq-M}UYKvP*4NCb=Qq%2nxtr8^qMJ6;rKa1DTJt;5Wt7Xl`d=HXLw`UsJcQJsR2}W| zQ0GdG4&ooZmp_iw3M7!4&qy`?52=2mXxI7X3{vw?_P;gOh@0@QQjhIe*H5Vho=fUf znntSqGE!Tb?%J;*H7Iogx{cHYegUZ!%6-I*JnHf>QiD<*o**^Dr(713TJvcndpqPs z=hI0I!KoE|71s*B;o6lt`sb4Tmm9Ls!E#a^*SNZ#)E4P~2dEi+MQVkkJyPYaew);w)Q)_Y)OJ@oS87KuboIh;cW5mEv_?x^gHjV#JO9Ata@VfZo>}R9 zm2;(5=ws)#uKtWv|MjF+Y@_q9bqTHoZzi?CMp6g;Hs?Q)8kCxG3#ooPoGUfMU9R0f zG4)@imgmtz7;@W2_Lb@w>N+a5;0~mg-Hp^kav(W?9PZjjkQ$WgKho9zL+1KBsN*O) zYJn$`L&zIleG{n#-A-yyYFFOr+Q+-haqas`&Hpaf?{3#GkUahx(Ls?%YC#3AgHl^s zNNUBNbgtC-?0HhVzJ$~Yy+W#ADXBpzFC#Uda#HiJbnQxAf`lw$L=!G{Sw-q;_z|f= zsrEIbj)6}}HGS!Plk;y#Enq9D4Qh6Et83rwJdCwhzs{uIf`s&7M7t=0)TypFN#BqG zq!xUb%NSA<9!YAtI9DgQJc(5QlSvIq&F4(#V_cp^YCh+8;rXv!b19%fsR^zn<+qSp z&>f^ExSP~2&vpKgs|#HFNd%7fMam;+l7mIu?I(zRUS;mwQ}#xPsU0y`-k^s4IGxsyk6@KAl~^?yg-a zkLb!Hs*wX7^m2I+sTuTf^}(bDr51RoYac-B5s!5_gw*_ok~#v0lNyxjcY>>x+7Bmo z)icUJ@@HznC(%JOI)zkys+%B*)RSclsUbMk?=07UtZP?l!RL`$@e5sEOlk!#)hnt- zE+;i8HKQwBUP)?2t|qlbH#onM)UdBqzuRcng2$6u;d@D~K%T27l3H%=Bu4ZsE_5A= zT!WEXkz$wATt4ISS(nd|91bBfNG)I%sTF?1wZBPfLFJ@|;DmBR7VvKj4PTR*u+e1` zsTJ5lY74fJTF`H#uCe#iN$>wSdHnz1nbF^yk2WZR71tg)K&j&|(rt-S4ZTS1%08qf z=K{vL4;|&|q2vYB50RQa-}yvR{icu_l>RphPcWhdKIO8Q)B>J$ z^^2q?EG0E4wcr`94o)qw%z1FiXF6AE{;zj)&;K_8&9L0%9G4X?=aQP>Jy*Z)a-s7o zQVUq(d>N^ue}!xR(D_HCHuMwcb=}zinsGg#2{yWhuUu{>wPKB~ZgRPmRR5n`y`5CQ z7FV~r`ZrhaCbeJ>uPa(!2bZ048PSB{4!XL!yQ?EyeSoWbx;o0$eO%qo)rXKeMh22v z;9%!Rky_4Bm&dsF+~eHH@ub#l6savch13M6IX}bIqe<=RGo6nmwV)K&e!k08*PceI z|3#$wUFO=aAQ^H)G@=Dw;~K6dHNkbRzMj+&oNB+pd2nilZ*s0wznfjHRQ)fPw~+o{ z!rtaO{BKe-yq)R$lTW%CPj&PAJE>pTgcuWWe1yE>CRA$1rLIlmDzZ9^71SL!ib;%cRK`7&4UE7ksiYY$Fs(5miy9bIeqq3f`()Qi)n zw5wm8%gR9*bqt5?IZC_Vhi|gjvmD-LT zq^8^7xl*4&4sd=rseZ9NIIcB=L4fiIQad<~)B@s3ZPzGLGd!8p5S*IPDbAJZmqcoQ zr#n}wKEq|Q^W+}xyfy~VBRG!Kf-i9$FLfRFm73vYu7A4ge-)`Mz1{hE=Q%DXkXqrp zUEbT55xp0mLTXTI3!Wf5*zSYEdih^@UZm9WULrO9%cOS9YoxBI=eT;lYhOUB|3a6G zTrMW{R$wKm=|3SgC{=$>YPt=ij)brD2&tjTbyR8vzH_xw9k-F%fxBG$ZkKyV4Z*4T zb<#%vS8BfD&i~9y{Qdk5T2Ob_L8*E7Beli{JO8Uxzy7YDGR#KxbvrnQQV;wf*R0gz zaHOl1I`@oleu8tQo+_u1TL06WD^;IPYJ1Nn^+1gywHj%p1~S)wxpuXKzmrZPt$YK1>=wNmq0>GGpoH}Z+=5S&_pPn|1Oe@1En>zpff0&66-pe9oNz9ZFdtIO|Q z{^0UQm$};*(FDz;&OWWA*6?>ygHkizLu%J`;2EwNdr3{#$<;nrcP7=po6G%34N4sg zQKaVIH{iMc5lzq^&N~+&6q^65^^>9)TRU)YcBso8W)O^k& zHQ%wM4#o>f|93GiW<)c(jMRIFOi~NDfz(kyp45anqz0v)?GKUa_b{miKH}PyTHxcP z=KCb6hv+5erOwMpj(@I%UBhfrGoD9kf$x!8!26^Ir53c<)k@XNNbQ*qNj)7uaar%$ zzaX_CTS(1st7~s2{oloCVMH_7O=?ZM@Reg73;UDWl3t`%C`w;NcBuvTb^QjoezC6K zAlFZ+J$fvuJu#fr{7xYGFXZg~*?z6jxvss}Mx`m{^ zruGS`{<-yxsN;H(}V=TbEnhbV@xPx4Zg($XtI1?UEgI)Rz86 zYK3;W{N2q!Dc|jCr4BA1zloBE>o-#Wm0HpLTziCT|Ig&{7ib3iyAFRRwTBLL{r@V} zuP6PKQ7#X1(<%LbaM8y>aB9mA$F*mUAT@&{No~K>Q(InO0ED78@qq;}q;q|R+mk(%ylQiD?d0;!!< z;@YPl;6Eqx3nB-lq=w+s3cZSJ25-22Z@GR-&2SE>e($*UcU@MJTCw*@4gdG_Z-1ac z9T(G4GpcekSnBF!E~`oPTjA=Jqz0u{WHqTRUqh!8%x z_X|=p`pS85YK9H?K+?yFM$>m8lspC0{)Sy(q+};k9nn7Pu6C6Tnm&cM? zks+ieILg(-NFC@SNcBr3HC+;^1)W7|Pn_-iJW_*FegVnyI);o>2U{?ZCxqso>G~

f(-Xk@?#iVvz4XNo@9>}q%j%xtT=o3;4 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 |{efayY0`&gVGQj`IDnMQToFbz$pq~yx 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%0AgJlO^JS}z$F^|R_|%>#X5s^@pR)=snuW4 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 U1zCIp9mun1;qr7c(c$O+15;07{r~^~ diff --git a/package.json b/package.json index 528f76e152..d7c38dc21a 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "build-docker-compose-prod": "docker compose -f docker-compose.build.yml build", "start-docker-compose-prod": "docker compose -f docker-compose.yml up", "build": "turbo build --filter=builder... --filter=viewer... --filter=landing-page... --filter=docs...", - "dev": "turbo dev --filter=builder... --filter=viewer...", + "dev": "turbo dev --filter=builder... --filter=viewer... --filter=@typebot.io/partykit... --concurrency=11", "generate-change-log": "git fetch --all && pnpx gitmoji-changelog", "sync-locales": "tolgee sync --continue-on-warning --remove-unused --patterns './apps/builder/src/**/*.ts?(x)' --backup './.tolgee/backup'", "pull-locales": "tolgee pull && prettier --write ./apps/builder/src/i18n", diff --git a/packages/blocks/core/src/helpers.ts b/packages/blocks/core/src/helpers.ts index 875b912761..1c7f06d986 100644 --- a/packages/blocks/core/src/helpers.ts +++ b/packages/blocks/core/src/helpers.ts @@ -10,8 +10,8 @@ import type { PictureChoiceBlock } from "@typebot.io/blocks-inputs/pictureChoice import type { InputBlock } from "@typebot.io/blocks-inputs/schema"; import type { TextInputBlock } from "@typebot.io/blocks-inputs/text/schema"; import { IntegrationBlockType } from "@typebot.io/blocks-integrations/constants"; +import type { HttpRequestBlock } from "@typebot.io/blocks-integrations/httpRequest/schema"; import type { IntegrationBlock } from "@typebot.io/blocks-integrations/schema"; -import type { HttpRequestBlock } from "@typebot.io/blocks-integrations/webhook/schema"; import type { ConditionBlock } from "@typebot.io/blocks-logic/condition/schema"; import { LogicBlockType } from "@typebot.io/blocks-logic/constants"; import type { LogicBlock } from "@typebot.io/blocks-logic/schema"; @@ -65,9 +65,9 @@ export const isIntegrationBlock = (block: Block): block is IntegrationBlock => ) as any[] ).includes(block.type); -export const isWebhookBlock = (block: Block): block is HttpRequestBlock => +export const isHttpRequestBlock = (block: Block): block is HttpRequestBlock => [ - IntegrationBlockType.WEBHOOK, + IntegrationBlockType.HTTP_REQUEST, IntegrationBlockType.PABBLY_CONNECT, IntegrationBlockType.ZAPIER, IntegrationBlockType.MAKE_COM, diff --git a/packages/blocks/core/src/migrations/migrateWebhookBlock.ts b/packages/blocks/core/src/migrations/migrateWebhookBlock.ts index 952e1f4691..a1cd670165 100644 --- a/packages/blocks/core/src/migrations/migrateWebhookBlock.ts +++ b/packages/blocks/core/src/migrations/migrateWebhookBlock.ts @@ -1,16 +1,16 @@ import { HttpMethod, - defaultWebhookAttributes, -} from "@typebot.io/blocks-integrations/webhook/constants"; -import type { HttpRequest } from "@typebot.io/blocks-integrations/webhook/schema"; + defaultHttpRequestAttributes, +} from "@typebot.io/blocks-integrations/httpRequest/constants"; +import type { HttpRequest } from "@typebot.io/blocks-integrations/httpRequest/schema"; import type { Prisma } from "@typebot.io/prisma/types"; -import { isWebhookBlock } from "../helpers"; +import { isHttpRequestBlock } from "../helpers"; import type { BlockV5 } from "../schemas/schema"; export const migrateWebhookBlock = (webhooks: Prisma.Webhook[]) => (block: BlockV5): BlockV5 => { - if (!isWebhookBlock(block)) return block; + if (!isHttpRequestBlock(block)) return block; const webhook = webhooks.find( (webhook) => "webhookId" in block && webhook.id === block.webhookId, ); @@ -31,7 +31,7 @@ export const migrateWebhookBlock = body: webhook.body ?? undefined, } : { - ...defaultWebhookAttributes, + ...defaultHttpRequestAttributes, id: "webhookId" in block ? (block.webhookId ?? "") : "", }, }, diff --git a/packages/blocks/integrations/src/constants.ts b/packages/blocks/integrations/src/constants.ts index cce176ca92..f5b71415a5 100644 --- a/packages/blocks/integrations/src/constants.ts +++ b/packages/blocks/integrations/src/constants.ts @@ -2,7 +2,7 @@ export enum IntegrationBlockType { GOOGLE_SHEETS = "Google Sheets", OPEN_AI = "OpenAI", GOOGLE_ANALYTICS = "Google Analytics", - WEBHOOK = "Webhook", + HTTP_REQUEST = "Webhook", EMAIL = "Email", ZAPIER = "Zapier", MAKE_COM = "Make.com", diff --git a/packages/blocks/integrations/src/webhook/constants.ts b/packages/blocks/integrations/src/httpRequest/constants.ts similarity index 83% rename from packages/blocks/integrations/src/webhook/constants.ts rename to packages/blocks/integrations/src/httpRequest/constants.ts index a1b853f684..845dd6e541 100644 --- a/packages/blocks/integrations/src/webhook/constants.ts +++ b/packages/blocks/integrations/src/httpRequest/constants.ts @@ -12,11 +12,11 @@ export enum HttpMethod { TRACE = "TRACE", } -export const defaultWebhookAttributes = { +export const defaultHttpRequestAttributes = { method: HttpMethod.POST, } as const; -export const defaultWebhookBlockOptions = { +export const defaultHttpRequestBlockOptions = { isAdvancedConfig: false, isCustomBody: false, isExecutedOnClient: false, diff --git a/packages/blocks/integrations/src/webhook/schema.ts b/packages/blocks/integrations/src/httpRequest/schema.ts similarity index 98% rename from packages/blocks/integrations/src/webhook/schema.ts rename to packages/blocks/integrations/src/httpRequest/schema.ts index 80e252af41..87c498f236 100644 --- a/packages/blocks/integrations/src/webhook/schema.ts +++ b/packages/blocks/integrations/src/httpRequest/schema.ts @@ -69,7 +69,7 @@ const httpRequestOptionsSchemas = { const httpBlockV5Schema = blockBaseSchema.merge( z.object({ type: z - .enum([IntegrationBlockType.WEBHOOK]) + .enum([IntegrationBlockType.HTTP_REQUEST]) .describe("Legacy name for HTTP Request block"), options: httpRequestOptionsSchemas.v5.optional(), webhookId: z.string().optional(), diff --git a/packages/blocks/integrations/src/makeCom/schema.ts b/packages/blocks/integrations/src/makeCom/schema.ts index c1ef9f69d8..bf718248c5 100644 --- a/packages/blocks/integrations/src/makeCom/schema.ts +++ b/packages/blocks/integrations/src/makeCom/schema.ts @@ -1,6 +1,6 @@ import { z } from "@typebot.io/zod"; import { IntegrationBlockType } from "../constants"; -import { httpBlockSchemas } from "../webhook/schema"; +import { httpBlockSchemas } from "../httpRequest/schema"; export const makeComBlockSchemas = { v5: httpBlockSchemas.v5.merge( diff --git a/packages/blocks/integrations/src/pabblyConnect/schema.ts b/packages/blocks/integrations/src/pabblyConnect/schema.ts index e89731e3ca..f3fdc581b3 100644 --- a/packages/blocks/integrations/src/pabblyConnect/schema.ts +++ b/packages/blocks/integrations/src/pabblyConnect/schema.ts @@ -1,6 +1,6 @@ import { z } from "@typebot.io/zod"; import { IntegrationBlockType } from "../constants"; -import { httpBlockSchemas } from "../webhook/schema"; +import { httpBlockSchemas } from "../httpRequest/schema"; export const pabblyConnectBlockSchemas = { v5: httpBlockSchemas.v5.merge( diff --git a/packages/blocks/integrations/src/schema.ts b/packages/blocks/integrations/src/schema.ts index a88c11698e..88cdbbcca6 100644 --- a/packages/blocks/integrations/src/schema.ts +++ b/packages/blocks/integrations/src/schema.ts @@ -2,12 +2,12 @@ import { z } from "@typebot.io/zod"; import { chatwootBlockSchema } from "./chatwoot/schema"; import { googleAnalyticsBlockSchema } from "./googleAnalytics/schema"; import { googleSheetsBlockSchemas } from "./googleSheets/schema"; +import { httpBlockSchemas } from "./httpRequest/schema"; import { makeComBlockSchemas } from "./makeCom/schema"; import { openAIBlockSchema } from "./openai/schema"; import { pabblyConnectBlockSchemas } from "./pabblyConnect/schema"; import { pixelBlockSchema } from "./pixel/schema"; import { sendEmailBlockSchema } from "./sendEmail/schema"; -import { httpBlockSchemas } from "./webhook/schema"; import { zapierBlockSchemas } from "./zapier/schema"; const integrationBlockSchemas = [ diff --git a/packages/blocks/integrations/src/zapier/schema.ts b/packages/blocks/integrations/src/zapier/schema.ts index f7f81421a9..61aaa1fe44 100644 --- a/packages/blocks/integrations/src/zapier/schema.ts +++ b/packages/blocks/integrations/src/zapier/schema.ts @@ -1,6 +1,6 @@ import { z } from "@typebot.io/zod"; import { IntegrationBlockType } from "../constants"; -import { httpBlockSchemas } from "../webhook/schema"; +import { httpBlockSchemas } from "../httpRequest/schema"; export const zapierBlockSchemas = { v5: httpBlockSchemas.v5.merge( diff --git a/packages/blocks/logic/src/constants.ts b/packages/blocks/logic/src/constants.ts index 360d4b67a2..339a7d1299 100644 --- a/packages/blocks/logic/src/constants.ts +++ b/packages/blocks/logic/src/constants.ts @@ -7,4 +7,5 @@ export enum LogicBlockType { WAIT = "Wait", JUMP = "Jump", AB_TEST = "AB test", + WEBHOOK = "webhook", } diff --git a/packages/blocks/logic/src/schema.ts b/packages/blocks/logic/src/schema.ts index 51bb6b22d4..81e4110997 100644 --- a/packages/blocks/logic/src/schema.ts +++ b/packages/blocks/logic/src/schema.ts @@ -7,6 +7,7 @@ import { scriptBlockSchema } from "./script/schema"; import { setVariableBlockSchema } from "./setVariable/schema"; import { typebotLinkBlockSchema } from "./typebotLink/schema"; import { waitBlockSchema } from "./wait/schema"; +import { webhookBlockSchema } from "./webhook/schema"; const logicBlockSchemas = [ scriptBlockSchema, @@ -28,6 +29,7 @@ export const logicBlockV6Schema = z.discriminatedUnion("type", [ ...logicBlockSchemas, conditionBlockSchemas.v6, abTestBlockSchemas.v6, + webhookBlockSchema, ]); export type LogicBlockV6 = z.infer; diff --git a/packages/blocks/logic/src/webhook/schema.ts b/packages/blocks/logic/src/webhook/schema.ts new file mode 100644 index 0000000000..40dbe4d841 --- /dev/null +++ b/packages/blocks/logic/src/webhook/schema.ts @@ -0,0 +1,29 @@ +import { blockBaseSchema } from "@typebot.io/blocks-base/schemas"; +import { z } from "@typebot.io/zod"; +import { LogicBlockType } from "../constants"; + +export const waitOptionsSchema = z.object({ + responseVariableMapping: z + .array( + z.object({ + id: z.string(), + variableId: z.string().optional(), + bodyPath: z.string().optional(), + }), + ) + .optional(), +}); + +export const webhookBlockSchema = blockBaseSchema + .merge( + z.object({ + type: z.enum([LogicBlockType.WEBHOOK]), + options: waitOptionsSchema.optional(), + }), + ) + .openapi({ + title: "Webhook", + ref: "webhookLogic", + }); + +export type WebhookBlock = z.infer; diff --git a/packages/bot-engine/src/apiHandlers/continueChat.ts b/packages/bot-engine/src/apiHandlers/continueChat.ts index 25a1d517ed..f3c6dadc4f 100644 --- a/packages/bot-engine/src/apiHandlers/continueChat.ts +++ b/packages/bot-engine/src/apiHandlers/continueChat.ts @@ -79,7 +79,7 @@ export const continueChat = async ({ clientSideActions, visitedEdges, setVariableHistory, - hasEmbedBubbleWithWaitEvent: messages.some( + isWaitingForExternalEvent: messages.some( (message) => message.type === "custom-embed" || (message.type === BubbleBlockType.EMBED && diff --git a/packages/bot-engine/src/apiHandlers/startChat.ts b/packages/bot-engine/src/apiHandlers/startChat.ts index 2e77b71162..98c55ba3af 100644 --- a/packages/bot-engine/src/apiHandlers/startChat.ts +++ b/packages/bot-engine/src/apiHandlers/startChat.ts @@ -76,7 +76,7 @@ export const startChat = async ({ clientSideActions, visitedEdges, setVariableHistory, - hasEmbedBubbleWithWaitEvent: messages.some( + isWaitingForExternalEvent: messages.some( (message) => message.type === "custom-embed" || (message.type === BubbleBlockType.EMBED && diff --git a/packages/bot-engine/src/apiHandlers/startChatPreview.ts b/packages/bot-engine/src/apiHandlers/startChatPreview.ts index 6810b7bc44..fdffc793d1 100644 --- a/packages/bot-engine/src/apiHandlers/startChatPreview.ts +++ b/packages/bot-engine/src/apiHandlers/startChatPreview.ts @@ -70,7 +70,7 @@ export const startChatPreview = async ({ clientSideActions, visitedEdges, setVariableHistory, - hasEmbedBubbleWithWaitEvent: messages.some( + isWaitingForExternalEvent: messages.some( (message) => message.type === "custom-embed" || (message.type === BubbleBlockType.EMBED && diff --git a/packages/bot-engine/src/blocks/integrations/webhook/executeWebhookBlock.ts b/packages/bot-engine/src/blocks/integrations/httpRequest/executeHttpRequestBlock.ts similarity index 92% rename from packages/bot-engine/src/blocks/integrations/webhook/executeWebhookBlock.ts rename to packages/bot-engine/src/blocks/integrations/httpRequest/executeHttpRequestBlock.ts index 1ce3528155..508071bb2a 100644 --- a/packages/bot-engine/src/blocks/integrations/webhook/executeWebhookBlock.ts +++ b/packages/bot-engine/src/blocks/integrations/httpRequest/executeHttpRequestBlock.ts @@ -1,18 +1,18 @@ -import type { MakeComBlock } from "@typebot.io/blocks-integrations/makeCom/schema"; -import type { PabblyConnectBlock } from "@typebot.io/blocks-integrations/pabblyConnect/schema"; import { HttpMethod, + defaultHttpRequestAttributes, defaultTimeout, - defaultWebhookAttributes, maxTimeout, -} from "@typebot.io/blocks-integrations/webhook/constants"; +} from "@typebot.io/blocks-integrations/httpRequest/constants"; import type { ExecutableHttpRequest, HttpRequest, HttpRequestBlock, HttpResponse, KeyValue, -} from "@typebot.io/blocks-integrations/webhook/schema"; +} from "@typebot.io/blocks-integrations/httpRequest/schema"; +import type { MakeComBlock } from "@typebot.io/blocks-integrations/makeCom/schema"; +import type { PabblyConnectBlock } from "@typebot.io/blocks-integrations/pabblyConnect/schema"; import type { ZapierBlock } from "@typebot.io/blocks-integrations/zapier/schema"; import { env } from "@typebot.io/env"; import { JSONParse } from "@typebot.io/lib/JSONParse"; @@ -30,7 +30,7 @@ import type { TypebotInSession, } from "../../../schemas/chatSession"; import type { ExecuteIntegrationResponse } from "../../../types"; -import { resumeWebhookExecution } from "./resumeWebhookExecution"; +import { saveDataInResponseVariableMapping } from "./saveDataInResponseVariableMapping"; type ParsedWebhook = ExecutableHttpRequest & { basicAuth: { username?: string; password?: string }; @@ -50,7 +50,7 @@ export const webhookErrorDescription = `Webhook returned an error.`; type Params = { disableRequestTimeout?: boolean; timeout?: number }; -export const executeWebhookBlock = async ( +export const executeHttpRequestBlock = async ( state: SessionState, block: HttpRequestBlock | ZapierBlock | MakeComBlock | PabblyConnectBlock, params: Params = {}, @@ -64,13 +64,13 @@ export const executeWebhookBlock = async ( })) as HttpRequest | null) : null); if (!webhook) return { outgoingEdgeId: block.outgoingEdgeId }; - const parsedWebhook = await parseWebhookAttributes({ + const parsedHttpRequest = await parseWebhookAttributes({ webhook, isCustomBody: block.options?.isCustomBody, typebot: state.typebotsQueue[0].typebot, answers: state.typebotsQueue[0].answers, }); - if (!parsedWebhook) { + if (!parsedHttpRequest) { logs.push({ status: "error", description: `Couldn't parse webhook attributes`, @@ -82,8 +82,8 @@ export const executeWebhookBlock = async ( outgoingEdgeId: block.outgoingEdgeId, clientSideActions: [ { - type: "webhookToExecute", - webhookToExecute: parsedWebhook, + type: "httpRequestToExecute", + httpRequestToExecute: parsedHttpRequest, expectsDedicatedReply: true, }, ], @@ -92,15 +92,18 @@ export const executeWebhookBlock = async ( response: webhookResponse, logs: executeWebhookLogs, startTimeShouldBeUpdated, - } = await executeWebhook(parsedWebhook, { + } = await executeHttpRequest(parsedHttpRequest, { ...params, timeout: block.options?.timeout, }); return { - ...resumeWebhookExecution({ + ...saveDataInResponseVariableMapping({ state, - block, + blockType: block.type, + blockId: block.id, + responseVariableMapping: block.options?.responseVariableMapping, + outgoingEdgeId: block.outgoingEdgeId, logs: executeWebhookLogs, response: webhookResponse, }), @@ -153,7 +156,7 @@ export const parseWebhookAttributes = async ({ variables: typebot.variables, isCustomBody, }); - const method = webhook.method ?? defaultWebhookAttributes.method; + const method = webhook.method ?? defaultHttpRequestAttributes.method; const { data: body, isJson } = bodyContent && method !== HttpMethod.GET ? safeJsonParse( @@ -175,7 +178,7 @@ export const parseWebhookAttributes = async ({ }; }; -export const executeWebhook = async ( +export const executeHttpRequest = async ( webhook: ParsedWebhook, params: Params = {}, ): Promise<{ diff --git a/packages/bot-engine/src/blocks/integrations/webhook/parseSampleResult.ts b/packages/bot-engine/src/blocks/integrations/httpRequest/parseSampleResult.ts similarity index 100% rename from packages/bot-engine/src/blocks/integrations/webhook/parseSampleResult.ts rename to packages/bot-engine/src/blocks/integrations/httpRequest/parseSampleResult.ts diff --git a/packages/bot-engine/src/blocks/integrations/webhook/resumeWebhookExecution.ts b/packages/bot-engine/src/blocks/integrations/httpRequest/saveDataInResponseVariableMapping.ts similarity index 61% rename from packages/bot-engine/src/blocks/integrations/webhook/resumeWebhookExecution.ts rename to packages/bot-engine/src/blocks/integrations/httpRequest/saveDataInResponseVariableMapping.ts index 571248b656..64c3225955 100644 --- a/packages/bot-engine/src/blocks/integrations/webhook/resumeWebhookExecution.ts +++ b/packages/bot-engine/src/blocks/integrations/httpRequest/saveDataInResponseVariableMapping.ts @@ -1,7 +1,5 @@ -import type { MakeComBlock } from "@typebot.io/blocks-integrations/makeCom/schema"; -import type { PabblyConnectBlock } from "@typebot.io/blocks-integrations/pabblyConnect/schema"; -import type { HttpRequestBlock } from "@typebot.io/blocks-integrations/webhook/schema"; -import type { ZapierBlock } from "@typebot.io/blocks-integrations/zapier/schema"; +import type { IntegrationBlockType } from "@typebot.io/blocks-integrations/constants"; +import { LogicBlockType } from "@typebot.io/blocks-logic/constants"; import { byId } from "@typebot.io/lib/utils"; import { createHttpReqResponseMappingRunner } from "@typebot.io/variables/codeRunners"; import { parseVariables } from "@typebot.io/variables/parseVariables"; @@ -13,46 +11,65 @@ import { updateVariablesInSession } from "../../../updateVariablesInSession"; type Props = { state: SessionState; - block: HttpRequestBlock | ZapierBlock | MakeComBlock | PabblyConnectBlock; + blockType: + | LogicBlockType.WEBHOOK + | IntegrationBlockType.HTTP_REQUEST + | IntegrationBlockType.ZAPIER + | IntegrationBlockType.MAKE_COM + | IntegrationBlockType.PABBLY_CONNECT; + blockId: string; + responseVariableMapping?: { + bodyPath?: string; + variableId?: string; + }[]; + outgoingEdgeId?: string; logs?: ChatLog[]; response: { - statusCode: number; + statusCode?: number; data?: unknown; }; }; -export const resumeWebhookExecution = ({ +export const saveDataInResponseVariableMapping = ({ state, - block, + blockType, + blockId, + responseVariableMapping, + outgoingEdgeId, logs = [], response, }: Props): ExecuteIntegrationResponse => { const { typebot } = state.typebotsQueue[0]; - const status = response.statusCode.toString(); - const isError = status.startsWith("4") || status.startsWith("5"); + const status = response.statusCode?.toString(); + const isError = status + ? status.startsWith("4") || status.startsWith("5") + : false; const responseFromClient = logs.length === 0; - if (responseFromClient) + if (responseFromClient) { + const blockLabel = + blockType === LogicBlockType.WEBHOOK ? "Webhook" : "HTTP request"; logs.push( isError ? { status: "error", - description: `Webhook returned error`, + description: `${blockLabel} returned error`, details: response.data, } : { status: "success", - description: `Webhook executed successfully!`, + description: `${blockLabel} executed successfully!`, details: response.data, }, ); + } - let run: (varMapping: string) => unknown; - if (block.options?.responseVariableMapping) { + let run: ((varMapping: string) => unknown) | undefined; + if (responseVariableMapping) { run = createHttpReqResponseMappingRunner(response); } - const newVariables = block.options?.responseVariableMapping?.reduce< + const newVariables = responseVariableMapping?.reduce< VariableWithUnknowValue[] >((newVariables, varMapping) => { if (!varMapping?.bodyPath || !varMapping.variableId || !run) @@ -75,10 +92,10 @@ export const resumeWebhookExecution = ({ const { updatedState, newSetVariableHistory } = updateVariablesInSession({ newVariables, state, - currentBlockId: block.id, + currentBlockId: blockId, }); return { - outgoingEdgeId: block.outgoingEdgeId, + outgoingEdgeId, newSessionState: updatedState, newSetVariableHistory, logs, @@ -86,7 +103,7 @@ export const resumeWebhookExecution = ({ } return { - outgoingEdgeId: block.outgoingEdgeId, + outgoingEdgeId, logs, }; }; diff --git a/packages/bot-engine/src/blocks/logic/webhook/executeWebhookBlock.ts b/packages/bot-engine/src/blocks/logic/webhook/executeWebhookBlock.ts new file mode 100644 index 0000000000..eb422fcbd6 --- /dev/null +++ b/packages/bot-engine/src/blocks/logic/webhook/executeWebhookBlock.ts @@ -0,0 +1,14 @@ +import type { WebhookBlock } from "@typebot.io/blocks-logic/webhook/schema"; +import type { ExecuteLogicResponse } from "../../../types"; + +export const executeWebhookBlock = ( + block: WebhookBlock, +): ExecuteLogicResponse => ({ + outgoingEdgeId: block.outgoingEdgeId, + clientSideActions: [ + { + type: "listenForWebhook", + expectsDedicatedReply: true, + }, + ], +}); diff --git a/packages/bot-engine/src/continueBotFlow.ts b/packages/bot-engine/src/continueBotFlow.ts index 5ea2cf70fd..54e90e989c 100644 --- a/packages/bot-engine/src/continueBotFlow.ts +++ b/packages/bot-engine/src/continueBotFlow.ts @@ -20,6 +20,7 @@ import type { ForgedBlock } from "@typebot.io/forge-repository/schemas"; import { getBlockById } from "@typebot.io/groups/helpers"; import type { Group } from "@typebot.io/groups/schemas"; import { isURL } from "@typebot.io/lib/isURL"; +import { stringifyError } from "@typebot.io/lib/stringifyError"; import { byId, isDefined } from "@typebot.io/lib/utils"; import type { Prisma } from "@typebot.io/prisma/types"; import type { AnswerInSessionState } from "@typebot.io/results/schemas/answers"; @@ -36,8 +37,8 @@ import { validateNumber } from "./blocks/inputs/number/validateNumber"; import { formatPhoneNumber } from "./blocks/inputs/phone/formatPhoneNumber"; import { parsePictureChoicesReply } from "./blocks/inputs/pictureChoice/parsePictureChoicesReply"; import { validateRatingReply } from "./blocks/inputs/rating/validateRatingReply"; +import { saveDataInResponseVariableMapping } from "./blocks/integrations/httpRequest/saveDataInResponseVariableMapping"; import { resumeChatCompletion } from "./blocks/integrations/legacy/openai/resumeChatCompletion"; -import { resumeWebhookExecution } from "./blocks/integrations/webhook/resumeWebhookExecution"; import { executeGroup, parseInput } from "./executeGroup"; import { getNextGroup } from "./getNextGroup"; import { saveAnswer } from "./queries/saveAnswer"; @@ -240,11 +241,31 @@ const processNonInputBlock = async ({ })(reply.text); newSessionState = result.newSessionState; } - } else if (reply && block.type === IntegrationBlockType.WEBHOOK) { - const result = resumeWebhookExecution({ + } else if ( + reply && + (block.type === IntegrationBlockType.HTTP_REQUEST || + block.type === LogicBlockType.WEBHOOK) + ) { + let response: { + statusCode?: number; + data?: unknown; + }; + try { + response = JSON.parse(reply.text); + } catch (err) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Provided response is not valid JSON", + cause: stringifyError(err), + }); + } + const result = saveDataInResponseVariableMapping({ state, - block, - response: JSON.parse(reply.text), + blockType: block.type, + blockId: block.id, + responseVariableMapping: block.options?.responseVariableMapping, + outgoingEdgeId: block.outgoingEdgeId, + response, }); if (result.newSessionState) newSessionState = result.newSessionState; } else if (isForgedBlockType(block.type)) { diff --git a/packages/bot-engine/src/executeIntegration.ts b/packages/bot-engine/src/executeIntegration.ts index 26c5c667d8..adcdec3df9 100644 --- a/packages/bot-engine/src/executeIntegration.ts +++ b/packages/bot-engine/src/executeIntegration.ts @@ -4,11 +4,11 @@ import { env } from "@typebot.io/env"; import { isNotDefined } from "@typebot.io/lib/utils"; import { executeChatwootBlock } from "./blocks/integrations/chatwoot/executeChatwootBlock"; import { executeGoogleSheetBlock } from "./blocks/integrations/googleSheets/executeGoogleSheetBlock"; +import { executeHttpRequestBlock } from "./blocks/integrations/httpRequest/executeHttpRequestBlock"; import { executeGoogleAnalyticsBlock } from "./blocks/integrations/legacy/googleAnalytics/executeGoogleAnalyticsBlock"; import { executeOpenAIBlock } from "./blocks/integrations/legacy/openai/executeOpenAIBlock"; import { executePixelBlock } from "./blocks/integrations/pixel/executePixelBlock"; import { executeSendEmailBlock } from "./blocks/integrations/sendEmail/executeSendEmailBlock"; -import { executeWebhookBlock } from "./blocks/integrations/webhook/executeWebhookBlock"; import { executeForgedBlock } from "./forge/executeForgedBlock"; import type { SessionState } from "./schemas/chatSession"; import type { ExecuteIntegrationResponse } from "./types"; @@ -32,14 +32,14 @@ export const executeIntegration = case IntegrationBlockType.MAKE_COM: case IntegrationBlockType.PABBLY_CONNECT: return { - ...(await executeWebhookBlock(state, block, { + ...(await executeHttpRequestBlock(state, block, { disableRequestTimeout: true, })), startTimeShouldBeUpdated: true, }; - case IntegrationBlockType.WEBHOOK: + case IntegrationBlockType.HTTP_REQUEST: return { - ...(await executeWebhookBlock(state, block, { + ...(await executeHttpRequestBlock(state, block, { disableRequestTimeout: isNotDefined(env.CHAT_API_TIMEOUT), })), }; diff --git a/packages/bot-engine/src/executeLogic.ts b/packages/bot-engine/src/executeLogic.ts index 0d928c739f..d8669e6246 100644 --- a/packages/bot-engine/src/executeLogic.ts +++ b/packages/bot-engine/src/executeLogic.ts @@ -9,6 +9,7 @@ import { executeScript } from "./blocks/logic/script/executeScript"; import { executeSetVariable } from "./blocks/logic/setVariable/executeSetVariable"; import { executeTypebotLink } from "./blocks/logic/typebotLink/executeTypebotLink"; import { executeWait } from "./blocks/logic/wait/executeWait"; +import { executeWebhookBlock } from "./blocks/logic/webhook/executeWebhookBlock"; import type { SessionState } from "./schemas/chatSession"; import type { ExecuteLogicResponse } from "./types"; @@ -35,5 +36,7 @@ export const executeLogic = return executeJumpBlock(state, block.options); case LogicBlockType.AB_TEST: return executeAbTest(state, block); + case LogicBlockType.WEBHOOK: + return executeWebhookBlock(block); } }; diff --git a/packages/bot-engine/src/logs/filterPotentiallySensitiveLogs.ts b/packages/bot-engine/src/logs/filterPotentiallySensitiveLogs.ts index ddf9753fe0..98a882d986 100644 --- a/packages/bot-engine/src/logs/filterPotentiallySensitiveLogs.ts +++ b/packages/bot-engine/src/logs/filterPotentiallySensitiveLogs.ts @@ -1,11 +1,11 @@ +import { + webhookErrorDescription, + webhookSuccessDescription, +} from "../blocks/integrations/httpRequest/executeHttpRequestBlock"; import { sendEmailErrorDescription, sendEmailSuccessDescription, } from "../blocks/integrations/sendEmail/executeSendEmailBlock"; -import { - webhookErrorDescription, - webhookSuccessDescription, -} from "../blocks/integrations/webhook/executeWebhookBlock"; export const filterPotentiallySensitiveLogs = (log: { status: string; diff --git a/packages/bot-engine/src/saveStateToDatabase.ts b/packages/bot-engine/src/saveStateToDatabase.ts index 97b07e5915..7ff84408c5 100644 --- a/packages/bot-engine/src/saveStateToDatabase.ts +++ b/packages/bot-engine/src/saveStateToDatabase.ts @@ -8,24 +8,24 @@ import { upsertResult } from "./queries/upsertResult"; import type { ChatSession, ContinueChatResponse } from "./schemas/api"; type Props = { - session: Pick & { id?: string }; + session: Pick & { id?: string; isReplying?: boolean }; input: ContinueChatResponse["input"]; logs: ContinueChatResponse["logs"]; clientSideActions: ContinueChatResponse["clientSideActions"]; visitedEdges: Prisma.VisitedEdge[]; setVariableHistory: SetVariableHistoryItem[]; - hasEmbedBubbleWithWaitEvent?: boolean; + isWaitingForExternalEvent?: boolean; initialSessionId?: string; }; export const saveStateToDatabase = async ({ - session: { state, id }, + session: { state, id, isReplying }, input, logs, clientSideActions, visitedEdges, setVariableHistory, - hasEmbedBubbleWithWaitEvent, + isWaitingForExternalEvent, initialSessionId, }: Props) => { const containsSetVariableClientSideAction = clientSideActions?.some( @@ -35,7 +35,7 @@ export const saveStateToDatabase = async ({ const isCompleted = Boolean( !input && !containsSetVariableClientSideAction && - !hasEmbedBubbleWithWaitEvent, + !isWaitingForExternalEvent, ); const queries: Prisma.PrismaPromise[] = []; @@ -44,12 +44,19 @@ export const saveStateToDatabase = async ({ if (id) { if (isCompleted && resultId) queries.push(deleteSession(id)); - else queries.push(updateSession({ id, state, isReplying: false })); + else + queries.push( + updateSession({ id, state, isReplying: isReplying ?? false }), + ); } const session = id ? { state, id } - : await createSession({ id: initialSessionId, state, isReplying: false }); + : await createSession({ + id: initialSessionId, + state, + isReplying: isReplying ?? false, + }); if (!resultId) { if (queries.length > 0) await prisma.$transaction(queries); diff --git a/packages/bot-engine/src/schemas/api.ts b/packages/bot-engine/src/schemas/api.ts index 665d108d6a..39e46090b5 100644 --- a/packages/bot-engine/src/schemas/api.ts +++ b/packages/bot-engine/src/schemas/api.ts @@ -76,7 +76,7 @@ const chatSessionSchema = z.object({ .describe( "Used in WhatsApp runtime to avoid concurrent replies from the bot", ), -}) satisfies z.ZodType; +}); export type ChatSession = z.infer; const textMessageSchema = z diff --git a/packages/bot-engine/src/schemas/clientSideAction.ts b/packages/bot-engine/src/schemas/clientSideAction.ts index 1e14d43a25..d586ca1464 100644 --- a/packages/bot-engine/src/schemas/clientSideAction.ts +++ b/packages/bot-engine/src/schemas/clientSideAction.ts @@ -1,7 +1,7 @@ import { googleAnalyticsOptionsSchema } from "@typebot.io/blocks-integrations/googleAnalytics/schema"; +import { executableHttpRequestSchema } from "@typebot.io/blocks-integrations/httpRequest/schema"; import { nativeMessageSchema } from "@typebot.io/blocks-integrations/openai/schema"; import { pixelOptionsSchema } from "@typebot.io/blocks-integrations/pixel/schema"; -import { executableHttpRequestSchema } from "@typebot.io/blocks-integrations/webhook/schema"; import { redirectOptionsSchema } from "@typebot.io/blocks-logic/redirect/schema"; import { listVariableValue } from "@typebot.io/variables/schemas"; import { z } from "@typebot.io/zod"; @@ -115,13 +115,13 @@ export const clientSideActionSchema = z.discriminatedUnion("type", [ }), z .object({ - type: z.literal("webhookToExecute"), - webhookToExecute: executableHttpRequestSchema, + type: z.literal("httpRequestToExecute"), + httpRequestToExecute: executableHttpRequestSchema, }) .merge(clientSideActionBaseSchema) .openapi({ - ref: "csaExecWebhook", - title: "Execute webhook", + ref: "csaHttpRequestToExecute", + title: "Execute HTTP request", }), z .object({ @@ -166,4 +166,14 @@ export const clientSideActionSchema = z.discriminatedUnion("type", [ ref: "csaCodeToExecute", title: "Execute code", }), + z + .object({ + type: z.literal("listenForWebhook"), + }) + .merge(clientSideActionBaseSchema) + .openapi({ + ref: "csaListenForWebhook", + title: "Listen to webhook", + }), ]); +export type ClientSideAction = z.infer; diff --git a/packages/deprecated/bot-engine/src/features/blocks/integrations/webhook/utils/executeWebhookBlock.ts b/packages/deprecated/bot-engine/src/features/blocks/integrations/webhook/utils/executeWebhookBlock.ts index f78f8498ad..33f24b4004 100644 --- a/packages/deprecated/bot-engine/src/features/blocks/integrations/webhook/utils/executeWebhookBlock.ts +++ b/packages/deprecated/bot-engine/src/features/blocks/integrations/webhook/utils/executeWebhookBlock.ts @@ -1,8 +1,8 @@ import { parseVariables } from "@/features/variables"; import type { IntegrationState } from "@/types"; +import type { HttpRequestBlock } from "@typebot.io/blocks-integrations/httpRequest/schema"; import type { MakeComBlock } from "@typebot.io/blocks-integrations/makeCom/schema"; import type { PabblyConnectBlock } from "@typebot.io/blocks-integrations/pabblyConnect/schema"; -import type { HttpRequestBlock } from "@typebot.io/blocks-integrations/webhook/schema"; import type { ZapierBlock } from "@typebot.io/blocks-integrations/zapier/schema"; import { byId, sendRequest } from "@typebot.io/lib/utils"; import type { VariableWithUnknowValue } from "@typebot.io/variables/schemas"; diff --git a/packages/deprecated/bot-engine/src/utils/executeIntegration.ts b/packages/deprecated/bot-engine/src/utils/executeIntegration.ts index 4b1d350559..8d6946f6e1 100644 --- a/packages/deprecated/bot-engine/src/utils/executeIntegration.ts +++ b/packages/deprecated/bot-engine/src/utils/executeIntegration.ts @@ -22,7 +22,7 @@ export const executeIntegration = ({ case IntegrationBlockType.ZAPIER: case IntegrationBlockType.MAKE_COM: case IntegrationBlockType.PABBLY_CONNECT: - case IntegrationBlockType.WEBHOOK: + case IntegrationBlockType.HTTP_REQUEST: return executeWebhook(block, context); case IntegrationBlockType.EMAIL: return executeSendEmailBlock(block, context); diff --git a/packages/embeds/js/package.json b/packages/embeds/js/package.json index 68c3ecd047..f206a0214e 100644 --- a/packages/embeds/js/package.json +++ b/packages/embeds/js/package.json @@ -1,6 +1,6 @@ { "name": "@typebot.io/js", - "version": "0.3.17", + "version": "0.3.18", "description": "Javascript library to display typebots on your website", "license": "AGPL-3.0-or-later", "type": "module", @@ -39,7 +39,8 @@ "ky": "1.2.4", "marked": "9.0.3", "solid-element": "1.7.1", - "solid-js": "1.7.8" + "solid-js": "1.7.8", + "partysocket": "1.0.2" }, "devDependencies": { "@babel/preset-typescript": "7.24.7", diff --git a/packages/embeds/js/src/components/ConversationContainer/ConversationContainer.tsx b/packages/embeds/js/src/components/ConversationContainer/ConversationContainer.tsx index 1adc1096f5..5c8f98a92f 100644 --- a/packages/embeds/js/src/components/ConversationContainer/ConversationContainer.tsx +++ b/packages/embeds/js/src/components/ConversationContainer/ConversationContainer.tsx @@ -284,6 +284,7 @@ export const ConversationContainer = (props: Props) => { context: { apiHost: props.context.apiHost, sessionId: props.initialChatReply.sessionId, + resultId: props.initialChatReply.resultId, }, onMessageStream: streamMessage, }); diff --git a/packages/embeds/js/src/features/blocks/integrations/webhook/executeWebhook.ts b/packages/embeds/js/src/features/blocks/integrations/httpRequest/executeHttpRequest.ts similarity index 75% rename from packages/embeds/js/src/features/blocks/integrations/webhook/executeWebhook.ts rename to packages/embeds/js/src/features/blocks/integrations/httpRequest/executeHttpRequest.ts index 813f90b565..4f9aec8ec7 100644 --- a/packages/embeds/js/src/features/blocks/integrations/webhook/executeWebhook.ts +++ b/packages/embeds/js/src/features/blocks/integrations/httpRequest/executeHttpRequest.ts @@ -1,9 +1,9 @@ -import type { ExecutableHttpRequest } from "@typebot.io/blocks-integrations/webhook/schema"; +import type { ExecutableHttpRequest } from "@typebot.io/blocks-integrations/httpRequest/schema"; -export const executeWebhook = async ( - webhookToExecute: ExecutableHttpRequest, +export const executeHttpRequest = async ( + httpRequestToExecute: ExecutableHttpRequest, ): Promise => { - const { url, method, body, headers } = webhookToExecute; + const { url, method, body, headers } = httpRequestToExecute; try { const response = await fetch(url, { method, diff --git a/packages/embeds/js/src/features/blocks/logic/script/executeScript.ts b/packages/embeds/js/src/features/blocks/logic/script/executeScript.ts index 8d9fd93403..f352b54666 100644 --- a/packages/embeds/js/src/features/blocks/logic/script/executeScript.ts +++ b/packages/embeds/js/src/features/blocks/logic/script/executeScript.ts @@ -15,7 +15,6 @@ export const executeScript = async ({ parseContent(content), ); await func(...args.map((arg) => arg.value)); - console.log(parseContent(content)); } catch (err) { console.log(err); return { diff --git a/packages/embeds/js/src/features/blocks/logic/webhook/listenForWebhook.ts b/packages/embeds/js/src/features/blocks/logic/webhook/listenForWebhook.ts new file mode 100644 index 0000000000..6915d4516a --- /dev/null +++ b/packages/embeds/js/src/features/blocks/logic/webhook/listenForWebhook.ts @@ -0,0 +1,45 @@ +import type { ChatLog } from "@typebot.io/bot-engine/schemas/api"; +import { getRuntimeVariable } from "@typebot.io/env/getRuntimeVariable"; +import PartySocket from "partysocket"; + +type Props = { + resultId?: string; + sessionId: string; +}; + +export const listenForWebhook = ({ sessionId, resultId }: Props) => { + const host = getRuntimeVariable("NEXT_PUBLIC_PARTYKIT_HOST"); + if (!host) return; + + const ws = new PartySocket({ + host, + room: getRoomName({ sessionId, resultId }), + }); + return new Promise<{ replyToSend: string | undefined; logs?: ChatLog[] }>( + (resolve) => { + ws.addEventListener("message", (event) => { + ws.close(); + resolve({ replyToSend: event.data }); + }); + + ws.addEventListener("error", (error) => { + resolve({ + logs: [ + { + status: "error", + description: "Websocket returned an error", + details: JSON.stringify(error, null, 2), + }, + ], + replyToSend: undefined, + }); + }); + }, + ); +}; + +const getRoomName = ({ sessionId, resultId }: Props) => { + if (resultId) return `${resultId}/webhooks`; + const [typebotId, userId] = sessionId.split("-"); + return `${userId}/${typebotId}/webhooks`; +}; diff --git a/packages/embeds/js/src/queries/startChatQuery.ts b/packages/embeds/js/src/queries/startChatQuery.ts index 5667c2258a..fa859e99de 100644 --- a/packages/embeds/js/src/queries/startChatQuery.ts +++ b/packages/embeds/js/src/queries/startChatQuery.ts @@ -49,65 +49,22 @@ export async function startChatQuery({ }) : undefined; if (paymentInProgressState) { - removePaymentInProgressFromStorage(); - - try { - const data = await ky - .post( - `${isNotEmpty(apiHost) ? apiHost : guessApiHost()}/api/v1/sessions/${ - paymentInProgressState.sessionId - }/continueChat`, - { - json: { - message: paymentInProgressState - ? stripeRedirectStatus === "failed" - ? "fail" - : "Success" - : undefined, - }, - timeout: false, - }, - ) - .json(); - - return { - data: { - ...data, - ...paymentInProgressState, - } satisfies StartChatResponse, - }; - } catch (error) { - return { error }; - } + return resumeChatAfterPaymentRedirect({ + apiHost, + stripeRedirectStatus, + paymentInProgressState, + }); } const typebotId = typeof typebot === "string" ? typebot : typebot.id; if (isPreview) { - try { - const data = await ky - .post( - `${ - isNotEmpty(apiHost) ? apiHost : guessApiHost() - }/api/v1/typebots/${typebotId}/preview/startChat`, - { - json: { - isStreamEnabled: true, - startFrom, - typebot, - prefilledVariables, - sessionId, - } satisfies Omit< - StartPreviewChatInput, - "typebotId" | "isOnlyRegistering" | "textBubbleContentFormat" - >, - timeout: false, - }, - ) - .json(); - - return { data }; - } catch (error) { - return { error }; - } + return startPreviewChat({ + apiHost, + typebotId, + startFrom, + typebot, + prefilledVariables, + sessionId, + }); } try { @@ -116,9 +73,7 @@ export async function startChatQuery({ ? new URL(document.referrer).origin : undefined; const response = await ky.post( - `${ - isNotEmpty(apiHost) ? apiHost : guessApiHost() - }/api/v1/typebots/${typebotId}/startChat`, + `${getApiHost(apiHost)}/api/v1/typebots/${typebotId}/startChat`, { headers: { "x-typebot-iframe-referrer-origin": iframeReferrerOrigin, @@ -151,3 +106,87 @@ export async function startChatQuery({ return { error }; } } + +const resumeChatAfterPaymentRedirect = async ({ + apiHost, + stripeRedirectStatus, + paymentInProgressState, +}: { + apiHost?: string; + stripeRedirectStatus?: string; + paymentInProgressState: { + sessionId: string; + typebot: BotContext["typebot"]; + }; +}) => { + removePaymentInProgressFromStorage(); + + try { + const data = await ky + .post( + `${getApiHost(apiHost)}/api/v1/sessions/${ + paymentInProgressState.sessionId + }/continueChat`, + { + json: { + message: stripeRedirectStatus === "failed" ? "fail" : "Success", + }, + timeout: false, + }, + ) + .json(); + + return { + data: { + ...data, + ...paymentInProgressState, + } as StartChatResponse, + }; + } catch (error) { + return { error }; + } +}; + +const startPreviewChat = async ({ + apiHost, + typebotId, + startFrom, + typebot, + prefilledVariables, + sessionId, +}: { + apiHost?: string; + typebotId: string; + startFrom?: StartFrom; + typebot: StartPreviewChatInput["typebot"]; + prefilledVariables?: Record; + sessionId?: string; +}) => { + try { + const data = await ky + .post( + `${getApiHost(apiHost)}/api/v1/typebots/${typebotId}/preview/startChat`, + { + json: { + isStreamEnabled: true, + startFrom, + typebot, + prefilledVariables, + sessionId, + } satisfies Omit< + StartPreviewChatInput, + "typebotId" | "isOnlyRegistering" | "textBubbleContentFormat" + >, + timeout: false, + }, + ) + .json(); + + return { data }; + } catch (error) { + return { error }; + } +}; + +const getApiHost = (apiHost?: string): string => + isNotEmpty(apiHost) ? apiHost : guessApiHost(); diff --git a/packages/embeds/js/src/types.ts b/packages/embeds/js/src/types.ts index 0531870e4f..eeda1c21d5 100644 --- a/packages/embeds/js/src/types.ts +++ b/packages/embeds/js/src/types.ts @@ -21,6 +21,7 @@ export type OutgoingLog = { export type ClientSideActionContext = { apiHost?: string; sessionId: string; + resultId?: string; }; export type ChatChunk = Pick< diff --git a/packages/embeds/js/src/utils/executeClientSideActions.ts b/packages/embeds/js/src/utils/executeClientSideActions.ts index e0c6388d58..e79911bf44 100644 --- a/packages/embeds/js/src/utils/executeClientSideActions.ts +++ b/packages/embeds/js/src/utils/executeClientSideActions.ts @@ -1,8 +1,8 @@ import { executeChatwoot } from "@/features/blocks/integrations/chatwoot/utils/executeChatwoot"; import { executeGoogleAnalyticsBlock } from "@/features/blocks/integrations/googleAnalytics/utils/executeGoogleAnalytics"; +import { executeHttpRequest } from "@/features/blocks/integrations/httpRequest/executeHttpRequest"; import { streamChat } from "@/features/blocks/integrations/openai/streamChat"; import { executePixel } from "@/features/blocks/integrations/pixel/executePixel"; -import { executeWebhook } from "@/features/blocks/integrations/webhook/executeWebhook"; import { executeRedirect } from "@/features/blocks/logic/redirect/utils/executeRedirect"; import { executeCode, @@ -10,6 +10,7 @@ import { } from "@/features/blocks/logic/script/executeScript"; import { executeSetVariable } from "@/features/blocks/logic/setVariable/executeSetVariable"; import { executeWait } from "@/features/blocks/logic/wait/utils/executeWait"; +import { listenForWebhook } from "@/features/blocks/logic/webhook/listenForWebhook"; import type { ClientSideActionContext } from "@/types"; import type { ChatLog, @@ -78,8 +79,10 @@ export const executeClientSideAction = async ({ }; return { replyToSend: message }; } - if ("webhookToExecute" in clientSideAction) { - const response = await executeWebhook(clientSideAction.webhookToExecute); + if ("httpRequestToExecute" in clientSideAction) { + const response = await executeHttpRequest( + clientSideAction.httpRequestToExecute, + ); return { replyToSend: response }; } if ("startPropsToInject" in clientSideAction) { @@ -91,4 +94,10 @@ export const executeClientSideAction = async ({ if ("codeToExecute" in clientSideAction) { return executeCode(clientSideAction.codeToExecute); } + if (clientSideAction.type === "listenForWebhook") { + return listenForWebhook({ + sessionId: context.sessionId, + resultId: context.resultId, + }); + } }; diff --git a/packages/embeds/nextjs/package.json b/packages/embeds/nextjs/package.json index be61ffa51e..e070db1f63 100644 --- a/packages/embeds/nextjs/package.json +++ b/packages/embeds/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@typebot.io/nextjs", - "version": "0.3.17", + "version": "0.3.18", "license": "AGPL-3.0-or-later", "description": "Convenient library to display typebots on your Next.js website", "type": "module", diff --git a/packages/embeds/react/package.json b/packages/embeds/react/package.json index 79e80a16ac..3a2f58d763 100644 --- a/packages/embeds/react/package.json +++ b/packages/embeds/react/package.json @@ -1,6 +1,6 @@ { "name": "@typebot.io/react", - "version": "0.3.17", + "version": "0.3.18", "description": "Convenient library to display typebots on your React app", "license": "AGPL-3.0-or-later", "type": "module", diff --git a/packages/env/src/index.ts b/packages/env/src/index.ts index 5b9384e8a5..8a5d5f7183 100644 --- a/packages/env/src/index.ts +++ b/packages/env/src/index.ts @@ -443,6 +443,15 @@ const keycloakEnv = { }, }; +const partykitEnv = { + client: { + NEXT_PUBLIC_PARTYKIT_HOST: z.string().min(1).optional(), + }, + runtimeEnv: { + NEXT_PUBLIC_PARTYKIT_HOST: getRuntimeVariable("NEXT_PUBLIC_PARTYKIT_HOST"), + }, +}; + export const env = createEnv({ server: { ...baseEnv.server, @@ -477,6 +486,7 @@ export const env = createEnv({ ...sentryEnv.client, ...posthogEnv.client, ...tolgeeEnv.client, + ...partykitEnv.client, }, experimental__runtimeEnv: { ...baseEnv.runtimeEnv, @@ -491,6 +501,7 @@ export const env = createEnv({ ...sentryEnv.runtimeEnv, ...posthogEnv.runtimeEnv, ...tolgeeEnv.runtimeEnv, + ...partykitEnv.runtimeEnv, }, skipValidation: process.env.SKIP_ENV_CHECK === "true" || diff --git a/packages/lib/src/api/utils.ts b/packages/lib/src/api/utils.ts index c9b44c9fd5..e74266e7cc 100644 --- a/packages/lib/src/api/utils.ts +++ b/packages/lib/src/api/utils.ts @@ -19,6 +19,12 @@ export const badRequest = (res: NextApiResponse, customMessage?: any) => export const forbidden = (res: NextApiResponse, customMessage?: string) => res.status(403).json({ message: customMessage ?? "Forbidden" }); +export const internalServerError = ( + res: NextApiResponse, + customMessage?: string, +) => + res.status(500).json({ message: customMessage ?? "Internal server error" }); + export const initMiddleware = ( handler: ( diff --git a/packages/partykit/package.json b/packages/partykit/package.json new file mode 100644 index 0000000000..59fba14f12 --- /dev/null +++ b/packages/partykit/package.json @@ -0,0 +1,11 @@ +{ + "name": "@typebot.io/partykit", + "scripts": { + "dev": "partykit dev --live", + "deploy": "partykit deploy" + }, + "devDependencies": { + "partykit": "0.0.110", + "@typebot.io/tsconfig": "workspace:*" + } +} diff --git a/packages/partykit/partykit.json b/packages/partykit/partykit.json new file mode 100644 index 0000000000..3509dd0fbb --- /dev/null +++ b/packages/partykit/partykit.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://www.partykit.io/schema.json", + "name": "typebot", + "main": "src/webhookServer.ts", + "compatibilityDate": "2024-10-01" +} diff --git a/packages/partykit/src/webhookServer.ts b/packages/partykit/src/webhookServer.ts new file mode 100644 index 0000000000..96e11b6443 --- /dev/null +++ b/packages/partykit/src/webhookServer.ts @@ -0,0 +1,36 @@ +import type * as Party from "partykit/server"; + +export default class Server implements Party.Server { + options: Party.ServerOptions = { + hibernate: true, + }; + + constructor(readonly room: Party.Room) {} + + async onRequest(request: Party.Request) { + if (request.method === "POST") { + let payload: unknown; + try { + payload = await request.json(); + } catch (err) { + return new Response("Invalid payload, please send JSON body", { + status: 400, + }); + } + if ( + payload === null || + typeof payload !== "object" || + Array.isArray(payload) + ) + return new Response("Invalid payload, please send JSON body", { + status: 400, + }); + this.room.broadcast(JSON.stringify({ data: payload })); + return new Response("OK"); + } + + return new Response("Method not allowed", { status: 405 }); + } +} + +Server satisfies Party.Worker; diff --git a/packages/partykit/tsconfig.json b/packages/partykit/tsconfig.json new file mode 100644 index 0000000000..f03bab32b7 --- /dev/null +++ b/packages/partykit/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@typebot.io/tsconfig/base.json", + "include": ["src/**/*.ts"] +} diff --git a/packages/tsconfig/base.json b/packages/tsconfig/base.json index c25736d9ce..ab80a7dd59 100644 --- a/packages/tsconfig/base.json +++ b/packages/tsconfig/base.json @@ -2,12 +2,6 @@ "$schema": "https://json.schemastore.org/tsconfig", "display": "Default", "compilerOptions": { - "plugins": [ - { - "name": "ts-plugin-sort-import-suggestions", - "moveUpPatterns": ["@typebot.io/"] - } - ], "esModuleInterop": true, "skipLibCheck": true, "target": "es2022", diff --git a/packages/tsconfig/nextjs.json b/packages/tsconfig/nextjs.json index a3cb862e31..1701e7ac66 100644 --- a/packages/tsconfig/nextjs.json +++ b/packages/tsconfig/nextjs.json @@ -3,13 +3,7 @@ "display": "Next.js", "extends": "./base.json", "compilerOptions": { - "plugins": [ - { "name": "next" }, - { - "name": "ts-plugin-sort-import-suggestions", - "moveUpPatterns": ["@typebot.io/"] - } - ], + "plugins": [{ "name": "next" }], "jsx": "preserve", "allowJs": true, "incremental": true, diff --git a/apps/builder/src/features/typebot/helpers/isReadTypebotForbidden.ts b/packages/typebot/src/helpers/isReadTypebotForbidden.ts similarity index 100% rename from apps/builder/src/features/typebot/helpers/isReadTypebotForbidden.ts rename to packages/typebot/src/helpers/isReadTypebotForbidden.ts diff --git a/packages/typebot/src/migrations/migrateTypebotFromV3ToV4.ts b/packages/typebot/src/migrations/migrateTypebotFromV3ToV4.ts index d0b9ebccae..2d6d565c04 100644 --- a/packages/typebot/src/migrations/migrateTypebotFromV3ToV4.ts +++ b/packages/typebot/src/migrations/migrateTypebotFromV3ToV4.ts @@ -1,4 +1,4 @@ -import { isWebhookBlock } from "@typebot.io/blocks-core/helpers"; +import { isHttpRequestBlock } from "@typebot.io/blocks-core/helpers"; import { migrateWebhookBlock } from "@typebot.io/blocks-core/migrations/migrateWebhookBlock"; import { isDefined } from "@typebot.io/lib/utils"; import prisma from "@typebot.io/prisma"; @@ -12,7 +12,7 @@ export const migrateTypebotFromV3ToV4 = async ( return typebot as Omit & { version: "4" }; const webhookBlocks = typebot.groups .flatMap((group) => group.blocks) - .filter(isWebhookBlock); + .filter(isHttpRequestBlock); const webhooks = await prisma.webhook.findMany({ where: { id: { diff --git a/packages/variables/src/codeRunners.ts b/packages/variables/src/codeRunners.ts index 0a6d7de0c2..fee38300b2 100644 --- a/packages/variables/src/codeRunners.ts +++ b/packages/variables/src/codeRunners.ts @@ -20,7 +20,13 @@ export const createCodeRunner = ({ variables }: { variables: Variable[] }) => { ); }; -export const createHttpReqResponseMappingRunner = (response: any) => { +export const createHttpReqResponseMappingRunner = (response: unknown) => { + if ( + response === null || + typeof response !== "object" || + Array.isArray(response) + ) + return; const isolate = new ivm.Isolate(); const context = isolate.createContextSync(); const jail = context.global; diff --git a/packages/whatsapp/src/receiveMessage.ts b/packages/whatsapp/src/receiveMessage.ts deleted file mode 100644 index 54b8f8dad7..0000000000 --- a/packages/whatsapp/src/receiveMessage.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { isNotDefined } from "@typebot.io/lib/utils"; -import { resumeWhatsAppFlow } from "./resumeWhatsAppFlow"; -import type { WhatsAppWebhookRequestBody } from "./schemas"; - -type Props = { - entry: WhatsAppWebhookRequestBody["entry"]; - credentialsId: string; - workspaceId: string; -}; - -export const receiveMessage = async ({ - entry, - credentialsId, - workspaceId, -}: Props) => { - const receivedMessage = entry.at(0)?.changes.at(0)?.value.messages?.at(0); - if (isNotDefined(receivedMessage)) return { message: "No message found" }; - const contactName = - entry.at(0)?.changes.at(0)?.value?.contacts?.at(0)?.profile?.name ?? ""; - const contactPhoneNumber = - entry.at(0)?.changes.at(0)?.value?.messages?.at(0)?.from ?? ""; - const phoneNumberId = entry.at(0)?.changes.at(0)?.value - .metadata.phone_number_id; - if (!phoneNumberId) return { message: "No phone number id found" }; - return resumeWhatsAppFlow({ - receivedMessage, - sessionId: `wa-${phoneNumberId}-${receivedMessage.from}`, - credentialsId, - workspaceId, - contact: { - name: contactName, - phoneNumber: contactPhoneNumber, - }, - }); -}; diff --git a/packages/whatsapp/src/receiveMessagePreview.ts b/packages/whatsapp/src/receiveMessagePreview.ts deleted file mode 100644 index c273328ce2..0000000000 --- a/packages/whatsapp/src/receiveMessagePreview.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { TRPCError } from "@trpc/server"; -import { env } from "@typebot.io/env"; -import { isNotDefined } from "@typebot.io/lib/utils"; -import { resumeWhatsAppFlow } from "./resumeWhatsAppFlow"; -import type { WhatsAppWebhookRequestBody } from "./schemas"; - -type Props = { - entry: WhatsAppWebhookRequestBody["entry"]; -}; -export const receiveMessagePreview = ({ entry }: Props) => { - if (!env.WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID) - throw new TRPCError({ - code: "INTERNAL_SERVER_ERROR", - message: "WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID is not defined", - }); - const receivedMessage = entry.at(0)?.changes.at(0)?.value.messages?.at(0); - if (isNotDefined(receivedMessage)) return { message: "No message found" }; - const contactName = - entry.at(0)?.changes.at(0)?.value?.contacts?.at(0)?.profile?.name ?? ""; - const contactPhoneNumber = - entry.at(0)?.changes.at(0)?.value?.messages?.at(0)?.from ?? ""; - - return resumeWhatsAppFlow({ - receivedMessage, - sessionId: `wa-preview-${receivedMessage.from}`, - contact: { - name: contactName, - phoneNumber: contactPhoneNumber, - }, - }); -}; diff --git a/packages/whatsapp/src/resumeWhatsAppFlow.ts b/packages/whatsapp/src/resumeWhatsAppFlow.ts index fa57ceb569..3645a75e16 100644 --- a/packages/whatsapp/src/resumeWhatsAppFlow.ts +++ b/packages/whatsapp/src/resumeWhatsAppFlow.ts @@ -6,7 +6,10 @@ import { removeIsReplyingInChatSession } from "@typebot.io/bot-engine/queries/re import { setIsReplyingInChatSession } from "@typebot.io/bot-engine/queries/setIsReplyingInChatSession"; import { saveStateToDatabase } from "@typebot.io/bot-engine/saveStateToDatabase"; import type { Message } from "@typebot.io/bot-engine/schemas/api"; -import type { SessionState } from "@typebot.io/bot-engine/schemas/chatSession"; +import type { + ChatSession, + SessionState, +} from "@typebot.io/bot-engine/schemas/chatSession"; import { env } from "@typebot.io/env"; import { getBlockById } from "@typebot.io/groups/helpers"; import { decrypt } from "@typebot.io/lib/api/encryption/decrypt"; @@ -27,7 +30,13 @@ type Props = { credentialsId?: string; phoneNumberId?: string; workspaceId?: string; - contact: NonNullable["contact"]; + contact?: NonNullable["contact"]; + origin?: "webhook"; +}; + +const isMessageTooOld = (receivedMessage: WhatsAppIncomingMessage) => { + const messageSendDate = new Date(Number(receivedMessage.timestamp) * 1000); + return messageSendDate.getTime() < Date.now() - 180000; }; export const resumeWhatsAppFlow = async ({ @@ -37,50 +46,37 @@ export const resumeWhatsAppFlow = async ({ credentialsId, phoneNumberId, contact, -}: Props): Promise<{ message: string }> => { - const messageSendDate = new Date(Number(receivedMessage.timestamp) * 1000); - const messageSentBefore3MinutesAgo = - messageSendDate.getTime() < Date.now() - 180000; - if (messageSentBefore3MinutesAgo) { - console.log("Message is too old", messageSendDate.getTime()); - return { - message: "Message received", - }; +}: Props) => { + if (isMessageTooOld(receivedMessage)) { + console.log("Message is too old"); + return; } const isPreview = workspaceId === undefined || credentialsId === undefined; const credentials = await getCredentials({ credentialsId, isPreview }); - if (!credentials) { console.error("Could not find credentials"); - return { - message: "Message received", - }; + return; } - if (credentials.phoneNumberId !== phoneNumberId && !isPreview) { + if (phoneNumberId && credentials.phoneNumberId !== phoneNumberId) { console.error("Credentials point to another phone ID, skipping..."); - return { - message: "Message received", - }; + return; } const session = await getSession(sessionId); - const { incomingMessages, isReplyingWasSet } = + const aggregationResponse = await aggregateParallelMediaMessagesIfRedisEnabled({ receivedMessage, existingSessionId: session?.id, newSessionId: sessionId, }); - if (incomingMessages.length === 0) { - if (isReplyingWasSet) await removeIsReplyingInChatSession(sessionId); - - return { - message: "Message received", - }; + if (aggregationResponse.status === "found newer message") { + console.log("Found newer message, skipping this one"); + return; } const isSessionExpired = @@ -88,13 +84,11 @@ export const resumeWhatsAppFlow = async ({ isDefined(session.state.expiryTimeout) && session?.updatedAt.getTime() + session.state.expiryTimeout < Date.now(); - if (!isReplyingWasSet) { - if (session?.isReplying) { + if (aggregationResponse.status === "treat as unique message") { + if (session?.isReplying && origin !== "webhook") { if (!isSessionExpired) { console.log("Is currently replying, skipping..."); - return { - message: "Message received", - }; + return; } } else { await setIsReplyingInChatSession({ @@ -109,9 +103,8 @@ export const resumeWhatsAppFlow = async ({ (currentTypebot && session?.state.currentBlockId ? getBlockById(session.state.currentBlockId, currentTypebot.groups) : undefined) ?? {}; - - const reply = await getIncomingMessageContent({ - messages: incomingMessages, + const reply = await convertWhatsAppMessageToTypebotMessage({ + messages: aggregationResponse.incomingMessages, workspaceId, accessToken: credentials?.systemUserAccessToken, typebotId: currentTypebot?.id, @@ -119,51 +112,26 @@ export const resumeWhatsAppFlow = async ({ block, }); - const resumeResponse = - session && !isSessionExpired - ? await continueBotFlow(reply, { - version: 2, - state: { ...session.state, whatsApp: { contact } }, - textBubbleContentFormat: "richText", - }) - : workspaceId - ? await startWhatsAppSession({ - incomingMessage: reply, - workspaceId, - credentials: { ...credentials, id: credentialsId as string }, - contact, - }) - : { error: "workspaceId not found" }; - - if ("error" in resumeResponse) { - await removeIsReplyingInChatSession(sessionId); - console.log("Chat not starting:", resumeResponse.error); - return { - message: "Message received", - }; - } - const { input, logs, - newSessionState, - messages, - clientSideActions, visitedEdges, setVariableHistory, - } = resumeResponse; - - const isFirstChatChunk = (!session || isSessionExpired) ?? false; - await sendChatReplyToWhatsApp({ - to: receivedMessage.from, - messages, - input, - isFirstChatChunk, - typingEmulation: newSessionState.typingEmulation, - clientSideActions, - credentials, - state: newSessionState, - }); + newSessionState, + isWaitingForWebhook, + } = + (await resumeFlowAndSendWhatsAppMessages({ + to: receivedMessage.from, + credentials, + isSessionExpired, + reply, + session, + sessionId, + contact, + workspaceId, + credentialsId, + })) ?? {}; + if (!newSessionState || !visitedEdges || !setVariableHistory) return; await saveStateToDatabase({ clientSideActions: [], @@ -171,11 +139,16 @@ export const resumeWhatsAppFlow = async ({ logs, session: { id: sessionId, + isReplying: isWaitingForWebhook, state: { ...newSessionState, - currentBlockId: !input ? undefined : newSessionState.currentBlockId, + currentBlockId: + !input && !isWaitingForWebhook + ? undefined + : newSessionState.currentBlockId, }, }, + isWaitingForExternalEvent: isWaitingForWebhook, visitedEdges, setVariableHistory, }); @@ -185,7 +158,7 @@ export const resumeWhatsAppFlow = async ({ }; }; -const getIncomingMessageContent = async ({ +const convertWhatsAppMessageToTypebotMessage = async ({ messages, workspaceId, accessToken, @@ -289,6 +262,9 @@ const getIncomingMessageContent = async ({ else text = location; break; } + case "webhook": { + text = message.webhook.data; + } } } @@ -348,10 +324,19 @@ const aggregateParallelMediaMessagesIfRedisEnabled = async ({ receivedMessage: WhatsAppIncomingMessage; existingSessionId?: string; newSessionId: string; -}): Promise<{ - isReplyingWasSet: boolean; - incomingMessages: WhatsAppIncomingMessage[]; -}> => { +}): Promise< + | { + status: "treat as unique message"; + incomingMessages: [WhatsAppIncomingMessage]; + } + | { + status: "found newer message"; + } + | { + status: "ready to reply"; + incomingMessages: WhatsAppIncomingMessage[]; + } +> => { if (redis && ["document", "video", "image"].includes(receivedMessage.type)) { const redisKey = `wasession:${newSessionId}`; try { @@ -370,15 +355,13 @@ const aggregateParallelMediaMessagesIfRedisEnabled = async ({ const newMessagesResponse = await redis.lrange(redisKey, 0, -1); - if (!newMessagesResponse || newMessagesResponse.length > len) { - // Current message was aggregated with other messages another webhook handler. Skipping... - return { isReplyingWasSet: true, incomingMessages: [] }; - } + if (!newMessagesResponse || newMessagesResponse.length > len) + return { status: "found newer message" }; redis.del(redisKey).then(); return { - isReplyingWasSet: true, + status: "ready to reply", incomingMessages: newMessagesResponse.map((msgStr) => JSON.parse(msgStr), ), @@ -389,7 +372,103 @@ const aggregateParallelMediaMessagesIfRedisEnabled = async ({ } return { - isReplyingWasSet: false, + status: "treat as unique message", incomingMessages: [receivedMessage], }; }; + +const resumeFlowAndSendWhatsAppMessages = async (props: { + to: string; + session: Pick | null; + sessionId: string; + reply: Message | undefined; + contact?: NonNullable["contact"]; + credentials: WhatsAppCredentials["data"]; + isSessionExpired: boolean | null; + credentialsId?: string; + workspaceId?: string; +}) => { + const resumeResponse = await resumeFlow(props); + if ("error" in resumeResponse) { + await removeIsReplyingInChatSession(props.sessionId); + console.error(resumeResponse.error); + return; + } + + const { + input, + logs, + messages, + clientSideActions, + visitedEdges, + setVariableHistory, + newSessionState, + } = resumeResponse; + + const isFirstChatChunk = (!props.session || props.isSessionExpired) ?? false; + const result = await sendChatReplyToWhatsApp({ + to: props.to, + messages, + input, + isFirstChatChunk, + clientSideActions, + credentials: props.credentials, + state: resumeResponse.newSessionState, + }); + if (result?.type === "replyToSend") + return resumeFlowAndSendWhatsAppMessages({ + ...props, + reply: result.replyToSend + ? { + type: "text", + text: result.replyToSend, + } + : undefined, + }); + + return { + input, + logs, + visitedEdges, + setVariableHistory, + newSessionState, + isWaitingForWebhook: result?.type === "shouldWaitForWebhook", + }; +}; + +const resumeFlow = ({ + session, + isSessionExpired, + reply, + contact, + credentials, + credentialsId, + workspaceId, +}: { + reply: Message | undefined; + contact?: NonNullable["contact"]; + session: Pick | null; + credentials: WhatsAppCredentials["data"]; + isSessionExpired: boolean | null; + credentialsId?: string; + workspaceId?: string; +}) => { + if (session?.state && !isSessionExpired) + return continueBotFlow(reply, { + version: 2, + state: contact + ? { ...session.state, whatsApp: { contact } } + : session.state, + textBubbleContentFormat: "richText", + }); + if (!workspaceId || !contact) + return { + error: "Can't start WhatsApp session without workspaceId or contact", + }; + return startWhatsAppSession({ + incomingMessage: reply, + workspaceId, + credentials: { ...credentials, id: credentialsId as string }, + contact, + }); +}; diff --git a/packages/whatsapp/src/schemas.ts b/packages/whatsapp/src/schemas.ts index 168ae0a37f..6493f0cdf7 100644 --- a/packages/whatsapp/src/schemas.ts +++ b/packages/whatsapp/src/schemas.ts @@ -142,6 +142,14 @@ export const incomingMessageSchema = z.discriminatedUnion("type", [ }), timestamp: z.string(), }), + z.object({ + from: z.string(), + type: z.literal("webhook"), + webhook: z.object({ + data: z.string(), + }), + timestamp: z.string(), + }), ]); export const whatsAppWebhookRequestBodySchema = z.object({ diff --git a/packages/whatsapp/src/sendChatReplyToWhatsApp.ts b/packages/whatsapp/src/sendChatReplyToWhatsApp.ts index 7d4597fb4e..bfb01b1213 100644 --- a/packages/whatsapp/src/sendChatReplyToWhatsApp.ts +++ b/packages/whatsapp/src/sendChatReplyToWhatsApp.ts @@ -2,9 +2,9 @@ import * as Sentry from "@sentry/nextjs"; import { BubbleBlockType } from "@typebot.io/blocks-bubbles/constants"; import { InputBlockType } from "@typebot.io/blocks-inputs/constants"; import { computeTypingDuration } from "@typebot.io/bot-engine/computeTypingDuration"; -import { continueBotFlow } from "@typebot.io/bot-engine/continueBotFlow"; import type { ContinueChatResponse } from "@typebot.io/bot-engine/schemas/api"; import type { SessionState } from "@typebot.io/bot-engine/schemas/chatSession"; +import type { ClientSideAction } from "@typebot.io/bot-engine/schemas/clientSideAction"; import { isNotDefined } from "@typebot.io/lib/utils"; import { defaultSettings } from "@typebot.io/settings/constants"; import type { Settings } from "@typebot.io/settings/schemas"; @@ -20,21 +20,19 @@ const messageAfterMediaTimeout = 5000; type Props = { to: string; isFirstChatChunk: boolean; - typingEmulation: SessionState["typingEmulation"]; credentials: WhatsAppCredentials["data"]; state: SessionState; } & Pick; export const sendChatReplyToWhatsApp = async ({ to, - typingEmulation, isFirstChatChunk, messages, input, clientSideActions, credentials, state, -}: Props): Promise => { +}: Props): Promise => { const messagesBeforeInput = isLastMessageIncludedInInput( input, messages.at(-1), @@ -49,47 +47,23 @@ export const sendChatReplyToWhatsApp = async ({ isNotDefined(action.lastBubbleBlockId), ) ?? []; - for (const action of clientSideActionsBeforeMessages) { - const result = await executeClientSideAction({ to, credentials })(action); - if (!result) continue; - const { input, newSessionState, messages, clientSideActions } = - await continueBotFlow( - result.replyToSend - ? { type: "text", text: result.replyToSend } - : undefined, - { - version: 2, - state, - textBubbleContentFormat: "richText", - }, - ); + const result = await executeClientSideActions({ + clientSideActions: clientSideActionsBeforeMessages, + to, + credentials, + }); - return sendChatReplyToWhatsApp({ - to, - messages, - input, - isFirstChatChunk: false, - typingEmulation: newSessionState.typingEmulation, - clientSideActions, - credentials, - state: newSessionState, - }); - } + if (result) return result; let i = -1; for (const message of messagesBeforeInput) { i += 1; - if ( - i > 0 && - (typingEmulation?.delayBetweenBubbles ?? - defaultSettings.typingEmulation.delayBetweenBubbles) > 0 - ) { + const delayBetweenBubbles = + state.typingEmulation?.delayBetweenBubbles ?? + defaultSettings.typingEmulation.delayBetweenBubbles; + if (i > 0 && delayBetweenBubbles > 0) { await new Promise((resolve) => - setTimeout( - resolve, - (typingEmulation?.delayBetweenBubbles ?? - defaultSettings.typingEmulation.delayBetweenBubbles) * 1000, - ), + setTimeout(resolve, delayBetweenBubbles * 1000), ); } const whatsAppMessage = convertMessageToWhatsAppMessage(message); @@ -98,16 +72,17 @@ export const sendChatReplyToWhatsApp = async ({ sentMessages.at(-1)?.type ?? "", ); + const isTypingEmulationDisabled = + state.typingEmulation?.isDisabledOnFirstMessage ?? + defaultSettings.typingEmulation.isDisabledOnFirstMessage; + const typingDuration = lastSentMessageIsMedia ? messageAfterMediaTimeout - : isFirstChatChunk && - i === 0 && - (typingEmulation?.isDisabledOnFirstMessage ?? - defaultSettings.typingEmulation.isDisabledOnFirstMessage) + : isFirstChatChunk && i === 0 && isTypingEmulationDisabled ? 0 : getTypingDuration({ message: whatsAppMessage, - typingEmulation, + typingEmulation: state.typingEmulation, }); if ((typingDuration ?? 0) > 0) await new Promise((resolve) => setTimeout(resolve, typingDuration)); @@ -122,34 +97,6 @@ export const sendChatReplyToWhatsApp = async ({ clientSideActions?.filter( (action) => action.lastBubbleBlockId === message.id, ) ?? []; - for (const action of clientSideActionsAfterMessage) { - const result = await executeClientSideAction({ to, credentials })( - action, - ); - if (!result) continue; - const { input, newSessionState, messages, clientSideActions } = - await continueBotFlow( - result.replyToSend - ? { type: "text", text: result.replyToSend } - : undefined, - { - version: 2, - state, - textBubbleContentFormat: "richText", - }, - ); - - return sendChatReplyToWhatsApp({ - to, - messages, - input, - isFirstChatChunk: false, - typingEmulation: newSessionState.typingEmulation, - clientSideActions, - credentials, - state: newSessionState, - }); - } } catch (err) { Sentry.captureException(err, { extra: { message } }); console.log("Failed to send message:", JSON.stringify(message, null, 2)); @@ -160,6 +107,16 @@ export const sendChatReplyToWhatsApp = async ({ await err.response.text(), ); } + const clientSideActionsAfterMessage = + clientSideActions?.filter( + (action) => action.lastBubbleBlockId === message.id, + ) ?? []; + const result = await executeClientSideActions({ + clientSideActions: clientSideActionsAfterMessage, + to, + credentials, + }); + if (result) return result; } if (input) { @@ -176,7 +133,7 @@ export const sendChatReplyToWhatsApp = async ({ ? messageAfterMediaTimeout : getTypingDuration({ message, - typingEmulation, + typingEmulation: state.typingEmulation, }); if (typingDuration) await new Promise((resolve) => setTimeout(resolve, typingDuration)); @@ -240,13 +197,32 @@ const isLastMessageIncludedInInput = ( ); }; +const executeClientSideActions = async ({ + to, + credentials, + clientSideActions, +}: { + clientSideActions: ClientSideAction[]; + to: string; + credentials: WhatsAppCredentials["data"]; +}) => { + for (const action of clientSideActions) { + const result = await executeClientSideAction({ to, credentials })(action); + if (result) return result; + } +}; + +type ClientSideActionExecutionResult = + | { type: "replyToSend"; replyToSend: string | undefined } + | { type: "shouldWaitForWebhook" } + | undefined; const executeClientSideAction = (context: { to: string; credentials: WhatsAppCredentials["data"] }) => async ( clientSideAction: NonNullable< ContinueChatResponse["clientSideActions"] >[number], - ): Promise<{ replyToSend: string | undefined } | void> => { + ): Promise => { if ("wait" in clientSideAction) { await new Promise((resolve) => setTimeout( @@ -256,6 +232,7 @@ const executeClientSideAction = ); if (!clientSideAction.expectsDedicatedReply) return; return { + type: "replyToSend", replyToSend: undefined, }; } @@ -287,4 +264,8 @@ const executeClientSideAction = ); } } + if (clientSideAction.type === "listenForWebhook") + return { + type: "shouldWaitForWebhook", + }; }; diff --git a/yarn.lock b/yarn.lock index e7d5c89cf7..042876f0ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,6 +1,6 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 -# bun ./bun.lockb --hash: 85B5D8D351D5F01B-f8838cd62e30a7d5-CD59DB2A8A329FA4-6b5105405502e5bd +# bun ./bun.lockb --hash: DA56811BB46E16FF-a5770c5bfe19493c-05D940B437DC2C13-a0a97b695c31817a "@ai-sdk/anthropic@0.0.30": @@ -2169,6 +2169,36 @@ sisteransi "^1.0.5" is-unicode-supported "*" +"@cloudflare/workerd-darwin-64@1.20240718.0": + version "1.20240718.0" + resolved "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240718.0.tgz" + integrity sha512-BsPZcSCgoGnufog2GIgdPuiKicYTNyO/Dp++HbpLRH+yQdX3x4aWx83M+a0suTl1xv76dO4g9aw7SIB6OSgIyQ== + +"@cloudflare/workerd-darwin-arm64@1.20240718.0": + version "1.20240718.0" + resolved "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240718.0.tgz" + integrity sha512-nlr4gaOO5gcJerILJQph3+2rnas/nx/lYsuaot1ntHu4LAPBoQo1q/Pucj2cSIav4UiMzTbDmoDwPlls4Kteog== + +"@cloudflare/workerd-linux-64@1.20240718.0": + version "1.20240718.0" + resolved "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240718.0.tgz" + integrity sha512-LJ/k3y47pBcjax0ee4K+6ZRrSsqWlfU4lbU8Dn6u5tSC9yzwI4YFNXDrKWInB0vd7RT3w4Yqq1S6ZEbfRrqVUg== + +"@cloudflare/workerd-linux-arm64@1.20240718.0": + version "1.20240718.0" + resolved "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240718.0.tgz" + integrity sha512-zBEZvy88EcAMGRGfuVtS00Yl7lJdUM9sH7i651OoL+q0Plv9kphlCC0REQPwzxrEYT1qibSYtWcD9IxQGgx2/g== + +"@cloudflare/workerd-windows-64@1.20240718.0": + version "1.20240718.0" + resolved "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240718.0.tgz" + integrity sha512-YpCRvvT47XanFum7C3SedOZKK6BfVhqmwdAAVAQFyc4gsCdegZo0JkUkdloC/jwuWlbCACOG2HTADHOqyeolzQ== + +"@cloudflare/workers-types@4.20240718.0": + version "4.20240718.0" + resolved "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20240718.0.tgz" + integrity sha512-7RqxXIM9HyhjfZ9ztXjITuc7mL0w4s+zXgypqKmMuvuObC3DgXutJ3bOYbQ+Ss5QbywrzWSNMlmGdL/ldg/yZg== + "@codemirror/autocomplete@^6.0.0", "@codemirror/autocomplete@^6.3.2", "@codemirror/autocomplete@^6.7.1": version "6.18.1" resolved "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.1.tgz" @@ -2511,6 +2541,13 @@ style-mod "^4.1.0" w3c-keyname "^2.2.4" +"@cspotcode/source-map-support@0.8.1": + version "0.8.1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + "@dnd-kit/accessibility@^3.0.0": version "3.1.0" resolved "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.0.tgz" @@ -2746,6 +2783,11 @@ resolved "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz" integrity sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g== +"@esbuild/aix-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz" + integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + "@esbuild/aix-ppc64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz" @@ -2761,6 +2803,11 @@ resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.11.tgz" integrity sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw== +"@esbuild/android-arm@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz" + integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + "@esbuild/android-arm@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz" @@ -2776,6 +2823,11 @@ resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz" integrity sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q== +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + "@esbuild/android-arm64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz" @@ -2791,6 +2843,11 @@ resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.11.tgz" integrity sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg== +"@esbuild/android-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz" + integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + "@esbuild/android-x64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz" @@ -2806,6 +2863,11 @@ resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz" integrity sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ== +"@esbuild/darwin-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + "@esbuild/darwin-arm64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz" @@ -2821,6 +2883,11 @@ resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz" integrity sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g== +"@esbuild/darwin-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + "@esbuild/darwin-x64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz" @@ -2836,6 +2903,11 @@ resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz" integrity sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA== +"@esbuild/freebsd-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz" + integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + "@esbuild/freebsd-arm64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz" @@ -2851,6 +2923,11 @@ resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz" integrity sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw== +"@esbuild/freebsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz" + integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + "@esbuild/freebsd-x64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz" @@ -2866,6 +2943,11 @@ resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz" integrity sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q== +"@esbuild/linux-arm@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz" + integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + "@esbuild/linux-arm@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz" @@ -2881,6 +2963,11 @@ resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz" integrity sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg== +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + "@esbuild/linux-arm64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz" @@ -2896,6 +2983,11 @@ resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz" integrity sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA== +"@esbuild/linux-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz" + integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + "@esbuild/linux-ia32@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz" @@ -2911,6 +3003,11 @@ resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz" integrity sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg== +"@esbuild/linux-loong64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz" + integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + "@esbuild/linux-loong64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz" @@ -2926,6 +3023,11 @@ resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz" integrity sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg== +"@esbuild/linux-mips64el@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz" + integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + "@esbuild/linux-mips64el@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz" @@ -2941,6 +3043,11 @@ resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz" integrity sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA== +"@esbuild/linux-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz" + integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + "@esbuild/linux-ppc64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz" @@ -2956,6 +3063,11 @@ resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz" integrity sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ== +"@esbuild/linux-riscv64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz" + integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + "@esbuild/linux-riscv64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz" @@ -2971,6 +3083,11 @@ resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz" integrity sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q== +"@esbuild/linux-s390x@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz" + integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + "@esbuild/linux-s390x@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz" @@ -2986,6 +3103,11 @@ resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz" integrity sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA== +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + "@esbuild/linux-x64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz" @@ -3001,6 +3123,11 @@ resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz" integrity sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ== +"@esbuild/netbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz" + integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + "@esbuild/netbsd-x64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz" @@ -3021,6 +3148,11 @@ resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz" integrity sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw== +"@esbuild/openbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz" + integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + "@esbuild/openbsd-x64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz" @@ -3036,6 +3168,11 @@ resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz" integrity sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ== +"@esbuild/sunos-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz" + integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + "@esbuild/sunos-x64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz" @@ -3051,6 +3188,11 @@ resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz" integrity sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ== +"@esbuild/win32-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz" + integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + "@esbuild/win32-arm64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz" @@ -3066,6 +3208,11 @@ resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz" integrity sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg== +"@esbuild/win32-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz" + integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + "@esbuild/win32-ia32@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz" @@ -3081,6 +3228,11 @@ resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz" integrity sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw== +"@esbuild/win32-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== + "@esbuild/win32-x64@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz" @@ -3093,6 +3245,11 @@ dependencies: lodash.kebabcase "^4.1.1" +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + "@fix-webm-duration/fix@1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@fix-webm-duration/fix/-/fix-1.0.1.tgz" @@ -3572,7 +3729,7 @@ "@jridgewell/trace-mapping" "^0.3.24" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/resolve-uri@^3.1.0": +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== @@ -3595,6 +3752,14 @@ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" @@ -5425,6 +5590,7 @@ dompurify "3.0.6" ky "1.2.4" marked "9.0.3" + partysocket "1.0.2" solid-element "1.7.1" solid-js "1.7.8" @@ -5505,6 +5671,13 @@ ky "1.2.4" openai "4.52.7" +"@typebot.io/partykit@packages/partykit": + version "workspace:packages/partykit" + resolved "workspace:packages/partykit" + devDependencies: + "@typebot.io/tsconfig" "packages/tsconfig" + partykit "0.0.110" + "@typebot.io/playwright@packages/playwright": version "workspace:packages/playwright" resolved "workspace:packages/playwright" @@ -7364,7 +7537,7 @@ accepts@~1.3.4, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.0.0, acorn@^8.1.0, acorn@^8.10.0, acorn@^8.11.0, acorn@^8.11.2, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.0.0, acorn@^8.1.0, acorn@^8.10.0, acorn@^8.11.0, acorn@^8.11.2, acorn@^8.8.0, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: version "8.12.1" resolved "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== @@ -7382,7 +7555,7 @@ acorn-jsx@^5.0.0, acorn-jsx@^5.3.2: resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^8.0.2: +acorn-walk@^8.0.2, acorn-walk@^8.2.0: version "8.3.4" resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz" integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== @@ -7589,6 +7762,13 @@ arrify@^2.0.0: resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== +as-table@^1.0.36: + version "1.0.55" + resolved "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz" + integrity sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ== + dependencies: + printable-characters "^1.0.42" + ast-types@^0.13.4: version "0.13.4" resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz" @@ -8208,6 +8388,7 @@ buffer-from@^1.0.0: nuqs "^1.19.3" openai "4.52.7" papaparse "5.4.1" + partysocket "1.0.2" pexels "^1.4.0" prettier "2.8.8" qs "6.11.2" @@ -8361,6 +8542,14 @@ canvas-confetti@1.6.0: resolved "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.6.0.tgz" integrity sha512-ej+w/m8Jzpv9Z7W7uJZer14Ke8P2ogsjg4ZMGIuq4iqUOqY2Jq8BNW42iGmNfRwREaaEfFIczLuZZiEVSYNHAA== +capnp-ts@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/capnp-ts/-/capnp-ts-0.7.0.tgz" + integrity sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g== + dependencies: + debug "^4.3.1" + tslib "^2.2.0" + ccount@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz" @@ -8557,6 +8746,15 @@ client-only@0.0.1, client-only@^0.0.1: resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== +clipboardy@4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/clipboardy/-/clipboardy-4.0.0.tgz" + integrity sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w== + dependencies: + execa "^8.0.1" + is-wsl "^3.1.0" + is64bit "^2.0.0" + cliui@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" @@ -9121,6 +9319,11 @@ csstype@3.1.3, csstype@^3.0.2, csstype@^3.1.0, csstype@^3.1.2, csstype@^3.1.3: resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +data-uri-to-buffer@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz" + integrity sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA== + data-uri-to-buffer@^6.0.2: version "6.0.2" resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz" @@ -9752,6 +9955,35 @@ esbuild@0.19.11: "@esbuild/linux-riscv64" "0.19.11" "@esbuild/linux-mips64el" "0.19.11" +esbuild@0.21.5: + version "0.21.5" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz" + integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.21.5" + "@esbuild/linux-arm" "0.21.5" + "@esbuild/linux-x64" "0.21.5" + "@esbuild/sunos-x64" "0.21.5" + "@esbuild/win32-x64" "0.21.5" + "@esbuild/darwin-x64" "0.21.5" + "@esbuild/linux-ia32" "0.21.5" + "@esbuild/netbsd-x64" "0.21.5" + "@esbuild/win32-ia32" "0.21.5" + "@esbuild/android-arm" "0.21.5" + "@esbuild/android-x64" "0.21.5" + "@esbuild/freebsd-x64" "0.21.5" + "@esbuild/linux-arm64" "0.21.5" + "@esbuild/linux-ppc64" "0.21.5" + "@esbuild/linux-s390x" "0.21.5" + "@esbuild/openbsd-x64" "0.21.5" + "@esbuild/win32-arm64" "0.21.5" + "@esbuild/darwin-arm64" "0.21.5" + "@esbuild/android-arm64" "0.21.5" + "@esbuild/freebsd-arm64" "0.21.5" + "@esbuild/linux-loong64" "0.21.5" + "@esbuild/linux-riscv64" "0.21.5" + "@esbuild/linux-mips64el" "0.21.5" + esbuild@>=0.18, esbuild@^0.23.0: version "0.23.1" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz" @@ -9930,6 +10162,11 @@ event-target-shim@^5.0.0: resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== +event-target-shim@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-6.0.2.tgz" + integrity sha512-8q3LsZjRezbFZ2PN+uP+Q7pnHUMmAOziU2vA2OwoFaKIXxlxl38IylhSSgUorWu/rf4er67w0ikBqjBFk/pomA== + eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" @@ -9966,11 +10203,31 @@ execa@^5.0.0, execa@^5.1.1: human-signals "^2.1.0" strip-final-newline "^2.0.0" +execa@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + onetime "^6.0.0" + is-stream "^3.0.0" + get-stream "^8.0.1" + cross-spawn "^7.0.3" + signal-exit "^4.1.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + human-signals "^5.0.0" + strip-final-newline "^3.0.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== +exit-hook@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz" + integrity sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw== + expand-template@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz" @@ -10329,7 +10586,7 @@ fsevents@2.3.2: resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3: +fsevents@2.3.3, fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -10405,6 +10662,14 @@ get-port@^6.1.2: resolved "https://registry.npmjs.org/get-port/-/get-port-6.1.2.tgz" integrity sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw== +get-source@^2.0.12: + version "2.0.12" + resolved "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz" + integrity sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w== + dependencies: + data-uri-to-buffer "^2.0.0" + source-map "^0.6.1" + get-stream@^5.1.0: version "5.2.0" resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" @@ -10417,6 +10682,11 @@ get-stream@^6.0.0, get-stream@^6.0.1: resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + get-tsconfig@^4.7.5: version "4.8.1" resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz" @@ -10499,6 +10769,11 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + globals@^11.1.0: version "11.12.0" resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" @@ -11134,6 +11409,11 @@ human-signals@^2.1.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz" @@ -11414,6 +11694,11 @@ is-docker@^2.0.0, is-docker@^2.1.1: resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + is-extendable@^0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" @@ -11458,6 +11743,13 @@ is-hotkey@^0.2.0: resolved "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.2.0.tgz" integrity sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw== +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz" @@ -11529,6 +11821,11 @@ is-stream@^2.0.0: resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + is-typed-array@^1.1.3: version "1.1.13" resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz" @@ -11563,6 +11860,20 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + +is64bit@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is64bit/-/is64bit-2.0.0.tgz" + integrity sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw== + dependencies: + system-architecture "^0.1.0" + isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" @@ -13618,6 +13929,11 @@ mimic-fn@^2.1.0: resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + mimic-response@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz" @@ -13628,6 +13944,24 @@ mimic-response@^4.0.0: resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz" integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== +miniflare@3.20240718.0: + version "3.20240718.0" + resolved "https://registry.npmjs.org/miniflare/-/miniflare-3.20240718.0.tgz" + integrity sha512-TKgSeyqPBeT8TBLxbDJOKPWlq/wydoJRHjAyDdgxbw59N6wbP8JucK6AU1vXCfu21eKhrEin77ssXOpbfekzPA== + dependencies: + ws "^8.17.1" + zod "^3.22.3" + acorn "^8.8.0" + youch "^3.2.2" + undici "^5.28.4" + workerd "1.20240718.0" + capnp-ts "^0.7.0" + exit-hook "^2.2.1" + stoppable "^1.1.0" + acorn-walk "^8.2.0" + glob-to-regexp "^0.4.1" + "@cspotcode/source-map-support" "0.8.1" + minimatch@^3.0.4, minimatch@^3.1.1: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" @@ -14083,6 +14417,11 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +mustache@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz" + integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== + mz@^2.7.0: version "2.7.0" resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz" @@ -14330,6 +14669,13 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npm-run-path@^5.1.0: + version "5.3.0" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz" + integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== + dependencies: + path-key "^4.0.0" + nprogress@0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz" @@ -14405,6 +14751,13 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + open@^8.4.0: version "8.4.2" resolved "https://registry.npmjs.org/open/-/open-8.4.2.tgz" @@ -14686,6 +15039,26 @@ parseurl@~1.3.3: resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +partykit@0.0.110: + version "0.0.110" + resolved "https://registry.npmjs.org/partykit/-/partykit-0.0.110.tgz" + integrity sha512-lAUeB/b6nZbMr6c789RKlSsGhbMtklNhjJZ0ri4zzaVEqhG2/k+cqTLCYTwHvfSI6eSa7cIF614R7CyMUrSLgA== + dependencies: + esbuild "0.21.5" + miniflare "3.20240718.0" + clipboardy "4.0.0" + yoga-wasm-web "0.3.3" + "@cloudflare/workers-types" "4.20240718.0" + optionalDependencies: + fsevents "2.3.3" + +partysocket@1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/partysocket/-/partysocket-1.0.2.tgz" + integrity sha512-rAFOUKImaq+VBk2B+2RTBsWEvlnarEP53nchoUHzpVs8V6fG2/estihOTslTQUWHVuHEKDL5k8htG8K3TngyFA== + dependencies: + event-target-shim "^6.0.2" + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" @@ -14706,6 +15079,11 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" @@ -15229,6 +15607,11 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: ansi-styles "^5.0.0" "@jest/schemas" "^29.6.3" +printable-characters@^1.0.42: + version "1.0.42" + resolved "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz" + integrity sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ== + prism-react-renderer@^1.3.5: version "1.3.5" resolved "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz" @@ -16420,7 +16803,7 @@ signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -signal-exit@^4.0.1: +signal-exit@^4.0.1, signal-exit@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== @@ -16719,6 +17102,14 @@ stacktrace-parser@^0.1.10: dependencies: type-fest "^0.7.1" +stacktracey@^2.1.8: + version "2.1.8" + resolved "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz" + integrity sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw== + dependencies: + as-table "^1.0.36" + get-source "^2.0.12" + standard-as-callback@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz" @@ -16741,6 +17132,11 @@ stdin-discarder@^0.1.0: dependencies: bl "^5.0.0" +stoppable@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz" + integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw== + "stories@packages/embeds/stories": version "workspace:packages/embeds/stories" resolved "workspace:packages/embeds/stories" @@ -16865,6 +17261,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" @@ -17058,6 +17459,11 @@ symbol-tree@^3.2.4: resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +system-architecture@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz" + integrity sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA== + tabbable@^6.0.1, tabbable@^6.2.0: version "6.2.0" resolved "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz" @@ -17377,7 +17783,7 @@ tslib@2.4.0: resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== -tslib@*, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.4.1: +tslib@*, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.4.1: version "2.7.0" resolved "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz" integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== @@ -17526,6 +17932,13 @@ unbzip2-stream@^1.4.3: buffer "^5.2.1" through "^2.3.8" +undici@^5.28.4: + version "5.28.4" + resolved "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz" + integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== + dependencies: + "@fastify/busboy" "^2.0.0" + undici-types@~5.26.4: version "5.26.5" resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" @@ -18068,6 +18481,7 @@ vfile-message@^4.0.0: nodemailer "6.9.15" openai "4.52.7" papaparse "5.4.1" + partysocket "1.0.2" react "18.2.0" react-dom "18.2.0" stripe "17.1.0" @@ -18275,6 +18689,17 @@ widest-line@^4.0.1: dependencies: string-width "^5.0.1" +workerd@1.20240718.0: + version "1.20240718.0" + resolved "https://registry.npmjs.org/workerd/-/workerd-1.20240718.0.tgz" + integrity sha512-w7lOLRy0XecQTg/ujTLWBiJJuoQvzB3CdQ6/8Wgex3QxFhV9Pbnh3UbwIuUfMw3OCCPQc4o7y+1P+mISAgp6yg== + optionalDependencies: + "@cloudflare/workerd-linux-64" "1.20240718.0" + "@cloudflare/workerd-darwin-64" "1.20240718.0" + "@cloudflare/workerd-windows-64" "1.20240718.0" + "@cloudflare/workerd-linux-arm64" "1.20240718.0" + "@cloudflare/workerd-darwin-arm64" "1.20240718.0" + wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" @@ -18320,7 +18745,7 @@ ws@~8.17.1: resolved "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== -ws@^8.11.0, ws@^8.18.0: +ws@^8.11.0, ws@^8.17.1, ws@^8.18.0: version "8.18.0" resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== @@ -18468,7 +18893,21 @@ yocto-queue@^0.1.0: resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zod@3.23.8, zod@^3.0.0, zod@^3.18.0, zod@^3.20.6, zod@^3.21.4, zod@^3.23.3: +yoga-wasm-web@0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/yoga-wasm-web/-/yoga-wasm-web-0.3.3.tgz" + integrity sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA== + +youch@^3.2.2: + version "3.3.3" + resolved "https://registry.npmjs.org/youch/-/youch-3.3.3.tgz" + integrity sha512-qSFXUk3UZBLfggAW3dJKg0BMblG5biqSF8M34E06o5CSsZtH92u9Hqmj2RzGiHDi64fhe83+4tENFP2DB6t6ZA== + dependencies: + cookie "^0.5.0" + mustache "^4.2.0" + stacktracey "^2.1.8" + +zod@3.23.8, zod@^3.0.0, zod@^3.18.0, zod@^3.20.6, zod@^3.21.4, zod@^3.22.3, zod@^3.23.3: version "3.23.8" resolved "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz" integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==