diff --git a/.github/ISSUE_TEMPLATE/epic.md b/.github/ISSUE_TEMPLATE/epic.md
new file mode 100644
index 0000000000..b8cf652125
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/epic.md
@@ -0,0 +1,24 @@
+---
+name: Epic
+about: Plan a new project
+title: ''
+labels: epic
+assignees: ''
+
+---
+
+## Description
+Brief summary of what this Epic is, whether it's a larger project, goal, or user story. Describe the job to be done, which persona this Epic is mainly for, or if more multiple, break it down by user and job story.
+
+## Spec
+Link to confluence spec
+
+## Teams and Stakeholders
+Describe who needs to be kept up-to-date about this Epic, included in discussions, or updated along the way. Stakeholders can be both in Product/Engineering, as well as other teams like Customer Success who might want to keep customers updated on the Epic project.
+
+
+## Workflow
+- [ ] Spec Created and pasted above
+- [ ] Product Review
+- [ ] Designs created
+- [ ] Individual Tasks created and assigned to Epic
diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml
index e940e6fa10..42a0c0a273 100644
--- a/.github/workflows/budibase_ci.yml
+++ b/.github/workflows/budibase_ci.yml
@@ -59,3 +59,9 @@ jobs:
with:
install: false
command: yarn test:e2e:ci
+
+ - name: QA Core Integration Tests
+ run: |
+ cd qa-core
+ yarn
+ yarn api:test:ci
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index f063e2224f..e1d3e6db0e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -63,6 +63,7 @@ typings/
# dotenv environment variables file
.env
+!qa-core/.env
!hosting/.env
hosting/.generated-nginx.dev.conf
hosting/proxy/.generated-nginx.prod.conf
@@ -102,4 +103,6 @@ packages/builder/cypress/reports
stats.html
# TypeScript cache
-*.tsbuildinfo
\ No newline at end of file
+*.tsbuildinfo
+budibase-component
+budibase-datasource
diff --git a/.prettierignore b/.prettierignore
index bbeff65da7..ad36a86b99 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -8,4 +8,4 @@ packages/server/client
packages/server/src/definitions/openapi.ts
packages/builder/.routify
packages/builder/cypress/support/queryLevelTransformerFunction.js
-packages/builder/cypress/support/queryLevelTransformerFunctionWithData.js
+packages/builder/cypress/support/queryLevelTransformerFunctionWithData.js
\ No newline at end of file
diff --git a/README.md b/README.md
index 1dec1737da..bd38610566 100644
--- a/README.md
+++ b/README.md
@@ -65,7 +65,7 @@ Budibase is open-source - licensed as GPL v3. This should fill you with confiden
### Load data or start from scratch
-Budibase pulls in data from multiple sources, including MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB, or a REST API. And unlike other platforms, with Budibase you can start from scratch and create business apps with no data sources. [Request new data sources](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
+Budibase pulls in data from multiple sources, including MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB, or a REST API. And unlike other platforms, with Budibase you can start from scratch and create business apps with no datasources. [Request new datasources](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
diff --git a/charts/budibase/templates/app-service-deployment.yaml b/charts/budibase/templates/app-service-deployment.yaml
index 74b98ac008..6517133a58 100644
--- a/charts/budibase/templates/app-service-deployment.yaml
+++ b/charts/budibase/templates/app-service-deployment.yaml
@@ -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
@@ -158,7 +162,10 @@ spec:
name: bbapps
ports:
- containerPort: {{ .Values.services.apps.port }}
- resources: {}
+ {{ with .Values.services.apps.resources }}
+ resources:
+ {{- toYaml . | nindent 10 }}
+ {{ end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
diff --git a/charts/budibase/templates/couchdb-backup.yaml b/charts/budibase/templates/couchdb-backup.yaml
index ae062475ce..68e5eab617 100644
--- a/charts/budibase/templates/couchdb-backup.yaml
+++ b/charts/budibase/templates/couchdb-backup.yaml
@@ -38,7 +38,10 @@ spec:
image: redgeoff/replicate-couchdb-cluster
imagePullPolicy: Always
name: couchdb-backup
- resources: {}
+ {{ with .Values.services.couchdb.backup.resources }}
+ resources:
+ {{- toYaml . | nindent 10 }}
+ {{ end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
diff --git a/charts/budibase/templates/minio-service-deployment.yaml b/charts/budibase/templates/minio-service-deployment.yaml
index 103f9e3ed2..144dbe539a 100644
--- a/charts/budibase/templates/minio-service-deployment.yaml
+++ b/charts/budibase/templates/minio-service-deployment.yaml
@@ -56,7 +56,10 @@ spec:
name: minio-service
ports:
- containerPort: {{ .Values.services.objectStore.port }}
- resources: {}
+ {{ with .Values.services.objectStore.resources }}
+ resources:
+ {{- toYaml . | nindent 10 }}
+ {{ end }}
volumeMounts:
- mountPath: /data
name: minio-data
diff --git a/charts/budibase/templates/proxy-service-deployment.yaml b/charts/budibase/templates/proxy-service-deployment.yaml
index 505a46f1e8..5588022032 100644
--- a/charts/budibase/templates/proxy-service-deployment.yaml
+++ b/charts/budibase/templates/proxy-service-deployment.yaml
@@ -30,7 +30,10 @@ spec:
name: proxy-service
ports:
- containerPort: {{ .Values.services.proxy.port }}
- resources: {}
+ {{ with .Values.services.proxy.resources }}
+ resources:
+ {{- toYaml . | nindent 10 }}
+ {{ end }}
volumeMounts:
{{- with .Values.affinity }}
affinity:
diff --git a/charts/budibase/templates/redis-service-deployment.yaml b/charts/budibase/templates/redis-service-deployment.yaml
index 6e09346cad..d94e4d70f8 100644
--- a/charts/budibase/templates/redis-service-deployment.yaml
+++ b/charts/budibase/templates/redis-service-deployment.yaml
@@ -35,7 +35,10 @@ spec:
name: redis-service
ports:
- containerPort: {{ .Values.services.redis.port }}
- resources: {}
+ {{ with .Values.services.redis.resources }}
+ resources:
+ {{- toYaml . | nindent 10 }}
+ {{ end }}
volumeMounts:
- mountPath: /data
name: redis-data
diff --git a/charts/budibase/templates/worker-service-deployment.yaml b/charts/budibase/templates/worker-service-deployment.yaml
index 083231eeff..902e9ac03d 100644
--- a/charts/budibase/templates/worker-service-deployment.yaml
+++ b/charts/budibase/templates/worker-service-deployment.yaml
@@ -151,7 +151,10 @@ spec:
name: bbworker
ports:
- containerPort: {{ .Values.services.worker.port }}
- resources: {}
+ {{ with .Values.services.worker.resources }}
+ resources:
+ {{- toYaml . | nindent 10 }}
+ {{ end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
diff --git a/charts/budibase/values.yaml b/charts/budibase/values.yaml
index 9b5e76d0d7..a15504d58c 100644
--- a/charts/budibase/values.yaml
+++ b/charts/budibase/values.yaml
@@ -60,19 +60,6 @@ ingress:
port:
number: 10000
-resources:
- {}
- # We usually recommend not to specify default resources and to leave this as a conscious
- # choice for the user. This also increases chances charts run on environments with little
- # resources, such as Minikube. If you do want to specify resources, uncomment the following
- # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
- # limits:
- # cpu: 100m
- # memory: 128Mi
- # requests:
- # cpu: 100m
- # memory: 128Mi
-
autoscaling:
enabled: false
minReplicas: 1
@@ -125,16 +112,19 @@ services:
proxy:
port: 10000
replicaCount: 1
+ resources: {}
apps:
port: 4002
replicaCount: 1
logLevel: info
+ resources: {}
# nodeDebug: "" # set the value of NODE_DEBUG
worker:
port: 4003
replicaCount: 1
+ resources: {}
couchdb:
enabled: true
@@ -148,6 +138,7 @@ services:
target: ""
# backup interval in seconds
interval: ""
+ resources: {}
redis:
enabled: true # disable if using external redis
@@ -161,6 +152,7 @@ services:
## If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner.
storageClass: ""
+ resources: {}
objectStore:
minio: true
@@ -177,6 +169,7 @@ services:
## If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner.
storageClass: ""
+ resources: {}
# Override values in couchDB subchart
couchdb:
diff --git a/examples/nextjs-api-sales/definitions/openapi.ts b/examples/nextjs-api-sales/definitions/openapi.ts
index 4f4ad45fc6..7f7f6befec 100644
--- a/examples/nextjs-api-sales/definitions/openapi.ts
+++ b/examples/nextjs-api-sales/definitions/openapi.ts
@@ -348,7 +348,7 @@ export interface paths {
}
}
responses: {
- /** Returns the created table, including the ID which has been generated for it. This can be internal or external data sources. */
+ /** Returns the created table, including the ID which has been generated for it. This can be internal or external datasources. */
200: {
content: {
"application/json": components["schemas"]["tableOutput"]
@@ -959,7 +959,7 @@ export interface components {
query: {
/** @description The ID of the query. */
_id: string
- /** @description The ID of the data source the query belongs to. */
+ /** @description The ID of the datasource the query belongs to. */
datasourceId?: string
/** @description The bindings which are required to perform this query. */
parameters?: string[]
@@ -983,7 +983,7 @@ export interface components {
data: {
/** @description The ID of the query. */
_id: string
- /** @description The ID of the data source the query belongs to. */
+ /** @description The ID of the datasource the query belongs to. */
datasourceId?: string
/** @description The bindings which are required to perform this query. */
parameters?: string[]
diff --git a/examples/nextjs-api-sales/package.json b/examples/nextjs-api-sales/package.json
index 6d75c85f01..41ce52e952 100644
--- a/examples/nextjs-api-sales/package.json
+++ b/examples/nextjs-api-sales/package.json
@@ -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"
}
-}
+}
\ No newline at end of file
diff --git a/examples/nextjs-api-sales/yarn.lock b/examples/nextjs-api-sales/yarn.lock
index 52c89967b2..f47fb84e33 100644
--- a/examples/nextjs-api-sales/yarn.lock
+++ b/examples/nextjs-api-sales/yarn.lock
@@ -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"
diff --git a/hosting/.env b/hosting/.env
index 11dd661bf1..c5638a266f 100644
--- a/hosting/.env
+++ b/hosting/.env
@@ -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=
\ No newline at end of file
+BB_ADMIN_USER_PASSWORD=
+
+# A path that is watched for plugin bundles. Any bundles found are imported automatically/
+PLUGINS_DIR=
\ No newline at end of file
diff --git a/hosting/docker-compose.yaml b/hosting/docker-compose.yaml
index c55ca34547..5b2adc2665 100644
--- a/hosting/docker-compose.yaml
+++ b/hosting/docker-compose.yaml
@@ -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
diff --git a/hosting/hosting.properties b/hosting/hosting.properties
index 11dd661bf1..c5638a266f 100644
--- a/hosting/hosting.properties
+++ b/hosting/hosting.properties
@@ -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=
\ No newline at end of file
+BB_ADMIN_USER_PASSWORD=
+
+# A path that is watched for plugin bundles. Any bundles found are imported automatically/
+PLUGINS_DIR=
\ No newline at end of file
diff --git a/hosting/nginx.dev.conf.hbs b/hosting/nginx.dev.conf.hbs
index 148007d958..14c32b1bba 100644
--- a/hosting/nginx.dev.conf.hbs
+++ b/hosting/nginx.dev.conf.hbs
@@ -80,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;
diff --git a/hosting/nginx.prod.conf.hbs b/hosting/nginx.prod.conf.hbs
index c57fce291c..f3202ad4a4 100644
--- a/hosting/nginx.prod.conf.hbs
+++ b/hosting/nginx.prod.conf.hbs
@@ -158,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;
diff --git a/hosting/single/nginx/nginx-default-site.conf b/hosting/single/nginx/nginx-default-site.conf
index c0d80a0185..bd89e21251 100644
--- a/hosting/single/nginx/nginx-default-site.conf
+++ b/hosting/single/nginx/nginx-default-site.conf
@@ -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;
diff --git a/lerna.json b/lerna.json
index 9a21166025..b0ca0a0cdf 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "1.3.21",
+ "version": "1.3.22-alpha.4",
"npmClient": "yarn",
"packages": [
"packages/*"
diff --git a/package.json b/package.json
index 4c24e0025b..d9b78368ba 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"js-yaml": "^4.1.0",
"kill-port": "^1.6.1",
"lerna": "3.14.1",
+ "madge": "^5.0.1",
"prettier": "^2.3.1",
"prettier-plugin-svelte": "^2.3.0",
"rimraf": "^3.0.2",
@@ -25,6 +26,7 @@
"bootstrap": "lerna bootstrap && lerna link && ./scripts/link-dependencies.sh",
"build": "lerna run build",
"build:dev": "lerna run prebuild && tsc --build --watch --preserveWatchOutput",
+ "deps:circular": "madge packages/server/dist/index.js packages/worker/src/index.ts packages/backend-core/dist/src/index.js packages/cli/src/index.js --circular",
"release": "lerna publish ${RELEASE_VERSION_TYPE:-patch} --yes --force-publish && yarn release:pro",
"release:develop": "lerna publish prerelease --yes --force-publish --dist-tag develop --exact && yarn release:pro:develop",
"release:pro": "bash scripts/pro/release.sh",
@@ -45,8 +47,8 @@
"lint:eslint": "eslint packages",
"lint:prettier": "prettier --check \"packages/**/*.{js,ts,svelte}\"",
"lint": "yarn run lint:eslint && yarn run lint:prettier",
- "lint:fix:eslint": "eslint --fix packages",
- "lint:fix:prettier": "prettier --write \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\"",
+ "lint:fix:eslint": "eslint --fix packages qa-core",
+ "lint:fix:prettier": "prettier --write \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\" && prettier --write \"qa-core/**/*.{js,ts,svelte}\"",
"lint:fix": "yarn run lint:fix:prettier && yarn run lint:fix:eslint",
"test:e2e": "lerna run cy:test --stream",
"test:e2e:ci": "lerna run cy:ci --stream",
diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json
index e2a9d25dce..e0e314fbee 100644
--- a/packages/backend-core/package.json
+++ b/packages/backend-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/backend-core",
- "version": "1.3.21",
+ "version": "1.3.22-alpha.4",
"description": "Budibase backend core libraries used in server and worker",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
@@ -20,11 +20,12 @@
"test:watch": "jest --watchAll"
},
"dependencies": {
- "@budibase/types": "^1.3.21",
+ "@budibase/types": "1.3.22-alpha.4",
"@shopify/jest-koa-mocks": "5.0.1",
"@techpass/passport-openidconnect": "0.3.2",
"aws-sdk": "2.1030.0",
"bcrypt": "5.0.1",
+ "bcryptjs": "2.4.3",
"dotenv": "16.0.1",
"emitter-listener": "1.1.2",
"ioredis": "4.28.0",
diff --git a/packages/backend-core/plugins.js b/packages/backend-core/plugins.js
new file mode 100644
index 0000000000..018e214dcb
--- /dev/null
+++ b/packages/backend-core/plugins.js
@@ -0,0 +1,3 @@
+module.exports = {
+ ...require("./src/plugin"),
+}
diff --git a/packages/backend-core/src/constants.js b/packages/backend-core/src/constants.js
index 172e66e603..44c271a4f8 100644
--- a/packages/backend-core/src/constants.js
+++ b/packages/backend-core/src/constants.js
@@ -7,6 +7,7 @@ exports.Cookies = {
CurrentApp: "budibase:currentapp",
Auth: "budibase:auth",
Init: "budibase:init",
+ ACCOUNT_RETURN_URL: "budibase:account:returnurl",
DatasourceAuth: "budibase:datasourceauth",
OIDC_CONFIG: "budibase:oidc:config",
}
diff --git a/packages/backend-core/src/context/index.ts b/packages/backend-core/src/context/index.ts
index 78ce764d55..8e3cf8a0a2 100644
--- a/packages/backend-core/src/context/index.ts
+++ b/packages/backend-core/src/context/index.ts
@@ -2,7 +2,7 @@ import env from "../environment"
import { SEPARATOR, DocumentType } from "../db/constants"
import cls from "./FunctionContext"
import { dangerousGetDB, closeDB } from "../db"
-import { baseGlobalDBName } from "../tenancy/utils"
+import { baseGlobalDBName } from "../db/tenancy"
import { IdentityContext } from "@budibase/types"
import { DEFAULT_TENANT_ID as _DEFAULT_TENANT_ID } from "../constants"
import { ContextKey } from "./constants"
diff --git a/packages/backend-core/src/db/constants.ts b/packages/backend-core/src/db/constants.ts
index 2c2c29cee2..62f4e8820f 100644
--- a/packages/backend-core/src/db/constants.ts
+++ b/packages/backend-core/src/db/constants.ts
@@ -44,6 +44,7 @@ export enum DocumentType {
DEV_INFO = "devinfo",
AUTOMATION_LOG = "log_au",
ACCOUNT_METADATA = "acc_metadata",
+ PLUGIN = "plg",
}
export const StaticDatabases = {
diff --git a/packages/backend-core/src/tenancy/utils.ts b/packages/backend-core/src/db/tenancy.ts
similarity index 91%
rename from packages/backend-core/src/tenancy/utils.ts
rename to packages/backend-core/src/db/tenancy.ts
index f99f1e30af..d920f7cd41 100644
--- a/packages/backend-core/src/tenancy/utils.ts
+++ b/packages/backend-core/src/db/tenancy.ts
@@ -1,5 +1,5 @@
import { DEFAULT_TENANT_ID } from "../constants"
-import { StaticDatabases, SEPARATOR } from "../db/constants"
+import { StaticDatabases, SEPARATOR } from "./constants"
import { getTenantId } from "../context"
export const getGlobalDBName = (tenantId?: string) => {
diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts
index c93c7b5662..a12c6bed4f 100644
--- a/packages/backend-core/src/db/utils.ts
+++ b/packages/backend-core/src/db/utils.ts
@@ -3,7 +3,7 @@ import { DEFAULT_TENANT_ID, Configs } from "../constants"
import env from "../environment"
import { SEPARATOR, DocumentType, UNICODE_MAX, ViewName } from "./constants"
import { getTenantId, getGlobalDB } from "../context"
-import { getGlobalDBName } from "../tenancy/utils"
+import { getGlobalDBName } from "./tenancy"
import fetch from "node-fetch"
import { doWithDB, allDbs } from "./index"
import { getCouchInfo } from "./pouch"
@@ -16,6 +16,7 @@ import * as events from "../events"
export * from "./constants"
export * from "./conversions"
export { default as Replication } from "./Replication"
+export * from "./tenancy"
/**
* Generates a new app ID.
@@ -367,6 +368,21 @@ export const generateDevInfoID = (userId: any) => {
return `${DocumentType.DEV_INFO}${SEPARATOR}${userId}`
}
+/**
+ * Generates a new plugin ID - to be used in the global DB.
+ * @returns {string} The new plugin ID which a plugin metadata document can be stored under.
+ */
+export const generatePluginID = (name: string) => {
+ return `${DocumentType.PLUGIN}${SEPARATOR}${name}`
+}
+
+/**
+ * Gets parameters for retrieving automations, this is a utility function for the getDocParams function.
+ */
+export const getPluginParams = (pluginId?: string | null, otherProps = {}) => {
+ return getDocParams(DocumentType.PLUGIN, pluginId, otherProps)
+}
+
/**
* Returns the most granular configuration document from the DB based on the type, workspace and userID passed.
* @param {Object} db - db instance to query
diff --git a/packages/backend-core/src/db/views.ts b/packages/backend-core/src/db/views.ts
index de313e8190..c337d26eaa 100644
--- a/packages/backend-core/src/db/views.ts
+++ b/packages/backend-core/src/db/views.ts
@@ -1,5 +1,6 @@
import { DocumentType, ViewName, DeprecatedViews, SEPARATOR } from "./utils"
import { getGlobalDB } from "../context"
+import PouchDB from "pouchdb"
import { StaticDatabases } from "./constants"
import { doWithDB } from "./"
@@ -201,13 +202,13 @@ export const queryView = async (
try {
let response = await db.query(`database/${viewName}`, params)
const rows = response.rows
- const docs = rows.map((resp: any) =>
- params.include_docs ? resp.doc : resp.value
- )
+ const docs = rows.map(row => (params.include_docs ? row.doc : row.value))
+ // if arrayResponse has been requested, always return array regardless of length
if (opts?.arrayResponse) {
return docs
} else {
+ // return the single document if there is only one
return docs.length <= 1 ? docs[0] : docs
}
} catch (err: any) {
diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts
index 0348d921ab..be1e1eacfc 100644
--- a/packages/backend-core/src/environment.ts
+++ b/packages/backend-core/src/environment.ts
@@ -19,6 +19,7 @@ if (!LOADED && isDev() && !isTest()) {
const env = {
isTest,
isDev,
+ JS_BCRYPT: process.env.JS_BCRYPT,
JWT_SECRET: process.env.JWT_SECRET,
COUCH_DB_URL: process.env.COUCH_DB_URL || "http://localhost:4005",
COUCH_DB_USERNAME: process.env.COUCH_DB_USER,
@@ -36,7 +37,7 @@ const env = {
MULTI_TENANCY: process.env.MULTI_TENANCY,
ACCOUNT_PORTAL_URL:
process.env.ACCOUNT_PORTAL_URL || "https://account.budibase.app",
- ACCOUNT_PORTAL_API_KEY: process.env.ACCOUNT_PORTAL_API_KEY,
+ ACCOUNT_PORTAL_API_KEY: process.env.ACCOUNT_PORTAL_API_KEY || "",
DISABLE_ACCOUNT_PORTAL: process.env.DISABLE_ACCOUNT_PORTAL,
SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED || ""),
COOKIE_DOMAIN: process.env.COOKIE_DOMAIN,
@@ -50,6 +51,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,
diff --git a/packages/backend-core/src/errors/base.js b/packages/backend-core/src/errors/base.js
deleted file mode 100644
index 7cb0c0fc23..0000000000
--- a/packages/backend-core/src/errors/base.js
+++ /dev/null
@@ -1,11 +0,0 @@
-class BudibaseError extends Error {
- constructor(message, code, type) {
- super(message)
- this.code = code
- this.type = type
- }
-}
-
-module.exports = {
- BudibaseError,
-}
diff --git a/packages/backend-core/src/errors/base.ts b/packages/backend-core/src/errors/base.ts
new file mode 100644
index 0000000000..801dcf168d
--- /dev/null
+++ b/packages/backend-core/src/errors/base.ts
@@ -0,0 +1,10 @@
+export class BudibaseError extends Error {
+ code: string
+ type: string
+
+ constructor(message: string, code: string, type: string) {
+ super(message)
+ this.code = code
+ this.type = type
+ }
+}
diff --git a/packages/backend-core/src/errors/generic.js b/packages/backend-core/src/errors/generic.js
deleted file mode 100644
index 5c7661f035..0000000000
--- a/packages/backend-core/src/errors/generic.js
+++ /dev/null
@@ -1,11 +0,0 @@
-const { BudibaseError } = require("./base")
-
-class GenericError extends BudibaseError {
- constructor(message, code, type) {
- super(message, code, type ? type : "generic")
- }
-}
-
-module.exports = {
- GenericError,
-}
diff --git a/packages/backend-core/src/errors/generic.ts b/packages/backend-core/src/errors/generic.ts
new file mode 100644
index 0000000000..71b3352438
--- /dev/null
+++ b/packages/backend-core/src/errors/generic.ts
@@ -0,0 +1,7 @@
+import { BudibaseError } from "./base"
+
+export class GenericError extends BudibaseError {
+ constructor(message: string, code: string, type: string) {
+ super(message, code, type ? type : "generic")
+ }
+}
diff --git a/packages/backend-core/src/errors/http.js b/packages/backend-core/src/errors/http.js
deleted file mode 100644
index 8e7cab4638..0000000000
--- a/packages/backend-core/src/errors/http.js
+++ /dev/null
@@ -1,12 +0,0 @@
-const { GenericError } = require("./generic")
-
-class HTTPError extends GenericError {
- constructor(message, httpStatus, code = "http", type = "generic") {
- super(message, code, type)
- this.status = httpStatus
- }
-}
-
-module.exports = {
- HTTPError,
-}
diff --git a/packages/backend-core/src/errors/http.ts b/packages/backend-core/src/errors/http.ts
new file mode 100644
index 0000000000..182e009f58
--- /dev/null
+++ b/packages/backend-core/src/errors/http.ts
@@ -0,0 +1,15 @@
+import { GenericError } from "./generic"
+
+export class HTTPError extends GenericError {
+ status: number
+
+ constructor(
+ message: string,
+ httpStatus: number,
+ code = "http",
+ type = "generic"
+ ) {
+ super(message, code, type)
+ this.status = httpStatus
+ }
+}
diff --git a/packages/backend-core/src/errors/index.js b/packages/backend-core/src/errors/index.ts
similarity index 65%
rename from packages/backend-core/src/errors/index.js
rename to packages/backend-core/src/errors/index.ts
index 31ffd739a0..be6657093d 100644
--- a/packages/backend-core/src/errors/index.js
+++ b/packages/backend-core/src/errors/index.ts
@@ -1,5 +1,6 @@
-const http = require("./http")
-const licensing = require("./licensing")
+import { HTTPError } from "./http"
+import { UsageLimitError, FeatureDisabledError } from "./licensing"
+import * as licensing from "./licensing"
const codes = {
...licensing.codes,
@@ -11,7 +12,7 @@ const context = {
...licensing.context,
}
-const getPublicError = err => {
+const getPublicError = (err: any) => {
let error
if (err.code || err.type) {
// add generic error information
@@ -32,13 +33,15 @@ const getPublicError = err => {
return error
}
-module.exports = {
+const pkg = {
codes,
types,
errors: {
- UsageLimitError: licensing.UsageLimitError,
- FeatureDisabledError: licensing.FeatureDisabledError,
- HTTPError: http.HTTPError,
+ UsageLimitError,
+ FeatureDisabledError,
+ HTTPError,
},
getPublicError,
}
+
+export = pkg
diff --git a/packages/backend-core/src/errors/licensing.js b/packages/backend-core/src/errors/licensing.js
deleted file mode 100644
index 85d207ac35..0000000000
--- a/packages/backend-core/src/errors/licensing.js
+++ /dev/null
@@ -1,43 +0,0 @@
-const { HTTPError } = require("./http")
-
-const type = "license_error"
-
-const codes = {
- USAGE_LIMIT_EXCEEDED: "usage_limit_exceeded",
- FEATURE_DISABLED: "feature_disabled",
-}
-
-const context = {
- [codes.USAGE_LIMIT_EXCEEDED]: err => {
- return {
- limitName: err.limitName,
- }
- },
- [codes.FEATURE_DISABLED]: err => {
- return {
- featureName: err.featureName,
- }
- },
-}
-
-class UsageLimitError extends HTTPError {
- constructor(message, limitName) {
- super(message, 400, codes.USAGE_LIMIT_EXCEEDED, type)
- this.limitName = limitName
- }
-}
-
-class FeatureDisabledError extends HTTPError {
- constructor(message, featureName) {
- super(message, 400, codes.FEATURE_DISABLED, type)
- this.featureName = featureName
- }
-}
-
-module.exports = {
- type,
- codes,
- context,
- UsageLimitError,
- FeatureDisabledError,
-}
diff --git a/packages/backend-core/src/errors/licensing.ts b/packages/backend-core/src/errors/licensing.ts
new file mode 100644
index 0000000000..7ffcefa167
--- /dev/null
+++ b/packages/backend-core/src/errors/licensing.ts
@@ -0,0 +1,39 @@
+import { HTTPError } from "./http"
+
+export const type = "license_error"
+
+export const codes = {
+ USAGE_LIMIT_EXCEEDED: "usage_limit_exceeded",
+ FEATURE_DISABLED: "feature_disabled",
+}
+
+export const context = {
+ [codes.USAGE_LIMIT_EXCEEDED]: (err: any) => {
+ return {
+ limitName: err.limitName,
+ }
+ },
+ [codes.FEATURE_DISABLED]: (err: any) => {
+ return {
+ featureName: err.featureName,
+ }
+ },
+}
+
+export class UsageLimitError extends HTTPError {
+ limitName: string
+
+ constructor(message: string, limitName: string) {
+ super(message, 400, codes.USAGE_LIMIT_EXCEEDED, type)
+ this.limitName = limitName
+ }
+}
+
+export class FeatureDisabledError extends HTTPError {
+ featureName: string
+
+ constructor(message: string, featureName: string) {
+ super(message, 400, codes.FEATURE_DISABLED, type)
+ this.featureName = featureName
+ }
+}
diff --git a/packages/backend-core/src/events/publishers/datasource.ts b/packages/backend-core/src/events/publishers/datasource.ts
index 3cd68033fc..d3ea7402f9 100644
--- a/packages/backend-core/src/events/publishers/datasource.ts
+++ b/packages/backend-core/src/events/publishers/datasource.ts
@@ -5,8 +5,15 @@ import {
DatasourceCreatedEvent,
DatasourceUpdatedEvent,
DatasourceDeletedEvent,
+ SourceName,
} from "@budibase/types"
+function isCustom(datasource: Datasource) {
+ const sources = Object.values(SourceName)
+ // if not in the base source list, then it must be custom
+ return !sources.includes(datasource.source)
+}
+
export async function created(
datasource: Datasource,
timestamp?: string | number
@@ -14,6 +21,7 @@ export async function created(
const properties: DatasourceCreatedEvent = {
datasourceId: datasource._id as string,
source: datasource.source,
+ custom: isCustom(datasource),
}
await publishEvent(Event.DATASOURCE_CREATED, properties, timestamp)
}
@@ -22,6 +30,7 @@ export async function updated(datasource: Datasource) {
const properties: DatasourceUpdatedEvent = {
datasourceId: datasource._id as string,
source: datasource.source,
+ custom: isCustom(datasource),
}
await publishEvent(Event.DATASOURCE_UPDATED, properties)
}
@@ -30,6 +39,7 @@ export async function deleted(datasource: Datasource) {
const properties: DatasourceDeletedEvent = {
datasourceId: datasource._id as string,
source: datasource.source,
+ custom: isCustom(datasource),
}
await publishEvent(Event.DATASOURCE_DELETED, properties)
}
diff --git a/packages/backend-core/src/events/publishers/index.ts b/packages/backend-core/src/events/publishers/index.ts
index 57fd0bf8e2..6fe42c4bda 100644
--- a/packages/backend-core/src/events/publishers/index.ts
+++ b/packages/backend-core/src/events/publishers/index.ts
@@ -18,3 +18,4 @@ export * as view from "./view"
export * as installation from "./installation"
export * as backfill from "./backfill"
export * as group from "./group"
+export * as plugin from "./plugin"
diff --git a/packages/backend-core/src/events/publishers/plugin.ts b/packages/backend-core/src/events/publishers/plugin.ts
new file mode 100644
index 0000000000..4e4d87cf56
--- /dev/null
+++ b/packages/backend-core/src/events/publishers/plugin.ts
@@ -0,0 +1,41 @@
+import { publishEvent } from "../events"
+import {
+ Event,
+ Plugin,
+ PluginDeletedEvent,
+ PluginImportedEvent,
+ PluginInitEvent,
+} from "@budibase/types"
+
+export async function init(plugin: Plugin) {
+ const properties: PluginInitEvent = {
+ type: plugin.schema.type,
+ name: plugin.name,
+ description: plugin.description,
+ version: plugin.version,
+ }
+ await publishEvent(Event.PLUGIN_INIT, properties)
+}
+
+export async function imported(plugin: Plugin) {
+ const properties: PluginImportedEvent = {
+ pluginId: plugin._id as string,
+ type: plugin.schema.type,
+ source: plugin.source,
+ name: plugin.name,
+ description: plugin.description,
+ version: plugin.version,
+ }
+ await publishEvent(Event.PLUGIN_IMPORTED, properties)
+}
+
+export async function deleted(plugin: Plugin) {
+ const properties: PluginDeletedEvent = {
+ pluginId: plugin._id as string,
+ type: plugin.schema.type,
+ name: plugin.name,
+ description: plugin.description,
+ version: plugin.version,
+ }
+ await publishEvent(Event.PLUGIN_DELETED, properties)
+}
diff --git a/packages/backend-core/src/featureFlags/index.js b/packages/backend-core/src/featureFlags/index.js
index 103ac4df59..b328839fda 100644
--- a/packages/backend-core/src/featureFlags/index.js
+++ b/packages/backend-core/src/featureFlags/index.js
@@ -31,20 +31,26 @@ const TENANT_FEATURE_FLAGS = getFeatureFlags()
exports.isEnabled = featureFlag => {
const tenantId = tenancy.getTenantId()
-
- return (
- TENANT_FEATURE_FLAGS &&
- TENANT_FEATURE_FLAGS[tenantId] &&
- TENANT_FEATURE_FLAGS[tenantId].includes(featureFlag)
- )
+ const flags = exports.getTenantFeatureFlags(tenantId)
+ return flags.includes(featureFlag)
}
exports.getTenantFeatureFlags = tenantId => {
- if (TENANT_FEATURE_FLAGS && TENANT_FEATURE_FLAGS[tenantId]) {
- return TENANT_FEATURE_FLAGS[tenantId]
+ const flags = []
+
+ if (TENANT_FEATURE_FLAGS) {
+ const globalFlags = TENANT_FEATURE_FLAGS["*"]
+ const tenantFlags = TENANT_FEATURE_FLAGS[tenantId]
+
+ if (globalFlags) {
+ flags.push(...globalFlags)
+ }
+ if (tenantFlags) {
+ flags.push(...tenantFlags)
+ }
}
- return []
+ return flags
}
exports.FeatureFlag = {
diff --git a/packages/backend-core/src/hashing.js b/packages/backend-core/src/hashing.js
index 45abe2f9bd..7524e66043 100644
--- a/packages/backend-core/src/hashing.js
+++ b/packages/backend-core/src/hashing.js
@@ -1,5 +1,5 @@
-const bcrypt = require("bcrypt")
const env = require("./environment")
+const bcrypt = env.JS_BCRYPT ? require("bcryptjs") : require("bcrypt")
const { v4 } = require("uuid")
const SALT_ROUNDS = env.SALT_ROUNDS || 10
diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts
index 74e79e7b95..2c234bd4b8 100644
--- a/packages/backend-core/src/index.ts
+++ b/packages/backend-core/src/index.ts
@@ -1,5 +1,4 @@
import errors from "./errors"
-
const errorClasses = errors.errors
import * as events from "./events"
import * as migrations from "./migrations"
@@ -15,9 +14,10 @@ import deprovisioning from "./context/deprovision"
import auth from "./auth"
import constants from "./constants"
import * as dbConstants from "./db/constants"
-import logging from "./logging"
+import * as 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 +56,7 @@ const core = {
errors,
logging,
roles,
+ plugins,
...pino,
...errorClasses,
middleware,
diff --git a/packages/backend-core/src/middleware/authenticated.ts b/packages/backend-core/src/middleware/authenticated.ts
index 062070785d..a3c6b67cde 100644
--- a/packages/backend-core/src/middleware/authenticated.ts
+++ b/packages/backend-core/src/middleware/authenticated.ts
@@ -106,6 +106,7 @@ export = (
user = await getUser(userId, session.tenantId)
}
user.csrfToken = session.csrfToken
+
if (session?.lastAccessedAt < timeMinusOneMinute()) {
// make sure we denote that the session is still in use
await updateSessionTTL(session)
diff --git a/packages/backend-core/src/migrations/definitions.ts b/packages/backend-core/src/migrations/definitions.ts
index 34ec0f0cad..0eea946be8 100644
--- a/packages/backend-core/src/migrations/definitions.ts
+++ b/packages/backend-core/src/migrations/definitions.ts
@@ -17,14 +17,6 @@ export const DEFINITIONS: MigrationDefinition[] = [
type: MigrationType.APP,
name: MigrationName.APP_URLS,
},
- {
- type: MigrationType.GLOBAL,
- name: MigrationName.DEVELOPER_QUOTA,
- },
- {
- type: MigrationType.GLOBAL,
- name: MigrationName.PUBLISHED_APP_QUOTA,
- },
{
type: MigrationType.APP,
name: MigrationName.EVENT_APP_BACKFILL,
diff --git a/packages/backend-core/src/migrations/migrations.ts b/packages/backend-core/src/migrations/migrations.ts
index ca238ff80e..90a12acec2 100644
--- a/packages/backend-core/src/migrations/migrations.ts
+++ b/packages/backend-core/src/migrations/migrations.ts
@@ -3,12 +3,8 @@ import { doWithDB } from "../db"
import { DocumentType, StaticDatabases } from "../db/constants"
import { getAllApps } from "../db/utils"
import environment from "../environment"
-import {
- doInTenant,
- getTenantIds,
- getGlobalDBName,
- getTenantId,
-} from "../tenancy"
+import { doInTenant, getTenantIds, getTenantId } from "../tenancy"
+import { getGlobalDBName } from "../db/tenancy"
import * as context from "../context"
import { DEFINITIONS } from "."
import {
diff --git a/packages/backend-core/src/objectStore/index.ts b/packages/backend-core/src/objectStore/index.ts
index 1b880ef7b2..a97aa8f65d 100644
--- a/packages/backend-core/src/objectStore/index.ts
+++ b/packages/backend-core/src/objectStore/index.ts
@@ -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}`)
}
diff --git a/packages/backend-core/src/objectStore/utils.js b/packages/backend-core/src/objectStore/utils.js
index a243553df8..acc1b9904e 100644
--- a/packages/backend-core/src/objectStore/utils.js
+++ b/packages/backend-core/src/objectStore/utils.js
@@ -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 () {
diff --git a/packages/backend-core/src/plugin/index.ts b/packages/backend-core/src/plugin/index.ts
new file mode 100644
index 0000000000..a6d1853007
--- /dev/null
+++ b/packages/backend-core/src/plugin/index.ts
@@ -0,0 +1,7 @@
+import * as utils from "./utils"
+
+const pkg = {
+ ...utils,
+}
+
+export = pkg
diff --git a/packages/backend-core/src/plugin/utils.js b/packages/backend-core/src/plugin/utils.js
new file mode 100644
index 0000000000..020fb4484d
--- /dev/null
+++ b/packages/backend-core/src/plugin/utils.js
@@ -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}`)
+ }
+}
diff --git a/packages/backend-core/src/security/sessions.ts b/packages/backend-core/src/security/sessions.ts
index f621b99dc2..33230afc60 100644
--- a/packages/backend-core/src/security/sessions.ts
+++ b/packages/backend-core/src/security/sessions.ts
@@ -2,28 +2,12 @@ const redis = require("../redis/init")
const { v4: uuidv4 } = require("uuid")
const { logWarn } = require("../logging")
const env = require("../environment")
-
-interface CreateSession {
- sessionId: string
- tenantId: string
- csrfToken?: string
-}
-
-interface Session extends CreateSession {
- userId: string
- lastAccessedAt: string
- createdAt: string
- // make optional attributes required
- csrfToken: string
-}
-
-interface SessionKey {
- key: string
-}
-
-interface ScannedSession {
- value: Session
-}
+import {
+ Session,
+ ScannedSession,
+ SessionKey,
+ CreateSession,
+} from "@budibase/types"
// a week in seconds
const EXPIRY_SECONDS = 86400 * 7
diff --git a/packages/backend-core/src/tenancy/index.ts b/packages/backend-core/src/tenancy/index.ts
index d1ccbbf001..e0006abab2 100644
--- a/packages/backend-core/src/tenancy/index.ts
+++ b/packages/backend-core/src/tenancy/index.ts
@@ -1,11 +1,9 @@
import * as context from "../context"
import * as tenancy from "./tenancy"
-import * as utils from "./utils"
const pkg = {
...context,
...tenancy,
- ...utils,
}
export = pkg
diff --git a/packages/backend-core/src/tenancy/tenancy.ts b/packages/backend-core/src/tenancy/tenancy.ts
index 1c71935eb0..a100888212 100644
--- a/packages/backend-core/src/tenancy/tenancy.ts
+++ b/packages/backend-core/src/tenancy/tenancy.ts
@@ -1,7 +1,7 @@
import { doWithDB } from "../db"
import { queryPlatformView } from "../db/views"
import { StaticDatabases, ViewName } from "../db/constants"
-import { getGlobalDBName } from "./utils"
+import { getGlobalDBName } from "../db/tenancy"
import {
getTenantId,
DEFAULT_TENANT_ID,
@@ -9,7 +9,7 @@ import {
getTenantIDFromAppID,
} from "../context"
import env from "../environment"
-import { PlatformUser, PlatformUserByEmail } from "@budibase/types"
+import { PlatformUser } from "@budibase/types"
const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants
const PLATFORM_INFO_DB = StaticDatabases.PLATFORM_INFO.name
diff --git a/packages/backend-core/src/users.js b/packages/backend-core/src/users.ts
similarity index 60%
rename from packages/backend-core/src/users.js
rename to packages/backend-core/src/users.ts
index 81bf28bb46..0793eeb1d9 100644
--- a/packages/backend-core/src/users.js
+++ b/packages/backend-core/src/users.ts
@@ -1,29 +1,39 @@
-const {
+import {
ViewName,
getUsersByAppParams,
getProdAppID,
generateAppUserID,
-} = require("./db/utils")
-const { queryGlobalView } = require("./db/views")
-const { UNICODE_MAX } = require("./db/constants")
+} from "./db/utils"
+import { queryGlobalView } from "./db/views"
+import { UNICODE_MAX } from "./db/constants"
+import { User } from "@budibase/types"
/**
* Given an email address this will use a view to search through
* all the users to find one with this email address.
* @param {string} email the email to lookup the user by.
*/
-exports.getGlobalUserByEmail = async email => {
+export const getGlobalUserByEmail = async (
+ email: String
+): Promise => {
if (email == null) {
throw "Must supply an email address to view"
}
- return await queryGlobalView(ViewName.USER_BY_EMAIL, {
+ const response = await queryGlobalView(ViewName.USER_BY_EMAIL, {
key: email.toLowerCase(),
include_docs: true,
})
+
+ if (Array.isArray(response)) {
+ // shouldn't be able to happen, but need to handle just in case
+ throw new Error(`Multiple users found with email address: ${email}`)
+ }
+
+ return response
}
-exports.searchGlobalUsersByApp = async (appId, opts) => {
+export const searchGlobalUsersByApp = async (appId: any, opts: any) => {
if (typeof appId !== "string") {
throw new Error("Must provide a string based app ID")
}
@@ -38,24 +48,24 @@ exports.searchGlobalUsersByApp = async (appId, opts) => {
return Array.isArray(response) ? response : [response]
}
-exports.getGlobalUserByAppPage = (appId, user) => {
+export const getGlobalUserByAppPage = (appId: string, user: User) => {
if (!user) {
return
}
- return generateAppUserID(getProdAppID(appId), user._id)
+ return generateAppUserID(getProdAppID(appId), user._id!)
}
/**
* Performs a starts with search on the global email view.
*/
-exports.searchGlobalUsersByEmail = async (email, opts) => {
+export const searchGlobalUsersByEmail = async (email: string, opts: any) => {
if (typeof email !== "string") {
throw new Error("Must provide a string to search by")
}
const lcEmail = email.toLowerCase()
// handle if passing up startkey for pagination
const startkey = opts && opts.startkey ? opts.startkey : lcEmail
- let response = await queryGlobalView(ViewName.USER_BY_EMAIL, {
+ let response = await queryGlobalView(ViewName.USER_BY_EMAIL, {
...opts,
startkey,
endkey: `${lcEmail}${UNICODE_MAX}`,
diff --git a/packages/backend-core/src/utils.js b/packages/backend-core/src/utils.js
index 0587267e9a..6b59c7cb72 100644
--- a/packages/backend-core/src/utils.js
+++ b/packages/backend-core/src/utils.js
@@ -42,6 +42,18 @@ async function resolveAppUrl(ctx) {
return app && app.appId ? app.appId : undefined
}
+exports.isServingApp = ctx => {
+ // dev app
+ if (ctx.path.startsWith(`/${APP_PREFIX}`)) {
+ return true
+ }
+ // prod app
+ if (ctx.path.startsWith(PROD_APP_PREFIX)) {
+ return true
+ }
+ return false
+}
+
/**
* Given a request tries to find the appId, which can be located in various places
* @param {object} ctx The main request body to look through.
diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock
index 22c17a9444..2e62aea734 100644
--- a/packages/backend-core/yarn.lock
+++ b/packages/backend-core/yarn.lock
@@ -1377,6 +1377,11 @@ bcrypt@5.0.1:
"@mapbox/node-pre-gyp" "^1.0.0"
node-addon-api "^3.1.0"
+bcryptjs@2.4.3:
+ version "2.4.3"
+ resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
+ integrity sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==
+
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
diff --git a/packages/bbui/package.json b/packages/bbui/package.json
index 8e41e3773f..d0916fda08 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": "1.3.21",
+ "version": "1.3.22-alpha.4",
"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.21",
+ "@budibase/string-templates": "1.3.22-alpha.4",
"@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2",
diff --git a/packages/bbui/src/Actions/position_dropdown.js b/packages/bbui/src/Actions/position_dropdown.js
index a25cc1bbd5..7570a39c8c 100644
--- a/packages/bbui/src/Actions/position_dropdown.js
+++ b/packages/bbui/src/Actions/position_dropdown.js
@@ -1,4 +1,4 @@
-export default function positionDropdown(element, { anchor, align }) {
+export default function positionDropdown(element, { anchor, align, maxWidth }) {
let positionSide = "top"
let maxHeight = 0
let dimensions = getDimensions(anchor)
@@ -34,13 +34,24 @@ export default function positionDropdown(element, { anchor, align }) {
}
function calcLeftPosition() {
- return align === "right"
- ? dimensions.left + dimensions.width - dimensions.containerWidth
- : dimensions.left
+ let left
+
+ if (align == "right") {
+ left = dimensions.left + dimensions.width - dimensions.containerWidth
+ } else if (align == "right-side") {
+ left = dimensions.left + dimensions.width
+ } else {
+ left = dimensions.left
+ }
+
+ return left
}
element.style.position = "absolute"
element.style.zIndex = "9999"
+ if (maxWidth) {
+ element.style.maxWidth = `${maxWidth}px`
+ }
element.style.minWidth = `${dimensions.width}px`
element.style.maxHeight = `${maxHeight.toFixed(0)}px`
element.style.transformOrigin = `center ${positionSide}`
@@ -54,10 +65,8 @@ export default function positionDropdown(element, { anchor, align }) {
element.style.left = `${calcLeftPosition(dimensions).toFixed(0)}px`
})
})
-
resizeObserver.observe(anchor)
resizeObserver.observe(element)
-
return {
destroy() {
resizeObserver.disconnect()
diff --git a/packages/bbui/src/Banner/BannerDisplay.svelte b/packages/bbui/src/Banner/BannerDisplay.svelte
index aad742b1bd..9ea2eaf2ec 100644
--- a/packages/bbui/src/Banner/BannerDisplay.svelte
+++ b/packages/bbui/src/Banner/BannerDisplay.svelte
@@ -4,22 +4,32 @@
import { banner } from "../Stores/banner"
import Banner from "./Banner.svelte"
import { fly } from "svelte/transition"
+ import TooltipWrapper from "../Tooltip/TooltipWrapper.svelte"
- {#if $banner.message}
+ {#each $banner.messages as message}
{
+ if (message.onChange) {
+ message.onChange()
+ }
+ }}
+ showCloseButton={typeof message.showCloseButton === "boolean"
+ ? message.showCloseButton
+ : true}
>
- {$banner.message}
+
+ {message.message}
+
- {/if}
+ {/each}
diff --git a/packages/bbui/src/InlineAlert/InlineAlert.svelte b/packages/bbui/src/InlineAlert/InlineAlert.svelte
index 25e14b7caf..94ac6b2c2a 100644
--- a/packages/bbui/src/InlineAlert/InlineAlert.svelte
+++ b/packages/bbui/src/InlineAlert/InlineAlert.svelte
@@ -6,6 +6,7 @@
export let header = ""
export let message = ""
export let onConfirm = undefined
+ export let buttonText = ""
$: icon = selectIcon(type)
// if newlines used, convert them to different elements
@@ -39,13 +40,16 @@
{splitMsg}
{/each}
{#if onConfirm}
-
diff --git a/packages/bbui/src/Modal/ModalContent.svelte b/packages/bbui/src/Modal/ModalContent.svelte
index 6d609d6f1b..25fac63ec8 100644
--- a/packages/bbui/src/Modal/ModalContent.svelte
+++ b/packages/bbui/src/Modal/ModalContent.svelte
@@ -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 @@
- {#if showCancelButton || showConfirmButton}
+ {#if showCancelButton || showConfirmButton || $$slots.footer}
{#if showTooltip}
@@ -54,7 +55,6 @@
transform: scale(0.75);
}
.icon-small {
- margin-top: -2px;
- margin-bottom: -5px;
+ margin-bottom: -2px;
}
diff --git a/packages/bbui/src/index.js b/packages/bbui/src/index.js
index b45f3e9ed6..ead226bdc3 100644
--- a/packages/bbui/src/index.js
+++ b/packages/bbui/src/index.js
@@ -34,6 +34,7 @@ export { default as Layout } from "./Layout/Layout.svelte"
export { default as Page } from "./Layout/Page.svelte"
export { default as Link } from "./Link/Link.svelte"
export { default as Tooltip } from "./Tooltip/Tooltip.svelte"
+export { default as TooltipWrapper } from "./Tooltip/TooltipWrapper.svelte"
export { default as Menu } from "./Menu/Menu.svelte"
export { default as MenuSection } from "./Menu/Section.svelte"
export { default as MenuSeparator } from "./Menu/Separator.svelte"
@@ -94,7 +95,7 @@ export { default as clickOutside } from "./Actions/click_outside"
// Stores
export { notifications, createNotificationStore } from "./Stores/notifications"
-export { banner } from "./Stores/banner"
+export { banner, BANNER_TYPES } from "./Stores/banner"
// Helpers
export * as Helpers from "./helpers"
diff --git a/packages/builder/cypress/integration/adminAndManagement/userManagement.spec.js b/packages/builder/cypress/integration/adminAndManagement/userManagement.spec.js
index 5a864e3bb3..000ca7cb54 100644
--- a/packages/builder/cypress/integration/adminAndManagement/userManagement.spec.js
+++ b/packages/builder/cypress/integration/adminAndManagement/userManagement.spec.js
@@ -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(() => {
diff --git a/packages/builder/cypress/integration/autoScreensUI.spec.js b/packages/builder/cypress/integration/autoScreensUI.spec.js
index 7a5dbef5a5..0253675c5b 100644
--- a/packages/builder/cypress/integration/autoScreensUI.spec.js
+++ b/packages/builder/cypress/integration/autoScreensUI.spec.js
@@ -82,10 +82,10 @@ filterTests(['smoke', 'all'], () => {
})
if (Cypress.env("TEST_ENV")) {
- it("should generate data source screens", () => {
- // Using MySQL data source for testing this
+ it("should generate datasource screens", () => {
+ // Using MySQL datasource for testing this
const datasource = "MySQL"
- // Select & configure MySQL data source
+ // Select & configure MySQL datasource
cy.selectExternalDatasource(datasource)
cy.addDatasourceConfig(datasource)
// Create Autogenerated screens from a MySQL table - MySQL contains books table
diff --git a/packages/builder/cypress/integration/createBinding.spec.js b/packages/builder/cypress/integration/createBinding.spec.js
index 160f23d2d6..0c1ddf1e7d 100644
--- a/packages/builder/cypress/integration/createBinding.spec.js
+++ b/packages/builder/cypress/integration/createBinding.spec.js
@@ -10,7 +10,7 @@ filterTests(['smoke', 'all'], () => {
it("should add a current user binding", () => {
cy.searchAndAddComponent("Paragraph").then(() => {
- addSettingBinding("text", "Current User._id")
+ addSettingBinding("text", ["Current User", "_id"], "Current User._id")
})
})
@@ -28,7 +28,7 @@ filterTests(['smoke', 'all'], () => {
const paramName = "foo"
cy.createScreen(`/test/:${paramName}`)
cy.searchAndAddComponent("Paragraph").then(componentId => {
- addSettingBinding("text", `URL.${paramName}`)
+ addSettingBinding("text", ["URL", paramName], `URL.${paramName}`)
// The builder preview pages don't have a real URL, so all we can do
// is check that we were able to bind to the property, and that the
// component exists on the page
@@ -47,11 +47,13 @@ filterTests(['smoke', 'all'], () => {
})
})
- const addSettingBinding = (setting, bindingText, clickOption = true) => {
+ const addSettingBinding = (setting, bindingCategories, bindingText, clickOption = true) => {
cy.get(`[data-cy="setting-${setting}"] [data-cy=text-binding-button]`).click()
+ cy.get(".category-list li").contains(bindingCategories[0])
cy.get(".drawer").within(() => {
if (clickOption) {
- cy.contains(bindingText).click()
+ cy.get(".category-list li").contains(bindingCategories[0]).click()
+ cy.get("li.binding").contains(bindingCategories[1]).click()
cy.get("textarea").should("have.value", `{{ ${bindingText} }}`)
} else {
cy.get("textarea").type(bindingText)
diff --git a/packages/builder/cypress/integration/createComponents.spec.js b/packages/builder/cypress/integration/createComponents.spec.js
index 649a77e442..7f29466258 100644
--- a/packages/builder/cypress/integration/createComponents.spec.js
+++ b/packages/builder/cypress/integration/createComponents.spec.js
@@ -2,7 +2,7 @@ import filterTests from "../support/filterTests"
const interact = require("../support/interact")
filterTests(["all"], () => {
- context("Create Components", () => {
+ xcontext("Create Components", () => {
let headlineId
before(() => {
@@ -20,7 +20,7 @@ filterTests(["all"], () => {
//Use the tree to delete a selected component
const deleteSelectedComponent = () => {
cy.get(
- ".nav-items-container .nav-item.selected .actions > div > .icon"
+ ".nav-item.selected .actions > div > .icon"
).click({
force: true,
})
@@ -91,7 +91,7 @@ filterTests(["all"], () => {
cy.searchAndAddComponent("Paragraph").then(componentId => {
cy.get("[data-cy=setting-_instanceName] input").type(componentId).blur()
cy.get(
- ".nav-items-container .nav-item.selected .actions > div > .icon"
+ ".nav-item.selected .actions > div > .icon"
).click({
force: true,
})
@@ -145,7 +145,7 @@ filterTests(["all"], () => {
return testFieldFocusOnCreate(label)
})
.then(() => {
- cy.get(".nav-items-container .nav-item")
+ cy.get(".nav-item")
.contains(formId)
.click({ force: true })
deleteSelectedComponent()
@@ -195,7 +195,7 @@ filterTests(["all"], () => {
return testFocusOnCreate(label)
})
.then(() => {
- cy.get(".nav-items-container .nav-item")
+ cy.get(".nav-item")
.contains(providerId)
.click({ force: true })
deleteSelectedComponent()
@@ -218,7 +218,7 @@ filterTests(["all"], () => {
.find(".component-placeholder")
.should("not.exist")
cy.getComponent(imageId).find(`img[alt=${imageId}]`).should("exist")
- cy.get(".nav-items-container .nav-item")
+ cy.get(".nav-item")
.contains(imageId)
.click({ force: true })
deleteSelectedComponent()
@@ -242,7 +242,7 @@ filterTests(["all"], () => {
cy.getComponent(markdownId)
.find(".editor-preview-full h1")
.contains("Hi")
- cy.get(".nav-items-container .nav-item")
+ cy.get(".nav-item")
.contains(markdownId)
.click({ force: true })
deleteSelectedComponent()
@@ -265,7 +265,7 @@ filterTests(["all"], () => {
.find(".component-placeholder")
.should("not.exist")
cy.getComponent(iconId).find("i.ri-save-fill").should("exist")
- cy.get(".nav-items-container .nav-item")
+ cy.get(".nav-item")
.contains(iconId)
.click({ force: true })
deleteSelectedComponent()
diff --git a/packages/builder/cypress/integration/datasources/datasourceWizard.spec.js b/packages/builder/cypress/integration/datasources/datasourceWizard.spec.js
index 14653d8286..837a433951 100644
--- a/packages/builder/cypress/integration/datasources/datasourceWizard.spec.js
+++ b/packages/builder/cypress/integration/datasources/datasourceWizard.spec.js
@@ -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()
diff --git a/packages/builder/cypress/integration/datasources/mySql.spec.js b/packages/builder/cypress/integration/datasources/mySql.spec.js
index 654705a24e..33aa72f0bb 100644
--- a/packages/builder/cypress/integration/datasources/mySql.spec.js
+++ b/packages/builder/cypress/integration/datasources/mySql.spec.js
@@ -11,8 +11,8 @@ filterTests(["all"], () => {
const queryName = "Cypress Test Query"
const queryRename = "CT Query Rename"
- it("Should add MySQL data source without configuration", () => {
- // Select MySQL data source
+ it("Should add MySQL datasource without configuration", () => {
+ // Select MySQL datasource
cy.selectExternalDatasource(datasource)
// Attempt to fetch tables without applying configuration
cy.intercept("**/datasources").as("datasource")
@@ -35,8 +35,8 @@ filterTests(["all"], () => {
cy.get(".spectrum-Button").contains("Skip table fetch").click({ force: true })
})
- it("should add MySQL data source and fetch tables", () => {
- // Add & configure MySQL data source
+ it("should add MySQL datasource and fetch tables", () => {
+ // Add & configure MySQL datasource
cy.selectExternalDatasource(datasource)
cy.intercept("**/datasources").as("datasource")
cy.addDatasourceConfig(datasource)
@@ -52,7 +52,7 @@ filterTests(["all"], () => {
})
it("should check table fetching error", () => {
- // MySQL test data source contains tables without primary keys
+ // MySQL test datasource contains tables without primary keys
cy.get(".spectrum-InLineAlert")
.should("contain", "Error fetching tables")
.and("contain", "No primary key constraint found")
diff --git a/packages/builder/cypress/integration/datasources/oracle.spec.js b/packages/builder/cypress/integration/datasources/oracle.spec.js
index 92a5737ff9..ae1ca5cd75 100644
--- a/packages/builder/cypress/integration/datasources/oracle.spec.js
+++ b/packages/builder/cypress/integration/datasources/oracle.spec.js
@@ -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()
@@ -11,8 +11,8 @@ filterTests(["all"], () => {
const queryName = "Cypress Test Query"
const queryRename = "CT Query Rename"
- it("Should add Oracle data source and skip table fetch", () => {
- // Select Oracle data source
+ it("Should add Oracle datasource and skip table fetch", () => {
+ // Select Oracle datasource
cy.selectExternalDatasource(datasource)
// Skip table fetch - no config added
cy.get(".spectrum-Button")
@@ -23,7 +23,7 @@ filterTests(["all"], () => {
cy.get(".spectrum-Textfield-input", { timeout: 500 })
.eq(1)
.should("have.value", "localhost")
- // Add another Oracle data source, configure & skip table fetch
+ // Add another Oracle datasource, configure & skip table fetch
cy.selectExternalDatasource(datasource)
cy.addDatasourceConfig(datasource, true)
// Confirm config and no tables
@@ -33,8 +33,8 @@ filterTests(["all"], () => {
cy.get(".spectrum-Body").eq(2).should("contain", "No tables found.")
})
- it("Should add Oracle data source and fetch tables without configuration", () => {
- // Select Oracle data source
+ it("Should add Oracle datasource and fetch tables without configuration", () => {
+ // Select Oracle datasource
cy.selectExternalDatasource(datasource)
// Attempt to fetch tables without applying configuration
cy.intercept("**/datasources").as("datasource")
@@ -49,8 +49,8 @@ filterTests(["all"], () => {
cy.get(".spectrum-Button").contains("Skip table fetch").click({ force: true })
})
- xit("should add Oracle data source and fetch tables", () => {
- // Add & configure Oracle data source
+ xit("should add Oracle datasource and fetch tables", () => {
+ // Add & configure Oracle datasource
cy.selectExternalDatasource(datasource)
cy.intercept("**/datasources").as("datasource")
cy.addDatasourceConfig(datasource)
diff --git a/packages/builder/cypress/integration/datasources/postgreSql.spec.js b/packages/builder/cypress/integration/datasources/postgreSql.spec.js
index bfb312a0c8..8ef574566e 100644
--- a/packages/builder/cypress/integration/datasources/postgreSql.spec.js
+++ b/packages/builder/cypress/integration/datasources/postgreSql.spec.js
@@ -11,8 +11,8 @@ filterTests(["all"], () => {
const queryName = "Cypress Test Query"
const queryRename = "CT Query Rename"
- xit("Should add PostgreSQL data source without configuration", () => {
- // Select PostgreSQL data source
+ xit("Should add PostgreSQL datasource without configuration", () => {
+ // Select PostgreSQL datasource
cy.selectExternalDatasource(datasource)
// Attempt to fetch tables without applying configuration
cy.intercept("**/datasources").as("datasource")
@@ -27,8 +27,8 @@ filterTests(["all"], () => {
cy.get(".spectrum-Button").contains("Skip table fetch").click({ force: true })
})
- it("should add PostgreSQL data source and fetch tables", () => {
- // Add & configure PostgreSQL data source
+ it("should add PostgreSQL datasource and fetch tables", () => {
+ // Add & configure PostgreSQL datasource
cy.selectExternalDatasource(datasource)
cy.intercept("**/datasources").as("datasource")
cy.addDatasourceConfig(datasource)
@@ -162,7 +162,7 @@ filterTests(["all"], () => {
switchSchema("randomText")
// No tables displayed
- cy.get(".spectrum-Body", { timeout: 5000 }).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)
@@ -173,7 +173,7 @@ filterTests(["all"], () => {
switchSchema("1")
// Confirm tables exist - Check for specific one
- cy.get(".spectrum-Table", { timeout: 5000 }).eq(0).should("contain", "test")
+ cy.get(".spectrum-Table", { timeout: 20000 }).eq(0).should("contain", "test")
cy.get(".spectrum-Table")
.eq(0)
.find(".spectrum-Table-row")
@@ -187,7 +187,7 @@ filterTests(["all"], () => {
switchSchema("public")
// Confirm tables exist - again
- cy.get(".spectrum-Table", { timeout: 5000 }).eq(0).should("contain", "REGIONS")
+ cy.get(".spectrum-Table", { timeout: 20000 }).eq(0).should("contain", "REGIONS")
cy.get(".spectrum-Table")
.eq(0)
.find(".spectrum-Table-row")
diff --git a/packages/builder/cypress/integration/datasources/rest.spec.js b/packages/builder/cypress/integration/datasources/rest.spec.js
index 488c30c0cf..ec9864a47d 100644
--- a/packages/builder/cypress/integration/datasources/rest.spec.js
+++ b/packages/builder/cypress/integration/datasources/rest.spec.js
@@ -10,11 +10,11 @@ filterTests(["smoke", "all"], () => {
const datasource = "REST"
const restUrl = "https://api.openbrewerydb.org/breweries"
- it("Should add REST data source with incorrect API", () => {
- // Select REST data source
+ it("Should add REST datasource with incorrect API", () => {
+ // Select REST datasource
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 })
diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js
index d4b0ec80e8..53accfbbe4 100644
--- a/packages/builder/cypress/support/commands.js
+++ b/packages/builder/cypress/support/commands.js
@@ -4,7 +4,7 @@ Cypress.on("uncaught:exception", () => {
// ACCOUNTS & USERS
Cypress.Commands.add("login", (email, password) => {
- cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 10000 })
+ cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 30000 })
cy.url()
.should("include", "/builder/")
.then(url => {
@@ -33,7 +33,7 @@ Cypress.Commands.add("login", (email, password) => {
})
Cypress.Commands.add("logOut", () => {
- cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 2000 })
+ cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 30000 })
cy.get(".user-dropdown .avatar > .icon").click({ force: true })
cy.get(".spectrum-Popover[data-cy='user-menu']").within(() => {
cy.get("li[data-cy='user-logout']").click({ force: true })
@@ -43,7 +43,7 @@ Cypress.Commands.add("logOut", () => {
Cypress.Commands.add("logoutNoAppGrid", () => {
// Logs user out when app grid is not present
- cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 30000 })
cy.get(".avatar > .icon").click({ force: true })
cy.get(".spectrum-Popover[data-cy='user-menu']").within(() => {
cy.get(".spectrum-Menu-item").contains("Log out").click({ force: true })
@@ -68,11 +68,14 @@ Cypress.Commands.add("createUser", (email, permission) => {
.click({ force: true })
})
}
- // Add user and wait for modal to change
- cy.get(".spectrum-Button").contains("Add user").click({ force: true })
+ // Add user
+ cy.get(".spectrum-Button").contains("Add users").click({ force: true })
cy.get(".spectrum-ActionButton").contains("Add email").should("not.exist")
})
// Onboarding modal
+ cy.get(".spectrum-Dialog-grid", { timeout: 5000 }).contains(
+ "Choose your onboarding"
+ )
cy.get(".spectrum-Dialog-grid").within(() => {
cy.get(".onboarding-type").eq(1).click()
cy.get(".spectrum-Button").contains("Done").click({ force: true })
@@ -163,7 +166,7 @@ Cypress.Commands.add("createApp", (name, addDefaultTable) => {
const shouldCreateDefaultTable =
typeof addDefaultTable != "boolean" ? true : addDefaultTable
- cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 10000 })
+ cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 30000 })
cy.url({ timeout: 30000 }).should("include", "/apps")
cy.get(`[data-cy="create-app-btn"]`, { timeout: 5000 }).click({ force: true })
@@ -197,7 +200,7 @@ Cypress.Commands.add("createApp", (name, addDefaultTable) => {
})
Cypress.Commands.add("deleteApp", name => {
- cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 5000 })
+ cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 30000 })
cy.wait(2000)
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
.its("body")
@@ -254,7 +257,7 @@ Cypress.Commands.add("deleteApp", name => {
})
Cypress.Commands.add("deleteAllApps", () => {
- cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 5000 })
+ cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 30000 })
cy.wait(500)
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`, {
timeout: 5000,
@@ -351,7 +354,7 @@ Cypress.Commands.add("alterAppVersion", (appId, version) => {
})
Cypress.Commands.add("importApp", (exportFilePath, name) => {
- cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 5000 })
+ cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 30000 })
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
.its("body")
@@ -386,7 +389,7 @@ Cypress.Commands.add("importApp", (exportFilePath, name) => {
// Filters visible with 1 or more
Cypress.Commands.add("searchForApplication", appName => {
- cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 30000 })
cy.wait(2000)
// No app filter functionality if only 1 app exists
@@ -409,7 +412,7 @@ Cypress.Commands.add("searchForApplication", appName => {
// Assumes there are no others
Cypress.Commands.add("applicationInAppTable", appName => {
- cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 10000 })
+ cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 30000 })
cy.get(".appTable", { timeout: 5000 }).within(() => {
cy.get(".title").contains(appName).should("exist")
})
@@ -454,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
)
@@ -634,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 })
@@ -758,7 +763,7 @@ Cypress.Commands.add("navigateToDataSection", () => {
})
Cypress.Commands.add("navigateToAutogeneratedModal", () => {
- // Screen name must already exist within data source
+ // Screen name must already exist within datasource
cy.contains("Design").click()
cy.get(".spectrum-Button").contains("Add screen").click({ force: true })
cy.get(".spectrum-Modal").within(() => {
@@ -774,7 +779,7 @@ Cypress.Commands.add("navigateToAutogeneratedModal", () => {
Cypress.Commands.add("selectExternalDatasource", datasourceName => {
// Navigates to Data Section
cy.navigateToDataSection()
- // Open Data Source modal
+ // Open Datasource modal
cy.get(".nav").within(() => {
cy.get(".add-button").click()
})
@@ -912,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", () => {
diff --git a/packages/builder/package.json b/packages/builder/package.json
index 11c96159c3..d1ec0cadf1 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
- "version": "1.3.21",
+ "version": "1.3.22-alpha.4",
"license": "GPL-3.0",
"private": true,
"scripts": {
@@ -71,10 +71,10 @@
}
},
"dependencies": {
- "@budibase/bbui": "^1.3.21",
- "@budibase/client": "^1.3.21",
- "@budibase/frontend-core": "^1.3.21",
- "@budibase/string-templates": "^1.3.21",
+ "@budibase/bbui": "1.3.22-alpha.4",
+ "@budibase/client": "1.3.22-alpha.4",
+ "@budibase/frontend-core": "1.3.22-alpha.4",
+ "@budibase/string-templates": "1.3.22-alpha.4",
"@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
@@ -98,7 +98,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",
@@ -120,7 +120,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"
}
diff --git a/packages/builder/src/App.svelte b/packages/builder/src/App.svelte
index 0fb0fe59d5..4d193df104 100644
--- a/packages/builder/src/App.svelte
+++ b/packages/builder/src/App.svelte
@@ -4,6 +4,7 @@
import { NotificationDisplay, BannerDisplay } from "@budibase/bbui"
import { parse, stringify } from "qs"
import HelpIcon from "components/common/HelpIcon.svelte"
+ import LicensingOverlays from "components/portal/licensing/LicensingOverlays.svelte"
const queryHandler = { parse, stringify }
@@ -12,6 +13,9 @@
+
+
+
diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js
index 36c88c162d..b6312ef8e8 100644
--- a/packages/builder/src/builderStore/dataBinding.js
+++ b/packages/builder/src/builderStore/dataBinding.js
@@ -297,7 +297,10 @@ const getProviderContextBindings = (asset, dataProviders) => {
schema = {}
const values = context.values || []
values.forEach(value => {
- schema[value.key] = { name: value.label, type: "string" }
+ schema[value.key] = {
+ name: value.label,
+ type: value.type || "string",
+ }
})
} else if (context.type === "schema") {
// Schema contexts are generated dynamically depending on their data
@@ -357,6 +360,12 @@ const getProviderContextBindings = (asset, dataProviders) => {
providerId,
// Table ID is used by JSON fields to know what table the field is in
tableId: table?._id,
+ category: component._instanceName,
+ icon: def.icon,
+ display: {
+ name: fieldSchema.name || key,
+ type: fieldSchema.type,
+ },
})
})
})
@@ -383,6 +392,9 @@ export const getUserBindings = () => {
// datasource options, based on bindable properties
fieldSchema,
providerId: "user",
+ category: "Current User",
+ icon: "User",
+ display: fieldSchema,
})
})
return bindings
@@ -399,11 +411,17 @@ const getDeviceBindings = () => {
type: "context",
runtimeBinding: `${safeDevice}.${makePropSafe("mobile")}`,
readableBinding: `Device.Mobile`,
+ category: "Device",
+ icon: "DevicePhone",
+ display: { type: "boolean", name: "mobile" },
})
bindings.push({
type: "context",
runtimeBinding: `${safeDevice}.${makePropSafe("tablet")}`,
readableBinding: `Device.Tablet`,
+ category: "Device",
+ icon: "DevicePhone",
+ display: { type: "boolean", name: "tablet" },
})
}
return bindings
@@ -427,6 +445,8 @@ const getSelectedRowsBindings = asset => {
"selectedRows"
)}`,
readableBinding: `${table._instanceName}.Selected rows`,
+ category: "Selected rows",
+ icon: "ViewRow",
}))
)
@@ -458,6 +478,9 @@ const getStateBindings = () => {
type: "context",
runtimeBinding: `${safeState}.${makePropSafe(key)}`,
readableBinding: `State.${key}`,
+ category: "State",
+ icon: "AutomatedSegment",
+ display: { name: key },
}))
}
return bindings
@@ -480,11 +503,17 @@ const getUrlBindings = asset => {
type: "context",
runtimeBinding: `${safeURL}.${makePropSafe(param)}`,
readableBinding: `URL.${param}`,
+ category: "URL",
+ icon: "RailTop",
+ display: { type: "string" },
}))
const queryParamsBinding = {
type: "context",
runtimeBinding: makePropSafe("query"),
readableBinding: "Query params",
+ category: "URL",
+ icon: "RailTop",
+ display: { type: "object" },
}
return urlParamBindings.concat([queryParamsBinding])
}
@@ -495,6 +524,9 @@ const getRoleBindings = () => {
type: "context",
runtimeBinding: `trim "${role._id}"`,
readableBinding: `Role.${role.name}`,
+ category: "Role",
+ icon: "UserGroup",
+ display: { type: "string", name: role.name },
}
})
}
@@ -516,6 +548,7 @@ export const getEventContextBindings = (
// Check if any context bindings are provided by the component for this
// setting
const component = findComponent(asset.props, componentId)
+ const def = store.actions.components.getDefinition(component?._component)
const settings = getComponentSettings(component?._component)
const eventSetting = settings.find(setting => setting.key === settingKey)
if (eventSetting?.context?.length) {
@@ -525,6 +558,8 @@ export const getEventContextBindings = (
runtimeBinding: `${makePropSafe("eventContext")}.${makePropSafe(
contextEntry.key
)}`,
+ category: component._instanceName,
+ icon: def.icon,
})
})
}
@@ -546,6 +581,8 @@ export const getEventContextBindings = (
bindings.push({
readableBinding: `Action ${idx + 1}.${contextValue.label}`,
runtimeBinding: `actions.${idx}.${contextValue.value}`,
+ category: "Actions",
+ icon: "JourneyAction",
})
})
}
diff --git a/packages/builder/src/builderStore/datasource.js b/packages/builder/src/builderStore/datasource.js
index 804d88bad6..e12b318e1c 100644
--- a/packages/builder/src/builderStore/datasource.js
+++ b/packages/builder/src/builderStore/datasource.js
@@ -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}`
@@ -27,7 +27,7 @@ export async function saveDatasource(config, skipFetch = false) {
// Create datasource
const resp = await datasources.save(datasource, !skipFetch && datasource.plus)
- // update the tables incase data source plus
+ // update the tables incase datasource plus
await tables.fetch()
await datasources.select(resp._id)
return resp
diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js
index 35c4587874..69bca7eac3 100644
--- a/packages/builder/src/builderStore/index.js
+++ b/packages/builder/src/builderStore/index.js
@@ -1,5 +1,6 @@
import { getFrontendStore } from "./store/frontend"
import { getAutomationStore } from "./store/automation"
+import { getTemporalStore } from "./store/temporal"
import { getThemeStore } from "./store/theme"
import { derived } from "svelte/store"
import { findComponent, findComponentPath } from "./componentUtils"
@@ -8,6 +9,7 @@ import { RoleUtils } from "@budibase/frontend-core"
export const store = getFrontendStore()
export const automationStore = getAutomationStore()
export const themeStore = getThemeStore()
+export const temporalStore = getTemporalStore()
export const selectedScreen = derived(store, $store => {
return $store.screens.find(screen => screen._id === $store.selectedScreenId)
diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js
index c5d953021f..b74261a7e8 100644
--- a/packages/builder/src/builderStore/store/frontend.js
+++ b/packages/builder/src/builderStore/store/frontend.js
@@ -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
@@ -368,9 +389,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) => {
diff --git a/packages/builder/src/builderStore/store/temporal.js b/packages/builder/src/builderStore/store/temporal.js
new file mode 100644
index 0000000000..ca70fd6293
--- /dev/null
+++ b/packages/builder/src/builderStore/store/temporal.js
@@ -0,0 +1,43 @@
+import { createLocalStorageStore } from "@budibase/frontend-core"
+import { get } from "svelte/store"
+
+export const getTemporalStore = () => {
+ const initialValue = {}
+
+ const localStorageKey = `bb-temporal`
+ const store = createLocalStorageStore(localStorageKey, initialValue)
+
+ const setExpiring = (key, data, duration) => {
+ const updated = {
+ ...data,
+ expiry: Date.now() + duration * 1000,
+ }
+
+ store.update(state => ({
+ ...state,
+ [key]: updated,
+ }))
+ }
+
+ const getExpiring = key => {
+ const entry = get(store)[key]
+ if (!entry) {
+ return
+ }
+ const currentExpiry = entry.expiry
+ if (currentExpiry < Date.now()) {
+ store.update(state => {
+ delete state[key]
+ return state
+ })
+ return null
+ } else {
+ return entry
+ }
+ }
+
+ return {
+ subscribe: store.subscribe,
+ actions: { setExpiring, getExpiring },
+ }
+}
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte
index 9faf00a199..19946a2386 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte
@@ -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 = []
@@ -32,8 +32,8 @@
: []
$: openDataSource = enrichedDataSources.find(x => x.open)
$: {
- // Ensure the open data source is always included in the list of open
- // data sources
+ // Ensure the open datasource is always included in the list of open
+ // datasources
if (openDataSource) {
openNode(openDataSource)
}
@@ -79,7 +79,7 @@
})
const containsActiveEntity = datasource => {
- // If we're view a query then the data source ID is in the URL
+ // If we're view a query then the datasource ID is in the URL
if ($params.selectedDatasource === datasource._id) {
return true
}
@@ -124,7 +124,7 @@
>
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte
index cd19523476..b81e818d5f 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte
@@ -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"
>
+
{
+ requireSpecificTables = e.detail
+ specificTables = null
+ }}
+ thin
+ text="Fetch listed tables only (one per line)"
+ />
+ {#if requireSpecificTables}
+
+ {/if}
+
If you have fetched tables from this database before, this action may
overwrite any changes you made after your initial fetch.
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/_components/DatasourceCard.svelte b/packages/builder/src/components/backend/DatasourceNavigator/_components/DatasourceCard.svelte
new file mode 100644
index 0000000000..6dffc70a63
--- /dev/null
+++ b/packages/builder/src/components/backend/DatasourceNavigator/_components/DatasourceCard.svelte
@@ -0,0 +1,65 @@
+
+
+ dispatcher("selected", integrationType)}
+ class="item hoverable"
+>
+
+
+
+ {schema.friendlyName}
+ {#if schema.type}
+ {schema.type || ""}
+ {/if}
+
+
+
+
+
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/Custom.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/Custom.svelte
new file mode 100644
index 0000000000..cc8c98f511
--- /dev/null
+++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/Custom.svelte
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
index 404895f05a..6d43258f45 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
+++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
@@ -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]
+ }
}
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte
index 8d34c292f3..40ef294339 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte
@@ -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()
}
@@ -119,7 +126,7 @@
- Connect to an external data source
+ Connect to an external datasource
- {#each Object.entries(integrations).filter(([key]) => key !== IntegrationTypes.INTERNAL) as [integrationType, schema]}
-
selectIntegration(integrationType)}
- class="item hoverable"
- >
-
-
-
- {schema.friendlyName}
- {#if schema.type}
- {schema.type || ""}
- {/if}
-
-
-
+ {#each Object.entries(integrations).filter(([key, val]) => key !== IntegrationTypes.INTERNAL && !val.custom) as [integrationType, schema]}
+
selectIntegration(evt.detail)}
+ {schema}
+ bind:integrationType
+ {integration}
+ />
{/each}
+
+ {#if customIntegrations.length > 0}
+
+ Custom datasource
+
+ {#each customIntegrations as [integrationType, schema]}
+ selectIntegration(evt.detail)}
+ {schema}
+ bind:integrationType
+ {integration}
+ />
+ {/each}
+
+
+ {/if}
diff --git a/packages/builder/src/components/portal/licensing/AppLimitModal.svelte b/packages/builder/src/components/portal/licensing/AppLimitModal.svelte
new file mode 100644
index 0000000000..39f553517e
--- /dev/null
+++ b/packages/builder/src/components/portal/licensing/AppLimitModal.svelte
@@ -0,0 +1,47 @@
+
+
+
+ {
+ window.location.href = upgradeUrl
+ }
+ : null}
+ >
+
+ You are currently on our Free plan . Upgrade
+ to our Pro plan to get unlimited apps and additional features.
+
+ {#if !$auth.user.accountPortalAccess}
+ Please contact the account holder to upgrade.
+ {/if}
+
+
+
+
diff --git a/packages/builder/src/components/portal/licensing/DayPassWarningModal.svelte b/packages/builder/src/components/portal/licensing/DayPassWarningModal.svelte
new file mode 100644
index 0000000000..b1aade2ca5
--- /dev/null
+++ b/packages/builder/src/components/portal/licensing/DayPassWarningModal.svelte
@@ -0,0 +1,78 @@
+
+
+
+ {#if $auth.user.accountPortalAccess}
+ {
+ window.location.href = upgradeUrl
+ }}
+ >
+
+ You have used {dayPassesUsed}% of
+ your plans Day Passes with {daysRemaining} day{daysRemaining == 1
+ ? ""
+ : "s"} remaining.
+
+
+
+
+ {dayPassesBody}
+
+ {:else}
+
+
+ You have used {dayPassesUsed}% of
+ your plans Day Passes with {daysRemaining} day{daysRemaining == 1
+ ? ""
+ : "s"} remaining.
+
+
+
+
+ Please contact your account holder to upgrade.
+
+ {/if}
+
+
+
diff --git a/packages/builder/src/components/portal/licensing/LicensingOverlays.svelte b/packages/builder/src/components/portal/licensing/LicensingOverlays.svelte
new file mode 100644
index 0000000000..736aac862f
--- /dev/null
+++ b/packages/builder/src/components/portal/licensing/LicensingOverlays.svelte
@@ -0,0 +1,118 @@
+
+
+
+
+
diff --git a/packages/builder/src/components/portal/licensing/PaymentFailedModal.svelte b/packages/builder/src/components/portal/licensing/PaymentFailedModal.svelte
new file mode 100644
index 0000000000..21a3c2dcd9
--- /dev/null
+++ b/packages/builder/src/components/portal/licensing/PaymentFailedModal.svelte
@@ -0,0 +1,84 @@
+
+
+
+ {#if $auth.user.accountPortalAccess}
+ {
+ window.location.href = upgradeUrl
+ }}
+ >
+ The payment for your subscription has failed
+
+ Please upgrade your billing details before your account gets downgraded
+ to the free plan
+
+
+
+ {`${$licensing.pastDueDaysRemaining} day${
+ $licensing.pastDueDaysRemaining == 1 ? "" : "s"
+ } remaining`}
+
+
+
+
+
+
+ {:else}
+
+ The payment for your subscription has failed
+
+ Please upgrade your billing details before your account gets downgraded
+ to the free plan
+
+ Please contact your account holder.
+
+
+ {`${$licensing.pastDueDaysRemaining} day${
+ $licensing.pastDueDaysRemaining == 1 ? "" : "s"
+ } remaining`}
+
+
+
+
+
+
+ {/if}
+
+
+
diff --git a/packages/builder/src/components/portal/licensing/constants.js b/packages/builder/src/components/portal/licensing/constants.js
new file mode 100644
index 0000000000..57f3a36709
--- /dev/null
+++ b/packages/builder/src/components/portal/licensing/constants.js
@@ -0,0 +1,15 @@
+export const ExpiringKeys = {
+ LICENSING_DAYPASS_WARNING_MODAL: "licensing_daypass_warning_90_modal",
+ LICENSING_DAYPASS_WARNING_BANNER: "licensing_daypass_warning_90_banner",
+ LICENSING_PAYMENT_FAILED: "licensing_payment_failed",
+ LICENSING_ACCOUNT_DOWNGRADED_MODAL: "licensing_account_downgraded_modal",
+ LICENSING_APP_LIMIT_MODAL: "licensing_app_limit_modal",
+ LICENSING_ROWS_WARNING_BANNER: "licensing_rows_warning_banner",
+ LICENSING_AUTOMATIONS_WARNING_BANNER: "licensing_automations_warning_banner",
+ LICENSING_QUERIES_WARNING_BANNER: "licensing_queries_warning_banner",
+}
+
+export const StripeStatus = {
+ PAST_DUE: "past_due",
+ ACTIVE: "active",
+}
diff --git a/packages/builder/src/components/portal/licensing/licensingBanners.js b/packages/builder/src/components/portal/licensing/licensingBanners.js
new file mode 100644
index 0000000000..37d8e4a540
--- /dev/null
+++ b/packages/builder/src/components/portal/licensing/licensingBanners.js
@@ -0,0 +1,156 @@
+import { ExpiringKeys } from "./constants"
+import { temporalStore } from "builderStore"
+import { admin, auth, licensing } from "stores/portal"
+import { get } from "svelte/store"
+import { BANNER_TYPES } from "@budibase/bbui"
+
+const oneDayInSeconds = 86400
+
+const defaultCacheFn = key => {
+ temporalStore.actions.setExpiring(key, {}, oneDayInSeconds)
+}
+
+const defaultAction = key => {
+ if (!get(auth).user.accountPortalAccess) {
+ return {}
+ }
+ return {
+ extraButtonText: "Upgrade Plan",
+ extraButtonAction: () => {
+ defaultCacheFn(key)
+ window.location.href = `${get(admin).accountPortalUrl}/portal/upgrade`
+ },
+ }
+}
+
+const buildUsageInfoBanner = (
+ metricKey,
+ metricLabel,
+ cacheKey,
+ percentageThreshold,
+ customMessage
+) => {
+ const appAuth = get(auth)
+ const appLicensing = get(licensing)
+
+ const displayPercent =
+ appLicensing?.usageMetrics[metricKey] > 100
+ ? 100
+ : appLicensing?.usageMetrics[metricKey]
+
+ let bannerConfig = {
+ key: cacheKey,
+ type: BANNER_TYPES.INFO,
+ onChange: () => {
+ defaultCacheFn(cacheKey)
+ },
+ message: customMessage
+ ? customMessage
+ : `You have used ${displayPercent}% of your monthly usage of ${metricLabel} with ${
+ appLicensing.quotaResetDaysRemaining
+ } day${
+ appLicensing.quotaResetDaysRemaining == 1 ? "" : "s"
+ } remaining. ${
+ appAuth.user.accountPortalAccess
+ ? ""
+ : "Please contact your account holder to upgrade"
+ }`,
+ criteria: () => {
+ return appLicensing?.usageMetrics[metricKey] >= percentageThreshold
+ },
+ tooltip: appLicensing?.quotaResetDate,
+ }
+
+ return !get(auth).user.accountPortalAccess
+ ? bannerConfig
+ : {
+ ...bannerConfig,
+ ...defaultAction(cacheKey),
+ }
+}
+
+const buildDayPassBanner = () => {
+ const appAuth = get(auth)
+ const appLicensing = get(licensing)
+ if (get(licensing)?.usageMetrics["dayPasses"] >= 100) {
+ return {
+ key: "max_dayPasses",
+ type: BANNER_TYPES.NEGATIVE,
+ criteria: () => {
+ return true
+ },
+ message: `Your apps are currently offline. You have exceeded your plans limit for Day Passes. ${
+ appAuth.user.accountPortalAccess
+ ? ""
+ : "Please contact your account holder to upgrade."
+ }`,
+ ...defaultAction(),
+ showCloseButton: false,
+ }
+ }
+
+ return buildUsageInfoBanner(
+ "dayPasses",
+ "Day Passes",
+ ExpiringKeys.LICENSING_DAYPASS_WARNING_BANNER,
+ 90,
+ `You have used ${
+ appLicensing?.usageMetrics["dayPasses"]
+ }% of your monthly usage of Day Passes with ${
+ appLicensing?.quotaResetDaysRemaining
+ } day${
+ get(licensing).quotaResetDaysRemaining == 1 ? "" : "s"
+ } remaining. All apps will be taken offline if this limit is reached. ${
+ appAuth.user.accountPortalAccess
+ ? ""
+ : "Please contact your account holder to upgrade."
+ }`
+ )
+}
+
+const buildPaymentFailedBanner = () => {
+ return {
+ key: "payment_Failed",
+ type: BANNER_TYPES.NEGATIVE,
+ criteria: () => {
+ return get(licensing)?.accountPastDue && !get(licensing).isFreePlan()
+ },
+ message: `Payment Failed - Please update your billing details or your account will be downgrades in
+ ${get(licensing)?.pastDueDaysRemaining} day${
+ get(licensing)?.pastDueDaysRemaining == 1 ? "" : "s"
+ }`,
+ ...defaultAction(),
+ showCloseButton: false,
+ tooltip: get(licensing).pastDueEndDate,
+ }
+}
+
+export const getBanners = () => {
+ return [
+ buildPaymentFailedBanner(),
+ buildDayPassBanner(ExpiringKeys.LICENSING_DAYPASS_WARNING_BANNER),
+ buildUsageInfoBanner(
+ "rows",
+ "Rows",
+ ExpiringKeys.LICENSING_ROWS_WARNING_BANNER,
+ 90
+ ),
+ buildUsageInfoBanner(
+ "automations",
+ "Automations",
+ ExpiringKeys.LICENSING_AUTOMATIONS_WARNING_BANNER,
+ 90
+ ),
+ buildUsageInfoBanner(
+ "queries",
+ "Queries",
+ ExpiringKeys.LICENSING_QUERIES_WARNING_BANNER,
+ 90
+ ),
+ ].filter(licensingBanner => {
+ return (
+ !temporalStore.actions.getExpiring(licensingBanner.key) &&
+ licensingBanner.criteria()
+ )
+ })
+}
diff --git a/packages/builder/src/components/usage/Usage.svelte b/packages/builder/src/components/usage/Usage.svelte
index cd9071785d..49c4205a4c 100644
--- a/packages/builder/src/components/usage/Usage.svelte
+++ b/packages/builder/src/components/usage/Usage.svelte
@@ -1,10 +1,15 @@
- {usage.name}
+
{#if unlimited}
- {usage.used}
+ {usage.used} / Unlimited
{:else}
{usage.used} / {usage.total}
{/if}
{#if unlimited}
- Unlimited
+
{:else}
-
+
+ {/if}
+ {#if showWarning}
+
+ To get more {usage.name.toLowerCase()}
+ {#if accountPortalAccess}
+
upgrade your plan
+ {:else}
+ contact your account holder
+ {/if}
+
{/if}
@@ -51,6 +89,13 @@
display: flex;
flex-direction: row;
justify-content: space-between;
- gap: var(--spacing-m);
+ margin-bottom: 12px;
+ }
+ .header-container {
+ display: flex;
+ }
+ .heading {
+ margin-top: 3px;
+ margin-left: 5px;
}
diff --git a/packages/builder/src/components/usage/UsageDashCard.svelte b/packages/builder/src/components/usage/UsageDashCard.svelte
new file mode 100644
index 0000000000..6dd0b3d969
--- /dev/null
+++ b/packages/builder/src/components/usage/UsageDashCard.svelte
@@ -0,0 +1,119 @@
+
+
+
+
+
diff --git a/packages/builder/src/components/usage/index.js b/packages/builder/src/components/usage/index.js
new file mode 100644
index 0000000000..48c0e2ea2c
--- /dev/null
+++ b/packages/builder/src/components/usage/index.js
@@ -0,0 +1,2 @@
+export { default as Usage } from "./Usage.svelte"
+export { default as DashCard } from "./UsageDashCard.svelte"
diff --git a/packages/builder/src/constants/index.js b/packages/builder/src/constants/index.js
index 4e2ca37b9c..49054ae247 100644
--- a/packages/builder/src/constants/index.js
+++ b/packages/builder/src/constants/index.js
@@ -57,3 +57,17 @@ export const DefaultAppTheme = {
navBackground: "var(--spectrum-global-color-gray-50)",
navTextColor: "var(--spectrum-global-color-gray-800)",
}
+
+export const PlanType = {
+ FREE: "free",
+ PRO: "pro",
+ BUSINESS: "business",
+ ENTERPRISE: "enterprise",
+}
+
+export const PluginSource = {
+ URL: "URL",
+ NPM: "NPM",
+ GITHUB: "Github",
+ FILE: "File Upload",
+}
diff --git a/packages/builder/src/pages/builder/_layout.svelte b/packages/builder/src/pages/builder/_layout.svelte
index 2e8ea2ef0a..8d604e8790 100644
--- a/packages/builder/src/pages/builder/_layout.svelte
+++ b/packages/builder/src/pages/builder/_layout.svelte
@@ -1,6 +1,6 @@
@@ -121,6 +133,7 @@
>
+
diff --git a/packages/builder/src/pages/builder/portal/manage/plugins/_components/DeletePluginModal.svelte b/packages/builder/src/pages/builder/portal/manage/plugins/_components/DeletePluginModal.svelte
new file mode 100644
index 0000000000..6b09e7b276
--- /dev/null
+++ b/packages/builder/src/pages/builder/portal/manage/plugins/_components/DeletePluginModal.svelte
@@ -0,0 +1,33 @@
+
+
+
+
+ Are you sure you want to delete {plugin?.name}
+
+
diff --git a/packages/builder/src/pages/builder/portal/manage/plugins/_components/PluginRow.svelte b/packages/builder/src/pages/builder/portal/manage/plugins/_components/PluginRow.svelte
new file mode 100644
index 0000000000..18390c7fcb
--- /dev/null
+++ b/packages/builder/src/pages/builder/portal/manage/plugins/_components/PluginRow.svelte
@@ -0,0 +1,155 @@
+
+
+ detailsModal.show()}>
+
+
+
+
+
+
+
+ {plugin.name}
+
+
+ {friendlyName}
+
+
+
+
+
{plugin.version}
+
+ {plugin.schema.type.charAt(0).toUpperCase() + plugin.schema.type.slice(1)}
+
+
+
+
+
+
+
+
+
+ Name
+
+
+
+
+ Friendly name
+
+
+
+
+ Type
+
+
+
+ Source
+
+
+
+ Version
+
+
+
+ License
+
+
+
+ Author
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/builder/src/pages/builder/portal/manage/plugins/index.svelte b/packages/builder/src/pages/builder/portal/manage/plugins/index.svelte
new file mode 100644
index 0000000000..5d73447710
--- /dev/null
+++ b/packages/builder/src/pages/builder/portal/manage/plugins/index.svelte
@@ -0,0 +1,95 @@
+
+
+
+
+ Plugins
+ Add your own custom datasources and components
+
+
+
+
+ {#if filteredPlugins?.length}
+
+ {#each filteredPlugins as plugin (plugin._id)}
+
+ {/each}
+
+ {/if}
+
+
+
+
+
+
+
+
diff --git a/packages/builder/src/pages/builder/portal/settings/organisation.svelte b/packages/builder/src/pages/builder/portal/settings/organisation.svelte
index 8c091ce952..5f352a32e4 100644
--- a/packages/builder/src/pages/builder/portal/settings/organisation.svelte
+++ b/packages/builder/src/pages/builder/portal/settings/organisation.svelte
@@ -120,8 +120,9 @@
Platform URL
+ Platform URL
+
@@ -150,15 +151,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;
}
diff --git a/packages/builder/src/pages/builder/portal/settings/upgrade.svelte b/packages/builder/src/pages/builder/portal/settings/upgrade.svelte
index 5200834ffa..68ace1a157 100644
--- a/packages/builder/src/pages/builder/portal/settings/upgrade.svelte
+++ b/packages/builder/src/pages/builder/portal/settings/upgrade.svelte
@@ -35,10 +35,14 @@
}
const activate = async () => {
- await API.activateLicenseKey({ licenseKey })
- await auth.getSelf()
- await setLicenseInfo()
- notifications.success("Successfully activated")
+ try {
+ await API.activateLicenseKey({ licenseKey })
+ await auth.getSelf()
+ await setLicenseInfo()
+ notifications.success("Successfully activated")
+ } catch (e) {
+ notifications.error(e.message)
+ }
}
const refresh = async () => {
diff --git a/packages/builder/src/pages/builder/portal/settings/usage.svelte b/packages/builder/src/pages/builder/portal/settings/usage.svelte
index 069c37b555..f2809452fd 100644
--- a/packages/builder/src/pages/builder/portal/settings/usage.svelte
+++ b/packages/builder/src/pages/builder/portal/settings/usage.svelte
@@ -5,49 +5,74 @@
Heading,
Layout,
notifications,
+ Detail,
Link,
+ TooltipWrapper,
} from "@budibase/bbui"
import { onMount } from "svelte"
- import { admin, auth, licensing } from "stores/portal"
- import Usage from "components/usage/Usage.svelte"
+ import { admin, auth, licensing } from "../../../../stores/portal"
+ import { PlanType } from "../../../../constants"
+ import { DashCard, Usage } from "../../../../components/usage"
let staticUsage = []
let monthlyUsage = []
+ let cancelAt
let loaded = false
+ let textRows = []
+ let daysRemainingInMonth
+ let primaryActionText
+
+ const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade`
+ const manageUrl = `${$admin.accountPortalUrl}/portal/billing`
+
+ const WARN_USAGE = ["Queries", "Automations", "Rows", "Day Passes"]
+ const EXCLUDE_QUOTAS = ["Queries"]
$: quotaUsage = $licensing.quotaUsage
$: license = $auth.user?.license
-
- const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade`
+ $: accountPortalAccess = $auth?.user?.accountPortalAccess
+ $: quotaReset = quotaUsage?.quotaReset
const setMonthlyUsage = () => {
monthlyUsage = []
if (quotaUsage.monthly) {
for (let [key, value] of Object.entries(license.quotas.usage.monthly)) {
+ if (EXCLUDE_QUOTAS.includes(value.name)) {
+ continue
+ }
const used = quotaUsage.monthly.current[key]
- if (used !== undefined) {
+ if (value.value !== 0) {
monthlyUsage.push({
name: value.name,
- used: used,
+ used: used ? used : 0,
total: value.value,
})
}
}
}
+ monthlyUsage = monthlyUsage.sort((a, b) => a.name.localeCompare(b.name))
}
const setStaticUsage = () => {
staticUsage = []
for (let [key, value] of Object.entries(license.quotas.usage.static)) {
+ if (EXCLUDE_QUOTAS.includes(value.name)) {
+ continue
+ }
const used = quotaUsage.usageQuota[key]
- if (used !== undefined) {
+ if (value.value !== 0) {
staticUsage.push({
name: value.name,
- used: used,
+ used: used ? used : 0,
total: value.value,
})
}
}
+ staticUsage = staticUsage.sort((a, b) => a.name.localeCompare(b.name))
+ }
+
+ const setCancelAt = () => {
+ cancelAt = license?.billing?.subscription?.cancelAt
}
const capitalise = string => {
@@ -56,6 +81,70 @@
}
}
+ const planTitle = () => {
+ return capitalise(license?.plan.type)
+ }
+
+ const getDaysRemaining = timestamp => {
+ if (!timestamp) {
+ return
+ }
+ const now = new Date()
+ now.setHours(0)
+ now.setMinutes(0)
+
+ const thenDate = new Date(timestamp)
+ thenDate.setHours(0)
+ thenDate.setMinutes(0)
+
+ const difference = thenDate.getTime() - now
+ // return the difference in days
+ return (difference / (1000 * 3600 * 24)).toFixed(0)
+ }
+
+ const setTextRows = () => {
+ textRows = []
+
+ if (cancelAt) {
+ textRows.push({ message: "Subscription has been cancelled" })
+ textRows.push({
+ message: `${getDaysRemaining(cancelAt * 1000)} days remaining`,
+ tooltip: new Date(cancelAt * 1000),
+ })
+ }
+ }
+
+ const setDaysRemainingInMonth = () => {
+ const resetDate = new Date(quotaReset)
+
+ const now = new Date()
+ const difference = resetDate.getTime() - now.getTime()
+
+ // return the difference in days
+ daysRemainingInMonth = (difference / (1000 * 3600 * 24)).toFixed(0)
+ }
+
+ const goToAccountPortal = () => {
+ if (license?.plan.type === PlanType.FREE) {
+ window.location.href = upgradeUrl
+ } else {
+ window.location.href = manageUrl
+ }
+ }
+
+ const setPrimaryActionText = () => {
+ if (license?.plan.type === PlanType.FREE) {
+ primaryActionText = "Upgrade"
+ return
+ }
+
+ if (cancelAt) {
+ primaryActionText = "Renew"
+ } else {
+ primaryActionText = "Manage"
+ }
+ }
+
const init = async () => {
try {
await licensing.getQuotaUsage()
@@ -71,69 +160,98 @@
})
$: {
- if (license && quotaUsage) {
- setMonthlyUsage()
- setStaticUsage()
+ if (license) {
+ setPrimaryActionText()
+ setCancelAt()
+ setTextRows()
+ setDaysRemainingInMonth()
+
+ if (quotaUsage) {
+ setMonthlyUsage()
+ setStaticUsage()
+ }
}
}
{#if loaded}
- Usage
- Get information about your current usage within Budibase.
- {#if $admin.cloud}
- {#if $auth.user?.accountPortalAccess}
+
+ Usage
+ Get information about your current usage within Budibase.
+ {#if accountPortalAccess}
To upgrade your plan and usage limits visit your Account.
+ on:click={goToAccountPortal}
+ size="L">Account
{:else}
- Contact your account holder to upgrade your usage limits.
+ To upgrade your plan and usage limits contact your account holder
{/if}
- {/if}
-
-
-
-
-
-
-
- YOUR PLAN
- {capitalise(license?.plan.type)}
+
-
- USAGE
-
- {#each staticUsage as usage}
-
-
+
+
+
+
+
+
+ {#each staticUsage as usage}
+
+
+
+ {/each}
+
- {/each}
-
-
- {#if monthlyUsage.length}
-
- MONTHLY
-
- {#each monthlyUsage as usage}
-
-
-
- {/each}
-
+
+ {#if monthlyUsage.length}
+
+
+ Monthly
+
+
+ Resets in {daysRemainingInMonth} days
+
+
+
+
+ {#each monthlyUsage as usage}
+
+
+
+ {/each}
+
+
+
+
+ {/if}
-
- {/if}
+
{/if}
diff --git a/packages/builder/src/stores/backend/datasources.js b/packages/builder/src/stores/backend/datasources.js
index 2423394c6a..07aeab1921 100644
--- a/packages/builder/src/stores/backend/datasources.js
+++ b/packages/builder/src/stores/backend/datasources.js
@@ -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) => {
diff --git a/packages/builder/src/stores/portal/index.js b/packages/builder/src/stores/portal/index.js
index b56ee94b00..fa7aa7e3cf 100644
--- a/packages/builder/src/stores/portal/index.js
+++ b/packages/builder/src/stores/portal/index.js
@@ -8,3 +8,4 @@ export { oidc } from "./oidc"
export { templates } from "./templates"
export { licensing } from "./licensing"
export { groups } from "./groups"
+export { plugins } from "./plugins"
diff --git a/packages/builder/src/stores/portal/licensing.js b/packages/builder/src/stores/portal/licensing.js
index 653dab52ed..e2b4570302 100644
--- a/packages/builder/src/stores/portal/licensing.js
+++ b/packages/builder/src/stores/portal/licensing.js
@@ -1,14 +1,24 @@
-import { writable } from "svelte/store"
+import { writable, get } from "svelte/store"
import { API } from "api"
+import { auth } from "stores/portal"
+import { Constants } from "@budibase/frontend-core"
+import { StripeStatus } from "components/portal/licensing/constants"
+import { FEATURE_FLAGS, isEnabled } from "../../helpers/featureFlags"
export const createLicensingStore = () => {
const DEFAULT = {
plans: {},
+ usageMetrics: {},
}
+ const oneDayInMilliseconds = 86400000
const store = writable(DEFAULT)
const actions = {
+ init: async () => {
+ await actions.getQuotaUsage()
+ await actions.getUsageMetrics()
+ },
getQuotaUsage: async () => {
const quotaUsage = await API.getQuotaUsage()
store.update(state => {
@@ -18,6 +28,82 @@ export const createLicensingStore = () => {
}
})
},
+ getUsageMetrics: async () => {
+ if (isEnabled(FEATURE_FLAGS.LICENSING)) {
+ const quota = get(store).quotaUsage
+ const license = get(auth).user.license
+ const now = new Date()
+
+ const getMetrics = (keys, license, quota) => {
+ if (!license || !quota || !keys) {
+ return {}
+ }
+ return keys.reduce((acc, key) => {
+ const quotaLimit = license[key].value
+ const quotaUsed = (quota[key] / quotaLimit) * 100
+ acc[key] = quotaLimit > -1 ? Math.round(quotaUsed) : -1
+ return acc
+ }, {})
+ }
+ const monthlyMetrics = getMetrics(
+ ["dayPasses", "queries", "automations"],
+ license.quotas.usage.monthly,
+ quota.monthly.current
+ )
+ const staticMetrics = getMetrics(
+ ["apps", "rows"],
+ license.quotas.usage.static,
+ quota.usageQuota
+ )
+
+ const getDaysBetween = (dateStart, dateEnd) => {
+ return dateEnd > dateStart
+ ? Math.round(
+ (dateEnd.getTime() - dateStart.getTime()) / oneDayInMilliseconds
+ )
+ : 0
+ }
+
+ const quotaResetDate = new Date(quota.quotaReset)
+ const quotaResetDaysRemaining = getDaysBetween(now, quotaResetDate)
+
+ const accountDowngraded =
+ license?.billing?.subscription?.downgradeAt &&
+ license?.billing?.subscription?.downgradeAt <= now.getTime() &&
+ license?.billing?.subscription?.status === StripeStatus.PAST_DUE &&
+ license?.plan.type === Constants.PlanType.FREE
+
+ const pastDueAtMilliseconds = license?.billing?.subscription?.pastDueAt
+ const downgradeAtMilliseconds =
+ license?.billing?.subscription?.downgradeAt
+ let pastDueDaysRemaining
+ let pastDueEndDate
+
+ if (pastDueAtMilliseconds && downgradeAtMilliseconds) {
+ pastDueEndDate = new Date(downgradeAtMilliseconds)
+ pastDueDaysRemaining = getDaysBetween(
+ new Date(pastDueAtMilliseconds),
+ pastDueEndDate
+ )
+ }
+
+ store.update(state => {
+ return {
+ ...state,
+ usageMetrics: { ...monthlyMetrics, ...staticMetrics },
+ quotaResetDaysRemaining,
+ quotaResetDate,
+ accountDowngraded,
+ accountPastDue: pastDueAtMilliseconds != null,
+ pastDueEndDate,
+ pastDueDaysRemaining,
+ isFreePlan: () => {
+ return license?.plan.type === Constants.PlanType.FREE
+ },
+ }
+ })
+ }
+ },
}
return {
diff --git a/packages/builder/src/stores/portal/plugins.js b/packages/builder/src/stores/portal/plugins.js
new file mode 100644
index 0000000000..8997e8f49d
--- /dev/null
+++ b/packages/builder/src/stores/portal/plugins.js
@@ -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()
diff --git a/packages/builder/vite.config.js b/packages/builder/vite.config.js
index 56ffbf20f5..95e2908995 100644
--- a/packages/builder/vite.config.js
+++ b/packages/builder/vite.config.js
@@ -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 }) => {
],
},
}
-}
+})
diff --git a/packages/builder/yarn.lock b/packages/builder/yarn.lock
index 88547fd4a6..6551981a9e 100644
--- a/packages/builder/yarn.lock
+++ b/packages/builder/yarn.lock
@@ -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"
diff --git a/packages/cli/package.json b/packages/cli/package.json
index c3d3c3f76d..af21baa296 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/cli",
- "version": "1.3.21",
+ "version": "1.3.22-alpha.4",
"description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js",
"bin": {
@@ -26,14 +26,16 @@
"outputPath": "build"
},
"dependencies": {
- "@budibase/backend-core": "^1.3.21",
+ "@budibase/backend-core": "1.3.22-alpha.4",
"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",
diff --git a/packages/cli/src/constants.js b/packages/cli/src/constants.js
index 5d66c3a39d..aa49523d4e 100644
--- a/packages/cli/src/constants.js
+++ b/packages/cli/src/constants.js
@@ -1,8 +1,11 @@
+const { Event } = require("@budibase/types")
+
exports.CommandWords = {
BACKUPS: "backups",
HOSTING: "hosting",
ANALYTICS: "analytics",
HELP: "help",
+ PLUGIN: "plugins",
}
exports.InitTypes = {
@@ -14,6 +17,7 @@ exports.AnalyticsEvents = {
OptOut: "analytics:opt:out",
OptIn: "analytics:opt:in",
SelfHostInit: "hosting:init",
+ PluginInit: Event.PLUGIN_INIT,
}
exports.POSTHOG_TOKEN = "phc_yGOn4i7jWKaCTapdGR6lfA4AvmuEQ2ijn5zAVSFYPlS"
diff --git a/packages/cli/src/environment.js b/packages/cli/src/environment.js
new file mode 100644
index 0000000000..c8c8fe87e9
--- /dev/null
+++ b/packages/cli/src/environment.js
@@ -0,0 +1,2 @@
+process.env.NO_JS = "1"
+process.env.JS_BCRYPT = "1"
diff --git a/packages/cli/src/events.js b/packages/cli/src/events.js
new file mode 100644
index 0000000000..63d4fca1ea
--- /dev/null
+++ b/packages/cli/src/events.js
@@ -0,0 +1,11 @@
+const AnalyticsClient = require("./analytics/Client")
+
+const client = new AnalyticsClient()
+
+exports.captureEvent = (event, properties) => {
+ client.capture({
+ distinctId: "cli",
+ event,
+ properties,
+ })
+}
diff --git a/packages/cli/src/exec.js b/packages/cli/src/exec.js
new file mode 100644
index 0000000000..72fd8e00eb
--- /dev/null
+++ b/packages/cli/src/exec.js
@@ -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)
+}
diff --git a/packages/cli/src/hosting/index.js b/packages/cli/src/hosting/index.js
index 2ebcee43a5..ae62c45992 100644
--- a/packages/cli/src/hosting/index.js
+++ b/packages/cli/src/hosting/index.js
@@ -13,7 +13,7 @@ const fs = require("fs")
const compose = require("docker-compose")
const makeEnv = require("./makeEnv")
const axios = require("axios")
-const AnalyticsClient = require("../analytics/Client")
+const { captureEvent } = require("../events")
const BUDIBASE_SERVICES = ["app-service", "worker-service", "proxy-service"]
const ERROR_FILE = "docker-error.log"
@@ -22,8 +22,6 @@ const FILE_URLS = [
]
const DO_USER_DATA_URL = "http://169.254.169.254/metadata/v1/user-data"
-const client = new AnalyticsClient()
-
async function downloadFiles() {
const promises = []
for (let url of FILE_URLS) {
@@ -72,12 +70,8 @@ async function init(type) {
return
}
}
- client.capture({
- distinctId: "cli",
- event: AnalyticsEvents.SelfHostInit,
- properties: {
- type,
- },
+ captureEvent(AnalyticsEvents.SelfHostInit, {
+ type,
})
await downloadFiles()
const config = isQuick ? makeEnv.QUICK_CONFIG : {}
diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js
index 0a3ced4200..691fc71928 100644
--- a/packages/cli/src/index.js
+++ b/packages/cli/src/index.js
@@ -1,5 +1,6 @@
#!/usr/bin/env node
require("./prebuilds")
+require("./environment")
const { getCommands } = require("./options")
const { Command } = require("commander")
const { getHelpDescription } = require("./utils")
diff --git a/packages/cli/src/options.js b/packages/cli/src/options.js
index 1ac66df9fb..1e58ee2725 100644
--- a/packages/cli/src/options.js
+++ b/packages/cli/src/options.js
@@ -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]
}
diff --git a/packages/cli/src/plugins/index.js b/packages/cli/src/plugins/index.js
new file mode 100644
index 0000000000..66cca8c19d
--- /dev/null
+++ b/packages/cli/src/plugins/index.js
@@ -0,0 +1,164 @@
+const Command = require("../structures/Command")
+const { CommandWords, AnalyticsEvents } = 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, moveDirectory } = require("../utils")
+const { captureEvent } = require("../events")
+
+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 askAboutTopLevel(name) {
+ const files = fs.readdirSync(process.cwd())
+ // we are in an empty git repo, don't ask
+ if (files.find(file => file === ".git")) {
+ return false
+ } else {
+ console.log(
+ info(`By default the plugin will be created in the directory "${name}"`)
+ )
+ console.log(
+ info(
+ "if you are already in an empty directory, such as a new Git repo, you can disable this functionality."
+ )
+ )
+ return questions.confirmation("Create top level directory?")
+ }
+}
+
+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 description = await questions.string(
+ "Description",
+ `An amazing Budibase ${type}!`
+ )
+ const version = await questions.string("Version", "1.0.0")
+ const topLevel = await askAboutTopLevel(name)
+ // get the skeleton
+ console.log(info("Retrieving project..."))
+ await getSkeleton(type, name)
+ await fleshOutSkeleton(type, name, description, version)
+ console.log(info("Installing dependencies..."))
+ await runPkgCommand("install", join(process.cwd(), name))
+ // if no parent directory desired move to cwd
+ if (!topLevel) {
+ moveDirectory(name, process.cwd())
+ console.log(info(`Plugin created in current directory.`))
+ } else {
+ console.log(info(`Plugin created in directory "${name}"`))
+ }
+ captureEvent(AnalyticsEvents.PluginInit, {
+ type,
+ name,
+ description,
+ version,
+ })
+}
+
+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
diff --git a/packages/cli/src/plugins/skeleton.js b/packages/cli/src/plugins/skeleton.js
new file mode 100644
index 0000000000..76b9aa2d8a
--- /dev/null
+++ b/packages/cli/src/plugins/skeleton.js
@@ -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)
+ }
+}
diff --git a/packages/cli/src/prebuilds.js b/packages/cli/src/prebuilds.js
index ecc59f964a..0decdc6c63 100644
--- a/packages/cli/src/prebuilds.js
+++ b/packages/cli/src/prebuilds.js
@@ -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,18 @@ function checkForBinaries() {
}
}
-function cleanup() {
+function cleanup(evt) {
+ if (!isNaN(evt)) {
+ return
+ }
+ if (evt) {
+ 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 })
}
diff --git a/packages/cli/src/utils.js b/packages/cli/src/utils.js
index 818153ef02..91f3263cc1 100644
--- a/packages/cli/src/utils.js
+++ b/packages/cli/src/utils.js
@@ -3,6 +3,7 @@ const fs = require("fs")
const axios = require("axios")
const path = require("path")
const progress = require("cli-progress")
+const { join } = require("path")
exports.downloadFile = async (url, filePath) => {
filePath = path.resolve(filePath)
@@ -67,3 +68,19 @@ exports.progressBar = total => {
exports.checkSlashesInUrl = url => {
return url.replace(/(https?:\/\/)|(\/)+/g, "$1$2")
}
+
+exports.moveDirectory = (oldPath, newPath) => {
+ const files = fs.readdirSync(oldPath)
+ // check any file exists already
+ for (let file of files) {
+ if (fs.existsSync(join(newPath, file))) {
+ throw new Error(
+ "Unable to remove top level directory - some skeleton files already exist."
+ )
+ }
+ }
+ for (let file of files) {
+ fs.renameSync(join(oldPath, file), join(newPath, file))
+ }
+ fs.rmdirSync(oldPath)
+}
diff --git a/packages/cli/yarn.lock b/packages/cli/yarn.lock
index b1c0a8c9c7..547e9fd3e2 100644
--- a/packages/cli/yarn.lock
+++ b/packages/cli/yarn.lock
@@ -58,6 +58,18 @@
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
+"@hapi/hoek@^9.0.0":
+ version "9.3.0"
+ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
+ integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==
+
+"@hapi/topo@^5.0.0":
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012"
+ integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==
+ dependencies:
+ "@hapi/hoek" "^9.0.0"
+
"@humanwhocodes/config-array@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9"
@@ -93,6 +105,28 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
+"@sideway/address@^4.1.3":
+ version "4.1.4"
+ resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0"
+ integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==
+ dependencies:
+ "@hapi/hoek" "^9.0.0"
+
+"@sideway/formula@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c"
+ integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==
+
+"@sideway/pinpoint@^2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
+ integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
+
+"@sindresorhus/is@^0.7.0":
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
+ integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==
+
abort-controller@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
@@ -200,6 +234,13 @@ aproba@^1.0.3:
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
+archive-type@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/archive-type/-/archive-type-4.0.0.tgz#f92e72233056dfc6969472749c267bdb046b1d70"
+ integrity sha512-zV4Ky0v1F8dBrdYElwTvQhweQ0P7Kwc1aluqJsYtOBP01jXcWCyW2IEfI1YiqsG+Iy7ZR+o5LF1N+PGECBxHWA==
+ dependencies:
+ file-type "^4.2.0"
+
are-we-there-yet@~1.1.2:
version "1.1.7"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146"
@@ -287,6 +328,14 @@ base64-js@^1.3.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+bl@^1.0.0:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7"
+ integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==
+ dependencies:
+ readable-stream "^2.3.5"
+ safe-buffer "^5.1.1"
+
bl@^4.0.3:
version "4.1.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
@@ -311,12 +360,35 @@ braces@^3.0.2:
dependencies:
fill-range "^7.0.1"
+buffer-alloc-unsafe@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
+ integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
+
+buffer-alloc@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
+ integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
+ dependencies:
+ buffer-alloc-unsafe "^1.1.0"
+ buffer-fill "^1.0.0"
+
+buffer-crc32@~0.2.3:
+ version "0.2.13"
+ resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
+ integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
+
+buffer-fill@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
+ integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==
+
buffer-from@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
-buffer@^5.5.0, buffer@^5.6.0:
+buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
@@ -324,6 +396,19 @@ buffer@^5.5.0, buffer@^5.6.0:
base64-js "^1.3.1"
ieee754 "^1.1.13"
+cacheable-request@^2.1.1:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d"
+ integrity sha512-vag0O2LKZ/najSoUwDbVlnlCFvhBE/7mGTY2B5FgCBDcRD+oVV1HYTOwM6JZfMg/hIcM6IwnTZ1uQQL5/X3xIQ==
+ dependencies:
+ clone-response "1.0.2"
+ get-stream "3.0.0"
+ http-cache-semantics "3.8.1"
+ keyv "3.0.0"
+ lowercase-keys "1.0.0"
+ normalize-url "2.0.1"
+ responselike "1.0.2"
+
callsites@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
@@ -407,6 +492,13 @@ clone-buffer@1.0.0:
resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58"
integrity sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==
+clone-response@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
+ integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==
+ dependencies:
+ mimic-response "^1.0.0"
+
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
@@ -461,6 +553,11 @@ commander@7.1.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-7.1.0.tgz#f2eaecf131f10e36e07d894698226e36ae0eb5ff"
integrity sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg==
+commander@^2.8.1:
+ version "2.20.3"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+ integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
component-type@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9"
@@ -476,6 +573,13 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
+content-disposition@^0.5.2:
+ version "0.5.4"
+ resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
+ integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
+ dependencies:
+ safe-buffer "5.2.1"
+
copyfiles@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.4.1.tgz#d2dcff60aaad1015f09d0b66e7f0f1c5cd3c5da5"
@@ -520,6 +624,18 @@ debug@4, debug@^4.0.1, debug@^4.1.1:
dependencies:
ms "2.1.2"
+decode-uri-component@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+ integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==
+
+decompress-response@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
+ integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==
+ dependencies:
+ mimic-response "^1.0.0"
+
decompress-response@^4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986"
@@ -527,6 +643,59 @@ decompress-response@^4.2.0:
dependencies:
mimic-response "^2.0.0"
+decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
+ integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==
+ dependencies:
+ file-type "^5.2.0"
+ is-stream "^1.1.0"
+ tar-stream "^1.5.2"
+
+decompress-tarbz2@^4.0.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
+ integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==
+ dependencies:
+ decompress-tar "^4.1.0"
+ file-type "^6.1.0"
+ is-stream "^1.1.0"
+ seek-bzip "^1.0.5"
+ unbzip2-stream "^1.0.9"
+
+decompress-targz@^4.0.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
+ integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==
+ dependencies:
+ decompress-tar "^4.1.1"
+ file-type "^5.2.0"
+ is-stream "^1.1.0"
+
+decompress-unzip@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69"
+ integrity sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==
+ dependencies:
+ file-type "^3.8.0"
+ get-stream "^2.2.0"
+ pify "^2.3.0"
+ yauzl "^2.4.2"
+
+decompress@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118"
+ integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==
+ dependencies:
+ decompress-tar "^4.0.0"
+ decompress-tarbz2 "^4.0.0"
+ decompress-targz "^4.0.0"
+ decompress-unzip "^4.0.1"
+ graceful-fs "^4.1.10"
+ make-dir "^1.0.0"
+ pify "^2.3.0"
+ strip-dirs "^2.0.0"
+
deep-extend@^0.6.0, deep-extend@~0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
@@ -584,6 +753,28 @@ double-ended-queue@2.1.0-0:
resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c"
integrity sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ==
+download@8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/download/-/download-8.0.0.tgz#afc0b309730811731aae9f5371c9f46be73e51b1"
+ integrity sha512-ASRY5QhDk7FK+XrQtQyvhpDKanLluEEQtWl/J7Lxuf/b+i8RYh997QeXvL85xitrmRKVlx9c7eTrcRdq2GS4eA==
+ dependencies:
+ archive-type "^4.0.0"
+ content-disposition "^0.5.2"
+ decompress "^4.2.1"
+ ext-name "^5.0.0"
+ file-type "^11.1.0"
+ filenamify "^3.0.0"
+ get-stream "^4.1.0"
+ got "^8.3.1"
+ make-dir "^2.1.0"
+ p-event "^2.1.0"
+ pify "^4.0.1"
+
+duplexer3@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e"
+ integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==
+
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
@@ -599,7 +790,7 @@ encoding-down@^6.3.0:
level-codec "^9.0.0"
level-errors "^2.0.0"
-end-of-stream@^1.1.0, end-of-stream@^1.4.1:
+end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
@@ -632,7 +823,7 @@ escalade@^3.1.1:
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
-escape-string-regexp@^1.0.5:
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
@@ -778,6 +969,21 @@ expand-template@^2.0.3:
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
+ext-list@^2.0.0:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37"
+ integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==
+ dependencies:
+ mime-db "^1.28.0"
+
+ext-name@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6"
+ integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==
+ dependencies:
+ ext-list "^2.0.0"
+ sort-keys-length "^1.0.0"
+
external-editor@^3.0.3:
version "3.1.0"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
@@ -825,6 +1031,13 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
+fd-slicer@~1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
+ integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==
+ dependencies:
+ pend "~1.2.0"
+
fetch-cookie@0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/fetch-cookie/-/fetch-cookie-0.11.0.tgz#e046d2abadd0ded5804ce7e2cae06d4331c15407"
@@ -854,6 +1067,45 @@ file-set@^5.1.2:
array-back "^6.2.2"
glob "^7.2.0"
+file-type@^11.1.0:
+ version "11.1.0"
+ resolved "https://registry.yarnpkg.com/file-type/-/file-type-11.1.0.tgz#93780f3fed98b599755d846b99a1617a2ad063b8"
+ integrity sha512-rM0UO7Qm9K7TWTtA6AShI/t7H5BPjDeGVDaNyg9BjHAj3PysKy7+8C8D137R88jnR3rFJZQB/tFgydl5sN5m7g==
+
+file-type@^3.8.0:
+ version "3.9.0"
+ resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
+ integrity sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==
+
+file-type@^4.2.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/file-type/-/file-type-4.4.0.tgz#1b600e5fca1fbdc6e80c0a70c71c8dba5f7906c5"
+ integrity sha512-f2UbFQEk7LXgWpi5ntcO86OeA/cC80fuDDDaX/fZ2ZGel+AF7leRQqBBW1eJNiiQkrZlAoM6P+VYP5P6bOlDEQ==
+
+file-type@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
+ integrity sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==
+
+file-type@^6.1.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
+ integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==
+
+filename-reserved-regex@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229"
+ integrity sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==
+
+filenamify@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-3.0.0.tgz#9603eb688179f8c5d40d828626dcbb92c3a4672c"
+ integrity sha512-5EFZ//MsvJgXjBAFJ+Bh2YaCTRF/VP1YOmGrgt+KJ4SFRLjI87EIdwLLuT6wQX0I4F9W41xutobzczjsOKlI/g==
+ dependencies:
+ filename-reserved-regex "^2.0.0"
+ strip-outer "^1.0.0"
+ trim-repeated "^1.0.0"
+
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@@ -886,7 +1138,7 @@ follow-redirects@^1.14.0:
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5"
integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
-from2@^2.3.0:
+from2@^2.1.1, from2@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
integrity sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==
@@ -950,6 +1202,26 @@ get-caller-file@^2.0.5:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+get-stream@3.0.0, get-stream@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
+ integrity sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==
+
+get-stream@^2.2.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
+ integrity sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==
+ dependencies:
+ object-assign "^4.0.1"
+ pinkie-promise "^2.0.0"
+
+get-stream@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
+ integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
+ dependencies:
+ pump "^3.0.0"
+
github-from-package@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
@@ -1000,7 +1272,30 @@ globby@^11.1.0:
merge2 "^1.4.1"
slash "^3.0.0"
-graceful-fs@^4.1.6, graceful-fs@^4.2.0:
+got@^8.3.1:
+ version "8.3.2"
+ resolved "https://registry.yarnpkg.com/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937"
+ integrity sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==
+ dependencies:
+ "@sindresorhus/is" "^0.7.0"
+ cacheable-request "^2.1.1"
+ decompress-response "^3.3.0"
+ duplexer3 "^0.1.4"
+ get-stream "^3.0.0"
+ into-stream "^3.1.0"
+ is-retry-allowed "^1.1.0"
+ isurl "^1.0.0-alpha5"
+ lowercase-keys "^1.0.0"
+ mimic-response "^1.0.0"
+ p-cancelable "^0.4.0"
+ p-timeout "^2.0.1"
+ pify "^3.0.0"
+ safe-buffer "^5.1.1"
+ timed-out "^4.0.1"
+ url-parse-lax "^3.0.0"
+ url-to-options "^1.0.1"
+
+graceful-fs@^4.1.10, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.10"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
@@ -1015,6 +1310,18 @@ has-flag@^4.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+has-symbol-support-x@^1.4.1:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455"
+ integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==
+
+has-to-string-tag-x@^1.2.0:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d"
+ integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==
+ dependencies:
+ has-symbol-support-x "^1.4.1"
+
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
@@ -1027,6 +1334,11 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
+http-cache-semantics@3.8.1:
+ version "3.8.1"
+ resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2"
+ integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==
+
https-proxy-agent@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
@@ -1122,6 +1434,14 @@ inquirer@8.0.0:
strip-ansi "^6.0.0"
through "^2.3.6"
+into-stream@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6"
+ integrity sha512-TcdjPibTksa1NQximqep2r17ISRiNE9fwlfbg3F8ANdvP5/yrFTew86VcO//jk4QTaMlbjypPBq76HN2zaKfZQ==
+ dependencies:
+ from2 "^2.1.1"
+ p-is-promise "^1.1.0"
+
into-stream@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-6.0.0.tgz#4bfc1244c0128224e18b8870e85b2de8e66c6702"
@@ -1173,16 +1493,41 @@ is-glob@^4.0.0, is-glob@^4.0.1:
dependencies:
is-extglob "^2.1.1"
+is-natural-number@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
+ integrity sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==
+
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+is-object@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf"
+ integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==
+
+is-plain-obj@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
+ integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==
+
+is-retry-allowed@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4"
+ integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==
+
is-retry-allowed@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d"
integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==
+is-stream@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+ integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==
+
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
@@ -1198,6 +1543,25 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+isurl@^1.0.0-alpha5:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67"
+ integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==
+ dependencies:
+ has-to-string-tag-x "^1.2.0"
+ is-object "^1.0.1"
+
+joi@17.6.0:
+ version "17.6.0"
+ resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2"
+ integrity sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==
+ dependencies:
+ "@hapi/hoek" "^9.0.0"
+ "@hapi/topo" "^5.0.0"
+ "@sideway/address" "^4.1.3"
+ "@sideway/formula" "^3.0.0"
+ "@sideway/pinpoint" "^2.0.0"
+
join-component@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5"
@@ -1216,6 +1580,11 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
+json-buffer@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
+ integrity sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==
+
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@@ -1245,6 +1614,13 @@ jsonfile@^6.0.1:
optionalDependencies:
graceful-fs "^4.1.6"
+keyv@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373"
+ integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==
+ dependencies:
+ json-buffer "3.0.0"
+
level-codec@9.0.2, level-codec@^9.0.0:
version "9.0.2"
resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc"
@@ -1394,6 +1770,16 @@ lookpath@1.1.0:
resolved "https://registry.yarnpkg.com/lookpath/-/lookpath-1.1.0.tgz#932d68371a2f0b4a5644f03d6a2b4728edba96d2"
integrity sha512-B9NM7XpVfkyWqfOBI/UW0kVhGw7pJztsduch+1wkbYDi90mYK6/InFul3lG0hYko/VEcVMARVBJ5daFRc5aKCw==
+lowercase-keys@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
+ integrity sha512-RPlX0+PHuvxVDZ7xX+EBVAp4RsVxP/TdDSN2mJYdiq1Lc4Hz7EUSjUI7RZrKKlmrIzVhf6Jo2stj7++gVarS0A==
+
+lowercase-keys@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
+ integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
+
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
@@ -1406,6 +1792,21 @@ ltgt@2.2.1, ltgt@^2.1.2:
resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5"
integrity sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==
+make-dir@^1.0.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
+ integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==
+ dependencies:
+ pify "^3.0.0"
+
+make-dir@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
+ integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
+ dependencies:
+ pify "^4.0.1"
+ semver "^5.6.0"
+
md5@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f"
@@ -1428,11 +1829,21 @@ micromatch@^4.0.4:
braces "^3.0.2"
picomatch "^2.3.1"
+mime-db@^1.28.0:
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+mimic-response@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
+ integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
+
mimic-response@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
@@ -1550,6 +1961,15 @@ noms@0.0.0:
inherits "^2.0.1"
readable-stream "~1.0.31"
+normalize-url@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
+ integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==
+ dependencies:
+ prepend-http "^2.0.0"
+ query-string "^5.0.1"
+ sort-keys "^2.0.0"
+
npmlog@^4.0.1:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
@@ -1565,7 +1985,7 @@ number-is-nan@^1.0.0:
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==
-object-assign@^4.1.0:
+object-assign@^4.0.1, object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
@@ -1613,11 +2033,40 @@ os-tmpdir@~1.0.2:
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
+p-cancelable@^0.4.0:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0"
+ integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==
+
+p-event@^2.1.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/p-event/-/p-event-2.3.1.tgz#596279ef169ab2c3e0cae88c1cfbb08079993ef6"
+ integrity sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA==
+ dependencies:
+ p-timeout "^2.0.1"
+
+p-finally@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+ integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==
+
+p-is-promise@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e"
+ integrity sha512-zL7VE4JVS2IFSkR2GQKDSPEVxkoH43/p7oEnwpdCndKYJO0HVeRB7fA8TJwuLOTBREtK0ea8eHaxdwcpob5dmg==
+
p-is-promise@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-3.0.0.tgz#58e78c7dfe2e163cf2a04ff869e7c1dba64a5971"
integrity sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==
+p-timeout@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038"
+ integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==
+ dependencies:
+ p-finally "^1.0.0"
+
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@@ -1645,11 +2094,43 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+pend@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
+ integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
+
picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+pify@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+ integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
+
+pify@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
+ integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==
+
+pify@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
+ integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+
+pinkie-promise@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+ integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==
+ dependencies:
+ pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+ integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==
+
pkg-fetch@3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/pkg-fetch/-/pkg-fetch-3.4.1.tgz#be68bb9f7fdb0f6ed995abc518ab2e35aa64d2fd"
@@ -1781,6 +2262,11 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==
+prepend-http@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
+ integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==
+
printj@^1.3.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/printj/-/printj-1.3.1.tgz#9af6b1d55647a1587ac44f4c1654a4b95b8e12cb"
@@ -1819,6 +2305,15 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+query-string@^5.0.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
+ integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==
+ dependencies:
+ decode-uri-component "^0.2.0"
+ object-assign "^4.1.0"
+ strict-uri-encode "^1.0.0"
+
querystringify@^2.1.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
@@ -1865,7 +2360,7 @@ readable-stream@1.1.14, readable-stream@^1.0.27-1:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
-readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@~2.3.6:
+readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@@ -1960,6 +2455,13 @@ resolve@^1.22.0:
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
+responselike@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
+ integrity sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==
+ dependencies:
+ lowercase-keys "^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"
@@ -1999,7 +2501,7 @@ rxjs@^6.6.6:
dependencies:
tslib "^1.9.0"
-safe-buffer@^5.0.1, safe-buffer@~5.2.0:
+safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@@ -2014,7 +2516,14 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
-semver@^5.4.1:
+seek-bzip@^1.0.5:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4"
+ integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==
+ dependencies:
+ commander "^2.8.1"
+
+semver@^5.4.1, semver@^5.6.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@@ -2076,6 +2585,27 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
+sort-keys-length@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188"
+ integrity sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==
+ dependencies:
+ sort-keys "^1.0.0"
+
+sort-keys@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
+ integrity sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==
+ dependencies:
+ is-plain-obj "^1.0.0"
+
+sort-keys@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"
+ integrity sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==
+ dependencies:
+ is-plain-obj "^1.0.0"
+
source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
@@ -2110,6 +2640,11 @@ stream-read-all@^3.0.1:
resolved "https://registry.yarnpkg.com/stream-read-all/-/stream-read-all-3.0.1.tgz#60762ae45e61d93ba0978cda7f3913790052ad96"
integrity sha512-EWZT9XOceBPlVJRrYcykW8jyRSZYbkb/0ZK36uLEmoWVO5gxBOnntNTseNzfREsqxqdfEGQrD8SXQ3QWbBmq8A==
+strict-uri-encode@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
+ integrity sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==
+
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@@ -2161,6 +2696,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
dependencies:
ansi-regex "^5.0.1"
+strip-dirs@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5"
+ integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==
+ dependencies:
+ is-natural-number "^4.0.1"
+
strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
@@ -2171,6 +2713,13 @@ strip-json-comments@~2.0.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
+strip-outer@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631"
+ integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==
+ dependencies:
+ escape-string-regexp "^1.0.2"
+
supports-color@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
@@ -2221,6 +2770,19 @@ tar-fs@^2.0.0, tar-fs@^2.1.1:
pump "^3.0.0"
tar-stream "^2.1.4"
+tar-stream@^1.5.2:
+ version "1.6.2"
+ resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555"
+ integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==
+ dependencies:
+ bl "^1.0.0"
+ buffer-alloc "^1.2.0"
+ end-of-stream "^1.0.0"
+ fs-constants "^1.0.0"
+ readable-stream "^2.3.0"
+ to-buffer "^1.1.1"
+ xtend "^4.0.0"
+
tar-stream@^2.1.4:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
@@ -2265,11 +2827,16 @@ through2@^2.0.0, through2@^2.0.1, through2@^2.0.2, through2@^2.0.3:
readable-stream "~2.3.6"
xtend "~4.0.1"
-through@^2.3.6:
+through@^2.3.6, through@^2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
+timed-out@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
+ integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==
+
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@@ -2277,6 +2844,11 @@ tmp@^0.0.33:
dependencies:
os-tmpdir "~1.0.2"
+to-buffer@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
+ integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==
+
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
@@ -2304,6 +2876,13 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
+trim-repeated@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21"
+ integrity sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==
+ dependencies:
+ escape-string-regexp "^1.0.2"
+
tslib@^1.9.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
@@ -2355,6 +2934,14 @@ typical@^7.1.1:
resolved "https://registry.yarnpkg.com/typical/-/typical-7.1.1.tgz#ba177ab7ab103b78534463ffa4c0c9754523ac1f"
integrity sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA==
+unbzip2-stream@^1.0.9:
+ version "1.4.3"
+ resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
+ integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==
+ dependencies:
+ buffer "^5.2.1"
+ through "^2.3.8"
+
universalify@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"
@@ -2377,6 +2964,13 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
+url-parse-lax@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c"
+ integrity sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==
+ dependencies:
+ prepend-http "^2.0.0"
+
url-parse@^1.5.3:
version "1.5.10"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
@@ -2385,6 +2979,11 @@ url-parse@^1.5.3:
querystringify "^2.1.1"
requires-port "^1.0.0"
+url-to-options@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
+ integrity sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A==
+
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@@ -2466,7 +3065,7 @@ write-stream@~0.4.3:
dependencies:
readable-stream "~0.0.2"
-xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1:
+xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
@@ -2498,3 +3097,11 @@ yargs@^16.1.0, yargs@^16.2.0:
string-width "^4.2.0"
y18n "^5.0.5"
yargs-parser "^20.2.2"
+
+yauzl@^2.4.2:
+ version "2.10.0"
+ resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
+ integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==
+ dependencies:
+ buffer-crc32 "~0.2.3"
+ fd-slicer "~1.1.0"
diff --git a/packages/client/manifest.json b/packages/client/manifest.json
index 87cc8b2567..b6d4941e4c 100644
--- a/packages/client/manifest.json
+++ b/packages/client/manifest.json
@@ -544,7 +544,8 @@
"values": [
{
"label": "Row Index",
- "key": "index"
+ "key": "index",
+ "type": "number"
}
]
}
@@ -2314,19 +2315,23 @@
"values": [
{
"label": "Value",
- "key": "__value"
+ "key": "__value",
+ "type": "object"
},
{
"label": "Valid",
- "key": "__valid"
+ "key": "__valid",
+ "type": "boolean"
},
{
"label": "Current Step",
- "key": "__currentStep"
+ "key": "__currentStep",
+ "type": "number"
},
{
"label": "Current Step Valid",
- "key": "__currentStepValid"
+ "key": "__currentStepValid",
+ "type": "boolean"
}
]
},
@@ -3550,23 +3555,28 @@
"values": [
{
"label": "Rows",
- "key": "rows"
+ "key": "rows",
+ "type": "array"
},
{
"label": "Extra Info",
- "key": "info"
+ "key": "info",
+ "type": "string"
},
{
"label": "Rows Length",
- "key": "rowsLength"
+ "key": "rowsLength",
+ "type": "number"
},
{
"label": "Schema",
- "key": "schema"
+ "key": "schema",
+ "type": "object"
},
{
"label": "Page Number",
- "key": "pageNumber"
+ "key": "pageNumber",
+ "type": "number"
}
]
}
@@ -4328,23 +4338,28 @@
"values": [
{
"label": "Rows",
- "key": "rows"
+ "key": "rows",
+ "type": "array"
},
{
"label": "Extra Info",
- "key": "info"
+ "key": "info",
+ "type": "string"
},
{
"label": "Rows Length",
- "key": "rowsLength"
+ "key": "rowsLength",
+ "type": "number"
},
{
"label": "Schema",
- "key": "schema"
+ "key": "schema",
+ "type": "object"
},
{
"label": "Page Number",
- "key": "pageNumber"
+ "key": "pageNumber",
+ "type": "number"
}
]
},
@@ -4354,7 +4369,8 @@
"values": [
{
"label": "Row Index",
- "key": "index"
+ "key": "index",
+ "type": "number"
}
]
},
diff --git a/packages/client/package.json b/packages/client/package.json
index 75fc9d3ef4..104371978b 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/client",
- "version": "1.3.21",
+ "version": "1.3.22-alpha.4",
"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.21",
- "@budibase/frontend-core": "^1.3.21",
- "@budibase/string-templates": "^1.3.21",
+ "@budibase/bbui": "1.3.22-alpha.4",
+ "@budibase/frontend-core": "1.3.22-alpha.4",
+ "@budibase/string-templates": "1.3.22-alpha.4",
"@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",
diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte
index fa503c2e23..c212fcf0f5 100644
--- a/packages/client/src/components/ClientApp.svelte
+++ b/packages/client/src/components/ClientApp.svelte
@@ -89,6 +89,14 @@
})
+
+ {#if $builderStore.usedPlugins?.length}
+ {#each $builderStore.usedPlugins as plugin (plugin.hash)}
+
+ {/each}
+ {/if}
+
+
{#if dataLoaded}
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,
@@ -167,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
@@ -182,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
@@ -239,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 => {
@@ -421,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),
})
}
})
diff --git a/packages/client/src/components/MadeInBudibase.svelte b/packages/client/src/components/FreeLogo.svelte
similarity index 78%
rename from packages/client/src/components/MadeInBudibase.svelte
rename to packages/client/src/components/FreeLogo.svelte
index 2e5d6336f1..2a5a936cdb 100644
--- a/packages/client/src/components/MadeInBudibase.svelte
+++ b/packages/client/src/components/FreeLogo.svelte
@@ -1,15 +1,20 @@
-
+ import { Link } from "@budibase/bbui"
+
+
+
-
Made In Budibase
+
Made with Budibase
-
+