2
0

🚸 (openai) Parse stream on client to correctly handle errors

This commit is contained in:
Baptiste Arnaud
2023-06-16 19:26:29 +02:00
parent 83f2a29faa
commit 524f1565d8
11 changed files with 209 additions and 154 deletions

View File

@ -35,6 +35,27 @@
"type": "string",
"description": "Session ID that you get from the initial chat request to a bot. If not provided, it will create a new session."
},
"clientLogs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"description": {
"type": "string"
},
"details": {}
},
"required": [
"status",
"description"
],
"additionalProperties": false
},
"description": "Logs while executing client side actions"
},
"startParams": {
"type": "object",
"properties": {

View File

@ -10,10 +10,9 @@ import { decrypt, isCredentialsV2 } from '@typebot.io/lib/api/encryption'
import { updateVariables } from '@/features/variables/updateVariables'
import { parseVariableNumber } from '@/features/variables/parseVariableNumber'
import { resumeChatCompletion } from './resumeChatCompletion'
import { isPlaneteScale } from '@/helpers/api/isPlanetScale'
import { isVercel } from '@/helpers/api/isVercel'
import { parseChatCompletionMessages } from './parseChatCompletionMessages'
import { executeChatCompletionOpenAIRequest } from './executeChatCompletionOpenAIRequest'
import { isPlaneteScale } from '@/helpers/api/isPlanetScale'
export const createChatCompletionOpenAI = async (
state: SessionState,
@ -58,7 +57,6 @@ export const createChatCompletionOpenAI = async (
if (
isPlaneteScale() &&
isVercel() &&
isCredentialsV2(credentials) &&
newSessionState.isStreamEnabled
)

View File

@ -6,11 +6,6 @@ import {
OpenAICredentials,
} from '@typebot.io/schemas/features/blocks/integrations/openai'
import { SessionState } from '@typebot.io/schemas/features/chat'
import {
ParsedEvent,
ReconnectInterval,
createParser,
} from 'eventsource-parser'
import type {
ChatCompletionRequestMessage,
CreateChatCompletionRequest,
@ -42,11 +37,6 @@ export const getChatCompletionStream =
options.advancedSettings?.temperature
)
const encoder = new TextEncoder()
const decoder = new TextDecoder()
let counter = 0
const res = await fetch('https://api.openai.com/v1/chat/completions', {
headers: {
'Content-Type': 'application/json',
@ -61,43 +51,5 @@ export const getChatCompletionStream =
} satisfies CreateChatCompletionRequest),
})
const stream = new ReadableStream({
async start(controller) {
function onParse(event: ParsedEvent | ReconnectInterval) {
if (event.type === 'event') {
const data = event.data
if (data === '[DONE]') {
controller.close()
return
}
try {
const json = JSON.parse(data) as {
choices: { delta: { content: string } }[]
}
const text = json.choices.at(0)?.delta.content
if (counter < 2 && (text?.match(/\n/) || []).length) {
return
}
const queue = encoder.encode(text)
controller.enqueue(queue)
counter++
} catch (e) {
controller.error(e)
}
}
}
// stream response (SSE) from OpenAI may be fragmented into multiple chunks
// this ensures we properly read chunks & invoke an event for each SSE event stream
const parser = createParser(onParse)
// https://web.dev/streams/#asynchronous-iteration
// eslint-disable-next-line @typescript-eslint/no-explicit-any
for await (const chunk of res.body as any) {
parser.feed(decoder.decode(chunk))
}
},
})
return stream
return res.body
}

View File

@ -27,6 +27,7 @@ import { prefillVariables } from '@/features/variables/prefillVariables'
import { injectVariablesFromExistingResult } from '@/features/variables/injectVariablesFromExistingResult'
import { deepParseVariables } from '@/features/variables/deepParseVariable'
import { parseVariables } from '@/features/variables/parseVariables'
import { saveLog } from '@/features/logs/saveLog'
export const sendMessage = publicProcedure
.meta({
@ -42,9 +43,23 @@ export const sendMessage = publicProcedure
.input(sendMessageInputSchema)
.output(chatReplySchema)
.query(
async ({ input: { sessionId, message, startParams }, ctx: { user } }) => {
async ({
input: { sessionId, message, startParams, clientLogs },
ctx: { user },
}) => {
const session = sessionId ? await getSession(sessionId) : null
if (clientLogs) {
for (const log of clientLogs) {
await saveLog({
message: log.description,
status: log.status as 'error' | 'success' | 'info',
resultId: session?.state.result.id,
details: log.details,
})
}
}
if (!session) {
const {
sessionId,

View File

@ -16,7 +16,7 @@ import {
SetVariableBlock,
WebhookBlock,
} from '@typebot.io/schemas'
import { isInputBlock, isNotDefined, byId, isDefined } from '@typebot.io/lib'
import { isInputBlock, isNotDefined, byId } from '@typebot.io/lib'
import { executeGroup } from './executeGroup'
import { getNextGroup } from './getNextGroup'
import { validateEmail } from '@/features/blocks/inputs/email/validateEmail'
@ -69,15 +69,16 @@ export const continueBotFlow =
)(JSON.parse(reply))
if (result.newSessionState) newSessionState = result.newSessionState
} else if (
isDefined(reply) &&
block.type === IntegrationBlockType.OPEN_AI &&
block.options.task === 'Create chat completion'
) {
if (reply) {
const result = await resumeChatCompletion(state, {
options: block.options,
outgoingEdgeId: block.outgoingEdgeId,
})(reply)
newSessionState = result.newSessionState
}
} else if (!isInputBlock(block))
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',

View File

@ -14,6 +14,7 @@
"dependencies": {
"@stripe/stripe-js": "1.53.0",
"@udecode/plate-common": "^21.1.5",
"eventsource-parser": "^1.0.0",
"solid-element": "1.7.0",
"solid-js": "1.7.5"
},

View File

@ -1,4 +1,4 @@
import { ChatReply, Theme } from '@typebot.io/schemas'
import { ChatReply, SendMessageInput, Theme } from '@typebot.io/schemas'
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/enums'
import { createEffect, createSignal, For, onMount, Show } from 'solid-js'
import { sendMessageQuery } from '@/queries/sendMessageQuery'
@ -79,7 +79,7 @@ export const ConversationContainer = (props: Props) => {
sessionId: props.initialChatReply.sessionId,
})
if (response && 'replyToSend' in response) {
sendMessage(response.replyToSend)
sendMessage(response.replyToSend, response.logs)
return
}
if (response && 'blockedPopupUrl' in response)
@ -95,7 +95,11 @@ export const ConversationContainer = (props: Props) => {
)
})
const sendMessage = async (message: string | undefined) => {
const sendMessage = async (
message: string | undefined,
clientLogs?: SendMessageInput['clientLogs']
) => {
if (clientLogs) props.onNewLogs?.(clientLogs)
setHasError(false)
const currentInputBlock = [...chatChunks()].pop()?.input
if (currentInputBlock?.id && props.onAnswer && message)
@ -114,6 +118,7 @@ export const ConversationContainer = (props: Props) => {
apiHost: props.context.apiHost,
sessionId: props.initialChatReply.sessionId,
message,
clientLogs,
})
clearTimeout(longRequest)
setIsSending(false)
@ -151,7 +156,7 @@ export const ConversationContainer = (props: Props) => {
sessionId: props.initialChatReply.sessionId,
})
if (response && 'replyToSend' in response) {
sendMessage(response.replyToSend)
sendMessage(response.replyToSend, response.logs)
return
}
if (response && 'blockedPopupUrl' in response)
@ -200,7 +205,7 @@ export const ConversationContainer = (props: Props) => {
sessionId: props.initialChatReply.sessionId,
})
if (response && 'replyToSend' in response) {
sendMessage(response.replyToSend)
sendMessage(response.replyToSend, response.logs)
return
}
if (response && 'blockedPopupUrl' in response)

View File

@ -1,5 +1,10 @@
import { getOpenAiStreamerQuery } from '@/queries/getOpenAiStreamerQuery'
import { ClientSideActionContext } from '@/types'
import {
createParser,
ParsedEvent,
ReconnectInterval,
} from 'eventsource-parser'
export const streamChat =
(context: ClientSideActionContext) =>
@ -8,25 +13,59 @@ export const streamChat =
content?: string | undefined
role?: 'system' | 'user' | 'assistant' | undefined
}[],
{ onStreamedMessage }: { onStreamedMessage?: (message: string) => void }
) => {
{
onStreamedMessage,
isRetrying,
}: { onStreamedMessage?: (message: string) => void; isRetrying?: boolean }
): Promise<{ message?: string; error?: object }> => {
const data = await getOpenAiStreamerQuery(context)(messages)
if (!data) {
return
}
if (!data) return { error: { message: "Couldn't get streamer data" } }
let message = ''
const reader = data.getReader()
const decoder = new TextDecoder()
let done = false
let message = ''
while (!done) {
const { value, done: doneReading } = await reader.read()
done = doneReading
const chunkValue = decoder.decode(value)
message += chunkValue
const onParse = (event: ParsedEvent | ReconnectInterval) => {
if (event.type === 'event') {
const data = event.data
try {
const json = JSON.parse(data) as {
choices: { delta: { content: string } }[]
}
const text = json.choices.at(0)?.delta.content
if (!text) return
message += text
onStreamedMessage?.(message)
} catch (e) {
console.error(e)
}
return message
}
}
const parser = createParser(onParse)
// eslint-disable-next-line no-constant-condition
while (true) {
const { value, done } = await reader.read()
if (done || !value) break
const dataString = decoder.decode(value)
if (dataString.includes('503 Service Temporarily Unavailable')) {
if (isRetrying)
return { error: { message: "Couldn't get streamer data" } }
await new Promise((resolve) => setTimeout(resolve, 1000))
return streamChat(context)(messages, {
onStreamedMessage,
isRetrying: true,
})
}
if (dataString.includes('[DONE]')) break
if (dataString.includes('"error":')) {
return { error: JSON.parse(dataString).error }
}
parser.feed(dataString)
}
return { message }
}

View File

@ -7,14 +7,16 @@ import { executeSetVariable } from '@/features/blocks/logic/setVariable/executeS
import { executeWait } from '@/features/blocks/logic/wait/utils/executeWait'
import { executeWebhook } from '@/features/blocks/integrations/webhook/executeWebhook'
import { ClientSideActionContext } from '@/types'
import type { ChatReply } from '@typebot.io/schemas'
import type { ChatReply, ReplyLog } from '@typebot.io/schemas'
export const executeClientSideAction = async (
clientSideAction: NonNullable<ChatReply['clientSideActions']>[0],
context: ClientSideActionContext,
onStreamedMessage?: (message: string) => void
): Promise<
{ blockedPopupUrl: string } | { replyToSend: string | undefined } | void
| { blockedPopupUrl: string }
| { replyToSend: string | undefined; logs?: ReplyLog[] }
| void
> => {
if ('chatwoot' in clientSideAction) {
return executeChatwoot(clientSideAction.chatwoot)
@ -35,11 +37,22 @@ export const executeClientSideAction = async (
return executeSetVariable(clientSideAction.setVariable.scriptToExecute)
}
if ('streamOpenAiChatCompletion' in clientSideAction) {
const text = await streamChat(context)(
const { error, message } = await streamChat(context)(
clientSideAction.streamOpenAiChatCompletion.messages,
{ onStreamedMessage }
)
return { replyToSend: text }
if (error)
return {
replyToSend: undefined,
logs: [
{
status: 'error',
description: 'Failed to stream OpenAI completion',
details: JSON.stringify(error, null, 2),
},
],
}
return { replyToSend: message }
}
if ('webhookToExecute' in clientSideAction) {
const response = await executeWebhook(clientSideAction.webhookToExecute)

View File

@ -168,6 +168,13 @@ const startParamsSchema = z.object({
isStreamEnabled: z.boolean().optional(),
})
const replyLogSchema = logSchema
.pick({
status: true,
description: true,
})
.merge(z.object({ details: z.unknown().optional() }))
export const sendMessageInputSchema = z.object({
message: z
.string()
@ -181,18 +188,15 @@ export const sendMessageInputSchema = z.object({
.describe(
'Session ID that you get from the initial chat request to a bot. If not provided, it will create a new session.'
),
clientLogs: z
.array(replyLogSchema)
.optional()
.describe('Logs while executing client side actions'),
startParams: startParamsSchema.optional(),
})
const runtimeOptionsSchema = paymentInputRuntimeOptionsSchema.optional()
const replyLogSchema = logSchema
.pick({
status: true,
description: true,
})
.merge(z.object({ details: z.unknown().optional() }))
const clientSideActionSchema = z
.object({
lastBubbleBlockId: z.string().optional(),

134
pnpm-lock.yaml generated
View File

@ -810,6 +810,9 @@ importers:
'@udecode/plate-common':
specifier: ^21.1.5
version: 21.1.5(@babel/core@7.21.8)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.23.0)(slate-history@0.93.0)(slate-react@0.94.2)(slate@0.94.1)
eventsource-parser:
specifier: ^1.0.0
version: 1.0.0
solid-element:
specifier: 1.7.0
version: 1.7.0(solid-js@1.7.5)
@ -5178,7 +5181,6 @@ packages:
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm64@0.17.5:
@ -5187,6 +5189,7 @@ packages:
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm@0.15.18:
@ -5204,7 +5207,6 @@ packages:
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm@0.17.5:
@ -5213,6 +5215,7 @@ packages:
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-x64@0.17.19:
@ -5221,7 +5224,6 @@ packages:
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-x64@0.17.5:
@ -5230,6 +5232,7 @@ packages:
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-arm64@0.17.19:
@ -5238,7 +5241,6 @@ packages:
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-arm64@0.17.5:
@ -5247,6 +5249,7 @@ packages:
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-x64@0.17.19:
@ -5255,7 +5258,6 @@ packages:
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-x64@0.17.5:
@ -5264,6 +5266,7 @@ packages:
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-arm64@0.17.19:
@ -5272,7 +5275,6 @@ packages:
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-arm64@0.17.5:
@ -5281,6 +5283,7 @@ packages:
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-x64@0.17.19:
@ -5289,7 +5292,6 @@ packages:
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-x64@0.17.5:
@ -5298,6 +5300,7 @@ packages:
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm64@0.17.19:
@ -5306,7 +5309,6 @@ packages:
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm64@0.17.5:
@ -5315,6 +5317,7 @@ packages:
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm@0.17.19:
@ -5323,7 +5326,6 @@ packages:
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm@0.17.5:
@ -5332,6 +5334,7 @@ packages:
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ia32@0.17.19:
@ -5340,7 +5343,6 @@ packages:
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ia32@0.17.5:
@ -5349,6 +5351,7 @@ packages:
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64@0.15.18:
@ -5366,7 +5369,6 @@ packages:
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64@0.17.5:
@ -5375,6 +5377,7 @@ packages:
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-mips64el@0.17.19:
@ -5383,7 +5386,6 @@ packages:
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-mips64el@0.17.5:
@ -5392,6 +5394,7 @@ packages:
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ppc64@0.17.19:
@ -5400,7 +5403,6 @@ packages:
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ppc64@0.17.5:
@ -5409,6 +5411,7 @@ packages:
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-riscv64@0.17.19:
@ -5417,7 +5420,6 @@ packages:
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-riscv64@0.17.5:
@ -5426,6 +5428,7 @@ packages:
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-s390x@0.17.19:
@ -5434,7 +5437,6 @@ packages:
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-s390x@0.17.5:
@ -5443,6 +5445,7 @@ packages:
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-x64@0.17.19:
@ -5451,7 +5454,6 @@ packages:
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-x64@0.17.5:
@ -5460,6 +5462,7 @@ packages:
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/netbsd-x64@0.17.19:
@ -5468,7 +5471,6 @@ packages:
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/netbsd-x64@0.17.5:
@ -5477,6 +5479,7 @@ packages:
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/openbsd-x64@0.17.19:
@ -5485,7 +5488,6 @@ packages:
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/openbsd-x64@0.17.5:
@ -5494,6 +5496,7 @@ packages:
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/sunos-x64@0.17.19:
@ -5502,7 +5505,6 @@ packages:
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/@esbuild/sunos-x64@0.17.5:
@ -5511,6 +5513,7 @@ packages:
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-arm64@0.17.19:
@ -5519,7 +5522,6 @@ packages:
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-arm64@0.17.5:
@ -5528,6 +5530,7 @@ packages:
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-ia32@0.17.19:
@ -5536,7 +5539,6 @@ packages:
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-ia32@0.17.5:
@ -5545,6 +5547,7 @@ packages:
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-x64@0.17.19:
@ -5553,7 +5556,6 @@ packages:
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-x64@0.17.5:
@ -5562,6 +5564,7 @@ packages:
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@eslint-community/eslint-utils@4.4.0(eslint@8.41.0):
@ -5788,7 +5791,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/types': 29.5.0
'@types/node': 20.2.3
'@types/node': 20.3.1
chalk: 4.1.2
jest-message-util: 29.5.0
jest-util: 29.5.0
@ -5809,14 +5812,14 @@ packages:
'@jest/test-result': 29.5.0
'@jest/transform': 29.5.0
'@jest/types': 29.5.0
'@types/node': 20.2.3
'@types/node': 20.3.1
ansi-escapes: 4.3.2
chalk: 4.1.2
ci-info: 3.8.0
exit: 0.1.2
graceful-fs: 4.2.11
jest-changed-files: 29.5.0
jest-config: 29.5.0(@types/node@20.2.3)
jest-config: 29.5.0(@types/node@20.3.1)
jest-haste-map: 29.5.0
jest-message-util: 29.5.0
jest-regex-util: 29.4.3
@ -5843,7 +5846,7 @@ packages:
dependencies:
'@jest/fake-timers': 29.5.0
'@jest/types': 29.5.0
'@types/node': 20.2.3
'@types/node': 20.3.1
jest-mock: 29.5.0
dev: true
@ -5870,7 +5873,7 @@ packages:
dependencies:
'@jest/types': 29.5.0
'@sinonjs/fake-timers': 10.2.0
'@types/node': 20.2.3
'@types/node': 20.3.1
jest-message-util: 29.5.0
jest-mock: 29.5.0
jest-util: 29.5.0
@ -5903,7 +5906,7 @@ packages:
'@jest/transform': 29.5.0
'@jest/types': 29.5.0
'@jridgewell/trace-mapping': 0.3.18
'@types/node': 20.2.3
'@types/node': 20.3.1
chalk: 4.1.2
collect-v8-coverage: 1.0.1
exit: 0.1.2
@ -5990,7 +5993,7 @@ packages:
'@jest/schemas': 29.4.3
'@types/istanbul-lib-coverage': 2.0.4
'@types/istanbul-reports': 3.0.1
'@types/node': 20.2.3
'@types/node': 20.3.1
'@types/yargs': 17.0.24
chalk: 4.1.2
@ -7692,13 +7695,13 @@ packages:
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
dependencies:
'@types/connect': 3.4.35
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: false
/@types/bonjour@3.5.10:
resolution: {integrity: sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: false
/@types/canvas-confetti@1.6.0:
@ -7709,13 +7712,13 @@ packages:
resolution: {integrity: sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==}
dependencies:
'@types/express-serve-static-core': 4.17.35
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: false
/@types/connect@3.4.35:
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: false
/@types/content-type@1.1.5:
@ -7749,7 +7752,7 @@ packages:
/@types/express-serve-static-core@4.17.35:
resolution: {integrity: sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
'@types/qs': 6.9.7
'@types/range-parser': 1.2.4
'@types/send': 0.17.1
@ -7771,7 +7774,7 @@ packages:
/@types/graceful-fs@4.1.6:
resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: true
/@types/hast@2.3.4:
@ -7800,7 +7803,7 @@ packages:
/@types/http-proxy@1.17.11:
resolution: {integrity: sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: false
/@types/is-hotkey@0.1.7:
@ -7834,7 +7837,7 @@ packages:
/@types/jsdom@20.0.1:
resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
'@types/tough-cookie': 4.0.2
parse5: 7.1.2
dev: true
@ -7849,13 +7852,13 @@ packages:
/@types/jsonwebtoken@9.0.2:
resolution: {integrity: sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: true
/@types/keyv@3.1.4:
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: false
/@types/lodash.mergewith@4.6.7:
@ -7883,7 +7886,7 @@ packages:
/@types/micro@7.3.7:
resolution: {integrity: sha512-MFsX7eCj0Tg3TtphOQvANNvNtFpya+s/rYOCdV6o+DFjOQPFi2EVRbBALjbbgZTXUaJP1Q281MJiJOD40d0UxQ==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: true
/@types/mime@1.3.2:
@ -7897,7 +7900,7 @@ packages:
/@types/minio@7.1.0:
resolution: {integrity: sha512-yUaFE62KI7lhQLH7lQpT1bzDmTOS+8bD7wGN8ySk8teLv9afbL7RIT3tSbeijq6sqeyS9kGpj9Kmp4gHNQJWCw==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: true
/@types/node@17.0.45:
@ -7911,6 +7914,9 @@ packages:
/@types/node@20.2.3:
resolution: {integrity: sha512-pg9d0yC4rVNWQzX8U7xb4olIOFuuVL9za3bzMT2pu2SU0SNEi66i2qrvhE2qt0HvkhuCaWJu7pLNOt/Pj8BIrw==}
/@types/node@20.3.1:
resolution: {integrity: sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==}
/@types/nodemailer@6.4.8:
resolution: {integrity: sha512-oVsJSCkqViCn8/pEu2hfjwVO+Gb3e+eTWjg3PcjeFKRItfKpKwHphQqbYmPQrlMk+op7pNNWPbsJIEthpFN/OQ==}
dependencies:
@ -7946,7 +7952,7 @@ packages:
/@types/prompts@2.4.4:
resolution: {integrity: sha512-p5N9uoTH76lLvSAaYSZtBCdEXzpOOufsRjnhjVSrZGXikVGHX9+cc9ERtHRV4hvBKHyZb1bg4K+56Bd2TqUn4A==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
kleur: 3.0.3
dev: true
@ -8046,7 +8052,7 @@ packages:
/@types/responselike@1.0.0:
resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: false
/@types/retry@0.12.0:
@ -8062,7 +8068,7 @@ packages:
/@types/sax@1.2.4:
resolution: {integrity: sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: false
/@types/scheduler@0.16.3:
@ -8076,7 +8082,7 @@ packages:
resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==}
dependencies:
'@types/mime': 1.3.2
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: false
/@types/serve-index@1.9.1:
@ -8089,13 +8095,13 @@ packages:
resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==}
dependencies:
'@types/mime': 3.0.1
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: false
/@types/sockjs@0.3.33:
resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: false
/@types/stack-utils@2.0.1:
@ -8117,7 +8123,7 @@ packages:
/@types/ws@8.5.4:
resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
dev: false
/@types/yargs-parser@21.0.0:
@ -11915,7 +11921,6 @@ packages:
'@esbuild/win32-arm64': 0.17.19
'@esbuild/win32-ia32': 0.17.19
'@esbuild/win32-x64': 0.17.19
dev: true
/esbuild@0.17.5:
resolution: {integrity: sha512-Bu6WLCc9NMsNoMJUjGl3yBzTjVLXdysMltxQWiLAypP+/vQrf+3L1Xe8fCXzxaECus2cEJ9M7pk4yKatEwQMqQ==}
@ -11945,6 +11950,7 @@ packages:
'@esbuild/win32-arm64': 0.17.5
'@esbuild/win32-ia32': 0.17.5
'@esbuild/win32-x64': 0.17.5
dev: true
/escalade@3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
@ -12382,7 +12388,7 @@ packages:
resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==}
engines: {node: '>= 0.8'}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
require-like: 0.1.2
dev: false
@ -14461,7 +14467,7 @@ packages:
'@jest/expect': 29.5.0
'@jest/test-result': 29.5.0
'@jest/types': 29.5.0
'@types/node': 20.2.3
'@types/node': 20.3.1
chalk: 4.1.2
co: 4.6.0
dedent: 0.7.0
@ -14498,7 +14504,7 @@ packages:
exit: 0.1.2
graceful-fs: 4.2.11
import-local: 3.1.0
jest-config: 29.5.0(@types/node@20.2.3)
jest-config: 29.5.0(@types/node@20.3.1)
jest-util: 29.5.0
jest-validate: 29.5.0
prompts: 2.4.2
@ -14509,7 +14515,7 @@ packages:
- ts-node
dev: true
/jest-config@29.5.0(@types/node@20.2.3):
/jest-config@29.5.0(@types/node@20.3.1):
resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
@ -14524,7 +14530,7 @@ packages:
'@babel/core': 7.21.8
'@jest/test-sequencer': 29.5.0
'@jest/types': 29.5.0
'@types/node': 20.2.3
'@types/node': 20.3.1
babel-jest: 29.5.0(@babel/core@7.21.8)
chalk: 4.1.2
ci-info: 3.8.0
@ -14606,7 +14612,7 @@ packages:
'@jest/environment': 29.5.0
'@jest/fake-timers': 29.5.0
'@jest/types': 29.5.0
'@types/node': 20.2.3
'@types/node': 20.3.1
jest-mock: 29.5.0
jest-util: 29.5.0
dev: true
@ -14622,7 +14628,7 @@ packages:
dependencies:
'@jest/types': 29.5.0
'@types/graceful-fs': 4.1.6
'@types/node': 20.2.3
'@types/node': 20.3.1
anymatch: 3.1.3
fb-watchman: 2.0.2
graceful-fs: 4.2.11
@ -14673,7 +14679,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/types': 29.5.0
'@types/node': 20.2.3
'@types/node': 20.3.1
jest-util: 29.5.0
dev: true
@ -14728,7 +14734,7 @@ packages:
'@jest/test-result': 29.5.0
'@jest/transform': 29.5.0
'@jest/types': 29.5.0
'@types/node': 20.2.3
'@types/node': 20.3.1
chalk: 4.1.2
emittery: 0.13.1
graceful-fs: 4.2.11
@ -14759,7 +14765,7 @@ packages:
'@jest/test-result': 29.5.0
'@jest/transform': 29.5.0
'@jest/types': 29.5.0
'@types/node': 20.2.3
'@types/node': 20.3.1
chalk: 4.1.2
cjs-module-lexer: 1.2.2
collect-v8-coverage: 1.0.1
@ -14814,7 +14820,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/types': 29.5.0
'@types/node': 20.2.3
'@types/node': 20.3.1
chalk: 4.1.2
ci-info: 3.8.0
graceful-fs: 4.2.11
@ -14838,7 +14844,7 @@ packages:
dependencies:
'@jest/test-result': 29.5.0
'@jest/types': 29.5.0
'@types/node': 20.2.3
'@types/node': 20.3.1
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.13.1
@ -14850,7 +14856,7 @@ packages:
resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
engines: {node: '>= 10.13.0'}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
merge-stream: 2.0.0
supports-color: 8.1.1
@ -14858,7 +14864,7 @@ packages:
resolution: {integrity: sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@types/node': 20.2.3
'@types/node': 20.3.1
jest-util: 29.5.0
merge-stream: 2.0.0
supports-color: 8.1.1
@ -20939,7 +20945,7 @@ packages:
optional: true
dependencies:
'@types/node': 20.2.3
esbuild: 0.17.5
esbuild: 0.17.19
postcss: 8.4.23
rollup: 3.23.0
optionalDependencies: