Browse Source

Merge branch 'master' of github.com:Budibase/budibase into fix/cypress-fixes

master
mike12345567 5 years ago
parent
commit
1e9ce62261
  1. 60
      README.md
  2. 19
      hosting/docker-compose.yaml
  3. 1
      hosting/envoy.dev.yaml.hbs
  4. 18
      hosting/envoy.yaml
  5. 2
      lerna.json
  6. 2
      packages/auth/package.json
  7. 3
      packages/auth/src/index.js
  8. 4
      packages/auth/src/middleware/auditLog.js
  9. 2
      packages/auth/src/middleware/index.js
  10. 3
      packages/auth/src/objectStore/index.js
  11. 1
      packages/auth/src/security/roles.js
  12. 2
      packages/bbui/package.json
  13. 6
      packages/bbui/src/Form/Core/RadioGroup.svelte
  14. 8
      packages/builder/package.json
  15. 12
      packages/builder/src/builderStore/dataBinding.js
  16. 2
      packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js
  17. 12
      packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte
  18. 3
      packages/builder/src/components/design/AppPreview/componentStructure.json
  19. 31
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte
  20. 6
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte
  21. 12
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js
  22. 2
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/FieldSelect.svelte
  23. 30
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte
  24. 2
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/FormFieldSelect.svelte
  25. 2
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/MultiFieldSelect.svelte
  26. 4
      packages/builder/src/pages/builder/portal/_layout.svelte
  27. 8
      packages/builder/src/pages/builder/portal/settings/organisation.svelte
  28. 70
      packages/builder/src/pages/builder/portal/settings/update.svelte
  29. 2
      packages/cli/package.json
  30. 6
      packages/client/package.json
  31. 4
      packages/client/src/components/Component.svelte
  32. 1
      packages/client/src/constants.js
  33. 2
      packages/client/src/sdk.js
  34. 4
      packages/client/src/utils/componentProps.js
  35. 6
      packages/client/src/utils/enrichDataBinding.js
  36. 19
      packages/client/src/utils/getAction.js
  37. 10
      packages/server/package.json
  38. 8
      packages/server/src/api/controllers/application.js
  39. 2
      packages/server/src/api/controllers/auth.js
  40. 4
      packages/server/src/api/controllers/routing.js
  41. 5
      packages/server/src/api/controllers/static/templates/BudibaseApp.svelte
  42. 5
      packages/server/src/api/controllers/user.js
  43. 3
      packages/server/src/api/index.js
  44. 4
      packages/server/src/api/routes/tests/routing.spec.js
  45. 17
      packages/server/src/api/routes/tests/utilities/TestFunctions.js
  46. 4
      packages/server/src/app.js
  47. 3
      packages/server/src/automations/actions.js
  48. 62
      packages/server/src/automations/steps/bash.js
  49. 2
      packages/server/src/automations/steps/executeScript.js
  50. 2
      packages/server/src/middleware/currentapp.js
  51. 10
      packages/server/src/tests/utilities/TestConfiguration.js
  52. 4
      packages/server/src/utilities/fileSystem/newApp.js
  53. 8
      packages/server/src/utilities/redis.js
  54. 5
      packages/server/src/utilities/rowProcessor.js
  55. 36
      packages/server/src/utilities/workerRequests.js
  56. 33
      packages/standard-components/manifest.json
  57. 5
      packages/standard-components/package.json
  58. 123
      packages/standard-components/src/DataProvider.svelte
  59. 77
      packages/standard-components/src/DateRangePicker.svelte
  60. 1
      packages/standard-components/src/forms/StringField.svelte
  61. 1
      packages/standard-components/src/index.js
  62. 146
      packages/standard-components/src/lucene.js
  63. 255
      packages/standard-components/yarn.lock
  64. 2
      packages/string-templates/package.json
  65. 4
      packages/string-templates/src/index.cjs
  66. 6
      packages/worker/package.json
  67. 3
      packages/worker/src/api/index.js
  68. 8
      packages/worker/src/utilities/redis.js
  69. 97
      packages/worker/yarn.lock

60
README.md

@ -30,9 +30,6 @@
<a href="https://github.com/Budibase/budibase/releases"> <a href="https://github.com/Budibase/budibase/releases">
<img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/Budibase/budibase"> <img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/Budibase/budibase">
</a> </a>
<a href="https://discord.gg/rCYayfe">
<img alt="Discord" src="https://img.shields.io/discord/733030666647765003">
</a>
<a href="https://twitter.com/intent/follow?screen_name=budibase"> <a href="https://twitter.com/intent/follow?screen_name=budibase">
<img src="https://img.shields.io/twitter/follow/budibase?style=social" alt="Follow @budibase" /> <img src="https://img.shields.io/twitter/follow/budibase?style=social" alt="Follow @budibase" />
</a> </a>
@ -52,8 +49,6 @@
<a href="https://github.com/Budibase/budibase/issues">Report a bug</a> <a href="https://github.com/Budibase/budibase/issues">Report a bug</a>
<span> · </span> <span> · </span>
Support: <a href="https://github.com/Budibase/budibase/discussions">Discussions</a> Support: <a href="https://github.com/Budibase/budibase/discussions">Discussions</a>
<span> & </span>
<a href="https://discord.gg/rCYayfe">Discord</a>
</h3> </h3>
@ -61,7 +56,7 @@
- **Build and ship real software.** Unlike other platforms, with Budibase you build and ship single page applications. Budibase applications have performance baked in and can be designed responsively, providing your users with a great experience. - **Build and ship real software.** Unlike other platforms, with Budibase you build and ship single page applications. Budibase applications have performance baked in and can be designed responsively, providing your users with a great experience.
- **Open source and extensable.** Budibase is open-source - licensed as GPL v3. This should fill you with confidence that Budibase will always be around. You can also code against Budibase or fork it and make changes as you please, providing a developer-friendly experience. - **Open source and extensible.** Budibase is open-source - licensed as GPL v3. This should fill you with confidence that Budibase will always be around. You can also code against Budibase or fork it and make changes as you please, providing a developer-friendly experience.
- **Load data or start from scratch.** Budibase pulls in data from multiple sources, including MongoDB, CouchDB, PostgreSQL, mySQL, Airtable, S3, DyanmoDB, or a REST API. And unlike other platforms, with Budibase you can start from scratch and create business apps with no data sources. [Request new data sources](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). - **Load data or start from scratch.** Budibase pulls in data from multiple sources, including MongoDB, CouchDB, PostgreSQL, mySQL, Airtable, S3, DyanmoDB, or a REST API. And unlike other platforms, with Budibase you can start from scratch and create business apps with no data sources. [Request new data sources](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
@ -79,28 +74,6 @@
--- ---
## ⌛ Status
- [x] Alpha: We are demoing Budibase to users and receiving feedback
- [x] Private Beta: We are testing Budibase with a closed set of customers
- [x] Public Beta: Anyone can [get started](https://docs.budibase.com/getting-started).
- [ ] Official Launch
Watch "releases" of this repo to get notified of major updates, and give the star button a click whilst you're there.
<p align="center">
<img src="https://i.imgur.com/cJpgqm8.png">
</p>
### Stargazers over time
[![Stargazers over time](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase)
If you are having issues between updates of the builder, please use the guide [here](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting) to clear down your environment.
---
## 🏁 Getting Started with Budibase in 5 minutes ## 🏁 Getting Started with Budibase in 5 minutes
To get started, you must have docker and docker compose installed on your machine. To get started, you must have docker and docker compose installed on your machine.
@ -141,21 +114,40 @@ Done! You are now ready to build powerful internal tools in minutes. For additio
The Budibase [documentation lives here](https://docs.budibase.com). The Budibase [documentation lives here](https://docs.budibase.com).
You can also follow a quick tutorial on [how to build a CRM with Budibase](https://docs.budibase.com/tutorial/tutorial-introduction)
---
## ⌛ Status
- [x] Alpha: We are demoing Budibase to users and receiving feedback
- [x] Private Beta: We are testing Budibase with a closed set of customers
- [x] Public Beta: Anyone can [get started](https://docs.budibase.com/getting-started).
- [ ] Official Launch
Watch "releases" of this repo to get notified of major updates, and give the star button a click whilst you're there.
<p align="center">
<img src="https://i.imgur.com/cJpgqm8.png">
</p>
--- ---
## Stargazers over time
[![Stargazers over time](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase)
## Roadmap If you are having issues between updates of the builder, please use the guide [here](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting) to clear down your environment.
Checkout our [Public Roadmap](https://github.com/Budibase/budibase/projects/10). If you would like to discuss some of the items on the roadmap, please feel to reach out on [Discord](https://discord.gg/rCYayfe), or via [Github discussions](https://github.com/Budibase/budibase/discussions)
---
## ❗ Code of Conduct ## ❗ Code of Conduct
Budibase is dedicated to providing a welcoming, diverse, and harrassment-free experience for everyone. We expect everyone in the Budibase community to abide by our [**Code of Conduct**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md). Please read it. Budibase is dedicated to providing a welcoming, diverse, and harrassment-free experience for everyone. We expect everyone in the Budibase community to abide by our [**Code of Conduct**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md). Please read it.
---
## 🙌 Contributing to Budibase ## 🙌 Contributing to Budibase
From opening a bug report to creating a pull request: every contribution is appreciated and welcomed. If you're planning to implement a new feature or change the API please create an issue first. This way we can ensure your work is not in vain. From opening a bug report to creating a pull request: every contribution is appreciated and welcomed. If you're planning to implement a new feature or change the API please create an issue first. This way we can ensure your work is not in vain.
@ -188,11 +180,7 @@ Budibase is open-source. The builder is licensed [AGPL v3](https://www.gnu.org/l
## 💬 Get in touch ## 💬 Get in touch
If you have a question or would like to talk with other Budibase users, please hop over to [Github discussions](https://github.com/Budibase/budibase/discussions) or join our Discord server: If you have a question or would like to talk with other Budibase users and join our community, please hop over to [Github discussions](https://github.com/Budibase/budibase/discussions)
[Discord chatroom](https://discord.gg/rCYayfe)
![Discord Shield](https://discordapp.com/api/guilds/733030666647765003/widget.png?style=shield)
--- ---

19
hosting/docker-compose.yaml

@ -24,6 +24,8 @@ services:
ENABLE_ANALYTICS: "true" ENABLE_ANALYTICS: "true"
REDIS_URL: redis-service:6379 REDIS_URL: redis-service:6379
REDIS_PASSWORD: ${REDIS_PASSWORD} REDIS_PASSWORD: ${REDIS_PASSWORD}
volumes:
- ./logs:/logs
depends_on: depends_on:
- worker-service - worker-service
@ -46,6 +48,8 @@ services:
INTERNAL_API_KEY: ${INTERNAL_API_KEY} INTERNAL_API_KEY: ${INTERNAL_API_KEY}
REDIS_URL: redis-service:6379 REDIS_URL: redis-service:6379
REDIS_PASSWORD: ${REDIS_PASSWORD} REDIS_PASSWORD: ${REDIS_PASSWORD}
volumes:
- ./logs:/logs
depends_on: depends_on:
- redis-service - redis-service
- minio-service - minio-service
@ -109,6 +113,21 @@ services:
- "${REDIS_PORT}:6379" - "${REDIS_PORT}:6379"
volumes: volumes:
- redis_data:/data - redis_data:/data
watchtower-service:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --debug --http-api-update budibase/apps budibase/worker
environment:
- WATCHTOWER_HTTP_API=true
- WATCHTOWER_HTTP_API_TOKEN=budibase
- WATCHTOWER_CLEANUP=true
labels:
- "com.centurylinklabs.watchtower.enable=false"
ports:
- 6161:8080
volumes: volumes:
couchdb3_data: couchdb3_data:

1
hosting/envoy.dev.yaml.hbs

@ -133,4 +133,3 @@ static_resources:
socket_address: socket_address:
address: {{ address }} address: {{ address }}
port_value: 4002 port_value: 4002

18
hosting/envoy.yaml

@ -21,6 +21,10 @@ static_resources:
cluster: app-service cluster: app-service
prefix_rewrite: "/" prefix_rewrite: "/"
- match: { path: "/v1/update" }
route:
cluster: watchtower-service
- match: { prefix: "/builder/" } - match: { prefix: "/builder/" }
route: route:
cluster: app-service cluster: app-service
@ -123,3 +127,17 @@ static_resources:
address: couchdb-service address: couchdb-service
port_value: 5984 port_value: 5984
- name: watchtower-service
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
load_assignment:
cluster_name: watchtower-service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: watchtower-service
port_value: 6161

2
lerna.json

@ -1,5 +1,5 @@
{ {
"version": "0.9.21", "version": "0.9.27",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

2
packages/auth/package.json

@ -1,6 +1,6 @@
{ {
"name": "@budibase/auth", "name": "@budibase/auth",
"version": "0.9.21", "version": "0.9.27",
"description": "Authentication middlewares for budibase builder and apps", "description": "Authentication middlewares for budibase builder and apps",
"main": "src/index.js", "main": "src/index.js",
"author": "Budibase", "author": "Budibase",

3
packages/auth/src/index.js

@ -2,7 +2,7 @@ const passport = require("koa-passport")
const LocalStrategy = require("passport-local").Strategy const LocalStrategy = require("passport-local").Strategy
const JwtStrategy = require("passport-jwt").Strategy const JwtStrategy = require("passport-jwt").Strategy
const { StaticDatabases } = require("./db/utils") const { StaticDatabases } = require("./db/utils")
const { jwt, local, authenticated, google } = require("./middleware") const { jwt, local, authenticated, google, auditLog } = require("./middleware")
const { setDB, getDB } = require("./db") const { setDB, getDB } = require("./db")
// Strategies // Strategies
@ -45,6 +45,7 @@ module.exports = {
passport, passport,
google, google,
jwt: require("jsonwebtoken"), jwt: require("jsonwebtoken"),
auditLog,
}, },
StaticDatabases, StaticDatabases,
constants: require("./constants"), constants: require("./constants"),

4
packages/auth/src/middleware/auditLog.js

@ -0,0 +1,4 @@
module.exports = async (ctx, next) => {
// Placeholder for audit log middleware
return next()
}

2
packages/auth/src/middleware/index.js

@ -2,10 +2,12 @@ const jwt = require("./passport/jwt")
const local = require("./passport/local") const local = require("./passport/local")
const google = require("./passport/google") const google = require("./passport/google")
const authenticated = require("./authenticated") const authenticated = require("./authenticated")
const auditLog = require("./auditLog")
module.exports = { module.exports = {
google, google,
jwt, jwt,
local, local,
authenticated, authenticated,
auditLog,
} }

3
packages/auth/src/objectStore/index.js

@ -159,7 +159,7 @@ exports.upload = async ({
* Similar to the upload function but can be used to send a file stream * Similar to the upload function but can be used to send a file stream
* through to the object store. * through to the object store.
*/ */
exports.streamUpload = async (bucketName, filename, stream) => { exports.streamUpload = async (bucketName, filename, stream, extra = {}) => {
const objectStore = exports.ObjectStore(bucketName) const objectStore = exports.ObjectStore(bucketName)
await exports.makeSureBucketExists(objectStore, bucketName) await exports.makeSureBucketExists(objectStore, bucketName)
@ -167,6 +167,7 @@ exports.streamUpload = async (bucketName, filename, stream) => {
Bucket: sanitizeBucket(bucketName), Bucket: sanitizeBucket(bucketName),
Key: sanitizeKey(filename), Key: sanitizeKey(filename),
Body: stream, Body: stream,
...extra,
} }
return objectStore.upload(params).promise() return objectStore.upload(params).promise()
} }

1
packages/auth/src/security/roles.js

@ -13,7 +13,6 @@ const BUILTIN_IDS = {
POWER: "POWER", POWER: "POWER",
BASIC: "BASIC", BASIC: "BASIC",
PUBLIC: "PUBLIC", PUBLIC: "PUBLIC",
BUILDER: "BUILDER",
} }
// exclude internal roles like builder // exclude internal roles like builder

2
packages/bbui/package.json

@ -1,7 +1,7 @@
{ {
"name": "@budibase/bbui", "name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.", "description": "A UI solution used in the different Budibase projects.",
"version": "0.9.21", "version": "0.9.27",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",

6
packages/bbui/src/Form/Core/RadioGroup.svelte

@ -37,3 +37,9 @@
{/each} {/each}
{/if} {/if}
</div> </div>
<style>
.spectrum-Radio-input {
opacity: 0;
}
</style>

8
packages/builder/package.json

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "0.9.21", "version": "0.9.27",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -65,10 +65,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.21", "@budibase/bbui": "^0.9.27",
"@budibase/client": "^0.9.21", "@budibase/client": "^0.9.27",
"@budibase/colorpicker": "1.1.2", "@budibase/colorpicker": "1.1.2",
"@budibase/string-templates": "^0.9.21", "@budibase/string-templates": "^0.9.27",
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",

12
packages/builder/src/builderStore/dataBinding.js

@ -136,7 +136,7 @@ const getContextBindings = (asset, componentId) => {
if (!datasource) { if (!datasource) {
return return
} }
const info = getSchemaForDatasource(datasource) const info = getSchemaForDatasource(asset, datasource)
schema = info.schema schema = info.schema
readablePrefix = info.table?.name readablePrefix = info.table?.name
} }
@ -191,7 +191,7 @@ const getContextBindings = (asset, componentId) => {
*/ */
const getUserBindings = () => { const getUserBindings = () => {
let bindings = [] let bindings = []
const { schema } = getSchemaForDatasource({ const { schema } = getSchemaForDatasource(null, {
type: "table", type: "table",
tableId: TableNames.USERS, tableId: TableNames.USERS,
}) })
@ -244,11 +244,15 @@ const getUrlBindings = asset => {
/** /**
* Gets a schema for a datasource object. * Gets a schema for a datasource object.
*/ */
export const getSchemaForDatasource = (datasource, isForm = false) => { export const getSchemaForDatasource = (asset, datasource, isForm = false) => {
let schema, table let schema, table
if (datasource) { if (datasource) {
const { type } = datasource const { type } = datasource
if (type === "query") { if (type === "provider") {
const component = findComponent(asset.props, datasource.providerId)
const source = getDatasourceForProvider(asset, component)
return getSchemaForDatasource(asset, source, isForm)
} else if (type === "query") {
const queries = get(queriesStores).list const queries = get(queriesStores).list
table = queries.find(query => query._id === datasource._id) table = queries.find(query => query._id === datasource._id)
} else { } else {

2
packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js

@ -174,7 +174,7 @@ const fieldTypeToComponentMap = {
} }
export function makeDatasourceFormComponents(datasource) { export function makeDatasourceFormComponents(datasource) {
const { schema } = getSchemaForDatasource(datasource, true) const { schema } = getSchemaForDatasource(null, datasource, true)
let components = [] let components = []
let fields = Object.keys(schema || {}) let fields = Object.keys(schema || {})
fields.forEach(field => { fields.forEach(field => {

12
packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte

@ -43,6 +43,7 @@
} }
let originalName = field.name let originalName = field.name
const linkEditDisabled = originalName != null
let primaryDisplay = let primaryDisplay =
$tables.selected.primaryDisplay == null || $tables.selected.primaryDisplay == null ||
$tables.selected.primaryDisplay === field.name $tables.selected.primaryDisplay === field.name
@ -198,7 +199,7 @@
<Input <Input
label="Name" label="Name"
bind:value={field.name} bind:value={field.name}
disabled={uneditable || field.type === LINK_TYPE} disabled={uneditable || (linkEditDisabled && field.type === LINK_TYPE)}
/> />
<Select <Select
@ -285,6 +286,7 @@
{:else if field.type === "link"} {:else if field.type === "link"}
<Select <Select
label="Table" label="Table"
disabled={linkEditDisabled}
bind:value={field.tableId} bind:value={field.tableId}
options={tableOptions} options={tableOptions}
getOptionLabel={table => table.name} getOptionLabel={table => table.name}
@ -292,7 +294,7 @@
/> />
{#if relationshipOptions && relationshipOptions.length > 0} {#if relationshipOptions && relationshipOptions.length > 0}
<RadioGroup <RadioGroup
disabled={originalName != null} disabled={linkEditDisabled}
label="Define the relationship" label="Define the relationship"
bind:value={field.relationshipType} bind:value={field.relationshipType}
options={relationshipOptions} options={relationshipOptions}
@ -300,7 +302,11 @@
getOptionValue={option => option.value} getOptionValue={option => option.value}
/> />
{/if} {/if}
<Input label={`Column name in other table`} bind:value={field.fieldName} /> <Input
disabled={linkEditDisabled}
label={`Column name in other table`}
bind:value={field.fieldName}
/>
{:else if field.type === FORMULA_TYPE} {:else if field.type === FORMULA_TYPE}
<ModalBindableInput <ModalBindableInput
title="Handlebars Formula" title="Handlebars Formula"

3
packages/builder/src/components/design/AppPreview/componentStructure.json

@ -18,7 +18,8 @@
"longformfield", "longformfield",
"datetimefield", "datetimefield",
"attachmentfield", "attachmentfield",
"relationshipfield" "relationshipfield",
"daterangepicker"
] ]
}, },
{ {

31
packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte

@ -1,5 +1,8 @@
<script> <script>
import { getBindableProperties } from "builderStore/dataBinding" import {
getBindableProperties,
getDataProviderComponents,
} from "builderStore/dataBinding"
import { import {
Button, Button,
Popover, Popover,
@ -61,6 +64,17 @@
$currentAsset, $currentAsset,
$store.selectedComponentId $store.selectedComponentId
) )
$: dataProviders = getDataProviderComponents(
$currentAsset,
$store.selectedComponentId
).map(provider => ({
label: provider._instanceName,
name: provider._instanceName,
providerId: provider._id,
value: `{{ literal [${provider._id}] }}`,
type: "provider",
schema: provider.schema,
}))
$: queryBindableProperties = bindableProperties.map(property => ({ $: queryBindableProperties = bindableProperties.map(property => ({
...property, ...property,
category: property.type === "instance" ? "Component" : "Table", category: property.type === "instance" ? "Component" : "Table",
@ -182,7 +196,20 @@
</li> </li>
{/each} {/each}
</ul> </ul>
<Divider size="S" />
<div class="title">
<Heading size="XS">Data Providers</Heading>
</div>
<ul>
{#each dataProviders as provider}
<li
class:selected={value === provider}
on:click={() => handleSelected(provider)}
>
{provider.label}
</li>
{/each}
</ul>
{#if otherSources?.length} {#if otherSources?.length}
<Divider size="S" /> <Divider size="S" />
<div class="title"> <div class="title">

6
packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte

@ -14,11 +14,11 @@
$currentAsset, $currentAsset,
$store.selectedComponentId $store.selectedComponentId
) )
$: schemaFields = getSchemaFields(parameters?.tableId) $: schemaFields = getSchemaFields($currentAsset, parameters?.tableId)
$: tableOptions = $tables.list || [] $: tableOptions = $tables.list || []
const getSchemaFields = tableId => { const getSchemaFields = (asset, tableId) => {
const { schema } = getSchemaForDatasource({ type: "table", tableId }) const { schema } = getSchemaForDatasource(asset, { type: "table", tableId })
return Object.values(schema || {}) return Object.values(schema || {})
} }

12
packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js

@ -5,11 +5,13 @@ import ExecuteQuery from "./ExecuteQuery.svelte"
import TriggerAutomation from "./TriggerAutomation.svelte" import TriggerAutomation from "./TriggerAutomation.svelte"
import ValidateForm from "./ValidateForm.svelte" import ValidateForm from "./ValidateForm.svelte"
// defines what actions are available, when adding a new one // Defines which actions are available to configure in the front end.
// the component is the setup panel for the action // Unfortunately the "name" property is used as the identifier so please don't
// NOTE that the "name" is used by the client library, // change them.
// so if you want to change it, you must change it client lib too // The client library removes any spaces when processing actions, so they can
// be considered as camel case too.
// There is technical debt here to sanitize all these and standardise them
// across the packages but it's a breaking change to existing apps.
export default [ export default [
{ {
name: "Save Row", name: "Save Row",

2
packages/builder/src/components/design/PropertiesPanel/PropertyControls/FieldSelect.svelte

@ -14,7 +14,7 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
$: datasource = getDatasourceForProvider($currentAsset, componentInstance) $: datasource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchemaForDatasource(datasource).schema $: schema = getSchemaForDatasource($currentAsset, datasource).schema
$: options = Object.keys(schema || {}) $: options = Object.keys(schema || {})
$: boundValue = getValidValue(value, options) $: boundValue = getValidValue(value, options)

30
packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte

@ -27,19 +27,16 @@
? tempValue.length ? tempValue.length
: Object.keys(tempValue || {}).length : Object.keys(tempValue || {}).length
$: dataSource = getDatasourceForProvider($currentAsset, componentInstance) $: dataSource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchemaForDatasource(dataSource)?.schema $: schema = getSchemaForDatasource($currentAsset, dataSource)?.schema
$: schemaFields = Object.values(schema || {}) $: schemaFields = Object.values(schema || {})
$: internalTable = dataSource?.type === "table" $: internalTable = dataSource?.type === "table"
// Reset value if value is wrong type for the datasource. // Reset value if value is wrong type for the datasource.
// Lucene editor needs an array, and simple editor needs an object. // Lucene editor needs an array, and simple editor needs an object.
$: { $: {
if (internalTable && !Array.isArray(value)) { if (!Array.isArray(value)) {
tempValue = [] tempValue = []
dispatch("change", []) dispatch("change", [])
} else if (!internalTable && Array.isArray(value)) {
tempValue = {}
dispatch("change", {})
} }
} }
@ -63,28 +60,7 @@
constaints. constaints.
{/if} {/if}
</Body> </Body>
{#if internalTable} <LuceneFilterBuilder bind:value={tempValue} {schemaFields} />
<LuceneFilterBuilder bind:value={tempValue} {schemaFields} />
{:else}
<div class="fields">
<SaveFields
parameterFields={Array.isArray(value) ? {} : value}
{schemaFields}
valueLabel="Equals"
on:change={e => (tempValue = e.detail)}
/>
</div>
{/if}
</Layout> </Layout>
</DrawerContent> </DrawerContent>
</Drawer> </Drawer>
<style>
.fields {
display: grid;
column-gap: var(--spacing-l);
row-gap: var(--spacing-s);
align-items: center;
grid-template-columns: auto 1fr auto 1fr auto;
}
</style>

2
packages/builder/src/components/design/PropertiesPanel/PropertyControls/FormFieldSelect.svelte

@ -18,7 +18,7 @@
component => component._component === "@budibase/standard-components/form" component => component._component === "@budibase/standard-components/form"
) )
$: datasource = getDatasourceForProvider($currentAsset, form) $: datasource = getDatasourceForProvider($currentAsset, form)
$: schema = getSchemaForDatasource(datasource, true).schema $: schema = getSchemaForDatasource($currentAsset, datasource, true).schema
$: options = getOptions(schema, type) $: options = getOptions(schema, type)
const getOptions = (schema, fieldType) => { const getOptions = (schema, fieldType) => {

2
packages/builder/src/components/design/PropertiesPanel/PropertyControls/MultiFieldSelect.svelte

@ -14,7 +14,7 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
$: datasource = getDatasourceForProvider($currentAsset, componentInstance) $: datasource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchemaForDatasource(datasource).schema $: schema = getSchemaForDatasource($currentAsset, datasource).schema
$: options = Object.keys(schema || {}) $: options = Object.keys(schema || {})
$: boundValue = getValidOptions(value, options) $: boundValue = getValidOptions(value, options)

4
packages/builder/src/pages/builder/portal/_layout.svelte

@ -43,6 +43,10 @@
title: "Theming", title: "Theming",
href: "/builder/portal/settings/theming", href: "/builder/portal/settings/theming",
}, },
{
title: "Updates",
href: "/builder/portal/settings/update",
},
]) ])
} else { } else {
menu = menu.concat([ menu = menu.concat([

8
packages/builder/src/pages/builder/portal/settings/organisation.svelte

@ -12,7 +12,7 @@
notifications, notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import { auth, organisation } from "stores/portal" import { auth, organisation } from "stores/portal"
import { post } from "builderStore/api" import { post, get } from "builderStore/api"
import analytics from "analytics" import analytics from "analytics"
import { writable } from "svelte/store" import { writable } from "svelte/store"
import { redirect } from "@roxi/routify" import { redirect } from "@roxi/routify"
@ -130,10 +130,10 @@
<Toggle text="" bind:value={$values.analytics} /> <Toggle text="" bind:value={$values.analytics} />
</div> </div>
</div> </div>
<div>
<Button disabled={loading} on:click={saveConfig} cta>Save</Button>
</div>
</Layout> </Layout>
<div>
<Button disabled={loading} on:click={saveConfig} cta>Save</Button>
</div>
</Layout> </Layout>
{/if} {/if}

70
packages/builder/src/pages/builder/portal/settings/update.svelte

@ -0,0 +1,70 @@
<script>
import {
Layout,
Heading,
Body,
Button,
Divider,
Label,
Input,
Toggle,
Dropzone,
notifications,
} from "@budibase/bbui"
import { auth, organisation } from "stores/portal"
import { post, get } from "builderStore/api"
import analytics from "analytics"
import { writable } from "svelte/store"
import { redirect } from "@roxi/routify"
// Only admins allowed here
$: {
if (!$auth.isAdmin) {
$redirect("../../portal")
}
}
async function updateBudibase() {
try {
notifications.info("Updating budibase..")
const response = await fetch("/v1/update", {
headers: {
Authorization: "Bearer budibase",
},
})
notifications.success("Your budibase installation is up to date.")
} catch (err) {
notifications.error(`Error installing budibase update ${err}`)
}
}
</script>
{#if $auth.isAdmin}
<Layout>
<Layout gap="XS" noPadding>
<Heading size="M">Update</Heading>
<Body>
Keep your budibase installation up to date to take advantage of the
latest features, security updates and much more.
</Body>
</Layout>
<Divider size="S" />
<div class="fields">
<div class="field">
<Button cta on:click={updateBudibase}>Check For Updates</Button>
</div>
</div>
</Layout>
{/if}
<style>
.fields {
display: grid;
grid-gap: var(--spacing-m);
}
.field {
display: grid;
grid-template-columns: 33% 1fr;
align-items: center;
}
</style>

2
packages/cli/package.json

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "0.9.21", "version": "0.9.27",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "src/index.js",
"bin": { "bin": {

6
packages/client/package.json

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "0.9.21", "version": "0.9.27",
"license": "MPL-2.0", "license": "MPL-2.0",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
@ -18,13 +18,13 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/string-templates": "^0.9.21", "@budibase/string-templates": "^0.9.27",
"regexparam": "^1.3.0", "regexparam": "^1.3.0",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5" "svelte-spa-router": "^3.0.5"
}, },
"devDependencies": { "devDependencies": {
"@budibase/standard-components": "^0.9.21", "@budibase/standard-components": "^0.9.27",
"@rollup/plugin-commonjs": "^18.0.0", "@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1", "@rollup/plugin-node-resolve": "^11.2.1",
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",

4
packages/client/src/components/Component.svelte

@ -55,13 +55,13 @@
} }
// Enriches any string component props using handlebars // Enriches any string component props using handlebars
const updateComponentProps = async (definition, context) => { const updateComponentProps = (definition, context) => {
// Record the timestamp so we can reference it after enrichment // Record the timestamp so we can reference it after enrichment
latestUpdateTime = Date.now() latestUpdateTime = Date.now()
const enrichmentTime = latestUpdateTime const enrichmentTime = latestUpdateTime
// Enrich props with context // Enrich props with context
const enrichedProps = await enrichProps(definition, context) const enrichedProps = enrichProps(definition, context)
// Abandon this update if a newer update has started // Abandon this update if a newer update has started
if (enrichmentTime !== latestUpdateTime) { if (enrichmentTime !== latestUpdateTime) {

1
packages/client/src/constants.js

@ -5,4 +5,5 @@ export const TableNames = {
export const ActionTypes = { export const ActionTypes = {
ValidateForm: "ValidateForm", ValidateForm: "ValidateForm",
RefreshDatasource: "RefreshDatasource", RefreshDatasource: "RefreshDatasource",
SetDataProviderQuery: "SetDataProviderQuery",
} }

2
packages/client/src/sdk.js

@ -9,6 +9,7 @@ import {
import { styleable } from "./utils/styleable" import { styleable } from "./utils/styleable"
import transition from "./utils/transition" import transition from "./utils/transition"
import { linkable } from "./utils/linkable" import { linkable } from "./utils/linkable"
import { getAction } from "./utils/getAction"
import Provider from "./components/Provider.svelte" import Provider from "./components/Provider.svelte"
import { ActionTypes } from "./constants" import { ActionTypes } from "./constants"
@ -22,6 +23,7 @@ export default {
styleable, styleable,
transition, transition,
linkable, linkable,
getAction,
Provider, Provider,
ActionTypes, ActionTypes,
} }

4
packages/client/src/utils/componentProps.js

@ -21,7 +21,7 @@ export const propsAreSame = (a, b) => {
* Enriches component props. * Enriches component props.
* Data bindings are enriched, and button actions are enriched. * Data bindings are enriched, and button actions are enriched.
*/ */
export const enrichProps = async (props, context) => { export const enrichProps = (props, context) => {
// Exclude all private props that start with an underscore // Exclude all private props that start with an underscore
let validProps = {} let validProps = {}
Object.entries(props) Object.entries(props)
@ -41,7 +41,7 @@ export const enrichProps = async (props, context) => {
} }
// Enrich all data bindings in top level props // Enrich all data bindings in top level props
let enrichedProps = await enrichDataBindings(validProps, totalContext) let enrichedProps = enrichDataBindings(validProps, totalContext)
// Enrich click actions if they exist // Enrich click actions if they exist
if (enrichedProps.onClick) { if (enrichedProps.onClick) {

6
packages/client/src/utils/enrichDataBinding.js

@ -1,5 +1,5 @@
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import { processString, processObject } from "@budibase/string-templates" import { processString, processObjectSync } from "@budibase/string-templates"
// Regex to test inputs with to see if they are likely candidates for template strings // Regex to test inputs with to see if they are likely candidates for template strings
const looksLikeTemplate = /{{.*}}/ const looksLikeTemplate = /{{.*}}/
@ -23,6 +23,6 @@ export const enrichDataBinding = async (input, context) => {
* Recursively enriches all props in a props object and returns the new props. * Recursively enriches all props in a props object and returns the new props.
* Props are deeply cloned so that no mutation is done to the source object. * Props are deeply cloned so that no mutation is done to the source object.
*/ */
export const enrichDataBindings = async (props, context) => { export const enrichDataBindings = (props, context) => {
return await processObject(cloneDeep(props), context) return processObjectSync(cloneDeep(props), context)
} }

19
packages/client/src/utils/getAction.js

@ -0,0 +1,19 @@
import { get } from "svelte/store"
import { getContext } from "svelte"
/**
* Gets a component action.
* @param id The component ID that provides the action
* @param type The action type to get
* @returns {null|*} The action function
*/
export const getAction = (id, type) => {
if (!id || !type) {
return null
}
const context = getContext("context")
if (!context) {
return null
}
return get(context)?.[`${id}_${type}`]
}

10
packages/server/package.json

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.9.21", "version": "0.9.27",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/electron.js", "main": "src/electron.js",
"repository": { "repository": {
@ -55,9 +55,9 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.21", "@budibase/auth": "^0.9.27",
"@budibase/client": "^0.9.21", "@budibase/client": "^0.9.27",
"@budibase/string-templates": "^0.9.21", "@budibase/string-templates": "^0.9.27",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",
"@koa/router": "8.0.0", "@koa/router": "8.0.0",
"@sendgrid/mail": "7.1.1", "@sendgrid/mail": "7.1.1",
@ -109,7 +109,7 @@
"devDependencies": { "devDependencies": {
"@babel/core": "^7.14.3", "@babel/core": "^7.14.3",
"@babel/preset-env": "^7.14.4", "@babel/preset-env": "^7.14.4",
"@budibase/standard-components": "^0.9.21", "@budibase/standard-components": "^0.9.27",
"@jest/test-sequencer": "^24.8.0", "@jest/test-sequencer": "^24.8.0",
"babel-jest": "^27.0.2", "babel-jest": "^27.0.2",
"docker-compose": "^0.23.6", "docker-compose": "^0.23.6",

8
packages/server/src/api/controllers/application.js

@ -99,12 +99,18 @@ async function createInstance(template) {
// replicate the template data to the instance DB // replicate the template data to the instance DB
// this is currently very hard to test, downloading and importing template files // this is currently very hard to test, downloading and importing template files
/* istanbul ignore next */ /* istanbul ignore next */
let _rev
if (template && template.useTemplate === "true") { if (template && template.useTemplate === "true") {
const { ok } = await db.load(await getTemplateStream(template)) const { ok } = await db.load(await getTemplateStream(template))
if (!ok) { if (!ok) {
throw "Error loading database dump from template." throw "Error loading database dump from template."
} }
var { _rev } = await db.get(DocumentTypes.APP_METADATA) try {
const response = await db.get(DocumentTypes.APP_METADATA)
_rev = response._rev
} catch (err) {
_rev = null
}
} else { } else {
// create the users table // create the users table
await db.put(USERS_TABLE_SCHEMA) await db.put(USERS_TABLE_SCHEMA)

2
packages/server/src/api/controllers/auth.js

@ -5,7 +5,7 @@ const { getFullUser } = require("../../utilities/users")
exports.fetchSelf = async ctx => { exports.fetchSelf = async ctx => {
const appId = ctx.appId const appId = ctx.appId
const { userId } = ctx.user let userId = ctx.user.userId || ctx.user._id
/* istanbul ignore next */ /* istanbul ignore next */
if (!userId) { if (!userId) {
ctx.body = {} ctx.body = {}

4
packages/server/src/api/controllers/routing.js

@ -63,10 +63,6 @@ exports.fetch = async ctx => {
exports.clientFetch = async ctx => { exports.clientFetch = async ctx => {
const routing = await getRoutingStructure(ctx.appId) const routing = await getRoutingStructure(ctx.appId)
let roleId = ctx.user.role._id let roleId = ctx.user.role._id
// builder is a special case, always return the full routing structure
if (roleId === BUILTIN_ROLE_IDS.BUILDER) {
roleId = BUILTIN_ROLE_IDS.ADMIN
}
const roleIds = await getUserRoleHierarchy(ctx.appId, roleId) const roleIds = await getUserRoleHierarchy(ctx.appId, roleId)
for (let topLevel of Object.values(routing.routes)) { for (let topLevel of Object.values(routing.routes)) {
for (let subpathKey of Object.keys(topLevel.subpaths)) { for (let subpathKey of Object.keys(topLevel.subpaths)) {

5
packages/server/src/api/controllers/static/templates/BudibaseApp.svelte

@ -31,7 +31,6 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
*, *,
*:before, *:before,
*:after { *:after {
@ -41,9 +40,9 @@
</svelte:head> </svelte:head>
<body id="app"> <body id="app">
<script src={clientLibPath}> <script type="application/javascript" src={clientLibPath}>
</script> </script>
<script> <script type="application/javascript">
loadBudibase() loadBudibase()
</script> </script>
</body> </body>

5
packages/server/src/api/controllers/user.js

@ -4,7 +4,6 @@ const {
getUserMetadataParams, getUserMetadataParams,
} = require("../../db/utils") } = require("../../db/utils")
const { InternalTables } = require("../../db/utils") const { InternalTables } = require("../../db/utils")
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { const {
getGlobalUsers, getGlobalUsers,
addAppRoleToUser, addAppRoleToUser,
@ -47,10 +46,6 @@ exports.fetchMetadata = async function (ctx) {
exports.updateSelfMetadata = async function (ctx) { exports.updateSelfMetadata = async function (ctx) {
// overwrite the ID with current users // overwrite the ID with current users
ctx.request.body._id = ctx.user._id ctx.request.body._id = ctx.user._id
if (ctx.user.builder && ctx.user.builder.global) {
// specific case, update self role in global user
await addAppRoleToUser(ctx, ctx.appId, BUILTIN_ROLE_IDS.ADMIN)
}
// make sure no stale rev // make sure no stale rev
delete ctx.request.body._rev delete ctx.request.body._rev
await exports.updateMetadata(ctx) await exports.updateMetadata(ctx)

3
packages/server/src/api/index.js

@ -1,5 +1,5 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const { buildAuthMiddleware } = require("@budibase/auth").auth const { buildAuthMiddleware, auditLog } = require("@budibase/auth").auth
const currentApp = require("../middleware/currentapp") const currentApp = require("../middleware/currentapp")
const compress = require("koa-compress") const compress = require("koa-compress")
const zlib = require("zlib") const zlib = require("zlib")
@ -37,6 +37,7 @@ router
}) })
) )
.use(currentApp) .use(currentApp)
.use(auditLog)
// error handling middleware // error handling middleware
router.use(async (ctx, next) => { router.use(async (ctx, next) => {

4
packages/server/src/api/routes/tests/routing.spec.js

@ -28,9 +28,7 @@ describe("/routing", () => {
it("returns the correct routing for basic user", async () => { it("returns the correct routing for basic user", async () => {
workerRequests.getGlobalUsers.mockImplementationOnce((ctx, appId) => { workerRequests.getGlobalUsers.mockImplementationOnce((ctx, appId) => {
return { return {
roles: { roleId: BUILTIN_ROLE_IDS.BASIC,
[appId]: BUILTIN_ROLE_IDS.BASIC,
}
} }
}) })
const res = await request const res = await request

17
packages/server/src/api/routes/tests/utilities/TestFunctions.js

@ -2,6 +2,7 @@ const rowController = require("../../../controllers/row")
const appController = require("../../../controllers/application") const appController = require("../../../controllers/application")
const CouchDB = require("../../../../db") const CouchDB = require("../../../../db")
const { AppStatus } = require("../../../../db/utils") const { AppStatus } = require("../../../../db/utils")
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
function Request(appId, params) { function Request(appId, params) {
this.appId = appId this.appId = appId
@ -77,11 +78,17 @@ exports.checkPermissionsEndpoint = async ({
.set(passHeader) .set(passHeader)
.expect(200) .expect(200)
user = await config.createUser("fail@budibase.com", password, failRole) let failHeader
const failHeader = await config.login("fail@budibase.com", password, { if (failRole === BUILTIN_ROLE_IDS.PUBLIC) {
roleId: failRole, failHeader = config.publicHeaders()
userId: user.globalId, } else {
}) user = await config.createUser("fail@budibase.com", password, failRole)
failHeader = await config.login("fail@budibase.com", password, {
roleId: failRole,
userId: user.globalId,
builder: false,
})
}
await exports await exports
.createRequest(config.request, method, url, body) .createRequest(config.request, method, url, body)

4
packages/server/src/app.js

@ -5,7 +5,7 @@ require("@budibase/auth").init(CouchDB)
const Koa = require("koa") const Koa = require("koa")
const destroyable = require("server-destroy") const destroyable = require("server-destroy")
const koaBody = require("koa-body") const koaBody = require("koa-body")
const logger = require("koa-pino-logger") const pino = require("koa-pino-logger")
const http = require("http") const http = require("http")
const api = require("./api") const api = require("./api")
const eventEmitter = require("./events") const eventEmitter = require("./events")
@ -29,7 +29,7 @@ app.use(
) )
app.use( app.use(
logger({ pino({
prettyPrint: { prettyPrint: {
levelFirst: true, levelFirst: true,
}, },

3
packages/server/src/automations/actions.js

@ -4,6 +4,7 @@ const createRow = require("./steps/createRow")
const updateRow = require("./steps/updateRow") const updateRow = require("./steps/updateRow")
const deleteRow = require("./steps/deleteRow") const deleteRow = require("./steps/deleteRow")
const executeScript = require("./steps/executeScript") const executeScript = require("./steps/executeScript")
const bash = require("./steps/bash")
const executeQuery = require("./steps/executeQuery") const executeQuery = require("./steps/executeQuery")
const outgoingWebhook = require("./steps/outgoingWebhook") const outgoingWebhook = require("./steps/outgoingWebhook")
const env = require("../environment") const env = require("../environment")
@ -21,6 +22,7 @@ const BUILTIN_ACTIONS = {
DELETE_ROW: deleteRow.run, DELETE_ROW: deleteRow.run,
OUTGOING_WEBHOOK: outgoingWebhook.run, OUTGOING_WEBHOOK: outgoingWebhook.run,
EXECUTE_SCRIPT: executeScript.run, EXECUTE_SCRIPT: executeScript.run,
EXECUTE_BASH: bash.run,
EXECUTE_QUERY: executeQuery.run, EXECUTE_QUERY: executeQuery.run,
} }
const BUILTIN_DEFINITIONS = { const BUILTIN_DEFINITIONS = {
@ -32,6 +34,7 @@ const BUILTIN_DEFINITIONS = {
OUTGOING_WEBHOOK: outgoingWebhook.definition, OUTGOING_WEBHOOK: outgoingWebhook.definition,
EXECUTE_SCRIPT: executeScript.definition, EXECUTE_SCRIPT: executeScript.definition,
EXECUTE_QUERY: executeQuery.definition, EXECUTE_QUERY: executeQuery.definition,
EXECUTE_BASH: bash.definition,
} }
let MANIFEST = null let MANIFEST = null

62
packages/server/src/automations/steps/bash.js

@ -0,0 +1,62 @@
const { execSync } = require("child_process")
const { processStringSync } = require("@budibase/string-templates")
module.exports.definition = {
name: "Bash Scripting",
tagline: "Execute a bash command",
icon: "ri-terminal-box-line",
description: "Run a bash script",
type: "ACTION",
stepId: "EXECUTE_BASH",
inputs: {},
schema: {
inputs: {
properties: {
code: {
type: "string",
customType: "code",
title: "Code",
},
},
required: ["code"],
},
outputs: {
properties: {
stdout: {
type: "string",
description: "Standard output of your bash command or script.",
},
},
},
required: ["stdout"],
},
}
module.exports.run = async function ({ inputs, context }) {
if (inputs.code == null) {
return {
stdout: "Budibase bash automation failed: Invalid inputs",
}
}
try {
const command = processStringSync(inputs.code, context)
let stdout
try {
stdout = execSync(command, { timeout: 500 })
} catch (err) {
stdout = err.message
}
return {
stdout,
}
} catch (err) {
console.error(err)
return {
success: false,
response: err,
}
}
}

2
packages/server/src/automations/steps/executeScript.js

@ -1,7 +1,7 @@
const scriptController = require("../../api/controllers/script") const scriptController = require("../../api/controllers/script")
module.exports.definition = { module.exports.definition = {
name: "Scripting", name: "JS Scripting",
tagline: "Execute JavaScript Code", tagline: "Execute JavaScript Code",
icon: "ri-terminal-box-line", icon: "ri-terminal-box-line",
description: "Run a piece of JavaScript code in your automation", description: "Run a piece of JavaScript code in your automation",

2
packages/server/src/middleware/currentapp.js

@ -33,7 +33,7 @@ module.exports = async (ctx, next) => {
updateCookie = true updateCookie = true
appId = requestAppId appId = requestAppId
// retrieving global user gets the right role // retrieving global user gets the right role
roleId = globalUser.roleId roleId = globalUser.roleId || BUILTIN_ROLE_IDS.PUBLIC
} else if (appCookie != null) { } else if (appCookie != null) {
appId = appCookie.appId appId = appCookie.appId
roleId = appCookie.roleId || BUILTIN_ROLE_IDS.PUBLIC roleId = appCookie.roleId || BUILTIN_ROLE_IDS.PUBLIC

10
packages/server/src/tests/utilities/TestConfiguration.js

@ -101,7 +101,7 @@ class TestConfiguration {
userId: GLOBAL_USER_ID, userId: GLOBAL_USER_ID,
} }
const app = { const app = {
roleId: BUILTIN_ROLE_IDS.BUILDER, roleId: BUILTIN_ROLE_IDS.ADMIN,
appId: this.appId, appId: this.appId,
} }
const authToken = jwt.sign(auth, env.JWT_SECRET) const authToken = jwt.sign(auth, env.JWT_SECRET)
@ -306,12 +306,9 @@ class TestConfiguration {
return await this._req(config, null, controllers.layout.save) return await this._req(config, null, controllers.layout.save)
} }
async createUser(roleId = BUILTIN_ROLE_IDS.POWER) { async createUser() {
const globalId = `us_${Math.random()}` const globalId = `us_${Math.random()}`
const resp = await this.globalUser( const resp = await this.globalUser(globalId)
globalId,
roleId === BUILTIN_ROLE_IDS.BUILDER
)
return { return {
...resp, ...resp,
globalId, globalId,
@ -319,7 +316,6 @@ class TestConfiguration {
} }
async login(email, password, { roleId, userId, builder } = {}) { async login(email, password, { roleId, userId, builder } = {}) {
roleId = !roleId ? BUILTIN_ROLE_IDS.BUILDER : roleId
userId = !userId ? `us_uuid1` : userId userId = !userId ? `us_uuid1` : userId
if (!this.request) { if (!this.request) {
throw "Server has not been opened, cannot login." throw "Server has not been opened, cannot login."

4
packages/server/src/utilities/fileSystem/newApp.js

@ -30,5 +30,7 @@ exports.uploadClientLibrary = async appId => {
const sourcepath = require.resolve("@budibase/client") const sourcepath = require.resolve("@budibase/client")
const destPath = join(appId, "budibase-client.js") const destPath = join(appId, "budibase-client.js")
await streamUpload(BUCKET_NAME, destPath, fs.createReadStream(sourcepath)) await streamUpload(BUCKET_NAME, destPath, fs.createReadStream(sourcepath), {
ContentType: "application/javascript",
})
} }

8
packages/server/src/utilities/redis.js

@ -12,12 +12,8 @@ exports.init = async () => {
} }
exports.shutdown = async () => { exports.shutdown = async () => {
if (devAppClient != null) { if (devAppClient) await devAppClient.finish()
await devAppClient.finish() if (debounceClient) await debounceClient.finish()
}
if (debounceClient != null) {
await debounceClient.finish()
}
} }
exports.doesUserHaveLock = async (devAppId, user) => { exports.doesUserHaveLock = async (devAppId, user) => {

5
packages/server/src/utilities/rowProcessor.js

@ -169,9 +169,14 @@ exports.inputProcessing = (user = {}, table, row) => {
let clonedRow = cloneDeep(row) let clonedRow = cloneDeep(row)
// need to copy the table so it can be differenced on way out // need to copy the table so it can be differenced on way out
const copiedTable = cloneDeep(table) const copiedTable = cloneDeep(table)
const dontCleanseKeys = ["type", "_id", "_rev", "tableId"]
for (let [key, value] of Object.entries(clonedRow)) { for (let [key, value] of Object.entries(clonedRow)) {
const field = table.schema[key] const field = table.schema[key]
// cleanse fields that aren't in the schema
if (!field) { if (!field) {
if (dontCleanseKeys.indexOf(key) === -1) {
delete clonedRow[key]
}
continue continue
} }
clonedRow[key] = exports.coerce(value, field.type) clonedRow[key] = exports.coerce(value, field.type)

36
packages/server/src/utilities/workerRequests.js

@ -9,19 +9,26 @@ function getAppRole(appId, user) {
if (!user.roles) { if (!user.roles) {
return user return user
} }
// always use the deployed app if (user.builder && user.builder.global) {
user.roleId = user.roles[getDeployedAppID(appId)] user.roleId = BUILTIN_ROLE_IDS.ADMIN
if (!user.roleId) { } else {
user.roleId = BUILTIN_ROLE_IDS.PUBLIC // always use the deployed app
user.roleId = user.roles[getDeployedAppID(appId)]
if (!user.roleId) {
user.roleId = BUILTIN_ROLE_IDS.PUBLIC
}
} }
delete user.roles delete user.roles
return user return user
} }
function request(ctx, request) { function request(ctx, request, noApiKey) {
if (!request.headers) { if (!request.headers) {
request.headers = {} request.headers = {}
} }
if (!noApiKey) {
request.headers["x-budibase-api-key"] = env.INTERNAL_API_KEY
}
if (request.body && Object.keys(request.body).length > 0) { if (request.body && Object.keys(request.body).length > 0) {
request.headers["Content-Type"] = "application/json" request.headers["Content-Type"] = "application/json"
request.body = request.body =
@ -44,9 +51,6 @@ exports.sendSmtpEmail = async (to, from, subject, contents) => {
checkSlashesInUrl(env.WORKER_URL + `/api/admin/email/send`), checkSlashesInUrl(env.WORKER_URL + `/api/admin/email/send`),
request(null, { request(null, {
method: "POST", method: "POST",
headers: {
"x-budibase-api-key": env.INTERNAL_API_KEY,
},
body: { body: {
email: to, email: to,
from, from,
@ -86,16 +90,6 @@ exports.getDeployedApps = async ctx => {
} }
} }
exports.deleteGlobalUser = async (ctx, globalId) => {
const endpoint = `/api/admin/users/${globalId}`
const reqCfg = { method: "DELETE" }
const response = await fetch(
checkSlashesInUrl(env.WORKER_URL + endpoint),
request(ctx, reqCfg)
)
return response.json()
}
exports.getGlobalUsers = async (ctx, appId = null, globalId = null) => { exports.getGlobalUsers = async (ctx, appId = null, globalId = null) => {
const endpoint = globalId const endpoint = globalId
? `/api/admin/users/${globalId}` ? `/api/admin/users/${globalId}`
@ -121,7 +115,8 @@ exports.getGlobalSelf = async (ctx, appId = null) => {
const endpoint = `/api/admin/users/self` const endpoint = `/api/admin/users/self`
const response = await fetch( const response = await fetch(
checkSlashesInUrl(env.WORKER_URL + endpoint), checkSlashesInUrl(env.WORKER_URL + endpoint),
request(ctx, { method: "GET" }) // we don't want to use API key when getting self
request(ctx, { method: "GET" }, true)
) )
if (response.status !== 200) { if (response.status !== 200) {
ctx.throw(400, "Unable to get self globally.") ctx.throw(400, "Unable to get self globally.")
@ -172,9 +167,6 @@ exports.removeAppFromUserRoles = async appId => {
checkSlashesInUrl(env.WORKER_URL + `/api/admin/roles/${deployedAppId}`), checkSlashesInUrl(env.WORKER_URL + `/api/admin/roles/${deployedAppId}`),
request(null, { request(null, {
method: "DELETE", method: "DELETE",
headers: {
"x-budibase-api-key": env.INTERNAL_API_KEY,
},
}) })
) )
if (response.status !== 200) { if (response.status !== 200) {

33
packages/standard-components/manifest.json

@ -1505,5 +1505,38 @@
"context": { "context": {
"type": "schema" "type": "schema"
} }
},
"daterangepicker": {
"name": "Date Range",
"icon": "Date",
"styleable": true,
"hasChildren": false,
"info": "Your data provider will be automatically filtered to the given date range.",
"settings": [
{
"type": "dataProvider",
"label": "Provider",
"key": "dataProvider"
},
{
"type": "field",
"label": "Date field",
"key": "field"
},
{
"type": "select",
"label": "Default range",
"key": "defaultValue",
"options": [
"Last 1 day",
"Last 7 days",
"Last 30 days",
"Last 3 months",
"Last 6 months",
"Last 1 year"
],
"defaultValue": "Last 30 days"
}
]
} }
} }

5
packages/standard-components/package.json

@ -29,14 +29,15 @@
"keywords": [ "keywords": [
"svelte" "svelte"
], ],
"version": "0.9.21", "version": "0.9.27",
"license": "MIT", "license": "MIT",
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.21", "@budibase/bbui": "^0.9.27",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",
"apexcharts": "^3.22.1", "apexcharts": "^3.22.1",
"dayjs": "^1.10.5",
"svelte-apexcharts": "^1.0.2", "svelte-apexcharts": "^1.0.2",
"svelte-flatpickr": "^3.1.0" "svelte-flatpickr": "^3.1.0"
} }

123
packages/standard-components/src/DataProvider.svelte

@ -1,6 +1,12 @@
<script> <script>
import { getContext } from "svelte" import { getContext } from "svelte"
import { ProgressCircle, Pagination } from "@budibase/bbui" import { ProgressCircle, Pagination } from "@budibase/bbui"
import {
buildLuceneQuery,
luceneQuery,
luceneSort,
luceneLimit,
} from "./lucene"
export let dataSource export let dataSource
export let filter export let filter
@ -25,9 +31,11 @@
let schema = {} let schema = {}
let bookmarks = [null] let bookmarks = [null]
let pageNumber = 0 let pageNumber = 0
let query = null
$: query = buildLuceneQuery(filter)
$: internalTable = dataSource?.type === "table" $: internalTable = dataSource?.type === "table"
$: query = internalTable ? buildLuceneQuery(filter) : null $: nestedProvider = dataSource?.type === "provider"
$: hasNextPage = bookmarks[pageNumber + 1] != null $: hasNextPage = bookmarks[pageNumber + 1] != null
$: hasPrevPage = pageNumber > 0 $: hasPrevPage = pageNumber > 0
$: getSchema(dataSource) $: getSchema(dataSource)
@ -48,22 +56,38 @@
} }
} }
$: { $: {
// Sort and limit rows in memory when we aren't searching internal tables
if (internalTable) { if (internalTable) {
// Internal tables are already processed server-side
rows = allRows rows = allRows
} else { } else {
const sortedRows = sortRows(allRows, sortColumn, sortOrder) // For anything else we use client-side implementations to filter, sort
rows = limitRows(sortedRows, limit) // and limit
const filtered = luceneQuery(allRows, query)
const sorted = luceneSort(filtered, sortColumn, sortOrder, sortType)
rows = luceneLimit(sorted, limit)
} }
} }
$: actions = [ $: actions = [
{ {
type: ActionTypes.RefreshDatasource, type: ActionTypes.RefreshDatasource,
callback: () => fetchData(dataSource), callback: () => refresh(),
metadata: { dataSource }, metadata: { dataSource },
}, },
{
type: ActionTypes.SetDataProviderQuery,
callback: newQuery => (query = newQuery),
},
] ]
$: dataContext = { rows, schema, rowsLength: rows.length } $: dataContext = {
rows,
schema,
rowsLength: rows.length,
// Undocumented properties. These aren't supposed to be used in builder
// bindings, but are used internally by other components
id: $component?.id,
state: { query },
}
const getSortType = (schema, sortColumn) => { const getSortType = (schema, sortColumn) => {
if (!schema || !sortColumn || !schema[sortColumn]) { if (!schema || !sortColumn || !schema[sortColumn]) {
@ -73,36 +97,18 @@
return type === "number" ? "number" : "string" return type === "number" ? "number" : "string"
} }
const buildLuceneQuery = filter => { const refresh = async () => {
let query = { if (schemaLoaded && !nestedProvider) {
string: {}, fetchData(
fuzzy: {}, dataSource,
range: {}, query,
equal: {}, limit,
notEqual: {}, sortColumn,
empty: {}, sortOrder,
notEmpty: {}, sortType,
} paginate
if (Array.isArray(filter)) { )
filter.forEach(({ operator, field, type, value }) => {
if (operator.startsWith("range")) {
if (!query.range[field]) {
query.range[field] = {
low: type === "number" ? Number.MIN_SAFE_INTEGER : "0000",
high: type === "number" ? Number.MAX_SAFE_INTEGER : "9999",
}
}
if (operator === "rangeLow") {
query.range[field].low = value
} else if (operator === "rangeHigh") {
query.range[field].high = value
}
} else if (query[operator]) {
query[operator][field] = value
}
})
} }
return query
} }
const fetchData = async ( const fetchData = async (
@ -116,6 +122,7 @@
) => { ) => {
loading = true loading = true
if (dataSource?.type === "table") { if (dataSource?.type === "table") {
// For internal tables we use server-side processing
const res = await API.searchTable({ const res = await API.searchTable({
tableId: dataSource.tableId, tableId: dataSource.tableId,
query, query,
@ -132,55 +139,27 @@
} else { } else {
bookmarks = [null] bookmarks = [null]
} }
} else if (dataSource?.type === "provider") {
// For providers referencing another provider, just use the rows it
// provides
allRows = dataSource?.value?.rows ?? []
} else { } else {
const rows = await API.fetchDatasource(dataSource) // For other data sources like queries or views, fetch all rows from the
allRows = inMemoryFilterRows(rows, filter) // server
allRows = await API.fetchDatasource(dataSource)
} }
loading = false loading = false
loaded = true loaded = true
} }
const inMemoryFilterRows = (rows, filter) => {
let filteredData = [...rows]
Object.entries(filter || {}).forEach(([field, value]) => {
if (value != null && value !== "") {
filteredData = filteredData.filter(row => {
return row[field] === value
})
}
})
return filteredData
}
const sortRows = (rows, sortColumn, sortOrder) => {
if (!sortColumn || !sortOrder) {
return rows
}
return rows.slice().sort((a, b) => {
const colA = a[sortColumn]
const colB = b[sortColumn]
if (sortOrder === "Descending") {
return colA > colB ? -1 : 1
} else {
return colA > colB ? 1 : -1
}
})
}
const limitRows = (rows, limit) => {
const numLimit = parseFloat(limit)
if (isNaN(numLimit)) {
return rows
}
return rows.slice(0, numLimit)
}
const getSchema = async dataSource => { const getSchema = async dataSource => {
if (dataSource?.schema) { if (dataSource?.schema) {
schema = dataSource.schema schema = dataSource.schema
} else if (dataSource?.tableId) { } else if (dataSource?.tableId) {
const definition = await API.fetchTableDefinition(dataSource.tableId) const definition = await API.fetchTableDefinition(dataSource.tableId)
schema = definition?.schema ?? {} schema = definition?.schema ?? {}
} else if (dataSource?.type === "provider") {
schema = dataSource.value?.schema ?? {}
} else { } else {
schema = {} schema = {}
} }

77
packages/standard-components/src/DateRangePicker.svelte

@ -0,0 +1,77 @@
<script>
import { Select } from "@budibase/bbui"
import { getContext } from "svelte"
import dayjs from "dayjs"
import utc from "dayjs/plugin/utc"
import { onMount } from "svelte"
dayjs.extend(utc)
export let dataProvider
export let field
export let defaultValue
const dataContext = getContext("context")
const component = getContext("component")
const { styleable, builderStore, ActionTypes, getAction } = getContext("sdk")
const setQuery = getAction(dataProvider?.id, ActionTypes.SetDataProviderQuery)
const options = [
"Last 1 day",
"Last 7 days",
"Last 30 days",
"Last 3 months",
"Last 6 months",
"Last 1 year",
]
let value = options.includes(defaultValue) ? defaultValue : "Last 30 days"
const updateDateRange = option => {
const query = dataProvider?.state?.query
if (!query || !setQuery) {
return
}
value = option
let low = dayjs.utc().subtract(1, "year")
let high = dayjs.utc().add(1, "day")
if (option === "Last 1 day") {
low = dayjs.utc().subtract(1, "day")
} else if (option === "Last 7 days") {
low = dayjs.utc().subtract(7, "days")
} else if (option === "Last 30 days") {
low = dayjs.utc().subtract(30, "days")
} else if (option === "Last 3 months") {
low = dayjs.utc().subtract(3, "months")
} else if (option === "Last 6 months") {
low = dayjs.utc().subtract(6, "months")
}
// Update data provider query with the new filter
setQuery({
...query,
range: {
...query.range,
[field]: {
high: high.format(),
low: low.format(),
},
},
})
}
// Update the range on mount to the initial value
onMount(() => {
updateDateRange(value)
})
</script>
<div use:styleable={$component.styles}>
<Select
placeholder={null}
{options}
{value}
on:change={e => updateDateRange(e.detail)}
/>
</div>

1
packages/standard-components/src/forms/StringField.svelte

@ -22,6 +22,7 @@
> >
{#if fieldState} {#if fieldState}
<CoreTextField <CoreTextField
updateOnChange={false}
value={$fieldState.value} value={$fieldState.value}
on:change={e => fieldApi.setValue(e.detail)} on:change={e => fieldApi.setValue(e.detail)}
disabled={$fieldState.disabled} disabled={$fieldState.disabled}

1
packages/standard-components/src/index.js

@ -27,6 +27,7 @@ export { default as cardhorizontal } from "./CardHorizontal.svelte"
export { default as cardstat } from "./CardStat.svelte" export { default as cardstat } from "./CardStat.svelte"
export { default as icon } from "./Icon.svelte" export { default as icon } from "./Icon.svelte"
export { default as backgroundimage } from "./BackgroundImage.svelte" export { default as backgroundimage } from "./BackgroundImage.svelte"
export { default as daterangepicker } from "./DateRangePicker.svelte"
export * from "./charts" export * from "./charts"
export * from "./forms" export * from "./forms"
export * from "./table" export * from "./table"

146
packages/standard-components/src/lucene.js

@ -0,0 +1,146 @@
/**
* Builds a lucene JSON query from the filter structure generated in the builder
* @param filter the builder filter structure
*/
export const buildLuceneQuery = filter => {
let query = {
string: {},
fuzzy: {},
range: {},
equal: {},
notEqual: {},
empty: {},
notEmpty: {},
}
if (Array.isArray(filter)) {
filter.forEach(({ operator, field, type, value }) => {
if (operator.startsWith("range")) {
if (!query.range[field]) {
query.range[field] = {
low:
type === "number"
? Number.MIN_SAFE_INTEGER
: "0000-00-00T00:00:00.000Z",
high:
type === "number"
? Number.MAX_SAFE_INTEGER
: "9999-00-00T00:00:00.000Z",
}
}
if (operator === "rangeLow" && value != null && value !== "") {
query.range[field].low = value
} else if (operator === "rangeHigh" && value != null && value !== "") {
query.range[field].high = value
}
} else if (query[operator]) {
query[operator][field] = value
}
})
}
return query
}
/**
* Performs a client-side lucene search on an array of data
* @param docs the data
* @param query the JSON lucene query
*/
export const luceneQuery = (docs, query) => {
if (!query) {
return docs
}
// Iterates over a set of filters and evaluates a fail function against a doc
const match = (type, failFn) => doc => {
const filters = Object.entries(query[type] || {})
for (let i = 0; i < filters.length; i++) {
if (failFn(filters[i][0], filters[i][1], doc)) {
return false
}
}
return true
}
// Process a string match (fails if the value does not start with the string)
const stringMatch = match("string", (key, value, doc) => {
return !doc[key] || !doc[key].startsWith(value)
})
// Process a range match
const rangeMatch = match("range", (key, value, doc) => {
return !doc[key] || doc[key] < value.low || doc[key] > value.high
})
// Process an equal match (fails if the value is different)
const equalMatch = match("equal", (key, value, doc) => {
return doc[key] !== value
})
// Process a not-equal match (fails if the value is the same)
const notEqualMatch = match("notEqual", (key, value, doc) => {
return doc[key] === value
})
// Process an empty match (fails if the value is not empty)
const emptyMatch = match("empty", (key, value, doc) => {
return doc[key] != null && doc[key] !== ""
})
// Process a not-empty match (fails is the value is empty)
const notEmptyMatch = match("notEmpty", (key, value, doc) => {
return doc[key] == null || doc[key] === ""
})
// Match a document against all criteria
const docMatch = doc => {
return (
stringMatch(doc) &&
rangeMatch(doc) &&
equalMatch(doc) &&
notEqualMatch(doc) &&
emptyMatch(doc) &&
notEmptyMatch(doc)
)
}
// Process all docs
return docs.filter(docMatch)
}
/**
* Performs a client-side sort from the equivalent server-side lucene sort
* parameters.
* @param docs the data
* @param sort the sort column
* @param sortOrder the sort order ("ascending" or "descending")
* @param sortType the type of sort ("string" or "number")
*/
export const luceneSort = (docs, sort, sortOrder, sortType = "string") => {
if (!sort || !sortOrder || !sortType) {
return docs
}
const parse = sortType === "string" ? x => `${x}` : x => parseFloat(x)
return docs.slice().sort((a, b) => {
const colA = parse(a[sort])
const colB = parse(b[sort])
if (sortOrder === "Descending") {
return colA > colB ? -1 : 1
} else {
return colA > colB ? 1 : -1
}
})
}
/**
* Limits the specified docs to the specified number of rows from the equivalent
* server-side lucene limit parameters.
* @param docs the data
* @param limit the number of docs to limit to
*/
export const luceneLimit = (docs, limit) => {
const numLimit = parseFloat(limit)
if (isNaN(numLimit)) {
return docs
}
return docs.slice(0, numLimit)
}

255
packages/standard-components/yarn.lock

@ -2,6 +2,59 @@
# yarn lockfile v1 # yarn lockfile v1
"@adobe/spectrum-css-workflow-icons@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@adobe/spectrum-css-workflow-icons/-/spectrum-css-workflow-icons-1.2.1.tgz#7e2cb3fcfb5c8b12d7275afafbb6ec44913551b4"
integrity sha512-uVgekyBXnOVkxp+CUssjN/gefARtudZC8duEn1vm0lBQFwGRZFlDEzU1QC+aIRWCrD1Z8OgRpmBYlSZ7QS003w==
"@budibase/bbui@^0.9.12":
version "0.9.16"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.16.tgz#6cf454790d3e8d776d4d642379958a56260b0840"
integrity sha512-8hV4Vj3h3clzLclNTvVxPdOK9f02xTIcXA/lW1QPD9HZLOUxDogphujpDpb+E0/NevPlPYMHaX68kVtefNL5Jw==
dependencies:
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
"@spectrum-css/actionbutton" "^1.0.1"
"@spectrum-css/actiongroup" "^1.0.1"
"@spectrum-css/avatar" "^3.0.2"
"@spectrum-css/button" "^3.0.1"
"@spectrum-css/buttongroup" "^3.0.2"
"@spectrum-css/checkbox" "^3.0.2"
"@spectrum-css/dialog" "^3.0.1"
"@spectrum-css/divider" "^1.0.1"
"@spectrum-css/dropzone" "^3.0.2"
"@spectrum-css/fieldgroup" "^3.0.2"
"@spectrum-css/fieldlabel" "^3.0.1"
"@spectrum-css/icon" "^3.0.1"
"@spectrum-css/illustratedmessage" "^3.0.2"
"@spectrum-css/inputgroup" "^3.0.2"
"@spectrum-css/label" "^2.0.10"
"@spectrum-css/link" "^3.1.1"
"@spectrum-css/menu" "^3.0.1"
"@spectrum-css/modal" "^3.0.1"
"@spectrum-css/pagination" "^3.0.3"
"@spectrum-css/picker" "^1.0.1"
"@spectrum-css/popover" "^3.0.1"
"@spectrum-css/progressbar" "^1.0.2"
"@spectrum-css/progresscircle" "^1.0.2"
"@spectrum-css/radio" "^3.0.2"
"@spectrum-css/search" "^3.0.2"
"@spectrum-css/sidenav" "^3.0.2"
"@spectrum-css/statuslight" "^3.0.2"
"@spectrum-css/switch" "^1.0.2"
"@spectrum-css/table" "^3.0.1"
"@spectrum-css/tabs" "^3.0.1"
"@spectrum-css/tags" "^3.0.2"
"@spectrum-css/textfield" "^3.0.1"
"@spectrum-css/toast" "^3.0.1"
"@spectrum-css/tooltip" "^3.0.3"
"@spectrum-css/treeview" "^3.0.2"
"@spectrum-css/typography" "^3.0.1"
"@spectrum-css/underlay" "^2.0.9"
"@spectrum-css/vars" "^3.0.1"
dayjs "^1.10.4"
svelte-flatpickr "^3.1.0"
svelte-portal "^1.0.0"
"@rollup/pluginutils@^4.1.0": "@rollup/pluginutils@^4.1.0":
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.0.tgz#0dcc61c780e39257554feb7f77207dceca13c838" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.0.tgz#0dcc61c780e39257554feb7f77207dceca13c838"
@ -10,6 +63,98 @@
estree-walker "^2.0.1" estree-walker "^2.0.1"
picomatch "^2.2.2" picomatch "^2.2.2"
"@spectrum-css/actionbutton@^1.0.1":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/actionbutton/-/actionbutton-1.0.3.tgz#8f7342a69b303c5acdcfa0a59f5e9267b9f3cb30"
integrity sha512-P9qoCPSiZ1SB6ZYqK5hub0vGty00YYqonQE0KTjtb1i+T1nYR/87vWqVPERx9j63uhgZncjwFYaThTvRqye7eQ==
"@spectrum-css/actiongroup@^1.0.1":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/actiongroup/-/actiongroup-1.0.3.tgz#4713ce65e6f5c72c404a7b638fbc3b4fd7e3874f"
integrity sha512-NlB9Q4ZlWixXxymoPIYG6g2hkwAGKFodHWPFfxHD8ddkjXWRB9G2akUP7cfsJ4DcYn4VisUORCOYQwqDiSmboQ==
"@spectrum-css/avatar@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/avatar/-/avatar-3.0.2.tgz#4f1826223eae330e64b6d3cc899e9bc2e98dac95"
integrity sha512-wEczvSqxttTWSiL3cOvXV/RmGRwSkw2w6+slcHhnf0kb7ovymMM+9oz8vvEpEsSeo5u598bc+7ktrKFpAd6soQ==
"@spectrum-css/button@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/button/-/button-3.0.3.tgz#2df1efaab6c7e0b3b06cb4b59e1eae59c7f1fc84"
integrity sha512-6CnLPqqtaU/PcSSIGeGRi0iFIIxIUByYLKFO6zn5NEUc12KQ28dJ4PLwB6WBa0L8vRoAGlnWWH2ZZweTijbXgg==
"@spectrum-css/buttongroup@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/buttongroup/-/buttongroup-3.0.3.tgz#719d868845ac9d2c4f939c1b9f6044507902d5aa"
integrity sha512-eXl8U4QWMWXqyTu654FdQdEGnmczgOYlpIFSHyCMVjhtPqZp2xwnLFiGh6LKw+bLio6eeOZ0L+vpk1GcoYqgkw==
"@spectrum-css/checkbox@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/checkbox/-/checkbox-3.0.3.tgz#8577067fc8b97e4609f92bd242364937a533a7bb"
integrity sha512-QVG9uMHq+lh70Dh6mDNnY+vEvNz2p7VC6xgLfDYfijp2qeiqYPq72fQK6p/SiyqPk96ZACzNRwgeROU6Xf6Wgg==
"@spectrum-css/dialog@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/dialog/-/dialog-3.0.3.tgz#7715a4ea435e753afb623d99ca5917ed1bcd6f34"
integrity sha512-AhmKgfRIVyTe3ABiJ8lLUQL34VB/H6fN16na2LlbDRJvyRMzkdN1Xf2i6U3f4OMd3qQ8Gm5xat4BvMxIQPBAUQ==
"@spectrum-css/divider@^1.0.1":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/divider/-/divider-1.0.3.tgz#639e2ebaa0834efa40f42397668bbd5c153ea385"
integrity sha512-Zy4Rn40w8UtzMh3wx/U9+CepSCpm1aOCGftHgWDub0XZuVTzh0c1WwyzTuLCx2Hf21z5VRGNiDh8bGEEzSbtNA==
dependencies:
"@spectrum-css/vars" "^3.0.2"
"@spectrum-css/dropzone@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/dropzone/-/dropzone-3.0.3.tgz#aee71697a2c195947599d7541b858c3c198741dc"
integrity sha512-ujrswdtB6bHigklyHsm6zomFd6rUnKJ3xVVRjroVF4+ouK4DxK5tX0iVd0EW6AOfOjx4Cc28uyFot3fpxp+MQw==
"@spectrum-css/fieldgroup@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/fieldgroup/-/fieldgroup-3.0.3.tgz#85d85da048d08200f25ceab378026dd2b11e0bb2"
integrity sha512-wXUXTXN1CPnR7M4Ltd+3uh7BfcNGUV1+Xe0/h0tCpq/j+S2Sd4xo7/pUMdU19sIDrAAtpEFp1tt+nouHcU5HGQ==
"@spectrum-css/fieldlabel@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/fieldlabel/-/fieldlabel-3.0.3.tgz#f73c04d20734d4718ffb620dc46458904685b449"
integrity sha512-nEvIkEXCD5n4fW67Unq6Iu7VXoauEd/JGpfTY02VsC5p4FJLnwKfPDbJUuUsqClAxqw7nAsmXVKtn4zQFf5yPQ==
"@spectrum-css/icon@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/icon/-/icon-3.0.3.tgz#5c612822380927087aebd526855d82ed2c3e2cba"
integrity sha512-hyloKOejPCXhP3MBNsm3SjR9j8xT1R1S19p32KW/0Qhj+VMUtfyEPmevyFptpn7wcovQccdl/vZVIVDuML/imw==
"@spectrum-css/illustratedmessage@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/illustratedmessage/-/illustratedmessage-3.0.2.tgz#6a480be98b027e050b086e7899e40d87adb0a8c0"
integrity sha512-dqnE8X27bGcO0HN8+dYx8O4o0dNNIAqeivOzDHhe2El+V4dTzMrNIerF6G0NLm3GjVf6XliwmitsZK+K6FmbtA==
"@spectrum-css/inputgroup@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/inputgroup/-/inputgroup-3.0.3.tgz#00c9a370ddc2c55cf0f37dd6069faa9501fd7eb5"
integrity sha512-FqRJTiLL7jiGfzDVXWUGVLqHryJjCcqQIrqAi+Tq0oenapzsBe2qc/zIrKgh2wtMI+NTIBLXTECvij3L1HlqAg==
"@spectrum-css/label@^2.0.10":
version "2.0.10"
resolved "https://registry.yarnpkg.com/@spectrum-css/label/-/label-2.0.10.tgz#2368651d7636a19385b5d300cdf6272db1916001"
integrity sha512-xCbtEiQkZIlLdWFikuw7ifDCC21DOC/KMgVrrVJHXFc4KRQe9LTZSqmGF3tovm+CSq1adE59mYoTbojVQ9YuEQ==
"@spectrum-css/link@^3.1.1":
version "3.1.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/link/-/link-3.1.3.tgz#b0e560a7e0acdb4a2b329b6669696aa3438f5993"
integrity sha512-8Pmy5d73MwKcATTPaj+fSrZYnIw7GmfX40AvpC1xx5LauOxsbUb4AVNp1kn2H8rrOBmxF7czyhoBBhEiv66QUg==
"@spectrum-css/menu@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/menu/-/menu-3.0.3.tgz#46a9b221bb5f470a2f8a934bdfd512d84d2fdc4d"
integrity sha512-qKA9J/MrikNKIpCEHsAazG2vY3im5tjGCmo6p9Pdnu8/aQMsiuZDHZayukeCttJUZkrb9guDVL9OIHlK5RZvcQ==
"@spectrum-css/modal@^3.0.1":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/modal/-/modal-3.0.2.tgz#58b6621cab65f90788d310374f40df1f7090473f"
integrity sha512-YnIivJhoaao7Otu+HV7sgebPyFbO6sd/oMvTN/Rb2wwgnaMnIIuIRdGandSrcgotN2uNgs+P0knG6mv/xA1/dg==
"@spectrum-css/page@^3.0.1": "@spectrum-css/page@^3.0.1":
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/@spectrum-css/page/-/page-3.0.1.tgz#5e1c3dd5b1a1ee591f9d636b75f03665f542d846" resolved "https://registry.yarnpkg.com/@spectrum-css/page/-/page-3.0.1.tgz#5e1c3dd5b1a1ee591f9d636b75f03665f542d846"
@ -17,11 +162,111 @@
dependencies: dependencies:
"@spectrum-css/vars" "^3.0.1" "@spectrum-css/vars" "^3.0.1"
"@spectrum-css/pagination@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/pagination/-/pagination-3.0.3.tgz#b204c3ada384c4af751a354bc428346d82eeea65"
integrity sha512-OJ/v9GeNXJOZ9Yr9LDBYPrR2NCiLOWP9wANT/a5sqFuugRnQbn/HYMnRp9TBxwpDY6ihaPo0T/wi7kLiAJFdDw==
"@spectrum-css/picker@^1.0.1":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/picker/-/picker-1.0.3.tgz#21379bcf8ae94277deeb6ad65dcd9e2bbfacb487"
integrity sha512-oHLGxBx5BwVCSGo7/T1C9PTHX1+/5AmVjyLiTJ4UoIdSJmOERw9YcRZbcGZgBJNWbxcjr4TyGtwj1EcSjEy97w==
"@spectrum-css/popover@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/popover/-/popover-3.0.3.tgz#6fb69873474fb968afb738eacb9e121f93e83a09"
integrity sha512-KvmXv4TV19FBx39KfmgVlDYtvtBqv/8RRK7RRLDDHGViTxZtShjVsVpwIgfkfgn4iJztCnXpWzFqRXWUu2XCpQ==
"@spectrum-css/progressbar@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/progressbar/-/progressbar-1.0.3.tgz#f70bcc38a2a21cff2f422ec825724ebbb9455e67"
integrity sha512-vJHplefUuy8+NjCw1X7fLbqHVGNVBpvGFXNAeaIj4SFf4ygxiUq/5c9iRhhsCQixEsJlfD/b7BnGXU7BUDkr6Q==
"@spectrum-css/progresscircle@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/progresscircle/-/progresscircle-1.0.2.tgz#258ea9170fb70f795edda03e38a61d93bef4487c"
integrity sha512-JLULpyzjIY95lzlWR1yE1gv4l1K6p+scQ+edmuZZUHBzwM3pUtkvHJmUlA9TYdResUYW6Uka60VRdY6lZ8gnFQ==
"@spectrum-css/radio@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/radio/-/radio-3.0.3.tgz#25c3bc5e9c30a8a8ae728717b7c7fb736cdae640"
integrity sha512-LaLGfz/eGNR2iyqouXYILIA+pKRqF769iPdwM0REm5RpWvMQDD7rPZ/kWlg18owjaFsyllEp25gEjmhRJIIVOw==
"@spectrum-css/search@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/search/-/search-3.0.3.tgz#3415dc106aca0d5dd996e87084a1b47c2b95a882"
integrity sha512-kdLpKTt0obljuhS1q1tukujRlvSs8sBwij76D4Qp8KpMzwePfZyvv1kYzuWPNZfTeISxWsmyZ6Wxd1uvzjn+UA==
"@spectrum-css/sidenav@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/sidenav/-/sidenav-3.0.3.tgz#132141fbd2500a927c312fa4e1d712c438b3d597"
integrity sha512-cQ+CgwjxGftrcc79i1XnGd66QTl7H7zopSU0UTV4Qq7hvqfrjjWxfZ6b+3tezrrhNlDope1ff9o8sm67PsPXtg==
"@spectrum-css/statuslight@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/statuslight/-/statuslight-3.0.2.tgz#dc54b6cd113413dcdb909c486b5d7bae60db65c5"
integrity sha512-xodB8g8vGJH20XmUj9ZsPlM1jHrGeRbvmVXkz0q7YvQrYAhim8pP3W+XKKZAletPFAuu8cmUOc6SWn6i4X4z6w==
"@spectrum-css/switch@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/switch/-/switch-1.0.2.tgz#f0b4c69271964573e02b08e90998096e49e1de44"
integrity sha512-zqmHpgWPNg1gEwdUNFYV3CBX5JaeALfIqcJIxE0FLZqr9d1C4+oLE0ItIFzt1bwr4bFAOmkEpvtiY+amluzGxQ==
"@spectrum-css/table@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/table/-/table-3.0.3.tgz#7f7f19905ef3275cbf907ce3a5818e63c30b2caf"
integrity sha512-nxwzVjLPsXoY/v4sdxOVYLcC+cEbGgJyLcLclT5LT9MGSbngFeUMJzzVR4EvehzuN4dH7hrATG7Mbuq29Mf0Hg==
"@spectrum-css/tabs@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/tabs/-/tabs-3.0.3.tgz#51dd6f168c897b0fdc3a7e9f901df7bd2288b4fc"
integrity sha512-iLP1I72bJWz9APdQB1jiw+pOv5a7N+hYOCJvRoc56Us/hJKVzowkyGRe3uH+8v36nCG9bHxiAQNLoU8eXisVrg==
"@spectrum-css/tags@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/tags/-/tags-3.0.3.tgz#fc76d2735cdc442de91b7eb3bee49a928c0767ac"
integrity sha512-SL8vPxVDfWcY5VdIuyl0TImEXcOU1I7yCyXkk7MudMwfnYs81FaIyY32hFV9OHj0Tz/36UzRzc7AVMSuRQ53pw==
"@spectrum-css/textfield@^3.0.1":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/textfield/-/textfield-3.0.2.tgz#907f62d2dc82852dd6236a820be99e252b531631"
integrity sha512-nkFgAb0cP4jUodkUBErMNfyF78jJLtgL1Mrr9/rvGpGobo10IAbb8zZY4CkZ64o8XmMy/85+wZTKcx+KHatqpg==
"@spectrum-css/toast@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/toast/-/toast-3.0.3.tgz#97c1527384707600832ecda35643ed304615250f"
integrity sha512-CjLeaMs+cjUXojCCRtbj0YkD2BoZW16kjj2o5omkEpUTjA34IJ8xJ1a+CCtDILWekhXvN0MBN4sbumcnwcnx8w==
"@spectrum-css/tooltip@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/tooltip/-/tooltip-3.0.3.tgz#26b8ca3b3d30e29630244d85eb4fc11d0c841281"
integrity sha512-ztRF7WW1FzyNavXBRc+80z67UoOrY9wl3cMYsVD3MpDnyxdzP8cjza1pCcolKBaFqRTcQKkxKw3GWtGICRKR5A==
"@spectrum-css/treeview@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/treeview/-/treeview-3.0.3.tgz#aeda5175158b9f8d7529cb2b394428eb2a428046"
integrity sha512-D5gGzZC/KtRArdx86Mesc9+99W9nTbUOeyYGqoJoAfJSOttoT6Tk5CrDvlCmAqjKf5rajemAkGri1ChqvUIwkw==
"@spectrum-css/typography@^3.0.1":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/typography/-/typography-3.0.2.tgz#ea3ca0a60e18064527819d48c8c4364cab4fcd38"
integrity sha512-5ZOLmQe0edzsDMyhghUd4hBb5uxGsFrxzf+WasfcUw9klSfTsRZ09n1BsaaWbgrLjlMQ+EEHS46v5VNo0Ms2CA==
"@spectrum-css/underlay@^2.0.9":
version "2.0.10"
resolved "https://registry.yarnpkg.com/@spectrum-css/underlay/-/underlay-2.0.10.tgz#8b75b646605a311850f6620caa18d4996cd64ed7"
integrity sha512-PmsmkzeGD/rY4pp3ILXHt9w8BW7uaEqXe08hQRS7rGki7wqCpG4mE0/8N3yEcA3QxWQclmG9gdkg5uz6wMmYzA==
"@spectrum-css/vars@^3.0.1": "@spectrum-css/vars@^3.0.1":
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.1.tgz#561fd69098f896a647242dd8d6108af603bfa31e" resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.1.tgz#561fd69098f896a647242dd8d6108af603bfa31e"
integrity sha512-l4oRcCOqInChYXZN6OQhpe3isk6l4OE6Ys8cgdlsiKp53suNoQxyyd9p/eGRbCjZgH3xQ8nK0t4DHa7QYC0S6w== integrity sha512-l4oRcCOqInChYXZN6OQhpe3isk6l4OE6Ys8cgdlsiKp53suNoQxyyd9p/eGRbCjZgH3xQ8nK0t4DHa7QYC0S6w==
"@spectrum-css/vars@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.2.tgz#ea9062c3c98dfc6ba59e5df14a03025ad8969999"
integrity sha512-vzS9KqYXot4J3AEER/u618MXWAS+IoMvYMNrOoscKiLLKYQWenaueakUWulFonToPd/9vIpqtdbwxznqrK5qDw==
"@sveltejs/vite-plugin-svelte@^1.0.0-next.5": "@sveltejs/vite-plugin-svelte@^1.0.0-next.5":
version "1.0.0-next.5" version "1.0.0-next.5"
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.5.tgz#8cf608f7a3c33dfa5b648397aae1ba90e6a4883f" resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.5.tgz#8cf608f7a3c33dfa5b648397aae1ba90e6a4883f"
@ -80,6 +325,11 @@ colorette@^1.2.2:
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
dayjs@^1.10.4, dayjs@^1.10.5:
version "1.10.5"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.5.tgz#5600df4548fc2453b3f163ebb2abbe965ccfb986"
integrity sha512-BUFis41ikLz+65iH6LHQCDm4YPMj5r1YFLdupPIyM4SGcXMmtiLQ7U37i+hGS8urIuqe7I/ou3IS1jVc4nbN4g==
debug@^4.3.2: debug@^4.3.2:
version "4.3.2" version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
@ -226,6 +476,11 @@ svelte-hmr@^0.13.3:
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.13.3.tgz#fba5739b477ea44caf70e542a24a4352bee2b897" resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.13.3.tgz#fba5739b477ea44caf70e542a24a4352bee2b897"
integrity sha512-gagW62pLQ2lULmvNA3pIZu9pBCYOaGu3rQikUOv6Nokz5VxUgT9/mQLfMxj9phDEKHCg/lgr3i6PkqZDbO9P2Q== integrity sha512-gagW62pLQ2lULmvNA3pIZu9pBCYOaGu3rQikUOv6Nokz5VxUgT9/mQLfMxj9phDEKHCg/lgr3i6PkqZDbO9P2Q==
svelte-portal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/svelte-portal/-/svelte-portal-1.0.0.tgz#36a47c5578b1a4d9b4dc60fa32a904640ec4cdd3"
integrity sha512-nHf+DS/jZ6jjnZSleBMSaZua9JlG5rZv9lOGKgJuaZStfevtjIlUJrkLc3vbV8QdBvPPVmvcjTlazAzfKu0v3Q==
svelte@^3.38.2: svelte@^3.38.2:
version "3.38.2" version "3.38.2"
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.38.2.tgz#55e5c681f793ae349b5cc2fe58e5782af4275ef5" resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.38.2.tgz#55e5c681f793ae349b5cc2fe58e5782af4275ef5"

2
packages/string-templates/package.json

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "0.9.21", "version": "0.9.27",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs", "main": "src/index.cjs",
"module": "dist/bundle.mjs", "module": "dist/bundle.mjs",

4
packages/string-templates/src/index.cjs

@ -33,7 +33,7 @@ function testObject(object) {
*/ */
module.exports.processObject = async (object, context) => { module.exports.processObject = async (object, context) => {
testObject(object) testObject(object)
for (let key of Object.keys(object)) { for (let key of Object.keys(object || {})) {
if (object[key] != null) { if (object[key] != null) {
let val = object[key] let val = object[key]
if (typeof val === "string") { if (typeof val === "string") {
@ -68,7 +68,7 @@ module.exports.processString = async (string, context) => {
*/ */
module.exports.processObjectSync = (object, context) => { module.exports.processObjectSync = (object, context) => {
testObject(object) testObject(object)
for (let key of Object.keys(object)) { for (let key of Object.keys(object || {})) {
let val = object[key] let val = object[key]
if (typeof val === "string") { if (typeof val === "string") {
object[key] = module.exports.processStringSync(object[key], context) object[key] = module.exports.processStringSync(object[key], context)

6
packages/worker/package.json

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.9.21", "version": "0.9.27",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.js", "main": "src/index.js",
"repository": { "repository": {
@ -21,8 +21,8 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.21", "@budibase/auth": "^0.9.27",
"@budibase/string-templates": "^0.9.21", "@budibase/string-templates": "^0.9.27",
"@koa/router": "^8.0.0", "@koa/router": "^8.0.0",
"aws-sdk": "^2.811.0", "aws-sdk": "^2.811.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",

3
packages/worker/src/api/index.js

@ -2,7 +2,7 @@ const Router = require("@koa/router")
const compress = require("koa-compress") const compress = require("koa-compress")
const zlib = require("zlib") const zlib = require("zlib")
const { routes } = require("./routes") const { routes } = require("./routes")
const { buildAuthMiddleware } = require("@budibase/auth").auth const { buildAuthMiddleware, auditLog } = require("@budibase/auth").auth
const PUBLIC_ENDPOINTS = [ const PUBLIC_ENDPOINTS = [
{ {
@ -62,6 +62,7 @@ router
} }
return next() return next()
}) })
.use(auditLog)
// error handling middleware // error handling middleware
router.use(async (ctx, next) => { router.use(async (ctx, next) => {

8
packages/worker/src/utilities/redis.js

@ -51,12 +51,8 @@ exports.init = async () => {
* make sure redis connection is closed. * make sure redis connection is closed.
*/ */
exports.shutdown = async () => { exports.shutdown = async () => {
if (pwResetClient != null) { if (pwResetClient) await pwResetClient.finish()
await pwResetClient.finish() if (invitationClient) await invitationClient.finish()
}
if (invitationClient != null) {
await invitationClient.finish()
}
} }
/** /**

97
packages/worker/yarn.lock

@ -910,7 +910,7 @@ array-unique@^0.3.2:
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
asn1@~0.2.3: asn1@~0.2.0, asn1@~0.2.3:
version "0.2.4" version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
@ -1076,7 +1076,7 @@ base@^0.11.1:
mixin-deep "^1.2.0" mixin-deep "^1.2.0"
pascalcase "^0.1.1" pascalcase "^0.1.1"
bcrypt-pbkdf@^1.0.0: bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
@ -1093,6 +1093,15 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
bl@^4.0.3:
version "4.1.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
dependencies:
buffer "^5.5.0"
inherits "^2.0.4"
readable-stream "^3.4.0"
boxen@^4.2.0: boxen@^4.2.0:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64"
@ -1337,6 +1346,11 @@ chokidar@^3.2.2:
optionalDependencies: optionalDependencies:
fsevents "~2.3.1" fsevents "~2.3.1"
chownr@^1.1.1:
version "1.1.4"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
ci-info@^2.0.0: ci-info@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
@ -1773,6 +1787,24 @@ diff-sequences@^26.6.2:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1"
integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==
docker-modem@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-3.0.0.tgz#cb912ad8daed42f858269fb3be6944df281ec12d"
integrity sha512-WwFajJ8I5geZ/dDZ5FDMDA6TBkWa76xWwGIGw8uzUjNUGCN0to83wJ8Oi1AxrJTC0JBn+7fvIxUctnawtlwXeg==
dependencies:
debug "^4.1.1"
readable-stream "^3.5.0"
split-ca "^1.0.1"
ssh2 "^0.8.7"
dockerode@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-3.3.0.tgz#bedaf48ef9fa9124275a54a9881a92374c51008e"
integrity sha512-St08lfOjpYCOXEM8XA0VLu3B3hRjtddODphNW5GFoA0AS3JHgoPQKOz0Qmdzg3P+hUPxhb02g1o1Cu1G+U3lRg==
dependencies:
docker-modem "^3.0.0"
tar-fs "~2.0.1"
domexception@^2.0.1: domexception@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304"
@ -1857,7 +1889,7 @@ encoding-down@^6.3.0:
level-codec "^9.0.0" level-codec "^9.0.0"
level-errors "^2.0.0" level-errors "^2.0.0"
end-of-stream@^1.1.0: end-of-stream@^1.1.0, end-of-stream@^1.4.1:
version "1.4.4" version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
@ -2238,6 +2270,11 @@ fresh@~0.5.2:
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
fs.realpath@^1.0.0: fs.realpath@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@ -3998,6 +4035,11 @@ mixin-deep@^1.2.0:
for-in "^1.0.2" for-in "^1.0.2"
is-extendable "^1.0.1" is-extendable "^1.0.1"
mkdirp-classic@^0.5.2:
version "0.5.3"
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
mkdirp@^0.5.0: mkdirp@^0.5.0:
version "0.5.5" version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
@ -4829,7 +4871,7 @@ readable-stream@1.1.14:
isarray "0.0.1" isarray "0.0.1"
string_decoder "~0.10.x" string_decoder "~0.10.x"
"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.4.0, readable-stream@^3.6.0: "readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0:
version "3.6.0" version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@ -5326,6 +5368,11 @@ spdx-license-ids@^3.0.0:
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65"
integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==
split-ca@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6"
integrity sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=
split-string@^3.0.1, split-string@^3.0.2: split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
@ -5345,6 +5392,22 @@ sprintf-js@~1.0.2:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
ssh2-streams@~0.4.10:
version "0.4.10"
resolved "https://registry.yarnpkg.com/ssh2-streams/-/ssh2-streams-0.4.10.tgz#48ef7e8a0e39d8f2921c30521d56dacb31d23a34"
integrity sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==
dependencies:
asn1 "~0.2.0"
bcrypt-pbkdf "^1.0.2"
streamsearch "~0.1.2"
ssh2@^0.8.7:
version "0.8.9"
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-0.8.9.tgz#54da3a6c4ba3daf0d8477a538a481326091815f3"
integrity sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw==
dependencies:
ssh2-streams "~0.4.10"
sshpk@^1.7.0: sshpk@^1.7.0:
version "1.16.1" version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
@ -5390,6 +5453,11 @@ stealthy-require@^1.1.1:
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
streamsearch@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a"
integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=
string-length@^4.0.1: string-length@^4.0.1:
version "4.0.2" version "4.0.2"
resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
@ -5529,6 +5597,27 @@ symbol-tree@^3.2.4:
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
tar-fs@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2"
integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==
dependencies:
chownr "^1.1.1"
mkdirp-classic "^0.5.2"
pump "^3.0.0"
tar-stream "^2.0.0"
tar-stream@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
dependencies:
bl "^4.0.3"
end-of-stream "^1.4.1"
fs-constants "^1.0.0"
inherits "^2.0.3"
readable-stream "^3.1.1"
term-size@^2.1.0: term-size@^2.1.0:
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"

Loading…
Cancel
Save