diff --git a/packages/embeds/js/package.json b/packages/embeds/js/package.json index 210bd8a64..2516920c6 100644 --- a/packages/embeds/js/package.json +++ b/packages/embeds/js/package.json @@ -1,6 +1,6 @@ { "name": "@typebot.io/js", - "version": "0.2.28", + "version": "0.2.29", "description": "Javascript library to display typebots on your website", "type": "module", "main": "dist/index.js", @@ -15,6 +15,7 @@ "@stripe/stripe-js": "1.54.1", "@udecode/plate-common": "21.1.5", "dompurify": "3.0.6", + "ky": "^1.1.3", "marked": "9.0.3", "solid-element": "1.7.1", "solid-js": "1.7.8" diff --git a/packages/embeds/js/src/components/Bot.tsx b/packages/embeds/js/src/components/Bot.tsx index 6e1d473b1..e2f09792a 100644 --- a/packages/embeds/js/src/components/Bot.tsx +++ b/packages/embeds/js/src/components/Bot.tsx @@ -16,6 +16,7 @@ import { InputBlock } from '@typebot.io/schemas' import { StartFrom } from '@typebot.io/schemas' import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants' import { clsx } from 'clsx' +import { HTTPError } from 'ky' export type BotProps = { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -52,7 +53,7 @@ export const Bot = (props: BotProps & { class?: string }) => { typeof props.typebot === 'string' ? props.typebot : undefined const isPreview = typeof props.typebot !== 'string' || (props.isPreview ?? false) - const { data, error, response } = await startChatQuery({ + const { data, error } = await startChatQuery({ stripeRedirectStatus: urlParams.get('redirect_status') ?? undefined, typebot: props.typebot, apiHost: props.apiHost, @@ -66,24 +67,42 @@ export const Bot = (props: BotProps & { class?: string }) => { }, startFrom: props.startFrom, }) - if (error && 'code' in error && typeof error.code === 'string') { + if (error instanceof HTTPError) { if (isPreview) { return setError( - new Error('An error occurred while loading the bot.', { - cause: error.message, + new Error(`An error occurred while loading the bot.`, { + cause: { + status: error.response.status, + body: await error.response.json(), + }, }) ) } - if (['BAD_REQUEST', 'FORBIDDEN'].includes(error.code)) + if (error.response.status === 400 || error.response.status === 403) return setError(new Error('This bot is now closed.')) - if (error.code === 'NOT_FOUND') + if (error.response.status === 404) return setError(new Error("The bot you're looking for doesn't exist.")) + return setError( + new Error( + `Error! Couldn't initiate the chat. (${error.response.statusText})` + ) + ) } if (!data) { - if (error) console.error(error) - console.error({ data, error, response }) - return setError(new Error("Error! Couldn't initiate the chat.")) + if (error) { + console.error(error) + if (isPreview) { + return setError( + new Error(`Error! Could not reach server. Check your connection.`, { + cause: error, + }) + ) + } + } + return setError( + new Error('Error! Could not reach server. Check your connection.') + ) } if (data.resultId && typebotIdFromProps) diff --git a/packages/embeds/js/src/components/ConversationContainer/ConversationContainer.tsx b/packages/embeds/js/src/components/ConversationContainer/ConversationContainer.tsx index 7934fbd4e..1a89c79d7 100644 --- a/packages/embeds/js/src/components/ConversationContainer/ConversationContainer.tsx +++ b/packages/embeds/js/src/components/ConversationContainer/ConversationContainer.tsx @@ -31,6 +31,7 @@ import { } from '@/utils/formattedMessagesSignal' import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants' import { saveClientLogsQuery } from '@/queries/saveClientLogsQuery' +import { HTTPError } from 'ky' const parseDynamicTheme = ( initialTheme: Theme, @@ -173,7 +174,13 @@ export const ConversationContainer = (props: Props) => { const errorLogs = [ { description: 'Failed to send the reply', - details: error, + details: + error instanceof HTTPError + ? { + status: error.response.status, + body: await error.response.json(), + } + : error, status: 'error', }, ] diff --git a/packages/embeds/js/src/components/ErrorMessage.tsx b/packages/embeds/js/src/components/ErrorMessage.tsx index 6399bbc54..2c834c90b 100644 --- a/packages/embeds/js/src/components/ErrorMessage.tsx +++ b/packages/embeds/js/src/components/ErrorMessage.tsx @@ -5,7 +5,7 @@ export const ErrorMessage = (props: Props) => { return (

{props.error.message}

-

{props.error.cause as string}

+
{JSON.stringify(props.error.cause, null, 2) as string}
) } diff --git a/packages/embeds/js/src/queries/continueChatQuery.ts b/packages/embeds/js/src/queries/continueChatQuery.ts index 04264b78c..9beff6f23 100644 --- a/packages/embeds/js/src/queries/continueChatQuery.ts +++ b/packages/embeds/js/src/queries/continueChatQuery.ts @@ -1,8 +1,9 @@ import { guessApiHost } from '@/utils/guessApiHost' -import { isNotEmpty, sendRequest } from '@typebot.io/lib' +import { isNotEmpty } from '@typebot.io/lib' import { ContinueChatResponse } from '@typebot.io/schemas' +import ky from 'ky' -export const continueChatQuery = ({ +export const continueChatQuery = async ({ apiHost, message, sessionId, @@ -10,13 +11,24 @@ export const continueChatQuery = ({ apiHost?: string message: string | undefined sessionId: string -}) => - sendRequest({ - method: 'POST', - url: `${ - isNotEmpty(apiHost) ? apiHost : guessApiHost() - }/api/v1/sessions/${sessionId}/continueChat`, - body: { - message, - }, - }) +}) => { + try { + const data = await ky + .post( + `${ + isNotEmpty(apiHost) ? apiHost : guessApiHost() + }/api/v1/sessions/${sessionId}/continueChat`, + { + json: { + message, + }, + timeout: false, + } + ) + .json() + + return { data } + } catch (error) { + return { error } + } +} diff --git a/packages/embeds/js/src/queries/getOpenAiStreamerQuery.ts b/packages/embeds/js/src/queries/getOpenAiStreamerQuery.ts deleted file mode 100644 index 21012ac76..000000000 --- a/packages/embeds/js/src/queries/getOpenAiStreamerQuery.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { guessApiHost } from '@/utils/guessApiHost' -import { isNotEmpty } from '@typebot.io/lib' - -export const getOpenAiStreamerQuery = - ({ apiHost, sessionId }: { apiHost?: string; sessionId: string }) => - async ( - messages: { - content?: string | undefined - role?: 'system' | 'user' | 'assistant' | undefined - }[] - ) => { - const response = await fetch( - `${ - isNotEmpty(apiHost) ? apiHost : guessApiHost() - }/api/integrations/openai/streamer`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - sessionId, - messages, - }), - } - ) - if (!response.ok) { - throw new Error(response.statusText) - } - - const data = response.body - return data - } diff --git a/packages/embeds/js/src/queries/saveClientLogsQuery.ts b/packages/embeds/js/src/queries/saveClientLogsQuery.ts index 47627da99..a0f3ac3dd 100644 --- a/packages/embeds/js/src/queries/saveClientLogsQuery.ts +++ b/packages/embeds/js/src/queries/saveClientLogsQuery.ts @@ -1,8 +1,9 @@ import { guessApiHost } from '@/utils/guessApiHost' import type { ChatLog } from '@typebot.io/schemas' -import { isNotEmpty, sendRequest } from '@typebot.io/lib' +import { isNotEmpty } from '@typebot.io/lib' +import ky from 'ky' -export const saveClientLogsQuery = ({ +export const saveClientLogsQuery = async ({ apiHost, sessionId, clientLogs, @@ -10,13 +11,19 @@ export const saveClientLogsQuery = ({ apiHost?: string sessionId: string clientLogs: ChatLog[] -}) => - sendRequest({ - method: 'POST', - url: `${ - isNotEmpty(apiHost) ? apiHost : guessApiHost() - }/api/v1/sessions/${sessionId}/clientLogs`, - body: { - clientLogs, - }, - }) +}) => { + try { + await ky.post( + `${ + isNotEmpty(apiHost) ? apiHost : guessApiHost() + }/api/v1/sessions/${sessionId}/clientLogs`, + { + json: { + clientLogs, + }, + } + ) + } catch (e) { + console.log(e) + } +} diff --git a/packages/embeds/js/src/queries/startChatQuery.ts b/packages/embeds/js/src/queries/startChatQuery.ts index e682b79bc..5fc6947bc 100644 --- a/packages/embeds/js/src/queries/startChatQuery.ts +++ b/packages/embeds/js/src/queries/startChatQuery.ts @@ -1,6 +1,6 @@ import { BotContext, InitialChatReply } from '@/types' import { guessApiHost } from '@/utils/guessApiHost' -import { isNotDefined, isNotEmpty, sendRequest } from '@typebot.io/lib' +import { isNotDefined, isNotEmpty } from '@typebot.io/lib' import { getPaymentInProgressInStorage, removePaymentInProgressFromStorage, @@ -10,6 +10,18 @@ import { StartFrom, StartPreviewChatInput, } from '@typebot.io/schemas' +import ky from 'ky' + +type Props = { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + typebot: string | any + stripeRedirectStatus?: string + apiHost?: string + startFrom?: StartFrom + isPreview: boolean + prefilledVariables?: Record + resultId?: string +} export async function startChatQuery({ typebot, @@ -19,16 +31,7 @@ export async function startChatQuery({ resultId, stripeRedirectStatus, startFrom, -}: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - typebot: string | any - stripeRedirectStatus?: string - apiHost?: string - startFrom?: StartFrom - isPreview: boolean - prefilledVariables?: Record - resultId?: string -}) { +}: Props) { if (isNotDefined(typebot)) throw new Error('Typebot ID is required to get initial messages') @@ -41,67 +44,75 @@ export async function startChatQuery({ : undefined if (paymentInProgressState) { removePaymentInProgressFromStorage() - const { data, error, response } = await sendRequest({ - method: 'POST', - url: `${isNotEmpty(apiHost) ? apiHost : guessApiHost()}/api/v1/sessions/${ - paymentInProgressState.sessionId - }/continueChat`, - body: { - message: paymentInProgressState - ? stripeRedirectStatus === 'failed' - ? 'fail' - : 'Success' - : undefined, - }, - }) - return { - data: data - ? { - ...data, - ...(paymentInProgressState - ? { typebot: paymentInProgressState.typebot } - : {}), + + 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, } - : undefined, - error, - response, + ) + .json() + + return { data } + } catch (error) { + return { error } } } const typebotId = typeof typebot === 'string' ? typebot : typebot.id if (isPreview) { - const { data, error, response } = await sendRequest({ - method: 'POST', - url: `${ - isNotEmpty(apiHost) ? apiHost : guessApiHost() - }/api/v1/typebots/${typebotId}/preview/startChat`, - body: { - isStreamEnabled: true, - startFrom, - typebot, - } satisfies Omit, - }) - return { - data, - error, - response, + try { + const data = await ky + .post( + `${ + isNotEmpty(apiHost) ? apiHost : guessApiHost() + }/api/v1/typebots/${typebotId}/preview/startChat`, + { + json: { + isStreamEnabled: true, + startFrom, + typebot, + } satisfies Omit, + timeout: false, + } + ) + .json() + + return { data } + } catch (error) { + return { error } } } - const { data, error, response } = await sendRequest({ - method: 'POST', - url: `${ - isNotEmpty(apiHost) ? apiHost : guessApiHost() - }/api/v1/typebots/${typebotId}/startChat`, - body: { - isStreamEnabled: true, - prefilledVariables, - resultId, - } satisfies Omit, - }) + try { + const data = await ky + .post( + `${ + isNotEmpty(apiHost) ? apiHost : guessApiHost() + }/api/v1/typebots/${typebotId}/startChat`, + { + json: { + isStreamEnabled: true, + prefilledVariables, + resultId, + } satisfies Omit, + timeout: false, + } + ) + .json() - return { - data, - error, - response, + return { data } + } catch (error) { + return { error } } } diff --git a/packages/embeds/nextjs/package.json b/packages/embeds/nextjs/package.json index 187711d4e..a778dce5f 100644 --- a/packages/embeds/nextjs/package.json +++ b/packages/embeds/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@typebot.io/nextjs", - "version": "0.2.28", + "version": "0.2.29", "description": "Convenient library to display typebots on your Next.js website", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/embeds/react/package.json b/packages/embeds/react/package.json index 42da16137..255d382fd 100644 --- a/packages/embeds/react/package.json +++ b/packages/embeds/react/package.json @@ -1,6 +1,6 @@ { "name": "@typebot.io/react", - "version": "0.2.28", + "version": "0.2.29", "description": "Convenient library to display typebots on your React app", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6b285ac8d..d69c71e0e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -956,6 +956,9 @@ importers: dompurify: specifier: 3.0.6 version: 3.0.6 + ky: + specifier: ^1.1.3 + version: 1.1.3 marked: specifier: 9.0.3 version: 9.0.3 @@ -17743,6 +17746,11 @@ packages: resolution: {integrity: sha512-RTSoaUAfLvpR357vWzAz/50Q/BmHfmE6ETSWfutT0AJiw10e6CmcdYRQJlLRd95B53D0Y2aD1jSxD3V3ySF+PA==} dev: true + /ky@1.1.3: + resolution: {integrity: sha512-t7q8sJfazzHbfYxiCtuLIH4P+pWoCgunDll17O/GBZBqMt2vHjGSx5HzSxhOc2BDEg3YN/EmeA7VKrHnwuWDag==} + engines: {node: '>=18'} + dev: false + /language-subtag-registry@0.3.22: resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} dev: false