1
0
Fork 0
mirror of synced 2024-07-03 05:20:32 +12:00

Merge branch 'cheeks-lab-day-eject-blocks' of github.com:Budibase/budibase into form-block

This commit is contained in:
Andrew Kingston 2022-09-13 11:32:04 +01:00
commit fcacd0e408
195 changed files with 7711 additions and 5426 deletions

4
.gitignore vendored
View file

@ -102,4 +102,6 @@ packages/builder/cypress/reports
stats.html
# TypeScript cache
*.tsbuildinfo
*.tsbuildinfo
budibase-component
budibase-datasource

View file

@ -124,11 +124,15 @@ spec:
value: {{ .Values.globals.tenantFeatureFlags | quote }}
{{ if .Values.globals.bbAdminUserEmail }}
- name: BB_ADMIN_USER_EMAIL
value: { { .Values.globals.bbAdminUserEmail | quote } }
value: {{ .Values.globals.bbAdminUserEmail | quote }}
{{ end }}
{{ if .Values.globals.bbAdminUserPassword }}
- name: BB_ADMIN_USER_PASSWORD
value: { { .Values.globals.bbAdminUserPassword | quote } }
value: {{ .Values.globals.bbAdminUserPassword | quote }}
{{ end }}
{{ if .Values.globals.pluginsDir }}
- name: PLUGINS_DIR
value: {{ .Values.globals.pluginsDir | quote }}
{{ end }}
{{ if .Values.services.apps.nodeDebug }}
- name: NODE_DEBUG

View file

@ -11,8 +11,8 @@
"dependencies": {
"bulma": "^0.9.3",
"next": "12.1.0",
"node-fetch": "^3.2.2",
"node-sass": "^7.0.1",
"node-fetch": "^3.2.10",
"sass": "^1.52.3",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-notifications-component": "^3.4.1"
@ -24,4 +24,4 @@
"eslint-config-next": "12.1.0",
"typescript": "4.6.2"
}
}
}

View file

@ -2020,10 +2020,10 @@ node-domexception@^1.0.0:
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
node-fetch@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.2.tgz#16d33fbe32ca7c6ca1ca8ba5dfea1dd885c59f04"
integrity sha512-Cwhq1JFIoon15wcIkFzubVNFE5GvXGV82pKf4knXXjvGmn7RJKcypeuqcVNZMGDZsAFWyIRya/anwAJr7TWJ7w==
node-fetch@^3.2.10:
version "3.2.10"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.10.tgz#e8347f94b54ae18b57c9c049ef641cef398a85c8"
integrity sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==
dependencies:
data-uri-to-buffer "^4.0.0"
fetch-blob "^3.1.4"

View file

@ -22,4 +22,7 @@ BUDIBASE_ENVIRONMENT=PRODUCTION
# An admin user can be automatically created initially if these are set
BB_ADMIN_USER_EMAIL=
BB_ADMIN_USER_PASSWORD=
BB_ADMIN_USER_PASSWORD=
# A path that is watched for plugin bundles. Any bundles found are imported automatically/
PLUGINS_DIR=

View file

@ -25,9 +25,12 @@ services:
REDIS_PASSWORD: ${REDIS_PASSWORD}
BB_ADMIN_USER_EMAIL: ${BB_ADMIN_USER_EMAIL}
BB_ADMIN_USER_PASSWORD: ${BB_ADMIN_USER_PASSWORD}
PLUGINS_DIR: ${PLUGINS_DIR}
depends_on:
- worker-service
- redis-service
# volumes:
# - /some/path/to/plugins:/plugins
worker-service:
restart: unless-stopped

View file

@ -22,4 +22,7 @@ BUDIBASE_ENVIRONMENT=PRODUCTION
# An admin user can be automatically created initially if these are set
BB_ADMIN_USER_EMAIL=
BB_ADMIN_USER_PASSWORD=
BB_ADMIN_USER_PASSWORD=
# A path that is watched for plugin bundles. Any bundles found are imported automatically/
PLUGINS_DIR=

View file

@ -65,10 +65,6 @@ http {
proxy_pass http://{{ address }}:4001;
}
location /preview {
proxy_pass http://{{ address }}:4001;
}
location /builder {
proxy_pass http://{{ address }}:3000;
rewrite ^/builder(.*)$ /builder/$1 break;
@ -84,6 +80,20 @@ http {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /vite/ {
proxy_pass http://{{ address }}:3000;
rewrite ^/vite(.*)$ /$1 break;
}
location /socket/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_pass http://{{ address }}:4001;
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

View file

@ -88,10 +88,6 @@ http {
proxy_pass http://$apps:4002;
}
location /preview {
proxy_pass http://$apps:4002;
}
location = / {
proxy_pass http://$apps:4002;
}
@ -162,6 +158,15 @@ http {
rewrite ^/db/(.*)$ /$1 break;
}
location /socket/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_pass http://$apps:4002;
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

View file

@ -4,9 +4,9 @@ echo ${TARGETBUILD} > /buildtarget.txt
if [[ "${TARGETBUILD}" = "aas" ]]; then
# Azure AppService uses /home for persisent data & SSH on port 2222
DATA_DIR=/home
mkdir -p $DATA_DIR/{search,minio,couchdb}
mkdir -p $DATA_DIR/couchdb/{dbs,views}
chown -R couchdb:couchdb $DATA_DIR/couchdb/
mkdir -p $DATA_DIR/{search,minio,couch}
mkdir -p $DATA_DIR/couch/{dbs,views}
chown -R couchdb:couchdb $DATA_DIR/couch/
apt update
apt-get install -y openssh-server
sed -i "s/#Port 22/Port 2222/" /etc/ssh/sshd_config
@ -16,5 +16,4 @@ if [[ "${TARGETBUILD}" = "aas" ]]; then
else
sed -i "s#DATA_DIR#/data#g" /opt/clouseau/clouseau.ini
sed -i "s#DATA_DIR#/data#g" /opt/couchdb/etc/local.ini
fi

View file

@ -1,5 +1,5 @@
; CouchDB Configuration Settings
[couchdb]
database_dir = DATA_DIR/couchdb/dbs
view_index_dir = DATA_DIR/couchdb/views
database_dir = DATA_DIR/couch/dbs
view_index_dir = DATA_DIR/couch/views

View file

@ -66,6 +66,15 @@ server {
rewrite ^/db/(.*)$ /$1 break;
}
location /socket/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_pass http://127.0.0.1:4001;
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

View file

@ -36,10 +36,10 @@ fi
export COUCH_DB_URL=http://$COUCHDB_USER:$COUCHDB_PASSWORD@localhost:5984
# make these directories in runner, incase of mount
mkdir -p ${DATA_DIR}/couchdb/{dbs,views}
mkdir -p ${DATA_DIR}/couch/{dbs,views}
mkdir -p ${DATA_DIR}/minio
mkdir -p ${DATA_DIR}/search
chown -R couchdb:couchdb ${DATA_DIR}/couchdb
chown -R couchdb:couchdb ${DATA_DIR}/couch
redis-server --requirepass $REDIS_PASSWORD &
/opt/clouseau/bin/clouseau &
/minio/minio server ${DATA_DIR}/minio &

View file

@ -1,5 +1,5 @@
{
"version": "1.3.4-alpha.2",
"version": "1.3.15-alpha.3",
"npmClient": "yarn",
"packages": [
"packages/*"

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/backend-core",
"version": "1.3.4-alpha.2",
"version": "1.3.15-alpha.3",
"description": "Budibase backend core libraries used in server and worker",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
@ -20,7 +20,8 @@
"test:watch": "jest --watchAll"
},
"dependencies": {
"@budibase/types": "1.3.4-alpha.2",
"@budibase/types": "1.3.15-alpha.3",
"@shopify/jest-koa-mocks": "5.0.1",
"@techpass/passport-openidconnect": "0.3.2",
"aws-sdk": "2.1030.0",
"bcrypt": "5.0.1",
@ -60,7 +61,6 @@
]
},
"devDependencies": {
"@shopify/jest-koa-mocks": "3.1.5",
"@types/jest": "27.5.1",
"@types/koa": "2.0.52",
"@types/lodash": "4.14.180",

View file

@ -0,0 +1,3 @@
module.exports = {
...require("./src/plugin"),
}

View file

@ -1,4 +1,5 @@
import { dangerousGetDB, closeDB } from "."
import { DocumentType } from "./constants"
class Replication {
source: any
@ -53,6 +54,14 @@ class Replication {
return this.replication
}
appReplicateOpts() {
return {
filter: (doc: any) => {
return doc._id !== DocumentType.APP_METADATA
},
}
}
/**
* Rollback the target DB back to the state of the source DB
*/
@ -60,6 +69,7 @@ class Replication {
await this.target.destroy()
// Recreate the DB again
this.target = dangerousGetDB(this.target.name)
// take the opportunity to remove deleted tombstones
await this.replicate()
}

View file

@ -254,7 +254,16 @@ export async function getAllApps({ dev, all, idsOnly, efficient }: any = {}) {
return false
})
if (idsOnly) {
return appDbNames
const devAppIds = appDbNames.filter(appId => isDevAppID(appId))
const prodAppIds = appDbNames.filter(appId => !isDevAppID(appId))
switch (dev) {
case true:
return devAppIds
case false:
return prodAppIds
default:
return appDbNames
}
}
const appPromises = appDbNames.map((app: any) =>
// skip setup otherwise databases could be re-created

View file

@ -50,6 +50,7 @@ const env = {
GLOBAL_BUCKET_NAME: process.env.GLOBAL_BUCKET_NAME || "global",
GLOBAL_CLOUD_BUCKET_NAME:
process.env.GLOBAL_CLOUD_BUCKET_NAME || "prod-budi-tenant-uploads",
PLUGIN_BUCKET_NAME: process.env.PLUGIN_BUCKET_NAME || "plugins",
USE_COUCH: process.env.USE_COUCH || true,
DISABLE_DEVELOPER_LICENSE: process.env.DISABLE_DEVELOPER_LICENSE,
DEFAULT_LICENSE: process.env.DEFAULT_LICENSE,

View file

@ -18,6 +18,7 @@ import * as dbConstants from "./db/constants"
import logging from "./logging"
import pino from "./pino"
import * as middleware from "./middleware"
import plugins from "./plugin"
// mimic the outer package exports
import * as db from "./pkg/db"
@ -56,6 +57,7 @@ const core = {
errors,
logging,
roles,
plugins,
...pino,
...errorClasses,
middleware,

View file

@ -57,7 +57,11 @@ function publicPolicy(bucketName: any) {
}
}
const PUBLIC_BUCKETS = [ObjectStoreBuckets.APPS, ObjectStoreBuckets.GLOBAL]
const PUBLIC_BUCKETS = [
ObjectStoreBuckets.APPS,
ObjectStoreBuckets.GLOBAL,
ObjectStoreBuckets.PLUGINS,
]
/**
* Gets a connection to the object store using the S3 SDK.
@ -172,6 +176,14 @@ export const streamUpload = async (
const objectStore = ObjectStore(bucketName)
await makeSureBucketExists(objectStore, bucketName)
// Set content type for certain known extensions
if (filename?.endsWith(".js")) {
extra = {
...extra,
ContentType: "application/javascript",
}
}
const params = {
Bucket: sanitizeBucket(bucketName),
Key: sanitizeKey(filename),
@ -295,9 +307,13 @@ export const uploadDirectory = async (
return files
}
exports.downloadTarballDirect = async (url: string, path: string) => {
exports.downloadTarballDirect = async (
url: string,
path: string,
headers = {}
) => {
path = sanitizeKey(path)
const response = await fetch(url)
const response = await fetch(url, { headers })
if (!response.ok) {
throw new Error(`unexpected response ${response.statusText}`)
}

View file

@ -8,6 +8,7 @@ exports.ObjectStoreBuckets = {
TEMPLATES: env.TEMPLATES_BUCKET_NAME,
GLOBAL: env.GLOBAL_BUCKET_NAME,
GLOBAL_CLOUD: env.GLOBAL_CLOUD_BUCKET_NAME,
PLUGINS: env.PLUGIN_BUCKET_NAME,
}
exports.budibaseTempDir = function () {

View file

@ -0,0 +1,7 @@
import * as utils from "./utils"
const pkg = {
...utils,
}
export = pkg

View file

@ -0,0 +1,94 @@
const {
DatasourceFieldType,
QueryType,
PluginType,
} = require("@budibase/types")
const joi = require("joi")
const DATASOURCE_TYPES = [
"Relational",
"Non-relational",
"Spreadsheet",
"Object store",
"Graph",
"API",
]
function runJoi(validator, schema) {
const { error } = validator.validate(schema)
if (error) {
throw error
}
}
function validateComponent(schema) {
const validator = joi.object({
type: joi.string().allow("component").required(),
metadata: joi.object().unknown(true).required(),
hash: joi.string().optional(),
version: joi.string().optional(),
schema: joi
.object({
name: joi.string().required(),
settings: joi.array().items(joi.object().unknown(true)).required(),
})
.unknown(true),
})
runJoi(validator, schema)
}
function validateDatasource(schema) {
const fieldValidator = joi.object({
type: joi
.string()
.allow(...Object.values(DatasourceFieldType))
.required(),
required: joi.boolean().required(),
default: joi.any(),
display: joi.string(),
})
const queryValidator = joi
.object({
type: joi.string().allow(...Object.values(QueryType)),
fields: joi.object().pattern(joi.string(), fieldValidator),
})
.required()
const validator = joi.object({
type: joi.string().allow("datasource").required(),
metadata: joi.object().unknown(true).required(),
hash: joi.string().optional(),
version: joi.string().optional(),
schema: joi.object({
docs: joi.string(),
friendlyName: joi.string().required(),
type: joi.string().allow(...DATASOURCE_TYPES),
description: joi.string().required(),
datasource: joi.object().pattern(joi.string(), fieldValidator).required(),
query: joi
.object({
create: queryValidator,
read: queryValidator,
update: queryValidator,
delete: queryValidator,
})
.unknown(true)
.required(),
}),
})
runJoi(validator, schema)
}
exports.validate = schema => {
switch (schema?.type) {
case PluginType.COMPONENT:
validateComponent(schema)
break
case PluginType.DATASOURCE:
validateDatasource(schema)
break
default:
throw new Error(`Unknown plugin type - check schema.json: ${schema.type}`)
}
}

View file

@ -543,13 +543,13 @@
semver "^7.3.5"
tar "^6.1.11"
"@shopify/jest-koa-mocks@3.1.5":
version "3.1.5"
resolved "https://registry.yarnpkg.com/@shopify/jest-koa-mocks/-/jest-koa-mocks-3.1.5.tgz#11f77ccfbcaf35cf5ee2c6108a286e61e6bea084"
integrity sha512-gQ3/7ELerv00TWO37AGFX5mT9CsFCS+3/UbKMuoIlKEU0QH2OX8BV9WBf/EKw7adCDNlxss0lqV6J8kf5pgr4A==
"@shopify/jest-koa-mocks@5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@shopify/jest-koa-mocks/-/jest-koa-mocks-5.0.1.tgz#fba490b6b7985fbb571eb9974897d396a3642e94"
integrity sha512-4YskS9q8+TEHNoyopmuoy2XyhInyqeOl7CF5ShJs19sm6m0EA/jGGvgf/osv2PeTfuf42/L2G9CzWUSg49yTSg==
dependencies:
koa "^2.13.4"
node-mocks-http "^1.5.8"
node-mocks-http "^1.11.0"
"@sideway/address@^4.1.3":
version "4.1.4"
@ -3914,7 +3914,7 @@ node-int64@^0.4.0:
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
node-mocks-http@^1.5.8:
node-mocks-http@^1.11.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/node-mocks-http/-/node-mocks-http-1.11.0.tgz#defc0febf6b935f08245397d47534a8de592996e"
integrity sha512-jS/WzSOcKbOeGrcgKbenZeNhxUNnP36Yw11+hL4TTxQXErGfqYZ+MaYNNvhaTiGIJlzNSqgQkk9j8dSu1YWSuw==

View file

@ -1,7 +1,7 @@
{
"name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.",
"version": "1.3.4-alpha.2",
"version": "1.3.15-alpha.3",
"license": "MPL-2.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",
@ -38,7 +38,7 @@
],
"dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
"@budibase/string-templates": "1.3.4-alpha.2",
"@budibase/string-templates": "1.3.15-alpha.3",
"@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2",

View file

@ -4,10 +4,15 @@
export let size = "M"
export let tooltip = ""
export let muted
</script>
<TooltipWrapper {tooltip} {size}>
<label for="" class={`spectrum-FieldLabel spectrum-FieldLabel--size${size}`}>
<label
class:muted
for=""
class={`spectrum-FieldLabel spectrum-FieldLabel--size${size}`}
>
<slot />
</label>
</TooltipWrapper>
@ -17,4 +22,8 @@
padding: 0;
white-space: nowrap;
}
.muted {
opacity: 0.5;
}
</style>

View file

@ -24,7 +24,6 @@
export let secondaryAction = undefined
export let secondaryButtonWarning = false
export let dataCy = null
const { hide, cancel } = getContext(Context.Modal)
let loading = false
$: confirmDisabled = disabled || loading
@ -88,12 +87,11 @@
<section class="spectrum-Dialog-content content-grid">
<slot />
</section>
{#if showCancelButton || showConfirmButton}
{#if showCancelButton || showConfirmButton || $$slots.footer}
<div
class="spectrum-ButtonGroup spectrum-Dialog-buttonGroup spectrum-Dialog-buttonGroup--noFooter"
>
<slot name="footer" />
{#if showSecondaryButton && secondaryButtonText && secondaryAction}
<div class="secondary-action">
<Button

View file

@ -10,6 +10,7 @@
export let noHorizPadding = false
export let quiet = false
export let emphasized = false
export let onTop = false
export let size = "M"
let thisSelected = undefined
@ -75,6 +76,7 @@
bind:this={container}
class:spectrum-Tabs--quiet={quiet}
class:noHorizPadding
class:onTop
class:spectrum-Tabs--vertical={vertical}
class:spectrum-Tabs--horizontal={!vertical}
class="spectrum-Tabs spectrum-Tabs--size{size}"
@ -122,4 +124,7 @@
.noPadding {
margin: 0;
}
.onTop {
z-index: 100;
}
</style>

View file

@ -74,11 +74,11 @@ filterTests(["smoke", "all"], () => {
.contains("Update role")
.click({ force: true })
})
cy.reload({ timeout: 5000 })
cy.reload()
cy.wait(1000)
}
// Confirm roles exist within Configure roles table
cy.get(interact.SPECTRUM_TABLE, { timeout: 2000 })
cy.get(interact.SPECTRUM_TABLE, { timeout: 20000 })
.eq(0)
.within(assginedRoles => {
expect(assginedRoles).to.contain("Admin")
@ -180,7 +180,7 @@ filterTests(["smoke", "all"], () => {
cy.reload()
// Confirm details have been saved
cy.get(interact.FIELD, { timeout: 1000 }).eq(1).within(() => {
cy.get(interact.FIELD, { timeout: 20000 }).eq(1).within(() => {
cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).should('have.value', "bb")
})
cy.get(interact.FIELD, { timeout: 1000 }).eq(2).within(() => {

View file

@ -1,7 +1,7 @@
import filterTests from "../../support/filterTests"
filterTests(['all'], () => {
context("Datasource Wizard", () => {
xcontext("Datasource Wizard", () => {
if (Cypress.env("TEST_ENV")) {
before(() => {
cy.login()

View file

@ -1,7 +1,7 @@
import filterTests from "../../support/filterTests"
filterTests(["all"], () => {
context("Oracle Datasource Testing", () => {
xcontext("Oracle Datasource Testing", () => {
if (Cypress.env("TEST_ENV")) {
before(() => {
cy.login()

View file

@ -162,7 +162,7 @@ filterTests(["all"], () => {
switchSchema("randomText")
// No tables displayed
cy.get(".spectrum-Body", { timeout: 20000 }).eq(2).should("contain", "No tables found")
cy.get(".spectrum-Body", { timeout: 10000 }).eq(2, { timeout: 10000 }).should("contain", "No tables found")
// Previously created query should be visible
cy.get(".spectrum-Table").should("contain", queryName)

View file

@ -14,7 +14,7 @@ filterTests(["smoke", "all"], () => {
// Select REST data source
cy.selectExternalDatasource(datasource)
// Enter incorrect api & attempt to send query
cy.get(".spectrum-Button", { timeout: 500 }).contains("Add query").click({ force: true })
cy.get(".query-buttons", { timeout: 1000 }).contains("Add query").click({ force: true })
cy.intercept("**/preview").as("queryError")
cy.get("input").clear().type("random text")
cy.get(".spectrum-Button").contains("Send").click({ force: true })

View file

@ -457,8 +457,8 @@ Cypress.Commands.add("createTable", (tableName, initialTable) => {
cy.get(".spectrum-ButtonGroup").contains("Create").click()
})
// Ensure modal has closed and table is created
cy.get(".spectrum-Modal").should("not.exist")
cy.get(".spectrum-Tabs-content", { timeout: 1000 }).should(
cy.get(".spectrum-Modal", { timeout: 2000 }).should("not.exist")
cy.get(".spectrum-Tabs-content", { timeout: 2000 }).should(
"contain",
tableName
)
@ -637,30 +637,32 @@ Cypress.Commands.add(
(datasourceNames, accessLevelLabel) => {
cy.contains("Design").click()
cy.get(".spectrum-Button").contains("Add screen").click({ force: true })
cy.get(".spectrum-Modal").within(() => {
cy.get(".item").contains("Autogenerated screens").click()
cy.get(".spectrum-Dialog-grid").within(() => {
cy.get("[data-cy='autogenerated-screens']").click()
cy.intercept("**/api/datasources").as("autoScreens")
cy.get(".spectrum-Button").contains("Continue").click({ force: true })
cy.wait("@autoScreens")
cy.wait(5000)
})
cy.get(".spectrum-Modal [data-cy='data-source-modal']", {
timeout: 500,
}).within(() => {
cy.get("[data-cy='autogenerated-screens']").should("not.exist")
cy.get("[data-cy='data-source-modal']", { timeout: 10000 }).within(() => {
for (let i = 0; i < datasourceNames.length; i++) {
cy.wait(500)
cy.get(".data-source-entry").contains(datasourceNames[i]).click()
//Ensure the check mark is visible
cy.get(".data-source-entry")
.contains(datasourceNames[i], { timeout: 20000 })
.click({ force: true })
// Ensure the check mark is visible
cy.get(".data-source-entry")
.contains(datasourceNames[i])
.get(".data-source-check")
.get(".data-source-check", { timeout: 20000 })
.should("exist")
}
cy.get(".spectrum-Button").contains("Confirm").click({ force: true })
})
cy.get(".spectrum-Modal").within(() => {
cy.get(".spectrum-Modal", { timeout: 10000 }).within(() => {
if (accessLevelLabel) {
cy.get(".spectrum-Picker-label").click()
cy.wait(500)
cy.get(".spectrum-Picker-label", { timeout: 10000 }).click()
cy.contains(accessLevelLabel).click()
}
cy.get(".spectrum-Button").contains("Done").click({ force: true })
@ -915,8 +917,9 @@ Cypress.Commands.add("createRestQuery", (method, restUrl, queryPrettyName) => {
Cypress.Commands.add("closeModal", () => {
cy.get(".spectrum-Modal", { timeout: 2000 }).within(() => {
cy.get(".close-icon").click()
cy.wait(1000) // Wait for modal to close
})
// Confirm modal has closed
cy.get(".spectrum-Modal", { timeout: 10000 }).should("not.exist")
})
Cypress.Commands.add("expandBudibaseConnection", () => {

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
"version": "1.3.4-alpha.2",
"version": "1.3.15-alpha.3",
"license": "GPL-3.0",
"private": true,
"scripts": {
@ -69,10 +69,10 @@
}
},
"dependencies": {
"@budibase/bbui": "1.3.4-alpha.2",
"@budibase/client": "1.3.4-alpha.2",
"@budibase/frontend-core": "1.3.4-alpha.2",
"@budibase/string-templates": "1.3.4-alpha.2",
"@budibase/bbui": "1.3.15-alpha.3",
"@budibase/client": "1.3.15-alpha.3",
"@budibase/frontend-core": "1.3.15-alpha.3",
"@budibase/string-templates": "1.3.15-alpha.3",
"@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
@ -96,7 +96,7 @@
"@babel/runtime": "^7.13.10",
"@rollup/plugin-replace": "^2.4.2",
"@roxi/routify": "2.18.5",
"@sveltejs/vite-plugin-svelte": "1.0.0-next.19",
"@sveltejs/vite-plugin-svelte": "1.0.1",
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/svelte": "^3.0.0",
"babel-jest": "^26.6.3",
@ -118,7 +118,7 @@
"ts-node": "^10.4.0",
"tsconfig-paths": "4.0.0",
"typescript": "^4.5.5",
"vite": "^2.1.5"
"vite": "^3.0.8"
},
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072"
}

View file

@ -9,7 +9,7 @@ function prepareData(config) {
ds => ds.source === config.type
).length
let baseName = IntegrationNames[config.type]
let baseName = IntegrationNames[config.type] || config.name
let name =
existingTypeCount === 0 ? baseName : `${baseName}-${existingTypeCount + 1}`

View file

@ -90,13 +90,21 @@ export const getFrontendStore = () => {
// Fetch component definitions.
// Allow errors to propagate.
let components = await API.fetchComponentLibDefinitions(application.appId)
const components = await API.fetchComponentLibDefinitions(
application.appId
)
// Filter out custom component keys so we can flag them
const customComponents = Object.keys(components).filter(name =>
name.startsWith("plugin/")
)
// Reset store state
store.update(state => ({
...state,
libraries: application.componentLibraries,
components,
customComponents,
clientFeatures: {
...INITIAL_FRONTEND_STATE.clientFeatures,
...components.features,
@ -116,6 +124,7 @@ export const getFrontendStore = () => {
version: application.version,
revertableVersion: application.revertableVersion,
navigation: application.navigation || {},
usedPlugins: application.usedPlugins || [],
}))
// Initialise backend stores
@ -189,9 +198,18 @@ export const getFrontendStore = () => {
})
},
save: async screen => {
const state = get(store)
const creatingNewScreen = screen._id === undefined
const savedScreen = await API.saveScreen(screen)
const routesResponse = await API.fetchAppRoutes()
let usedPlugins = state.usedPlugins
// If plugins changed we need to fetch the latest app metadata
if (savedScreen.pluginAdded) {
const { application } = await API.fetchAppPackage(state.appId)
usedPlugins = application.usedPlugins || []
}
store.update(state => {
// Update screen object
const idx = state.screens.findIndex(x => x._id === savedScreen._id)
@ -210,6 +228,9 @@ export const getFrontendStore = () => {
// Update routes
state.routes = routesResponse.routes
// Update used plugins
state.usedPlugins = usedPlugins
return state
})
return savedScreen
@ -378,9 +399,6 @@ export const getFrontendStore = () => {
if (!componentName) {
return null
}
if (!componentName.startsWith("@budibase")) {
componentName = `@budibase/standard-components/${componentName}`
}
return get(store).components[componentName]
},
createInstance: (componentName, presetProps) => {

View file

@ -1,6 +1,7 @@
<script>
import { Button, Select, Input, Label } from "@budibase/bbui"
import { createEventDispatcher } from "svelte"
import { onMount, createEventDispatcher } from "svelte"
import { flags } from "stores/backend"
const dispatch = createEventDispatcher()
export let value
@ -29,11 +30,16 @@
label: "Every Night at Midnight",
value: "0 0 * * *",
},
{
label: "Every Budibase Reboot",
value: "@reboot",
},
]
onMount(() => {
if (!$flags.cloud) {
CRON_EXPRESSIONS.push({
label: "Every Budibase Reboot",
value: "@reboot",
})
}
})
</script>
<div class="block-field">

View file

@ -13,7 +13,7 @@
customQueryIconColor,
customQueryText,
} from "helpers/data/utils"
import ICONS from "./icons"
import { getIcon } from "./icons"
import { notifications } from "@budibase/bbui"
let openDataSources = []
@ -124,7 +124,7 @@
>
<div class="datasource-icon" slot="icon">
<svelte:component
this={ICONS[datasource.source]}
this={getIcon(datasource.source, datasource.schema)}
height="18"
width="18"
/>

View file

@ -8,6 +8,7 @@
notifications,
Modal,
Table,
Toggle,
} from "@budibase/bbui"
import { datasources, integrations, tables } from "stores/backend"
import CreateEditRelationship from "components/backend/Datasources/CreateEditRelationship.svelte"
@ -15,6 +16,7 @@
import ArrayRenderer from "components/common/renderers/ArrayRenderer.svelte"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import { goto } from "@roxi/routify"
import ValuesList from "components/common/ValuesList.svelte"
export let datasource
export let save
@ -31,6 +33,8 @@
let createExternalTableModal
let selectedFromRelationship, selectedToRelationship
let confirmDialog
let specificTables = null
let requireSpecificTables = false
$: integration = datasource && $integrations[datasource.source]
$: plusTables = datasource?.plus
@ -87,7 +91,7 @@
async function updateDatasourceSchema() {
try {
await datasources.updateSchema(datasource)
await datasources.updateSchema(datasource, specificTables)
notifications.success(`Datasource ${name} tables updated successfully.`)
await tables.fetch()
} catch (error) {
@ -150,6 +154,19 @@
warning={false}
title="Confirm table fetch"
>
<Toggle
bind:value={requireSpecificTables}
on:change={e => {
requireSpecificTables = e.detail
specificTables = null
}}
thin
text="Fetch listed tables only (one per line)"
/>
{#if requireSpecificTables}
<ValuesList label="" bind:values={specificTables} />
{/if}
<br />
<Body>
If you have fetched tables from this database before, this action may
overwrite any changes you made after your initial fetch.

View file

@ -0,0 +1,65 @@
<script>
import { createEventDispatcher } from "svelte"
import { Heading, Detail } from "@budibase/bbui"
import { getIcon } from "../icons"
export let integration
export let integrationType
export let schema
let dispatcher = createEventDispatcher()
</script>
<div
class:selected={integration.type === integrationType}
on:click={() => dispatcher("selected", integrationType)}
class="item hoverable"
>
<div class="item-body" class:with-type={!!schema.type}>
<svelte:component
this={getIcon(integrationType, schema)}
height="20"
width="20"
/>
<div class="text">
<Heading size="XXS">{schema.friendlyName}</Heading>
{#if schema.type}
<Detail size="S">{schema.type || ""}</Detail>
{/if}
</div>
</div>
</div>
<style>
.item {
cursor: pointer;
display: grid;
grid-gap: var(--spectrum-alias-grid-margin-xsmall);
padding: var(--spectrum-alias-item-padding-s)
var(--spectrum-alias-item-padding-m);
background: var(--spectrum-alias-background-color-secondary);
transition: background 0.13s ease-out;
border: solid var(--spectrum-alias-border-color);
border-radius: 5px;
box-sizing: border-box;
border-width: 2px;
}
.item:hover,
.item.selected {
background: var(--spectrum-alias-background-color-tertiary);
}
.item-body {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: var(--spacing-m);
}
.item-body.with-type {
align-items: flex-start;
}
.item-body.with-type :global(svg) {
margin-top: 4px;
}
</style>

View file

@ -0,0 +1,52 @@
<script>
export let width = "100"
export let height = "100"
</script>
<svg
{width}
{height}
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
viewBox="0 0 18.43 17.62"
xml:space="preserve"
>
<style type="text/css">
.custom-st0 {
fill: var(
--spectrum-heading-xxs-text-color,
var(--spectrum-alias-heading-text-color)
);
}
</style>
<ellipse
id="Ellipse_9514"
class="custom-st0"
cx="9.14"
cy="2.88"
rx="8"
ry="2.5"
/>
<path
id="Path_8355"
class="custom-st0"
d="M7.64,12.88c0-0.48,0.06-0.95,0.17-1.41c-2.53-0.16-5.92-0.76-6.67-1.95v4.36
c0,1.34,3.39,2.43,7.63,2.49C8.03,15.36,7.63,14.13,7.64,12.88z"
/>
<path
id="Path_8356"
class="custom-st0"
d="M13.64,6.88c1.25,0,2.47,0.39,3.48,1.12c0.01-0.04,0.02-0.08,0.02-0.12V4.51
c-1.22,1.55-5.53,2-8,2s-7.11-0.58-8-2v3.36c0,1.28,3.09,2.33,7.06,2.48C9.18,8.24,11.3,6.88,13.64,6.88z"
/>
<path
id="Path_8353"
class="custom-st0"
d="M10.32,14.42c-0.56-0.56-0.56-1.47,0-2.03l1.24-1.24l-0.01-0.01c-0.25-0.25-0.25-0.65,0-0.9l0,0
L12,9.79c0.25-0.25,0.65-0.25,0.9,0c0,0,0,0,0,0l0,0l0.45,0.45l1.53-1.53c0.09-0.09,0.24-0.09,0.34,0l0.34,0.34
c0.09,0.09,0.09,0.24,0,0.34l-1.53,1.53l1.35,1.35l1.53-1.53c0.09-0.09,0.24-0.09,0.34,0l0.34,0.34c0.09,0.09,0.09,0.24,0,0.34
l-1.53,1.53l0.45,0.45c0.25,0.25,0.25,0.65,0,0.9l0,0l-0.45,0.45c-0.25,0.25-0.65,0.25-0.9,0l0,0l-0.01-0.01l-1.24,1.24
c-0.56,0.56-1.47,0.56-2.03,0l0,0L10.32,14.42z"
/>
</svg>

View file

@ -15,8 +15,9 @@ import GoogleSheets from "./GoogleSheets.svelte"
import Firebase from "./Firebase.svelte"
import Redis from "./Redis.svelte"
import Snowflake from "./Snowflake.svelte"
import Custom from "./Custom.svelte"
export default {
const ICONS = {
BUDIBASE: Budibase,
POSTGRES: Postgres,
DYNAMODB: DynamoDB,
@ -34,4 +35,15 @@ export default {
FIRESTORE: Firebase,
REDIS: Redis,
SNOWFLAKE: Snowflake,
CUSTOM: Custom,
}
export default ICONS
export function getIcon(integrationType, schema) {
if (schema?.custom || !ICONS[integrationType]) {
return ICONS.CUSTOM
} else {
return ICONS[integrationType]
}
}

View file

@ -18,6 +18,7 @@
import { createRestDatasource } from "builderStore/datasource"
import { goto } from "@roxi/routify"
import ImportRestQueriesModal from "./ImportRestQueriesModal.svelte"
import DatasourceCard from "../_components/DatasourceCard.svelte"
export let modal
let integrations = {}
@ -27,6 +28,9 @@
let importModal
$: showImportButton = false
$: customIntegrations = Object.entries(integrations).filter(
entry => entry[1].custom
)
checkShowImport()
@ -49,6 +53,9 @@
schema: selected.datasource,
auth: selected.auth,
}
if (selected.friendlyName) {
integration.name = selected.friendlyName
}
checkShowImport()
}
@ -150,36 +157,39 @@
<Layout noPadding gap="XS">
<Body size="S">Connect to an external data source</Body>
<div class="item-list">
{#each Object.entries(integrations).filter(([key]) => key !== IntegrationTypes.INTERNAL) as [integrationType, schema]}
<div
class:selected={integration.type === integrationType}
on:click={() => selectIntegration(integrationType)}
class="item hoverable"
>
<div class="item-body" class:with-type={!!schema.type}>
<svelte:component
this={ICONS[integrationType]}
height="20"
width="20"
/>
<div class="text">
<Heading size="XXS">{schema.friendlyName}</Heading>
{#if schema.type}
<Detail size="S">{schema.type || ""}</Detail>
{/if}
</div>
</div>
</div>
{#each Object.entries(integrations).filter(([key, val]) => key !== IntegrationTypes.INTERNAL && !val.custom) as [integrationType, schema]}
<DatasourceCard
on:selected={evt => selectIntegration(evt.detail)}
{schema}
bind:integrationType
{integration}
/>
{/each}
</div>
</Layout>
{#if customIntegrations.length > 0}
<Layout noPadding gap="XS">
<Body size="S">Custom data source</Body>
<div class="item-list">
{#each customIntegrations as [integrationType, schema]}
<DatasourceCard
on:selected={evt => selectIntegration(evt.detail)}
{schema}
bind:integrationType
{integration}
/>
{/each}
</div>
</Layout>
{/if}
</ModalContent>
</Modal>
<style>
.item-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
grid-template-columns: repeat(2, minmax(150px, 1fr));
grid-gap: var(--spectrum-alias-grid-baseline);
}

View file

@ -14,8 +14,14 @@
let datasource = cloneDeep(integration)
let skipFetch = false
$: name =
IntegrationNames[datasource.type] || datasource.name || datasource.type
async function saveDatasource() {
try {
if (!datasource.name) {
datasource.name = name
}
const resp = await save(datasource, skipFetch)
$goto(`./datasource/${resp._id}`)
notifications.success(`Datasource updated successfully.`)
@ -32,7 +38,7 @@
</script>
<ModalContent
title={`Connect to ${IntegrationNames[datasource.type]}`}
title={`Connect to ${name}`}
onConfirm={() => saveDatasource()}
onCancel={() => modal.show()}
confirmText={datasource.plus

View file

@ -23,6 +23,7 @@
const dispatch = createEventDispatcher()
let bindingDrawer
let valid = true
let currentVal = value
$: readableValue = runtimeToReadableBinding(bindings, value)
$: tempValue = readableValue
@ -30,11 +31,17 @@
const saveBinding = () => {
onChange(tempValue)
onBlur()
bindingDrawer.hide()
}
const onChange = value => {
dispatch("change", readableToRuntimeBinding(bindings, value))
currentVal = readableToRuntimeBinding(bindings, value)
dispatch("change", currentVal)
}
const onBlur = () => {
dispatch("blur", currentVal)
}
</script>
@ -45,6 +52,7 @@
readonly={isJS}
value={isJS ? "(JavaScript function)" : readableValue}
on:change={event => onChange(event.detail)}
on:blur={onBlur}
{placeholder}
{updateOnChange}
/>

View file

@ -53,7 +53,7 @@
)
newBlock.inputs = {
fields: Object.keys(parameters.fields).reduce((fields, key) => {
fields: Object.keys(parameters.fields ?? {}).reduce((fields, key) => {
fields[key] = "string"
return fields
}, {}),

View file

@ -107,7 +107,7 @@
placeholder={keyPlaceholder}
readonly={readOnly}
bind:value={field.name}
on:change={changed}
on:blur={changed}
/>
{#if options}
<Select bind:value={field.value} on:change={changed} {options} />
@ -115,7 +115,10 @@
<DrawerBindableInput
{bindings}
placeholder="Value"
on:change={e => (field.value = e.detail)}
on:blur={e => {
field.value = e.detail
changed()
}}
disabled={readOnly}
value={field.value}
allowJS={false}
@ -127,7 +130,7 @@
placeholder={valuePlaceholder}
readonly={readOnly}
bind:value={field.value}
on:change={changed}
on:blur={changed}
/>
{/if}
{#if toggle}

View file

@ -1,16 +1,24 @@
<script>
import { ModalContent, Toggle } from "@budibase/bbui"
import { ModalContent, Toggle, Body } from "@budibase/bbui"
export let app
export let published
let excludeRows = false
$: title = published ? "Export published app" : "Export latest app"
$: confirmText = published ? "Export published" : "Export latest"
const exportApp = () => {
const id = app.deployed ? app.prodId : app.devId
const id = published ? app.prodId : app.devId
const appName = encodeURIComponent(app.name)
window.location = `/api/backups/export?appId=${id}&appname=${appName}&excludeRows=${excludeRows}`
}
</script>
<ModalContent title={"Export"} confirmText={"Export"} onConfirm={exportApp}>
<ModalContent {title} {confirmText} onConfirm={exportApp}>
<Body
>Apps can be exported with or without data that is within internal tables -
select this below.</Body
>
<Toggle text="Exclude Rows" bind:value={excludeRows} />
</ModalContent>

View file

@ -57,3 +57,10 @@ export const DefaultAppTheme = {
navBackground: "var(--spectrum-global-color-gray-50)",
navTextColor: "var(--spectrum-global-color-gray-800)",
}
export const PluginSource = {
URL: "URL",
NPM: "NPM",
GITHUB: "Github",
FILE: "File Upload",
}

View file

@ -46,7 +46,7 @@ export function buildQueryString(obj) {
if (str !== "") {
str += "&"
}
str += `${key}=${value || ""}`
str += `${key}=${encodeURIComponent(value || "")}`
}
}
return str

View file

@ -31,7 +31,9 @@
class="add-button"
data-cy={`new-${isExternal ? "datasource" : "table"}`}
>
<Icon hoverable name="AddCircle" on:click={modal.show} />
{#if modal}
<Icon hoverable name="AddCircle" on:click={modal.show} />
{/if}
</div>
</div>
<div class="content">

View file

@ -28,25 +28,25 @@
import { onMount } from "svelte"
import restUtils from "helpers/data/utils"
import {
RestBodyTypes as bodyTypes,
SchemaTypeOptions,
PaginationLocations,
PaginationTypes,
RawRestBodyTypes,
RestBodyTypes as bodyTypes,
SchemaTypeOptions,
} from "constants/backend"
import JSONPreview from "components/integration/JSONPreview.svelte"
import AccessLevelSelect from "components/integration/AccessLevelSelect.svelte"
import DynamicVariableModal from "../../_components/DynamicVariableModal.svelte"
import Placeholder from "assets/bb-spaceship.svg"
import { cloneDeep } from "lodash/fp"
import { RawRestBodyTypes } from "constants/backend"
import {
getRestBindings,
toBindingsArray,
runtimeToReadableBinding,
readableToRuntimeBinding,
runtimeToReadableMap,
readableToRuntimeMap,
runtimeToReadableBinding,
runtimeToReadableMap,
toBindingsArray,
} from "builderStore/dataBinding"
let query, datasource
@ -95,7 +95,7 @@
$: runtimeUrlQueries = readableToRuntimeMap(mergedBindings, breakQs)
function getSelectedQuery() {
const cloneQuery = cloneDeep(
return cloneDeep(
$queries.list.find(q => q._id === $queries.selected) || {
datasourceId: $params.selectedDatasource,
parameters: [],
@ -107,7 +107,6 @@
queryVerb: "read",
}
)
return cloneQuery
}
function checkQueryName(inputUrl = null) {
@ -121,14 +120,15 @@
if (!base) {
return base
}
const qs = restUtils.buildQueryString(
let qs = restUtils.buildQueryString(
runtimeToReadableMap(mergedBindings, qsObj)
)
let newUrl = base
if (base.includes("?")) {
newUrl = base.split("?")[0]
const split = base.split("?")
newUrl = split[0]
}
return qs.length > 0 ? `${newUrl}?${qs}` : newUrl
return qs.length === 0 ? newUrl : `${newUrl}?${qs}`
}
function buildQuery() {
@ -314,6 +314,25 @@
}
}
const paramsChanged = evt => {
breakQs = {}
for (let param of evt.detail) {
breakQs[param.name] = param.value
}
}
const urlChanged = evt => {
breakQs = {}
const qs = evt.target.value.split("?")[1]
if (qs && qs.length > 0) {
const parts = qs.split("&")
for (let part of parts) {
const [key, value] = part.split("=")
breakQs[key] = value
}
}
}
onMount(async () => {
query = getSelectedQuery()
@ -426,7 +445,11 @@
/>
</div>
<div class="url">
<Input bind:value={url} placeholder="http://www.api.com/endpoint" />
<Input
on:blur={urlChanged}
bind:value={url}
placeholder="http://www.api.com/endpoint"
/>
</div>
<Button primary disabled={!url} on:click={runQuery}>Send</Button>
<Button
@ -456,13 +479,16 @@
/>
</Tab>
<Tab title="Params">
<KeyValueBuilder
bind:object={breakQs}
name="param"
headings
bindings={mergedBindings}
bindingDrawerLeft="260px"
/>
{#key breakQs}
<KeyValueBuilder
on:change={paramsChanged}
object={breakQs}
name="param"
headings
bindings={mergedBindings}
bindingDrawerLeft="260px"
/>
{/key}
</Tab>
<Tab title="Headers">
<KeyValueBuilder

View file

@ -85,6 +85,8 @@
? [$store.componentToPaste?._id]
: [],
isBudibaseEvent: true,
usedPlugins: $store.usedPlugins,
location: window.location,
}
// Refresh the preview when required
@ -303,7 +305,7 @@
<iframe
title="componentPreview"
bind:this={iframe}
src="/preview"
src="/app/preview"
class:hidden={loading || error}
class:tablet={$store.previewDevice === "tablet"}
class:mobile={$store.previewDevice === "mobile"}

View file

@ -60,7 +60,7 @@
},
}
const handleKeyAction = async (component, key, ctrlKey = false) => {
const handleKeyAction = async (event, component, key, ctrlKey = false) => {
if (!component || !key) {
return false
}
@ -76,6 +76,9 @@
const handler = keyHandlers[key]
if (!handler) {
return false
} else if (event) {
event.preventDefault()
event.stopPropagation()
}
return handler(component)
} catch (error) {
@ -95,14 +98,19 @@
return
}
// Key events are always for the selected component
return handleKeyAction($selectedComponent, e.key, e.ctrlKey || e.metaKey)
return await handleKeyAction(
e,
$selectedComponent,
e.key,
e.ctrlKey || e.metaKey
)
}
const handleComponentMenu = async e => {
// Menu events can be for any component
const { id, key, ctrlKey } = e.detail
const component = findComponent($selectedScreen.props, id)
return await handleKeyAction(component, key, ctrlKey)
return await handleKeyAction(null, component, key, ctrlKey)
}
onMount(() => {

View file

@ -28,7 +28,7 @@
{#if $selectedComponent}
{#key $selectedComponent._id}
<Panel {title} icon={componentDefinition.icon} borderLeft>
<Panel {title} icon={componentDefinition?.icon} borderLeft>
<ComponentSettingsSection
{componentInstance}
{componentDefinition}

View file

@ -24,7 +24,11 @@
$: currentDefinition = store.actions.components.getDefinition(
$selectedComponent?._component
)
$: enrichedStructure = enrichStructure(structure, $store.components)
$: enrichedStructure = enrichStructure(
structure,
$store.components,
$store.customComponents
)
$: filteredStructure = filterStructure(
enrichedStructure,
section,
@ -46,8 +50,25 @@
// Parses the structure in the manifest and returns an enriched structure with
// explicit categories
const enrichStructure = (structure, definitions) => {
const enrichStructure = (structure, definitions, customComponents) => {
let enrichedStructure = []
// Add custom components category
if (customComponents?.length) {
enrichedStructure.push({
name: "Plugins",
isCategory: true,
children: customComponents
.map(x => ({
...definitions[x],
name: definitions[x].friendlyName || definitions[x].name,
}))
.sort((a, b) => {
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1
}),
})
}
structure.forEach(item => {
if (typeof item === "string") {
const def = definitions[`@budibase/standard-components/${item}`]
@ -65,6 +86,7 @@
})
}
})
return enrichedStructure
}
@ -225,7 +247,7 @@
position: fixed;
right: 0;
z-index: 1;
height: 100%;
height: calc(100% - 60px);
display: flex;
flex-direction: row;
align-items: stretch;

View file

@ -55,6 +55,8 @@
},
{ title: "Auth", href: "/builder/portal/manage/auth" },
{ title: "Email", href: "/builder/portal/manage/email" },
{ title: "Plugins", href: "/builder/portal/manage/plugins" },
{
title: "Organisation",
href: "/builder/portal/settings/organisation",

View file

@ -15,7 +15,6 @@
import Spinner from "components/common/Spinner.svelte"
import CreateAppModal from "components/start/CreateAppModal.svelte"
import UpdateAppModal from "components/start/UpdateAppModal.svelte"
import ExportAppModal from "components/start/ExportAppModal.svelte"
import { store, automationStore } from "builderStore"
import { API } from "api"
@ -33,7 +32,6 @@
let selectedApp
let creationModal
let updatingModal
let exportModal
let creatingApp = false
let loaded = $apps?.length || $templates?.length
let searchTerm = ""
@ -407,10 +405,6 @@
<UpdateAppModal app={selectedApp} />
</Modal>
<Modal bind:this={exportModal} padding={false} width="600px">
<ExportAppModal app={selectedApp} />
</Modal>
<style>
.appTable {
border-top: var(--border-light);

View file

@ -0,0 +1,127 @@
<script>
import {
ModalContent,
Label,
Input,
Select,
Dropzone,
Body,
notifications,
} from "@budibase/bbui"
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
import { plugins } from "stores/portal"
import { PluginSource } from "constants"
function opt(name, optional) {
if (optional) {
return { name, optional }
}
return { name }
}
let authOptions = {
[PluginSource.URL]: [opt("URL"), opt("Headers", true)],
[PluginSource.NPM]: [opt("URL")],
[PluginSource.GITHUB]: [opt("URL"), opt("Github Token", true)],
[PluginSource.FILE]: [opt("File Upload")],
}
let file
let source = PluginSource.URL
let dynamicValues = {}
let validation
$: validation = source === "File Upload" ? file : dynamicValues["URL"]
function infoMessage(optionName) {
switch (optionName) {
case PluginSource.URL:
return "Please specify a URL which directs to a built plugin TAR archive. You can provide headers if authentication is required."
case PluginSource.NPM:
return "Please specify the URL to a public NPM package which contains the built version of the plugin you wish to install."
case PluginSource.GITHUB:
return "Please specify the URL to a Github repository which contains built plugin releases. If this is a private repo you can provide a token to access it."
case PluginSource.FILE:
return "Please provide a built plugin TAR archive. You can build a plugin locally using the Budibase CLI."
}
}
async function save() {
try {
if (source === PluginSource.FILE) {
await plugins.uploadPlugin(file)
} else {
const url = dynamicValues["URL"]
let auth =
source === PluginSource.GITHUB
? dynamicValues["Github Token"]
: source === PluginSource.URL
? dynamicValues["Headers"]
: undefined
await plugins.createPlugin(source, url, auth)
}
notifications.success("Plugin added successfully.")
} catch (err) {
const msg = err?.message ? err.message : JSON.stringify(err)
notifications.error(`Failed to add plugin: ${msg}`)
}
}
</script>
<ModalContent
confirmText={"Save"}
onConfirm={save}
disabled={!validation}
size="M"
title="Add new plugin"
>
<div class="form-row">
<Label size="M">Source</Label>
<Select
placeholder={null}
bind:value={source}
options={Object.values(PluginSource)}
/>
</div>
<Body size="S">{infoMessage(source)}</Body>
{#each authOptions[source] as option}
{#if option.name === PluginSource.FILE}
<div class="form-row">
<Label size="M">{option.name}</Label>
<Dropzone
gallery={false}
value={[file]}
on:change={e => {
if (!e.detail || e.detail.length === 0) {
file = null
} else {
file = e.detail[0]
}
}}
/>
</div>
{:else}
<div class="form-row">
<div>
<Label size="M">{option.name}</Label>
{#if option.optional}
<Label size="S" muted><i>Optional</i></Label>
{/if}
</div>
{#if option.name === "Headers"}
<KeyValueBuilder bind:object={dynamicValues[option.name]} />
{:else}
<Input bind:value={dynamicValues[option.name]} />
{/if}
</div>
{/if}
{/each}
</ModalContent>
<style>
.form-row {
display: grid;
grid-template-columns: 60px 1fr;
grid-gap: var(--spacing-l);
align-items: center;
}
</style>

View file

@ -0,0 +1,33 @@
<script>
import { Body, ModalContent, notifications } from "@budibase/bbui"
import { plugins } from "stores/portal"
import { createEventDispatcher } from "svelte"
export let plugin
let dispatch = createEventDispatcher()
async function deletePlugin() {
try {
await plugins.deletePlugin(plugin._id)
notifications.success(`Plugin ${plugin?.name} deleted`)
dispatch("deleted")
} catch (error) {
const msg = error?.message ? error.message : JSON.stringify(error)
notifications.error(`Error deleting plugin: ${msg}`)
}
}
</script>
<ModalContent
warning
onConfirm={deletePlugin}
title="Delete Plugin"
confirmText="Delete plugin"
cancelText="Cancel"
showCloseIcon={false}
>
<Body>
Are you sure you want to delete <strong>{plugin?.name}</strong>
</Body>
</ModalContent>

View file

@ -0,0 +1,145 @@
<script>
import {
Icon,
Body,
Modal,
ModalContent,
Button,
Label,
Input,
} from "@budibase/bbui"
import DeletePluginModal from "../_components/DeletePluginModal.svelte"
export let plugin
let detailsModal
let deleteModal
let icon =
plugin.schema.type === "component"
? plugin.schema.schema.icon || "Book"
: plugin.schema.schema.icon || "Beaker"
function pluginDeleted() {
if (detailsModal) {
detailsModal.hide()
}
}
</script>
<div class="row" on:click={() => detailsModal.show()}>
<div class="title">
<div class="name">
<div>
<Icon size="M" name={icon} />
</div>
<div>
<Body
size="S"
color="var(--spectrum-global-color-gray-900)"
weight="800"
>
{plugin.name}
</Body>
</div>
</div>
</div>
<div class="desktop">{plugin.version}</div>
<div class="desktop">
{plugin.schema.type.charAt(0).toUpperCase() + plugin.schema.type.slice(1)}
</div>
<div>
<Icon name="ChevronRight" />
</div>
</div>
<Modal bind:this={detailsModal}>
<ModalContent
size="M"
title="Plugin details"
showConfirmButton={false}
showCancelButton={false}
>
<div class="details-row">
<Label size="M">Name</Label>
<Input disabled value={plugin.name} />
</div>
<div class="details-row">
<Label size="M">Type</Label>
<Input
disabled
value={plugin.schema.type.charAt(0).toUpperCase() +
plugin.schema.type.slice(1)}
/>
</div>
<div class="details-row">
<Label size="M">Source</Label>
<Input disabled value={plugin.source || "N/A"} />
</div>
<div class="details-row">
<Label size="M">Version</Label>
<Input disabled value={plugin.version} />
</div>
<div class="details-row">
<Label size="M">License</Label>
<Input disabled value={plugin.package.license} />
</div>
<div class="details-row">
<Label size="M">Author</Label>
<Input disabled value={plugin.package.author || "N/A"} />
</div>
<div class="footer" slot="footer">
<Button newStyles on:click={deleteModal.show()} warning>Delete</Button>
</div>
</ModalContent>
<Modal bind:this={deleteModal}>
<DeletePluginModal {plugin} on:deleted={pluginDeleted} />
</Modal>
</Modal>
<style>
.row {
display: grid;
grid-template-columns: 1fr 110px 140px 20px;
align-items: center;
background: var(--background);
border-radius: 4px;
padding: 0 16px;
height: 56px;
background: var(--spectrum-global-color-gray-50);
border: 1px solid var(--spectrum-global-color-gray-300);
transition: background 130ms ease-out;
}
.row:hover {
cursor: pointer;
background: var(--spectrum-global-color-gray-75);
}
.name {
grid-gap: var(--spacing-m);
grid-template-columns: 75px 75px;
align-items: center;
display: flex;
}
.details-row {
display: grid;
grid-template-columns: 60px 1fr;
grid-gap: var(--spacing-l) var(--spacing-l);
align-items: center;
}
@media (max-width: 640px) {
.desktop {
display: none !important;
}
}
.footer {
display: flex;
gap: var(--spacing-l);
}
</style>

View file

@ -0,0 +1,95 @@
<script>
import {
Layout,
Heading,
Body,
Button,
Select,
Divider,
Modal,
Search,
} from "@budibase/bbui"
import { onMount } from "svelte"
import { plugins } from "stores/portal"
import PluginRow from "./_components/PluginRow.svelte"
import AddPluginModal from "./_components/AddPluginModal.svelte"
let modal
let searchTerm = ""
let filter = "all"
let filterOptions = [
{ label: "All plugins", value: "all" },
{ label: "Components", value: "component" },
{ label: "Datasources", value: "datasource" },
]
$: filteredPlugins = $plugins
.filter(plugin => {
return filter === "all" || plugin.schema.type === filter
})
.filter(plugin => {
return (
!searchTerm ||
plugin?.name?.toLowerCase().includes(searchTerm.toLowerCase())
)
})
onMount(async () => {
await plugins.load()
})
</script>
<Layout noPadding>
<Layout gap="XS" noPadding>
<Heading size="M">Plugins</Heading>
<Body>Add your own custom datasources and components</Body>
</Layout>
<Divider size="S" />
<Layout noPadding>
<div class="controls">
<div>
<Button on:click={modal.show} newStyles cta icon={"Add"}>
Add plugin
</Button>
</div>
<div class="filters">
<div class="select">
<Select
bind:value={filter}
placeholder={null}
options={filterOptions}
autoWidth
quiet
/>
</div>
<Search bind:value={searchTerm} placeholder="Search plugins" />
</div>
</div>
{#if filteredPlugins?.length}
<Layout noPadding gap="S">
{#each filteredPlugins as plugin (plugin._id)}
<PluginRow {plugin} />
{/each}
</Layout>
{/if}
</Layout>
</Layout>
<Modal bind:this={modal}>
<AddPluginModal />
</Modal>
<style>
.filters {
display: flex;
gap: var(--spacing-xl);
}
.controls {
display: flex;
gap: var(--spacing-xl);
justify-content: space-between;
}
.controls :global(.spectrum-Search) {
width: 200px;
}
</style>

View file

@ -16,6 +16,7 @@
MenuItem,
Icon,
Helpers,
Modal,
} from "@budibase/bbui"
import OverviewTab from "../_components/OverviewTab.svelte"
import SettingsTab from "../_components/SettingsTab.svelte"
@ -29,6 +30,7 @@
import EditableIcon from "components/common/EditableIcon.svelte"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import HistoryTab from "components/portal/overview/automation/HistoryTab.svelte"
import ExportAppModal from "components/start/ExportAppModal.svelte"
import { checkIncomingDeploymentStatus } from "components/deploy/utils"
import { onDestroy, onMount } from "svelte"
@ -38,7 +40,9 @@
let loaded = false
let deletionModal
let unpublishModal
let exportModal
let appName = ""
let published
// App
$: filteredApps = $apps.filter(app => app.devId === application)
@ -140,11 +144,9 @@
notifications.success("App ID copied to clipboard.")
}
const exportApp = (app, opts = { published: false }) => {
const appName = encodeURIComponent(app.name)
const id = opts?.published ? app.prodId : app.devId
// always export the development version
window.location = `/api/backups/export?appId=${id}&appname=${appName}`
const exportApp = opts => {
published = opts.published
exportModal.show()
}
const unpublishApp = app => {
@ -206,6 +208,10 @@
})
</script>
<Modal bind:this={exportModal} padding={false} width="600px">
<ExportAppModal app={selectedApp} {published} />
</Modal>
<span class="overview-wrap">
<Page wide noPadding>
{#await promise}
@ -269,14 +275,14 @@
<Icon hoverable name="More" />
</span>
<MenuItem
on:click={() => exportApp(selectedApp, { published: false })}
on:click={() => exportApp({ published: false })}
icon="DownloadFromCloud"
>
Export latest
</MenuItem>
{#if isPublished}
<MenuItem
on:click={() => exportApp(selectedApp, { published: true })}
on:click={() => exportApp({ published: true })}
icon="DownloadFromCloudOutline"
>
Export published

View file

@ -88,14 +88,14 @@
<Heading size="S">Information</Heading>
<Body size="S">Here you can update your logo and organization name.</Body>
</Layout>
<div class="fields">
<div class="field">
<div className="fields">
<div className="field">
<Label size="L">Org. name</Label>
<Input thin bind:value={$values.company} />
</div>
<div class="field logo">
<div className="field logo">
<Label size="L">Logo</Label>
<div class="file">
<div className="file">
<Dropzone
value={[$values.logo]}
on:change={e => {
@ -115,8 +115,8 @@
<Heading size="S">Platform</Heading>
<Body size="S">Here you can set up general platform settings.</Body>
</Layout>
<div class="fields">
<div class="field">
<div className="fields">
<div className="field">
<Label
size="L"
tooltip={"Update the Platform URL to match your Budibase web URL. This keeps email templates and authentication configs up to date."}
@ -150,15 +150,18 @@
display: grid;
grid-gap: var(--spacing-m);
}
.field {
display: grid;
grid-template-columns: 100px 1fr;
grid-gap: var(--spacing-l);
align-items: center;
}
.file {
max-width: 30ch;
}
.logo {
align-items: start;
}

View file

@ -62,8 +62,11 @@ export function createDatasourcesStore() {
unselect: () => {
update(state => ({ ...state, selected: null }))
},
updateSchema: async datasource => {
const response = await API.buildDatasourceSchema(datasource?._id)
updateSchema: async (datasource, tablesFilter) => {
const response = await API.buildDatasourceSchema({
datasourceId: datasource?._id,
tablesFilter,
})
return await updateDatasource(response)
},
save: async (body, fetchSchema = false) => {

View file

@ -8,3 +8,4 @@ export { oidc } from "./oidc"
export { templates } from "./templates"
export { licensing } from "./licensing"
export { groups } from "./groups"
export { plugins } from "./plugins"

View file

@ -0,0 +1,77 @@
import { writable } from "svelte/store"
import { API } from "api"
import { PluginSource } from "constants"
export function createPluginsStore() {
const { subscribe, set, update } = writable([])
async function load() {
const plugins = await API.getPlugins()
set(plugins)
}
async function deletePlugin(pluginId) {
await API.deletePlugin(pluginId)
update(state => {
state = state.filter(existing => existing._id !== pluginId)
return state
})
}
async function createPlugin(source, url, auth = null) {
let pluginData = {
source,
url,
}
switch (source) {
case PluginSource.URL:
pluginData.headers = auth
break
case PluginSource.GITHUB:
pluginData.githubToken = auth
break
}
let res = await API.createPlugin(pluginData)
let newPlugin = res.plugins[0]
update(state => {
const currentIdx = state.findIndex(plugin => plugin._id === newPlugin._id)
if (currentIdx >= 0) {
state.splice(currentIdx, 1, newPlugin)
} else {
state.push(newPlugin)
}
return state
})
}
async function uploadPlugin(file) {
if (!file) {
return
}
let data = new FormData()
data.append("file", file)
let resp = await API.uploadPlugin(data)
let newPlugin = resp.plugins[0]
update(state => {
const currentIdx = state.findIndex(plugin => plugin._id === newPlugin._id)
if (currentIdx >= 0) {
state.splice(currentIdx, 1, newPlugin)
} else {
state.push(newPlugin)
}
return state
})
}
return {
subscribe,
load,
createPlugin,
deletePlugin,
uploadPlugin,
}
}
export const plugins = createPluginsStore()

View file

@ -1,15 +1,23 @@
import { svelte } from "@sveltejs/vite-plugin-svelte"
import replace from "@rollup/plugin-replace"
import { defineConfig, loadEnv } from "vite"
import path from "path"
export default ({ mode }) => {
export default defineConfig(({ mode }) => {
const isProduction = mode === "production"
const env = loadEnv(mode, process.cwd())
return {
server: {
fs: {
strict: false,
},
hmr: {
protocol: env.VITE_HMR_PROTOCOL || "ws",
clientPort: env.VITE_HMR_CLIENT_PORT || 3000,
path: env.VITE_HMR_PATH || "/",
},
port: 3000,
},
base: "/builder/",
build: {
@ -79,4 +87,4 @@ export default ({ mode }) => {
],
},
}
}
})

View file

@ -967,6 +967,11 @@
debug "^3.1.0"
lodash.once "^4.1.1"
"@esbuild/linux-loong64@0.14.54":
version "0.14.54"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028"
integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==
"@hapi/hoek@^9.0.0":
version "9.2.1"
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17"
@ -1215,10 +1220,10 @@
estree-walker "^1.0.1"
picomatch "^2.2.2"
"@rollup/pluginutils@^4.1.1":
version "4.1.1"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.1.tgz#1d4da86dd4eded15656a57d933fda2b9a08d47ec"
integrity sha512-clDjivHqWGXi7u+0d2r2sBi4Ie6VLEAzWMIkvJLnDmxoOhBYOTfzGbOQBA32THHm11/LiJbd01tJUpJsbshSWQ==
"@rollup/pluginutils@^4.2.1":
version "4.2.1"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==
dependencies:
estree-walker "^2.0.1"
picomatch "^2.2.2"
@ -1350,17 +1355,17 @@
resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-4.3.0.tgz#03ddf67d3aa8a9a4cb0edbbd259465c9ced7e70d"
integrity sha512-ZQ2XAhgu4G9yBeXQNDAz07Z8oZNnMt5o9vzf/mpBA7Teb/JI+8qXp2wt8D245SzmtNlFkG/bzRYvQc0scgZeCQ==
"@sveltejs/vite-plugin-svelte@1.0.0-next.19":
version "1.0.0-next.19"
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.19.tgz#9646abc2cd1982146db4bb341aafdb5f32f19dd2"
integrity sha512-q9hHkMzodScwDq64pNaWhekpj97vWg3wO9T0rqd8bC2EsrBQs2uD1qMJvDqlNd63AbO2uSJMGo+TQ0Xt2xgyYg==
"@sveltejs/vite-plugin-svelte@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.1.tgz#7f468f03c933fcdfc60d4773671c73f33b9ef4d6"
integrity sha512-PorCgUounn0VXcpeJu+hOweZODKmGuLHsLomwqSj+p26IwjjGffmYQfVHtiTWq+NqaUuuHWWG7vPge6UFw4Aeg==
dependencies:
"@rollup/pluginutils" "^4.1.1"
debug "^4.3.2"
kleur "^4.1.4"
magic-string "^0.25.7"
require-relative "^0.8.7"
svelte-hmr "^0.14.7"
"@rollup/pluginutils" "^4.2.1"
debug "^4.3.4"
deepmerge "^4.2.2"
kleur "^4.1.5"
magic-string "^0.26.2"
svelte-hmr "^0.14.12"
"@testing-library/dom@^7.0.3":
version "7.31.2"
@ -2493,6 +2498,13 @@ debug@^3.1.0:
dependencies:
ms "^2.1.1"
debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@ -2655,113 +2667,132 @@ error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
esbuild-android-arm64@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.14.tgz#c85083ece26be3d67e6c720e088968a98409e023"
integrity sha512-Q+Xhfp827r+ma8/DJgpMRUbDZfefsk13oePFEXEIJ4gxFbNv5+vyiYXYuKm43/+++EJXpnaYmEnu4hAKbAWYbA==
esbuild-android-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==
esbuild-darwin-64@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.14.tgz#8e4e237ad847cc54a1d3a5caee26a746b9f0b81f"
integrity sha512-YmOhRns6QBNSjpVdTahi/yZ8dscx9ai7a6OY6z5ACgOuQuaQ2Qk2qgJ0/siZ6LgD0gJFMV8UINFV5oky5TFNQQ==
esbuild-android-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771"
integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==
esbuild-darwin-arm64@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.14.tgz#b3b5ebd40b2cb06ee0f6fb342dd4bdcca54ad273"
integrity sha512-Lp00VTli2jqZghSa68fx3fEFCPsO1hK59RMo1PRap5RUjhf55OmaZTZYnCDI0FVlCtt+gBwX5qwFt4lc6tI1xg==
esbuild-darwin-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25"
integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==
esbuild-freebsd-64@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.14.tgz#175ecb2fa8141428cf70ea2d5f4c27534bad53e0"
integrity sha512-BKosI3jtvTfnmsCW37B1TyxMUjkRWKqopR0CE9AF2ratdpkxdR24Vpe3gLKNyWiZ7BE96/SO5/YfhbPUzY8wKw==
esbuild-darwin-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73"
integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==
esbuild-freebsd-arm64@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.14.tgz#a7d64e41d1fa581f8db7775e5200f18e67d70c4d"
integrity sha512-yd2uh0yf+fWv5114+SYTl4/1oDWtr4nN5Op+PGxAkMqHfYfLjFKpcxwCo/QOS/0NWqPVE8O41IYZlFhbEN2B8Q==
esbuild-freebsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d"
integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==
esbuild-linux-32@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.14.tgz#14bdd4f6b6cfd35c65c835894651ba335c2117da"
integrity sha512-a8rOnS1oWSfkkYWXoD2yXNV4BdbDKA7PNVQ1klqkY9SoSApL7io66w5H44mTLsfyw7G6Z2vLlaLI2nz9MMAowA==
esbuild-freebsd-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48"
integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==
esbuild-linux-64@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.14.tgz#7fd56851b2982fdd0cd8447ee9858c2c5711708a"
integrity sha512-yPZSoMs9W2MC3Dw+6kflKt5FfQm6Dicex9dGIr1OlHRsn3Hm7yGMUTctlkW53KknnZdOdcdd5upxvbxqymczVQ==
esbuild-linux-32@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5"
integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==
esbuild-linux-arm64@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.14.tgz#a55634d70679ba509adeafd68eebb9fd1ec5af6c"
integrity sha512-Lvo391ln9PzC334e+jJ2S0Rt0cxP47eoH5gFyv/E8HhOnEJTvm7A+RRnMjjHnejELacTTfYgFGQYPjLsi/jObQ==
esbuild-linux-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652"
integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
esbuild-linux-arm@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.14.tgz#bb96a99677e608b31ff61f37564326d38e846ca2"
integrity sha512-8chZE4pkKRvJ/M/iwsNQ1KqsRg2RyU5eT/x2flNt/f8F2TVrDreR7I0HEeCR50wLla3B1C3wTIOzQBmjuc6uWg==
esbuild-linux-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b"
integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==
esbuild-linux-mips64le@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.14.tgz#6a55362a8fd1e593dea2ecc41877beed8b8184b9"
integrity sha512-MZhgxbmrWbpY3TOE029O6l5tokG9+Yoj2hW7vdit/d/VnmneqeGrSHADuDL6qXM8L5jaCiaivb4VhsyVCpdAbQ==
esbuild-linux-arm@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59"
integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==
esbuild-linux-ppc64le@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.14.tgz#9e0048587ece0a7f184ab147f20d077098045e7f"
integrity sha512-un7KMwS7fX1Un6BjfSZxTT8L5cV/8Uf4SAhM7WYy2XF8o8TI+uRxxD03svZnRNIPsN2J5cl6qV4n7Iwz+yhhVw==
esbuild-linux-mips64le@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34"
integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==
esbuild-netbsd-64@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.14.tgz#dcab16a4bbcfa16e2e8535dadc5f64fdc891c63b"
integrity sha512-5ekKx/YbOmmlTeNxBjh38Uh5TGn5C4uyqN17i67k18pS3J+U2hTVD7rCxcFcRS1AjNWumkVL3jWqYXadFwMS0Q==
esbuild-linux-ppc64le@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e"
integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==
esbuild-openbsd-64@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.14.tgz#3c7453b155ebb68dc34d5aec3bd6505337bdda08"
integrity sha512-9bzvwewHjct2Cv5XcVoE1yW5YTW12Sk838EYfA46abgnhxGoFSD1mFcaztp5HHC43AsF+hQxbSFG/RilONARUA==
esbuild-linux-riscv64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8"
integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==
esbuild-sunos-64@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.14.tgz#85addf5fef6b5db154a955d4f2e88953359d75ce"
integrity sha512-mjMrZB76M6FmoiTvj/RGWilrioR7gVwtFBRVugr9qLarXMIU1W/pQx+ieEOtflrW61xo8w1fcxyHsVVGRvoQ0w==
esbuild-linux-s390x@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6"
integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==
esbuild-windows-32@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.14.tgz#f77f98f30a5c636c44db2428ecdf9bcbbaedb1a7"
integrity sha512-GZa6mrx2rgfbH/5uHg0Rdw50TuOKbdoKCpEBitzmG5tsXBdce+cOL+iFO5joZc6fDVCLW3Y6tjxmSXRk/v20Hg==
esbuild-netbsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81"
integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==
esbuild-windows-64@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.14.tgz#bc778674c40d65150d12385e0f23eb3a0badbd0d"
integrity sha512-Lsgqah24bT7ClHjLp/Pj3A9wxjhIAJyWQcrOV4jqXAFikmrp2CspA8IkJgw7HFjx6QrJuhpcKVbCAe/xw0i2yw==
esbuild-openbsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b"
integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==
esbuild-windows-arm64@0.13.14:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.14.tgz#91a8dad35ab2c4dd27cd83860742955b25a354d7"
integrity sha512-KP8FHVlWGhM7nzYtURsGnskXb/cBCPTfj0gOKfjKq2tHtYnhDZywsUG57nk7TKhhK0fL11LcejHG3LRW9RF/9A==
esbuild-sunos-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da"
integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==
esbuild@^0.13.2:
version "0.13.14"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.14.tgz#98a3f7f42809abdc2b57c84565d0f713382dc1a5"
integrity sha512-xu4D+1ji9x53ocuomcY+KOrwAnWzhBu/wTEjpdgZ8I1c8i5vboYIeigMdzgY1UowYBKa2vZgVgUB32bu7gkxeg==
esbuild-windows-32@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31"
integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==
esbuild-windows-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4"
integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==
esbuild-windows-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982"
integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==
esbuild@^0.14.47:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2"
integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==
optionalDependencies:
esbuild-android-arm64 "0.13.14"
esbuild-darwin-64 "0.13.14"
esbuild-darwin-arm64 "0.13.14"
esbuild-freebsd-64 "0.13.14"
esbuild-freebsd-arm64 "0.13.14"
esbuild-linux-32 "0.13.14"
esbuild-linux-64 "0.13.14"
esbuild-linux-arm "0.13.14"
esbuild-linux-arm64 "0.13.14"
esbuild-linux-mips64le "0.13.14"
esbuild-linux-ppc64le "0.13.14"
esbuild-netbsd-64 "0.13.14"
esbuild-openbsd-64 "0.13.14"
esbuild-sunos-64 "0.13.14"
esbuild-windows-32 "0.13.14"
esbuild-windows-64 "0.13.14"
esbuild-windows-arm64 "0.13.14"
"@esbuild/linux-loong64" "0.14.54"
esbuild-android-64 "0.14.54"
esbuild-android-arm64 "0.14.54"
esbuild-darwin-64 "0.14.54"
esbuild-darwin-arm64 "0.14.54"
esbuild-freebsd-64 "0.14.54"
esbuild-freebsd-arm64 "0.14.54"
esbuild-linux-32 "0.14.54"
esbuild-linux-64 "0.14.54"
esbuild-linux-arm "0.14.54"
esbuild-linux-arm64 "0.14.54"
esbuild-linux-mips64le "0.14.54"
esbuild-linux-ppc64le "0.14.54"
esbuild-linux-riscv64 "0.14.54"
esbuild-linux-s390x "0.14.54"
esbuild-netbsd-64 "0.14.54"
esbuild-openbsd-64 "0.14.54"
esbuild-sunos-64 "0.14.54"
esbuild-windows-32 "0.14.54"
esbuild-windows-64 "0.14.54"
esbuild-windows-arm64 "0.14.54"
escalade@^3.1.1:
version "3.1.1"
@ -3519,6 +3550,13 @@ is-core-module@^2.2.0:
dependencies:
has "^1.0.3"
is-core-module@^2.9.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed"
integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==
dependencies:
has "^1.0.3"
is-data-descriptor@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
@ -4285,10 +4323,10 @@ kleur@^3.0.3:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
kleur@^4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d"
integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==
kleur@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
lazy-ass@1.6.0, lazy-ass@^1.6.0:
version "1.6.0"
@ -4425,6 +4463,13 @@ magic-string@^0.25.7:
dependencies:
sourcemap-codec "^1.4.4"
magic-string@^0.26.2:
version "0.26.2"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.2.tgz#5331700e4158cd6befda738bb6b0c7b93c0d4432"
integrity sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==
dependencies:
sourcemap-codec "^1.4.8"
make-dir@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
@ -4615,10 +4660,10 @@ nanoid@^2.1.0:
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==
nanoid@^3.1.30:
version "3.1.30"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362"
integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==
nanoid@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
nanomatch@^1.2.9:
version "1.2.13"
@ -4887,7 +4932,7 @@ path-key@^3.0.0, path-key@^3.1.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-parse@^1.0.6:
path-parse@^1.0.6, path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
@ -4948,14 +4993,14 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
postcss@^8.3.8:
version "8.3.11"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.11.tgz#c3beca7ea811cd5e1c4a3ec6d2e7599ef1f8f858"
integrity sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==
postcss@^8.4.16:
version "8.4.16"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
dependencies:
nanoid "^3.1.30"
nanoid "^3.3.4"
picocolors "^1.0.0"
source-map-js "^0.6.2"
source-map-js "^1.0.2"
posthog-js@1.4.5:
version "1.4.5"
@ -5184,11 +5229,6 @@ require-main-filename@^2.0.0:
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
require-relative@^0.8.7:
version "0.8.7"
resolved "https://registry.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de"
integrity sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=
resolve-cwd@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
@ -5206,7 +5246,7 @@ resolve-url@^0.2.1:
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
resolve@^1.10.0, resolve@^1.14.2, resolve@^1.18.1, resolve@^1.20.0:
resolve@^1.10.0, resolve@^1.14.2, resolve@^1.18.1:
version "1.20.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
@ -5214,6 +5254,15 @@ resolve@^1.10.0, resolve@^1.14.2, resolve@^1.18.1, resolve@^1.20.0:
is-core-module "^2.2.0"
path-parse "^1.0.6"
resolve@^1.22.1:
version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
dependencies:
is-core-module "^2.9.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
restore-cursor@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
@ -5262,7 +5311,14 @@ rollup-pluginutils@^2.8.2:
dependencies:
estree-walker "^0.6.1"
rollup@^2.44.0, rollup@^2.57.0:
"rollup@>=2.75.6 <2.77.0 || ~2.77.0":
version "2.77.3"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.77.3.tgz#8f00418d3a2740036e15deb653bed1a90ee0cc12"
integrity sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==
optionalDependencies:
fsevents "~2.3.2"
rollup@^2.44.0:
version "2.60.0"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.60.0.tgz#4ee60ab7bdd0356763f87d7099f413e5460fc193"
integrity sha512-cHdv9GWd58v58rdseC8e8XIaPUo8a9cgZpnCMMDGZFDZKEODOiPPEQFXLriWr/TjXzhPPmG5bkAztPsOARIcGQ==
@ -5475,10 +5531,10 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^3.1.0"
source-map-js@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
source-map-resolve@^0.5.0:
version "0.5.3"
@ -5527,7 +5583,7 @@ source-map@^0.7.3:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
sourcemap-codec@^1.4.4:
sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8:
version "1.4.8"
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
@ -5707,15 +5763,20 @@ supports-hyperlinks@^2.0.0:
has-flag "^4.0.0"
supports-color "^7.0.0"
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
svelte-dnd-action@^0.9.8:
version "0.9.12"
resolved "https://registry.yarnpkg.com/svelte-dnd-action/-/svelte-dnd-action-0.9.12.tgz#78cf33097986488c6d069eca517af473cd998730"
integrity sha512-GlXIB3/56IMR5A0+qUx+FX7Q7n8uCAIeuYdgSBmn9iOlxWc+mgM8P1kNwAKCMSTdQ4IQETVQILNgWVY1KIFzsg==
svelte-hmr@^0.14.7:
version "0.14.7"
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.14.7.tgz#7fa8261c7b225d9409f0a86f3b9ea5c3ca6f6607"
integrity sha512-pDrzgcWSoMaK6AJkBWkmgIsecW0GChxYZSZieIYfCP0v2oPyx2CYU/zm7TBIcjLVUPP714WxmViE9Thht4etog==
svelte-hmr@^0.14.12:
version "0.14.12"
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.14.12.tgz#a127aec02f1896500b10148b2d4d21ddde39973f"
integrity sha512-4QSW/VvXuqVcFZ+RhxiR8/newmwOCTlbYIezvkeN6302YFRE8cXy0naamHcjz8Y9Ce3ITTZtrHrIL0AGfyo61w==
svelte-jester@^1.3.2:
version "1.8.2"
@ -6081,15 +6142,15 @@ verror@1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"
vite@^2.1.5:
version "2.6.14"
resolved "https://registry.yarnpkg.com/vite/-/vite-2.6.14.tgz#35c09a15e4df823410819a2a239ab11efb186271"
integrity sha512-2HA9xGyi+EhY2MXo0+A2dRsqsAG3eFNEVIo12olkWhOmc8LfiM+eMdrXf+Ruje9gdXgvSqjLI9freec1RUM5EA==
vite@^3.0.8:
version "3.0.8"
resolved "https://registry.yarnpkg.com/vite/-/vite-3.0.8.tgz#aa095ad8e3e5da46d9ec7e878f262678965d6531"
integrity sha512-AOZ4eN7mrkJiOLuw8IA7piS4IdOQyQCA81GxGsAQvAZzMRi9ZwGB3TOaYsj4uLAWK46T5L4AfQ6InNGlxX30IQ==
dependencies:
esbuild "^0.13.2"
postcss "^8.3.8"
resolve "^1.20.0"
rollup "^2.57.0"
esbuild "^0.14.47"
postcss "^8.4.16"
resolve "^1.22.1"
rollup ">=2.75.6 <2.77.0 || ~2.77.0"
optionalDependencies:
fsevents "~2.3.2"

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/cli",
"version": "1.3.4-alpha.2",
"version": "1.3.15-alpha.3",
"description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js",
"bin": {
@ -26,14 +26,18 @@
"outputPath": "build"
},
"dependencies": {
"@budibase/backend-core": "1.3.4-alpha.2",
"@budibase/backend-core": "1.3.15-alpha.3",
"@budibase/string-templates": "1.3.15-alpha.3",
"@budibase/types": "1.3.15-alpha.3",
"axios": "0.21.2",
"chalk": "4.1.0",
"cli-progress": "3.11.2",
"commander": "7.1.0",
"docker-compose": "0.23.6",
"dotenv": "16.0.1",
"download": "8.0.0",
"inquirer": "8.0.0",
"joi": "17.6.0",
"lookpath": "1.1.0",
"node-fetch": "2",
"pkg": "5.7.0",

View file

@ -3,6 +3,7 @@ exports.CommandWords = {
HOSTING: "hosting",
ANALYTICS: "analytics",
HELP: "help",
PLUGIN: "plugins",
}
exports.InitTypes = {

View file

@ -0,0 +1 @@
process.env.NO_JS = "1"

27
packages/cli/src/exec.js Normal file
View file

@ -0,0 +1,27 @@
const util = require("util")
const exec = util.promisify(require("child_process").exec)
exports.exec = async (command, dir = "./") => {
const { stdout } = await exec(command, { cwd: dir })
return stdout
}
exports.utilityInstalled = async utilName => {
try {
await exports.exec(`${utilName} --version`)
return true
} catch (err) {
return false
}
}
exports.runPkgCommand = async (command, dir = "./") => {
const yarn = await exports.utilityInstalled("yarn")
const npm = await exports.utilityInstalled("npm")
if (!yarn && !npm) {
throw new Error("Must have yarn or npm installed to run build.")
}
const npmCmd = command === "install" ? `npm ${command}` : `npm run ${command}`
const cmd = yarn ? `yarn ${command}` : npmCmd
await exports.exec(cmd, dir)
}

View file

@ -1,5 +1,6 @@
#!/usr/bin/env node
require("./prebuilds")
require("./environment")
const { getCommands } = require("./options")
const { Command } = require("commander")
const { getHelpDescription } = require("./utils")

View file

@ -1,7 +1,8 @@
const analytics = require("./analytics")
const hosting = require("./hosting")
const backups = require("./backups")
const plugins = require("./plugins")
exports.getCommands = () => {
return [hosting.command, analytics.command, backups.command]
return [hosting.command, analytics.command, backups.command, plugins.command]
}

View file

@ -0,0 +1,132 @@
const Command = require("../structures/Command")
const { CommandWords } = require("../constants")
const { getSkeleton, fleshOutSkeleton } = require("./skeleton")
const questions = require("../questions")
const fs = require("fs")
const { PLUGIN_TYPE_ARR } = require("@budibase/types")
const { validate } = require("@budibase/backend-core/plugins")
const { runPkgCommand } = require("../exec")
const { join } = require("path")
const { success, error, info } = require("../utils")
function checkInPlugin() {
if (!fs.existsSync("package.json")) {
throw new Error(
"Please run in a plugin directory - must contain package.json"
)
}
if (!fs.existsSync("schema.json")) {
throw new Error(
"Please run in a plugin directory - must contain schema.json"
)
}
}
async function init(opts) {
const type = opts["init"] || opts
if (!type || !PLUGIN_TYPE_ARR.includes(type)) {
console.log(
error(
"Please provide a type to init, either 'component' or 'datasource'."
)
)
return
}
console.log(info("Lets get some details about your new plugin:"))
const name = await questions.string("Name", `budibase-${type}`)
if (fs.existsSync(name)) {
console.log(
error("Directory by plugin name already exists, pick a new name.")
)
return
}
const desc = await questions.string(
"Description",
`An amazing Budibase ${type}!`
)
const version = await questions.string("Version", "1.0.0")
// get the skeleton
console.log(info("Retrieving project..."))
await getSkeleton(type, name)
await fleshOutSkeleton(type, name, desc, version)
console.log(info("Installing dependencies..."))
await runPkgCommand("install", join(process.cwd(), name))
console.log(info(`Plugin created in directory "${name}"`))
}
async function verify() {
// will throw errors if not acceptable
checkInPlugin()
console.log(info("Verifying plugin..."))
const schema = fs.readFileSync("schema.json", "utf8")
const pkg = fs.readFileSync("package.json", "utf8")
let name, version
try {
const schemaJson = JSON.parse(schema)
const pkgJson = JSON.parse(pkg)
if (!pkgJson.name || !pkgJson.version || !pkgJson.description) {
throw new Error(
"package.json is missing one of 'name', 'version' or 'description'."
)
}
name = pkgJson.name
version = pkgJson.version
validate(schemaJson)
return { name, version }
} catch (err) {
if (err && err.message && err.message.includes("not valid JSON")) {
console.log(error(`schema.json is not valid JSON: ${err.message}`))
} else {
console.log(error(`Invalid schema/package.json: ${err.message}`))
}
}
}
async function build() {
const verified = await verify()
if (!verified.name) {
return
}
console.log(success("Verified!"))
console.log(info("Building plugin..."))
await runPkgCommand("build")
const output = join("dist", `${verified.name}-${verified.version}.tar.gz`)
console.log(success(`Build complete - output in: ${output}`))
}
async function watch() {
const verified = await verify()
if (!verified.name) {
return
}
const output = join("dist", `${verified.name}-${verified.version}.tar.gz`)
console.log(info(`Watching - build in: ${output}`))
try {
await runPkgCommand("watch")
} catch (err) {
// always errors when user escapes
console.log(success("Watch exited."))
}
}
const command = new Command(`${CommandWords.PLUGIN}`)
.addHelp(
"Custom plugins for Budibase, init, build and verify your components and datasources with this tool."
)
.addSubOption(
"--init [type]",
"Init a new plugin project, with a type of either component or datasource.",
init
)
.addSubOption(
"--build",
"Build your plugin, this will verify and produce a final tarball for your project.",
build
)
.addSubOption(
"--watch",
"Automatically build any changes to your plugin.",
watch
)
exports.command = command

View file

@ -0,0 +1,60 @@
const fetch = require("node-fetch")
const download = require("download")
const fs = require("fs")
const os = require("os")
const { join } = require("path")
const tar = require("tar")
const { processStringSync } = require("@budibase/string-templates")
const HBS_FILES = ["package.json.hbs", "schema.json.hbs", "README.md.hbs"]
async function getSkeletonUrl(type) {
const resp = await fetch(
"https://api.github.com/repos/budibase/budibase-skeleton/releases/latest"
)
if (resp.status >= 300) {
throw new Error("Failed to retrieve skeleton metadata")
}
const json = await resp.json()
for (let asset of json["assets"]) {
if (asset.name && asset.name.includes(type)) {
return asset["browser_download_url"]
}
}
throw new Error("No skeleton found in latest release.")
}
exports.getSkeleton = async (type, name) => {
const url = await getSkeletonUrl(type)
const tarballFile = join(os.tmpdir(), "skeleton.tar.gz")
// download the full skeleton tarball
fs.writeFileSync(tarballFile, await download(url))
fs.mkdirSync(name)
// extract it and get what we need
await tar.extract({
file: tarballFile,
C: name,
})
// clear up
fs.rmSync(tarballFile)
}
exports.fleshOutSkeleton = async (type, name, description, version) => {
for (let file of HBS_FILES) {
const oldFile = join(name, file),
newFile = join(name, file.substring(0, file.length - 4))
const hbsContents = fs.readFileSync(oldFile, "utf8")
if (!hbsContents) {
continue
}
const output = processStringSync(hbsContents, {
name,
description,
version,
})
// write the updated file and remove the HBS file
fs.writeFileSync(newFile, output)
fs.rmSync(oldFile)
}
}

View file

@ -1,11 +1,15 @@
const os = require("os")
const { join } = require("path")
const fs = require("fs")
const { error } = require("./utils")
const PREBUILDS = "prebuilds"
const ARCH = `${os.platform()}-${os.arch()}`
const PREBUILD_DIR = join(process.execPath, "..", PREBUILDS, ARCH)
checkForBinaries()
// running as built CLI pkg bundle
if (!process.argv[0].includes("node")) {
checkForBinaries()
}
function checkForBinaries() {
const readDir = join(__filename, "..", "..", PREBUILDS, ARCH)
@ -22,7 +26,15 @@ function checkForBinaries() {
}
}
function cleanup() {
function cleanup(evt) {
if (evt && evt.errno) {
console.error(
error(
"Failed to run CLI command - please report with the following message:"
)
)
console.error(error(evt))
}
if (fs.existsSync(PREBUILD_DIR)) {
fs.rmSync(PREBUILD_DIR, { recursive: true })
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/client",
"version": "1.3.4-alpha.2",
"version": "1.3.15-alpha.3",
"license": "MPL-2.0",
"module": "dist/budibase-client.js",
"main": "dist/budibase-client.js",
@ -19,9 +19,9 @@
"dev:builder": "rollup -cw"
},
"dependencies": {
"@budibase/bbui": "1.3.4-alpha.2",
"@budibase/frontend-core": "1.3.4-alpha.2",
"@budibase/string-templates": "1.3.4-alpha.2",
"@budibase/bbui": "1.3.15-alpha.3",
"@budibase/frontend-core": "1.3.15-alpha.3",
"@budibase/string-templates": "1.3.15-alpha.3",
"@spectrum-css/button": "^3.0.3",
"@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3",
@ -39,6 +39,7 @@
"sanitize-html": "^2.7.0",
"screenfull": "^6.0.1",
"shortid": "^2.2.15",
"socket.io-client": "^4.5.1",
"svelte": "^3.49.0",
"svelte-apexcharts": "^1.0.2",
"svelte-flatpickr": "^3.1.0",

View file

@ -89,6 +89,14 @@
})
</script>
<svelte:head>
{#if $builderStore.usedPlugins?.length}
{#each $builderStore.usedPlugins as plugin (plugin.hash)}
<script src={`/plugins/${plugin.jsUrl}?r=${plugin.hash || ""}`}></script>
{/each}
{/if}
</svelte:head>
{#if dataLoaded}
<div
id="spectrum-root"

View file

@ -11,8 +11,6 @@
<script>
import { getContext, setContext, onMount, onDestroy } from "svelte"
import { writable, get } from "svelte/store"
import * as AppComponents from "components/app"
import Router from "./Router.svelte"
import {
enrichProps,
propsAreSame,
@ -127,7 +125,9 @@
// Empty components are those which accept children but do not have any.
// Empty states can be shown for these components, but can be disabled
// in the component manifest.
$: empty = interactive && !children.length && hasChildren
$: empty =
(interactive && !children.length && hasChildren) ||
hasMissingRequiredSettings
$: emptyState = empty && showEmptyState
// Enrich component settings
@ -165,14 +165,14 @@
missingRequiredSettings,
})
const initialise = instance => {
const initialise = (instance, force = false) => {
if (instance == null) {
return
}
// Ensure we're processing a new instance
const instanceKey = Helpers.hashString(JSON.stringify(instance))
if (instanceKey === lastInstanceKey) {
if (instanceKey === lastInstanceKey && !force) {
return
} else {
lastInstanceKey = instanceKey
@ -180,7 +180,7 @@
// Pull definition and constructor
const component = instance._component
constructor = getComponentConstructor(component)
constructor = componentStore.actions.getComponentConstructor(component)
definition = componentStore.actions.getComponentDefinition(component)
if (!definition) {
return
@ -237,16 +237,6 @@
})
}
// Gets the component constructor for the specified component
const getComponentConstructor = component => {
const split = component?.split("/")
const name = split?.[split.length - 1]
if (name === "screenslot" && !insideScreenslot) {
return Router
}
return AppComponents[name]
}
const getSettingsDefinitionMap = settingsDefinition => {
let map = {}
settingsDefinition?.forEach(setting => {
@ -419,9 +409,11 @@
!componentStore.actions.isComponentRegistered(id)
) {
componentStore.actions.registerInstance(id, {
component: instance._component,
getSettings: () => cachedSettings,
getRawSettings: () => ({ ...staticSettings, ...dynamicSettings }),
getDataContext: () => get(context),
reload: () => initialise(instance, true),
})
}
})

View file

@ -2,28 +2,25 @@
import { getContext } from "svelte"
import { builderStore } from "stores"
const { styleable } = getContext("sdk")
const component = getContext("component")
$: requiredSetting = $component.missingRequiredSettings?.[0]
</script>
{#if $builderStore.inBuilder && requiredSetting}
<div use:styleable={$component.styles}>
<div class="component-placeholder">
<span>
Add the <mark>{requiredSetting.label}</mark> setting to start using your
component -
</span>
<span
class="spectrum-Link"
on:click={() => {
builderStore.actions.highlightSetting(requiredSetting.key)
}}
>
Show me
</span>
</div>
<div class="component-placeholder">
<span>
Add the <mark>{requiredSetting.label}</mark> setting to start using your component
-
</span>
<span
class="spectrum-Link"
on:click={() => {
builderStore.actions.highlightSetting(requiredSetting.key)
}}
>
Show me
</span>
</div>
{/if}

View file

@ -1,7 +1,6 @@
<script>
import { getContext } from "svelte"
import { ProgressCircle, Pagination } from "@budibase/bbui"
import Placeholder from "./Placeholder.svelte"
import { fetchData, LuceneUtils } from "@budibase/frontend-core"
export let dataSource
@ -133,11 +132,7 @@
<ProgressCircle />
</div>
{:else}
{#if $component.emptyState}
<Placeholder />
{:else}
<slot />
{/if}
<slot />
{#if paginate && $fetch.supportsPagination}
<div class="pagination">
<Pagination

View file

@ -19,7 +19,10 @@
label="Active screen"
value={$screenStore.activeScreen?.routing.route}
/>
<DevToolsStat label="Components" value={$componentStore.mountedComponents} />
<DevToolsStat
label="Components"
value={$componentStore.mountedComponentCount}
/>
<DevToolsStat label="User" value={$authStore.email} />
<DevToolsStat label="Role" value={$authStore.roleId} />
</Layout>

View file

@ -1,7 +1,20 @@
import ClientApp from "./components/ClientApp.svelte"
import { builderStore, appStore, devToolsStore, blockStore } from "./stores"
import {
builderStore,
appStore,
devToolsStore,
blockStore,
componentStore,
} from "./stores"
import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-rollup.js"
import { get } from "svelte/store"
import { initWebsocket } from "./websocket.js"
// Provide svelte and svelte/internal as globals for custom components
import * as svelte from "svelte"
import * as internal from "svelte/internal"
window.svelte_internal = internal
window.svelte = svelte
// Initialise spectrum icons
loadSpectrumIcons()
@ -21,6 +34,8 @@ const loadBudibase = () => {
previewDevice: window["##BUDIBASE_PREVIEW_DEVICE##"],
navigation: window["##BUDIBASE_PREVIEW_NAVIGATION##"],
hiddenComponentIds: window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"],
usedPlugins: window["##BUDIBASE_USED_PLUGINS##"],
location: window["##BUDIBASE_LOCATION##"],
})
// Set app ID - this window flag is set by both the preview and the real
@ -43,6 +58,21 @@ const loadBudibase = () => {
}
}
// Register any custom components
if (window["##BUDIBASE_CUSTOM_COMPONENTS##"]) {
window["##BUDIBASE_CUSTOM_COMPONENTS##"].forEach(component => {
componentStore.actions.registerCustomComponent(component)
})
}
// Make a callback available for custom component bundles to register
// themselves at runtime
window.registerCustomComponent =
componentStore.actions.registerCustomComponent
// Initialise websocket
initWebsocket()
// Create app if one hasn't been created yet
if (!app) {
app = new ClientApp({

View file

@ -19,6 +19,7 @@ const createBuilderStore = () => {
isDragging: false,
navigation: null,
hiddenComponentIds: [],
usedPlugins: null,
// Legacy - allow the builder to specify a layout
layout: null,
@ -87,6 +88,20 @@ const createBuilderStore = () => {
ejectBlock: (id, definition) => {
dispatchEvent("eject-block", { id, definition })
},
updateUsedPlugin: (name, hash) => {
// Check if we used this plugin
const used = get(store)?.usedPlugins?.find(x => x.name === name)
if (used) {
store.update(state => {
state.usedPlugins = state.usedPlugins.filter(x => x.name !== name)
state.usedPlugins.push({
...used,
hash,
})
return state
})
}
},
}
return {
...store,

View file

@ -4,9 +4,17 @@ import { findComponentById, findComponentPathById } from "../utils/components"
import { devToolsStore } from "./devTools"
import { screenStore } from "./screens"
import { builderStore } from "./builder"
import Router from "../components/Router.svelte"
import * as AppComponents from "../components/app/index.js"
const budibasePrefix = "@budibase/standard-components/"
const createComponentStore = () => {
const store = writable({})
const store = writable({
customComponentManifest: {},
customComponentMap: {},
mountedComponents: {},
})
const derivedStore = derived(
[store, builderStore, devToolsStore, screenStore],
@ -25,41 +33,65 @@ const createComponentStore = () => {
asset = $screenState.activeScreen
}
const component = findComponentById(asset?.props, selectedComponentId)
const prefix = "@budibase/standard-components/"
const type = component?._component?.replace(prefix, "")
const definition = type ? Manifest[type] : null
const definition = getComponentDefinition(component?._component)
// Derive the selected component path
const path =
findComponentPathById(asset?.props, selectedComponentId) || []
return {
selectedComponentInstance: $store[selectedComponentId],
customComponentManifest: $store.customComponentManifest,
selectedComponentInstance:
$store.mountedComponents[selectedComponentId],
selectedComponent: component,
selectedComponentDefinition: definition,
selectedComponentPath: path?.map(component => component._id),
mountedComponents: Object.keys($store).length,
mountedComponentCount: Object.keys($store.mountedComponents).length,
currentAsset: asset,
}
}
)
const registerInstance = (id, instance) => {
store.update(state => ({
...state,
[id]: instance,
}))
store.update(state => {
// If this is a custom component, flag it so we can reload this component
// later if required
const component = instance.component
if (component?.startsWith("plugin")) {
if (!state.customComponentMap[component]) {
state.customComponentMap[component] = [id]
} else {
state.customComponentMap[component].push(id)
}
}
// Register to mounted components
state.mountedComponents[id] = instance
return state
})
}
const unregisterInstance = id => {
store.update(state => {
delete state[id]
// Remove from custom component map if required
const component = state.mountedComponents[id]?.instance?.component
let customComponentMap = state.customComponentMap
if (component?.startsWith("plugin")) {
customComponentMap[component] = customComponentMap[component].filter(
x => {
return x !== id
}
)
}
// Remove from mounted components
delete state.mountedComponents[id]
return state
})
}
const isComponentRegistered = id => {
return get(store)[id] != null
return get(store).mountedComponents[id] != null
}
const getComponentById = id => {
@ -68,9 +100,66 @@ const createComponentStore = () => {
}
const getComponentDefinition = type => {
const prefix = "@budibase/standard-components/"
type = type?.replace(prefix, "")
return type ? Manifest[type] : null
if (!type) {
return null
}
// Screenslot is an edge case
if (type === "screenslot") {
type = `${budibasePrefix}${type}`
}
// Handle built-in components
if (type.startsWith(budibasePrefix)) {
type = type.replace(budibasePrefix, "")
return type ? Manifest[type] : null
}
// Handle custom components
const { customComponentManifest } = get(store)
return customComponentManifest?.[type]?.schema?.schema
}
const getComponentConstructor = type => {
if (!type) {
return null
}
if (type === "screenslot") {
return Router
}
// Handle budibase components
if (type.startsWith(budibasePrefix)) {
const split = type.split("/")
const name = split[split.length - 1]
return AppComponents[name]
}
// Handle custom components
const { customComponentManifest } = get(store)
return customComponentManifest?.[type]?.Component
}
const registerCustomComponent = ({ Component, schema, version }) => {
if (!Component || !schema?.schema?.name || !version) {
return
}
const component = `plugin/${schema.schema.name}`
store.update(state => {
state.customComponentManifest[component] = {
Component,
schema,
}
return state
})
// Reload any mounted instances of this custom component
const state = get(store)
if (state.customComponentMap[component]?.length) {
state.customComponentMap[component].forEach(id => {
state.mountedComponents[id]?.reload()
})
}
}
return {
@ -81,6 +170,8 @@ const createComponentStore = () => {
isComponentRegistered,
getComponentById,
getComponentDefinition,
getComponentConstructor,
registerCustomComponent,
},
}
}

View file

@ -0,0 +1,26 @@
import { builderStore } from "./stores/index.js"
import { get } from "svelte/store"
import { io } from "socket.io-client"
export const initWebsocket = () => {
const { inBuilder, location } = get(builderStore)
// Only connect when we're inside the builder preview, for now
if (!inBuilder || !location) {
return
}
// Initialise connection
const tls = location.protocol === "https:"
const proto = tls ? "wss:" : "ws:"
const host = location.hostname
const port = location.port || (tls ? 443 : 80)
const socket = io(`${proto}//${host}:${port}`, {
path: "/socket/client",
})
// Event handlers
socket.on("plugin-update", data => {
builderStore.actions.updateUsedPlugin(data.name, data.hash)
})
}

View file

@ -113,6 +113,11 @@
estree-walker "^1.0.1"
picomatch "^2.2.2"
"@socket.io/component-emitter@~3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
"@spectrum-css/button@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/button/-/button-3.0.3.tgz#2df1efaab6c7e0b3b06cb4b59e1eae59c7f1fc84"
@ -469,6 +474,13 @@ dayjs@^1.10.5:
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.8.tgz#267df4bc6276fcb33c04a6735287e3f429abec41"
integrity sha512-wbNwDfBHHur9UOzNUjeKUOJ0fCb0a52Wx0xInmQ7Y8FstyajiV1NmK1e00cxsr9YrE9r7yAChE0VvpuY5Rnlow==
debug@~4.3.1, debug@~4.3.2:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
deepmerge@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
@ -536,6 +548,22 @@ emojis-list@^3.0.0:
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
engine.io-client@~6.2.1:
version "6.2.2"
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.2.2.tgz#c6c5243167f5943dcd9c4abee1bfc634aa2cbdd0"
integrity sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.1"
engine.io-parser "~5.0.3"
ws "~8.2.3"
xmlhttprequest-ssl "~2.0.0"
engine.io-parser@~5.0.3:
version "5.0.4"
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0"
integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==
entities@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
@ -824,6 +852,11 @@ minimist@^1.2.0:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nanoid@^2.1.0:
version "2.1.11"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
@ -1389,6 +1422,24 @@ slash@^3.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
socket.io-client@^4.5.1:
version "4.5.1"
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.1.tgz#cab8da71976a300d3090414e28c2203a47884d84"
integrity sha512-e6nLVgiRYatS+AHXnOnGi4ocOpubvOUCGhyWw8v+/FxW8saHkinG6Dfhi9TU0Kt/8mwJIAASxvw6eujQmjdZVA==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.2"
engine.io-client "~6.2.1"
socket.io-parser "~4.2.0"
socket.io-parser@~4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5"
integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.1"
source-map-js@^1.0.1, source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
@ -1598,6 +1649,16 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
ws@~8.2.3:
version "8.2.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
xmlhttprequest-ssl@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"

View file

@ -1,12 +1,12 @@
{
"name": "@budibase/frontend-core",
"version": "1.3.4-alpha.2",
"version": "1.3.15-alpha.3",
"description": "Budibase frontend core libraries used in builder and client",
"author": "Budibase",
"license": "MPL-2.0",
"svelte": "src/index.js",
"dependencies": {
"@budibase/bbui": "1.3.4-alpha.2",
"@budibase/bbui": "1.3.15-alpha.3",
"lodash": "^4.17.21",
"svelte": "^3.46.2"
}

View file

@ -11,10 +11,14 @@ export const buildDatasourceEndpoints = API => ({
/**
* Prompts the server to build the schema for a datasource.
* @param datasourceId the datasource ID to build the schema for
* @param tablesFilter list of specific table names to be build the schema
*/
buildDatasourceSchema: async datasourceId => {
buildDatasourceSchema: async ({ datasourceId, tablesFilter }) => {
return await API.post({
url: `/api/datasources/${datasourceId}/schema`,
body: {
tablesFilter,
},
})
},

View file

@ -24,6 +24,7 @@ import { buildSelfEndpoints } from "./self"
import { buildViewEndpoints } from "./views"
import { buildLicensingEndpoints } from "./licensing"
import { buildGroupsEndpoints } from "./groups"
import { buildPluginEndpoints } from "./plugins"
const defaultAPIClientConfig = {
/**
@ -243,5 +244,6 @@ export const createAPIClient = config => {
...buildSelfEndpoints(API),
...buildLicensingEndpoints(API),
...buildGroupsEndpoints(API),
...buildPluginEndpoints(API),
}
}

View file

@ -0,0 +1,44 @@
export const buildPluginEndpoints = API => ({
/**
* Uploads a plugin tarball bundle
* @param data the plugin tarball bundle to upload
*/
uploadPlugin: async data => {
return await API.post({
url: `/api/plugin/upload`,
body: data,
json: false,
})
},
/**
* Creates a plugin from URL, Github or NPM
*/
createPlugin: async data => {
return await API.post({
url: `/api/plugin`,
body: data,
})
},
/**
* Gets a list of all plugins
*/
getPlugins: async () => {
return await API.get({
url: "/api/plugin",
})
},
/**
* Deletes a plugin.
* @param pluginId the ID of the plugin to delete
*
* * @param pluginId the revision of the plugin to delete
*/
deletePlugin: async pluginId => {
return await API.delete({
url: `/api/plugin/${pluginId}`,
})
},
})

View file

@ -80,6 +80,19 @@ const cleanupQuery = query => {
return query
}
/**
* Removes a numeric prefix on field names designed to give fields uniqueness
*/
const removeKeyNumbering = key => {
if (typeof key === "string" && key.match(/\d[0-9]*:/g) != null) {
const parts = key.split(":")
parts.shift()
return parts.join(":")
} else {
return key
}
}
/**
* Builds a lucene JSON query from the filter structure generated in the builder
* @param filter the builder filter structure
@ -194,7 +207,7 @@ export const runLuceneQuery = (docs, query) => {
const filters = Object.entries(query[type] || {})
for (let i = 0; i < filters.length; i++) {
const [key, testValue] = filters[i]
const docValue = Helpers.deepGet(doc, key)
const docValue = Helpers.deepGet(doc, removeKeyNumbering(key))
if (failFn(docValue, testValue)) {
return false
}

View file

@ -33,7 +33,7 @@ module MongoMock {
})
}
mongodb.ObjectID = require("mongodb").ObjectID
mongodb.ObjectID = jest.requireActual("mongodb").ObjectID
module.exports = mongodb
}

View file

@ -1,7 +1,7 @@
{
"name": "@budibase/server",
"email": "hi@budibase.com",
"version": "1.3.4-alpha.2",
"version": "1.3.15-alpha.3",
"description": "Budibase Web Server",
"main": "src/index.ts",
"repository": {
@ -77,11 +77,11 @@
"license": "GPL-3.0",
"dependencies": {
"@apidevtools/swagger-parser": "10.0.3",
"@budibase/backend-core": "1.3.4-alpha.2",
"@budibase/client": "1.3.4-alpha.2",
"@budibase/pro": "1.3.4-alpha.2",
"@budibase/string-templates": "1.3.4-alpha.2",
"@budibase/types": "1.3.4-alpha.2",
"@budibase/backend-core": "1.3.15-alpha.3",
"@budibase/client": "1.3.15-alpha.3",
"@budibase/pro": "1.3.15-alpha.3",
"@budibase/string-templates": "1.3.15-alpha.3",
"@budibase/types": "1.3.15-alpha.3",
"@bull-board/api": "3.7.0",
"@bull-board/koa": "3.9.4",
"@elastic/elasticsearch": "7.10.0",
@ -95,6 +95,7 @@
"bcryptjs": "2.4.3",
"bull": "4.8.5",
"chmodr": "1.2.0",
"chokidar": "3.5.3",
"csvtojson": "2.0.10",
"curlconverter": "3.21.0",
"dotenv": "8.2.0",
@ -138,8 +139,10 @@
"redis": "4",
"server-destroy": "1.0.1",
"snowflake-promise": "^4.5.0",
"socket.io": "^4.5.1",
"svelte": "3.49.0",
"swagger-parser": "10.0.3",
"tar": "6.1.11",
"to-json-schema": "0.2.5",
"uuid": "3.3.2",
"validate.js": "0.13.1",

View file

@ -58,6 +58,7 @@ async function init() {
DEPLOYMENT_ENVIRONMENT: "development",
BB_ADMIN_USER_EMAIL: "",
BB_ADMIN_USER_PASSWORD: "",
PLUGINS_DIR: "",
}
let envFile = ""
Object.keys(envFileJson).forEach(key => {

Some files were not shown because too many files have changed in this diff Show more