Merge branch 'develop' of github.com:Budibase/budibase into views-v2-frontend

This commit is contained in:
Andrew Kingston 2023-07-26 16:28:21 +01:00
commit c4e4b5c979
47 changed files with 1161 additions and 244 deletions

View File

@ -1,4 +1,4 @@
name: Deploy Budibase Single Container Image to DockerHub name: release-singleimage
on: on:
workflow_dispatch: workflow_dispatch:
@ -8,8 +8,8 @@ env:
PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
REGISTRY_URL: registry.hub.docker.com REGISTRY_URL: registry.hub.docker.com
jobs: jobs:
build: build-amd64:
name: "build" name: "build-amd64"
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
@ -27,14 +27,12 @@ jobs:
submodules: true submodules: true
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
fetch-depth: 0 fetch-depth: 0
- name: Fail if tag is not in master - name: Fail if tag is not in master
run: | run: |
if ! git merge-base --is-ancestor ${{ github.sha }} origin/master; then if ! git merge-base --is-ancestor ${{ github.sha }} origin/master; then
echo "Tag is not in master. This pipeline can only execute tags that are present on the master branch" echo "Tag is not in master. This pipeline can only execute tags that are present on the master branch"
exit 1 exit 1
fi fi
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
@ -70,9 +68,139 @@ jobs:
with: with:
context: . context: .
push: true push: true
platforms: linux/amd64,linux/arm64 platforms: linux/amd64
tags: budibase/budibase,budibase/budibase:${{ env.RELEASE_VERSION }} tags: budibase/budibase,budibase/budibase:v${{ env.RELEASE_VERSION }}
file: ./hosting/single/Dockerfile file: ./hosting/single/Dockerfile
- name: Tag and release Budibase Azure App Service docker image
uses: docker/build-push-action@v2
with:
context: .
push: true
platforms: linux/amd64
build-args: TARGETBUILD=aas
tags: budibase/budibase-aas,budibase/budibase-aas:v${{ env.RELEASE_VERSION }}
file: ./hosting/single/Dockerfile
build-arm64:
name: "build-arm64"
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x]
steps:
- name: Fail if not a tag
run: |
if [[ $GITHUB_REF != refs/tags/* ]]; then
echo "Workflow Dispatch can only be run on tags"
exit 1
fi
- name: "Checkout"
uses: actions/checkout@v2
with:
submodules: true
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
fetch-depth: 0
- name: Fail if tag is not in master
run: |
if ! git merge-base --is-ancestor ${{ github.sha }} origin/master; then
echo "Tag is not in master. This pipeline can only execute tags that are present on the master branch"
exit 1
fi
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Setup QEMU
uses: docker/setup-qemu-action@v1
- name: Setup Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Run Yarn
run: yarn
- name: Update versions
run: ./scripts/updateVersions.sh
- name: Runt Yarn Lint
run: yarn lint
- name: Update versions
run: ./scripts/updateVersions.sh
- name: Run Yarn Build
run: yarn build:docker:pre
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_API_KEY }}
- name: Get the latest release version
id: version
run: |
release_version=$(cat lerna.json | jq -r '.version')
echo $release_version
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
- name: Tag and release Budibase service docker image
uses: docker/build-push-action@v2
with:
context: .
push: true
platforms: linux/arm64
tags: budibase/budibase,budibase/budibase:v${{ env.RELEASE_VERSION }}
file: ./hosting/single/Dockerfile
build-aas:
name: "build-aas"
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x]
steps:
- name: Fail if not a tag
run: |
if [[ $GITHUB_REF != refs/tags/* ]]; then
echo "Workflow Dispatch can only be run on tags"
exit 1
fi
- name: "Checkout"
uses: actions/checkout@v2
with:
submodules: true
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
fetch-depth: 0
- name: Fail if tag is not in master
run: |
if ! git merge-base --is-ancestor ${{ github.sha }} origin/master; then
echo "Tag is not in master. This pipeline can only execute tags that are present on the master branch"
exit 1
fi
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Setup QEMU
uses: docker/setup-qemu-action@v1
- name: Setup Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Run Yarn
run: yarn
- name: Update versions
run: ./scripts/updateVersions.sh
- name: Runt Yarn Lint
run: yarn lint
- name: Update versions
run: ./scripts/updateVersions.sh
- name: Run Yarn Build
run: yarn build:docker:pre
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_API_KEY }}
- name: Get the latest release version
id: version
run: |
release_version=$(cat lerna.json | jq -r '.version')
echo $release_version
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
- name: Tag and release Budibase Azure App Service docker image - name: Tag and release Budibase Azure App Service docker image
uses: docker/build-push-action@v2 uses: docker/build-push-action@v2
with: with:

View File

@ -1,5 +1,5 @@
{ {
"version": "2.8.22-alpha.4", "version": "2.8.28-alpha.0",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -6,8 +6,8 @@
"@nx/js": "16.4.3", "@nx/js": "16.4.3",
"@rollup/plugin-json": "^4.0.2", "@rollup/plugin-json": "^4.0.2",
"@typescript-eslint/parser": "5.45.0", "@typescript-eslint/parser": "5.45.0",
"esbuild": "^0.17.18", "esbuild": "^0.18.17",
"esbuild-node-externals": "^1.7.0", "esbuild-node-externals": "^1.8.0",
"eslint": "^8.44.0", "eslint": "^8.44.0",
"eslint-plugin-cypress": "^2.11.3", "eslint-plugin-cypress": "^2.11.3",
"husky": "^8.0.3", "husky": "^8.0.3",
@ -51,9 +51,9 @@
"kill-builder": "kill-port 3000", "kill-builder": "kill-port 3000",
"kill-server": "kill-port 4001 4002", "kill-server": "kill-port 4001 4002",
"kill-all": "yarn run kill-builder && yarn run kill-server", "kill-all": "yarn run kill-builder && yarn run kill-server",
"dev": "yarn run kill-all && lerna run --stream dev:builder --stream", "dev": "yarn run kill-all && yarn nx run-many --target=dev:builder",
"dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && lerna run --stream dev:builder --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker", "dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && yarn nx run-many --target=dev:builder --exclude=@budibase/backend-core,@budibase/server,@budibase/worker",
"dev:server": "yarn run kill-server && lerna run --stream dev:builder --scope @budibase/worker --scope @budibase/server", "dev:server": "yarn run kill-server && yarn nx run-many --target=dev:builder --projects=@budibase/worker,@budibase/server",
"dev:built": "yarn run kill-all && cd packages/server && yarn dev:stack:up && cd ../../ && lerna run --stream dev:built", "dev:built": "yarn run kill-all && cd packages/server && yarn dev:stack:up && cd ../../ && lerna run --stream dev:built",
"dev:docker": "yarn build:docker:pre && docker-compose -f hosting/docker-compose.build.yaml -f hosting/docker-compose.dev.yaml --env-file hosting/.env up --build --scale proxy-service=0", "dev:docker": "yarn build:docker:pre && docker-compose -f hosting/docker-compose.build.yaml -f hosting/docker-compose.dev.yaml --env-file hosting/.env up --build --scale proxy-service=0",
"test": "lerna run --stream test --stream", "test": "lerna run --stream test --stream",

View File

@ -0,0 +1,10 @@
export const CONSTANT_INTERNAL_ROW_COLS = [
"_id",
"_rev",
"type",
"createdAt",
"updatedAt",
"tableId",
] as const
export const CONSTANT_EXTERNAL_ROW_COLS = ["_id", "_rev", "tableId"] as const

View File

@ -2,3 +2,4 @@ export * from "./connections"
export * from "./DatabaseImpl" export * from "./DatabaseImpl"
export * from "./utils" export * from "./utils"
export { init, getPouch, getPouchDB, closePouchDB } from "./pouchDB" export { init, getPouch, getPouchDB, closePouchDB } from "./pouchDB"
export * from "../constants"

View File

@ -1,3 +1,5 @@
import { db } from "../../../src"
export function expectFunctionWasCalledTimesWith( export function expectFunctionWasCalledTimesWith(
jestFunction: any, jestFunction: any,
times: number, times: number,
@ -7,3 +9,22 @@ export function expectFunctionWasCalledTimesWith(
jestFunction.mock.calls.filter((call: any) => call[0] === argument).length jestFunction.mock.calls.filter((call: any) => call[0] === argument).length
).toBe(times) ).toBe(times)
} }
export const expectAnyInternalColsAttributes: {
[K in (typeof db.CONSTANT_INTERNAL_ROW_COLS)[number]]: any
} = {
tableId: expect.anything(),
type: expect.anything(),
_id: expect.anything(),
_rev: expect.anything(),
createdAt: expect.anything(),
updatedAt: expect.anything(),
}
export const expectAnyExternalColsAttributes: {
[K in (typeof db.CONSTANT_EXTERNAL_ROW_COLS)[number]]: any
} = {
tableId: expect.anything(),
_id: expect.anything(),
_rev: expect.anything(),
}

View File

@ -64,7 +64,7 @@ export default function positionDropdown(element, opts) {
// Apply styles // Apply styles
Object.entries(styles).forEach(([style, value]) => { Object.entries(styles).forEach(([style, value]) => {
if (value) { if (value != null) {
element.style[style] = `${value.toFixed(0)}px` element.style[style] = `${value.toFixed(0)}px`
} else { } else {
element.style[style] = null element.style[style] = null

View File

@ -491,6 +491,7 @@ const getSelectedRowsBindings = asset => {
readableBinding: `${table._instanceName}.Selected rows`, readableBinding: `${table._instanceName}.Selected rows`,
category: "Selected rows", category: "Selected rows",
icon: "ViewRow", icon: "ViewRow",
display: { name: table._instanceName },
})) }))
) )
@ -506,6 +507,7 @@ const getSelectedRowsBindings = asset => {
)}.${makePropSafe("selectedRows")}`, )}.${makePropSafe("selectedRows")}`,
readableBinding: `${block._instanceName}.Selected rows`, readableBinding: `${block._instanceName}.Selected rows`,
category: "Selected rows", category: "Selected rows",
display: { name: block._instanceName },
})) }))
) )
} }

View File

@ -1,5 +1,5 @@
<script> <script>
import { datasources, tables } from "stores/backend" import { datasources, tables, integrations } from "stores/backend"
import EditRolesButton from "./buttons/EditRolesButton.svelte" import EditRolesButton from "./buttons/EditRolesButton.svelte"
import { TableNames } from "constants" import { TableNames } from "constants"
import { Grid } from "@budibase/frontend-core" import { Grid } from "@budibase/frontend-core"
@ -31,6 +31,17 @@
tableId: id, tableId: id,
} }
$: datasource = $datasources.list.find(datasource => {
return datasource._id === $tables.selected?.sourceId
})
$: relationshipsEnabled = relationshipSupport(datasource)
const relationshipSupport = datasource => {
const integration = $integrations[datasource?.source]
return !isInternal && integration?.relationships !== false
}
const handleGridTableUpdate = async e => { const handleGridTableUpdate = async e => {
tables.replaceTable(id, e.detail) tables.replaceTable(id, e.detail)
@ -62,7 +73,7 @@
<GridCreateViewButton /> <GridCreateViewButton />
{/if} {/if}
<GridManageAccessButton /> <GridManageAccessButton />
{#if !isInternal} {#if relationshipsEnabled}
<GridRelationshipButton /> <GridRelationshipButton />
{/if} {/if}
{#if isUsersTable} {#if isUsersTable}

View File

@ -341,7 +341,7 @@
</Tab> </Tab>
{/if} {/if}
<div class="drawer-actions"> <div class="drawer-actions">
{#if typeof drawerActions.hide === "function" && drawerActions.headless} {#if typeof drawerActions?.hide === "function" && drawerActions?.headless}
<Button <Button
secondary secondary
quiet quiet
@ -352,7 +352,7 @@
Cancel Cancel
</Button> </Button>
{/if} {/if}
{#if typeof bindingDrawerActions?.save === "function" && drawerActions.headless} {#if typeof bindingDrawerActions?.save === "function" && drawerActions?.headless}
<Button <Button
cta cta
disabled={!valid} disabled={!valid}

View File

@ -206,6 +206,11 @@
return allBindings return allBindings
} }
const toDisplay = eventKey => {
const type = actionTypes.find(action => action.name == eventKey)
return type?.displayName || type?.name
}
</script> </script>
<DrawerContent> <DrawerContent>
@ -231,7 +236,9 @@
<ul> <ul>
{#each category as actionType} {#each category as actionType}
<li on:click={onAddAction(actionType)}> <li on:click={onAddAction(actionType)}>
<span class="action-name">{actionType.name}</span> <span class="action-name">
{actionType.displayName || actionType.name}
</span>
</li> </li>
{/each} {/each}
</ul> </ul>
@ -262,7 +269,7 @@
> >
<Icon name="DragHandle" size="XL" /> <Icon name="DragHandle" size="XL" />
<div class="action-header"> <div class="action-header">
{index + 1}.&nbsp;{action[EVENT_TYPE_KEY]} {index + 1}.&nbsp;{toDisplay(action[EVENT_TYPE_KEY])}
</div> </div>
<Icon <Icon
name="Close" name="Close"

View File

@ -1,5 +1,5 @@
<script> <script>
import { Select, Label, Checkbox, Input } from "@budibase/bbui" import { Select, Label, Checkbox, Input, Body } from "@budibase/bbui"
import { tables } from "stores/backend" import { tables } from "stores/backend"
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
@ -10,47 +10,59 @@
</script> </script>
<div class="root"> <div class="root">
<Label>Table</Label> <Body size="small">Please specify one or more rows to delete.</Body>
<Select <div class="params">
bind:value={parameters.tableId} <Label>Table</Label>
options={tableOptions} <Select
getOptionLabel={table => table.name} bind:value={parameters.tableId}
getOptionValue={table => table._id} options={tableOptions}
/> getOptionLabel={table => table.name}
getOptionValue={table => table._id}
<Label small>Row ID</Label>
<DrawerBindableInput
{bindings}
title="Row ID to delete"
value={parameters.rowId}
on:change={value => (parameters.rowId = value.detail)}
/>
<Label small />
<Checkbox
text="Do not display default notification"
bind:value={parameters.notificationOverride}
/>
<br />
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
{#if parameters.confirm}
<Label small>Confirm text</Label>
<Input
placeholder="Are you sure you want to delete this row?"
bind:value={parameters.confirmText}
/> />
{/if}
<Label small>Row IDs</Label>
<DrawerBindableInput
{bindings}
title="Rows to delete"
value={parameters.rowId}
on:change={value => (parameters.rowId = value.detail)}
/>
<Label small />
<Checkbox
text="Do not display default notification"
bind:value={parameters.notificationOverride}
/>
<br />
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
{#if parameters.confirm}
<Label small>Confirm text</Label>
<Input
placeholder="Are you sure you want to delete?"
bind:value={parameters.confirmText}
/>
{/if}
</div>
</div> </div>
<style> <style>
.root { .root {
width: 100%;
max-width: 800px;
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-xl);
}
.params {
display: grid; display: grid;
column-gap: var(--spacing-l); column-gap: var(--spacing-l);
row-gap: var(--spacing-s); row-gap: var(--spacing-s);
grid-template-columns: 60px 1fr; grid-template-columns: 60px 1fr;
align-items: center; align-items: center;
max-width: 800px;
margin: 0 auto;
} }
</style> </style>

View File

@ -73,9 +73,12 @@
{#if query?.parameters?.length > 0} {#if query?.parameters?.length > 0}
<div class="params"> <div class="params">
<BindingBuilder <BindingBuilder
bind:customParams={parameters.queryParams} customParams={parameters.queryParams}
queryBindings={query.parameters} queryBindings={query.parameters}
bind:bindings bind:bindings
on:change={v => {
parameters.queryParams = { ...v.detail }
}}
/> />
<IntegrationQueryEditor <IntegrationQueryEditor
height={200} height={200}

View File

@ -24,6 +24,7 @@
}, },
{ {
"name": "Delete Row", "name": "Delete Row",
"displayName": "Delete Rows",
"type": "data", "type": "data",
"component": "DeleteRow" "component": "DeleteRow"
}, },

View File

@ -143,13 +143,12 @@
} }
const openQueryParamsDrawer = () => { const openQueryParamsDrawer = () => {
tmpQueryParams = value.queryParams tmpQueryParams = { ...value.queryParams }
drawer.show() drawer.show()
} }
const getQueryValue = queries => { const getQueryValue = queries => {
value = queries.find(q => q._id === value._id) || value return queries.find(q => q._id === value._id) || value
return value
} }
const saveQueryParams = () => { const saveQueryParams = () => {
@ -176,7 +175,10 @@
<Layout noPadding gap="XS"> <Layout noPadding gap="XS">
{#if getQueryParams(value).length > 0} {#if getQueryParams(value).length > 0}
<BindingBuilder <BindingBuilder
bind:customParams={tmpQueryParams} customParams={tmpQueryParams}
on:change={v => {
tmpQueryParams = { ...v.detail }
}}
queryBindings={getQueryParams(value)} queryBindings={getQueryParams(value)}
bind:bindings bind:bindings
/> />

View File

@ -5,6 +5,9 @@
runtimeToReadableBinding, runtimeToReadableBinding,
} from "builderStore/dataBinding" } from "builderStore/dataBinding"
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
import { createEventDispatcher } from "svelte"
const dispatch = createEventDispatcher()
export let bindable = true export let bindable = true
export let queryBindings = [] export let queryBindings = []
@ -20,7 +23,10 @@
// The readable binding in the UI gets converted to a UUID value that the client understands // The readable binding in the UI gets converted to a UUID value that the client understands
// for parsing, then converted back so we can display it the readable form in the UI // for parsing, then converted back so we can display it the readable form in the UI
function onBindingChange(param, valueToParse) { function onBindingChange(param, valueToParse) {
customParams[param] = readableToRuntimeBinding(bindings, valueToParse) dispatch("change", {
...customParams,
[param]: readableToRuntimeBinding(bindings, valueToParse),
})
} }
</script> </script>

View File

@ -14,8 +14,9 @@
Tab, Tab,
Modal, Modal,
ModalContent, ModalContent,
notifications,
Divider,
} from "@budibase/bbui" } from "@budibase/bbui"
import { notifications, Divider } from "@budibase/bbui"
import ExtraQueryConfig from "./ExtraQueryConfig.svelte" import ExtraQueryConfig from "./ExtraQueryConfig.svelte"
import IntegrationQueryEditor from "components/integration/index.svelte" import IntegrationQueryEditor from "components/integration/index.svelte"
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte" import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
@ -28,6 +29,7 @@
import KeyValueBuilder from "./KeyValueBuilder.svelte" import KeyValueBuilder from "./KeyValueBuilder.svelte"
import { fieldsToSchema, schemaToFields } from "helpers/data/utils" import { fieldsToSchema, schemaToFields } from "helpers/data/utils"
import AccessLevelSelect from "./AccessLevelSelect.svelte" import AccessLevelSelect from "./AccessLevelSelect.svelte"
import { ValidQueryNameRegex } from "@budibase/shared-core"
export let query export let query
@ -47,6 +49,7 @@
let saveModal let saveModal
let override = false let override = false
let navigateTo = null let navigateTo = null
let nameError = null
// seed the transformer // seed the transformer
if (query && !query.transformer) { if (query && !query.transformer) {
@ -77,7 +80,7 @@
$: queryConfig = integrationInfo?.query $: queryConfig = integrationInfo?.query
$: shouldShowQueryConfig = queryConfig && query.queryVerb $: shouldShowQueryConfig = queryConfig && query.queryVerb
$: readQuery = query.queryVerb === "read" || query.readable $: readQuery = query.queryVerb === "read" || query.readable
$: queryInvalid = !query.name || (readQuery && data.length === 0) $: queryInvalid = !query.name || nameError || (readQuery && data.length === 0)
//Cast field in query preview response to number if specified by schema //Cast field in query preview response to number if specified by schema
$: { $: {
@ -139,9 +142,10 @@
queryStr = JSON.stringify(query) queryStr = JSON.stringify(query)
} }
notifications.success("Query saved successfully")
return response return response
} catch (error) { } catch (error) {
notifications.error("Error saving query") notifications.error(error.message || "Error saving query")
} }
} }
</script> </script>
@ -183,8 +187,14 @@
value={query.name} value={query.name}
on:input={e => { on:input={e => {
let newValue = e.target.value || "" let newValue = e.target.value || ""
query.name = newValue.trim() if (newValue.match(ValidQueryNameRegex)) {
query.name = newValue.trim()
nameError = null
} else {
nameError = "Invalid query name"
}
}} }}
error={nameError}
/> />
</div> </div>
{#if queryConfig} {#if queryConfig}
@ -250,9 +260,9 @@
size="L" size="L"
/> />
</div> </div>
<Body size="S" <Body size="S">
>Add a JavaScript function to transform the query result.</Body Add a JavaScript function to transform the query result.
> </Body>
<CodeMirrorEditor <CodeMirrorEditor
height={200} height={200}
label="Transformer" label="Transformer"
@ -264,13 +274,12 @@
</div> </div>
<div class="viewer-controls"> <div class="viewer-controls">
<Heading size="S">Results</Heading> <Heading size="S">Results</Heading>
<ButtonGroup gap="XS"> <ButtonGroup gap="S">
<Button <Button
cta cta
disabled={queryInvalid} disabled={queryInvalid}
on:click={async () => { on:click={async () => {
await saveQuery() await saveQuery()
notifications.success(`Query saved successfully`)
// Go to the correct URL if we just created a new query // Go to the correct URL if we just created a new query
if (!query._rev) { if (!query._rev) {
$goto(`../../${query._id}`) $goto(`../../${query._id}`)

View File

@ -47,6 +47,14 @@
) )
} }
// If the data changes, double check that the selected elements are still present.
$: if (data) {
let rowIds = data.map(row => row._id)
if (rowIds.length) {
selectedRows = selectedRows.filter(row => rowIds.includes(row._id))
}
}
const getFields = (schema, customColumns, showAutoColumns) => { const getFields = (schema, customColumns, showAutoColumns) => {
// Check for an invalid column selection // Check for an invalid column selection
let invalid = false let invalid = false

View File

@ -102,12 +102,46 @@ const fetchRowHandler = async action => {
} }
const deleteRowHandler = async action => { const deleteRowHandler = async action => {
const { tableId, revId, rowId, notificationOverride } = action.parameters const { tableId, rowId: rowConfig, notificationOverride } = action.parameters
if (tableId && rowId) {
if (tableId && rowConfig) {
try { try {
await API.deleteRow({ tableId, rowId, revId }) let requestConfig
let parsedRowConfig = []
if (typeof rowConfig === "string") {
try {
parsedRowConfig = JSON.parse(rowConfig)
} catch (e) {
parsedRowConfig = rowConfig
.split(",")
.map(id => id.trim())
.filter(id => id)
}
} else {
parsedRowConfig = rowConfig
}
if (
typeof parsedRowConfig === "object" &&
parsedRowConfig.constructor === Object
) {
requestConfig = [parsedRowConfig]
} else if (Array.isArray(parsedRowConfig)) {
requestConfig = parsedRowConfig
}
if (!requestConfig.length) {
notificationStore.actions.warning("No valid rows were supplied")
return false
}
const resp = await API.deleteRows({ tableId, rows: requestConfig })
if (!notificationOverride) { if (!notificationOverride) {
notificationStore.actions.success("Row deleted") notificationStore.actions.success(
resp?.length == 1 ? "Row deleted" : `${resp.length} Rows deleted`
)
} }
// Refresh related datasources // Refresh related datasources
@ -115,8 +149,10 @@ const deleteRowHandler = async action => {
invalidateRelationships: true, invalidateRelationships: true,
}) })
} catch (error) { } catch (error) {
// Abort next actions console.error(error)
return false notificationStore.actions.error(
"An error occurred while executing the query"
)
} }
} }
} }

@ -1 +1 @@
Subproject commit 347ee5326812c01ef07f0e744f691ab4823e185a Subproject commit a60183319f410d05aaa1c2f2718b772978b54d64

View File

@ -183,6 +183,16 @@
}, },
"nx": { "nx": {
"targets": { "targets": {
"dev:builder": {
"dependsOn": [
{
"projects": [
"@budibase/backend-core"
],
"target": "build"
}
]
},
"test": { "test": {
"dependsOn": [ "dependsOn": [
{ {

View File

@ -43,6 +43,12 @@ CREATE TABLE test.table1 (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
Name varchar(255) Name varchar(255)
); );
CREATE TABLE CompositeTable (
KeyPartOne varchar(128),
KeyPartTwo varchar(128),
Name varchar(255),
PRIMARY KEY (KeyPartOne, KeyPartTwo)
);
INSERT INTO Persons (FirstName, LastName, Address, City, Type) VALUES ('Mike', 'Hughes', '123 Fake Street', 'Belfast', 'qa'); INSERT INTO Persons (FirstName, LastName, Address, City, Type) VALUES ('Mike', 'Hughes', '123 Fake Street', 'Belfast', 'qa');
INSERT INTO Persons (FirstName, LastName, Address, City, Type) VALUES ('John', 'Smith', '64 Updown Road', 'Dublin', 'programmer'); INSERT INTO Persons (FirstName, LastName, Address, City, Type) VALUES ('John', 'Smith', '64 Updown Road', 'Dublin', 'programmer');
INSERT INTO Tasks (ExecutorID, QaID, TaskName, Completed) VALUES (1, 2, 'assembling', TRUE); INSERT INTO Tasks (ExecutorID, QaID, TaskName, Completed) VALUES (1, 2, 'assembling', TRUE);
@ -55,3 +61,6 @@ INSERT INTO Products_Tasks (ProductID, TaskID) VALUES (2, 1);
INSERT INTO Products_Tasks (ProductID, TaskID) VALUES (3, 1); INSERT INTO Products_Tasks (ProductID, TaskID) VALUES (3, 1);
INSERT INTO Products_Tasks (ProductID, TaskID) VALUES (1, 2); INSERT INTO Products_Tasks (ProductID, TaskID) VALUES (1, 2);
INSERT INTO test.table1 (Name) VALUES ('Test'); INSERT INTO test.table1 (Name) VALUES ('Test');
INSERT INTO CompositeTable (KeyPartOne, KeyPartTwo, Name) VALUES ('aaa', 'bbb', 'Michael');
INSERT INTO CompositeTable (KeyPartOne, KeyPartTwo, Name) VALUES ('bbb', 'ccc', 'Andrew');
INSERT INTO CompositeTable (KeyPartOne, KeyPartTwo, Name) VALUES ('ddd', '', 'OneKey');

View File

@ -5,7 +5,7 @@ import { convertBookmark } from "../../../utilities"
// makes sure that the user doesn't need to pass in the type, tableId or _id params for // makes sure that the user doesn't need to pass in the type, tableId or _id params for
// the call to be correct // the call to be correct
function fixRow(row: Row, params: any) { export function fixRow(row: Row, params: any) {
if (!params || !row) { if (!params || !row) {
return row return row
} }

View File

@ -10,6 +10,7 @@ import { events, context, utils, constants } from "@budibase/backend-core"
import sdk from "../../../sdk" import sdk from "../../../sdk"
import { QueryEvent } from "../../../threads/definitions" import { QueryEvent } from "../../../threads/definitions"
import { Query } from "@budibase/types" import { Query } from "@budibase/types"
import { ValidQueryNameRegex } from "@budibase/shared-core"
const Runner = new Thread(ThreadType.QUERY, { const Runner = new Thread(ThreadType.QUERY, {
timeoutMs: env.QUERY_THREAD_TIMEOUT || 10000, timeoutMs: env.QUERY_THREAD_TIMEOUT || 10000,
@ -76,6 +77,11 @@ export async function save(ctx: any) {
const db = context.getAppDB() const db = context.getAppDB()
const query = ctx.request.body const query = ctx.request.body
// Validate query name
if (!query?.name.match(ValidQueryNameRegex)) {
ctx.throw(400, "Invalid query name")
}
const datasource = await sdk.datasources.get(query.datasourceId) const datasource = await sdk.datasources.get(query.datasourceId)
let eventFn let eventFn

View File

@ -163,7 +163,7 @@ function generateIdForRow(
fieldName: field, fieldName: field,
isLinked, isLinked,
}) })
if (fieldValue) { if (fieldValue != null) {
idParts.push(fieldValue) idParts.push(fieldValue)
} }
} }

View File

@ -4,6 +4,11 @@ import * as external from "./external"
import { isExternalTable } from "../../../integrations/utils" import { isExternalTable } from "../../../integrations/utils"
import { import {
Ctx, Ctx,
UserCtx,
DeleteRowRequest,
DeleteRow,
DeleteRows,
Row,
SearchResponse, SearchResponse,
SortOrder, SortOrder,
SortType, SortType,
@ -11,6 +16,8 @@ import {
} from "@budibase/types" } from "@budibase/types"
import * as utils from "./utils" import * as utils from "./utils"
import { gridSocket } from "../../../websockets" import { gridSocket } from "../../../websockets"
import { addRev } from "../public/utils"
import { fixRow } from "../public/rows"
import sdk from "../../../sdk" import sdk from "../../../sdk"
import * as exporters from "../view/exporters" import * as exporters from "../view/exporters"
import { apiFileReturn } from "../../../utilities/fileSystem" import { apiFileReturn } from "../../../utilities/fileSystem"
@ -104,35 +111,83 @@ export async function find(ctx: any) {
}) })
} }
export async function destroy(ctx: any) { function isDeleteRows(input: any): input is DeleteRows {
const appId = ctx.appId return input.rows !== undefined && Array.isArray(input.rows)
const inputs = ctx.request.body }
function isDeleteRow(input: any): input is DeleteRow {
return input._id !== undefined
}
async function processDeleteRowsRequest(ctx: UserCtx<DeleteRowRequest>) {
let request = ctx.request.body as DeleteRows
const tableId = utils.getTableId(ctx) const tableId = utils.getTableId(ctx)
let response, row
if (inputs.rows) { const processedRows = request.rows.map(row => {
let { rows } = await quotas.addQuery<any>( let processedRow: Row = typeof row == "string" ? { _id: row } : row
() => pickApi(tableId).bulkDestroy(ctx), return !processedRow._rev
{ ? addRev(fixRow(processedRow, ctx.params), tableId)
datasourceId: tableId, : fixRow(processedRow, ctx.params)
} })
)
await quotas.removeRows(rows.length) return await Promise.all(processedRows)
response = rows }
for (let row of rows) {
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row) async function deleteRows(ctx: UserCtx<DeleteRowRequest>) {
gridSocket?.emitRowDeletion(ctx, row._id!) const tableId = utils.getTableId(ctx)
} const appId = ctx.appId
} else {
let resp = await quotas.addQuery<any>(() => pickApi(tableId).destroy(ctx), { let deleteRequest = ctx.request.body as DeleteRows
const rowDeletes: Row[] = await processDeleteRowsRequest(ctx)
deleteRequest.rows = rowDeletes
let { rows } = await quotas.addQuery<any>(
() => pickApi(tableId).bulkDestroy(ctx),
{
datasourceId: tableId, datasourceId: tableId,
}) }
await quotas.removeRow() )
response = resp.response await quotas.removeRows(rows.length)
row = resp.row
for (let row of rows) {
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row) ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
gridSocket?.emitRowDeletion(ctx, row._id!) gridSocket?.emitRowDeletion(ctx, row._id!)
} }
return rows
}
async function deleteRow(ctx: UserCtx<DeleteRowRequest>) {
const appId = ctx.appId
const tableId = utils.getTableId(ctx)
let resp = await quotas.addQuery<any>(() => pickApi(tableId).destroy(ctx), {
datasourceId: tableId,
})
await quotas.removeRow()
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, resp.row)
gridSocket?.emitRowDeletion(ctx, resp.row._id)
return resp
}
export async function destroy(ctx: UserCtx<DeleteRowRequest>) {
let response, row
ctx.status = 200 ctx.status = 200
if (isDeleteRows(ctx.request.body)) {
response = await deleteRows(ctx)
} else if (isDeleteRow(ctx.request.body)) {
const deleteResp = await deleteRow(ctx)
response = deleteResp.response
row = deleteResp.row
} else {
ctx.status = 400
response = { message: "Invalid delete rows request" }
}
// for automations include the row that was deleted // for automations include the row that was deleted
ctx.row = row || {} ctx.row = row || {}
ctx.body = response ctx.body = response

View File

@ -1,5 +1,10 @@
import sdk from "../../../sdk" import sdk from "../../../sdk"
import { CreateViewRequest, Ctx, ViewResponse } from "@budibase/types" import {
CreateViewRequest,
Ctx,
UpdateViewRequest,
ViewResponse,
} from "@budibase/types"
export async function create(ctx: Ctx<CreateViewRequest, ViewResponse>) { export async function create(ctx: Ctx<CreateViewRequest, ViewResponse>) {
const view = ctx.request.body const view = ctx.request.body
@ -12,6 +17,25 @@ export async function create(ctx: Ctx<CreateViewRequest, ViewResponse>) {
} }
} }
export async function update(ctx: Ctx<UpdateViewRequest, ViewResponse>) {
const view = ctx.request.body
if (view.version !== 2) {
ctx.throw(400, "Only views V2 can be updated")
}
if (ctx.params.viewId !== view.id) {
ctx.throw(400, "View id does not match between the body and the uri path")
}
const { tableId } = view
const result = await sdk.views.update(tableId, view)
ctx.body = {
data: result,
}
}
export async function remove(ctx: Ctx) { export async function remove(ctx: Ctx) {
const { viewId } = ctx.params const { viewId } = ctx.params

View File

@ -5,7 +5,7 @@ tk.freeze(timestamp)
import { outputProcessing } from "../../../utilities/rowProcessor" import { outputProcessing } from "../../../utilities/rowProcessor"
import * as setup from "./utilities" import * as setup from "./utilities"
const { basicRow } = setup.structures const { basicRow } = setup.structures
import { context, tenancy } from "@budibase/backend-core" import { context, db, tenancy } from "@budibase/backend-core"
import { quotas } from "@budibase/pro" import { quotas } from "@budibase/pro"
import { import {
QuotaUsageType, QuotaUsageType,
@ -17,7 +17,11 @@ import {
SortType, SortType,
SortOrder, SortOrder,
} from "@budibase/types" } from "@budibase/types"
import { generator, structures } from "@budibase/backend-core/tests" import {
expectAnyInternalColsAttributes,
generator,
structures,
} from "@budibase/backend-core/tests"
describe("/rows", () => { describe("/rows", () => {
let request = setup.getRequest() let request = setup.getRequest()
@ -519,6 +523,81 @@ describe("/rows", () => {
await assertRowUsage(rowUsage - 2) await assertRowUsage(rowUsage - 2)
await assertQueryUsage(queryUsage + 1) await assertQueryUsage(queryUsage + 1)
}) })
it("should be able to delete a variety of row set types", async () => {
const row1 = await config.createRow()
const row2 = await config.createRow()
const row3 = await config.createRow()
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
const res = await request
.delete(`/api/${table._id}/rows`)
.send({
rows: [row1, row2._id, { _id: row3._id }],
})
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200)
expect(res.body.length).toEqual(3)
await loadRow(row1._id!, table._id!, 404)
await assertRowUsage(rowUsage - 3)
await assertQueryUsage(queryUsage + 1)
})
it("should accept a valid row object and delete the row", async () => {
const row1 = await config.createRow()
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
const res = await request
.delete(`/api/${table._id}/rows`)
.send(row1)
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200)
expect(res.body.id).toEqual(row1._id)
await loadRow(row1._id!, table._id!, 404)
await assertRowUsage(rowUsage - 1)
await assertQueryUsage(queryUsage + 1)
})
it("Should ignore malformed/invalid delete requests", async () => {
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
const res = await request
.delete(`/api/${table._id}/rows`)
.send({ not: "valid" })
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(400)
expect(res.body.message).toEqual("Invalid delete rows request")
const res2 = await request
.delete(`/api/${table._id}/rows`)
.send({ rows: 123 })
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(400)
expect(res2.body.message).toEqual("Invalid delete rows request")
const res3 = await request
.delete(`/api/${table._id}/rows`)
.send("invalid")
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(400)
expect(res3.body.message).toEqual("Invalid delete rows request")
await assertRowUsage(rowUsage)
await assertQueryUsage(queryUsage)
})
}) })
describe("fetchView", () => { describe("fetchView", () => {
@ -894,7 +973,7 @@ describe("/rows", () => {
} }
) )
it("when schema is defined, no other columns are returned", async () => { it("when schema is defined, defined columns and row attributes are returned", async () => {
const table = await config.createTable(userTable()) const table = await config.createTable(userTable())
const rows = [] const rows = []
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
@ -914,7 +993,12 @@ describe("/rows", () => {
expect(response.body.rows).toHaveLength(10) expect(response.body.rows).toHaveLength(10)
expect(response.body.rows).toEqual( expect(response.body.rows).toEqual(
expect.arrayContaining(rows.map(r => ({ name: r.name }))) expect.arrayContaining(
rows.map(r => ({
...expectAnyInternalColsAttributes,
name: r.name,
}))
)
) )
}) })

View File

@ -86,6 +86,124 @@ describe("/v2/views", () => {
}) })
}) })
describe("update", () => {
let view: ViewV2
beforeEach(async () => {
await config.createTable(priceTable())
view = await config.api.viewV2.create({ name: "View A" })
})
it("can update an existing view data", async () => {
const tableId = config.table!._id!
await config.api.viewV2.update({
...view,
query: { equal: { newField: "thatValue" } },
})
expect(await config.api.table.get(tableId)).toEqual({
...config.table,
views: {
[view.name]: {
...view,
query: { equal: { newField: "thatValue" } },
schema: expect.anything(),
},
},
_rev: expect.any(String),
updatedAt: expect.any(String),
})
})
it("can update an existing view name", async () => {
const tableId = config.table!._id!
await config.api.viewV2.update({ ...view, name: "View B" })
expect(await config.api.table.get(tableId)).toEqual(
expect.objectContaining({
views: {
"View B": { ...view, name: "View B", schema: expect.anything() },
},
})
)
})
it("cannot update an unexisting views nor edit ids", async () => {
const tableId = config.table!._id!
await config.api.viewV2.update(
{ ...view, id: generator.guid() },
{ expectStatus: 404 }
)
expect(await config.api.table.get(tableId)).toEqual(
expect.objectContaining({
views: {
[view.name]: {
...view,
schema: expect.anything(),
},
},
})
)
})
it("cannot update views with the wrong tableId", async () => {
const tableId = config.table!._id!
await config.api.viewV2.update(
{
...view,
tableId: generator.guid(),
query: { equal: { newField: "thatValue" } },
},
{ expectStatus: 404 }
)
expect(await config.api.table.get(tableId)).toEqual(
expect.objectContaining({
views: {
[view.name]: {
...view,
schema: expect.anything(),
},
},
})
)
})
it("cannot update views v1", async () => {
const viewV1 = await config.createView()
await config.api.viewV2.update(
{
...viewV1,
},
{
expectStatus: 400,
handleResponse: r => {
expect(r.body).toEqual({
message: "Only views V2 can be updated",
status: 400,
})
},
}
)
})
it("cannot update the a view with unmatching ids between url and body", async () => {
const anotherView = await config.api.viewV2.create()
const result = await config
.request!.put(`/api/v2/views/${anotherView.id}`)
.send(view)
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(400)
expect(result.body).toEqual({
message: "View id does not match between the body and the uri path",
status: 400,
})
})
})
describe("delete", () => { describe("delete", () => {
let view: ViewV2 let view: ViewV2

View File

@ -13,6 +13,11 @@ router
authorized(permissions.BUILDER), authorized(permissions.BUILDER),
viewController.v2.create viewController.v2.create
) )
.put(
`/api/v2/views/:viewId`,
authorized(permissions.BUILDER),
viewController.v2.update
)
.delete( .delete(
`/api/v2/views/:viewId`, `/api/v2/views/:viewId`,
authorized(permissions.BUILDER), authorized(permissions.BUILDER),

View File

@ -3,7 +3,6 @@ import { isExternalTable } from "../../../integrations/utils"
import * as internal from "./search/internal" import * as internal from "./search/internal"
import * as external from "./search/external" import * as external from "./search/external"
import { Format } from "../../../api/controllers/view/exporters" import { Format } from "../../../api/controllers/view/exporters"
import _ from "lodash"
export interface SearchParams { export interface SearchParams {
tableId: string tableId: string
@ -37,12 +36,7 @@ export async function search(options: SearchParams): Promise<{
hasNextPage?: boolean hasNextPage?: boolean
bookmark?: number | null bookmark?: number | null
}> { }> {
const result = await pickApi(options.tableId).search(options) return pickApi(options.tableId).search(options)
if (options.fields) {
result.rows = result.rows.map((r: any) => _.pick(r, options.fields!))
}
return result
} }
export interface ExportRowsParams { export interface ExportRowsParams {

View File

@ -14,7 +14,8 @@ import { breakExternalTableId } from "../../../../integrations/utils"
import { cleanExportRows } from "../utils" import { cleanExportRows } from "../utils"
import { utils } from "@budibase/shared-core" import { utils } from "@budibase/shared-core"
import { ExportRowsParams, ExportRowsResult, SearchParams } from "../search" import { ExportRowsParams, ExportRowsResult, SearchParams } from "../search"
import { HTTPError } from "@budibase/backend-core" import { HTTPError, db } from "@budibase/backend-core"
import pick from "lodash/pick"
export async function search(options: SearchParams) { export async function search(options: SearchParams) {
const { tableId } = options const { tableId } = options
@ -48,7 +49,7 @@ export async function search(options: SearchParams) {
} }
} }
try { try {
const rows = (await handleRequest(Operation.READ, tableId, { let rows = (await handleRequest(Operation.READ, tableId, {
filters: query, filters: query,
sort, sort,
paginate: paginateObj as PaginationJson, paginate: paginateObj as PaginationJson,
@ -67,6 +68,12 @@ export async function search(options: SearchParams) {
})) as Row[] })) as Row[]
hasNextPage = nextRows.length > 0 hasNextPage = nextRows.length > 0
} }
if (options.fields) {
const fields = [...options.fields, ...db.CONSTANT_EXTERNAL_ROW_COLS]
rows = rows.map((r: any) => pick(r, fields))
}
// need wrapper object for bookmarks etc when paginating // need wrapper object for bookmarks etc when paginating
return { rows, hasNextPage, bookmark: bookmark && bookmark + 1 } return { rows, hasNextPage, bookmark: bookmark && bookmark + 1 }
} catch (err: any) { } catch (err: any) {

View File

@ -1,5 +1,6 @@
import { import {
context, context,
db,
SearchParams as InternalSearchParams, SearchParams as InternalSearchParams,
} from "@budibase/backend-core" } from "@budibase/backend-core"
import env from "../../../../environment" import env from "../../../../environment"
@ -28,6 +29,7 @@ import {
} from "../../../../api/controllers/view/utils" } from "../../../../api/controllers/view/utils"
import sdk from "../../../../sdk" import sdk from "../../../../sdk"
import { ExportRowsParams, ExportRowsResult, SearchParams } from "../search" import { ExportRowsParams, ExportRowsResult, SearchParams } from "../search"
import pick from "lodash/pick"
export async function search(options: SearchParams) { export async function search(options: SearchParams) {
const { tableId } = options const { tableId } = options
@ -72,6 +74,12 @@ export async function search(options: SearchParams) {
response.rows = await getGlobalUsersFromMetadata(response.rows) response.rows = await getGlobalUsersFromMetadata(response.rows)
} }
table = table || (await sdk.tables.getTable(tableId)) table = table || (await sdk.tables.getTable(tableId))
if (options.fields) {
const fields = [...options.fields, ...db.CONSTANT_INTERNAL_ROW_COLS]
response.rows = response.rows.map((r: any) => pick(r, fields))
}
response.rows = await outputProcessing(table, response.rows) response.rows = await outputProcessing(table, response.rows)
} }

View File

@ -0,0 +1,143 @@
import { GenericContainer } from "testcontainers"
import { Datasource, FieldType, Row, SourceName, Table } from "@budibase/types"
import TestConfiguration from "../../../../../tests/utilities/TestConfiguration"
import { SearchParams } from "../../search"
import { search } from "../external"
import {
expectAnyExternalColsAttributes,
generator,
} from "@budibase/backend-core/tests"
jest.unmock("mysql2/promise")
jest.setTimeout(30000)
describe("external", () => {
const config = new TestConfiguration()
let externalDatasource: Datasource
const tableData: Table = {
name: generator.word(),
type: "external",
primary: ["id"],
schema: {
id: {
name: "id",
type: FieldType.AUTO,
autocolumn: true,
},
name: {
name: "name",
type: FieldType.STRING,
},
surname: {
name: "surname",
type: FieldType.STRING,
},
age: {
name: "age",
type: FieldType.NUMBER,
},
address: {
name: "address",
type: FieldType.STRING,
},
},
}
beforeAll(async () => {
const container = await new GenericContainer("mysql")
.withExposedPorts(3306)
.withEnv("MYSQL_ROOT_PASSWORD", "admin")
.withEnv("MYSQL_DATABASE", "db")
.withEnv("MYSQL_USER", "user")
.withEnv("MYSQL_PASSWORD", "password")
.start()
const host = container.getContainerIpAddress()
const port = container.getMappedPort(3306)
await config.init()
externalDatasource = await config.createDatasource({
datasource: {
type: "datasource",
name: "Test",
source: SourceName.MYSQL,
plus: true,
config: {
host,
port,
user: "user",
database: "db",
password: "password",
rejectUnauthorized: true,
},
},
})
})
describe("search", () => {
const rows: Row[] = []
beforeAll(async () => {
const table = await config.createTable({
...tableData,
sourceId: externalDatasource._id,
})
for (let i = 0; i < 10; i++) {
rows.push(
await config.createRow({
tableId: table._id,
name: generator.first(),
surname: generator.last(),
age: generator.age(),
address: generator.address(),
})
)
}
})
it("default search returns all the data", async () => {
await config.doInContext(config.appId, async () => {
const tableId = config.table!._id!
const searchParams: SearchParams = {
tableId,
query: {},
}
const result = await search(searchParams)
expect(result.rows).toHaveLength(10)
expect(result.rows).toEqual(
expect.arrayContaining(rows.map(r => expect.objectContaining(r)))
)
})
})
it("querying by fields will always return data attribute columns", async () => {
await config.doInContext(config.appId, async () => {
const tableId = config.table!._id!
const searchParams: SearchParams = {
tableId,
query: {},
fields: ["name", "age"],
}
const result = await search(searchParams)
expect(result.rows).toHaveLength(10)
expect(result.rows).toEqual(
expect.arrayContaining(
rows.map(r => ({
...expectAnyExternalColsAttributes,
name: r.name,
age: r.age,
}))
)
)
})
})
})
})

View File

@ -0,0 +1,109 @@
import { FieldType, Row, Table } from "@budibase/types"
import TestConfiguration from "../../../../../tests/utilities/TestConfiguration"
import { SearchParams } from "../../search"
import { search } from "../internal"
import {
expectAnyInternalColsAttributes,
generator,
} from "@budibase/backend-core/tests"
describe("internal", () => {
const config = new TestConfiguration()
const tableData: Table = {
name: generator.word(),
type: "table",
schema: {
name: {
name: "name",
type: FieldType.STRING,
constraints: {
type: FieldType.STRING,
},
},
surname: {
name: "surname",
type: FieldType.STRING,
constraints: {
type: FieldType.STRING,
},
},
age: {
name: "age",
type: FieldType.NUMBER,
constraints: {
type: FieldType.NUMBER,
},
},
address: {
name: "address",
type: FieldType.STRING,
constraints: {
type: FieldType.STRING,
},
},
},
}
beforeAll(async () => {
await config.init()
})
describe("search", () => {
const rows: Row[] = []
beforeAll(async () => {
await config.createTable(tableData)
for (let i = 0; i < 10; i++) {
rows.push(
await config.createRow({
name: generator.first(),
surname: generator.last(),
age: generator.age(),
address: generator.address(),
})
)
}
})
it("default search returns all the data", async () => {
await config.doInContext(config.appId, async () => {
const tableId = config.table!._id!
const searchParams: SearchParams = {
tableId,
query: {},
}
const result = await search(searchParams)
expect(result.rows).toHaveLength(10)
expect(result.rows).toEqual(
expect.arrayContaining(rows.map(r => expect.objectContaining(r)))
)
})
})
it("querying by fields will always return data attribute columns", async () => {
await config.doInContext(config.appId, async () => {
const tableId = config.table!._id!
const searchParams: SearchParams = {
tableId,
query: {},
fields: ["name", "age"],
}
const result = await search(searchParams)
expect(result.rows).toHaveLength(10)
expect(result.rows).toEqual(
expect.arrayContaining(
rows.map(r => ({
...expectAnyInternalColsAttributes,
name: r.name,
age: r.age,
}))
)
)
})
})
})
})

View File

@ -33,6 +33,24 @@ export async function create(
return view return view
} }
export async function update(tableId: string, view: ViewV2): Promise<ViewV2> {
const db = context.getAppDB()
const table = await sdk.tables.getTable(tableId)
table.views ??= {}
const existingView = Object.values(table.views).find(
v => isV2(v) && v.id === view.id
)
if (!existingView) {
throw new HTTPError(`View ${view.id} not found in table ${tableId}`, 404)
}
delete table.views[existingView.name]
table.views[view.name] = view
await db.put(table)
return view
}
export function isV2(view: View | ViewV2): view is ViewV2 { export function isV2(view: View | ViewV2): view is ViewV2 {
return (view as ViewV2).version === 2 return (view as ViewV2).version === 2
} }

View File

@ -1,7 +1,8 @@
import { SortOrder, SortType, ViewV2 } from "@budibase/types" import { CreateViewRequest, SortOrder, SortType, ViewV2 } from "@budibase/types"
import TestConfiguration from "../TestConfiguration" import TestConfiguration from "../TestConfiguration"
import { TestAPI } from "./base" import { TestAPI } from "./base"
import { generator } from "@budibase/backend-core/tests" import { generator } from "@budibase/backend-core/tests"
import { Response } from "superagent"
export class ViewV2API extends TestAPI { export class ViewV2API extends TestAPI {
constructor(config: TestConfiguration) { constructor(config: TestConfiguration) {
@ -9,7 +10,7 @@ export class ViewV2API extends TestAPI {
} }
create = async ( create = async (
viewData?: Partial<ViewV2>, viewData?: Partial<CreateViewRequest>,
{ expectStatus } = { expectStatus: 201 } { expectStatus } = { expectStatus: 201 }
): Promise<ViewV2> => { ): Promise<ViewV2> => {
let tableId = viewData?.tableId let tableId = viewData?.tableId
@ -31,6 +32,29 @@ export class ViewV2API extends TestAPI {
return result.body.data as ViewV2 return result.body.data as ViewV2
} }
update = async (
view: ViewV2,
{
expectStatus,
handleResponse,
}: {
expectStatus: number
handleResponse?: (response: Response) => void
} = { expectStatus: 200 }
): Promise<ViewV2> => {
const result = await this.request
.put(`/api/v2/views/${view.id}`)
.send(view)
.set(this.config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(expectStatus)
if (handleResponse) {
handleResponse(result)
}
return result.body.data as ViewV2
}
delete = async (viewId: string, { expectStatus } = { expectStatus: 204 }) => { delete = async (viewId: string, { expectStatus } = { expectStatus: 204 }) => {
return this.request return this.request
.delete(`/api/v2/views/${viewId}`) .delete(`/api/v2/views/${viewId}`)

View File

@ -94,4 +94,5 @@ export enum BuilderSocketEvent {
} }
export const SocketSessionTTL = 60 export const SocketSessionTTL = 60
export const ValidQueryNameRegex = /^[^()]*$/
export const ValidColumnNameRegex = /^[_a-zA-Z0-9\s]*$/g export const ValidColumnNameRegex = /^[_a-zA-Z0-9\s]*$/g

View File

@ -1,5 +1,6 @@
export * from "./backup" export * from "./backup"
export * from "./datasource" export * from "./datasource"
export * from "./row"
export * from "./view" export * from "./view"
export * from "./rows" export * from "./rows"
export * from "./table" export * from "./table"

View File

@ -0,0 +1,11 @@
import { Row } from "../../../documents/app/row"
export interface DeleteRows {
rows: (Row | string)[]
}
export interface DeleteRow {
_id: string
}
export type DeleteRowRequest = DeleteRows | DeleteRow

View File

@ -5,3 +5,5 @@ export interface ViewResponse {
} }
export type CreateViewRequest = Omit<ViewV2, "version" | "id"> export type CreateViewRequest = Omit<ViewV2, "version" | "id">
export type UpdateViewRequest = ViewV2

View File

@ -1,6 +1,10 @@
const actual = jest.requireActual("@budibase/pro") const actual = jest.requireActual("@budibase/pro")
const pro = { const pro = {
...actual, ...actual,
features: {
...actual.features,
isSSOEnforced: jest.fn(),
},
licensing: { licensing: {
keys: { keys: {
activateLicenseKey: jest.fn(), activateLicenseKey: jest.fn(),

View File

@ -104,5 +104,19 @@
"typescript": "4.7.3", "typescript": "4.7.3",
"update-dotenv": "1.1.1" "update-dotenv": "1.1.1"
}, },
"nx": {
"targets": {
"dev:builder": {
"dependsOn": [
{
"projects": [
"@budibase/backend-core"
],
"target": "build"
}
]
}
}
},
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc"
} }

View File

@ -277,6 +277,7 @@ describe("configs", () => {
describe("GET /api/global/configs/public", () => { describe("GET /api/global/configs/public", () => {
it("should return the expected public settings", async () => { it("should return the expected public settings", async () => {
await saveSettingsConfig() await saveSettingsConfig()
mocks.pro.features.isSSOEnforced.mockResolvedValue(false)
const res = await config.api.configs.getPublicSettings() const res = await config.api.configs.getPublicSettings()
const body = res.body as GetPublicSettingsResponse const body = res.body as GetPublicSettingsResponse

View File

@ -1,14 +1,9 @@
import { structures } from "../../../tests" import { structures, mocks } from "../../../tests"
import { mocks } from "@budibase/backend-core/tests"
import { env, context } from "@budibase/backend-core" import { env, context } from "@budibase/backend-core"
import * as users from "../users" import * as users from "../users"
import { CloudAccount } from "@budibase/types" import { CloudAccount } from "@budibase/types"
import { isPreventPasswordActions } from "../users" import { isPreventPasswordActions } from "../users"
jest.mock("@budibase/pro")
import * as _pro from "@budibase/pro"
const pro = jest.mocked(_pro, true)
describe("users", () => { describe("users", () => {
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks() jest.clearAllMocks()
@ -56,7 +51,7 @@ describe("users", () => {
it("returns true for all users when sso is enforced", async () => { it("returns true for all users when sso is enforced", async () => {
await context.doInTenant(structures.tenant.id(), async () => { await context.doInTenant(structures.tenant.id(), async () => {
const user = structures.users.user() const user = structures.users.user()
pro.features.isSSOEnforced.mockResolvedValueOnce(true) mocks.pro.features.isSSOEnforced.mockResolvedValueOnce(true)
const result = await users.isPreventPasswordActions(user) const result = await users.isPreventPasswordActions(user)
expect(result).toBe(true) expect(result).toBe(true)
}) })

View File

@ -18,6 +18,20 @@ var argv = require("minimist")(process.argv.slice(2))
function runBuild(entry, outfile) { function runBuild(entry, outfile) {
const isDev = process.env.NODE_ENV !== "production" const isDev = process.env.NODE_ENV !== "production"
const tsconfig = argv["p"] || `tsconfig.build.json` const tsconfig = argv["p"] || `tsconfig.build.json`
const tsconfigPathPluginContent = JSON.parse(
fs.readFileSync(tsconfig, "utf-8")
)
if (!fs.existsSync("../pro/src")) {
// If we don't have pro, we cannot bundle backend-core.
// Otherwise, the main context will not be shared between libraries
delete tsconfigPathPluginContent.compilerOptions.paths[
"@budibase/backend-core"
]
delete tsconfigPathPluginContent.compilerOptions.paths[
"@budibase/backend-core/*"
]
}
const sharedConfig = { const sharedConfig = {
entryPoints: [entry], entryPoints: [entry],
@ -25,7 +39,10 @@ function runBuild(entry, outfile) {
minify: !isDev, minify: !isDev,
sourcemap: isDev, sourcemap: isDev,
tsconfig, tsconfig,
plugins: [TsconfigPathsPlugin({ tsconfig }), nodeExternalsPlugin()], plugins: [
TsconfigPathsPlugin({ tsconfig: tsconfigPathPluginContent }),
nodeExternalsPlugin(),
],
target: "node14", target: "node14",
preserveSymlinks: true, preserveSymlinks: true,
loader: { loader: {

240
yarn.lock
View File

@ -2977,10 +2977,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.17.tgz#164b054d58551f8856285f386e1a8f45d9ba3a31" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.17.tgz#164b054d58551f8856285f386e1a8f45d9ba3a31"
integrity sha512-jaJ5IlmaDLFPNttv0ofcwy/cfeY4bh/n705Tgh+eLObbGtQBK3EPAu+CzL95JVE4nFAliyrnEu0d32Q5foavqg== integrity sha512-jaJ5IlmaDLFPNttv0ofcwy/cfeY4bh/n705Tgh+eLObbGtQBK3EPAu+CzL95JVE4nFAliyrnEu0d32Q5foavqg==
"@esbuild/android-arm64@0.17.18": "@esbuild/android-arm64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz#4aa8d8afcffb4458736ca9b32baa97d7cb5861ea" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.17.tgz#9e00eb6865ed5f2dbe71a1e96f2c52254cd92903"
integrity sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw== integrity sha512-9np+YYdNDed5+Jgr1TdWBsozZ85U1Oa3xW0c7TWqH0y2aGghXtZsuT8nYRbzOMcl0bXZXjOGbksoTtVOlWrRZg==
"@esbuild/android-arm@0.15.18": "@esbuild/android-arm@0.15.18":
version "0.15.18" version "0.15.18"
@ -2997,10 +2997,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.17.tgz#1b3b5a702a69b88deef342a7a80df4c894e4f065" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.17.tgz#1b3b5a702a69b88deef342a7a80df4c894e4f065"
integrity sha512-E6VAZwN7diCa3labs0GYvhEPL2M94WLF8A+czO8hfjREXxba8Ng7nM5VxV+9ihNXIY1iQO1XxUU4P7hbqbICxg== integrity sha512-E6VAZwN7diCa3labs0GYvhEPL2M94WLF8A+czO8hfjREXxba8Ng7nM5VxV+9ihNXIY1iQO1XxUU4P7hbqbICxg==
"@esbuild/android-arm@0.17.18": "@esbuild/android-arm@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.18.tgz#74a7e95af4ee212ebc9db9baa87c06a594f2a427" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.17.tgz#1aa013b65524f4e9f794946b415b32ae963a4618"
integrity sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw== integrity sha512-wHsmJG/dnL3OkpAcwbgoBTTMHVi4Uyou3F5mf58ZtmUyIKfcdA7TROav/6tCzET4A3QW2Q2FC+eFneMU+iyOxg==
"@esbuild/android-x64@0.16.17": "@esbuild/android-x64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3012,10 +3012,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.17.tgz#6781527e3c4ea4de532b149d18a2167f06783e7f" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.17.tgz#6781527e3c4ea4de532b149d18a2167f06783e7f"
integrity sha512-446zpfJ3nioMC7ASvJB1pszHVskkw4u/9Eu8s5yvvsSDTzYh4p4ZIRj0DznSl3FBF0Z/mZfrKXTtt0QCoFmoHA== integrity sha512-446zpfJ3nioMC7ASvJB1pszHVskkw4u/9Eu8s5yvvsSDTzYh4p4ZIRj0DznSl3FBF0Z/mZfrKXTtt0QCoFmoHA==
"@esbuild/android-x64@0.17.18": "@esbuild/android-x64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.18.tgz#1dcd13f201997c9fe0b204189d3a0da4eb4eb9b6" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.17.tgz#c2bd0469b04ded352de011fae34a7a1d4dcecb79"
integrity sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg== integrity sha512-O+FeWB/+xya0aLg23hHEM2E3hbfwZzjqumKMSIqcHbNvDa+dza2D0yLuymRBQQnC34CWrsJUXyH2MG5VnLd6uw==
"@esbuild/darwin-arm64@0.16.17": "@esbuild/darwin-arm64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3027,10 +3027,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.17.tgz#c5961ef4d3c1cc80dafe905cc145b5a71d2ac196" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.17.tgz#c5961ef4d3c1cc80dafe905cc145b5a71d2ac196"
integrity sha512-m/gwyiBwH3jqfUabtq3GH31otL/0sE0l34XKpSIqR7NjQ/XHQ3lpmQHLHbG8AHTGCw8Ao059GvV08MS0bhFIJQ== integrity sha512-m/gwyiBwH3jqfUabtq3GH31otL/0sE0l34XKpSIqR7NjQ/XHQ3lpmQHLHbG8AHTGCw8Ao059GvV08MS0bhFIJQ==
"@esbuild/darwin-arm64@0.17.18": "@esbuild/darwin-arm64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz#444f3b961d4da7a89eb9bd35cfa4415141537c2a" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.17.tgz#0c21a59cb5bd7a2cec66c7a42431dca42aefeddd"
integrity sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ== integrity sha512-M9uJ9VSB1oli2BE/dJs3zVr9kcCBBsE883prage1NWz6pBS++1oNn/7soPNS3+1DGj0FrkSvnED4Bmlu1VAE9g==
"@esbuild/darwin-x64@0.16.17": "@esbuild/darwin-x64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3042,10 +3042,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.17.tgz#b81f3259cc349691f67ae30f7b333a53899b3c20" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.17.tgz#b81f3259cc349691f67ae30f7b333a53899b3c20"
integrity sha512-4utIrsX9IykrqYaXR8ob9Ha2hAY2qLc6ohJ8c0CN1DR8yWeMrTgYFjgdeQ9LIoTOfLetXjuCu5TRPHT9yKYJVg== integrity sha512-4utIrsX9IykrqYaXR8ob9Ha2hAY2qLc6ohJ8c0CN1DR8yWeMrTgYFjgdeQ9LIoTOfLetXjuCu5TRPHT9yKYJVg==
"@esbuild/darwin-x64@0.17.18": "@esbuild/darwin-x64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz#a6da308d0ac8a498c54d62e0b2bfb7119b22d315" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.17.tgz#92f8763ff6f97dff1c28a584da7b51b585e87a7b"
integrity sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A== integrity sha512-XDre+J5YeIJDMfp3n0279DFNrGCXlxOuGsWIkRb1NThMZ0BsrWXoTg23Jer7fEXQ9Ye5QjrvXpxnhzl3bHtk0g==
"@esbuild/freebsd-arm64@0.16.17": "@esbuild/freebsd-arm64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3057,10 +3057,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.17.tgz#db846ad16cf916fd3acdda79b85ea867cb100e87" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.17.tgz#db846ad16cf916fd3acdda79b85ea867cb100e87"
integrity sha512-4PxjQII/9ppOrpEwzQ1b0pXCsFLqy77i0GaHodrmzH9zq2/NEhHMAMJkJ635Ns4fyJPFOlHMz4AsklIyRqFZWA== integrity sha512-4PxjQII/9ppOrpEwzQ1b0pXCsFLqy77i0GaHodrmzH9zq2/NEhHMAMJkJ635Ns4fyJPFOlHMz4AsklIyRqFZWA==
"@esbuild/freebsd-arm64@0.17.18": "@esbuild/freebsd-arm64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz#b83122bb468889399d0d63475d5aea8d6829c2c2" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.17.tgz#934f74bdf4022e143ba2f21d421b50fd0fead8f8"
integrity sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA== integrity sha512-cjTzGa3QlNfERa0+ptykyxs5A6FEUQQF0MuilYXYBGdBxD3vxJcKnzDlhDCa1VAJCmAxed6mYhA2KaJIbtiNuQ==
"@esbuild/freebsd-x64@0.16.17": "@esbuild/freebsd-x64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3072,10 +3072,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.17.tgz#4dd99acbaaba00949d509e7c144b1b6ef9e1815b" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.17.tgz#4dd99acbaaba00949d509e7c144b1b6ef9e1815b"
integrity sha512-lQRS+4sW5S3P1sv0z2Ym807qMDfkmdhUYX30GRBURtLTrJOPDpoU0kI6pVz1hz3U0+YQ0tXGS9YWveQjUewAJw== integrity sha512-lQRS+4sW5S3P1sv0z2Ym807qMDfkmdhUYX30GRBURtLTrJOPDpoU0kI6pVz1hz3U0+YQ0tXGS9YWveQjUewAJw==
"@esbuild/freebsd-x64@0.17.18": "@esbuild/freebsd-x64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz#af59e0e03fcf7f221b34d4c5ab14094862c9c864" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.17.tgz#16b6e90ba26ecc865eab71c56696258ec7f5d8bf"
integrity sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew== integrity sha512-sOxEvR8d7V7Kw8QqzxWc7bFfnWnGdaFBut1dRUYtu+EIRXefBc/eIsiUiShnW0hM3FmQ5Zf27suDuHsKgZ5QrA==
"@esbuild/linux-arm64@0.16.17": "@esbuild/linux-arm64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3087,10 +3087,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.17.tgz#7f9274140b2bb9f4230dbbfdf5dc2761215e30f6" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.17.tgz#7f9274140b2bb9f4230dbbfdf5dc2761215e30f6"
integrity sha512-2+pwLx0whKY1/Vqt8lyzStyda1v0qjJ5INWIe+d8+1onqQxHLLi3yr5bAa4gvbzhZqBztifYEu8hh1La5+7sUw== integrity sha512-2+pwLx0whKY1/Vqt8lyzStyda1v0qjJ5INWIe+d8+1onqQxHLLi3yr5bAa4gvbzhZqBztifYEu8hh1La5+7sUw==
"@esbuild/linux-arm64@0.17.18": "@esbuild/linux-arm64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz#8551d72ba540c5bce4bab274a81c14ed01eafdcf" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.17.tgz#179a58e8d4c72116eb068563629349f8f4b48072"
integrity sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ== integrity sha512-c9w3tE7qA3CYWjT+M3BMbwMt+0JYOp3vCMKgVBrCl1nwjAlOMYzEo+gG7QaZ9AtqZFj5MbUc885wuBBmu6aADQ==
"@esbuild/linux-arm@0.16.17": "@esbuild/linux-arm@0.16.17":
version "0.16.17" version "0.16.17"
@ -3102,10 +3102,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.17.tgz#5c8e44c2af056bb2147cf9ad13840220bcb8948b" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.17.tgz#5c8e44c2af056bb2147cf9ad13840220bcb8948b"
integrity sha512-biDs7bjGdOdcmIk6xU426VgdRUpGg39Yz6sT9Xp23aq+IEHDb/u5cbmu/pAANpDB4rZpY/2USPhCA+w9t3roQg== integrity sha512-biDs7bjGdOdcmIk6xU426VgdRUpGg39Yz6sT9Xp23aq+IEHDb/u5cbmu/pAANpDB4rZpY/2USPhCA+w9t3roQg==
"@esbuild/linux-arm@0.17.18": "@esbuild/linux-arm@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz#e09e76e526df4f665d4d2720d28ff87d15cdf639" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.17.tgz#9d78cf87a310ae9ed985c3915d5126578665c7b5"
integrity sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg== integrity sha512-2d3Lw6wkwgSLC2fIvXKoMNGVaeY8qdN0IC3rfuVxJp89CRfA3e3VqWifGDfuakPmp90+ZirmTfye1n4ncjv2lg==
"@esbuild/linux-ia32@0.16.17": "@esbuild/linux-ia32@0.16.17":
version "0.16.17" version "0.16.17"
@ -3117,10 +3117,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.17.tgz#18a6b3798658be7f46e9873fa0c8d4bec54c9212" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.17.tgz#18a6b3798658be7f46e9873fa0c8d4bec54c9212"
integrity sha512-IBTTv8X60dYo6P2t23sSUYym8fGfMAiuv7PzJ+0LcdAndZRzvke+wTVxJeCq4WgjppkOpndL04gMZIFvwoU34Q== integrity sha512-IBTTv8X60dYo6P2t23sSUYym8fGfMAiuv7PzJ+0LcdAndZRzvke+wTVxJeCq4WgjppkOpndL04gMZIFvwoU34Q==
"@esbuild/linux-ia32@0.17.18": "@esbuild/linux-ia32@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz#47878860ce4fe73a36fd8627f5647bcbbef38ba4" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.17.tgz#6fed202602d37361bca376c9d113266a722a908c"
integrity sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ== integrity sha512-1DS9F966pn5pPnqXYz16dQqWIB0dmDfAQZd6jSSpiT9eX1NzKh07J6VKR3AoXXXEk6CqZMojiVDSZi1SlmKVdg==
"@esbuild/linux-loong64@0.15.18": "@esbuild/linux-loong64@0.15.18":
version "0.15.18" version "0.15.18"
@ -3137,10 +3137,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.17.tgz#a8d93514a47f7b4232716c9f02aeb630bae24c40" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.17.tgz#a8d93514a47f7b4232716c9f02aeb630bae24c40"
integrity sha512-WVMBtcDpATjaGfWfp6u9dANIqmU9r37SY8wgAivuKmgKHE+bWSuv0qXEFt/p3qXQYxJIGXQQv6hHcm7iWhWjiw== integrity sha512-WVMBtcDpATjaGfWfp6u9dANIqmU9r37SY8wgAivuKmgKHE+bWSuv0qXEFt/p3qXQYxJIGXQQv6hHcm7iWhWjiw==
"@esbuild/linux-loong64@0.17.18": "@esbuild/linux-loong64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz#3f8fbf5267556fc387d20b2e708ce115de5c967a" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.17.tgz#cdc60304830be1e74560c704bfd72cab8a02fa06"
integrity sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ== integrity sha512-EvLsxCk6ZF0fpCB6w6eOI2Fc8KW5N6sHlIovNe8uOFObL2O+Mr0bflPHyHwLT6rwMg9r77WOAWb2FqCQrVnwFg==
"@esbuild/linux-mips64el@0.16.17": "@esbuild/linux-mips64el@0.16.17":
version "0.16.17" version "0.16.17"
@ -3152,10 +3152,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.17.tgz#4784efb1c3f0eac8133695fa89253d558149ee1b" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.17.tgz#4784efb1c3f0eac8133695fa89253d558149ee1b"
integrity sha512-2kYCGh8589ZYnY031FgMLy0kmE4VoGdvfJkxLdxP4HJvWNXpyLhjOvxVsYjYZ6awqY4bgLR9tpdYyStgZZhi2A== integrity sha512-2kYCGh8589ZYnY031FgMLy0kmE4VoGdvfJkxLdxP4HJvWNXpyLhjOvxVsYjYZ6awqY4bgLR9tpdYyStgZZhi2A==
"@esbuild/linux-mips64el@0.17.18": "@esbuild/linux-mips64el@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz#9d896d8f3c75f6c226cbeb840127462e37738226" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.17.tgz#c367b2855bb0902f5576291a2049812af2088086"
integrity sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA== integrity sha512-e0bIdHA5p6l+lwqTE36NAW5hHtw2tNRmHlGBygZC14QObsA3bD4C6sXLJjvnDIjSKhW1/0S3eDy+QmX/uZWEYQ==
"@esbuild/linux-ppc64@0.16.17": "@esbuild/linux-ppc64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3167,10 +3167,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.17.tgz#ef6558ec5e5dd9dc16886343e0ccdb0699d70d3c" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.17.tgz#ef6558ec5e5dd9dc16886343e0ccdb0699d70d3c"
integrity sha512-KIdG5jdAEeAKogfyMTcszRxy3OPbZhq0PPsW4iKKcdlbk3YE4miKznxV2YOSmiK/hfOZ+lqHri3v8eecT2ATwQ== integrity sha512-KIdG5jdAEeAKogfyMTcszRxy3OPbZhq0PPsW4iKKcdlbk3YE4miKznxV2YOSmiK/hfOZ+lqHri3v8eecT2ATwQ==
"@esbuild/linux-ppc64@0.17.18": "@esbuild/linux-ppc64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz#3d9deb60b2d32c9985bdc3e3be090d30b7472783" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.17.tgz#7fdc0083d42d64a4651711ee0a7964f489242f45"
integrity sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ== integrity sha512-BAAilJ0M5O2uMxHYGjFKn4nJKF6fNCdP1E0o5t5fvMYYzeIqy2JdAP88Az5LHt9qBoUa4tDaRpfWt21ep5/WqQ==
"@esbuild/linux-riscv64@0.16.17": "@esbuild/linux-riscv64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3182,10 +3182,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.17.tgz#13a87fdbcb462c46809c9d16bcf79817ecf9ce6f" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.17.tgz#13a87fdbcb462c46809c9d16bcf79817ecf9ce6f"
integrity sha512-Cj6uWLBR5LWhcD/2Lkfg2NrkVsNb2sFM5aVEfumKB2vYetkA/9Uyc1jVoxLZ0a38sUhFk4JOVKH0aVdPbjZQeA== integrity sha512-Cj6uWLBR5LWhcD/2Lkfg2NrkVsNb2sFM5aVEfumKB2vYetkA/9Uyc1jVoxLZ0a38sUhFk4JOVKH0aVdPbjZQeA==
"@esbuild/linux-riscv64@0.17.18": "@esbuild/linux-riscv64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz#8a943cf13fd24ff7ed58aefb940ef178f93386bc" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.17.tgz#5198a417f3f5b86b10c95647b8bc032e5b6b2b1c"
integrity sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA== integrity sha512-Wh/HW2MPnC3b8BqRSIme/9Zhab36PPH+3zam5pqGRH4pE+4xTrVLx2+XdGp6fVS3L2x+DrsIcsbMleex8fbE6g==
"@esbuild/linux-s390x@0.16.17": "@esbuild/linux-s390x@0.16.17":
version "0.16.17" version "0.16.17"
@ -3197,10 +3197,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.17.tgz#83cb16d1d3ac0dca803b3f031ba3dc13f1ec7ade" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.17.tgz#83cb16d1d3ac0dca803b3f031ba3dc13f1ec7ade"
integrity sha512-lK+SffWIr0XsFf7E0srBjhpkdFVJf3HEgXCwzkm69kNbRar8MhezFpkIwpk0qo2IOQL4JE4mJPJI8AbRPLbuOQ== integrity sha512-lK+SffWIr0XsFf7E0srBjhpkdFVJf3HEgXCwzkm69kNbRar8MhezFpkIwpk0qo2IOQL4JE4mJPJI8AbRPLbuOQ==
"@esbuild/linux-s390x@0.17.18": "@esbuild/linux-s390x@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz#66cb01f4a06423e5496facabdce4f7cae7cb80e5" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.17.tgz#7459c2fecdee2d582f0697fb76a4041f4ad1dd1e"
integrity sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw== integrity sha512-j/34jAl3ul3PNcK3pfI0NSlBANduT2UO5kZ7FCaK33XFv3chDhICLY8wJJWIhiQ+YNdQ9dxqQctRg2bvrMlYgg==
"@esbuild/linux-x64@0.16.17": "@esbuild/linux-x64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3212,10 +3212,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.17.tgz#7bc400568690b688e20a0c94b2faabdd89ae1a79" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.17.tgz#7bc400568690b688e20a0c94b2faabdd89ae1a79"
integrity sha512-XcSGTQcWFQS2jx3lZtQi7cQmDYLrpLRyz1Ns1DzZCtn898cWfm5Icx/DEWNcTU+T+tyPV89RQtDnI7qL2PObPg== integrity sha512-XcSGTQcWFQS2jx3lZtQi7cQmDYLrpLRyz1Ns1DzZCtn898cWfm5Icx/DEWNcTU+T+tyPV89RQtDnI7qL2PObPg==
"@esbuild/linux-x64@0.17.18": "@esbuild/linux-x64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz#23c26050c6c5d1359c7b774823adc32b3883b6c9" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.17.tgz#948cdbf46d81c81ebd7225a7633009bc56a4488c"
integrity sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA== integrity sha512-QM50vJ/y+8I60qEmFxMoxIx4de03pGo2HwxdBeFd4nMh364X6TIBZ6VQ5UQmPbQWUVWHWws5MmJXlHAXvJEmpQ==
"@esbuild/netbsd-x64@0.16.17": "@esbuild/netbsd-x64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3227,10 +3227,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.17.tgz#1b5dcfbc4bfba80e67a11e9148de836af5b58b6c" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.17.tgz#1b5dcfbc4bfba80e67a11e9148de836af5b58b6c"
integrity sha512-RNLCDmLP5kCWAJR+ItLM3cHxzXRTe4N00TQyQiimq+lyqVqZWGPAvcyfUBM0isE79eEZhIuGN09rAz8EL5KdLA== integrity sha512-RNLCDmLP5kCWAJR+ItLM3cHxzXRTe4N00TQyQiimq+lyqVqZWGPAvcyfUBM0isE79eEZhIuGN09rAz8EL5KdLA==
"@esbuild/netbsd-x64@0.17.18": "@esbuild/netbsd-x64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz#789a203d3115a52633ff6504f8cbf757f15e703b" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.17.tgz#6bb89668c0e093c5a575ded08e1d308bd7fd63e7"
integrity sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg== integrity sha512-/jGlhWR7Sj9JPZHzXyyMZ1RFMkNPjC6QIAan0sDOtIo2TYk3tZn5UDrkE0XgsTQCxWTTOcMPf9p6Rh2hXtl5TQ==
"@esbuild/openbsd-x64@0.16.17": "@esbuild/openbsd-x64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3242,10 +3242,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.17.tgz#e275098902291149a5dcd012c9ea0796d6b7adff" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.17.tgz#e275098902291149a5dcd012c9ea0796d6b7adff"
integrity sha512-PAXswI5+cQq3Pann7FNdcpSUrhrql3wKjj3gVkmuz6OHhqqYxKvi6GgRBoaHjaG22HV/ZZEgF9TlS+9ftHVigA== integrity sha512-PAXswI5+cQq3Pann7FNdcpSUrhrql3wKjj3gVkmuz6OHhqqYxKvi6GgRBoaHjaG22HV/ZZEgF9TlS+9ftHVigA==
"@esbuild/openbsd-x64@0.17.18": "@esbuild/openbsd-x64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz#d7b998a30878f8da40617a10af423f56f12a5e90" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.17.tgz#abac2ae75fef820ef6c2c48da4666d092584c79d"
integrity sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA== integrity sha512-rSEeYaGgyGGf4qZM2NonMhMOP/5EHp4u9ehFiBrg7stH6BYEEjlkVREuDEcQ0LfIl53OXLxNbfuIj7mr5m29TA==
"@esbuild/sunos-x64@0.16.17": "@esbuild/sunos-x64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3257,10 +3257,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.17.tgz#10603474866f64986c0370a2d4fe5a2bb7fee4f5" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.17.tgz#10603474866f64986c0370a2d4fe5a2bb7fee4f5"
integrity sha512-V63egsWKnx/4V0FMYkr9NXWrKTB5qFftKGKuZKFIrAkO/7EWLFnbBZNM1CvJ6Sis+XBdPws2YQSHF1Gqf1oj/Q== integrity sha512-V63egsWKnx/4V0FMYkr9NXWrKTB5qFftKGKuZKFIrAkO/7EWLFnbBZNM1CvJ6Sis+XBdPws2YQSHF1Gqf1oj/Q==
"@esbuild/sunos-x64@0.17.18": "@esbuild/sunos-x64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz#ecad0736aa7dae07901ba273db9ef3d3e93df31f" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.17.tgz#74a45fe1db8ea96898f1a9bb401dcf1dadfc8371"
integrity sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg== integrity sha512-Y7ZBbkLqlSgn4+zot4KUNYst0bFoO68tRgI6mY2FIM+b7ZbyNVtNbDP5y8qlu4/knZZ73fgJDlXID+ohY5zt5g==
"@esbuild/win32-arm64@0.16.17": "@esbuild/win32-arm64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3272,10 +3272,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.17.tgz#521a6d97ee0f96b7c435930353cc4e93078f0b54" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.17.tgz#521a6d97ee0f96b7c435930353cc4e93078f0b54"
integrity sha512-YtUXLdVnd6YBSYlZODjWzH+KzbaubV0YVd6UxSfoFfa5PtNJNaW+1i+Hcmjpg2nEe0YXUCNF5bkKy1NnBv1y7Q== integrity sha512-YtUXLdVnd6YBSYlZODjWzH+KzbaubV0YVd6UxSfoFfa5PtNJNaW+1i+Hcmjpg2nEe0YXUCNF5bkKy1NnBv1y7Q==
"@esbuild/win32-arm64@0.17.18": "@esbuild/win32-arm64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz#58dfc177da30acf956252d7c8ae9e54e424887c4" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.17.tgz#fd95ffd217995589058a4ed8ac17ee72a3d7f615"
integrity sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg== integrity sha512-bwPmTJsEQcbZk26oYpc4c/8PvTY3J5/QK8jM19DVlEsAB41M39aWovWoHtNm78sd6ip6prilxeHosPADXtEJFw==
"@esbuild/win32-ia32@0.16.17": "@esbuild/win32-ia32@0.16.17":
version "0.16.17" version "0.16.17"
@ -3287,10 +3287,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.17.tgz#56f88462ebe82dad829dc2303175c0e0ccd8e38e" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.17.tgz#56f88462ebe82dad829dc2303175c0e0ccd8e38e"
integrity sha512-yczSLRbDdReCO74Yfc5tKG0izzm+lPMYyO1fFTcn0QNwnKmc3K+HdxZWLGKg4pZVte7XVgcFku7TIZNbWEJdeQ== integrity sha512-yczSLRbDdReCO74Yfc5tKG0izzm+lPMYyO1fFTcn0QNwnKmc3K+HdxZWLGKg4pZVte7XVgcFku7TIZNbWEJdeQ==
"@esbuild/win32-ia32@0.17.18": "@esbuild/win32-ia32@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz#340f6163172b5272b5ae60ec12c312485f69232b" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.17.tgz#9b7ef5d0df97593a80f946b482e34fcba3fa4aaf"
integrity sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw== integrity sha512-H/XaPtPKli2MhW+3CQueo6Ni3Avggi6hP/YvgkEe1aSaxw+AeO8MFjq8DlgfTd9Iz4Yih3QCZI6YLMoyccnPRg==
"@esbuild/win32-x64@0.16.17": "@esbuild/win32-x64@0.16.17":
version "0.16.17" version "0.16.17"
@ -3302,10 +3302,10 @@
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.17.tgz#2b577b976e6844106715bbe0cdc57cd1528063f9" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.17.tgz#2b577b976e6844106715bbe0cdc57cd1528063f9"
integrity sha512-FNZw7H3aqhF9OyRQbDDnzUApDXfC1N6fgBhkqEO2jvYCJ+DxMTfZVqg3AX0R1khg1wHTBRD5SdcibSJ+XF6bFg== integrity sha512-FNZw7H3aqhF9OyRQbDDnzUApDXfC1N6fgBhkqEO2jvYCJ+DxMTfZVqg3AX0R1khg1wHTBRD5SdcibSJ+XF6bFg==
"@esbuild/win32-x64@0.17.18": "@esbuild/win32-x64@0.18.17":
version "0.17.18" version "0.18.17"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz#3a8e57153905308db357fd02f57c180ee3a0a1fa" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.17.tgz#bcb2e042631b3c15792058e189ed879a22b2968b"
integrity sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg== integrity sha512-fGEb8f2BSA3CW7riJVurug65ACLuQAzKq0SSqkY2b2yHHH0MzDfbLyKIGzHwOI/gkHcxM/leuSW6D5w/LMNitA==
"@eslint-community/eslint-utils@^4.2.0": "@eslint-community/eslint-utils@^4.2.0":
version "4.4.0" version "4.4.0"
@ -11810,10 +11810,10 @@ esbuild-netbsd-64@0.15.18:
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz#ae75682f60d08560b1fe9482bfe0173e5110b998" resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz#ae75682f60d08560b1fe9482bfe0173e5110b998"
integrity sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg== integrity sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==
esbuild-node-externals@^1.7.0: esbuild-node-externals@^1.8.0:
version "1.7.0" version "1.8.0"
resolved "https://registry.yarnpkg.com/esbuild-node-externals/-/esbuild-node-externals-1.7.0.tgz#f6d755c577aec1ffa8548b0a648f13df27551805" resolved "https://registry.yarnpkg.com/esbuild-node-externals/-/esbuild-node-externals-1.8.0.tgz#878fbe458d4e58337753c2eacfd7200dc1077bd1"
integrity sha512-nfY3hxtO2anCTZ87LgfzCTfBuyG6de+NyiCNMF1mgrBufS0NgoYlBwF77HHuOInsJLxsAJf0BfLeV6ekZ3hRuA== integrity sha512-pYslmT8Bl383UnfxzHQQRpCgBNIOwAzDaYheuIeI4CODxelsN/eQroVn5STDow5QOpRalMgWUR+R8LfSgUROcw==
dependencies: dependencies:
find-up "^5.0.0" find-up "^5.0.0"
tslib "^2.4.1" tslib "^2.4.1"
@ -11899,34 +11899,6 @@ esbuild@^0.16.17:
"@esbuild/win32-ia32" "0.16.17" "@esbuild/win32-ia32" "0.16.17"
"@esbuild/win32-x64" "0.16.17" "@esbuild/win32-x64" "0.16.17"
esbuild@^0.17.18:
version "0.17.18"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.18.tgz#f4f8eb6d77384d68cd71c53eb6601c7efe05e746"
integrity sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==
optionalDependencies:
"@esbuild/android-arm" "0.17.18"
"@esbuild/android-arm64" "0.17.18"
"@esbuild/android-x64" "0.17.18"
"@esbuild/darwin-arm64" "0.17.18"
"@esbuild/darwin-x64" "0.17.18"
"@esbuild/freebsd-arm64" "0.17.18"
"@esbuild/freebsd-x64" "0.17.18"
"@esbuild/linux-arm" "0.17.18"
"@esbuild/linux-arm64" "0.17.18"
"@esbuild/linux-ia32" "0.17.18"
"@esbuild/linux-loong64" "0.17.18"
"@esbuild/linux-mips64el" "0.17.18"
"@esbuild/linux-ppc64" "0.17.18"
"@esbuild/linux-riscv64" "0.17.18"
"@esbuild/linux-s390x" "0.17.18"
"@esbuild/linux-x64" "0.17.18"
"@esbuild/netbsd-x64" "0.17.18"
"@esbuild/openbsd-x64" "0.17.18"
"@esbuild/sunos-x64" "0.17.18"
"@esbuild/win32-arm64" "0.17.18"
"@esbuild/win32-ia32" "0.17.18"
"@esbuild/win32-x64" "0.17.18"
esbuild@^0.17.5: esbuild@^0.17.5:
version "0.17.17" version "0.17.17"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.17.tgz#fa906ab11b11d2ed4700f494f4f764229b25c916" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.17.tgz#fa906ab11b11d2ed4700f494f4f764229b25c916"
@ -11955,6 +11927,34 @@ esbuild@^0.17.5:
"@esbuild/win32-ia32" "0.17.17" "@esbuild/win32-ia32" "0.17.17"
"@esbuild/win32-x64" "0.17.17" "@esbuild/win32-x64" "0.17.17"
esbuild@^0.18.17:
version "0.18.17"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.17.tgz#2aaf6bc6759b0c605777fdc435fea3969e091cad"
integrity sha512-1GJtYnUxsJreHYA0Y+iQz2UEykonY66HNWOb0yXYZi9/kNrORUEHVg87eQsCtqh59PEJ5YVZJO98JHznMJSWjg==
optionalDependencies:
"@esbuild/android-arm" "0.18.17"
"@esbuild/android-arm64" "0.18.17"
"@esbuild/android-x64" "0.18.17"
"@esbuild/darwin-arm64" "0.18.17"
"@esbuild/darwin-x64" "0.18.17"
"@esbuild/freebsd-arm64" "0.18.17"
"@esbuild/freebsd-x64" "0.18.17"
"@esbuild/linux-arm" "0.18.17"
"@esbuild/linux-arm64" "0.18.17"
"@esbuild/linux-ia32" "0.18.17"
"@esbuild/linux-loong64" "0.18.17"
"@esbuild/linux-mips64el" "0.18.17"
"@esbuild/linux-ppc64" "0.18.17"
"@esbuild/linux-riscv64" "0.18.17"
"@esbuild/linux-s390x" "0.18.17"
"@esbuild/linux-x64" "0.18.17"
"@esbuild/netbsd-x64" "0.18.17"
"@esbuild/openbsd-x64" "0.18.17"
"@esbuild/sunos-x64" "0.18.17"
"@esbuild/win32-arm64" "0.18.17"
"@esbuild/win32-ia32" "0.18.17"
"@esbuild/win32-x64" "0.18.17"
escalade@^3.1.1: escalade@^3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"