2
0

feat(editor): Add file upload input

This commit is contained in:
Baptiste Arnaud
2022-06-12 17:34:33 +02:00
parent d4c52d47b3
commit 75365a0d82
48 changed files with 1022 additions and 587 deletions

View File

@ -20,6 +20,7 @@ import {
SendEmailIcon,
StarIcon,
TextIcon,
UploadIcon,
WebhookIcon,
} from 'assets/icons'
import {
@ -68,6 +69,8 @@ export const BlockIcon = ({ type, ...props }: BlockIconProps) => {
return <CreditCardIcon color="orange.500" {...props} />
case InputBlockType.RATING:
return <StarIcon color="orange.500" {...props} />
case InputBlockType.FILE:
return <UploadIcon color="orange.500" {...props} />
case LogicBlockType.SET_VARIABLE:
return <EditIcon color="purple.500" {...props} />
case LogicBlockType.CONDITION:

View File

@ -43,6 +43,12 @@ export const BlockTypeLabel = ({ type }: Props): JSX.Element => {
return <Text>Payment</Text>
case InputBlockType.RATING:
return <Text>Rating</Text>
case InputBlockType.FILE:
return (
<Tooltip label="Upload Files">
<Text>File</Text>
</Tooltip>
)
case LogicBlockType.SET_VARIABLE:
return <Text>Set variable</Text>
case LogicBlockType.CONDITION:

View File

@ -19,12 +19,14 @@ type Props = {
isReadOnly?: boolean
debounceTimeout?: number
withVariableButton?: boolean
height?: string
onChange?: (value: string) => void
}
export const CodeEditor = ({
value,
lang,
onChange,
height = '250px',
withVariableButton = true,
isReadOnly = false,
debounceTimeout = 1000,
@ -92,7 +94,7 @@ export const CodeEditor = ({
extensions.push(
EditorView.theme({
'&': { maxHeight: '500px' },
'.cm-gutter,.cm-content': { minHeight: isReadOnly ? '0' : '250px' },
'.cm-gutter,.cm-content': { minHeight: isReadOnly ? '0' : height },
'.cm-scroller': { overflow: 'auto' },
})
)

View File

@ -19,6 +19,7 @@ import {
WithVariableContent,
} from './contents'
import { ConfigureContent } from './contents/ConfigureContent'
import { FileInputContent } from './contents/FileInputContent'
import { ImageBubbleContent } from './contents/ImageBubbleContent'
import { PaymentInputContent } from './contents/PaymentInputContent'
import { PlaceholderContent } from './contents/PlaceholderContent'
@ -80,6 +81,9 @@ export const BlockNodeContent = ({ block, indices }: Props): JSX.Element => {
case InputBlockType.RATING: {
return <RatingInputContent block={block} />
}
case InputBlockType.FILE: {
return <FileInputContent options={block.options} />
}
case LogicBlockType.SET_VARIABLE: {
return <SetVariableContent block={block} />
}

View File

@ -0,0 +1,12 @@
import { Text } from '@chakra-ui/react'
import { FileInputOptions } from 'models'
type Props = {
options: FileInputOptions
}
export const FileInputContent = ({ options: { isMultipleAllowed } }: Props) => (
<Text noOfLines={0} pr="6">
Collect {isMultipleAllowed ? 'files' : 'file'}
</Text>
)

View File

@ -29,6 +29,7 @@ import {
import { ChoiceInputSettingsBody } from './bodies/ChoiceInputSettingsBody'
import { CodeSettings } from './bodies/CodeSettings'
import { ConditionSettingsBody } from './bodies/ConditionSettingsBody'
import { FileInputSettings } from './bodies/FileInputSettings'
import { GoogleAnalyticsSettings } from './bodies/GoogleAnalyticsSettings'
import { GoogleSheetsSettingsBody } from './bodies/GoogleSheetsSettingsBody'
import { PaymentSettings } from './bodies/PaymentSettings'
@ -173,6 +174,14 @@ export const BlockSettings = ({
/>
)
}
case InputBlockType.FILE: {
return (
<FileInputSettings
options={block.options}
onOptionsChange={handleOptionsChange}
/>
)
}
case LogicBlockType.SET_VARIABLE: {
return (
<SetVariableSettings

View File

@ -0,0 +1,64 @@
import { FormLabel, Stack } from '@chakra-ui/react'
import { CodeEditor } from 'components/shared/CodeEditor'
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
import { Input } from 'components/shared/Textbox'
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
import { FileInputOptions, Variable } from 'models'
import React from 'react'
type Props = {
options: FileInputOptions
onOptionsChange: (options: FileInputOptions) => void
}
export const FileInputSettings = ({ options, onOptionsChange }: Props) => {
const handleButtonLabelChange = (button: string) =>
onOptionsChange({ ...options, labels: { ...options.labels, button } })
const handlePlaceholderLabelChange = (placeholder: string) =>
onOptionsChange({ ...options, labels: { ...options.labels, placeholder } })
const handleLongChange = (isMultipleAllowed: boolean) =>
onOptionsChange({ ...options, isMultipleAllowed })
const handleVariableChange = (variable?: Variable) =>
onOptionsChange({ ...options, variableId: variable?.id })
return (
<Stack spacing={4}>
<SwitchWithLabel
id="switch"
label="Allow multiple files?"
initialValue={options.isMultipleAllowed}
onCheckChange={handleLongChange}
/>
<Stack>
<FormLabel mb="0">Placeholder:</FormLabel>
<CodeEditor
lang="html"
onChange={handlePlaceholderLabelChange}
value={options.labels.placeholder}
height={'100px'}
withVariableButton={false}
/>
</Stack>
<Stack>
<FormLabel mb="0" htmlFor="button">
Button label:
</FormLabel>
<Input
id="button"
defaultValue={options.labels.button}
onChange={handleButtonLabelChange}
withVariableButton={false}
/>
</Stack>
<Stack>
<FormLabel mb="0" htmlFor="variable">
Save upload URL{options.isMultipleAllowed ? 's' : ''} in a variable:
</FormLabel>
<VariableSearchInput
initialVariableId={options.variableId}
onSelectVariable={handleVariableChange}
/>
</Stack>
</Stack>
)
}

View File

@ -171,7 +171,12 @@ export const TypebotHeader = () => {
<HStack right="40px" pos="absolute" display={['none', 'flex']}>
<CollaborationMenuButton />
{router.pathname.includes('/edit') && isNotDefined(rightPanel) && (
<Button onClick={handlePreviewClick}>Preview</Button>
<Button
onClick={handlePreviewClick}
isLoading={isNotDefined(typebot)}
>
Preview
</Button>
)}
<PublishButton />
</HStack>

View File

@ -1,6 +1,7 @@
import { Button, ButtonProps, chakra } from '@chakra-ui/react'
import React, { ChangeEvent, useState } from 'react'
import { compressFile, uploadFile } from 'services/utils'
import { compressFile } from 'services/utils'
import { uploadFiles } from 'utils'
type UploadButtonProps = {
filePath: string
@ -20,11 +21,15 @@ export const UploadButton = ({
if (!e.target?.files) return
setIsUploading(true)
const file = e.target.files[0]
const { url } = await uploadFile(
await compressFile(file),
filePath + (includeFileName ? `/${file.name}` : '')
)
if (url) onFileUploaded(url)
const urls = await uploadFiles({
files: [
{
file: await compressFile(file),
path: filePath + (includeFileName ? `/${file.name}` : ''),
},
],
})
if (urls.length) onFileUploaded(urls[0])
setIsUploading(false)
}

View File

@ -43,7 +43,6 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
const fetchTemplate = async (template: TemplateProps) => {
setSelectedTemplate(template)
const { data, error } = await sendRequest(`/templates/${template.fileName}`)
console.log(data, error)
if (error)
return showToast({ title: error.name, description: error.message })
setTypebot(data as Typebot)

View File

@ -132,7 +132,7 @@ export const SubmissionsContent = ({
return convertResultsToTableData(results, resultHeader)
}
const tableData: { [key: string]: string }[] = useMemo(
const tableData: { [key: string]: string | JSX.Element }[] = useMemo(
() =>
publishedTypebot ? convertResultsToTableData(results, resultHeader) : [],
// eslint-disable-next-line react-hooks/exhaustive-deps

View File

@ -37,6 +37,7 @@
"@udecode/plate-link": "^11.0.0",
"@udecode/plate-ui-link": "^11.0.0",
"@udecode/plate-ui-toolbar": "^11.0.0",
"aws-sdk": "^2.1152.0",
"bot-engine": "*",
"browser-image-compression": "^2.0.0",
"canvas-confetti": "^1.5.1",
@ -57,7 +58,6 @@
"kbar": "^0.1.0-beta.34",
"micro": "^9.3.4",
"micro-cors": "^0.1.1",
"minio": "^7.0.28",
"models": "*",
"next": "^12.1.6",
"next-auth": "4.3.4",
@ -85,12 +85,12 @@
},
"devDependencies": {
"@playwright/test": "^1.22.0",
"@types/aws-sdk": "^2.7.0",
"@types/canvas-confetti": "^1.4.2",
"@types/emoji-mart": "^3.0.9",
"@types/google-spreadsheet": "^3.2.1",
"@types/jsonwebtoken": "8.5.8",
"@types/micro-cors": "^0.1.2",
"@types/minio": "^7.0.13",
"@types/node": "^17.0.33",
"@types/nodemailer": "^6.4.4",
"@types/nprogress": "^0.2.0",

View File

@ -1,8 +1,7 @@
import { withSentry } from '@sentry/nextjs'
import { Client } from 'minio'
import { NextApiRequest, NextApiResponse } from 'next'
import { getSession } from 'next-auth/react'
import { badRequest, methodNotAllowed } from 'utils'
import { badRequest, generatePresignedUrl, methodNotAllowed } from 'utils'
const handler = async (
req: NextApiRequest,
@ -21,27 +20,14 @@ const handler = async (
!process.env.S3_ACCESS_KEY ||
!process.env.S3_SECRET_KEY
)
return res.send({
message:
'S3 not properly configured. Missing one of those variables: S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY',
})
const s3 = new Client({
endPoint: process.env.S3_ENDPOINT,
port: process.env.S3_PORT ? Number(process.env.S3_PORT) : undefined,
useSSL:
process.env.S3_SSL && process.env.S3_SSL === 'false' ? false : true,
accessKey: process.env.S3_ACCESS_KEY,
secretKey: process.env.S3_SECRET_KEY,
region: process.env.S3_REGION,
})
return badRequest(
res,
'S3 not properly configured. Missing one of those variables: S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY'
)
const filePath = req.query.filePath as string | undefined
if (!filePath) return badRequest(res)
const presignedUrl = await s3.presignedPutObject(
process.env.S3_BUCKET ?? 'typebot',
filePath
)
const fileType = req.query.fileType as string | undefined
if (!filePath || !fileType) return badRequest(res)
const presignedUrl = generatePresignedUrl({ fileType, filePath })
return res.status(200).send({ presignedUrl })
}

View File

@ -108,6 +108,6 @@ test.describe('Collaborator', () => {
await page.click('text=Group #1', { force: true })
await expect(page.locator('input[value="Group #1"]')).toBeHidden()
await page.goto(`/typebots/${typebotId}/results`)
await expect(page.locator('text="content199"')).toBeVisible()
await expect(page.locator('text="See logs" >> nth=10')).toBeVisible()
})
})

View File

@ -0,0 +1,51 @@
import test, { expect } from '@playwright/test'
import {
createTypebots,
parseDefaultGroupWithBlock,
} from '../../services/database'
import { defaultFileInputOptions, InputBlockType } from 'models'
import { typebotViewer } from '../../services/selectorUtils'
import cuid from 'cuid'
import path from 'path'
test('options should work', async ({ page }) => {
const typebotId = cuid()
await createTypebots([
{
id: typebotId,
...parseDefaultGroupWithBlock({
type: InputBlockType.FILE,
options: defaultFileInputOptions,
}),
},
])
await page.goto(`/typebots/${typebotId}/edit`)
await page.click('text=Preview')
await expect(
typebotViewer(page).locator(`text=Click to upload`)
).toBeVisible()
await typebotViewer(page)
.locator(`input[type="file"]`)
.setInputFiles([path.join(__dirname, '../../fixtures/avatar.jpg')])
await expect(typebotViewer(page).locator(`text=File uploaded`)).toBeVisible()
await page.click('text="Collect file"')
await page.click('text="Allow multiple files?"')
await page.fill('div[contenteditable=true]', '<strong>Upload now!!</strong>')
await page.fill('[value="Upload"]', 'Go')
await page.click('text="Restart"')
await expect(typebotViewer(page).locator(`text="Upload now!!"`)).toBeVisible()
await typebotViewer(page)
.locator(`input[type="file"]`)
.setInputFiles([
path.join(__dirname, '../../fixtures/avatar.jpg'),
path.join(__dirname, '../../fixtures/avatar.jpg'),
path.join(__dirname, '../../fixtures/avatar.jpg'),
])
await expect(typebotViewer(page).locator(`text="3"`)).toBeVisible()
await typebotViewer(page).locator('text="Go 3 files"').click()
await expect(
typebotViewer(page).locator(`text="3 files uploaded"`)
).toBeVisible()
})

View File

@ -66,7 +66,7 @@ test.describe('Send email block', () => {
'email1@gmail.com, email2@gmail.com'
)
await page.fill('[data-testid="subject-input"]', 'Email subject')
await page.click('text="Custom body?"')
await page.click('text="Custom content?"')
await page.fill('[data-testid="body-input"]', 'Here is my email')
await page.click('text=Preview')

View File

@ -36,8 +36,6 @@ test.describe.parallel('Settings page', () => {
).toBeVisible()
await page.click('text=Prefill input')
await page.click('text=Theme')
await page.waitForTimeout(1000)
await page.click('text=Settings')
await expect(
typebotViewer(page).locator(
`input[placeholder="${defaultTextInputOptions.labels.placeholder}"]`
@ -77,8 +75,12 @@ test.describe.parallel('Settings page', () => {
}
)
await page.goto(`/typebots/${typebotId}/settings`)
await expect(
typebotViewer(page).locator(
`input[placeholder="${defaultTextInputOptions.labels.placeholder}"]`
)
).toHaveValue('Baptiste')
await page.click('button:has-text("Metadata")')
await page.waitForTimeout(1000)
// Fav icon
const favIconImg = page.locator('img >> nth=0')

View File

@ -1,11 +1,17 @@
import { ResultWithAnswers, VariableWithValue, ResultHeaderCell } from 'models'
import {
ResultWithAnswers,
VariableWithValue,
ResultHeaderCell,
InputBlockType,
} from 'models'
import useSWRInfinite from 'swr/infinite'
import { stringify } from 'qs'
import { Answer } from 'db'
import { isDefined, isEmpty, sendRequest } from 'utils'
import { fetcher } from 'services/utils'
import { HStack, Text } from '@chakra-ui/react'
import { CodeIcon, CalendarIcon } from 'assets/icons'
import { HStack, Text, Wrap, WrapItem } from '@chakra-ui/react'
import { CodeIcon, CalendarIcon, FileIcon } from 'assets/icons'
import { Link } from '@chakra-ui/react'
import { BlockIcon } from 'components/editor/BlocksSideBar/BlockIcon'
const paginationLimit = 50
@ -147,28 +153,47 @@ const HeaderIcon = ({ header }: { header: ResultHeaderCell }) =>
export const convertResultsToTableData = (
results: ResultWithAnswers[] | undefined,
header: ResultHeaderCell[]
): { [key: string]: string }[] =>
headerCells: ResultHeaderCell[]
): { [key: string]: JSX.Element | string }[] =>
(results ?? []).map((result) => ({
'Submitted at': parseDateToReadable(result.createdAt),
...[...result.answers, ...result.variables].reduce<{
[key: string]: string
[key: string]: JSX.Element | string
}>((o, answerOrVariable) => {
if ('groupId' in answerOrVariable) {
const answer = answerOrVariable as Answer
const key = answer.variableId
? header.find((h) => h.variableId === answer.variableId)?.label
: header.find((h) => h.blockId === answer.blockId)?.label
if (!key) return o
const header = answer.variableId
? headerCells.find((h) => h.variableId === answer.variableId)
: headerCells.find((h) => h.blockId === answer.blockId)
if (!header || !header.blockId || !header.blockType) return o
return {
...o,
[key]: answer.content,
[header.label]: parseContent(answer.content, header.blockType),
}
}
const variable = answerOrVariable as VariableWithValue
if (isDefined(o[variable.id])) return o
const key = header.find((h) => h.variableId === variable.id)?.label
const key = headerCells.find((h) => h.variableId === variable.id)?.label
if (!key) return o
return { ...o, [key]: variable.value }
}, {}),
}))
const parseContent = (str: string, blockType: InputBlockType) =>
blockType === InputBlockType.FILE ? parseFileContent(str) : str
const parseFileContent = (str: string) => {
const fileNames = str.split(', ')
return (
<Wrap maxW="300px">
{fileNames.map((name) => (
<HStack as={WrapItem} key={name}>
<FileIcon />
<Link href={name} isExternal color="blue.500">
{name.split('/').pop()}
</Link>
</HStack>
))}
</Wrap>
)
}

View File

@ -39,6 +39,7 @@ import {
ConditionBlock,
defaultPaymentInputOptions,
defaultRatingInputOptions,
defaultFileInputOptions,
} from 'models'
import { Typebot } from 'models'
import useSWR from 'swr'
@ -329,6 +330,8 @@ const parseDefaultBlockOptions = (type: BlockWithOptionsType): BlockOptions => {
return defaultPaymentInputOptions
case InputBlockType.RATING:
return defaultRatingInputOptions
case InputBlockType.FILE:
return defaultFileInputOptions
case LogicBlockType.SET_VARIABLE:
return defaultSetVariablesOptions
case LogicBlockType.REDIRECT:

View File

@ -1,7 +1,6 @@
import imageCompression from 'browser-image-compression'
import { Parser } from 'htmlparser2'
import { Block, Typebot } from 'models'
import { sendRequest } from 'utils'
export const fetcher = async (input: RequestInfo, init?: RequestInit) => {
const res = await fetch(input, init)
@ -37,26 +36,6 @@ export const toKebabCase = (value: string) => {
return matched.map((x) => x.toLowerCase()).join('-')
}
export const uploadFile = async (file: File, filePath: string) => {
const { data } = await sendRequest<{ presignedUrl: string }>(
`/api/storage/upload-url?filePath=${encodeURIComponent(filePath)}`
)
if (!data?.presignedUrl)
return {
url: null,
}
await fetch(data.presignedUrl, {
method: 'PUT',
body: file,
})
return {
url: data.presignedUrl.split('?')[0],
}
}
export const compressFile = async (file: File) => {
const options = {
maxSizeMB: 0.5,

View File

@ -287,6 +287,25 @@ Used when executing a Google Sheets block. Make sure to set the required scopes
</p></details>
<details><summary><h3>S3 Storage (File upload input)</h3></summary>
<p>
Used for the file upload input. It can be any S3 compatible object storage service (Minio, Digital Oceans Space, AWS S3...)
| Parameter | Default | Description |
| ------------- | ------- | -------------------------------------------------------------- |
| S3_ACCESS_KEY | -- | S3 access key. Also used to check if upload feature is enabled |
| S3_SECRET_KEY | -- | S3 secret key. |
| S3_BUCKET | typebot | Name of the bucket where assets will be uploaded in. |
| S3_PORT | -- | S3 Host port number |
| S3_ENDPOINT | -- | S3 secret key. |
| S3_SSL | true | Use SSL when establishing the connection. |
| S3_REGION | -- | S3 region. |
Your bucket must have the following policy that tells S3 to allow public read when an object is located under the public folder:
</p></details>
:::note
If you're self-hosting Typebot, [sponsoring me](https://github.com/sponsors/baptisteArno) is a great way to give back to the community and to contribute to the long-term sustainability of the project.

View File

@ -2,5 +2,12 @@ ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6 #256-bits secret (can be gene
NEXT_PUBLIC_VIEWER_URL=http://localhost:3001
DATABASE_URL=postgresql://postgres:typebot@localhost:5432/typebot
S3_ACCESS_KEY=minio
S3_SECRET_KEY=minio123
S3_BUCKET=typebot
S3_PORT=9000
S3_ENDPOINT=localhost
S3_SSL=false
# For more configuration options check out:
# https://docs.typebot.io/self-hosting/configuration
# https://docs.typebot.io/self-hosting/configuration

View File

@ -13,6 +13,7 @@
},
"dependencies": {
"@sentry/nextjs": "^6.19.7",
"aws-sdk": "^2.1152.0",
"bot-engine": "*",
"cors": "^2.8.5",
"cuid": "^2.1.8",
@ -31,6 +32,7 @@
},
"devDependencies": {
"@playwright/test": "^1.22.0",
"@types/aws-sdk": "^2.7.0",
"@types/cors": "^2.8.12",
"@types/google-spreadsheet": "^3.2.1",
"@types/node": "^17.0.33",

View File

@ -0,0 +1,30 @@
import { withSentry } from '@sentry/nextjs'
import { NextApiRequest, NextApiResponse } from 'next'
import { badRequest, generatePresignedUrl, methodNotAllowed } from 'utils'
const handler = async (
req: NextApiRequest,
res: NextApiResponse
): Promise<void> => {
res.setHeader('Access-Control-Allow-Origin', '*')
if (req.method === 'GET') {
if (
!process.env.S3_ENDPOINT ||
!process.env.S3_ACCESS_KEY ||
!process.env.S3_SECRET_KEY
)
return badRequest(
res,
'S3 not properly configured. Missing one of those variables: S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY'
)
const filePath = req.query.filePath as string | undefined
const fileType = req.query.fileType as string | undefined
if (!filePath || !fileType) return badRequest(res)
const presignedUrl = generatePresignedUrl({ fileType, filePath })
return res.status(200).send({ presignedUrl })
}
return methodNotAllowed(res)
}
export default withSentry(handler)

View File

@ -32,7 +32,6 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
})),
]
}, [])
console.log({ blocks: emptyWebhookBlocks })
return res.send({ blocks: emptyWebhookBlocks })
}
return methodNotAllowed(res)

View File

@ -0,0 +1,131 @@
{
"id": "cl45ojo7z01383q1av699t0qj",
"createdAt": "2022-06-08T14:22:14.879Z",
"updatedAt": "2022-06-08T16:19:32.893Z",
"icon": null,
"name": "My typebot",
"publishedTypebotId": "cl45ol3j8000f2e6gcifqf21t",
"folderId": null,
"groups": [
{
"id": "cl45ojo7y00013q1aaysi2o6i",
"blocks": [
{
"id": "cl45ojo7y00023q1aavrwd411",
"type": "start",
"label": "Start",
"groupId": "cl45ojo7y00013q1aaysi2o6i",
"outgoingEdgeId": "cl45ojxvc00082e6gw1xqnxpp"
}
],
"title": "Start",
"graphCoordinates": { "x": 0, "y": 0 }
},
{
"id": "cl45ojrrd00062e6g17tuu9t0",
"blocks": [
{
"id": "cl45ojrre00072e6gk91592pj",
"type": "text",
"groupId": "cl45ojrrd00062e6g17tuu9t0",
"content": {
"html": "<div>Hey there, upload please</div>",
"richText": [
{
"type": "p",
"children": [{ "text": "Hey there, upload please" }]
}
],
"plainText": "Hey there, upload please"
}
},
{
"id": "cl45ojzs300092e6gkno525c4",
"type": "file input",
"groupId": "cl45ojrrd00062e6g17tuu9t0",
"options": {
"labels": {
"button": "Upload",
"placeholder": "<strong>\n Click to upload\n </strong> or drag and drop<br>\n (size limit: 10MB)"
},
"variableId": "vcl45ok77i000a2e6g79ye53a2",
"isMultipleAllowed": true
},
"outgoingEdgeId": "cl45okfgz000d2e6g7z3wnqgq"
}
],
"title": "Group #1",
"graphCoordinates": { "x": 416, "y": 98 }
},
{
"id": "cl45ok963000b2e6g2ky0wkvx",
"blocks": [
{
"id": "cl45ok963000c2e6g9snvbhw4",
"type": "text",
"groupId": "cl45ok963000b2e6g2ky0wkvx",
"content": {
"html": "<div>Thank you!</div>",
"richText": [
{ "type": "p", "children": [{ "text": "Thank you!" }] }
],
"plainText": "Thank you!"
}
}
],
"title": "Group #2",
"graphCoordinates": { "x": 863, "y": 249 }
}
],
"variables": [{ "id": "vcl45ok77i000a2e6g79ye53a2", "name": "Files" }],
"edges": [
{
"id": "cl45ojxvc00082e6gw1xqnxpp",
"to": { "groupId": "cl45ojrrd00062e6g17tuu9t0" },
"from": {
"blockId": "cl45ojo7y00023q1aavrwd411",
"groupId": "cl45ojo7y00013q1aaysi2o6i"
}
},
{
"id": "cl45okfgz000d2e6g7z3wnqgq",
"to": { "groupId": "cl45ok963000b2e6g2ky0wkvx" },
"from": {
"blockId": "cl45ojzs300092e6gkno525c4",
"groupId": "cl45ojrrd00062e6g17tuu9t0"
}
}
],
"theme": {
"chat": {
"inputs": {
"color": "#303235",
"backgroundColor": "#FFFFFF",
"placeholderColor": "#9095A0"
},
"buttons": { "color": "#FFFFFF", "backgroundColor": "#0042DA" },
"hostAvatar": {
"url": "https://avatars.githubusercontent.com/u/16015833?v=4",
"isEnabled": true
},
"hostBubbles": { "color": "#303235", "backgroundColor": "#F7F8FF" },
"guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" }
},
"general": { "font": "Open Sans", "background": { "type": "None" } }
},
"settings": {
"general": {
"isBrandingEnabled": true,
"isInputPrefillEnabled": true,
"isHideQueryParamsEnabled": true,
"isNewResultOnRefreshEnabled": false
},
"metadata": {
"description": "Build beautiful conversational forms and embed them directly in your applications without a line of code. Triple your response rate and collect answers that has more value compared to a traditional form."
},
"typingEmulation": { "speed": 300, "enabled": true, "maxDelay": 1.5 }
},
"publicId": "my-typebot-699t0qj",
"customDomain": null,
"workspaceId": "proWorkspace"
}

View File

@ -8,7 +8,7 @@ import {
Typebot,
Webhook,
} from 'models'
import { PrismaClient, WorkspaceRole } from 'db'
import { Plan, PrismaClient, WorkspaceRole } from 'db'
import { readFileSync } from 'fs'
import { encrypt } from 'utils'
@ -46,6 +46,7 @@ export const createUser = () =>
create: {
id: proWorkspaceId,
name: 'Pro workspace',
plan: Plan.PRO,
},
},
},

View File

@ -0,0 +1,39 @@
import test, { expect } from '@playwright/test'
import cuid from 'cuid'
import path from 'path'
import { typebotViewer } from '../services/selectorUtils'
import { importTypebotInDatabase } from '../services/database'
test('should work as expected', async ({ page, context }) => {
const typebotId = cuid()
await importTypebotInDatabase(
path.join(__dirname, '../fixtures/typebots/fileUpload.json'),
{ id: typebotId, publicId: `${typebotId}-public` }
)
await page.goto(`/${typebotId}-public`)
await typebotViewer(page)
.locator(`input[type="file"]`)
.setInputFiles([
path.join(__dirname, '../fixtures/typebots/api.json'),
path.join(__dirname, '../fixtures/typebots/fileUpload.json'),
path.join(__dirname, '../fixtures/typebots/hugeGroup.json'),
])
await expect(typebotViewer(page).locator(`text="3"`)).toBeVisible()
await typebotViewer(page).locator('text="Upload 3 files"').click()
await expect(
typebotViewer(page).locator(`text="3 files uploaded"`)
).toBeVisible()
await page.goto(`http://localhost:3000/typebots/${typebotId}/results`)
await expect(page.locator('text="api.json"')).toHaveAttribute(
'href',
/.+\/api\.json/
)
await expect(page.locator('text="fileUpload.json"')).toHaveAttribute(
'href',
/.+\/fileUpload\.json/
)
await expect(page.locator('text="api.json"')).toHaveAttribute(
'href',
/.+\/api\.json/
)
})

View File

@ -218,3 +218,26 @@ textarea {
.rating-icon-container:active svg {
filter: brightness(0.75);
}
.upload-progress-bar {
background-color: var(--typebot-button-bg-color);
}
.total-files-indicator {
background-color: var(--typebot-button-bg-color);
color: var(--typebot-button-color);
font-size: 10px;
}
.typebot-upload-input {
transition: border-color 100ms ease-out;
}
.typebot-upload-input.dragging-over {
border-color: var(--typebot-button-bg-color);
}
.secondary-button {
background-color: var(--typebot-host-bubble-bg-color);
color: var(--typebot-host-bubble-color);
}

View File

@ -11,6 +11,9 @@ import { parseVariables } from '../../../services/variable'
import { isInputValid } from 'services/inputs'
import { PaymentForm } from './inputs/PaymentForm'
import { RatingForm } from './inputs/RatingForm'
import { FileUploadForm } from './inputs/FileUploadForm'
export type InputSubmitContent = { label?: string; value: string }
export const InputChatBlock = ({
block,
@ -21,7 +24,10 @@ export const InputChatBlock = ({
block: InputBlock
hasGuestAvatar: boolean
hasAvatar: boolean
onTransitionEnd: (answerContent?: string, isRetry?: boolean) => void
onTransitionEnd: (
answerContent?: InputSubmitContent,
isRetry?: boolean
) => void
}) => {
const { typebot } = useTypebot()
const { addAnswer } = useAnswers()
@ -34,17 +40,17 @@ export const InputChatBlock = ({
? variableId && typebot.variables.find(byId(variableId))?.value
: undefined
const handleSubmit = async (content: string) => {
setAnswer(content)
const isRetry = !isInputValid(content, block.type)
const handleSubmit = async ({ label, value }: InputSubmitContent) => {
setAnswer(label ?? value)
const isRetry = !isInputValid(value, block.type)
if (!isRetry && addAnswer)
await addAnswer({
blockId: block.id,
groupId: block.groupId,
content,
content: value,
variableId: variableId ?? null,
})
if (!isEditting) onTransitionEnd(content, isRetry)
if (!isEditting) onTransitionEnd({ label, value }, isRetry)
setIsEditting(false)
}
@ -87,7 +93,7 @@ const Input = ({
hasGuestAvatar,
}: {
block: InputBlock
onSubmit: (value: string) => void
onSubmit: (value: InputSubmitContent) => void
defaultValue?: string
hasGuestAvatar: boolean
}) => {
@ -113,10 +119,14 @@ const Input = ({
return (
<PaymentForm
options={block.options}
onSuccess={() => onSubmit(block.options.labels.success ?? 'Success')}
onSuccess={() =>
onSubmit({ value: block.options.labels.success ?? 'Success' })
}
/>
)
case InputBlockType.RATING:
return <RatingForm block={block} onSubmit={onSubmit} />
case InputBlockType.FILE:
return <FileUploadForm block={block} onSubmit={onSubmit} />
}
}

View File

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

View File

@ -1,11 +1,12 @@
import { useAnswers } from 'contexts/AnswersContext'
import { ChoiceInputBlock } from 'models'
import React, { useState } from 'react'
import { InputSubmitContent } from '../InputChatBlock'
import { SendButton } from './SendButton'
type ChoiceFormProps = {
block: ChoiceInputBlock
onSubmit: (value: string) => void
onSubmit: (value: InputSubmitContent) => void
}
export const ChoiceForm = ({ block, onSubmit }: ChoiceFormProps) => {
@ -15,7 +16,7 @@ export const ChoiceForm = ({ block, onSubmit }: ChoiceFormProps) => {
const handleClick = (itemIndex: number) => (e: React.MouseEvent) => {
e.preventDefault()
if (block.options?.isMultipleChoice) toggleSelectedItemIndex(itemIndex)
else onSubmit(block.items[itemIndex].content ?? '')
else onSubmit({ value: block.items[itemIndex].content ?? '' })
}
const toggleSelectedItemIndex = (itemIndex: number) => {
@ -29,11 +30,11 @@ export const ChoiceForm = ({ block, onSubmit }: ChoiceFormProps) => {
}
const handleSubmit = () =>
onSubmit(
selectedIndices
onSubmit({
value: selectedIndices
.map((itemIndex) => block.items[itemIndex].content)
.join(', ')
)
.join(', '),
})
const isUniqueFirstButton =
resultValues &&

View File

@ -1,9 +1,10 @@
import { DateInputOptions } from 'models'
import React, { useState } from 'react'
import { InputSubmitContent } from '../InputChatBlock'
import { SendButton } from './SendButton'
type DateInputProps = {
onSubmit: (inputValue: `${string} to ${string}` | string) => void
onSubmit: (inputValue: InputSubmitContent) => void
options?: DateInputOptions
}
@ -23,9 +24,11 @@ export const DateForm = ({
onSubmit={(e) => {
if (inputValues.from === '' && inputValues.to === '') return
e.preventDefault()
onSubmit(
`${inputValues.from}${isRange ? ` to ${inputValues.to}` : ''}`
)
onSubmit({
value: `${inputValues.from}${
isRange ? ` to ${inputValues.to}` : ''
}`,
})
}}
>
<div className="flex flex-col">

View File

@ -0,0 +1,236 @@
import { useAnswers } from 'contexts/AnswersContext'
import { useTypebot } from 'contexts/TypebotContext'
import { FileInputBlock } from 'models'
import React, { ChangeEvent, FormEvent, useState, DragEvent } from 'react'
import { uploadFiles } from 'utils'
import { InputSubmitContent } from '../InputChatBlock'
import { SendButton, Spinner } from './SendButton'
type Props = {
block: FileInputBlock
onSubmit: (url: InputSubmitContent) => void
}
const tenMB = 10 * 1024 * 1024
export const FileUploadForm = ({
block: {
id,
options: { isMultipleAllowed, labels },
},
onSubmit,
}: Props) => {
const { isPreview } = useTypebot()
const { resultId } = useAnswers()
const [selectedFiles, setSelectedFiles] = useState<File[]>([])
const [isUploading, setIsUploading] = useState(false)
const [uploadProgressPercent, setUploadProgressPercent] = useState(20)
const [isDraggingOver, setIsDraggingOver] = useState(false)
const [errorMessage, setErrorMessage] = useState<string>()
const handleFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
if (!e.target.files) return
onNewFiles(e.target.files)
}
const onNewFiles = (files: FileList) => {
setErrorMessage(undefined)
const newFiles = Array.from(files)
if (newFiles.some((file) => file.size > tenMB))
return setErrorMessage('A file is larger than 10MB')
if (!isMultipleAllowed && files) return startSingleFileUpload(newFiles[0])
setSelectedFiles([...selectedFiles, ...newFiles])
}
const handleSubmit = async (e: FormEvent) => {
e.preventDefault()
if (selectedFiles.length === 0) return
startFilesUpload(selectedFiles)
}
const startSingleFileUpload = async (file: File) => {
if (isPreview)
return onSubmit({
label: `File uploaded`,
value: 'http://fake-upload-url.com',
})
setIsUploading(true)
const urls = await uploadFiles({
files: [
{
file,
path: `public/results/${resultId}/${id}`,
},
],
})
setIsUploading(false)
if (urls.length) return onSubmit({ label: `File uploaded`, value: urls[0] })
setErrorMessage('An error occured while uploading the file')
}
const startFilesUpload = async (files: File[]) => {
if (isPreview)
return onSubmit({
label: `${files.length} file${files.length > 1 ? 's' : ''} uploaded`,
value: files
.map((_, idx) => `http://fake-upload-url.com/${idx}`)
.join(', '),
})
setIsUploading(true)
const urls = await uploadFiles({
files: files.map((file) => ({
file: file,
path: `public/results/${resultId}/${id}/${file.name}`,
})),
onUploadProgress: setUploadProgressPercent,
})
if (urls.length !== files.length)
return setErrorMessage('An error occured while uploading the files')
onSubmit({
label: `${urls.length} file${urls.length > 1 ? 's' : ''} uploaded`,
value: urls.join(', '),
})
}
const handleDragOver = (e: DragEvent) => {
e.preventDefault()
setIsDraggingOver(true)
}
const handleDragLeave = () => setIsDraggingOver(false)
const handleDropFile = (e: DragEvent<HTMLLabelElement>) => {
e.preventDefault()
e.stopPropagation()
if (!e.dataTransfer.files) return
onNewFiles(e.dataTransfer.files)
}
const clearFiles = () => setSelectedFiles([])
return (
<form className="flex flex-col w-full" onSubmit={handleSubmit}>
<label
htmlFor="dropzone-file"
className={
'typebot-upload-input py-6 flex flex-col justify-center items-center w-full bg-gray-50 rounded-lg border-2 border-gray-300 border-dashed cursor-pointer dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600 px-8 mb-2 ' +
(isDraggingOver ? 'dragging-over' : '')
}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDropFile}
>
{isUploading && uploadProgressPercent ? (
<>
{selectedFiles.length === 1 ? (
<Spinner />
) : (
<div className="w-full bg-gray-200 rounded-full h-2.5 dark:bg-gray-700">
<div
className="upload-progress-bar h-2.5 rounded-full"
style={{
width: `${uploadProgressPercent}%`,
transition: 'width 150ms cubic-bezier(0.4, 0, 0.2, 1)',
}}
/>
</div>
)}
</>
) : (
<>
<div className="flex flex-col justify-center items-center">
{selectedFiles.length ? (
<span className="relative">
<FileIcon />
<div
className="total-files-indicator flex items-center justify-center absolute -right-1 rounded-full px-1 h-4"
style={{ bottom: '5px' }}
>
{selectedFiles.length}
</div>
</span>
) : (
<UploadIcon />
)}
<p
className="text-sm text-gray-500 dark:text-gray-400 text-center"
dangerouslySetInnerHTML={{ __html: labels.placeholder }}
/>
</div>
<input
id="dropzone-file"
type="file"
className="hidden"
multiple={isMultipleAllowed}
onChange={handleFileChange}
/>
</>
)}
</label>
{isMultipleAllowed && selectedFiles.length > 0 && !isUploading && (
<div className="flex justify-end">
<div className="flex">
{selectedFiles.length && (
<button
className={
'secondary-button py-2 px-4 justify-center font-semibold rounded-md text-white focus:outline-none flex items-center disabled:opacity-50 disabled:cursor-not-allowed disabled:brightness-100 transition-all filter hover:brightness-90 active:brightness-75 mr-2'
}
onClick={clearFiles}
>
Clear
</button>
)}
<SendButton
type="submit"
label={
labels.button
? `${labels.button} ${selectedFiles.length} file${
selectedFiles.length > 1 ? 's' : ''
}`
: 'Upload'
}
disableIcon
/>
</div>
</div>
)}
{errorMessage && <p className="text-red-500 text-sm">{errorMessage}</p>}
</form>
)
}
const UploadIcon = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="mb-3"
>
<polyline points="16 16 12 12 8 16"></polyline>
<line x1="12" y1="12" x2="12" y2="21"></line>
<path d="M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3"></path>
<polyline points="16 16 12 12 8 16"></polyline>
</svg>
)
const FileIcon = () => (
<svg
className="mb-3"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path>
<polyline points="13 2 13 9 20 9"></polyline>
</svg>
)

View File

@ -1,11 +1,12 @@
import { RatingInputOptions, RatingInputBlock } from 'models'
import React, { FormEvent, useRef, useState } from 'react'
import { isDefined, isEmpty, isNotDefined } from 'utils'
import { InputSubmitContent } from '../InputChatBlock'
import { SendButton } from './SendButton'
type Props = {
block: RatingInputBlock
onSubmit: (value: string) => void
onSubmit: (value: InputSubmitContent) => void
}
export const RatingForm = ({ block, onSubmit }: Props) => {
@ -15,7 +16,7 @@ export const RatingForm = ({ block, onSubmit }: Props) => {
const handleSubmit = (e: FormEvent) => {
e.preventDefault()
if (isNotDefined(rating)) return
onSubmit(rating.toString())
onSubmit({ value: rating.toString() })
}
const handleClick = (rating: number) => setRating(rating)

View File

@ -43,6 +43,7 @@ export const Spinner = (props: SVGProps<SVGSVGElement>) => (
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
data-testid="loading-spinner"
>
<circle
className="opacity-25"

View File

@ -7,6 +7,7 @@ import {
UrlInputBlock,
} from 'models'
import React, { FormEvent, useState } from 'react'
import { InputSubmitContent } from '../../InputChatBlock'
import { SendButton } from '../SendButton'
import { TextInput } from './TextInputContent'
@ -17,7 +18,7 @@ type TextFormProps = {
| NumberInputBlock
| UrlInputBlock
| PhoneNumberInputBlock
onSubmit: (value: string) => void
onSubmit: (value: InputSubmitContent) => void
defaultValue?: string
hasGuestAvatar: boolean
}
@ -44,7 +45,7 @@ export const TextForm = ({
const handleSubmit = (e: FormEvent) => {
e.preventDefault()
if (inputValue === '') return
onSubmit(inputValue)
onSubmit({ value: inputValue })
}
return (

View File

@ -23,10 +23,10 @@ import {
PublicTypebot,
Block,
} from 'models'
import { HostBubble } from './ChatBlock/bubbles/HostBubble'
import { getLastChatBlockType } from '../../services/chat'
import { useChat } from 'contexts/ChatContext'
import { InputChatBlock } from './ChatBlock'
import { getLastChatBlockType } from 'services/chat'
import { HostBubble } from './ChatBlock/bubbles/HostBubble'
import { InputChatBlock, InputSubmitContent } from './ChatBlock/InputChatBlock'
type ChatGroupProps = {
blocks: Block[]
@ -162,7 +162,10 @@ export const ChatGroup = ({
onGroupEnd({ edgeId: currentBlock.outgoingEdgeId })
}
const displayNextBlock = (answerContent?: string, isRetry?: boolean) => {
const displayNextBlock = (
answerContent?: InputSubmitContent,
isRetry?: boolean
) => {
scroll()
const currentBlock = [...processedBlocks].pop()
if (currentBlock) {
@ -175,13 +178,16 @@ export const ChatGroup = ({
currentBlock.options?.variableId &&
answerContent
) {
updateVariableValue(currentBlock.options.variableId, answerContent)
updateVariableValue(
currentBlock.options.variableId,
answerContent.value
)
}
const isSingleChoiceBlock =
isChoiceInput(currentBlock) && !currentBlock.options.isMultipleChoice
if (isSingleChoiceBlock) {
const nextEdgeId = currentBlock.items.find(
(i) => i.content === answerContent
(i) => i.content === answerContent?.value
)?.outgoingEdgeId
if (nextEdgeId) return onGroupEnd({ edgeId: nextEdgeId })
}
@ -224,7 +230,10 @@ type Props = {
hostAvatar: { isEnabled: boolean; src?: string }
hasGuestAvatar: boolean
keepShowingHostAvatar: boolean
onDisplayNextBlock: (answerContent?: string, isRetry?: boolean) => void
onDisplayNextBlock: (
answerContent?: InputSubmitContent,
isRetry?: boolean
) => void
}
const ChatChunks = ({
displayChunk: { bubbles, input },

View File

@ -20,14 +20,6 @@ export const defaultChoiceInputOptions: ChoiceInputOptions = {
isMultipleChoice: false,
}
export const choiceInputSchema = blockBaseSchema.and(
z.object({
type: z.enum([InputBlockType.CHOICE]),
items: z.array(z.any()),
options: choiceInputOptionsSchema,
})
)
export const buttonItemSchema = itemBaseSchema.and(
z.object({
type: z.literal(ItemType.BUTTON),
@ -35,6 +27,14 @@ export const buttonItemSchema = itemBaseSchema.and(
})
)
export const choiceInputSchema = blockBaseSchema.and(
z.object({
type: z.enum([InputBlockType.CHOICE]),
items: z.array(buttonItemSchema),
options: choiceInputOptionsSchema,
})
)
export type ButtonItem = z.infer<typeof buttonItemSchema>
export type ChoiceInputBlock = z.infer<typeof choiceInputSchema>
export type ChoiceInputOptions = z.infer<typeof choiceInputOptionsSchema>

View File

@ -0,0 +1,33 @@
import { z } from 'zod'
import { InputBlockType, optionBaseSchema, blockBaseSchema } from '../shared'
export const fileInputOptionsSchema = optionBaseSchema.and(
z.object({
isMultipleAllowed: z.boolean(),
labels: z.object({
placeholder: z.string(),
button: z.string(),
}),
})
)
export const fileInputStepSchema = blockBaseSchema.and(
z.object({
type: z.literal(InputBlockType.FILE),
options: fileInputOptionsSchema,
})
)
export const defaultFileInputOptions: FileInputOptions = {
isMultipleAllowed: false,
labels: {
placeholder: `<strong>
Click to upload
</strong> or drag and drop<br>
(size limit: 10MB)`,
button: 'Upload',
},
}
export type FileInputBlock = z.infer<typeof fileInputStepSchema>
export type FileInputOptions = z.infer<typeof fileInputOptionsSchema>

View File

@ -8,3 +8,4 @@ export * from './choice'
export * from './payment'
export * from './phone'
export * from './rating'
export * from './file'

View File

@ -11,6 +11,7 @@ import {
} from './phone'
import { ratingInputOptionsSchema, ratingInputBlockSchema } from './rating'
import { textInputOptionsSchema, textInputSchema } from './text'
import { fileInputOptionsSchema, fileInputStepSchema } from './file'
import { urlInputOptionsSchema, urlInputSchema } from './url'
export type OptionBase = z.infer<typeof optionBaseSchema>
@ -24,6 +25,7 @@ export const inputBlockOptionsSchema = textInputOptionsSchema
.or(dateInputOptionsSchema)
.or(paymentInputOptionsSchema)
.or(ratingInputOptionsSchema)
.or(fileInputOptionsSchema)
export const inputBlockSchema = textInputSchema
.or(numberInputSchema)
@ -34,6 +36,7 @@ export const inputBlockSchema = textInputSchema
.or(choiceInputSchema)
.or(paymentInputSchema)
.or(ratingInputBlockSchema)
.or(fileInputStepSchema)
export type InputBlock = z.infer<typeof inputBlockSchema>
export type InputBlockOptions = z.infer<typeof inputBlockOptionsSchema>

View File

@ -40,6 +40,7 @@ export enum InputBlockType {
CHOICE = 'choice input',
PAYMENT = 'payment input',
RATING = 'rating input',
FILE = 'file input',
}
export enum LogicBlockType {

View File

@ -10,6 +10,7 @@
"@rollup/plugin-commonjs": "^22.0.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"@rollup/plugin-typescript": "^8.3.2",
"@types/aws-sdk": "^2.7.0",
"rollup": "^2.72.1",
"rollup-plugin-dts": "^4.2.1",
"rollup-plugin-peer-deps-external": "^2.2.4",
@ -17,12 +18,15 @@
"typescript": "^4.6.4"
},
"dependencies": {
"aws-sdk": "^2.1152.0",
"models": "*",
"next": "^12.1.6"
},
"peerDependencies": {
"next": "^12.1.6",
"models": "*"
"aws-sdk": "^2.1152.0",
"@types/aws-sdk": "^2.7.0",
"models": "*",
"next": "^12.1.6"
},
"scripts": {
"build": "yarn rollup -c",

View File

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

View File

@ -0,0 +1,51 @@
import { config, Endpoint, S3 } from 'aws-sdk'
type GeneratePresignedUrlProps = {
filePath: string
fileType: string
}
const tenMB = 10485760
const oneHundredAndTwentySeconds = 120
export const generatePresignedUrl = ({
filePath,
fileType,
}: GeneratePresignedUrlProps): S3.PresignedPost => {
if (
!process.env.S3_ENDPOINT ||
!process.env.S3_ACCESS_KEY ||
!process.env.S3_SECRET_KEY
)
throw new Error(
'S3 not properly configured. Missing one of those variables: S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY'
)
const sslEnabled =
process.env.S3_SSL && process.env.S3_SSL === 'false' ? false : true
config.update({
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_SECRET_KEY,
region: process.env.S3_REGION,
sslEnabled,
})
const protocol = sslEnabled ? 'https' : 'http'
const s3 = new S3({
endpoint: new Endpoint(
`${protocol}://${process.env.S3_ENDPOINT}${
process.env.S3_PORT ? `:${process.env.S3_PORT}` : ''
}`
),
})
const presignedUrl = s3.createPresignedPost({
Bucket: process.env.S3_BUCKET ?? 'typebot',
Fields: {
key: filePath,
'Content-Type': fileType,
},
Expires: oneHundredAndTwentySeconds,
Conditions: [['content-length-range', 0, tenMB]],
})
return presignedUrl
}

View File

@ -24,7 +24,7 @@ export const sendRequest = async <ResponseData>(
| {
url: string
method: string
body?: Record<string, unknown>
body?: Record<string, unknown> | FormData
}
| string
): Promise<{ data?: ResponseData; error?: Error }> => {
@ -191,3 +191,53 @@ export const generateId = (idDesiredLength: number): string => {
})
.join('')
}
type UploadFileProps = {
files: {
file: File
path: string
}[]
onUploadProgress?: (percent: number) => void
}
type UrlList = string[]
export const uploadFiles = async ({
files,
onUploadProgress,
}: UploadFileProps): Promise<UrlList> => {
const requests = files.map(async ({ file, path }) => {
const { data } = await sendRequest<{
presignedUrl: { url: string; fields: any }
}>(
`/api/storage/upload-url?filePath=${encodeURIComponent(path)}&fileType=${
file.type
}`
)
if (!data?.presignedUrl) return null
const { url, fields } = data.presignedUrl
const formData = new FormData()
Object.entries({ ...fields, file }).forEach(([key, value]) => {
formData.append(key, value as string | Blob)
})
const upload = await fetch(url, {
method: 'POST',
body: formData,
})
if (!upload.ok) return
return `${url.split('?')[0]}/${path}`
})
const urls = []
let i = 0
for (const request of requests) {
i += 1
const url = await request
onUploadProgress && onUploadProgress((i / requests.length) * 100)
if (!url) continue
urls.push(url)
}
return urls
}

566
yarn.lock
View File

@ -3897,6 +3897,13 @@
resolved "https://registry.yarnpkg.com/@types/aos/-/aos-3.0.4.tgz#fd0dce430f18d118081aaced2c79c30617b15818"
integrity sha512-mna6Jd6bdK1NpwarLopGvXOgUoCfj0470IwLxuVOFDElTGI0JTd7xSGQ0AjbAEnHErC/b3fA9t2uB3IXVKmckA==
"@types/aws-sdk@^2.7.0":
version "2.7.0"
resolved "https://registry.yarnpkg.com/@types/aws-sdk/-/aws-sdk-2.7.0.tgz#83588b3d14ebdca1d4ce5e023387577568ce82f3"
integrity sha512-bF6brnwPN9+kheqdKCpinMgCkj+sJIUEj+0v0LPug9OQwL5/1jy+kiJwl+Nkw4Kh+7oaL1phhC4gMz6Oq60jMg==
dependencies:
aws-sdk "*"
"@types/babel__core@^7.1.14":
version "7.1.19"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460"
@ -4203,13 +4210,6 @@
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
"@types/minio@^7.0.13":
version "7.0.13"
resolved "https://registry.yarnpkg.com/@types/minio/-/minio-7.0.13.tgz#53c97e1b8382a1cdb3a846bfab45ba9a81e1795c"
integrity sha512-Warrx6uN4KEpBCAWiTh7ngrPoaNIckawtBXqWbFzaatG/NPRZToAv/fa9yAPA7a2FHAGhe154KqaVIqacIn2pQ==
dependencies:
"@types/node" "*"
"@types/node@*", "@types/node@>=8.1.0", "@types/node@^17.0.33", "@types/node@^17.0.5":
version "17.0.33"
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.33.tgz#3c1879b276dc63e73030bb91165e62a4509cd506"
@ -4769,11 +4769,6 @@
resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
"@zxing/text-encoding@0.9.0":
version "0.9.0"
resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b"
integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==
abab@^2.0.5, abab@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
@ -5130,16 +5125,6 @@ asap@~2.0.3:
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
asn1.js@^5.2.0:
version "5.4.1"
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==
dependencies:
bn.js "^4.0.0"
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
safer-buffer "^2.1.0"
ast-types-flow@^0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
@ -5150,11 +5135,6 @@ astral-regex@^2.0.0:
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
async@^3.1.0:
version "3.2.3"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9"
integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@ -5177,10 +5157,20 @@ autoprefixer@^10.3.7, autoprefixer@^10.4.2, autoprefixer@^10.4.5, autoprefixer@^
picocolors "^1.0.0"
postcss-value-parser "^4.2.0"
available-typed-arrays@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
aws-sdk@*, aws-sdk@^2.1152.0:
version "2.1152.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1152.0.tgz#73e4fb81b3a9c289234b5d6848bcdb854f169bdf"
integrity sha512-Lqwk0bDhm3vzpYb3AAM9VgGHeDpbB8+o7UJnP9R+CO23kJfi/XRpKihAcbyKDD/AUQ+O1LJaUVpvaJYLS9Am7w==
dependencies:
buffer "4.9.2"
events "1.1.1"
ieee754 "1.1.13"
jmespath "0.16.0"
querystring "0.2.0"
sax "1.2.1"
url "0.10.3"
uuid "8.0.0"
xml2js "0.4.19"
axe-core@^4.3.5:
version "4.4.2"
@ -5385,7 +5375,7 @@ base16@^1.0.0:
resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70"
integrity sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=
base64-js@^1.3.0, base64-js@^1.3.1:
base64-js@^1.0.2, base64-js@^1.3.0, base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
@ -5419,28 +5409,11 @@ bl@^4.1.0:
inherits "^2.0.4"
readable-stream "^3.4.0"
block-stream2@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/block-stream2/-/block-stream2-2.1.0.tgz#ac0c5ef4298b3857796e05be8ebed72196fa054b"
integrity sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==
dependencies:
readable-stream "^3.4.0"
bluebird@^3.7.1:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9:
version "4.12.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
bn.js@^5.0.0, bn.js@^5.1.1:
version "5.2.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002"
integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==
body-parser@1.20.0:
version "1.20.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5"
@ -5517,11 +5490,6 @@ braces@^3.0.2, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
brorand@^1.0.1, brorand@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
browser-image-compression@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/browser-image-compression/-/browser-image-compression-2.0.0.tgz#f421381a76d474d4da7dcd82810daf595b09bef6"
@ -5529,70 +5497,11 @@ browser-image-compression@^2.0.0:
dependencies:
uzip "0.20201231.0"
browser-or-node@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/browser-or-node/-/browser-or-node-1.3.0.tgz#f2a4e8568f60263050a6714b2cc236bb976647a7"
integrity sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg==
browser-process-hrtime@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==
dependencies:
buffer-xor "^1.0.3"
cipher-base "^1.0.0"
create-hash "^1.1.0"
evp_bytestokey "^1.0.3"
inherits "^2.0.1"
safe-buffer "^5.0.1"
browserify-cipher@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0"
integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==
dependencies:
browserify-aes "^1.0.4"
browserify-des "^1.0.0"
evp_bytestokey "^1.0.0"
browserify-des@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c"
integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==
dependencies:
cipher-base "^1.0.1"
des.js "^1.0.0"
inherits "^2.0.1"
safe-buffer "^5.1.2"
browserify-rsa@^4.0.0, browserify-rsa@^4.0.1:
version "4.1.0"
resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d"
integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==
dependencies:
bn.js "^5.0.0"
randombytes "^2.0.1"
browserify-sign@^4.0.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3"
integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==
dependencies:
bn.js "^5.1.1"
browserify-rsa "^4.0.1"
create-hash "^1.2.0"
create-hmac "^1.1.7"
elliptic "^6.5.3"
inherits "^2.0.4"
parse-asn1 "^5.1.5"
readable-stream "^3.6.0"
safe-buffer "^5.2.0"
browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.18.1, browserslist@^4.20.2, browserslist@^4.20.3:
version "4.20.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.3.tgz#eb7572f49ec430e054f56d52ff0ebe9be915f8bf"
@ -5618,11 +5527,6 @@ bser@2.1.1:
dependencies:
node-int64 "^0.4.0"
buffer-crc32@^0.2.13:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
buffer-equal-constant-time@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
@ -5633,10 +5537,14 @@ buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
buffer-xor@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
buffer@4.9.2:
version "4.9.2"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
isarray "^1.0.0"
buffer@^5.5.0:
version "5.7.1"
@ -5884,14 +5792,6 @@ ci-info@^3.2.0:
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.1.tgz#58331f6f472a25fe3a50a351ae3052936c2c7f32"
integrity sha512-SXgeMX9VwDe7iFFaEWkA5AstuER9YKqy4EhHqr4DVqkwmD9rpVimkMKWHdjn30Ja45txyjhSn63lVX69eVCckg==
cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==
dependencies:
inherits "^2.0.1"
safe-buffer "^5.0.1"
cjs-module-lexer@^1.0.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40"
@ -6303,14 +6203,6 @@ country-flag-icons@^1.0.2:
resolved "https://registry.yarnpkg.com/country-flag-icons/-/country-flag-icons-1.4.26.tgz#e324b2e79c00f180d4f44a8ce034a3e824097619"
integrity sha512-fUBQ58zfQsSL12ErkFSfBxnQZapzdC8+5ZKqhD1z0EHqPnOuiFl7nZTv8Gqpms+jvweP+pbCYGp7G4aiO9eiMw==
create-ecdh@^4.0.0:
version "4.0.4"
resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e"
integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==
dependencies:
bn.js "^4.1.0"
elliptic "^6.5.3"
create-emotion@^10.0.27:
version "10.0.27"
resolved "https://registry.yarnpkg.com/create-emotion/-/create-emotion-10.0.27.tgz#cb4fa2db750f6ca6f9a001a33fbf1f6c46789503"
@ -6321,29 +6213,6 @@ create-emotion@^10.0.27:
"@emotion/sheet" "0.9.4"
"@emotion/utils" "0.11.3"
create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
dependencies:
cipher-base "^1.0.1"
inherits "^2.0.1"
md5.js "^1.3.4"
ripemd160 "^2.0.1"
sha.js "^2.4.0"
create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
dependencies:
cipher-base "^1.0.3"
create-hash "^1.1.0"
inherits "^2.0.1"
ripemd160 "^2.0.0"
safe-buffer "^5.0.1"
sha.js "^2.4.8"
create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
@ -6388,23 +6257,6 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
crypto-browserify@^3.12.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==
dependencies:
browserify-cipher "^1.0.0"
browserify-sign "^4.0.0"
create-ecdh "^4.0.0"
create-hash "^1.1.0"
create-hmac "^1.1.0"
diffie-hellman "^5.0.0"
inherits "^2.0.1"
pbkdf2 "^3.0.3"
public-encrypt "^4.0.0"
randombytes "^2.0.0"
randomfill "^1.0.3"
crypto-random-string@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
@ -6824,14 +6676,6 @@ dequal@^2.0.2:
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d"
integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==
des.js@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843"
integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==
dependencies:
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
destroy@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
@ -6904,15 +6748,6 @@ diff@^4.0.1:
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
diffie-hellman@^5.0.0:
version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==
dependencies:
bn.js "^4.1.0"
miller-rabin "^4.0.0"
randombytes "^2.0.0"
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@ -7149,19 +6984,6 @@ electron-to-chromium@^1.4.118:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz#186180a45617283f1c012284458510cd99d6787f"
integrity sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==
elliptic@^6.5.3:
version "6.5.4"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==
dependencies:
bn.js "^4.11.9"
brorand "^1.1.0"
hash.js "^1.0.0"
hmac-drbg "^1.0.1"
inherits "^2.0.4"
minimalistic-assert "^1.0.1"
minimalistic-crypto-utils "^1.0.1"
emittery@^0.10.2:
version "0.10.2"
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933"
@ -7273,7 +7095,7 @@ error-stack-parser@^2.0.6:
dependencies:
stackframe "^1.1.1"
es-abstract@^1.17.2, es-abstract@^1.18.5, es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5:
es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5:
version "1.20.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.0.tgz#b2d526489cceca004588296334726329e0a6bfb6"
integrity sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA==
@ -7323,11 +7145,6 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
es6-error@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@ -7671,19 +7488,16 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.4:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
events@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==
events@^3.2.0, events@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==
dependencies:
md5.js "^1.3.4"
safe-buffer "^5.1.1"
execa@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
@ -7826,13 +7640,6 @@ fast-url-parser@1.1.3:
dependencies:
punycode "^1.3.2"
fast-xml-parser@^3.17.5:
version "3.21.1"
resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz#152a1d51d445380f7046b304672dd55d15c9e736"
integrity sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg==
dependencies:
strnum "^1.0.4"
fastest-stable-stringify@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76"
@ -8025,11 +7832,6 @@ follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.14.7:
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.0.tgz#06441868281c86d0dda4ad8bdaead2d02dca89d4"
integrity sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==
foreach@^2.0.5:
version "2.0.6"
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.6.tgz#87bcc8a1a0e74000ff2bf9802110708cfb02eb6e"
integrity sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==
fork-ts-checker-webpack-plugin@^6.5.0:
version "6.5.2"
resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz#4f67183f2f9eb8ba7df7177ce3cf3e75cdafb340"
@ -8592,23 +8394,6 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
hash-base@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33"
integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==
dependencies:
inherits "^2.0.4"
readable-stream "^3.6.0"
safe-buffer "^5.2.0"
hash.js@^1.0.0, hash.js@^1.0.3:
version "1.1.7"
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
dependencies:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
hast-to-hyperscript@^9.0.0:
version "9.0.1"
resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz#9b67fd188e4c81e8ad66f803855334173920218d"
@ -8725,15 +8510,6 @@ history@^4.9.0:
tiny-warning "^1.0.0"
value-equal "^1.0.1"
hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
dependencies:
hash.js "^1.0.3"
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
@ -8969,7 +8745,12 @@ icss-utils@^5.0.0, icss-utils@^5.1.0:
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
ieee754@^1.1.13:
ieee754@1.1.13:
version "1.1.13"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
ieee754@^1.1.13, ieee754@^1.1.4:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
@ -9168,14 +8949,6 @@ is-alphanumerical@^1.0.0:
is-alphabetical "^1.0.0"
is-decimal "^1.0.0"
is-arguments@^1.0.4:
version "1.1.1"
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==
dependencies:
call-bind "^1.0.2"
has-tostringtag "^1.0.0"
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
@ -9278,13 +9051,6 @@ is-generator-fn@^2.0.0:
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
is-generator-function@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72"
integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==
dependencies:
has-tostringtag "^1.0.0"
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
@ -9450,17 +9216,6 @@ is-symbol@^1.0.2, is-symbol@^1.0.3:
dependencies:
has-symbols "^1.0.2"
is-typed-array@^1.1.3, is-typed-array@^1.1.7:
version "1.1.8"
resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.8.tgz#cbaa6585dc7db43318bc5b89523ea384a6f65e79"
integrity sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==
dependencies:
available-typed-arrays "^1.0.5"
call-bind "^1.0.2"
es-abstract "^1.18.5"
foreach "^2.0.5"
has-tostringtag "^1.0.0"
is-typedarray@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@ -9505,7 +9260,7 @@ isarray@0.0.1:
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
isarray@~1.0.0:
isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
@ -9976,6 +9731,11 @@ jest@^28.1.0:
import-local "^3.0.2"
jest-cli "^28.1.0"
jmespath@0.16.0:
version "0.16.0"
resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076"
integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==
joi@^17.6.0:
version "17.6.0"
resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2"
@ -10117,11 +9877,6 @@ json-stable-stringify-without-jsonify@^1.0.1:
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
json-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-stream/-/json-stream-1.0.0.tgz#1a3854e28d2bbeeab31cc7ddf683d2ddc5652708"
integrity sha1-GjhU4o0rvuqzHMfd9oPS3cVlJwg=
json5@2.x, json5@^2.1.2, json5@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
@ -10624,15 +10379,6 @@ markdown-escapes@^1.0.0:
resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535"
integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==
md5.js@^1.3.4:
version "1.3.5"
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==
dependencies:
hash-base "^3.0.0"
inherits "^2.0.1"
safe-buffer "^5.1.2"
mdast-squeeze-paragraphs@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97"
@ -10749,14 +10495,6 @@ micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
braces "^3.0.2"
picomatch "^2.3.1"
miller-rabin@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==
dependencies:
bn.js "^4.0.0"
brorand "^1.0.1"
mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
@ -10774,7 +10512,7 @@ mime-types@2.1.18:
dependencies:
mime-db "~1.33.0"
mime-types@^2.1.12, mime-types@^2.1.14, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@^2.1.34, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34:
mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@^2.1.34, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
@ -10821,16 +10559,11 @@ mini-css-extract-plugin@^2.5.3, mini-css-extract-plugin@^2.6.0:
dependencies:
schema-utils "^4.0.0"
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
minimalistic-assert@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
minimatch@3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
@ -10850,30 +10583,7 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
minio@^7.0.28:
version "7.0.28"
resolved "https://registry.yarnpkg.com/minio/-/minio-7.0.28.tgz#1f527ded42df457833e0b203ea51316d6d3d58a4"
integrity sha512-4Oua0R73oCxxmxhh2NiXDJo4Md159I/mdG8ybu6351leMQoB2Sy8S4HmgG6CxuPlEJ0h9M8/WyaI2CARDeeDTQ==
dependencies:
async "^3.1.0"
block-stream2 "^2.0.0"
browser-or-node "^1.3.0"
buffer-crc32 "^0.2.13"
crypto-browserify "^3.12.0"
es6-error "^4.1.1"
fast-xml-parser "^3.17.5"
ipaddr.js "^2.0.1"
json-stream "^1.0.0"
lodash "^4.17.21"
mime-types "^2.1.14"
mkdirp "^0.5.1"
querystring "0.2.0"
through2 "^3.0.1"
web-encoding "^1.1.5"
xml "^1.0.0"
xml2js "^0.4.15"
mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1:
mkdirp@^0.5.5, mkdirp@~0.5.1:
version "0.5.6"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
@ -11488,17 +11198,6 @@ parent-module@^1.0.0:
dependencies:
callsites "^3.0.0"
parse-asn1@^5.0.0, parse-asn1@^5.1.5:
version "5.1.6"
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4"
integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==
dependencies:
asn1.js "^5.2.0"
browserify-aes "^1.0.0"
evp_bytestokey "^1.0.0"
pbkdf2 "^3.0.3"
safe-buffer "^5.1.1"
parse-entities@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8"
@ -11638,17 +11337,6 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
pbkdf2@^3.0.3:
version "3.1.2"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075"
integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==
dependencies:
create-hash "^1.1.2"
create-hmac "^1.1.4"
ripemd160 "^2.0.1"
safe-buffer "^5.0.1"
sha.js "^2.4.8"
php-parser@3.1.0-beta.5:
version "3.1.0-beta.5"
resolved "https://registry.yarnpkg.com/php-parser/-/php-parser-3.1.0-beta.5.tgz#81fd04ede37f3796ba2585b985831caffd62ba50"
@ -12209,18 +11897,6 @@ psl@^1.1.33:
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
public-encrypt@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0"
integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==
dependencies:
bn.js "^4.1.0"
browserify-rsa "^4.0.0"
create-hash "^1.1.0"
parse-asn1 "^5.0.0"
randombytes "^2.0.1"
safe-buffer "^5.1.2"
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
@ -12229,6 +11905,11 @@ pump@^3.0.0:
end-of-stream "^1.1.0"
once "^1.3.1"
punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==
punycode@^1.3.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
@ -12300,21 +11981,13 @@ quick-lru@^5.1.1:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
randombytes@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
dependencies:
safe-buffer "^5.1.0"
randomfill@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"
integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==
dependencies:
randombytes "^2.0.5"
safe-buffer "^5.1.0"
range-parser@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
@ -12684,15 +12357,6 @@ read-pkg@^3.0.0:
normalize-package-data "^2.3.2"
path-type "^3.0.0"
"readable-stream@2 || 3", readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-stream@^2.0.1, readable-stream@^2.0.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
@ -12706,6 +12370,15 @@ readable-stream@^2.0.1, readable-stream@^2.0.6:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
@ -13022,14 +12695,6 @@ rimraf@^3.0.0, rimraf@^3.0.2:
dependencies:
glob "^7.1.3"
ripemd160@^2.0.0, ripemd160@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==
dependencies:
hash-base "^3.0.0"
inherits "^2.0.1"
rollup-plugin-dts@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/rollup-plugin-dts/-/rollup-plugin-dts-4.2.1.tgz#c17968a0f7c5ae70a9e0ab37e715f3ef63da01c7"
@ -13157,7 +12822,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0:
safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@ -13167,7 +12832,7 @@ safe-identifier@^0.4.2:
resolved "https://registry.yarnpkg.com/safe-identifier/-/safe-identifier-0.4.2.tgz#cf6bfca31c2897c588092d1750d30ef501d59fcb"
integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==
"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.0:
"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@ -13184,6 +12849,11 @@ sanitize-html@^2.7.0:
parse-srcset "^1.0.2"
postcss "^8.3.11"
sax@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==
sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
@ -13406,14 +13076,6 @@ setprototypeof@1.2.0:
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
sha.js@^2.4.0, sha.js@^2.4.8:
version "2.4.11"
resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
dependencies:
inherits "^2.0.1"
safe-buffer "^5.0.1"
shallow-clone@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
@ -13933,11 +13595,6 @@ stripe@^9.4.0:
"@types/node" ">=8.1.0"
qs "^6.10.3"
strnum@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db"
integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==
style-inject@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/style-inject/-/style-inject-0.3.0.tgz#d21c477affec91811cc82355832a700d22bf8dd3"
@ -14191,14 +13848,6 @@ throttle-debounce@^3.0.1:
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb"
integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==
through2@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4"
integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==
dependencies:
inherits "^2.0.4"
readable-stream "2 || 3"
through@^2.3.6:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@ -14743,6 +14392,14 @@ url-template@^2.0.8:
resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21"
integrity sha1-/FZaPMy/93MMd19WQflVV5FDnyE=
url@0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64"
integrity sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==
dependencies:
punycode "1.3.2"
querystring "0.2.0"
use-callback-ref@^1.2.5, use-callback-ref@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.0.tgz#772199899b9c9a50526fedc4993fc7fa1f7e32d5"
@ -14807,18 +14464,6 @@ util.promisify@~1.0.0:
has-symbols "^1.0.1"
object.getownpropertydescriptors "^2.1.0"
util@^0.12.3:
version "0.12.4"
resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253"
integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==
dependencies:
inherits "^2.0.3"
is-arguments "^1.0.4"
is-generator-function "^1.0.7"
is-typed-array "^1.1.3"
safe-buffer "^5.1.2"
which-typed-array "^1.1.2"
utila@~0.4:
version "0.4.0"
resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
@ -14834,6 +14479,11 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
uuid@8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c"
integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==
uuid@^8.0.0, uuid@^8.3.0, uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
@ -14970,15 +14620,6 @@ wcwidth@^1.0.1:
dependencies:
defaults "^1.0.3"
web-encoding@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/web-encoding/-/web-encoding-1.1.5.tgz#fc810cf7667364a6335c939913f5051d3e0c4864"
integrity sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==
dependencies:
util "^0.12.3"
optionalDependencies:
"@zxing/text-encoding" "0.9.0"
web-namespaces@^1.0.0, web-namespaces@^1.1.2:
version "1.1.4"
resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec"
@ -15205,18 +14846,6 @@ which-boxed-primitive@^1.0.2:
is-string "^1.0.5"
is-symbol "^1.0.3"
which-typed-array@^1.1.2:
version "1.1.7"
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.7.tgz#2761799b9a22d4b8660b3c1b40abaa7739691793"
integrity sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==
dependencies:
available-typed-arrays "^1.0.5"
call-bind "^1.0.2"
es-abstract "^1.18.5"
foreach "^2.0.5"
has-tostringtag "^1.0.0"
is-typed-array "^1.1.7"
which@^1.2.9, which@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
@ -15330,23 +14959,18 @@ xml-name-validator@^4.0.0:
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
xml2js@^0.4.15:
version "0.4.23"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
xml2js@0.4.19:
version "0.4.19"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"
integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==
dependencies:
sax ">=0.6.0"
xmlbuilder "~11.0.0"
xmlbuilder "~9.0.1"
xml@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=
xmlbuilder@~11.0.0:
version "11.0.1"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
xmlbuilder@~9.0.1:
version "9.0.7"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
integrity sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==
xmlchars@^2.2.0:
version "2.2.0"