1
0
Fork 0
mirror of synced 2024-07-07 07:15:43 +12:00

Merge branch 'feature/count-rows-to-return' of github.com:Budibase/budibase into feature/count-rows-to-return

This commit is contained in:
mike12345567 2024-06-19 11:53:11 +01:00
commit c973562398
38 changed files with 762 additions and 226 deletions

View file

@ -92,7 +92,8 @@
// differs to external, but the API is broadly the same
"jest/no-conditional-expect": "off",
// have to turn this off to allow function overloading in typescript
"no-dupe-class-members": "off"
"no-dupe-class-members": "off",
"no-redeclare": "off"
}
},
{

View file

@ -162,6 +162,7 @@
max-height: 100%;
}
.modal-inner-wrapper {
padding: 40px;
flex: 1 1 auto;
display: flex;
flex-direction: row;
@ -176,7 +177,6 @@
border: 2px solid var(--spectrum-global-color-gray-200);
overflow: visible;
max-height: none;
margin: 40px 0;
transform: none;
--spectrum-dialog-confirm-border-radius: var(
--spectrum-global-dimension-size-100

View file

@ -16,6 +16,8 @@
DatePicker,
DrawerContent,
Toggle,
Icon,
Divider,
} from "@budibase/bbui"
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
import { automationStore, selectedAutomation, tables } from "stores/builder"
@ -89,6 +91,8 @@
? [hbAutocomplete([...bindingsToCompletions(bindings, codeMode)])]
: []
let testDataRowVisibility = {}
const getInputData = (testData, blockInputs) => {
// Test data is not cloned for reactivity
let newInputData = testData || cloneDeep(blockInputs)
@ -196,7 +200,8 @@
(automation.trigger?.event === "row:update" ||
automation.trigger?.event === "row:save")
) {
if (name !== "id" && name !== "revision") return `trigger.row.${name}`
let noRowKeywordBindings = ["id", "revision", "oldRow"]
if (!noRowKeywordBindings.includes(name)) return `trigger.row.${name}`
}
/* End special cases for generating custom schemas based on triggers */
@ -372,7 +377,11 @@
function getFieldLabel(key, value) {
const requiredSuffix = requiredProperties.includes(key) ? "*" : ""
return `${value.title || (key === "row" ? "Table" : key)} ${requiredSuffix}`
return `${value.title || (key === "row" ? "Row" : key)} ${requiredSuffix}`
}
function toggleTestDataRowVisibility(key) {
testDataRowVisibility[key] = !testDataRowVisibility[key]
}
function handleAttachmentParams(keyValueObj) {
@ -607,20 +616,48 @@
on:change={e => onChange(e, key)}
/>
{:else if value.customType === "row"}
<RowSelector
value={inputData[key]}
meta={inputData["meta"] || {}}
on:change={e => {
if (e.detail?.key) {
onChange(e, e.detail.key)
} else {
onChange(e, key)
}
}}
{bindings}
{isTestModal}
{isUpdateRow}
/>
{#if isTestModal}
<div class="align-horizontally">
<Icon
name={testDataRowVisibility[key] ? "Remove" : "Add"}
hoverable
on:click={() => toggleTestDataRowVisibility(key)}
/>
<Label size="XL">{label}</Label>
</div>
{#if testDataRowVisibility[key]}
<RowSelector
value={inputData[key]}
meta={inputData["meta"] || {}}
on:change={e => {
if (e.detail?.key) {
onChange(e, e.detail.key)
} else {
onChange(e, key)
}
}}
{bindings}
{isTestModal}
{isUpdateRow}
/>
{/if}
<Divider />
{:else}
<RowSelector
value={inputData[key]}
meta={inputData["meta"] || {}}
on:change={e => {
if (e.detail?.key) {
onChange(e, e.detail.key)
} else {
onChange(e, key)
}
}}
{bindings}
{isTestModal}
{isUpdateRow}
/>
{/if}
{:else if value.customType === "webhookUrl"}
<WebhookDisplay
on:change={e => onChange(e, key)}
@ -736,6 +773,12 @@
width: 320px;
}
.align-horizontally {
display: flex;
gap: var(--spacing-s);
align-items: center;
}
.fields {
display: flex;
flex-direction: column;

View file

@ -0,0 +1,8 @@
<div class="root">This action doesn't require any settings.</div>
<style>
.root {
max-width: 400px;
margin: 0 auto;
}
</style>

View file

@ -0,0 +1,36 @@
<script>
import { Select, Label } from "@budibase/bbui"
import { selectedScreen } from "stores/builder"
import { findAllMatchingComponents } from "helpers/components"
export let parameters
$: modalOptions = getModalOptions($selectedScreen)
const getModalOptions = screen => {
const modalComponents = findAllMatchingComponents(screen.props, component =>
component._component.endsWith("/modal")
)
return modalComponents.map(modal => ({
label: modal._instanceName,
value: modal._id,
}))
}
</script>
<div class="root">
<Label small>Modal</Label>
<Select bind:value={parameters.id} options={modalOptions} />
</div>
<style>
.root {
display: grid;
column-gap: var(--spacing-l);
row-gap: var(--spacing-s);
grid-template-columns: 60px 1fr;
align-items: center;
max-width: 400px;
margin: 0 auto;
}
</style>

View file

@ -21,5 +21,7 @@ export { default as ShowNotification } from "./ShowNotification.svelte"
export { default as PromptUser } from "./PromptUser.svelte"
export { default as OpenSidePanel } from "./OpenSidePanel.svelte"
export { default as CloseSidePanel } from "./CloseSidePanel.svelte"
export { default as OpenModal } from "./OpenModal.svelte"
export { default as CloseModal } from "./CloseModal.svelte"
export { default as ClearRowSelection } from "./ClearRowSelection.svelte"
export { default as DownloadFile } from "./DownloadFile.svelte"

View file

@ -157,6 +157,18 @@
"component": "CloseSidePanel",
"dependsOnFeature": "sidePanel"
},
{
"name": "Open Modal",
"type": "application",
"component": "OpenModal",
"dependsOnFeature": "modal"
},
{
"name": "Close Modal",
"type": "application",
"component": "CloseModal",
"dependsOnFeature": "modal"
},
{
"name": "Clear Row Selection",
"type": "data",

View file

@ -59,7 +59,14 @@
// Build up list of illegal children from ancestors
let illegalChildren = definition.illegalChildren || []
path.forEach(ancestor => {
if (ancestor._component === `@budibase/standard-components/sidepanel`) {
// Sidepanels and modals can be nested anywhere in the component tree, but really they are always rendered at the top level.
// Because of this, it doesn't make sense to carry over any parent illegal children to them, so the array is reset here.
if (
[
"@budibase/standard-components/sidepanel",
"@budibase/standard-components/modal",
].includes(ancestor._component)
) {
illegalChildren = []
}
const def = componentStore.getDefinition(ancestor._component)

View file

@ -14,7 +14,7 @@
{
"name": "Layout",
"icon": "ClassicGridView",
"children": ["container", "section", "sidepanel"]
"children": ["container", "section", "sidepanel", "modal"]
},
{
"name": "Data",

View file

@ -125,7 +125,14 @@ export class ScreenStore extends BudiStore {
return
}
if (type === "@budibase/standard-components/sidepanel") {
// Sidepanels and modals can be nested anywhere in the component tree, but really they are always rendered at the top level.
// Because of this, it doesn't make sense to carry over any parent illegal children to them, so the array is reset here.
if (
[
"@budibase/standard-components/sidepanel",
"@budibase/standard-components/modal",
].includes(type)
) {
illegalChildren = []
}

View file

@ -32,7 +32,7 @@
"pouchdb": "7.3.0",
"pouchdb-replication-stream": "1.2.9",
"randomstring": "1.1.5",
"tar": "6.1.15",
"tar": "6.2.1",
"yaml": "^2.1.1"
},
"devDependencies": {

View file

@ -11,6 +11,7 @@
"continueIfAction": true,
"showNotificationAction": true,
"sidePanel": true,
"modal": true,
"skeletonLoader": true
},
"typeSupportPresets": {
@ -6975,7 +6976,7 @@
"name": "Side Panel",
"icon": "RailRight",
"hasChildren": true,
"illegalChildren": ["section", "sidepanel"],
"illegalChildren": ["section", "sidepanel", "modal"],
"showEmptyState": false,
"draggable": false,
"info": "Side panels are hidden by default. They will only be revealed when triggered by the 'Open Side Panel' action.",
@ -6993,6 +6994,52 @@
}
]
},
"modal": {
"name": "Modal",
"icon": "MBox",
"hasChildren": true,
"illegalChildren": ["section", "modal", "sidepanel"],
"showEmptyState": false,
"draggable": false,
"info": "Modals are hidden by default. They will only be revealed when triggered by the 'Open Modal' action.",
"settings": [
{
"type": "boolean",
"key": "ignoreClicksOutside",
"label": "Ignore clicks outside",
"defaultValue": false
},
{
"type": "event",
"key": "onClose",
"label": "On close"
},
{
"type": "select",
"label": "Size",
"key": "size",
"defaultValue": "small",
"options": [
{
"label": "Small",
"value": "small"
},
{
"label": "Medium",
"value": "medium"
},
{
"label": "Large",
"value": "large"
},
{
"label": "Fullscreen",
"value": "fullscreen"
}
]
}
]
},
"rowexplorer": {
"block": true,
"name": "Row Explorer Block",

View file

@ -20,6 +20,7 @@
devToolsEnabled,
environmentStore,
sidePanelStore,
modalStore,
} from "stores"
import NotificationDisplay from "components/overlay/NotificationDisplay.svelte"
import ConfirmationDisplay from "components/overlay/ConfirmationDisplay.svelte"
@ -104,10 +105,15 @@
})
}
const handleHashChange = () => {
const { open } = $sidePanelStore
if (open) {
const { open: sidePanelOpen } = $sidePanelStore
if (sidePanelOpen) {
sidePanelStore.actions.close()
}
const { open: modalOpen } = $modalStore
if (modalOpen) {
modalStore.actions.close()
}
}
window.addEventListener("hashchange", handleHashChange)
return () => {

View file

@ -12,6 +12,7 @@
linkable,
builderStore,
sidePanelStore,
modalStore,
appStore,
} = sdk
const context = getContext("context")
@ -77,6 +78,7 @@
!$builderStore.inBuilder &&
$sidePanelStore.open &&
!$sidePanelStore.ignoreClicksOutside
$: screenId = $builderStore.inBuilder
? `${$builderStore.screen?._id}-screen`
: "screen"
@ -198,6 +200,7 @@
const handleClickLink = () => {
mobileOpen = false
sidePanelStore.actions.close()
modalStore.actions.close()
}
</script>

View file

@ -1,7 +1,7 @@
<script>
import { getContext } from "svelte"
const { linkable, styleable, builderStore, sidePanelStore } =
const { linkable, styleable, builderStore, sidePanelStore, modalStore } =
getContext("sdk")
const component = getContext("component")
@ -29,6 +29,11 @@
// overrides the color when it's passed as inline style.
$: styles = enrichStyles($component.styles, color)
const handleUrlChange = () => {
sidePanelStore.actions.close()
modalStore.actions.close()
}
const getSanitizedUrl = (url, externalLink, newTab) => {
if (!url) {
return externalLink || newTab ? "#/" : "/"
@ -109,7 +114,7 @@
class:italic
class:underline
class="align--{align || 'left'} size--{size || 'M'}"
on:click={sidePanelStore.actions.close}
on:click={handleUrlChange}
>
{componentText}
</a>

View file

@ -0,0 +1,141 @@
<script>
import { getContext } from "svelte"
import { Modal, Icon } from "@budibase/bbui"
const component = getContext("component")
const { styleable, modalStore, builderStore, dndIsDragging } =
getContext("sdk")
export let onClose
export let ignoreClicksOutside
export let size
let modal
// Open modal automatically in builder
$: {
if ($builderStore.inBuilder) {
if (
$component.inSelectedPath &&
$modalStore.contentId !== $component.id
) {
modalStore.actions.open($component.id)
} else if (
!$component.inSelectedPath &&
$modalStore.contentId === $component.id &&
!$dndIsDragging
) {
modalStore.actions.close()
}
}
}
$: open = $modalStore.contentId === $component.id
const handleModalClose = async () => {
if (onClose) {
await onClose()
}
modalStore.actions.close()
}
const handleOpen = (open, modal) => {
if (!modal) return
if (open) {
modal.show()
} else {
modal.hide()
}
}
$: handleOpen(open, modal)
</script>
<!-- Conditional displaying in the builder is necessary otherwise previews don't update properly upon component deletion -->
{#if !$builderStore.inBuilder || open}
<Modal
on:cancel={handleModalClose}
bind:this={modal}
disableCancel={$builderStore.inBuilder}
zIndex={2}
>
<div use:styleable={$component.styles} class={`modal-content ${size}`}>
<div class="modal-header">
<Icon
color="var(--spectrum-global-color-gray-800)"
name="Close"
hoverable
on:click={handleModalClose}
/>
</div>
<div class="modal-main">
<div class="modal-main-inner">
<slot />
</div>
</div>
</div>
</Modal>
{/if}
<style>
.modal-content {
display: flex;
flex-direction: column;
max-width: 100%;
box-sizing: border-box;
padding: 12px 0px 40px;
}
.small {
width: 400px;
min-height: 200px;
}
.medium {
width: 600px;
min-height: 400px;
}
.large {
width: 800px;
min-height: 600px;
}
.fullscreen {
width: calc(100vw - 80px);
min-height: calc(100vh - 80px);
}
.modal-header {
display: flex;
flex-direction: row;
justify-content: flex-end;
flex-shrink: 0;
flex-grow: 0;
padding: 0 12px 12px;
box-sizing: border-box;
}
.modal-main {
padding: 0 40px;
flex-grow: 1;
display: flex;
flex-direction: column;
}
.modal-main :global(.component > *) {
max-width: 100%;
}
.modal-main-inner {
flex-grow: 1;
display: flex;
flex-direction: column;
word-break: break-word;
}
.modal-main-inner:empty {
border-radius: 3px;
border: 2px dashed var(--spectrum-global-color-gray-400);
}
</style>

View file

@ -31,41 +31,23 @@
let schema
$: formattedFields = convertOldFieldFormat(fields)
$: fieldsOrDefault = getDefaultFields(formattedFields, schema)
$: fetchSchema(dataSource)
$: id = $component.id
// We could simply spread $$props into the inner form and append our
// additions, but that would create svelte warnings about unused props and
// make maintenance in future more confusing as we typically always have a
// proper mapping of schema settings to component exports, without having to
// search multiple files
$: innerProps = {
dataSource,
actionUrl,
actionType,
size,
disabled,
fields: fieldsOrDefault,
title,
description,
schema,
notificationOverride,
buttons:
buttons ||
Utils.buildFormBlockButtonConfig({
_id: id,
showDeleteButton,
showSaveButton,
saveButtonLabel,
deleteButtonLabel,
notificationOverride,
actionType,
actionUrl,
dataSource,
}),
buttonPosition: buttons ? buttonPosition : "top",
}
$: formattedFields = convertOldFieldFormat(fields)
$: fieldsOrDefault = getDefaultFields(formattedFields, schema)
$: buttonsOrDefault =
buttons ||
Utils.buildFormBlockButtonConfig({
_id: id,
showDeleteButton,
showSaveButton,
saveButtonLabel,
deleteButtonLabel,
notificationOverride,
actionType,
actionUrl,
dataSource,
})
// Provide additional data context for live binding eval
export const getAdditionalDataContext = () => {
@ -123,5 +105,18 @@
</script>
<FormBlockWrapper {actionType} {dataSource} {rowId} {noRowsMessage}>
<InnerFormBlock {...innerProps} />
<InnerFormBlock
{dataSource}
{actionUrl}
{actionType}
{size}
{disabled}
fields={fieldsOrDefault}
{title}
{description}
{schema}
{notificationOverride}
buttons={buttonsOrDefault}
buttonPosition={buttons ? buttonPosition : "top"}
/>
</FormBlockWrapper>

View file

@ -91,15 +91,13 @@
{#if description}
<BlockComponent type="text" props={{ text: description }} order={1} />
{/if}
{#key fields}
<BlockComponent type="container">
<div class="form-block fields" class:mobile={$context.device.mobile}>
{#each fields as field, idx}
<FormBlockComponent {field} {schema} order={idx} />
{/each}
</div>
</BlockComponent>
{/key}
<BlockComponent type="container">
<div class="form-block fields" class:mobile={$context.device.mobile}>
{#each fields as field, idx}
<FormBlockComponent {field} {schema} order={idx} />
{/each}
</div>
</BlockComponent>
</BlockComponent>
{#if buttonPosition === "bottom"}
<BlockComponent

View file

@ -37,6 +37,7 @@ export { default as markdownviewer } from "./MarkdownViewer.svelte"
export { default as embeddedmap } from "./embedded-map/EmbeddedMap.svelte"
export { default as grid } from "./Grid.svelte"
export { default as sidepanel } from "./SidePanel.svelte"
export { default as modal } from "./Modal.svelte"
export { default as gridblock } from "./GridBlock.svelte"
export * from "./charts"
export * from "./forms"

View file

@ -57,7 +57,9 @@
return
}
nextState.indicators[idx].visible =
nextState.indicators[idx].insideSidePanel || entries[0].isIntersecting
nextState.indicators[idx].insideModal ||
nextState.indicators[idx].insideSidePanel ||
entries[0].isIntersecting
if (++callbackCount === observers.length) {
state = nextState
updating = false
@ -139,6 +141,7 @@
height: elBounds.height + 4,
visible: false,
insideSidePanel: !!child.closest(".side-panel"),
insideModal: !!child.closest(".modal-content"),
})
})
}

View file

@ -11,6 +11,7 @@ import {
currentRole,
environmentStore,
sidePanelStore,
modalStore,
dndIsDragging,
confirmationStore,
roleStore,
@ -53,6 +54,7 @@ export default {
componentStore,
environmentStore,
sidePanelStore,
modalStore,
dndIsDragging,
currentRole,
confirmationStore,

View file

@ -27,6 +27,7 @@ export {
dndIsDragging,
} from "./dnd"
export { sidePanelStore } from "./sidePanel"
export { modalStore } from "./modal"
export { hoverStore } from "./hover"
// Context stores are layered and duplicated, so it is not a singleton

View file

@ -0,0 +1,32 @@
import { writable } from "svelte/store"
export const createModalStore = () => {
const initialState = {
contentId: null,
}
const store = writable(initialState)
const open = id => {
store.update(state => {
state.contentId = id
return state
})
}
const close = () => {
store.update(state => {
state.contentId = null
return state
})
}
return {
subscribe: store.subscribe,
actions: {
open,
close,
},
}
}
export const modalStore = createModalStore()

View file

@ -12,6 +12,7 @@ import {
uploadStore,
rowSelectionStore,
sidePanelStore,
modalStore,
} from "stores"
import { API } from "api"
import { ActionTypes } from "constants"
@ -436,6 +437,17 @@ const closeSidePanelHandler = () => {
sidePanelStore.actions.close()
}
const openModalHandler = action => {
const { id } = action.parameters
if (id) {
modalStore.actions.open(id)
}
}
const closeModalHandler = () => {
modalStore.actions.close()
}
const downloadFileHandler = async action => {
const { url, fileName } = action.parameters
try {
@ -499,6 +511,8 @@ const handlerMap = {
["Prompt User"]: promptUserHandler,
["Open Side Panel"]: openSidePanelHandler,
["Close Side Panel"]: closeSidePanelHandler,
["Open Modal"]: openModalHandler,
["Close Modal"]: closeModalHandler,
["Download File"]: downloadFileHandler,
}

View file

@ -161,6 +161,9 @@ export const buildFormBlockButtonConfig = props => {
{
"##eventHandlerType": "Close Side Panel",
},
{
"##eventHandlerType": "Close Modal",
},
// Clear a create form once submitted
...(actionType !== "Create"
? []

View file

@ -39,9 +39,10 @@ export async function handleRequest<T extends Operation>(
export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
const tableId = utils.getTableId(ctx)
const { _id, ...rowData } = ctx.request.body
const { _id, ...rowData } = ctx.request.body
const table = await sdk.tables.getTable(tableId)
const { row: dataToUpdate } = await inputProcessing(
ctx.user?._id,
cloneDeep(table),
@ -79,6 +80,7 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
...response,
row: enrichedRow,
table,
oldRow: beforeRow,
}
}

View file

@ -55,13 +55,13 @@ export async function patch(
return save(ctx)
}
try {
const { row, table } = await pickApi(tableId).patch(ctx)
const { row, table, oldRow } = await pickApi(tableId).patch(ctx)
if (!row) {
ctx.throw(404, "Row not found")
}
ctx.status = 200
ctx.eventEmitter &&
ctx.eventEmitter.emitRow(`row:update`, appId, row, table)
ctx.eventEmitter.emitRow(`row:update`, appId, row, table, oldRow)
ctx.message = `${table.name} updated successfully.`
ctx.body = row
gridSocket?.emitRowUpdate(ctx, row)

View file

@ -85,13 +85,15 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
// the row has been updated, need to put it into the ctx
ctx.request.body = row as any
await userController.updateMetadata(ctx as any)
return { row: ctx.body as Row, table }
return { row: ctx.body as Row, table, oldRow }
}
return finaliseRow(table, row, {
const result = await finaliseRow(table, row, {
oldTable: dbTable,
updateFormula: true,
})
return { ...result, oldRow }
}
export async function find(ctx: UserCtx): Promise<Row> {

View file

@ -13,6 +13,7 @@ import { events } from "@budibase/backend-core"
import sdk from "../../../sdk"
import { Automation } from "@budibase/types"
import { mocks } from "@budibase/backend-core/tests"
import { FilterConditions } from "../../../automations/steps/filter"
const MAX_RETRIES = 4
let {
@ -21,6 +22,7 @@ let {
automationTrigger,
automationStep,
collectAutomation,
filterAutomation,
} = setup.structures
describe("/automations", () => {
@ -155,7 +157,12 @@ describe("/automations", () => {
automation.appId = config.appId
automation = await config.createAutomation(automation)
await setup.delay(500)
const res = await testAutomation(config, automation)
const res = await testAutomation(config, automation, {
row: {
name: "Test",
description: "TEST",
},
})
expect(events.automation.tested).toHaveBeenCalledTimes(1)
// this looks a bit mad but we don't actually have a way to wait for a response from the automation to
// know that it has finished all of its actions - this is currently the best way
@ -436,4 +443,38 @@ describe("/automations", () => {
expect(res).toEqual(true)
})
})
describe("Update Row Old / New Row comparison", () => {
it.each([
{ oldCity: "asdsadsadsad", newCity: "new" },
{ oldCity: "Belfast", newCity: "Belfast" },
])(
"triggers an update row automation and compares new to old rows with old city '%s' and new city '%s'",
async ({ oldCity, newCity }) => {
const expectedResult = oldCity === newCity
let table = await config.createTable()
let automation = await filterAutomation()
automation.definition.trigger.inputs.tableId = table._id
automation.definition.steps[0].inputs = {
condition: FilterConditions.EQUAL,
field: "{{ trigger.row.City }}",
value: "{{ trigger.oldRow.City }}",
}
automation.appId = config.appId!
automation = await config.createAutomation(automation)
let triggerInputs = {
oldRow: {
City: oldCity,
},
row: {
City: newCity,
},
}
const res = await testAutomation(config, automation, triggerInputs)
expect(res.body.steps[1].outputs.result).toEqual(expectedResult)
}
)
})
})

View file

@ -1,6 +1,7 @@
import { DatabaseName, getDatasource } from "../../../integrations/tests/utils"
import tk from "timekeeper"
import emitter from "../../../../src/events"
import { outputProcessing } from "../../../utilities/rowProcessor"
import * as setup from "./utilities"
import { context, InternalTable, tenancy } from "@budibase/backend-core"
@ -24,6 +25,7 @@ import {
StaticQuotaName,
Table,
TableSourceType,
UpdatedRowEventEmitter,
} from "@budibase/types"
import { generator, mocks } from "@budibase/backend-core/tests"
import _, { merge } from "lodash"
@ -31,6 +33,28 @@ import * as uuid from "uuid"
const timestamp = new Date("2023-01-26T11:48:57.597Z").toISOString()
tk.freeze(timestamp)
interface WaitOptions {
name: string
matchFn?: (event: any) => boolean
}
async function waitForEvent(
opts: WaitOptions,
callback: () => Promise<void>
): Promise<any> {
const p = new Promise((resolve: any) => {
const listener = (event: any) => {
if (opts.matchFn && !opts.matchFn(event)) {
return
}
resolve(event)
emitter.off(opts.name, listener)
}
emitter.on(opts.name, listener)
})
await callback()
return await p
}
describe.each([
["internal", undefined],
@ -608,6 +632,31 @@ describe.each([
await assertRowUsage(rowUsage)
})
it("should update only the fields that are supplied and emit the correct oldRow", async () => {
let beforeRow = await config.api.row.save(table._id!, {
name: "test",
description: "test",
})
const opts = {
name: "row:update",
matchFn: (event: UpdatedRowEventEmitter) =>
event.row._id === beforeRow._id,
}
const event = await waitForEvent(opts, async () => {
await config.api.row.patch(table._id!, {
_id: beforeRow._id!,
_rev: beforeRow._rev!,
tableId: table._id!,
name: "Updated Name",
})
})
expect(event.oldRow).toBeDefined()
expect(event.oldRow.name).toEqual("test")
expect(event.row.name).toEqual("Updated Name")
expect(event.oldRow.description).toEqual(beforeRow.description)
expect(event.row.description).toEqual(beforeRow.description)
})
it("should throw an error when given improper types", async () => {
const existing = await config.api.row.save(table._id!, {})
const rowUsage = await getRowUsage()

View file

@ -158,15 +158,16 @@ export const getDB = () => {
return context.getAppDB()
}
export const testAutomation = async (config: any, automation: any) => {
export const testAutomation = async (
config: any,
automation: any,
triggerInputs: any
) => {
return runRequest(automation.appId, async () => {
return await config.request
.post(`/api/automations/${automation._id}/test`)
.send({
row: {
name: "Test",
description: "TEST",
},
...triggerInputs,
})
.set(config.defaultHeaders())
.expect("Content-Type", /json/)

View file

@ -27,10 +27,17 @@ export const definition: AutomationTriggerSchema = {
},
outputs: {
properties: {
row: {
oldRow: {
type: AutomationIOType.OBJECT,
customType: AutomationCustomIOType.ROW,
description: "The row that was updated",
title: "Old Row",
},
row: {
type: AutomationIOType.OBJECT,
customType: AutomationCustomIOType.ROW,
description: "The row before it was updated",
title: "Row",
},
id: {
type: AutomationIOType.STRING,

View file

@ -8,7 +8,13 @@ import { checkTestFlag } from "../utilities/redis"
import * as utils from "./utils"
import env from "../environment"
import { context, db as dbCore } from "@budibase/backend-core"
import { Automation, Row, AutomationData, AutomationJob } from "@budibase/types"
import {
Automation,
Row,
AutomationData,
AutomationJob,
UpdatedRowEventEmitter,
} from "@budibase/types"
import { executeInThread } from "../threads/automation"
export const TRIGGER_DEFINITIONS = definitions
@ -65,7 +71,7 @@ async function queueRelevantRowAutomations(
})
}
emitter.on("row:save", async function (event) {
emitter.on("row:save", async function (event: UpdatedRowEventEmitter) {
/* istanbul ignore next */
if (!event || !event.row || !event.row.tableId) {
return

View file

@ -13,8 +13,14 @@ import { Table, Row } from "@budibase/types"
* This is specifically quite important for template strings used in automations.
*/
class BudibaseEmitter extends EventEmitter {
emitRow(eventName: string, appId: string, row: Row, table?: Table) {
rowEmission({ emitter: this, eventName, appId, row, table })
emitRow(
eventName: string,
appId: string,
row: Row,
table?: Table,
oldRow?: Row
) {
rowEmission({ emitter: this, eventName, appId, row, table, oldRow })
}
emitTable(eventName: string, appId: string, table?: Table) {

View file

@ -7,6 +7,7 @@ type BBEventOpts = {
appId: string
table?: Table
row?: Row
oldRow?: Row
metadata?: any
}
@ -18,6 +19,7 @@ type BBEvent = {
appId: string
tableId?: string
row?: Row
oldRow?: Row
table?: BBEventTable
id?: string
revision?: string
@ -31,9 +33,11 @@ export function rowEmission({
row,
table,
metadata,
oldRow,
}: BBEventOpts) {
let event: BBEvent = {
row,
oldRow,
appId,
tableId: row?.tableId,
}

View file

@ -359,6 +359,36 @@ export function collectAutomation(tableId?: string): Automation {
return automation as Automation
}
export function filterAutomation(tableId?: string): Automation {
const automation: any = {
name: "looping",
type: "automation",
definition: {
steps: [
{
id: "b",
type: "ACTION",
internal: true,
stepId: AutomationActionStepId.FILTER,
inputs: {},
schema: BUILTIN_ACTION_DEFINITIONS.EXECUTE_SCRIPT.schema,
},
],
trigger: {
id: "a",
type: "TRIGGER",
event: "row:save",
stepId: AutomationTriggerStepId.ROW_SAVED,
inputs: {
tableId,
},
schema: TRIGGER_DEFINITIONS.ROW_SAVED.schema,
},
},
}
return automation as Automation
}
export function basicAutomationResults(
automationId: string
): AutomationResults {

View file

@ -2,6 +2,8 @@ import { Document } from "../document"
import { EventEmitter } from "events"
import { User } from "../global"
import { ReadStream } from "fs"
import { Row } from "./row"
import { Table } from "./table"
export enum AutomationIOType {
OBJECT = "object",
@ -252,3 +254,10 @@ export type BucketedContent = AutomationAttachmentContent & {
bucket: string
path: string
}
export type UpdatedRowEventEmitter = {
row: Row
oldRow: Row
table: Table
appId: string
}

290
yarn.lock
View file

@ -2193,9 +2193,9 @@
"@bull-board/api" "5.10.2"
"@camunda8/sdk@^8.5.3":
version "8.6.2"
resolved "https://registry.yarnpkg.com/@camunda8/sdk/-/sdk-8.6.2.tgz#7f1ed90dfb5ad50ac22e5f984e92739c4e54f216"
integrity sha512-QdpuU3qsbJVKYDuIIYIgryl9HbnOoUqmeUcCU4YZPBhoWVkbCjnP0GD4Q3485SE3WzpbbAMoLtYCHi7hJwnAcA==
version "8.6.6"
resolved "https://registry.yarnpkg.com/@camunda8/sdk/-/sdk-8.6.6.tgz#39f894f89b485df7c2a803e590d4175fbfb6fb8f"
integrity sha512-u0A1Q0Fwh6W33i9ky2nfA6DJUKgLES8FAhp7k3L7L8ldNM5NgLRLiz1eZgUWK5CT4D78aFoSkm3VobFo+V42yQ==
dependencies:
"@grpc/grpc-js" "1.10.9"
"@grpc/proto-loader" "0.7.13"
@ -2401,230 +2401,230 @@
find-up "^5.0.0"
strip-json-comments "^3.1.1"
"@esbuild/aix-ppc64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537"
integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==
"@esbuild/aix-ppc64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
"@esbuild/android-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622"
integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==
"@esbuild/android-arm64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9"
integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==
"@esbuild/android-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052"
integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==
"@esbuild/android-arm@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682"
integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==
"@esbuild/android-arm@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995"
integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==
"@esbuild/android-arm@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
"@esbuild/android-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2"
integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==
"@esbuild/android-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98"
integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==
"@esbuild/android-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
"@esbuild/darwin-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1"
integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==
"@esbuild/darwin-arm64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb"
integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==
"@esbuild/darwin-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a"
integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
"@esbuild/darwin-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d"
integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==
"@esbuild/darwin-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0"
integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==
"@esbuild/darwin-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22"
integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
"@esbuild/freebsd-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54"
integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==
"@esbuild/freebsd-arm64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911"
integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==
"@esbuild/freebsd-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
"@esbuild/freebsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e"
integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==
"@esbuild/freebsd-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c"
integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==
"@esbuild/freebsd-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
"@esbuild/linux-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0"
integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==
"@esbuild/linux-arm64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5"
integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==
"@esbuild/linux-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
"@esbuild/linux-arm@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0"
integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==
"@esbuild/linux-arm@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c"
integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==
"@esbuild/linux-arm@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
"@esbuild/linux-ia32@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7"
integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==
"@esbuild/linux-ia32@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa"
integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==
"@esbuild/linux-ia32@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2"
integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==
"@esbuild/linux-loong64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d"
integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==
"@esbuild/linux-loong64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5"
integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==
"@esbuild/linux-loong64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
"@esbuild/linux-mips64el@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231"
integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==
"@esbuild/linux-mips64el@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa"
integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==
"@esbuild/linux-mips64el@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
"@esbuild/linux-ppc64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb"
integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==
"@esbuild/linux-ppc64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20"
integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==
"@esbuild/linux-ppc64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
"@esbuild/linux-riscv64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6"
integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==
"@esbuild/linux-riscv64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300"
integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==
"@esbuild/linux-riscv64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
"@esbuild/linux-s390x@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071"
integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==
"@esbuild/linux-s390x@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685"
integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==
"@esbuild/linux-s390x@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
"@esbuild/linux-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338"
integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==
"@esbuild/linux-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff"
integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==
"@esbuild/linux-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0"
integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
"@esbuild/netbsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1"
integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==
"@esbuild/netbsd-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6"
integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==
"@esbuild/netbsd-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
"@esbuild/openbsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae"
integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==
"@esbuild/openbsd-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf"
integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==
"@esbuild/openbsd-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
"@esbuild/sunos-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d"
integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==
"@esbuild/sunos-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f"
integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==
"@esbuild/sunos-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
"@esbuild/win32-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9"
integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==
"@esbuild/win32-arm64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90"
integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==
"@esbuild/win32-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
"@esbuild/win32-ia32@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102"
integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==
"@esbuild/win32-ia32@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23"
integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==
"@esbuild/win32-ia32@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
"@esbuild/win32-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d"
integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==
"@esbuild/win32-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc"
integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==
"@esbuild/win32-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c"
integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0"
@ -5941,9 +5941,9 @@
integrity sha512-7GgtHCs/QZrBrDzgIJnQtuSvhFSwhyYSI2uafSwZoNt1iOGhEN5fwNrQMjtONyHm9+/LoA4453jH0CMYcr06Pg==
"@types/node@>=8.1.0":
version "20.14.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.2.tgz#a5f4d2bcb4b6a87bffcaa717718c5a0f208f4a18"
integrity sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==
version "20.14.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.5.tgz#fe35e3022ebe58b8f201580eb24e1fcfc0f2487d"
integrity sha512-aoRR+fJkZT2l0aGOJhuA8frnCSoNX6W7U2mpNq63+BxBIj5BQFt8rHy627kijCmm63ijdSdwvGgpUsU6MBsZZA==
dependencies:
undici-types "~5.26.4"
@ -6881,11 +6881,18 @@ acorn-walk@^8.0.2, acorn-walk@^8.1.1:
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
acorn-walk@^8.2.0, acorn-walk@^8.3.2:
acorn-walk@^8.2.0:
version "8.3.2"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa"
integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==
acorn-walk@^8.3.2:
version "8.3.3"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e"
integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==
dependencies:
acorn "^8.11.0"
acorn@^5.2.1, acorn@^5.7.3:
version "5.7.4"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
@ -6901,6 +6908,11 @@ acorn@^8.1.0, acorn@^8.10.0, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b"
integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==
acorn@^8.11.0:
version "8.12.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c"
integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==
acorn@^8.11.3, acorn@^8.8.1:
version "8.11.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
@ -10542,34 +10554,34 @@ esbuild@^0.18.10, esbuild@^0.18.17:
"@esbuild/win32-ia32" "0.18.20"
"@esbuild/win32-x64" "0.18.20"
esbuild@^0.20.1:
version "0.20.2"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1"
integrity sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==
esbuild@^0.21.3:
version "0.21.5"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
optionalDependencies:
"@esbuild/aix-ppc64" "0.20.2"
"@esbuild/android-arm" "0.20.2"
"@esbuild/android-arm64" "0.20.2"
"@esbuild/android-x64" "0.20.2"
"@esbuild/darwin-arm64" "0.20.2"
"@esbuild/darwin-x64" "0.20.2"
"@esbuild/freebsd-arm64" "0.20.2"
"@esbuild/freebsd-x64" "0.20.2"
"@esbuild/linux-arm" "0.20.2"
"@esbuild/linux-arm64" "0.20.2"
"@esbuild/linux-ia32" "0.20.2"
"@esbuild/linux-loong64" "0.20.2"
"@esbuild/linux-mips64el" "0.20.2"
"@esbuild/linux-ppc64" "0.20.2"
"@esbuild/linux-riscv64" "0.20.2"
"@esbuild/linux-s390x" "0.20.2"
"@esbuild/linux-x64" "0.20.2"
"@esbuild/netbsd-x64" "0.20.2"
"@esbuild/openbsd-x64" "0.20.2"
"@esbuild/sunos-x64" "0.20.2"
"@esbuild/win32-arm64" "0.20.2"
"@esbuild/win32-ia32" "0.20.2"
"@esbuild/win32-x64" "0.20.2"
"@esbuild/aix-ppc64" "0.21.5"
"@esbuild/android-arm" "0.21.5"
"@esbuild/android-arm64" "0.21.5"
"@esbuild/android-x64" "0.21.5"
"@esbuild/darwin-arm64" "0.21.5"
"@esbuild/darwin-x64" "0.21.5"
"@esbuild/freebsd-arm64" "0.21.5"
"@esbuild/freebsd-x64" "0.21.5"
"@esbuild/linux-arm" "0.21.5"
"@esbuild/linux-arm64" "0.21.5"
"@esbuild/linux-ia32" "0.21.5"
"@esbuild/linux-loong64" "0.21.5"
"@esbuild/linux-mips64el" "0.21.5"
"@esbuild/linux-ppc64" "0.21.5"
"@esbuild/linux-riscv64" "0.21.5"
"@esbuild/linux-s390x" "0.21.5"
"@esbuild/linux-x64" "0.21.5"
"@esbuild/netbsd-x64" "0.21.5"
"@esbuild/openbsd-x64" "0.21.5"
"@esbuild/sunos-x64" "0.21.5"
"@esbuild/win32-arm64" "0.21.5"
"@esbuild/win32-ia32" "0.21.5"
"@esbuild/win32-x64" "0.21.5"
escalade@^3.1.1:
version "3.1.1"
@ -18139,9 +18151,9 @@ posthog-js@^1.118.0:
preact "^10.19.3"
posthog-js@^1.13.4:
version "1.139.1"
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.139.1.tgz#25db31d94ce218357a2be43be4a55cfbb940f295"
integrity sha512-+JDu2S7z6sh9Q5kj0oh/W8PZJMQ1gSigWi7gbY4NwwCq2M3t0wNFjxlfHbAo1GncRWDxen+IC+3J7oJ8TJGnkA==
version "1.139.2"
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.139.2.tgz#f8de29edf2770da47fcccb7838902d1e89d6b43d"
integrity sha512-myyuOADqZvYwgqmriwlKDEUDwLhscivFLh67UWBj4Wt9kOlmklvJb36W0ES2GAS6IdojbnGZGH5lF3heqreLWQ==
dependencies:
fflate "^0.4.8"
preact "^10.19.3"
@ -22195,11 +22207,11 @@ vite@^4.5.0:
fsevents "~2.3.2"
vite@^5.0.0:
version "5.2.13"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.13.tgz#945ababcbe3d837ae2479c29f661cd20bc5e1a80"
integrity sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==
version "5.3.1"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.3.1.tgz#bb2ca6b5fd7483249d3e86b25026e27ba8a663e6"
integrity sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==
dependencies:
esbuild "^0.20.1"
esbuild "^0.21.3"
postcss "^8.4.38"
rollup "^4.13.0"
optionalDependencies:
@ -22640,14 +22652,14 @@ write-stream@~0.4.3:
readable-stream "~0.0.2"
ws@^7.4.6:
version "7.5.9"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
version "7.5.10"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9"
integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==
ws@^8.13.0:
version "8.13.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
version "8.17.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"
integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==
ws@~8.11.0:
version "8.11.0"