diff --git a/lerna.json b/lerna.json index 9a771b2983..1249d4508f 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.0.6", + "version": "2.0.8", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index bd5a839edc..dfa9d0fd7f 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "2.0.6", + "version": "2.0.8", "description": "Budibase backend core libraries used in server and worker", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -20,7 +20,7 @@ "test:watch": "jest --watchAll" }, "dependencies": { - "@budibase/types": "^2.0.6", + "@budibase/types": "^2.0.8", "@shopify/jest-koa-mocks": "5.0.1", "@techpass/passport-openidconnect": "0.3.2", "aws-sdk": "2.1030.0", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 6ebe70be0b..c5ad85c419 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "2.0.6", + "version": "2.0.8", "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": "^2.0.6", + "@budibase/string-templates": "^2.0.8", "@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/avatar": "^3.0.2", diff --git a/packages/builder/package.json b/packages/builder/package.json index a7d9d7d264..2638b5ba1d 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "2.0.6", + "version": "2.0.8", "license": "GPL-3.0", "private": true, "scripts": { @@ -71,10 +71,10 @@ } }, "dependencies": { - "@budibase/bbui": "^2.0.6", - "@budibase/client": "^2.0.6", - "@budibase/frontend-core": "^2.0.6", - "@budibase/string-templates": "^2.0.6", + "@budibase/bbui": "^2.0.8", + "@budibase/client": "^2.0.8", + "@budibase/frontend-core": "^2.0.8", + "@budibase/string-templates": "^2.0.8", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentTree.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentTree.svelte index 31d6eb132b..427fd98775 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentTree.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentTree.svelte @@ -18,6 +18,8 @@ let closedNodes = {} + $: currentScreen = get(selectedScreen) + $: filteredComponents = components?.filter(component => { return ( !$store.componentToPaste?.isCut || @@ -68,9 +70,27 @@ closedNodes = closedNodes } - const onDrop = async e => { + const onDrop = async (e, component) => { e.stopPropagation() try { + const compDef = store.actions.components.getDefinition( + $dndStore.source?._component + ) + const compTypeName = compDef.name.toLowerCase() + const path = findComponentPath(currentScreen.props, component._id) + + for (let pathComp of path) { + const pathCompDef = store.actions.components.getDefinition( + pathComp?._component + ) + if (pathCompDef?.illegalChildren?.indexOf(compTypeName) > -1) { + notifications.warning( + `${compDef.name} cannot be a child of ${pathCompDef.name} (${pathComp._instanceName})` + ) + return + } + } + await dndStore.actions.drop() } catch (error) { console.error(error) @@ -114,7 +134,9 @@ on:dragstart={() => dndStore.actions.dragstart(component)} on:dragover={dragover(component, index)} on:iconClick={() => toggleNodeOpen(component._id)} - on:drop={onDrop} + on:drop={e => { + onDrop(e, component) + }} text={getComponentText(component)} icon={getComponentIcon(component)} withArrow={componentHasChildren(component)} diff --git a/packages/cli/package.json b/packages/cli/package.json index 1d42e01246..f4a7749a8a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "2.0.6", + "version": "2.0.8", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { @@ -26,9 +26,9 @@ "outputPath": "build" }, "dependencies": { - "@budibase/backend-core": "^2.0.6", - "@budibase/string-templates": "^2.0.6", - "@budibase/types": "^2.0.6", + "@budibase/backend-core": "^2.0.8", + "@budibase/string-templates": "^2.0.8", + "@budibase/types": "^2.0.8", "axios": "0.21.2", "chalk": "4.1.0", "cli-progress": "3.11.2", @@ -36,6 +36,7 @@ "docker-compose": "0.23.6", "dotenv": "16.0.1", "download": "8.0.0", + "find-free-port": "^2.0.0", "inquirer": "8.0.0", "joi": "17.6.0", "lookpath": "1.1.0", @@ -45,7 +46,8 @@ "pouchdb": "7.3.0", "pouchdb-replication-stream": "1.2.9", "randomstring": "1.1.5", - "tar": "6.1.11" + "tar": "6.1.11", + "yaml": "^2.1.1" }, "devDependencies": { "copyfiles": "^2.4.1", diff --git a/packages/cli/src/hosting/genUser.js b/packages/cli/src/hosting/genUser.js new file mode 100644 index 0000000000..b6e078c8bd --- /dev/null +++ b/packages/cli/src/hosting/genUser.js @@ -0,0 +1,17 @@ +const { success } = require("../utils") +const { updateDockerComposeService } = require("./utils") +const randomString = require("randomstring") + +exports.generateUser = async () => { + const email = "admin@admin.com" + const password = randomString.generate({ length: 6 }) + updateDockerComposeService(service => { + service.environment["BB_ADMIN_USER_EMAIL"] = email + service.environment["BB_ADMIN_USER_PASSWORD"] = password + }) + console.log( + success( + `User admin credentials configured, access with email: ${email} - password: ${password}` + ) + ) +} diff --git a/packages/cli/src/hosting/index.js b/packages/cli/src/hosting/index.js index ae62c45992..d8133c4959 100644 --- a/packages/cli/src/hosting/index.js +++ b/packages/cli/src/hosting/index.js @@ -1,164 +1,18 @@ const Command = require("../structures/Command") -const { CommandWords, InitTypes, AnalyticsEvents } = require("../constants") -const { lookpath } = require("lookpath") -const { - downloadFile, - logErrorToFile, - success, - info, - parseEnv, -} = require("../utils") -const { confirmation } = require("../questions") -const fs = require("fs") -const compose = require("docker-compose") -const makeEnv = require("./makeEnv") -const axios = require("axios") -const { captureEvent } = require("../events") - -const BUDIBASE_SERVICES = ["app-service", "worker-service", "proxy-service"] -const ERROR_FILE = "docker-error.log" -const FILE_URLS = [ - "https://raw.githubusercontent.com/Budibase/budibase/master/hosting/docker-compose.yaml", -] -const DO_USER_DATA_URL = "http://169.254.169.254/metadata/v1/user-data" - -async function downloadFiles() { - const promises = [] - for (let url of FILE_URLS) { - const fileName = url.split("/").slice(-1)[0] - promises.push(downloadFile(url, `./${fileName}`)) - } - await Promise.all(promises) -} - -async function checkDockerConfigured() { - const error = - "docker/docker-compose has not been installed, please follow instructions at: https://docs.budibase.com/docs/docker-compose" - const docker = await lookpath("docker") - const compose = await lookpath("docker-compose") - if (!docker || !compose) { - throw error - } -} - -function checkInitComplete() { - if (!fs.existsSync(makeEnv.filePath)) { - throw "Please run the hosting --init command before any other hosting command." - } -} - -async function handleError(func) { - try { - await func() - } catch (err) { - if (err && err.err) { - logErrorToFile(ERROR_FILE, err.err) - } - throw `Failed to start - logs written to file: ${ERROR_FILE}` - } -} - -async function init(type) { - const isQuick = type === InitTypes.QUICK || type === InitTypes.DIGITAL_OCEAN - await checkDockerConfigured() - if (!isQuick) { - const shouldContinue = await confirmation( - "This will create multiple files in current directory, should continue?" - ) - if (!shouldContinue) { - console.log("Stopping.") - return - } - } - captureEvent(AnalyticsEvents.SelfHostInit, { - type, - }) - await downloadFiles() - const config = isQuick ? makeEnv.QUICK_CONFIG : {} - if (type === InitTypes.DIGITAL_OCEAN) { - try { - const output = await axios.get(DO_USER_DATA_URL) - const response = parseEnv(output.data) - for (let [key, value] of Object.entries(makeEnv.ConfigMap)) { - if (response[key]) { - config[value] = response[key] - } - } - } catch (err) { - // don't need to handle error, just don't do anything - } - } - await makeEnv.make(config) -} - -async function start() { - await checkDockerConfigured() - checkInitComplete() - console.log( - info( - "Starting services, this may take a moment - first time this may take a few minutes to download images." - ) - ) - const port = makeEnv.get("MAIN_PORT") - await handleError(async () => { - // need to log as it makes it more clear - await compose.upAll({ cwd: "./", log: true }) - }) - console.log( - success( - `Services started, please go to http://localhost:${port} for next steps.` - ) - ) -} - -async function status() { - await checkDockerConfigured() - checkInitComplete() - console.log(info("Budibase status")) - await handleError(async () => { - const response = await compose.ps() - console.log(response.out) - }) -} - -async function stop() { - await checkDockerConfigured() - checkInitComplete() - console.log(info("Stopping services, this may take a moment.")) - await handleError(async () => { - await compose.stop() - }) - console.log(success("Services have been stopped successfully.")) -} - -async function update() { - await checkDockerConfigured() - checkInitComplete() - if (await confirmation("Do you wish to update you docker-compose.yaml?")) { - await downloadFiles() - } - await handleError(async () => { - const status = await compose.ps() - const parts = status.out.split("\n") - const isUp = parts[2] && parts[2].indexOf("Up") !== -1 - if (isUp) { - console.log(info("Stopping services, this may take a moment.")) - await compose.stop() - } - console.log(info("Beginning update, this may take a few minutes.")) - await compose.pullMany(BUDIBASE_SERVICES, { log: true }) - if (isUp) { - console.log(success("Update complete, restarting services...")) - await start() - } - }) -} +const { CommandWords } = require("../constants") +const { init } = require("./init") +const { start } = require("./start") +const { stop } = require("./stop") +const { status } = require("./status") +const { update } = require("./update") +const { generateUser } = require("./genUser") +const { watchPlugins } = require("./watch") const command = new Command(`${CommandWords.HOSTING}`) .addHelp("Controls self hosting on the Budibase platform.") .addSubOption( "--init [type]", - "Configure a self hosted platform in current directory, type can be unspecified or 'quick'.", + "Configure a self hosted platform in current directory, type can be unspecified, 'quick' or 'single'.", init ) .addSubOption( @@ -181,5 +35,16 @@ const command = new Command(`${CommandWords.HOSTING}`) "Update the Budibase images to the latest version.", update ) + .addSubOption( + "--watch-plugin-dir [directory]", + "Add plugin directory watching to a Budibase install.", + watchPlugins + ) + .addSubOption( + "--gen-user", + "Create an admin user automatically as part of first start.", + generateUser + ) + .addSubOption("--single", "Specify this with init to use the single image.") exports.command = command diff --git a/packages/cli/src/hosting/init.js b/packages/cli/src/hosting/init.js new file mode 100644 index 0000000000..37dfe9b4db --- /dev/null +++ b/packages/cli/src/hosting/init.js @@ -0,0 +1,73 @@ +const { InitTypes, AnalyticsEvents } = require("../constants") +const { confirmation } = require("../questions") +const { captureEvent } = require("../events") +const makeFiles = require("./makeFiles") +const axios = require("axios") +const { parseEnv } = require("../utils") +const { checkDockerConfigured, downloadFiles } = require("./utils") +const { watchPlugins } = require("./watch") +const { generateUser } = require("./genUser") + +const DO_USER_DATA_URL = "http://169.254.169.254/metadata/v1/user-data" + +async function getInitConfig(type, isQuick, port) { + const config = isQuick ? makeFiles.QUICK_CONFIG : {} + if (type === InitTypes.DIGITAL_OCEAN) { + try { + const output = await axios.get(DO_USER_DATA_URL) + const response = parseEnv(output.data) + for (let [key, value] of Object.entries(makeFiles.ConfigMap)) { + if (response[key]) { + config[value] = response[key] + } + } + } catch (err) { + // don't need to handle error, just don't do anything + } + } + // override port + if (port) { + config[makeFiles.ConfigMap.MAIN_PORT] = port + } + return config +} + +exports.init = async opts => { + let type, isSingle, watchDir, genUser, port + if (typeof opts === "string") { + type = opts + } else { + type = opts["init"] + isSingle = opts["single"] + watchDir = opts["watchPluginDir"] + genUser = opts["genUser"] + port = opts["port"] + } + const isQuick = type === InitTypes.QUICK || type === InitTypes.DIGITAL_OCEAN + await checkDockerConfigured() + if (!isQuick) { + const shouldContinue = await confirmation( + "This will create multiple files in current directory, should continue?" + ) + if (!shouldContinue) { + console.log("Stopping.") + return + } + } + captureEvent(AnalyticsEvents.SelfHostInit, { + type, + }) + const config = await getInitConfig(type, isQuick, port) + if (!isSingle) { + await downloadFiles() + await makeFiles.makeEnv(config) + } else { + await makeFiles.makeSingleCompose(config) + } + if (watchDir) { + await watchPlugins(watchDir) + } + if (genUser) { + await generateUser() + } +} diff --git a/packages/cli/src/hosting/makeEnv.js b/packages/cli/src/hosting/makeEnv.js deleted file mode 100644 index d1d23999f8..0000000000 --- a/packages/cli/src/hosting/makeEnv.js +++ /dev/null @@ -1,66 +0,0 @@ -const { number } = require("../questions") -const { success } = require("../utils") -const fs = require("fs") -const path = require("path") -const randomString = require("randomstring") - -const FILE_PATH = path.resolve("./.env") - -function getContents(port) { - return ` -# Use the main port in the builder for your self hosting URL, e.g. localhost:10000 -MAIN_PORT=${port} - -# This section contains all secrets pertaining to the system -JWT_SECRET=${randomString.generate()} -MINIO_ACCESS_KEY=${randomString.generate()} -MINIO_SECRET_KEY=${randomString.generate()} -COUCH_DB_PASSWORD=${randomString.generate()} -COUCH_DB_USER=${randomString.generate()} -REDIS_PASSWORD=${randomString.generate()} -INTERNAL_API_KEY=${randomString.generate()} - -# This section contains variables that do not need to be altered under normal circumstances -APP_PORT=4002 -WORKER_PORT=4003 -MINIO_PORT=4004 -COUCH_DB_PORT=4005 -REDIS_PORT=6379 -WATCHTOWER_PORT=6161 -BUDIBASE_ENVIRONMENT=PRODUCTION` -} - -module.exports.filePath = FILE_PATH -module.exports.ConfigMap = { - MAIN_PORT: "port", -} -module.exports.QUICK_CONFIG = { - key: "budibase", - port: 10000, -} - -module.exports.make = async (inputs = {}) => { - const hostingPort = - inputs.port || - (await number( - "Please enter the port on which you want your installation to run: ", - 10000 - )) - const fileContents = getContents(hostingPort) - fs.writeFileSync(FILE_PATH, fileContents) - console.log( - success( - "Configuration has been written successfully - please check .env file for more details." - ) - ) -} - -module.exports.get = property => { - const props = fs.readFileSync(FILE_PATH, "utf8").split(property) - if (props[0].charAt(0) === "=") { - property = props[0] - } else { - property = props[1] - } - return property.split("=")[1].split("\n")[0] -} diff --git a/packages/cli/src/hosting/makeFiles.js b/packages/cli/src/hosting/makeFiles.js new file mode 100644 index 0000000000..0723d5e03d --- /dev/null +++ b/packages/cli/src/hosting/makeFiles.js @@ -0,0 +1,135 @@ +const { number } = require("../questions") +const { success, stringifyToDotEnv } = require("../utils") +const fs = require("fs") +const path = require("path") +const randomString = require("randomstring") +const yaml = require("yaml") +const { getAppService } = require("./utils") + +const SINGLE_IMAGE = "budibase/budibase:latest" +const VOL_NAME = "budibase_data" +const COMPOSE_PATH = path.resolve("./docker-compose.yaml") +const ENV_PATH = path.resolve("./.env") + +function getSecrets(opts = { single: false }) { + const secrets = [ + "JWT_SECRET", + "MINIO_ACCESS_KEY", + "MINIO_SECRET_KEY", + "REDIS_PASSWORD", + "INTERNAL_API_KEY", + ] + const obj = {} + secrets.forEach(secret => (obj[secret] = randomString.generate())) + // setup couch creds separately + if (opts && opts.single) { + obj["COUCHDB_USER"] = "admin" + obj["COUCHDB_PASSWORD"] = randomString.generate() + } else { + obj["COUCH_DB_USER"] = "admin" + obj["COUCH_DB_PASSWORD"] = randomString.generate() + } + return obj +} + +function getSingleCompose(port) { + const singleComposeObj = { + version: "3", + services: { + budibase: { + restart: "unless-stopped", + image: SINGLE_IMAGE, + ports: [`${port}:80`], + environment: getSecrets({ single: true }), + volumes: [`${VOL_NAME}:/data`], + }, + }, + volumes: { + [VOL_NAME]: { + driver: "local", + }, + }, + } + return yaml.stringify(singleComposeObj) +} + +function getEnv(port) { + const partOne = stringifyToDotEnv({ + MAIN_PORT: port, + }) + const partTwo = stringifyToDotEnv(getSecrets()) + const partThree = stringifyToDotEnv({ + APP_PORT: 4002, + WORKER_PORT: 4003, + MINIO_PORT: 4004, + COUCH_DB_PORT: 4005, + REDIS_PORT: 6379, + WATCHTOWER_PORT: 6161, + BUDIBASE_ENVIRONMENT: "PRODUCTION", + }) + return [ + "# Use the main port in the builder for your self hosting URL, e.g. localhost:10000", + partOne, + "# This section contains all secrets pertaining to the system", + partTwo, + "# This section contains variables that do not need to be altered under normal circumstances", + partThree, + ].join("\n") +} + +exports.ENV_PATH = ENV_PATH +exports.COMPOSE_PATH = COMPOSE_PATH + +module.exports.ConfigMap = { + MAIN_PORT: "port", +} + +module.exports.QUICK_CONFIG = { + key: "budibase", + port: 10000, +} + +async function make(path, contentsFn, inputs = {}) { + const port = + inputs.port || + (await number( + "Please enter the port on which you want your installation to run: ", + 10000 + )) + const fileContents = contentsFn(port) + fs.writeFileSync(path, fileContents) + console.log( + success( + `Configuration has been written successfully - please check ${path} for more details.` + ) + ) +} + +module.exports.makeEnv = async (inputs = {}) => { + return make(ENV_PATH, getEnv, inputs) +} + +module.exports.makeSingleCompose = async (inputs = {}) => { + return make(COMPOSE_PATH, getSingleCompose, inputs) +} + +module.exports.getEnvProperty = property => { + const props = fs.readFileSync(ENV_PATH, "utf8").split(property) + if (props[0].charAt(0) === "=") { + property = props[0] + } else { + property = props[1] + } + return property.split("=")[1].split("\n")[0] +} + +module.exports.getComposeProperty = property => { + const { service } = getAppService(COMPOSE_PATH) + if (property === "port" && Array.isArray(service.ports)) { + const port = service.ports[0] + return port.split(":")[0] + } else if (service.environment) { + return service.environment[property] + } + return null +} diff --git a/packages/cli/src/hosting/start.js b/packages/cli/src/hosting/start.js new file mode 100644 index 0000000000..33b5eb92ce --- /dev/null +++ b/packages/cli/src/hosting/start.js @@ -0,0 +1,34 @@ +const { + checkDockerConfigured, + checkInitComplete, + handleError, +} = require("./utils") +const { info, success } = require("../utils") +const makeFiles = require("./makeFiles") +const compose = require("docker-compose") +const fs = require("fs") + +exports.start = async () => { + await checkDockerConfigured() + checkInitComplete() + console.log( + info( + "Starting services, this may take a moment - first time this may take a few minutes to download images." + ) + ) + let port + if (fs.existsSync(makeFiles.ENV_PATH)) { + port = makeFiles.getEnvProperty("MAIN_PORT") + } else { + port = makeFiles.getComposeProperty("port") + } + await handleError(async () => { + // need to log as it makes it more clear + await compose.upAll({ cwd: "./", log: true }) + }) + console.log( + success( + `Services started, please go to http://localhost:${port} for next steps.` + ) + ) +} diff --git a/packages/cli/src/hosting/status.js b/packages/cli/src/hosting/status.js new file mode 100644 index 0000000000..2b98392133 --- /dev/null +++ b/packages/cli/src/hosting/status.js @@ -0,0 +1,17 @@ +const { + checkDockerConfigured, + checkInitComplete, + handleError, +} = require("./utils") +const { info } = require("../utils") +const compose = require("docker-compose") + +exports.status = async () => { + await checkDockerConfigured() + checkInitComplete() + console.log(info("Budibase status")) + await handleError(async () => { + const response = await compose.ps() + console.log(response.out) + }) +} diff --git a/packages/cli/src/hosting/stop.js b/packages/cli/src/hosting/stop.js new file mode 100644 index 0000000000..5f38c93484 --- /dev/null +++ b/packages/cli/src/hosting/stop.js @@ -0,0 +1,17 @@ +const { + checkDockerConfigured, + checkInitComplete, + handleError, +} = require("./utils") +const { info, success } = require("../utils") +const compose = require("docker-compose") + +exports.stop = async () => { + await checkDockerConfigured() + checkInitComplete() + console.log(info("Stopping services, this may take a moment.")) + await handleError(async () => { + await compose.stop() + }) + console.log(success("Services have been stopped successfully.")) +} diff --git a/packages/cli/src/hosting/update.js b/packages/cli/src/hosting/update.js new file mode 100644 index 0000000000..7d3367ce57 --- /dev/null +++ b/packages/cli/src/hosting/update.js @@ -0,0 +1,49 @@ +const { + checkDockerConfigured, + checkInitComplete, + downloadFiles, + handleError, + getServices, +} = require("./utils") +const { confirmation } = require("../questions") +const compose = require("docker-compose") +const { COMPOSE_PATH } = require("./makeFiles") +const { info, success } = require("../utils") +const { start } = require("./start") + +const BB_COMPOSE_SERVICES = ["app-service", "worker-service", "proxy-service"] +const BB_SINGLE_SERVICE = ["budibase"] + +exports.update = async () => { + const { services } = getServices(COMPOSE_PATH) + const isSingle = Object.keys(services).length === 1 + await checkDockerConfigured() + checkInitComplete() + if ( + !isSingle && + (await confirmation("Do you wish to update you docker-compose.yaml?")) + ) { + await downloadFiles() + } + await handleError(async () => { + const status = await compose.ps() + const parts = status.out.split("\n") + const isUp = parts[2] && parts[2].indexOf("Up") !== -1 + if (isUp) { + console.log(info("Stopping services, this may take a moment.")) + await compose.stop() + } + console.log(info("Beginning update, this may take a few minutes.")) + let services + if (isSingle) { + services = BB_SINGLE_SERVICE + } else { + services = BB_COMPOSE_SERVICES + } + await compose.pullMany(services, { log: true }) + if (isUp) { + console.log(success("Update complete, restarting services...")) + await start() + } + }) +} diff --git a/packages/cli/src/hosting/utils.js b/packages/cli/src/hosting/utils.js new file mode 100644 index 0000000000..952974ee83 --- /dev/null +++ b/packages/cli/src/hosting/utils.js @@ -0,0 +1,87 @@ +const { lookpath } = require("lookpath") +const fs = require("fs") +const makeFiles = require("./makeFiles") +const { logErrorToFile, downloadFile, error } = require("../utils") +const yaml = require("yaml") + +const ERROR_FILE = "docker-error.log" +const FILE_URLS = [ + "https://raw.githubusercontent.com/Budibase/budibase/master/hosting/docker-compose.yaml", +] + +exports.downloadFiles = async () => { + const promises = [] + for (let url of FILE_URLS) { + const fileName = url.split("/").slice(-1)[0] + promises.push(downloadFile(url, `./${fileName}`)) + } + await Promise.all(promises) +} + +exports.checkDockerConfigured = async () => { + const error = + "docker/docker-compose has not been installed, please follow instructions at: https://docs.budibase.com/docs/docker-compose" + const docker = await lookpath("docker") + const compose = await lookpath("docker-compose") + if (!docker || !compose) { + throw error + } +} + +exports.checkInitComplete = () => { + if ( + !fs.existsSync(makeFiles.ENV_PATH) && + !fs.existsSync(makeFiles.COMPOSE_PATH) + ) { + throw "Please run the hosting --init command before any other hosting command." + } +} + +exports.handleError = async func => { + try { + await func() + } catch (err) { + if (err && err.err) { + logErrorToFile(ERROR_FILE, err.err) + } + throw `Failed to start - logs written to file: ${ERROR_FILE}` + } +} + +exports.getServices = path => { + const dockerYaml = fs.readFileSync(path, "utf8") + const parsedYaml = yaml.parse(dockerYaml) + return { yaml: parsedYaml, services: parsedYaml.services } +} + +exports.getAppService = path => { + const { yaml, services } = exports.getServices(path), + serviceList = Object.keys(services) + let service + if (services["app-service"]) { + service = services["app-service"] + } else if (serviceList.length === 1) { + service = services[serviceList[0]] + } + return { yaml, service } +} + +exports.updateDockerComposeService = updateFn => { + const opts = ["docker-compose.yaml", "docker-compose.yml"] + const dockerFilePath = opts.find(name => fs.existsSync(name)) + if (!dockerFilePath) { + console.log(error("Unable to locate docker-compose YAML.")) + return + } + const { yaml: parsedYaml, service } = exports.getAppService(dockerFilePath) + if (!service) { + console.log( + error( + "Unable to locate service within compose file, is it a valid Budibase configuration?" + ) + ) + return + } + updateFn(service) + fs.writeFileSync(dockerFilePath, yaml.stringify(parsedYaml)) +} diff --git a/packages/cli/src/hosting/watch.js b/packages/cli/src/hosting/watch.js new file mode 100644 index 0000000000..fb51aa9ad8 --- /dev/null +++ b/packages/cli/src/hosting/watch.js @@ -0,0 +1,34 @@ +const { resolve } = require("path") +const fs = require("fs") +const { error, success } = require("../utils") +const { updateDockerComposeService } = require("./utils") + +exports.watchPlugins = async pluginPath => { + const PLUGIN_PATH = "/plugins" + // get absolute path + pluginPath = resolve(pluginPath) + if (!fs.existsSync(pluginPath)) { + console.log( + error( + `The directory "${pluginPath}" does not exist, please create and then try again.` + ) + ) + return + } + updateDockerComposeService(service => { + // set environment variable + service.environment["PLUGINS_DIR"] = PLUGIN_PATH + // add volumes to parsed yaml + if (!service.volumes) { + service.volumes = [] + } + const found = service.volumes.find(vol => vol.includes(PLUGIN_PATH)) + if (found) { + service.volumes.splice(service.volumes.indexOf(found), 1) + } + service.volumes.push(`${pluginPath}:${PLUGIN_PATH}`) + }) + console.log( + success(`Docker compose configured to watch directory: ${pluginPath}`) + ) +} diff --git a/packages/cli/src/plugins/index.js b/packages/cli/src/plugins/index.js index 66cca8c19d..1057ca9aaa 100644 --- a/packages/cli/src/plugins/index.js +++ b/packages/cli/src/plugins/index.js @@ -1,5 +1,5 @@ const Command = require("../structures/Command") -const { CommandWords, AnalyticsEvents } = require("../constants") +const { CommandWords, AnalyticsEvents, InitTypes } = require("../constants") const { getSkeleton, fleshOutSkeleton } = require("./skeleton") const questions = require("../questions") const fs = require("fs") @@ -9,6 +9,9 @@ const { runPkgCommand } = require("../exec") const { join } = require("path") const { success, error, info, moveDirectory } = require("../utils") const { captureEvent } = require("../events") +const fp = require("find-free-port") +const { init: hostingInit } = require("../hosting/init") +const { start: hostingStart } = require("../hosting/start") function checkInPlugin() { if (!fs.existsSync("package.json")) { @@ -141,6 +144,19 @@ async function watch() { } } +async function dev() { + const pluginDir = await questions.string("Directory to watch", "./") + const [port] = await fp(10000) + await hostingInit({ + init: InitTypes.QUICK, + single: true, + watchPluginDir: pluginDir, + genUser: true, + port, + }) + await hostingStart() +} + const command = new Command(`${CommandWords.PLUGIN}`) .addHelp( "Custom plugins for Budibase, init, build and verify your components and datasources with this tool." @@ -160,5 +176,10 @@ const command = new Command(`${CommandWords.PLUGIN}`) "Automatically build any changes to your plugin.", watch ) + .addSubOption( + "--dev", + "Run a development environment which automatically watches the current directory.", + dev + ) exports.command = command diff --git a/packages/cli/src/structures/Command.js b/packages/cli/src/structures/Command.js index dfce96504d..e383c14263 100644 --- a/packages/cli/src/structures/Command.js +++ b/packages/cli/src/structures/Command.js @@ -1,4 +1,9 @@ -const { getSubHelpDescription, getHelpDescription, error } = require("../utils") +const { + getSubHelpDescription, + getHelpDescription, + error, + capitaliseFirstLetter, +} = require("../utils") class Command { constructor(command, func = null) { @@ -8,6 +13,15 @@ class Command { this.func = func } + convertToCommander(lookup) { + const parts = lookup.toLowerCase().split("-") + // camel case, separate out first + const first = parts.shift() + return [first] + .concat(parts.map(part => capitaliseFirstLetter(part))) + .join("") + } + addHelp(help) { this.help = help return this @@ -25,10 +39,7 @@ class Command { command = command.description(getHelpDescription(thisCmd.help)) } for (let opt of thisCmd.opts) { - command = command.option( - `${opt.command}`, - getSubHelpDescription(opt.help) - ) + command = command.option(opt.command, getSubHelpDescription(opt.help)) } command.helpOption( "--help", @@ -36,17 +47,25 @@ class Command { ) command.action(async options => { try { - let executed = false + let executed = false, + found = false for (let opt of thisCmd.opts) { - const lookup = opt.command.split(" ")[0].replace("--", "") - if (!executed && options[lookup]) { + let lookup = opt.command.split(" ")[0].replace("--", "") + // need to handle how commander converts watch-plugin-dir to watchPluginDir + lookup = this.convertToCommander(lookup) + found = !executed && options[lookup] + if (found && opt.func) { const input = Object.keys(options).length > 1 ? options : options[lookup] await opt.func(input) executed = true } } - if (!executed) { + if (found && !executed) { + console.log( + error(`${Object.keys(options)[0]} is an option, not an operation.`) + ) + } else if (!executed) { console.log(error(`Unknown ${this.command} option.`)) command.help() } diff --git a/packages/cli/src/structures/ConfigManager.js b/packages/cli/src/structures/ConfigManager.js index 04b7875b57..35799b8e92 100644 --- a/packages/cli/src/structures/ConfigManager.js +++ b/packages/cli/src/structures/ConfigManager.js @@ -33,11 +33,10 @@ class ConfigManager { } setValue(key, value) { - const updated = { + this.config = { ...this.config, [key]: value, } - this.config = updated } removeKey(key) { diff --git a/packages/cli/src/utils.js b/packages/cli/src/utils.js index 91f3263cc1..ba793420e7 100644 --- a/packages/cli/src/utils.js +++ b/packages/cli/src/utils.js @@ -84,3 +84,15 @@ exports.moveDirectory = (oldPath, newPath) => { } fs.rmdirSync(oldPath) } + +exports.capitaliseFirstLetter = str => { + return str.charAt(0).toUpperCase() + str.slice(1) +} + +exports.stringifyToDotEnv = json => { + let str = "" + for (let [key, value] of Object.entries(json)) { + str += `${key}=${value}\n` + } + return str +} diff --git a/packages/cli/yarn.lock b/packages/cli/yarn.lock index 547e9fd3e2..0850f94154 100644 --- a/packages/cli/yarn.lock +++ b/packages/cli/yarn.lock @@ -1113,6 +1113,11 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +find-free-port@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-free-port/-/find-free-port-2.0.0.tgz#4b22e5f6579eb1a38c41ac6bcb3efed1b6da9b1b" + integrity sha512-J1j8gfEVf5FN4PR5w5wrZZ7NYs2IvqsHcd03cAeQx3Ec/mo+lKceaVNhpsRKoZpZKbId88o8qh+dwUwzBV6WCg== + find-replace@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" @@ -3080,6 +3085,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.1.tgz#1e06fb4ca46e60d9da07e4f786ea370ed3c3cfec" + integrity sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw== + yargs-parser@^20.2.2: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" diff --git a/packages/client/package.json b/packages/client/package.json index cfbb361dab..bcb1279937 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "2.0.6", + "version": "2.0.8", "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": "^2.0.6", - "@budibase/frontend-core": "^2.0.6", - "@budibase/string-templates": "^2.0.6", + "@budibase/bbui": "^2.0.8", + "@budibase/frontend-core": "^2.0.8", + "@budibase/string-templates": "^2.0.8", "@spectrum-css/button": "^3.0.3", "@spectrum-css/card": "^3.0.3", "@spectrum-css/divider": "^1.0.3", diff --git a/packages/client/src/index.js b/packages/client/src/index.js index 3d0aaa1038..0e8ab8c258 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -1,5 +1,11 @@ import ClientApp from "./components/ClientApp.svelte" -import { componentStore, builderStore, appStore, devToolsStore } from "./stores" +import { + componentStore, + builderStore, + appStore, + devToolsStore, + environmentStore, +} from "./stores" import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-rollup.js" import { get } from "svelte/store" import { initWebsocket } from "./websocket.js" @@ -15,7 +21,7 @@ loadSpectrumIcons() let app -const loadBudibase = () => { +const loadBudibase = async () => { // Update builder store with any builder flags builderStore.set({ inBuilder: !!window["##BUDIBASE_IN_BUILDER##"], @@ -36,6 +42,9 @@ const loadBudibase = () => { // server rendered app HTML appStore.actions.setAppId(window["##BUDIBASE_APP_ID##"]) + // Fetch environment info + await environmentStore.actions.fetchEnvironment() + // Enable dev tools or not. We need to be using a dev app and not inside // the builder preview to enable them. const enableDevTools = !get(builderStore).inBuilder && get(appStore).isDevApp diff --git a/packages/client/src/stores/initialise.js b/packages/client/src/stores/initialise.js index 4ad85dfd40..1900e62ce1 100644 --- a/packages/client/src/stores/initialise.js +++ b/packages/client/src/stores/initialise.js @@ -1,9 +1,7 @@ import { routeStore } from "./routes" import { appStore } from "./app" -import { environmentStore } from "./environment" export async function initialise() { await routeStore.actions.fetchRoutes() await appStore.actions.fetchAppDefinition() - await environmentStore.actions.fetchEnvironment() } diff --git a/packages/frontend-core/package.json b/packages/frontend-core/package.json index b44f15137a..91ae8a3ac5 100644 --- a/packages/frontend-core/package.json +++ b/packages/frontend-core/package.json @@ -1,12 +1,12 @@ { "name": "@budibase/frontend-core", - "version": "2.0.6", + "version": "2.0.8", "description": "Budibase frontend core libraries used in builder and client", "author": "Budibase", "license": "MPL-2.0", "svelte": "src/index.js", "dependencies": { - "@budibase/bbui": "^2.0.6", + "@budibase/bbui": "^2.0.8", "lodash": "^4.17.21", "svelte": "^3.46.2" } diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 70fb754fff..21b287a884 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/sdk", - "version": "2.0.6", + "version": "2.0.8", "description": "Budibase Public API SDK", "author": "Budibase", "license": "MPL-2.0", diff --git a/packages/server/package.json b/packages/server/package.json index 38b37812c4..8cb664d204 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "2.0.6", + "version": "2.0.8", "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": "^2.0.6", - "@budibase/client": "^2.0.6", - "@budibase/pro": "2.0.6", - "@budibase/string-templates": "^2.0.6", - "@budibase/types": "^2.0.6", + "@budibase/backend-core": "^2.0.8", + "@budibase/client": "^2.0.8", + "@budibase/pro": "2.0.8", + "@budibase/string-templates": "^2.0.8", + "@budibase/types": "^2.0.8", "@bull-board/api": "3.7.0", "@bull-board/koa": "3.9.4", "@elastic/elasticsearch": "7.10.0", diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 305b715d55..451e2b186e 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1094,12 +1094,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.6.tgz#1e80e5a06a53c294def350f10fcce3c224d73a84" - integrity sha512-BxBfFH/qW66MXatD1DSrefPp/dQxgY9hq+Z8hB8raOZfoL+fxIa0m/U8Ihqe5atYPXf9B4/RdjjQk+ioZ/Bjaw== +"@budibase/backend-core@2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.8.tgz#c6a0f2f61a6f1d73e4e86c47a1a2371d63f58f36" + integrity sha512-KXlXTzk7PCHs/OIx++fSHPsvmCgT+aOK4tCN1nchG9VZZZLHabkDBM1uNulmJ8VXpvdBFtCl7dCQXr0ZPNm7LQ== dependencies: - "@budibase/types" "^2.0.6" + "@budibase/types" "^2.0.8" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -1180,13 +1180,13 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.6.tgz#ac3d32e595c7649c1051c021347d1d8c7d870e0a" - integrity sha512-qvcalSJOEEE//cFazLuR8qrd12CBx6l/RIMKq9+KMXEA9ZyG7UfsCl3C2Yk6bgtW+ZkmmJabO9QjTTU81Ktn0A== +"@budibase/pro@2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.8.tgz#e2e15ecaf2c7b6fc571747128f30ce8f78ab91cd" + integrity sha512-oWLIpr3/JP6VQz5BNn87uecKoEiZyRRBTlRP5ijS1Uciu8UndgQJE/WKfh6SgaM6NNWjrPDW7yYrG3aWHupR0A== dependencies: - "@budibase/backend-core" "2.0.6" - "@budibase/types" "2.0.6" + "@budibase/backend-core" "2.0.8" + "@budibase/types" "2.0.8" "@koa/router" "8.0.8" joi "17.6.0" node-fetch "^2.6.1" @@ -1209,10 +1209,10 @@ svelte-apexcharts "^1.0.2" svelte-flatpickr "^3.1.0" -"@budibase/types@2.0.6", "@budibase/types@^2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.6.tgz#03f294e4809c4044197a52325a7d1b55f40643a4" - integrity sha512-D2vQ+zSJqOpjCc7KdxnUtB4MucNC0OEsxcSp0uiIb+2Tv0dfRUUKl/IOrvXrkwqZ+uLDUMYYjKUQQcKxdBrnqA== +"@budibase/types@2.0.8", "@budibase/types@^2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.8.tgz#3b8fa41a35167c0eab61d4d9a61521b721a8b808" + integrity sha512-c7A5CkfoPUPK27t7r8p1E7bmUA5QCPbn2xbtMyc3MVNLI7Z6abKLpcATKFwxV8tT4xUGoJd835DOojctZm1fVw== "@bull-board/api@3.7.0": version "3.7.0" diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index a205959db2..5922fbb4cb 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "2.0.6", + "version": "2.0.8", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/types/package.json b/packages/types/package.json index 7d6a2936ab..393c580d9b 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/types", - "version": "2.0.6", + "version": "2.0.8", "description": "Budibase types", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/worker/package.json b/packages/worker/package.json index 9bf4319bd5..5a1815c603 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "2.0.6", + "version": "2.0.8", "description": "Budibase background service", "main": "src/index.ts", "repository": { @@ -36,10 +36,10 @@ "author": "Budibase", "license": "GPL-3.0", "dependencies": { - "@budibase/backend-core": "^2.0.6", - "@budibase/pro": "2.0.6", - "@budibase/string-templates": "^2.0.6", - "@budibase/types": "^2.0.6", + "@budibase/backend-core": "^2.0.8", + "@budibase/pro": "2.0.8", + "@budibase/string-templates": "^2.0.8", + "@budibase/types": "^2.0.8", "@koa/router": "8.0.8", "@sentry/node": "6.17.7", "@techpass/passport-openidconnect": "0.3.2", diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index 5945f7b962..57ef1464a2 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -291,12 +291,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.6.tgz#1e80e5a06a53c294def350f10fcce3c224d73a84" - integrity sha512-BxBfFH/qW66MXatD1DSrefPp/dQxgY9hq+Z8hB8raOZfoL+fxIa0m/U8Ihqe5atYPXf9B4/RdjjQk+ioZ/Bjaw== +"@budibase/backend-core@2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.8.tgz#c6a0f2f61a6f1d73e4e86c47a1a2371d63f58f36" + integrity sha512-KXlXTzk7PCHs/OIx++fSHPsvmCgT+aOK4tCN1nchG9VZZZLHabkDBM1uNulmJ8VXpvdBFtCl7dCQXr0ZPNm7LQ== dependencies: - "@budibase/types" "^2.0.6" + "@budibase/types" "^2.0.8" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -327,21 +327,21 @@ uuid "8.3.2" zlib "1.0.5" -"@budibase/pro@2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.6.tgz#ac3d32e595c7649c1051c021347d1d8c7d870e0a" - integrity sha512-qvcalSJOEEE//cFazLuR8qrd12CBx6l/RIMKq9+KMXEA9ZyG7UfsCl3C2Yk6bgtW+ZkmmJabO9QjTTU81Ktn0A== +"@budibase/pro@2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.8.tgz#e2e15ecaf2c7b6fc571747128f30ce8f78ab91cd" + integrity sha512-oWLIpr3/JP6VQz5BNn87uecKoEiZyRRBTlRP5ijS1Uciu8UndgQJE/WKfh6SgaM6NNWjrPDW7yYrG3aWHupR0A== dependencies: - "@budibase/backend-core" "2.0.6" - "@budibase/types" "2.0.6" + "@budibase/backend-core" "2.0.8" + "@budibase/types" "2.0.8" "@koa/router" "8.0.8" joi "17.6.0" node-fetch "^2.6.1" -"@budibase/types@2.0.6", "@budibase/types@^2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.6.tgz#03f294e4809c4044197a52325a7d1b55f40643a4" - integrity sha512-D2vQ+zSJqOpjCc7KdxnUtB4MucNC0OEsxcSp0uiIb+2Tv0dfRUUKl/IOrvXrkwqZ+uLDUMYYjKUQQcKxdBrnqA== +"@budibase/types@2.0.8", "@budibase/types@^2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.8.tgz#3b8fa41a35167c0eab61d4d9a61521b721a8b808" + integrity sha512-c7A5CkfoPUPK27t7r8p1E7bmUA5QCPbn2xbtMyc3MVNLI7Z6abKLpcATKFwxV8tT4xUGoJd835DOojctZm1fVw== "@cspotcode/source-map-consumer@0.8.0": version "0.8.0"