2
0

♻️ (viewer) Remove barrel exports and flatten folder arch

This commit is contained in:
Baptiste Arnaud
2023-03-15 12:21:52 +01:00
parent 44d7a0bcb8
commit f3af07b7ff
113 changed files with 398 additions and 426 deletions

View File

@ -13,7 +13,7 @@
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"update-search": "docker run -it --rm --env-file=.env -e \"CONFIG=$(cat docsearch-scrapper-config.json | jq -r tostring)\" algolia/docsearch-scraper",
"api:generate": "tsx --tsconfig ../builder/tsconfig.json ../builder/src/helpers/server/generateOpenApi.ts && tsx --tsconfig ../viewer/openapi.tsconfig.json ../viewer/src/utils/server/generateOpenApi.ts"
"api:generate": "tsx --tsconfig ../builder/tsconfig.json ../builder/src/helpers/server/generateOpenApi.ts && tsx --tsconfig ../viewer/openapi.tsconfig.json ../viewer/src/helpers/server/generateOpenApi.ts"
},
"dependencies": {
"@docusaurus/core": "2.3.1",

View File

@ -15,13 +15,14 @@ import {
} from '@typebot.io/lib'
import { SEO } from './Seo'
import { ErrorPage } from './ErrorPage'
import { createResultQuery, updateResultQuery } from '@/features/results'
import { upsertAnswerQuery } from '@/features/answers'
import { gtmBodyElement } from '@/lib/google-tag-manager'
import {
getExistingResultFromSession,
setResultInSession,
} from '@/utils/sessionStorage'
} from '@/helpers/sessionStorage'
import { upsertAnswerQuery } from '@/features/answers/queries/upsertAnswerQuery'
import { createResultQuery } from '@/features/results/queries/createResultQuery'
import { updateResultQuery } from '@/features/results/queries/updateResultQuery'
export type TypebotPageProps = {
publishedTypebot: Omit<PublicTypebot, 'createdAt' | 'updatedAt'> & {

View File

@ -1 +0,0 @@
export { upsertAnswerQuery } from './queries/upsertAnswerQuery'

View File

@ -1 +0,0 @@
export { authenticateUser } from './authenticateUser'

View File

@ -1,4 +1,3 @@
import { deepParseVariable } from '@/features/variables/utils'
import {
SessionState,
VariableWithValue,
@ -6,6 +5,7 @@ import {
ItemType,
} from '@typebot.io/schemas'
import { isDefined } from '@typebot.io/lib'
import { deepParseVariables } from '@/features/variables/deepParseVariable'
export const injectVariableValuesInButtonsInputBlock =
(variables: SessionState['typebot']['variables']) =>
@ -27,5 +27,5 @@ export const injectVariableValuesInButtonsInputBlock =
})),
}
}
return deepParseVariable(variables)(block)
return deepParseVariables(variables)(block)
}

View File

@ -1 +0,0 @@
export * from './utils'

View File

@ -1 +0,0 @@
export * from './parseReadableDate'

View File

@ -1 +0,0 @@
export * from './utils'

View File

@ -1 +0,0 @@
export * from './validateEmail'

View File

@ -1 +0,0 @@
export * from './utils'

View File

@ -1 +0,0 @@
export * from './computePaymentInputRuntimeOptions'

View File

@ -1,4 +1,4 @@
import { parseVariables } from '@/features/variables'
import { parseVariables } from '@/features/variables/parseVariables'
import prisma from '@/lib/prisma'
import { TRPCError } from '@trpc/server'
import {

View File

@ -1 +0,0 @@
export * from './utils'

View File

@ -1,2 +0,0 @@
export * from './formatPhoneNumber'
export * from './validatePhoneNumber'

View File

@ -1 +0,0 @@
export { validateUrl } from './utils/validateUrl'

View File

@ -1 +0,0 @@
export * from './utils/executeChatwootBlock'

View File

@ -1,9 +1,7 @@
import { ExecuteIntegrationResponse } from '@/features/chat'
import {
parseVariables,
parseCorrectValueType,
extractVariablesFromText,
} from '@/features/variables'
import { ExecuteIntegrationResponse } from '@/features/chat/types'
import { extractVariablesFromText } from '@/features/variables/extractVariablesFromText'
import { parseGuessedValueType } from '@/features/variables/parseGuessedValueType'
import { parseVariables } from '@/features/variables/parseVariables'
import {
ChatwootBlock,
ChatwootOptions,
@ -85,7 +83,7 @@ export const executeChatwootBlock = (
args: extractVariablesFromText(variables)(chatwootCode).map(
(variable) => ({
id: variable.id,
value: parseCorrectValueType(variable.value),
value: parseGuessedValueType(variable.value),
})
),
},

View File

@ -1 +0,0 @@
export { executeGoogleAnalyticsBlock } from './utils/executeGoogleAnalyticsBlock'

View File

@ -1,5 +1,5 @@
import { ExecuteIntegrationResponse } from '@/features/chat'
import { deepParseVariable } from '@/features/variables'
import { ExecuteIntegrationResponse } from '@/features/chat/types'
import { deepParseVariables } from '@/features/variables/deepParseVariable'
import { GoogleAnalyticsBlock, SessionState } from '@typebot.io/schemas'
export const executeGoogleAnalyticsBlock = (
@ -10,7 +10,7 @@ export const executeGoogleAnalyticsBlock = (
outgoingEdgeId: block.outgoingEdgeId,
clientSideActions: [
{
googleAnalytics: deepParseVariable(variables)(block.options),
googleAnalytics: deepParseVariables(variables)(block.options),
lastBubbleBlockId,
},
],

View File

@ -1 +0,0 @@
export * from './utils'

View File

@ -1 +0,0 @@
export * from './executeGoogleSheetBlock'

View File

@ -1,12 +1,12 @@
import { ExecuteIntegrationResponse } from '@/features/chat'
import {
GoogleSheetsBlock,
GoogleSheetsAction,
SessionState,
} from '@typebot.io/schemas'
import { getRow } from './getRow'
import { insertRow } from './insertRow'
import { updateRow } from './updateRow'
import { getRow } from './getRow'
import { ExecuteIntegrationResponse } from '@/features/chat/types'
export const executeGoogleSheetBlock = async (
state: SessionState,

View File

@ -6,12 +6,13 @@ import {
LogicalOperator,
ReplyLog,
} from '@typebot.io/schemas'
import { saveErrorLog } from '@/features/logs/api'
import { getAuthenticatedGoogleDoc } from './helpers'
import { deepParseVariable, updateVariables } from '@/features/variables'
import { isNotEmpty, byId, isDefined } from '@typebot.io/lib'
import { ExecuteIntegrationResponse } from '@/features/chat'
import type { GoogleSpreadsheetRow } from 'google-spreadsheet'
import { getAuthenticatedGoogleDoc } from './helpers/getAuthenticatedGoogleDoc'
import { ExecuteIntegrationResponse } from '@/features/chat/types'
import { saveErrorLog } from '@/features/logs/saveErrorLog'
import { updateVariables } from '@/features/variables/updateVariables'
import { deepParseVariables } from '@/features/variables/deepParseVariable'
export const getRow = async (
state: SessionState,
@ -20,7 +21,7 @@ export const getRow = async (
options,
}: { outgoingEdgeId?: string; options: GoogleSheetsGetOptions }
): Promise<ExecuteIntegrationResponse> => {
const { sheetId, cellsToExtract, referenceCell, filter } = deepParseVariable(
const { sheetId, cellsToExtract, referenceCell, filter } = deepParseVariables(
state.typebot.variables
)(options)
if (!sheetId) return { outgoingEdgeId }

View File

@ -1,20 +1,6 @@
import { parseVariables } from '@/features/variables'
import { getAuthenticatedGoogleClient } from '@/lib/google-sheets'
import { TRPCError } from '@trpc/server'
import { GoogleSpreadsheet } from 'google-spreadsheet'
import { Variable, Cell } from '@typebot.io/schemas'
export const parseCellValues =
(variables: Variable[]) =>
(cells: Cell[]): { [key: string]: string } =>
cells.reduce((row, cell) => {
return !cell.column || !cell.value
? row
: {
...row,
[cell.column]: parseVariables(variables)(cell.value),
}
}, {})
export const getAuthenticatedGoogleDoc = async ({
credentialsId,

View File

@ -0,0 +1,14 @@
import { parseVariables } from '@/features/variables/parseVariables'
import { Variable, Cell } from '@typebot.io/schemas'
export const parseCellValues =
(variables: Variable[]) =>
(cells: Cell[]): { [key: string]: string } =>
cells.reduce((row, cell) => {
return !cell.column || !cell.value
? row
: {
...row,
[cell.column]: parseVariables(variables)(cell.value),
}
}, {})

View File

@ -3,9 +3,11 @@ import {
GoogleSheetsInsertRowOptions,
ReplyLog,
} from '@typebot.io/schemas'
import { saveErrorLog, saveSuccessLog } from '@/features/logs/api'
import { getAuthenticatedGoogleDoc, parseCellValues } from './helpers'
import { ExecuteIntegrationResponse } from '@/features/chat'
import { parseCellValues } from './helpers/parseCellValues'
import { getAuthenticatedGoogleDoc } from './helpers/getAuthenticatedGoogleDoc'
import { ExecuteIntegrationResponse } from '@/features/chat/types'
import { saveErrorLog } from '@/features/logs/saveErrorLog'
import { saveSuccessLog } from '@/features/logs/saveSuccessLog'
export const insertRow = async (
{ result, typebot: { variables } }: SessionState,

View File

@ -3,11 +3,13 @@ import {
GoogleSheetsUpdateRowOptions,
ReplyLog,
} from '@typebot.io/schemas'
import { saveErrorLog, saveSuccessLog } from '@/features/logs/api'
import { getAuthenticatedGoogleDoc, parseCellValues } from './helpers'
import { TRPCError } from '@trpc/server'
import { deepParseVariable } from '@/features/variables'
import { ExecuteIntegrationResponse } from '@/features/chat'
import { parseCellValues } from './helpers/parseCellValues'
import { getAuthenticatedGoogleDoc } from './helpers/getAuthenticatedGoogleDoc'
import { deepParseVariables } from '@/features/variables/deepParseVariable'
import { ExecuteIntegrationResponse } from '@/features/chat/types'
import { saveErrorLog } from '@/features/logs/saveErrorLog'
import { saveSuccessLog } from '@/features/logs/saveSuccessLog'
export const updateRow = async (
{ result, typebot: { variables } }: SessionState,
@ -16,7 +18,7 @@ export const updateRow = async (
options,
}: { outgoingEdgeId?: string; options: GoogleSheetsUpdateRowOptions }
): Promise<ExecuteIntegrationResponse> => {
const { sheetId, referenceCell } = deepParseVariable(variables)(options)
const { sheetId, referenceCell } = deepParseVariables(variables)(options)
if (!options.cellsToUpsert || !sheetId || !referenceCell)
return { outgoingEdgeId }

View File

@ -1,7 +1,5 @@
import { ExecuteIntegrationResponse } from '@/features/chat/types'
import { saveErrorLog } from '@/features/logs/api'
import { transformStringVariablesToList } from '@/features/variables/transformVariablesToList'
import { parseVariables, updateVariables } from '@/features/variables/utils'
import prisma from '@/lib/prisma'
import {
SessionState,
@ -16,6 +14,9 @@ import {
import { OpenAIApi, Configuration, ChatCompletionRequestMessage } from 'openai'
import { isDefined, byId, isNotEmpty } from '@typebot.io/lib'
import { decrypt } from '@typebot.io/lib/api/encryption'
import { saveErrorLog } from '@/features/logs/saveErrorLog'
import { updateVariables } from '@/features/variables/updateVariables'
import { parseVariables } from 'bot-engine'
export const createChatCompletionOpenAI = async (
state: SessionState,

View File

@ -1 +0,0 @@
export { executeSendEmailBlock } from './utils/executeSendEmailBlock'

View File

@ -1,6 +1,4 @@
import { ExecuteIntegrationResponse } from '@/features/chat'
import { saveErrorLog, saveSuccessLog } from '@/features/logs/api'
import { parseVariables } from '@/features/variables'
import { parseVariables } from '@/features/variables/parseVariables'
import prisma from '@/lib/prisma'
import { render } from '@faire/mjml-react/utils/render'
import { DefaultBotNotificationEmail } from '@typebot.io/emails'
@ -17,7 +15,10 @@ import Mail from 'nodemailer/lib/mailer'
import { byId, isEmpty, isNotDefined, omit } from '@typebot.io/lib'
import { parseAnswers } from '@typebot.io/lib/results'
import { decrypt } from '@typebot.io/lib/api'
import { defaultFrom, defaultTransportOptions } from '../constants'
import { defaultFrom, defaultTransportOptions } from './constants'
import { ExecuteIntegrationResponse } from '@/features/chat/types'
import { saveErrorLog } from '@/features/logs/saveErrorLog'
import { saveSuccessLog } from '@/features/logs/saveSuccessLog'
export const executeSendEmailBlock = async (
{ result, typebot }: SessionState,

View File

@ -1 +0,0 @@
export * from './utils'

View File

@ -1,2 +0,0 @@
export * from './executeWebhookBlock'
export * from './parseSampleResult'

View File

@ -1,6 +1,3 @@
import { ExecuteIntegrationResponse } from '@/features/chat'
import { saveErrorLog, saveSuccessLog } from '@/features/logs/api'
import { parseVariables, updateVariables } from '@/features/variables'
import prisma from '@/lib/prisma'
import {
WebhookBlock,
@ -26,6 +23,11 @@ import { byId, omit } from '@typebot.io/lib'
import { parseAnswers } from '@typebot.io/lib/results'
import got, { Method, Headers, HTTPError } from 'got'
import { parseSampleResult } from './parseSampleResult'
import { ExecuteIntegrationResponse } from '@/features/chat/types'
import { saveErrorLog } from '@/features/logs/saveErrorLog'
import { saveSuccessLog } from '@/features/logs/saveSuccessLog'
import { updateVariables } from '@/features/variables/updateVariables'
import { parseVariables } from 'bot-engine'
export const executeWebhookBlock = async (
state: SessionState,

View File

@ -1 +0,0 @@
export { executeCondition } from './utils/executeCondition'

View File

@ -1,5 +1,3 @@
import { ExecuteLogicResponse } from '@/features/chat'
import { findUniqueVariableValue, parseVariables } from '@/features/variables'
import {
Comparison,
ComparisonOperators,
@ -9,6 +7,9 @@ import {
Variable,
} from '@typebot.io/schemas'
import { isNotDefined, isDefined } from '@typebot.io/lib'
import { ExecuteLogicResponse } from '@/features/chat/types'
import { findUniqueVariableValue } from '@/features/variables/findUniqueVariableValue'
import { parseVariables } from 'bot-engine'
export const executeCondition = (
{ typebot: { variables } }: SessionState,

View File

@ -1,8 +1,8 @@
import { ExecuteLogicResponse } from '@/features/chat'
import {
addEdgeToTypebot,
createPortalEdge,
} from '@/features/chat/api/utils/addEdgeToTypebot'
} from '@/features/chat/helpers/addEdgeToTypebot'
import { ExecuteLogicResponse } from '@/features/chat/types'
import { TRPCError } from '@trpc/server'
import { SessionState } from '@typebot.io/schemas'
import { JumpBlock } from '@typebot.io/schemas/features/blocks/logic/jump'

View File

@ -1 +0,0 @@
export { executeRedirect } from './utils/executeRedirect'

View File

@ -1,7 +1,7 @@
import { ExecuteLogicResponse } from '@/features/chat'
import { parseVariables } from '@/features/variables'
import { parseVariables } from '@/features/variables/parseVariables'
import { RedirectBlock, SessionState } from '@typebot.io/schemas'
import { sanitizeUrl } from '@typebot.io/lib'
import { ExecuteLogicResponse } from '@/features/chat/types'
export const executeRedirect = (
{ typebot: { variables } }: SessionState,

View File

@ -1,9 +1,7 @@
import { ExecuteLogicResponse } from '@/features/chat'
import {
parseVariables,
parseCorrectValueType,
extractVariablesFromText,
} from '@/features/variables'
import { ExecuteLogicResponse } from '@/features/chat/types'
import { extractVariablesFromText } from '@/features/variables/extractVariablesFromText'
import { parseGuessedValueType } from '@/features/variables/parseGuessedValueType'
import { parseVariables } from '@/features/variables/parseVariables'
import { ScriptBlock, SessionState } from '@typebot.io/schemas'
export const executeScript = (
@ -19,7 +17,7 @@ export const executeScript = (
const args = extractVariablesFromText(variables)(block.options.content).map(
(variable) => ({
id: variable.id,
value: parseCorrectValueType(variable.value),
value: parseGuessedValueType(variable.value),
})
)

View File

@ -1 +0,0 @@
export { executeSetVariable } from './utils/executeSetVariable'

View File

@ -1,11 +1,9 @@
import { SessionState, SetVariableBlock, Variable } from '@typebot.io/schemas'
import { byId } from '@typebot.io/lib'
import {
parseVariables,
parseCorrectValueType,
updateVariables,
} from '@/features/variables'
import { ExecuteLogicResponse } from '@/features/chat'
import { ExecuteLogicResponse } from '@/features/chat/types'
import { updateVariables } from '@/features/variables/updateVariables'
import { parseVariables } from '@/features/variables/parseVariables'
import { parseGuessedValueType } from '@/features/variables/parseGuessedValueType'
export const executeSetVariable = async (
state: SessionState,
@ -45,7 +43,7 @@ const evaluateSetVariableExpression =
)
try {
const func = Function(...variables.map((v) => v.id), evaluating)
return func(...variables.map((v) => parseCorrectValueType(v.value)))
return func(...variables.map((v) => parseGuessedValueType(v.value)))
} catch (err) {
return parseVariables(variables)(str)
}

View File

@ -1 +0,0 @@
export * from './utils'

View File

@ -1,3 +0,0 @@
export * from './executeTypebotLink'
export * from './getLinkedTypebots'
export * from './getLinkedTypebotsChildren'

View File

@ -1,9 +1,7 @@
import { ExecuteLogicResponse } from '@/features/chat'
import {
addEdgeToTypebot,
createPortalEdge,
} from '@/features/chat/api/utils/addEdgeToTypebot'
import { saveErrorLog } from '@/features/logs/api'
} from '@/features/chat/helpers/addEdgeToTypebot'
import prisma from '@/lib/prisma'
import {
TypebotLinkBlock,
@ -12,6 +10,8 @@ import {
Variable,
} from '@typebot.io/schemas'
import { byId } from '@typebot.io/lib'
import { ExecuteLogicResponse } from '@/features/chat/types'
import { saveErrorLog } from '@/features/logs/saveErrorLog'
export const executeTypebotLink = async (
state: SessionState,

View File

@ -1,5 +1,5 @@
import prisma from '@/lib/prisma'
import { canReadTypebots } from '@/utils/api/dbRules'
import { canReadTypebots } from '@/helpers/api/dbRules'
import { User } from '@typebot.io/prisma'
import {
LogicBlockType,

View File

@ -1,5 +1,5 @@
import { ExecuteLogicResponse } from '@/features/chat'
import { parseVariables } from '@/features/variables'
import { ExecuteLogicResponse } from '@/features/chat/types'
import { parseVariables } from '@/features/variables/parseVariables'
import { SessionState, WaitBlock } from '@typebot.io/schemas'
export const executeWait = async (

View File

@ -1,6 +0,0 @@
import { router } from '@/utils/server/trpc'
import { sendMessageProcedure } from './procedures'
export const chatRouter = router({
sendMessage: sendMessageProcedure,
})

View File

@ -1,2 +0,0 @@
export * from './chatRouter'
export { getSession } from './utils'

View File

@ -1 +0,0 @@
export * from './sendMessageProcedure'

View File

@ -0,0 +1,6 @@
import { router } from '@/helpers/server/trpc'
import { sendMessage } from './sendMessage'
export const chatRouter = router({
sendMessage,
})

View File

@ -1,12 +1,5 @@
import { checkChatsUsage } from '@/features/usage'
import {
prefillVariables,
deepParseVariable,
parseVariables,
injectVariablesFromExistingResult,
} from '@/features/variables'
import prisma from '@/lib/prisma'
import { publicProcedure } from '@/utils/server/trpc'
import { publicProcedure } from '@/helpers/server/trpc'
import { TRPCError } from '@trpc/server'
import { Prisma } from '@typebot.io/prisma'
import {
@ -28,10 +21,15 @@ import {
getSession,
setResultAsCompleted,
startBotFlow,
} from '../utils'
} from '../helpers'
import { env, isDefined, omit } from '@typebot.io/lib'
import { prefillVariables } from '@/features/variables/prefillVariables'
import { checkChatsUsage } from '@/features/usage/checkChatsUsage'
import { injectVariablesFromExistingResult } from '@/features/variables/injectVariablesFromExistingResult'
import { deepParseVariables } from '@/features/variables/deepParseVariable'
import { parseVariables } from '@/features/variables/parseVariables'
export const sendMessageProcedure = publicProcedure
export const sendMessage = publicProcedure
.meta({
openapi: {
method: 'POST',
@ -165,10 +163,10 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
clientSideActions,
typebot: {
id: typebot.id,
settings: deepParseVariable(newInitialState.typebot.variables)(
settings: deepParseVariables(newInitialState.typebot.variables)(
typebot.settings
),
theme: deepParseVariable(newInitialState.typebot.variables)(
theme: deepParseVariables(newInitialState.typebot.variables)(
typebot.theme
),
},
@ -195,10 +193,10 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
sessionId: session.id,
typebot: {
id: typebot.id,
settings: deepParseVariable(newInitialState.typebot.variables)(
settings: deepParseVariables(newInitialState.typebot.variables)(
typebot.settings
),
theme: deepParseVariable(newInitialState.typebot.variables)(
theme: deepParseVariables(newInitialState.typebot.variables)(
typebot.theme
),
},

View File

@ -1,10 +1,3 @@
import { validateEmail } from '@/features/blocks/inputs/email/api'
import {
formatPhoneNumber,
validatePhoneNumber,
} from '@/features/blocks/inputs/phone/api'
import { validateUrl } from '@/features/blocks/inputs/url/api'
import { parseVariables, updateVariables } from '@/features/variables'
import prisma from '@/lib/prisma'
import { TRPCError } from '@trpc/server'
import { Prisma } from '@typebot.io/prisma'
@ -22,6 +15,12 @@ import {
import { isInputBlock, isNotDefined } from '@typebot.io/lib'
import { executeGroup } from './executeGroup'
import { getNextGroup } from './getNextGroup'
import { validateEmail } from '@/features/blocks/inputs/email/validateEmail'
import { formatPhoneNumber } from '@/features/blocks/inputs/phone/formatPhoneNumber'
import { validatePhoneNumber } from '@/features/blocks/inputs/phone/validatePhoneNumber'
import { validateUrl } from '@/features/blocks/inputs/url/validateUrl'
import { updateVariables } from '@/features/variables/updateVariables'
import { parseVariables } from '@/features/variables/parseVariables'
export const continueBotFlow =
(state: SessionState) =>

View File

@ -1,4 +1,3 @@
import { deepParseVariable } from '@/features/variables'
import {
BubbleBlock,
BubbleBlockType,
@ -19,8 +18,9 @@ import {
import { executeLogic } from './executeLogic'
import { getNextGroup } from './getNextGroup'
import { executeIntegration } from './executeIntegration'
import { computePaymentInputRuntimeOptions } from '@/features/blocks/inputs/payment/api'
import { injectVariableValuesInButtonsInputBlock } from '@/features/blocks/inputs/buttons/api/utils/injectVariableValuesInButtonsInputBlock'
import { injectVariableValuesInButtonsInputBlock } from '@/features/blocks/inputs/buttons/injectVariableValuesInButtonsInputBlock'
import { deepParseVariables } from '@/features/variables/deepParseVariable'
import { computePaymentInputRuntimeOptions } from '@/features/blocks/inputs/payment/computePaymentInputRuntimeOptions'
export const executeGroup =
(
@ -137,7 +137,7 @@ const parseBubbleBlock =
(block: BubbleBlock): ChatReply['messages'][0] => {
switch (block.type) {
case BubbleBlockType.EMBED: {
const message = deepParseVariable(variables)(block)
const message = deepParseVariables(variables)(block)
return {
...message,
content: {
@ -150,7 +150,7 @@ const parseBubbleBlock =
}
}
default:
return deepParseVariable(variables)(block)
return deepParseVariables(variables)(block)
}
}
@ -164,7 +164,7 @@ const injectVariablesValueInBlock =
)
}
default: {
return deepParseVariable(state.typebot.variables)({
return deepParseVariables(state.typebot.variables)({
...block,
runtimeOptions: await computeRuntimeOptions(state)(block),
prefilledValue: getPrefilledInputValue(state.typebot.variables)(

View File

@ -1,15 +1,15 @@
import { executeChatwootBlock } from '@/features/blocks/integrations/chatwoot/api'
import { executeGoogleAnalyticsBlock } from '@/features/blocks/integrations/googleAnalytics/api'
import { executeGoogleSheetBlock } from '@/features/blocks/integrations/googleSheets/api'
import { executeOpenAIBlock } from '@/features/blocks/integrations/openai/executeOpenAIBlock'
import { executeSendEmailBlock } from '@/features/blocks/integrations/sendEmail/api'
import { executeWebhookBlock } from '@/features/blocks/integrations/webhook/api'
import { executeSendEmailBlock } from '@/features/blocks/integrations/sendEmail/executeSendEmailBlock'
import { executeWebhookBlock } from '@/features/blocks/integrations/webhook/executeWebhookBlock'
import { executeChatwootBlock } from '@/features/blocks/integrations/chatwoot/executeChatwootBlock'
import { executeGoogleAnalyticsBlock } from '@/features/blocks/integrations/googleAnalytics/executeGoogleAnalyticsBlock'
import { executeGoogleSheetBlock } from '@/features/blocks/integrations/googleSheets/executeGoogleSheetBlock'
import {
IntegrationBlock,
IntegrationBlockType,
SessionState,
} from '@typebot.io/schemas'
import { ExecuteIntegrationResponse } from '../../types'
import { ExecuteIntegrationResponse } from '../types'
export const executeIntegration =
(state: SessionState, lastBubbleBlockId?: string) =>

View File

@ -1,12 +1,12 @@
import { executeCondition } from '@/features/blocks/logic/condition/api'
import { executeRedirect } from '@/features/blocks/logic/redirect/api'
import { executeSetVariable } from '@/features/blocks/logic/setVariable/api'
import { executeTypebotLink } from '@/features/blocks/logic/typebotLink/api'
import { executeWait } from '@/features/blocks/logic/wait/api/utils/executeWait'
import { executeWait } from '@/features/blocks/logic/wait/executeWait'
import { LogicBlock, LogicBlockType, SessionState } from '@typebot.io/schemas'
import { ExecuteLogicResponse } from '../../types'
import { ExecuteLogicResponse } from '../types'
import { executeScript } from '@/features/blocks/logic/script/executeScript'
import { executeJumpBlock } from '@/features/blocks/logic/jump/executeJumpBlock'
import { executeRedirect } from '@/features/blocks/logic/redirect/executeRedirect'
import { executeCondition } from '@/features/blocks/logic/condition/executeCondition'
import { executeSetVariable } from '@/features/blocks/logic/setVariable/executeSetVariable'
import { executeTypebotLink } from '@/features/blocks/logic/typebotLink/executeTypebotLink'
export const executeLogic =
(state: SessionState, lastBubbleBlockId?: string) =>

View File

@ -1 +0,0 @@
export * from './types'

View File

@ -1,2 +0,0 @@
export * from './saveErrorLog'
export * from './saveSuccessLog'

View File

@ -1,4 +1,4 @@
import { saveLog } from './utils'
import { saveLog } from './saveLog'
export const saveErrorLog = ({
resultId,

View File

@ -1,4 +1,4 @@
import { saveLog } from './utils'
import { saveLog } from './saveLog'
export const saveSuccessLog = ({
resultId,

View File

@ -1 +0,0 @@
export { createResultQuery, updateResultQuery } from './queries'

View File

@ -1,2 +0,0 @@
export * from './createResultQuery'
export * from './updateResultQuery'

View File

@ -1 +0,0 @@
export * from './utils'

View File

@ -1 +0,0 @@
export * from './checkChatsUsage'

View File

@ -0,0 +1,28 @@
import { Variable } from '@typebot.io/schemas'
import { parseVariables } from './parseVariables'
export const deepParseVariables =
(variables: Variable[]) =>
<T extends Record<string, unknown>>(object: T): T =>
Object.keys(object).reduce<T>((newObj, key) => {
const currentValue = object[key]
if (typeof currentValue === 'string')
return { ...newObj, [key]: parseVariables(variables)(currentValue) }
if (currentValue instanceof Object && currentValue.constructor === Object)
return {
...newObj,
[key]: deepParseVariables(variables)(
currentValue as Record<string, unknown>
),
}
if (currentValue instanceof Array)
return {
...newObj,
[key]: currentValue.map(deepParseVariables(variables)),
}
return { ...newObj, [key]: currentValue }
}, {} as T)

View File

@ -0,0 +1,15 @@
import { Variable } from '@typebot.io/schemas'
export const extractVariablesFromText =
(variables: Variable[]) =>
(text: string): Variable[] => {
const matches = [...text.matchAll(/\{\{(.*?)\}\}/g)]
return matches.reduce<Variable[]>((acc, match) => {
const variableName = match[1]
const variable = variables.find(
(variable) => variable.name === variableName
)
if (!variable) return acc
return [...acc, variable]
}, [])
}

View File

@ -0,0 +1,12 @@
import { Variable } from '@typebot.io/schemas'
export const findUniqueVariableValue =
(variables: Variable[]) =>
(value: string | undefined): string | string[] | null => {
if (!value || !value.startsWith('{{') || !value.endsWith('}}')) return null
const variableName = value.slice(2, -2)
const variable = variables.find(
(variable) => variable.name === variableName
)
return variable?.value ?? null
}

View File

@ -0,0 +1 @@
export const hasVariable = (str: string): boolean => /\{\{(.*?)\}\}/g.test(str)

View File

@ -1 +0,0 @@
export * from './utils'

View File

@ -0,0 +1,17 @@
import { Result, Variable } from '@typebot.io/schemas'
export const injectVariablesFromExistingResult = (
variables: Variable[],
resultVariables: Result['variables']
): Variable[] =>
variables.map((variable) => {
const resultVariable = resultVariables.find(
(resultVariable) =>
resultVariable.name === variable.name && !variable.value
)
if (!resultVariable) return variable
return {
...variable,
value: resultVariable.value,
}
})

View File

@ -0,0 +1,20 @@
import { Variable } from '@typebot.io/schemas'
export const parseGuessedValueType = (
value: Variable['value']
): string | string[] | boolean | number | null | undefined => {
if (value === null) return null
if (value === undefined) return undefined
if (typeof value !== 'string') return value
const isNumberStartingWithZero =
value.startsWith('0') && !value.startsWith('0.') && value.length > 1
if (typeof value === 'string' && isNumberStartingWithZero) return value
if (typeof value === 'number') return value
if (value === 'true') return true
if (value === 'false') return false
if (value === 'null') return null
if (value === 'undefined') return undefined
// isNaN works with strings
if (isNaN(value as unknown as number)) return value
return Number(value)
}

View File

@ -0,0 +1,40 @@
import { isDefined } from '@typebot.io/lib'
import { Variable, VariableWithValue } from '@typebot.io/schemas'
import { safeStringify } from './safeStringify'
export const parseVariables =
(
variables: Variable[],
options: { fieldToParse?: 'value' | 'id'; escapeForJson?: boolean } = {
fieldToParse: 'value',
escapeForJson: false,
}
) =>
(text: string | undefined): string => {
if (!text || text === '') return ''
return text.replace(/\{\{(.*?)\}\}/g, (_, fullVariableString) => {
const matchedVarName = fullVariableString.replace(/{{|}}/g, '')
const variable = variables.find((variable) => {
return (
matchedVarName === variable.name &&
(options.fieldToParse === 'id' || isDefined(variable.value))
)
}) as VariableWithValue | undefined
if (!variable) return ''
if (options.fieldToParse === 'id') return variable.id
const { value } = variable
if (options.escapeForJson)
return jsonParse(
typeof value !== 'string' ? JSON.stringify(value) : value
)
const parsedValue = safeStringify(value)
if (!parsedValue) return ''
return parsedValue
})
}
const jsonParse = (str: string) =>
str
.replace(/\n/g, `\\n`)
.replace(/"/g, `\\"`)
.replace(/\\[^n"]/g, `\\\\ `)

View File

@ -0,0 +1,15 @@
import { StartParams, Variable } from '@typebot.io/schemas'
import { safeStringify } from './safeStringify'
export const prefillVariables = (
variables: Variable[],
prefilledVariables: NonNullable<StartParams['prefilledVariables']>
): Variable[] =>
variables.map((variable) => {
const prefilledVariable = prefilledVariables[variable.name]
if (!prefilledVariable) return variable
return {
...variable,
value: safeStringify(prefilledVariable),
}
})

View File

@ -0,0 +1,12 @@
import { isNotDefined } from '@typebot.io/lib'
export const safeStringify = (val: unknown): string | null => {
if (isNotDefined(val)) return null
if (typeof val === 'string') return val
try {
return JSON.stringify(val)
} catch {
console.warn('Failed to safely stringify variable value', val)
return null
}
}

View File

@ -0,0 +1,77 @@
import prisma from '@/lib/prisma'
import { isDefined } from '@typebot.io/lib'
import {
SessionState,
VariableWithUnknowValue,
VariableWithValue,
Variable,
} from '@typebot.io/schemas'
import { safeStringify } from './safeStringify'
export const updateVariables =
(state: SessionState) =>
async (newVariables: VariableWithUnknowValue[]): Promise<SessionState> => ({
...state,
typebot: {
...state.typebot,
variables: updateTypebotVariables(state)(newVariables),
},
result: {
...state.result,
variables: await updateResultVariables(state)(newVariables),
},
})
const updateResultVariables =
({ result }: Pick<SessionState, 'result' | 'typebot'>) =>
async (
newVariables: VariableWithUnknowValue[]
): Promise<VariableWithValue[]> => {
const serializedNewVariables = newVariables.map((variable) => ({
...variable,
value: Array.isArray(variable.value)
? variable.value.map(safeStringify).filter(isDefined)
: safeStringify(variable.value),
}))
const updatedVariables = [
...result.variables.filter((existingVariable) =>
serializedNewVariables.every(
(newVariable) => existingVariable.id !== newVariable.id
)
),
...serializedNewVariables,
].filter((variable) => isDefined(variable.value)) as VariableWithValue[]
if (result.id)
await prisma.result.update({
where: {
id: result.id,
},
data: {
variables: updatedVariables,
},
})
return updatedVariables
}
const updateTypebotVariables =
({ typebot }: Pick<SessionState, 'result' | 'typebot'>) =>
(newVariables: VariableWithUnknowValue[]): Variable[] => {
const serializedNewVariables = newVariables.map((variable) => ({
...variable,
value: Array.isArray(variable.value)
? variable.value.map(safeStringify).filter(isDefined)
: safeStringify(variable.value),
}))
return [
...typebot.variables.filter((existingVariable) =>
serializedNewVariables.every(
(newVariable) => existingVariable.id !== newVariable.id
)
),
...serializedNewVariables,
]
}

View File

@ -1,229 +0,0 @@
import prisma from '@/lib/prisma'
import {
Result,
SessionState,
StartParams,
Typebot,
Variable,
VariableWithUnknowValue,
VariableWithValue,
} from '@typebot.io/schemas'
import { isDefined, isNotDefined } from '@typebot.io/lib'
export const stringContainsVariable = (str: string): boolean =>
/\{\{(.*?)\}\}/g.test(str)
export const parseVariables =
(
variables: Variable[],
options: { fieldToParse?: 'value' | 'id'; escapeForJson?: boolean } = {
fieldToParse: 'value',
escapeForJson: false,
}
) =>
(text: string | undefined): string => {
if (!text || text === '') return ''
return text.replace(/\{\{(.*?)\}\}/g, (_, fullVariableString) => {
const matchedVarName = fullVariableString.replace(/{{|}}/g, '')
const variable = variables.find((variable) => {
return (
matchedVarName === variable.name &&
(options.fieldToParse === 'id' || isDefined(variable.value))
)
}) as VariableWithValue | undefined
if (!variable) return ''
if (options.fieldToParse === 'id') return variable.id
const { value } = variable
if (options.escapeForJson)
return jsonParse(
typeof value !== 'string' ? JSON.stringify(value) : value
)
const parsedValue = safeStringify(value)
if (!parsedValue) return ''
return parsedValue
})
}
export const extractVariablesFromText =
(variables: Variable[]) =>
(text: string): Variable[] => {
const matches = [...text.matchAll(/\{\{(.*?)\}\}/g)]
return matches.reduce<Variable[]>((acc, match) => {
const variableName = match[1]
const variable = variables.find(
(variable) => variable.name === variableName
)
if (!variable) return acc
return [...acc, variable]
}, [])
}
export const safeStringify = (val: unknown): string | null => {
if (isNotDefined(val)) return null
if (typeof val === 'string') return val
try {
return JSON.stringify(val)
} catch {
console.warn('Failed to safely stringify variable value', val)
return null
}
}
export const parseCorrectValueType = (
value: Variable['value']
): string | string[] | boolean | number | null | undefined => {
if (value === null) return null
if (value === undefined) return undefined
if (typeof value !== 'string') return value
const isNumberStartingWithZero =
value.startsWith('0') && !value.startsWith('0.') && value.length > 1
if (typeof value === 'string' && isNumberStartingWithZero) return value
if (typeof value === 'number') return value
if (value === 'true') return true
if (value === 'false') return false
if (value === 'null') return null
if (value === 'undefined') return undefined
// isNaN works with strings
if (isNaN(value as unknown as number)) return value
return Number(value)
}
const jsonParse = (str: string) =>
str
.replace(/\n/g, `\\n`)
.replace(/"/g, `\\"`)
.replace(/\\[^n"]/g, `\\\\ `)
export const deepParseVariable =
(variables: Variable[]) =>
<T extends Record<string, unknown>>(object: T): T =>
Object.keys(object).reduce<T>((newObj, key) => {
const currentValue = object[key]
if (typeof currentValue === 'string')
return { ...newObj, [key]: parseVariables(variables)(currentValue) }
if (currentValue instanceof Object && currentValue.constructor === Object)
return {
...newObj,
[key]: deepParseVariable(variables)(
currentValue as Record<string, unknown>
),
}
if (currentValue instanceof Array)
return {
...newObj,
[key]: currentValue.map(deepParseVariable(variables)),
}
return { ...newObj, [key]: currentValue }
}, {} as T)
export const prefillVariables = (
variables: Typebot['variables'],
prefilledVariables: NonNullable<StartParams['prefilledVariables']>
): Variable[] =>
variables.map((variable) => {
const prefilledVariable = prefilledVariables[variable.name]
if (!prefilledVariable) return variable
return {
...variable,
value: safeStringify(prefilledVariable),
}
})
export const injectVariablesFromExistingResult = (
variables: Typebot['variables'],
resultVariables: Result['variables']
): Variable[] =>
variables.map((variable) => {
const resultVariable = resultVariables.find(
(resultVariable) =>
resultVariable.name === variable.name && !variable.value
)
if (!resultVariable) return variable
return {
...variable,
value: resultVariable.value,
}
})
export const updateVariables =
(state: SessionState) =>
async (newVariables: VariableWithUnknowValue[]): Promise<SessionState> => ({
...state,
typebot: {
...state.typebot,
variables: updateTypebotVariables(state)(newVariables),
},
result: {
...state.result,
variables: await updateResultVariables(state)(newVariables),
},
})
const updateResultVariables =
({ result }: Pick<SessionState, 'result' | 'typebot'>) =>
async (
newVariables: VariableWithUnknowValue[]
): Promise<VariableWithValue[]> => {
const serializedNewVariables = newVariables.map((variable) => ({
...variable,
value: Array.isArray(variable.value)
? variable.value.map(safeStringify).filter(isDefined)
: safeStringify(variable.value),
}))
const updatedVariables = [
...result.variables.filter((existingVariable) =>
serializedNewVariables.every(
(newVariable) => existingVariable.id !== newVariable.id
)
),
...serializedNewVariables,
].filter((variable) => isDefined(variable.value)) as VariableWithValue[]
if (result.id)
await prisma.result.update({
where: {
id: result.id,
},
data: {
variables: updatedVariables,
},
})
return updatedVariables
}
const updateTypebotVariables =
({ typebot }: Pick<SessionState, 'result' | 'typebot'>) =>
(newVariables: VariableWithUnknowValue[]): Variable[] => {
const serializedNewVariables = newVariables.map((variable) => ({
...variable,
value: Array.isArray(variable.value)
? variable.value.map(safeStringify).filter(isDefined)
: safeStringify(variable.value),
}))
return [
...typebot.variables.filter((existingVariable) =>
serializedNewVariables.every(
(newVariable) => existingVariable.id !== newVariable.id
)
),
...serializedNewVariables,
]
}
export const findUniqueVariableValue =
(variables: Variable[]) =>
(value: string | undefined): string | string[] | null => {
if (!value || !value.startsWith('{{') || !value.endsWith('}}')) return null
const variableName = value.slice(2, -2)
const variable = variables.find(
(variable) => variable.name === variableName
)
return variable?.value ?? null
}

View File

@ -1,4 +1,4 @@
import { chatRouter } from '@/features/chat/api'
import { chatRouter } from '@/features/chat/api/router'
import { router } from '../../trpc'
export const appRouter = router({

View File

@ -17,7 +17,8 @@ import {
} from '@typebot.io/schemas'
import Cors from 'cors'
import { getAuthenticatedGoogleClient } from '@/lib/google-sheets'
import { saveErrorLog, saveSuccessLog } from '@/features/logs/api'
import { saveErrorLog } from '@/features/logs/saveErrorLog'
import { saveSuccessLog } from '@/features/logs/saveSuccessLog'
const cors = initMiddleware(Cors())

View File

@ -15,7 +15,7 @@ import {
Variable,
} from '@typebot.io/schemas'
import prisma from '@/lib/prisma'
import { parseVariables } from '@/features/variables'
import { parseVariables } from '@/features/variables/parseVariables'
const cors = initMiddleware(Cors())

View File

@ -1,4 +1,4 @@
import { authenticateUser } from '@/features/auth/api'
import { authenticateUser } from '@/helpers/authenticateUser'
import prisma from '@/lib/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { methodNotAllowed } from '@typebot.io/lib/api'

Some files were not shown because too many files have changed in this diff Show More