Merge remote-tracking branch 'origin/develop' into fix/pc-fixes
This commit is contained in:
commit
3cedc62bc2
|
@ -68,18 +68,28 @@ jobs:
|
||||||
]
|
]
|
||||||
env:
|
env:
|
||||||
KUBECONFIG_FILE: '${{ secrets.RELEASE_KUBECONFIG }}'
|
KUBECONFIG_FILE: '${{ secrets.RELEASE_KUBECONFIG }}'
|
||||||
|
|
||||||
- name: Set the base64 kubeconfig
|
|
||||||
run: |
|
|
||||||
base64_kubeconfig=$(echo ${{ secrets.RELEASE_KUBECONFIG }} | base64)
|
|
||||||
echo "RELEASE_KUBECONFIG=$base64_kubeconfig" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Re roll the services
|
- name: Re roll app-service
|
||||||
uses: actions-hub/kubectl@master
|
uses: actions-hub/kubectl@master
|
||||||
env:
|
env:
|
||||||
KUBE_CONFIG: ${{ env.RELEASE_KUBECONFIG }}
|
KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG_BASE64 }}
|
||||||
with:
|
with:
|
||||||
args: rollout restart deployment proxy-service -n budibase && kubectl rollout restart deployment app-service -n budibase && kubectl rollout restart deployment worker-service -n budibase
|
args: rollout restart deployment app-service -n budibase
|
||||||
|
|
||||||
|
- name: Re roll proxy-service
|
||||||
|
uses: actions-hub/kubectl@master
|
||||||
|
env:
|
||||||
|
KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG_BASE64 }}
|
||||||
|
with:
|
||||||
|
args: rollout restart deployment proxy-service -n budibase
|
||||||
|
|
||||||
|
- name: Re roll worker-service
|
||||||
|
uses: actions-hub/kubectl@master
|
||||||
|
env:
|
||||||
|
KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG_BASE64 }}
|
||||||
|
with:
|
||||||
|
args: rollout restart deployment worker-service -n budibase
|
||||||
|
|
||||||
|
|
||||||
- name: Discord Webhook Action
|
- name: Discord Webhook Action
|
||||||
uses: tsickert/discord-webhook@v4.0.0
|
uses: tsickert/discord-webhook@v4.0.0
|
||||||
|
|
|
@ -121,17 +121,26 @@ jobs:
|
||||||
env:
|
env:
|
||||||
KUBECONFIG_FILE: '${{ secrets.RELEASE_KUBECONFIG }}'
|
KUBECONFIG_FILE: '${{ secrets.RELEASE_KUBECONFIG }}'
|
||||||
|
|
||||||
- name: Set the base64 kubeconfig
|
- name: Re roll app-service
|
||||||
run: |
|
|
||||||
base64_kubeconfig=$(echo ${{ secrets.RELEASE_KUBECONFIG }} | base64)
|
|
||||||
echo "RELEASE_KUBECONFIG=$base64_kubeconfig" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Re roll the services
|
|
||||||
uses: actions-hub/kubectl@master
|
uses: actions-hub/kubectl@master
|
||||||
env:
|
env:
|
||||||
KUBE_CONFIG: ${{ env.RELEASE_KUBECONFIG }}
|
KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG_BASE64 }}
|
||||||
with:
|
with:
|
||||||
args: rollout restart deployment proxy-service -n budibase && kubectl rollout restart deployment app-service -n budibase && kubectl rollout restart deployment worker-service -n budibase
|
args: rollout restart deployment app-service -n budibase
|
||||||
|
|
||||||
|
- name: Re roll proxy-service
|
||||||
|
uses: actions-hub/kubectl@master
|
||||||
|
env:
|
||||||
|
KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG_BASE64 }}
|
||||||
|
with:
|
||||||
|
args: rollout restart deployment proxy-service -n budibase
|
||||||
|
|
||||||
|
- name: Re roll worker-service
|
||||||
|
uses: actions-hub/kubectl@master
|
||||||
|
env:
|
||||||
|
KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG_BASE64 }}
|
||||||
|
with:
|
||||||
|
args: rollout restart deployment worker-service -n budibase
|
||||||
|
|
||||||
- name: Discord Webhook Action
|
- name: Discord Webhook Action
|
||||||
uses: tsickert/discord-webhook@v4.0.0
|
uses: tsickert/discord-webhook@v4.0.0
|
||||||
|
|
|
@ -102,4 +102,6 @@ packages/builder/cypress/reports
|
||||||
stats.html
|
stats.html
|
||||||
|
|
||||||
# TypeScript cache
|
# TypeScript cache
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
budibase-component
|
||||||
|
budibase-datasource
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"singleQuote": false,
|
"singleQuote": false,
|
||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
"arrowParens": "avoid",
|
"arrowParens": "avoid",
|
||||||
"jsxBracketSameLine": false,
|
"bracketSameLine": false,
|
||||||
"plugins": ["prettier-plugin-svelte"],
|
"plugins": ["prettier-plugin-svelte"],
|
||||||
"svelteSortOrder": "options-scripts-markup-styles"
|
"svelteSortOrder": "options-scripts-markup-styles"
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,11 +124,31 @@ spec:
|
||||||
value: {{ .Values.globals.tenantFeatureFlags | quote }}
|
value: {{ .Values.globals.tenantFeatureFlags | quote }}
|
||||||
{{ if .Values.globals.bbAdminUserEmail }}
|
{{ if .Values.globals.bbAdminUserEmail }}
|
||||||
- name: BB_ADMIN_USER_EMAIL
|
- name: BB_ADMIN_USER_EMAIL
|
||||||
value: { { .Values.globals.bbAdminUserEmail | quote } }
|
value: {{ .Values.globals.bbAdminUserEmail | quote }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ if .Values.globals.bbAdminUserPassword }}
|
{{ if .Values.globals.bbAdminUserPassword }}
|
||||||
- name: BB_ADMIN_USER_PASSWORD
|
- 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
|
||||||
|
value: {{ .Values.services.apps.nodeDebug | quote }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Values.globals.elasticApmEnabled }}
|
||||||
|
- name: ELASTIC_APM_ENABLED
|
||||||
|
value: {{ .Values.globals.elasticApmEnabled | quote }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Values.globals.elasticApmSecretToken }}
|
||||||
|
- name: ELASTIC_APM_SECRET_TOKEN
|
||||||
|
value: {{ .Values.globals.elasticApmSecretToken | quote }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Values.globals.elasticApmServerUrl }}
|
||||||
|
- name: ELASTIC_APM_SERVER_URL
|
||||||
|
value: {{ .Values.globals.elasticApmServerUrl | quote }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
image: budibase/apps:{{ .Values.globals.appVersion }}
|
image: budibase/apps:{{ .Values.globals.appVersion }}
|
||||||
|
@ -142,7 +162,10 @@ spec:
|
||||||
name: bbapps
|
name: bbapps
|
||||||
ports:
|
ports:
|
||||||
- containerPort: {{ .Values.services.apps.port }}
|
- containerPort: {{ .Values.services.apps.port }}
|
||||||
resources: {}
|
{{ with .Values.services.apps.resources }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{ end }}
|
||||||
{{- with .Values.affinity }}
|
{{- with .Values.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
|
|
|
@ -38,7 +38,10 @@ spec:
|
||||||
image: redgeoff/replicate-couchdb-cluster
|
image: redgeoff/replicate-couchdb-cluster
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: couchdb-backup
|
name: couchdb-backup
|
||||||
resources: {}
|
{{ with .Values.services.couchdb.backup.resources }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{ end }}
|
||||||
{{- with .Values.affinity }}
|
{{- with .Values.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
|
|
|
@ -56,7 +56,10 @@ spec:
|
||||||
name: minio-service
|
name: minio-service
|
||||||
ports:
|
ports:
|
||||||
- containerPort: {{ .Values.services.objectStore.port }}
|
- containerPort: {{ .Values.services.objectStore.port }}
|
||||||
resources: {}
|
{{ with .Values.services.objectStore.resources }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{ end }}
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- mountPath: /data
|
- mountPath: /data
|
||||||
name: minio-data
|
name: minio-data
|
||||||
|
|
|
@ -30,7 +30,10 @@ spec:
|
||||||
name: proxy-service
|
name: proxy-service
|
||||||
ports:
|
ports:
|
||||||
- containerPort: {{ .Values.services.proxy.port }}
|
- containerPort: {{ .Values.services.proxy.port }}
|
||||||
resources: {}
|
{{ with .Values.services.proxy.resources }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{ end }}
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
{{- with .Values.affinity }}
|
{{- with .Values.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
|
|
|
@ -35,7 +35,10 @@ spec:
|
||||||
name: redis-service
|
name: redis-service
|
||||||
ports:
|
ports:
|
||||||
- containerPort: {{ .Values.services.redis.port }}
|
- containerPort: {{ .Values.services.redis.port }}
|
||||||
resources: {}
|
{{ with .Values.services.redis.resources }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{ end }}
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- mountPath: /data
|
- mountPath: /data
|
||||||
name: redis-data
|
name: redis-data
|
||||||
|
|
|
@ -27,6 +27,8 @@ spec:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- env:
|
- env:
|
||||||
|
- name: BUDIBASE_ENVIRONMENT
|
||||||
|
value: {{ .Values.globals.budibaseEnv }}
|
||||||
- name: DEPLOYMENT_ENVIRONMENT
|
- name: DEPLOYMENT_ENVIRONMENT
|
||||||
value: "kubernetes"
|
value: "kubernetes"
|
||||||
- name: CLUSTER_PORT
|
- name: CLUSTER_PORT
|
||||||
|
@ -125,6 +127,19 @@ spec:
|
||||||
value: {{ .Values.globals.google.secret | quote }}
|
value: {{ .Values.globals.google.secret | quote }}
|
||||||
- name: TENANT_FEATURE_FLAGS
|
- name: TENANT_FEATURE_FLAGS
|
||||||
value: {{ .Values.globals.tenantFeatureFlags | quote }}
|
value: {{ .Values.globals.tenantFeatureFlags | quote }}
|
||||||
|
{{ if .Values.globals.elasticApmEnabled }}
|
||||||
|
- name: ELASTIC_APM_ENABLED
|
||||||
|
value: {{ .Values.globals.elasticApmEnabled | quote }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Values.globals.elasticApmSecretToken }}
|
||||||
|
- name: ELASTIC_APM_SECRET_TOKEN
|
||||||
|
value: {{ .Values.globals.elasticApmSecretToken | quote }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Values.globals.elasticApmServerUrl }}
|
||||||
|
- name: ELASTIC_APM_SERVER_URL
|
||||||
|
value: {{ .Values.globals.elasticApmServerUrl | quote }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
image: budibase/worker:{{ .Values.globals.appVersion }}
|
image: budibase/worker:{{ .Values.globals.appVersion }}
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
|
@ -136,7 +151,10 @@ spec:
|
||||||
name: bbworker
|
name: bbworker
|
||||||
ports:
|
ports:
|
||||||
- containerPort: {{ .Values.services.worker.port }}
|
- containerPort: {{ .Values.services.worker.port }}
|
||||||
resources: {}
|
{{ with .Values.services.worker.resources }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{ end }}
|
||||||
{{- with .Values.affinity }}
|
{{- with .Values.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
|
|
|
@ -60,19 +60,6 @@ ingress:
|
||||||
port:
|
port:
|
||||||
number: 10000
|
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:
|
autoscaling:
|
||||||
enabled: false
|
enabled: false
|
||||||
minReplicas: 1
|
minReplicas: 1
|
||||||
|
@ -114,6 +101,10 @@ globals:
|
||||||
smtp:
|
smtp:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
|
# elasticApmEnabled:
|
||||||
|
# elasticApmSecretToken:
|
||||||
|
# elasticApmServerUrl:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
budibaseVersion: latest
|
budibaseVersion: latest
|
||||||
dns: cluster.local
|
dns: cluster.local
|
||||||
|
@ -121,15 +112,19 @@ services:
|
||||||
proxy:
|
proxy:
|
||||||
port: 10000
|
port: 10000
|
||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
|
resources: {}
|
||||||
|
|
||||||
apps:
|
apps:
|
||||||
port: 4002
|
port: 4002
|
||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
logLevel: info
|
logLevel: info
|
||||||
|
resources: {}
|
||||||
|
# nodeDebug: "" # set the value of NODE_DEBUG
|
||||||
|
|
||||||
worker:
|
worker:
|
||||||
port: 4003
|
port: 4003
|
||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
|
resources: {}
|
||||||
|
|
||||||
couchdb:
|
couchdb:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
@ -143,6 +138,7 @@ services:
|
||||||
target: ""
|
target: ""
|
||||||
# backup interval in seconds
|
# backup interval in seconds
|
||||||
interval: ""
|
interval: ""
|
||||||
|
resources: {}
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
enabled: true # disable if using external redis
|
enabled: true # disable if using external redis
|
||||||
|
@ -156,6 +152,7 @@ services:
|
||||||
## If undefined (the default) or set to null, no storageClassName spec is
|
## If undefined (the default) or set to null, no storageClassName spec is
|
||||||
## set, choosing the default provisioner.
|
## set, choosing the default provisioner.
|
||||||
storageClass: ""
|
storageClass: ""
|
||||||
|
resources: {}
|
||||||
|
|
||||||
objectStore:
|
objectStore:
|
||||||
minio: true
|
minio: true
|
||||||
|
@ -172,6 +169,7 @@ services:
|
||||||
## If undefined (the default) or set to null, no storageClassName spec is
|
## If undefined (the default) or set to null, no storageClassName spec is
|
||||||
## set, choosing the default provisioner.
|
## set, choosing the default provisioner.
|
||||||
storageClass: ""
|
storageClass: ""
|
||||||
|
resources: {}
|
||||||
|
|
||||||
# Override values in couchDB subchart
|
# Override values in couchDB subchart
|
||||||
couchdb:
|
couchdb:
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bulma": "^0.9.3",
|
"bulma": "^0.9.3",
|
||||||
"next": "12.1.0",
|
"next": "12.1.0",
|
||||||
"node-fetch": "^3.2.2",
|
"node-fetch": "^3.2.10",
|
||||||
"node-sass": "^7.0.1",
|
"node-sass": "^7.0.1",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
|
|
|
@ -2020,10 +2020,10 @@ node-domexception@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
|
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
|
||||||
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
|
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
|
||||||
|
|
||||||
node-fetch@^3.2.2:
|
node-fetch@^3.2.10:
|
||||||
version "3.2.2"
|
version "3.2.10"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.2.tgz#16d33fbe32ca7c6ca1ca8ba5dfea1dd885c59f04"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.10.tgz#e8347f94b54ae18b57c9c049ef641cef398a85c8"
|
||||||
integrity sha512-Cwhq1JFIoon15wcIkFzubVNFE5GvXGV82pKf4knXXjvGmn7RJKcypeuqcVNZMGDZsAFWyIRya/anwAJr7TWJ7w==
|
integrity sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==
|
||||||
dependencies:
|
dependencies:
|
||||||
data-uri-to-buffer "^4.0.0"
|
data-uri-to-buffer "^4.0.0"
|
||||||
fetch-blob "^3.1.4"
|
fetch-blob "^3.1.4"
|
||||||
|
|
|
@ -22,4 +22,7 @@ BUDIBASE_ENVIRONMENT=PRODUCTION
|
||||||
|
|
||||||
# An admin user can be automatically created initially if these are set
|
# An admin user can be automatically created initially if these are set
|
||||||
BB_ADMIN_USER_EMAIL=
|
BB_ADMIN_USER_EMAIL=
|
||||||
BB_ADMIN_USER_PASSWORD=
|
BB_ADMIN_USER_PASSWORD=
|
||||||
|
|
||||||
|
# A path that is watched for plugin bundles. Any bundles found are imported automatically/
|
||||||
|
PLUGINS_DIR=
|
|
@ -25,9 +25,12 @@ services:
|
||||||
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||||
BB_ADMIN_USER_EMAIL: ${BB_ADMIN_USER_EMAIL}
|
BB_ADMIN_USER_EMAIL: ${BB_ADMIN_USER_EMAIL}
|
||||||
BB_ADMIN_USER_PASSWORD: ${BB_ADMIN_USER_PASSWORD}
|
BB_ADMIN_USER_PASSWORD: ${BB_ADMIN_USER_PASSWORD}
|
||||||
|
PLUGINS_DIR: ${PLUGINS_DIR}
|
||||||
depends_on:
|
depends_on:
|
||||||
- worker-service
|
- worker-service
|
||||||
- redis-service
|
- redis-service
|
||||||
|
# volumes:
|
||||||
|
# - /some/path/to/plugins:/plugins
|
||||||
|
|
||||||
worker-service:
|
worker-service:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
@ -78,6 +81,7 @@ services:
|
||||||
image: budibase/proxy
|
image: budibase/proxy
|
||||||
environment:
|
environment:
|
||||||
- PROXY_RATE_LIMIT_WEBHOOKS_PER_SECOND=10
|
- PROXY_RATE_LIMIT_WEBHOOKS_PER_SECOND=10
|
||||||
|
- PROXY_RATE_LIMIT_API_PER_SECOND=20
|
||||||
depends_on:
|
depends_on:
|
||||||
- minio-service
|
- minio-service
|
||||||
- worker-service
|
- worker-service
|
||||||
|
|
|
@ -22,4 +22,7 @@ BUDIBASE_ENVIRONMENT=PRODUCTION
|
||||||
|
|
||||||
# An admin user can be automatically created initially if these are set
|
# An admin user can be automatically created initially if these are set
|
||||||
BB_ADMIN_USER_EMAIL=
|
BB_ADMIN_USER_EMAIL=
|
||||||
BB_ADMIN_USER_PASSWORD=
|
BB_ADMIN_USER_PASSWORD=
|
||||||
|
|
||||||
|
# A path that is watched for plugin bundles. Any bundles found are imported automatically/
|
||||||
|
PLUGINS_DIR=
|
|
@ -15,7 +15,10 @@ http {
|
||||||
|
|
||||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
'$status $body_bytes_sent "$http_referer" '
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
'"$http_user_agent" "$http_x_forwarded_for" '
|
||||||
|
'response_time=$upstream_response_time proxy_host=$proxy_host upstream_addr=$upstream_addr';
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log main;
|
||||||
|
|
||||||
map $http_upgrade $connection_upgrade {
|
map $http_upgrade $connection_upgrade {
|
||||||
default "upgrade";
|
default "upgrade";
|
||||||
|
@ -81,6 +84,11 @@ http {
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /vite {
|
||||||
|
proxy_pass http://{{ address }}:3000;
|
||||||
|
rewrite ^/vite(.*)$ /$1 break;
|
||||||
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|
|
@ -11,7 +11,7 @@ events {
|
||||||
http {
|
http {
|
||||||
# rate limiting
|
# rate limiting
|
||||||
limit_req_status 429;
|
limit_req_status 429;
|
||||||
limit_req_zone $binary_remote_addr zone=ratelimit:10m rate=20r/s;
|
limit_req_zone $binary_remote_addr zone=ratelimit:10m rate=${PROXY_RATE_LIMIT_API_PER_SECOND}r/s;
|
||||||
limit_req_zone $binary_remote_addr zone=webhooks:10m rate=${PROXY_RATE_LIMIT_WEBHOOKS_PER_SECOND}r/s;
|
limit_req_zone $binary_remote_addr zone=webhooks:10m rate=${PROXY_RATE_LIMIT_WEBHOOKS_PER_SECOND}r/s;
|
||||||
|
|
||||||
include /etc/nginx/mime.types;
|
include /etc/nginx/mime.types;
|
||||||
|
@ -33,7 +33,10 @@ http {
|
||||||
|
|
||||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
'$status $body_bytes_sent "$http_referer" '
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
'"$http_user_agent" "$http_x_forwarded_for" '
|
||||||
|
'response_time=$upstream_response_time proxy_host=$proxy_host upstream_addr=$upstream_addr';
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log main;
|
||||||
|
|
||||||
map $http_upgrade $connection_upgrade {
|
map $http_upgrade $connection_upgrade {
|
||||||
default "upgrade";
|
default "upgrade";
|
||||||
|
|
|
@ -10,4 +10,5 @@ COPY .generated-nginx.prod.conf /etc/nginx/templates/nginx.conf.template
|
||||||
COPY error.html /usr/share/nginx/html/error.html
|
COPY error.html /usr/share/nginx/html/error.html
|
||||||
|
|
||||||
# Default environment
|
# Default environment
|
||||||
ENV PROXY_RATE_LIMIT_WEBHOOKS_PER_SECOND=10
|
ENV PROXY_RATE_LIMIT_WEBHOOKS_PER_SECOND=10
|
||||||
|
ENV PROXY_RATE_LIMIT_API_PER_SECOND=20
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "1.2.44-alpha.2",
|
"version": "1.3.12-alpha.3",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/backend-core",
|
"name": "@budibase/backend-core",
|
||||||
"version": "1.2.44-alpha.2",
|
"version": "1.3.12-alpha.3",
|
||||||
"description": "Budibase backend core libraries used in server and worker",
|
"description": "Budibase backend core libraries used in server and worker",
|
||||||
"main": "dist/src/index.js",
|
"main": "dist/src/index.js",
|
||||||
"types": "dist/src/index.d.ts",
|
"types": "dist/src/index.d.ts",
|
||||||
|
@ -20,7 +20,8 @@
|
||||||
"test:watch": "jest --watchAll"
|
"test:watch": "jest --watchAll"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/types": "1.2.44-alpha.2",
|
"@budibase/types": "1.3.12-alpha.3",
|
||||||
|
"@shopify/jest-koa-mocks": "5.0.1",
|
||||||
"@techpass/passport-openidconnect": "0.3.2",
|
"@techpass/passport-openidconnect": "0.3.2",
|
||||||
"aws-sdk": "2.1030.0",
|
"aws-sdk": "2.1030.0",
|
||||||
"bcrypt": "5.0.1",
|
"bcrypt": "5.0.1",
|
||||||
|
@ -60,7 +61,6 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@shopify/jest-koa-mocks": "3.1.5",
|
|
||||||
"@types/jest": "27.5.1",
|
"@types/jest": "27.5.1",
|
||||||
"@types/koa": "2.0.52",
|
"@types/koa": "2.0.52",
|
||||||
"@types/lodash": "4.14.180",
|
"@types/lodash": "4.14.180",
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
const passport = require("koa-passport")
|
const passport = require("koa-passport")
|
||||||
const LocalStrategy = require("passport-local").Strategy
|
const LocalStrategy = require("passport-local").Strategy
|
||||||
const JwtStrategy = require("passport-jwt").Strategy
|
const JwtStrategy = require("passport-jwt").Strategy
|
||||||
const { getGlobalDB } = require("./tenancy")
|
import { getGlobalDB } from "./tenancy"
|
||||||
const refresh = require("passport-oauth2-refresh")
|
const refresh = require("passport-oauth2-refresh")
|
||||||
const { Configs } = require("./constants")
|
import { Configs } from "./constants"
|
||||||
const { getScopedConfig } = require("./db/utils")
|
import { getScopedConfig } from "./db/utils"
|
||||||
const {
|
import {
|
||||||
jwt,
|
jwt,
|
||||||
local,
|
local,
|
||||||
authenticated,
|
authenticated,
|
||||||
|
@ -13,7 +13,6 @@ const {
|
||||||
oidc,
|
oidc,
|
||||||
auditLog,
|
auditLog,
|
||||||
tenancy,
|
tenancy,
|
||||||
appTenancy,
|
|
||||||
authError,
|
authError,
|
||||||
ssoCallbackUrl,
|
ssoCallbackUrl,
|
||||||
csrf,
|
csrf,
|
||||||
|
@ -22,32 +21,36 @@ const {
|
||||||
builderOnly,
|
builderOnly,
|
||||||
builderOrAdmin,
|
builderOrAdmin,
|
||||||
joiValidator,
|
joiValidator,
|
||||||
} = require("./middleware")
|
} from "./middleware"
|
||||||
|
import { invalidateUser } from "./cache/user"
|
||||||
const { invalidateUser } = require("./cache/user")
|
import { User } from "@budibase/types"
|
||||||
|
|
||||||
// Strategies
|
// Strategies
|
||||||
passport.use(new LocalStrategy(local.options, local.authenticate))
|
passport.use(new LocalStrategy(local.options, local.authenticate))
|
||||||
passport.use(new JwtStrategy(jwt.options, jwt.authenticate))
|
passport.use(new JwtStrategy(jwt.options, jwt.authenticate))
|
||||||
|
|
||||||
passport.serializeUser((user, done) => done(null, user))
|
passport.serializeUser((user: User, done: any) => done(null, user))
|
||||||
|
|
||||||
passport.deserializeUser(async (user, done) => {
|
passport.deserializeUser(async (user: User, done: any) => {
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await db.get(user._id)
|
const dbUser = await db.get(user._id)
|
||||||
return done(null, user)
|
return done(null, dbUser)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`User not found`, err)
|
console.error(`User not found`, err)
|
||||||
return done(null, false, { message: "User not found" })
|
return done(null, false, { message: "User not found" })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
async function refreshOIDCAccessToken(db, chosenConfig, refreshToken) {
|
async function refreshOIDCAccessToken(
|
||||||
|
db: any,
|
||||||
|
chosenConfig: any,
|
||||||
|
refreshToken: string
|
||||||
|
) {
|
||||||
const callbackUrl = await oidc.getCallbackUrl(db, chosenConfig)
|
const callbackUrl = await oidc.getCallbackUrl(db, chosenConfig)
|
||||||
let enrichedConfig
|
let enrichedConfig: any
|
||||||
let strategy
|
let strategy: any
|
||||||
|
|
||||||
try {
|
try {
|
||||||
enrichedConfig = await oidc.fetchStrategyConfig(chosenConfig, callbackUrl)
|
enrichedConfig = await oidc.fetchStrategyConfig(chosenConfig, callbackUrl)
|
||||||
|
@ -70,22 +73,28 @@ async function refreshOIDCAccessToken(db, chosenConfig, refreshToken) {
|
||||||
refresh.requestNewAccessToken(
|
refresh.requestNewAccessToken(
|
||||||
Configs.OIDC,
|
Configs.OIDC,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
(err, accessToken, refreshToken, params) => {
|
(err: any, accessToken: string, refreshToken: any, params: any) => {
|
||||||
resolve({ err, accessToken, refreshToken, params })
|
resolve({ err, accessToken, refreshToken, params })
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshGoogleAccessToken(db, config, refreshToken) {
|
async function refreshGoogleAccessToken(
|
||||||
|
db: any,
|
||||||
|
config: any,
|
||||||
|
refreshToken: any
|
||||||
|
) {
|
||||||
let callbackUrl = await google.getCallbackUrl(db, config)
|
let callbackUrl = await google.getCallbackUrl(db, config)
|
||||||
|
|
||||||
let strategy
|
let strategy
|
||||||
try {
|
try {
|
||||||
strategy = await google.strategyFactory(config, callbackUrl)
|
strategy = await google.strategyFactory(config, callbackUrl)
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
throw new Error("Error constructing OIDC refresh strategy", err)
|
throw new Error(
|
||||||
|
`Error constructing OIDC refresh strategy: message=${err.message}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh.use(strategy)
|
refresh.use(strategy)
|
||||||
|
@ -94,14 +103,18 @@ async function refreshGoogleAccessToken(db, config, refreshToken) {
|
||||||
refresh.requestNewAccessToken(
|
refresh.requestNewAccessToken(
|
||||||
Configs.GOOGLE,
|
Configs.GOOGLE,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
(err, accessToken, refreshToken, params) => {
|
(err: any, accessToken: string, refreshToken: string, params: any) => {
|
||||||
resolve({ err, accessToken, refreshToken, params })
|
resolve({ err, accessToken, refreshToken, params })
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshOAuthToken(refreshToken, configType, configId) {
|
async function refreshOAuthToken(
|
||||||
|
refreshToken: string,
|
||||||
|
configType: string,
|
||||||
|
configId: string
|
||||||
|
) {
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
|
|
||||||
const config = await getScopedConfig(db, {
|
const config = await getScopedConfig(db, {
|
||||||
|
@ -113,7 +126,7 @@ async function refreshOAuthToken(refreshToken, configType, configId) {
|
||||||
let refreshResponse
|
let refreshResponse
|
||||||
if (configType === Configs.OIDC) {
|
if (configType === Configs.OIDC) {
|
||||||
// configId - retrieved from cookie.
|
// configId - retrieved from cookie.
|
||||||
chosenConfig = config.configs.filter(c => c.uuid === configId)[0]
|
chosenConfig = config.configs.filter((c: any) => c.uuid === configId)[0]
|
||||||
if (!chosenConfig) {
|
if (!chosenConfig) {
|
||||||
throw new Error("Invalid OIDC configuration")
|
throw new Error("Invalid OIDC configuration")
|
||||||
}
|
}
|
||||||
|
@ -134,7 +147,7 @@ async function refreshOAuthToken(refreshToken, configType, configId) {
|
||||||
return refreshResponse
|
return refreshResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateUserOAuth(userId, oAuthConfig) {
|
async function updateUserOAuth(userId: string, oAuthConfig: any) {
|
||||||
const details = {
|
const details = {
|
||||||
accessToken: oAuthConfig.accessToken,
|
accessToken: oAuthConfig.accessToken,
|
||||||
refreshToken: oAuthConfig.refreshToken,
|
refreshToken: oAuthConfig.refreshToken,
|
||||||
|
@ -162,14 +175,13 @@ async function updateUserOAuth(userId, oAuthConfig) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
export = {
|
||||||
buildAuthMiddleware: authenticated,
|
buildAuthMiddleware: authenticated,
|
||||||
passport,
|
passport,
|
||||||
google,
|
google,
|
||||||
oidc,
|
oidc,
|
||||||
jwt: require("jsonwebtoken"),
|
jwt: require("jsonwebtoken"),
|
||||||
buildTenancyMiddleware: tenancy,
|
buildTenancyMiddleware: tenancy,
|
||||||
buildAppTenancyMiddleware: appTenancy,
|
|
||||||
auditLog,
|
auditLog,
|
||||||
authError,
|
authError,
|
||||||
buildCsrfMiddleware: csrf,
|
buildCsrfMiddleware: csrf,
|
|
@ -18,6 +18,7 @@ export enum ViewName {
|
||||||
LINK = "by_link",
|
LINK = "by_link",
|
||||||
ROUTING = "screen_routes",
|
ROUTING = "screen_routes",
|
||||||
AUTOMATION_LOGS = "automation_logs",
|
AUTOMATION_LOGS = "automation_logs",
|
||||||
|
ACCOUNT_BY_EMAIL = "account_by_email",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DeprecatedViews = {
|
export const DeprecatedViews = {
|
||||||
|
@ -41,6 +42,7 @@ export enum DocumentType {
|
||||||
MIGRATIONS = "migrations",
|
MIGRATIONS = "migrations",
|
||||||
DEV_INFO = "devinfo",
|
DEV_INFO = "devinfo",
|
||||||
AUTOMATION_LOG = "log_au",
|
AUTOMATION_LOG = "log_au",
|
||||||
|
ACCOUNT_METADATA = "acc_metadata",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StaticDatabases = {
|
export const StaticDatabases = {
|
||||||
|
|
|
@ -5,6 +5,8 @@ const {
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
} = require("./utils")
|
} = require("./utils")
|
||||||
const { getGlobalDB } = require("../tenancy")
|
const { getGlobalDB } = require("../tenancy")
|
||||||
|
const { StaticDatabases } = require("./constants")
|
||||||
|
const { doWithDB } = require("./")
|
||||||
|
|
||||||
const DESIGN_DB = "_design/database"
|
const DESIGN_DB = "_design/database"
|
||||||
|
|
||||||
|
@ -56,6 +58,31 @@ exports.createNewUserEmailView = async () => {
|
||||||
await db.put(designDoc)
|
await db.put(designDoc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.createAccountEmailView = async () => {
|
||||||
|
await doWithDB(StaticDatabases.PLATFORM_INFO.name, async db => {
|
||||||
|
let designDoc
|
||||||
|
try {
|
||||||
|
designDoc = await db.get(DESIGN_DB)
|
||||||
|
} catch (err) {
|
||||||
|
// no design doc, make one
|
||||||
|
designDoc = DesignDoc()
|
||||||
|
}
|
||||||
|
const view = {
|
||||||
|
// if using variables in a map function need to inject them before use
|
||||||
|
map: `function(doc) {
|
||||||
|
if (doc._id.startsWith("${DocumentType.ACCOUNT_METADATA}${SEPARATOR}")) {
|
||||||
|
emit(doc.email.toLowerCase(), doc._id)
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
}
|
||||||
|
designDoc.views = {
|
||||||
|
...designDoc.views,
|
||||||
|
[ViewName.ACCOUNT_BY_EMAIL]: view,
|
||||||
|
}
|
||||||
|
await db.put(designDoc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
exports.createUserAppView = async () => {
|
exports.createUserAppView = async () => {
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
let designDoc
|
let designDoc
|
||||||
|
@ -128,6 +155,39 @@ exports.createUserBuildersView = async () => {
|
||||||
await db.put(designDoc)
|
await db.put(designDoc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.queryView = async (viewName, params, db, CreateFuncByName) => {
|
||||||
|
try {
|
||||||
|
let response = (await db.query(`database/${viewName}`, params)).rows
|
||||||
|
response = response.map(resp =>
|
||||||
|
params.include_docs ? resp.doc : resp.value
|
||||||
|
)
|
||||||
|
if (params.arrayResponse) {
|
||||||
|
return response
|
||||||
|
} else {
|
||||||
|
return response.length <= 1 ? response[0] : response
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err != null && err.name === "not_found") {
|
||||||
|
const createFunc = CreateFuncByName[viewName]
|
||||||
|
await removeDeprecated(db, viewName)
|
||||||
|
await createFunc()
|
||||||
|
return exports.queryView(viewName, params, db, CreateFuncByName)
|
||||||
|
} else {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.queryPlatformView = async (viewName, params) => {
|
||||||
|
const CreateFuncByName = {
|
||||||
|
[ViewName.ACCOUNT_BY_EMAIL]: exports.createAccountEmailView,
|
||||||
|
}
|
||||||
|
|
||||||
|
return doWithDB(StaticDatabases.PLATFORM_INFO.name, async db => {
|
||||||
|
return exports.queryView(viewName, params, db, CreateFuncByName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
exports.queryGlobalView = async (viewName, params, db = null) => {
|
exports.queryGlobalView = async (viewName, params, db = null) => {
|
||||||
const CreateFuncByName = {
|
const CreateFuncByName = {
|
||||||
[ViewName.USER_BY_EMAIL]: exports.createNewUserEmailView,
|
[ViewName.USER_BY_EMAIL]: exports.createNewUserEmailView,
|
||||||
|
@ -139,20 +199,5 @@ exports.queryGlobalView = async (viewName, params, db = null) => {
|
||||||
if (!db) {
|
if (!db) {
|
||||||
db = getGlobalDB()
|
db = getGlobalDB()
|
||||||
}
|
}
|
||||||
try {
|
return exports.queryView(viewName, params, db, CreateFuncByName)
|
||||||
let response = (await db.query(`database/${viewName}`, params)).rows
|
|
||||||
response = response.map(resp =>
|
|
||||||
params.include_docs ? resp.doc : resp.value
|
|
||||||
)
|
|
||||||
return response.length <= 1 ? response[0] : response
|
|
||||||
} catch (err) {
|
|
||||||
if (err != null && err.name === "not_found") {
|
|
||||||
const createFunc = CreateFuncByName[viewName]
|
|
||||||
await removeDeprecated(db, viewName)
|
|
||||||
await createFunc()
|
|
||||||
return exports.queryGlobalView(viewName, params)
|
|
||||||
} else {
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ const env = {
|
||||||
GLOBAL_BUCKET_NAME: process.env.GLOBAL_BUCKET_NAME || "global",
|
GLOBAL_BUCKET_NAME: process.env.GLOBAL_BUCKET_NAME || "global",
|
||||||
GLOBAL_CLOUD_BUCKET_NAME:
|
GLOBAL_CLOUD_BUCKET_NAME:
|
||||||
process.env.GLOBAL_CLOUD_BUCKET_NAME || "prod-budi-tenant-uploads",
|
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,
|
USE_COUCH: process.env.USE_COUCH || true,
|
||||||
DISABLE_DEVELOPER_LICENSE: process.env.DISABLE_DEVELOPER_LICENSE,
|
DISABLE_DEVELOPER_LICENSE: process.env.DISABLE_DEVELOPER_LICENSE,
|
||||||
DEFAULT_LICENSE: process.env.DEFAULT_LICENSE,
|
DEFAULT_LICENSE: process.env.DEFAULT_LICENSE,
|
||||||
|
|
|
@ -8,4 +8,5 @@ import { processors } from "./processors"
|
||||||
|
|
||||||
export const shutdown = () => {
|
export const shutdown = () => {
|
||||||
processors.shutdown()
|
processors.shutdown()
|
||||||
|
console.log("Events shutdown")
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import constants from "./constants"
|
||||||
import * as dbConstants from "./db/constants"
|
import * as dbConstants from "./db/constants"
|
||||||
import logging from "./logging"
|
import logging from "./logging"
|
||||||
import pino from "./pino"
|
import pino from "./pino"
|
||||||
|
import * as middleware from "./middleware"
|
||||||
|
|
||||||
// mimic the outer package exports
|
// mimic the outer package exports
|
||||||
import * as db from "./pkg/db"
|
import * as db from "./pkg/db"
|
||||||
|
@ -57,6 +58,7 @@ const core = {
|
||||||
roles,
|
roles,
|
||||||
...pino,
|
...pino,
|
||||||
...errorClasses,
|
...errorClasses,
|
||||||
|
middleware,
|
||||||
}
|
}
|
||||||
|
|
||||||
export = core
|
export = core
|
||||||
|
|
|
@ -65,7 +65,7 @@ async function checkApiKey(apiKey: string, populateUser?: Function) {
|
||||||
* The tenancy modules should not be used here and it should be assumed that the tenancy context
|
* The tenancy modules should not be used here and it should be assumed that the tenancy context
|
||||||
* has not yet been populated.
|
* has not yet been populated.
|
||||||
*/
|
*/
|
||||||
module.exports = (
|
export = (
|
||||||
noAuthPatterns = [],
|
noAuthPatterns = [],
|
||||||
opts: { publicAllowed: boolean; populateUser?: Function } = {
|
opts: { publicAllowed: boolean; populateUser?: Function } = {
|
||||||
publicAllowed: false,
|
publicAllowed: false,
|
||||||
|
|
|
@ -13,7 +13,8 @@ const adminOnly = require("./adminOnly")
|
||||||
const builderOrAdmin = require("./builderOrAdmin")
|
const builderOrAdmin = require("./builderOrAdmin")
|
||||||
const builderOnly = require("./builderOnly")
|
const builderOnly = require("./builderOnly")
|
||||||
const joiValidator = require("./joi-validator")
|
const joiValidator = require("./joi-validator")
|
||||||
module.exports = {
|
|
||||||
|
const pkg = {
|
||||||
google,
|
google,
|
||||||
oidc,
|
oidc,
|
||||||
jwt,
|
jwt,
|
||||||
|
@ -33,3 +34,5 @@ module.exports = {
|
||||||
builderOrAdmin,
|
builderOrAdmin,
|
||||||
joiValidator,
|
joiValidator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export = pkg
|
|
@ -13,10 +13,13 @@ function validate(schema, property) {
|
||||||
params = ctx.request[property]
|
params = ctx.request[property]
|
||||||
}
|
}
|
||||||
|
|
||||||
schema = schema.append({
|
// not all schemas have the append property e.g. array schemas
|
||||||
createdAt: Joi.any().optional(),
|
if (schema.append) {
|
||||||
updatedAt: Joi.any().optional(),
|
schema = schema.append({
|
||||||
})
|
createdAt: Joi.any().optional(),
|
||||||
|
updatedAt: Joi.any().optional(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const { error } = schema.validate(params)
|
const { error } = schema.validate(params)
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
|
@ -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.
|
* Gets a connection to the object store using the S3 SDK.
|
||||||
|
@ -66,15 +70,13 @@ const PUBLIC_BUCKETS = [ObjectStoreBuckets.APPS, ObjectStoreBuckets.GLOBAL]
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
export const ObjectStore = (bucket: any) => {
|
export const ObjectStore = (bucket: any) => {
|
||||||
AWS.config.update({
|
|
||||||
accessKeyId: env.MINIO_ACCESS_KEY,
|
|
||||||
secretAccessKey: env.MINIO_SECRET_KEY,
|
|
||||||
region: env.AWS_REGION,
|
|
||||||
})
|
|
||||||
const config: any = {
|
const config: any = {
|
||||||
s3ForcePathStyle: true,
|
s3ForcePathStyle: true,
|
||||||
signatureVersion: "v4",
|
signatureVersion: "v4",
|
||||||
apiVersion: "2006-03-01",
|
apiVersion: "2006-03-01",
|
||||||
|
accessKeyId: env.MINIO_ACCESS_KEY,
|
||||||
|
secretAccessKey: env.MINIO_SECRET_KEY,
|
||||||
|
region: env.AWS_REGION,
|
||||||
}
|
}
|
||||||
if (bucket) {
|
if (bucket) {
|
||||||
config.params = {
|
config.params = {
|
||||||
|
@ -174,6 +176,14 @@ export const streamUpload = async (
|
||||||
const objectStore = ObjectStore(bucketName)
|
const objectStore = ObjectStore(bucketName)
|
||||||
await makeSureBucketExists(objectStore, bucketName)
|
await makeSureBucketExists(objectStore, bucketName)
|
||||||
|
|
||||||
|
// Set content type for certain known extensions
|
||||||
|
if (filename?.endsWith(".js")) {
|
||||||
|
extra = {
|
||||||
|
...extra,
|
||||||
|
ContentType: "application/javascript",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
Bucket: sanitizeBucket(bucketName),
|
Bucket: sanitizeBucket(bucketName),
|
||||||
Key: sanitizeKey(filename),
|
Key: sanitizeKey(filename),
|
||||||
|
|
|
@ -8,6 +8,7 @@ exports.ObjectStoreBuckets = {
|
||||||
TEMPLATES: env.TEMPLATES_BUCKET_NAME,
|
TEMPLATES: env.TEMPLATES_BUCKET_NAME,
|
||||||
GLOBAL: env.GLOBAL_BUCKET_NAME,
|
GLOBAL: env.GLOBAL_BUCKET_NAME,
|
||||||
GLOBAL_CLOUD: env.GLOBAL_CLOUD_BUCKET_NAME,
|
GLOBAL_CLOUD: env.GLOBAL_CLOUD_BUCKET_NAME,
|
||||||
|
PLUGINS: env.PLUGIN_BUCKET_NAME,
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.budibaseTempDir = function () {
|
exports.budibaseTempDir = function () {
|
||||||
|
|
|
@ -3,17 +3,27 @@ const { v4: uuidv4 } = require("uuid")
|
||||||
const { logWarn } = require("../logging")
|
const { logWarn } = require("../logging")
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
|
|
||||||
interface Session {
|
interface CreateSession {
|
||||||
key: string
|
|
||||||
userId: string
|
|
||||||
sessionId: string
|
sessionId: string
|
||||||
lastAccessedAt: string
|
tenantId: string
|
||||||
createdAt: string
|
|
||||||
csrfToken?: string
|
csrfToken?: string
|
||||||
value: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionKey = { key: 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
|
||||||
|
}
|
||||||
|
|
||||||
// a week in seconds
|
// a week in seconds
|
||||||
const EXPIRY_SECONDS = 86400 * 7
|
const EXPIRY_SECONDS = 86400 * 7
|
||||||
|
@ -22,14 +32,14 @@ function makeSessionID(userId: string, sessionId: string) {
|
||||||
return `${userId}/${sessionId}`
|
return `${userId}/${sessionId}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSessionsForUser(userId: string) {
|
export async function getSessionsForUser(userId: string): Promise<Session[]> {
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
console.trace("Cannot get sessions for undefined userId")
|
console.trace("Cannot get sessions for undefined userId")
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
const client = await redis.getSessionClient()
|
const client = await redis.getSessionClient()
|
||||||
const sessions = await client.scan(userId)
|
const sessions: ScannedSession[] = await client.scan(userId)
|
||||||
return sessions.map((session: Session) => session.value)
|
return sessions.map(session => session.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function invalidateSessions(
|
export async function invalidateSessions(
|
||||||
|
@ -39,33 +49,32 @@ export async function invalidateSessions(
|
||||||
try {
|
try {
|
||||||
const reason = opts?.reason || "unknown"
|
const reason = opts?.reason || "unknown"
|
||||||
let sessionIds: string[] = opts.sessionIds || []
|
let sessionIds: string[] = opts.sessionIds || []
|
||||||
let sessions: SessionKey
|
let sessionKeys: SessionKey[]
|
||||||
|
|
||||||
// If no sessionIds, get all the sessions for the user
|
// If no sessionIds, get all the sessions for the user
|
||||||
if (sessionIds.length === 0) {
|
if (sessionIds.length === 0) {
|
||||||
sessions = await getSessionsForUser(userId)
|
const sessions = await getSessionsForUser(userId)
|
||||||
sessions.forEach(
|
sessionKeys = sessions.map(session => ({
|
||||||
(session: any) =>
|
key: makeSessionID(session.userId, session.sessionId),
|
||||||
(session.key = makeSessionID(session.userId, session.sessionId))
|
}))
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
// use the passed array of sessionIds
|
// use the passed array of sessionIds
|
||||||
sessionIds = Array.isArray(sessionIds) ? sessionIds : [sessionIds]
|
sessionIds = Array.isArray(sessionIds) ? sessionIds : [sessionIds]
|
||||||
sessions = sessionIds.map((sessionId: string) => ({
|
sessionKeys = sessionIds.map(sessionId => ({
|
||||||
key: makeSessionID(userId, sessionId),
|
key: makeSessionID(userId, sessionId),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sessions && sessions.length > 0) {
|
if (sessionKeys && sessionKeys.length > 0) {
|
||||||
const client = await redis.getSessionClient()
|
const client = await redis.getSessionClient()
|
||||||
const promises = []
|
const promises = []
|
||||||
for (let session of sessions) {
|
for (let sessionKey of sessionKeys) {
|
||||||
promises.push(client.delete(session.key))
|
promises.push(client.delete(sessionKey.key))
|
||||||
}
|
}
|
||||||
if (!env.isTest()) {
|
if (!env.isTest()) {
|
||||||
logWarn(
|
logWarn(
|
||||||
`Invalidating sessions for ${userId} (reason: ${reason}) - ${sessions
|
`Invalidating sessions for ${userId} (reason: ${reason}) - ${sessionKeys
|
||||||
.map(session => session.key)
|
.map(sessionKey => sessionKey.key)
|
||||||
.join(", ")}`
|
.join(", ")}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -76,22 +85,26 @@ export async function invalidateSessions(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createASession(userId: string, session: Session) {
|
export async function createASession(
|
||||||
|
userId: string,
|
||||||
|
createSession: CreateSession
|
||||||
|
) {
|
||||||
// invalidate all other sessions
|
// invalidate all other sessions
|
||||||
await invalidateSessions(userId, { reason: "creation" })
|
await invalidateSessions(userId, { reason: "creation" })
|
||||||
|
|
||||||
const client = await redis.getSessionClient()
|
const client = await redis.getSessionClient()
|
||||||
const sessionId = session.sessionId
|
const sessionId = createSession.sessionId
|
||||||
if (!session.csrfToken) {
|
const csrfToken = createSession.csrfToken ? createSession.csrfToken : uuidv4()
|
||||||
session.csrfToken = uuidv4()
|
const key = makeSessionID(userId, sessionId)
|
||||||
}
|
|
||||||
session = {
|
const session: Session = {
|
||||||
...session,
|
...createSession,
|
||||||
|
csrfToken,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
lastAccessedAt: new Date().toISOString(),
|
lastAccessedAt: new Date().toISOString(),
|
||||||
userId,
|
userId,
|
||||||
}
|
}
|
||||||
await client.store(makeSessionID(userId, sessionId), session, EXPIRY_SECONDS)
|
await client.store(key, session, EXPIRY_SECONDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateSessionTTL(session: Session) {
|
export async function updateSessionTTL(session: Session) {
|
||||||
|
@ -106,7 +119,10 @@ export async function endSession(userId: string, sessionId: string) {
|
||||||
await client.delete(makeSessionID(userId, sessionId))
|
await client.delete(makeSessionID(userId, sessionId))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSession(userId: string, sessionId: string) {
|
export async function getSession(
|
||||||
|
userId: string,
|
||||||
|
sessionId: string
|
||||||
|
): Promise<Session> {
|
||||||
if (!userId || !sessionId) {
|
if (!userId || !sessionId) {
|
||||||
throw new Error(`Invalid session details - ${userId} - ${sessionId}`)
|
throw new Error(`Invalid session details - ${userId} - ${sessionId}`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ const { UNICODE_MAX } = require("./db/constants")
|
||||||
* Given an email address this will use a view to search through
|
* Given an email address this will use a view to search through
|
||||||
* all the users to find one with this email address.
|
* all the users to find one with this email address.
|
||||||
* @param {string} email the email to lookup the user by.
|
* @param {string} email the email to lookup the user by.
|
||||||
* @return {Promise<object|null>}
|
|
||||||
*/
|
*/
|
||||||
exports.getGlobalUserByEmail = async email => {
|
exports.getGlobalUserByEmail = async email => {
|
||||||
if (email == null) {
|
if (email == null) {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export const getAccount = jest.fn()
|
||||||
|
export const getAccountByTenantId = jest.fn()
|
||||||
|
|
||||||
|
jest.mock("../../../src/cloud/accounts", () => ({
|
||||||
|
getAccount,
|
||||||
|
getAccountByTenantId,
|
||||||
|
}))
|
|
@ -1,2 +0,0 @@
|
||||||
exports.MOCK_DATE = new Date("2020-01-01T00:00:00.000Z")
|
|
||||||
exports.MOCK_DATE_TIMESTAMP = 1577836800000
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const MOCK_DATE = new Date("2020-01-01T00:00:00.000Z")
|
||||||
|
export const MOCK_DATE_TIMESTAMP = 1577836800000
|
|
@ -1,9 +0,0 @@
|
||||||
const posthog = require("./posthog")
|
|
||||||
const events = require("./events")
|
|
||||||
const date = require("./date")
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
posthog,
|
|
||||||
date,
|
|
||||||
events,
|
|
||||||
}
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
import "./posthog"
|
||||||
|
import "./events"
|
||||||
|
export * as accounts from "./accounts"
|
||||||
|
export * as date from "./date"
|
|
@ -543,13 +543,13 @@
|
||||||
semver "^7.3.5"
|
semver "^7.3.5"
|
||||||
tar "^6.1.11"
|
tar "^6.1.11"
|
||||||
|
|
||||||
"@shopify/jest-koa-mocks@3.1.5":
|
"@shopify/jest-koa-mocks@5.0.1":
|
||||||
version "3.1.5"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@shopify/jest-koa-mocks/-/jest-koa-mocks-3.1.5.tgz#11f77ccfbcaf35cf5ee2c6108a286e61e6bea084"
|
resolved "https://registry.yarnpkg.com/@shopify/jest-koa-mocks/-/jest-koa-mocks-5.0.1.tgz#fba490b6b7985fbb571eb9974897d396a3642e94"
|
||||||
integrity sha512-gQ3/7ELerv00TWO37AGFX5mT9CsFCS+3/UbKMuoIlKEU0QH2OX8BV9WBf/EKw7adCDNlxss0lqV6J8kf5pgr4A==
|
integrity sha512-4YskS9q8+TEHNoyopmuoy2XyhInyqeOl7CF5ShJs19sm6m0EA/jGGvgf/osv2PeTfuf42/L2G9CzWUSg49yTSg==
|
||||||
dependencies:
|
dependencies:
|
||||||
koa "^2.13.4"
|
koa "^2.13.4"
|
||||||
node-mocks-http "^1.5.8"
|
node-mocks-http "^1.11.0"
|
||||||
|
|
||||||
"@sideway/address@^4.1.3":
|
"@sideway/address@^4.1.3":
|
||||||
version "4.1.4"
|
version "4.1.4"
|
||||||
|
@ -3914,7 +3914,7 @@ node-int64@^0.4.0:
|
||||||
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
|
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
|
||||||
integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
|
integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
|
||||||
|
|
||||||
node-mocks-http@^1.5.8:
|
node-mocks-http@^1.11.0:
|
||||||
version "1.11.0"
|
version "1.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/node-mocks-http/-/node-mocks-http-1.11.0.tgz#defc0febf6b935f08245397d47534a8de592996e"
|
resolved "https://registry.yarnpkg.com/node-mocks-http/-/node-mocks-http-1.11.0.tgz#defc0febf6b935f08245397d47534a8de592996e"
|
||||||
integrity sha512-jS/WzSOcKbOeGrcgKbenZeNhxUNnP36Yw11+hL4TTxQXErGfqYZ+MaYNNvhaTiGIJlzNSqgQkk9j8dSu1YWSuw==
|
integrity sha512-jS/WzSOcKbOeGrcgKbenZeNhxUNnP36Yw11+hL4TTxQXErGfqYZ+MaYNNvhaTiGIJlzNSqgQkk9j8dSu1YWSuw==
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/bbui",
|
"name": "@budibase/bbui",
|
||||||
"description": "A UI solution used in the different Budibase projects.",
|
"description": "A UI solution used in the different Budibase projects.",
|
||||||
"version": "1.2.44-alpha.2",
|
"version": "1.3.12-alpha.3",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"module": "dist/bbui.es.js",
|
"module": "dist/bbui.es.js",
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
|
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
|
||||||
"@budibase/string-templates": "1.2.44-alpha.2",
|
"@budibase/string-templates": "1.3.12-alpha.3",
|
||||||
"@spectrum-css/actionbutton": "^1.0.1",
|
"@spectrum-css/actionbutton": "^1.0.1",
|
||||||
"@spectrum-css/actiongroup": "^1.0.1",
|
"@spectrum-css/actiongroup": "^1.0.1",
|
||||||
"@spectrum-css/avatar": "^3.0.2",
|
"@spectrum-css/avatar": "^3.0.2",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export default function positionDropdown(element, { anchor, align }) {
|
export default function positionDropdown(element, { anchor, align, maxWidth }) {
|
||||||
let positionSide = "top"
|
let positionSide = "top"
|
||||||
let maxHeight = 0
|
let maxHeight = 0
|
||||||
let dimensions = getDimensions(anchor)
|
let dimensions = getDimensions(anchor)
|
||||||
|
@ -34,13 +34,24 @@ export default function positionDropdown(element, { anchor, align }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcLeftPosition() {
|
function calcLeftPosition() {
|
||||||
return align === "right"
|
let left
|
||||||
? dimensions.left + dimensions.width - dimensions.containerWidth
|
|
||||||
: dimensions.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.position = "absolute"
|
||||||
element.style.zIndex = "9999"
|
element.style.zIndex = "9999"
|
||||||
|
if (maxWidth) {
|
||||||
|
element.style.maxWidth = `${maxWidth}px`
|
||||||
|
}
|
||||||
element.style.minWidth = `${dimensions.width}px`
|
element.style.minWidth = `${dimensions.width}px`
|
||||||
element.style.maxHeight = `${maxHeight.toFixed(0)}px`
|
element.style.maxHeight = `${maxHeight.toFixed(0)}px`
|
||||||
element.style.transformOrigin = `center ${positionSide}`
|
element.style.transformOrigin = `center ${positionSide}`
|
||||||
|
@ -54,10 +65,8 @@ export default function positionDropdown(element, { anchor, align }) {
|
||||||
element.style.left = `${calcLeftPosition(dimensions).toFixed(0)}px`
|
element.style.left = `${calcLeftPosition(dimensions).toFixed(0)}px`
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
resizeObserver.observe(anchor)
|
resizeObserver.observe(anchor)
|
||||||
resizeObserver.observe(element)
|
resizeObserver.observe(element)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
destroy() {
|
destroy() {
|
||||||
resizeObserver.disconnect()
|
resizeObserver.disconnect()
|
||||||
|
|
|
@ -67,6 +67,13 @@
|
||||||
|
|
||||||
// If time only set date component to 2000-01-01
|
// If time only set date component to 2000-01-01
|
||||||
if (timeOnly) {
|
if (timeOnly) {
|
||||||
|
// Classic flackpickr causing issues.
|
||||||
|
// When selecting a value for the first time for a "time only" field,
|
||||||
|
// the time is always offset by 1 hour for some reason (regardless of time
|
||||||
|
// zone) so we need to correct it.
|
||||||
|
if (!value && newValue) {
|
||||||
|
newValue = new Date(dates[0].getTime() + 60 * 60 * 1000).toISOString()
|
||||||
|
}
|
||||||
newValue = `2000-01-01T${newValue.split("T")[1]}`
|
newValue = `2000-01-01T${newValue.split("T")[1]}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,13 @@
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<div class="filename">
|
<div class="filename">
|
||||||
{#if selectedUrl}
|
{#if selectedUrl}
|
||||||
<Link href={selectedUrl}>{selectedImage.name}</Link>
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
download={selectedImage.name}
|
||||||
|
href={selectedUrl}
|
||||||
|
>
|
||||||
|
{selectedImage.name}
|
||||||
|
</Link>
|
||||||
{:else}
|
{:else}
|
||||||
{selectedImage.name}
|
{selectedImage.name}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let getOptionLabel = option => option
|
export let getOptionLabel = option => option
|
||||||
export let getOptionValue = option => option
|
export let getOptionValue = option => option
|
||||||
|
export let getOptionTitle = option => option
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const onChange = e => dispatch("change", e.target.value)
|
const onChange = e => dispatch("change", e.target.value)
|
||||||
|
@ -19,7 +20,7 @@
|
||||||
{#if options && Array.isArray(options)}
|
{#if options && Array.isArray(options)}
|
||||||
{#each options as option}
|
{#each options as option}
|
||||||
<div
|
<div
|
||||||
title={getOptionLabel(option)}
|
title={getOptionTitle(option)}
|
||||||
class="spectrum-Radio spectrum-FieldGroup-item spectrum-Radio--emphasized"
|
class="spectrum-Radio spectrum-FieldGroup-item spectrum-Radio--emphasized"
|
||||||
class:is-invalid={!!error}
|
class:is-invalid={!!error}
|
||||||
>
|
>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
export let direction = "vertical"
|
export let direction = "vertical"
|
||||||
export let getOptionLabel = option => extractProperty(option, "label")
|
export let getOptionLabel = option => extractProperty(option, "label")
|
||||||
export let getOptionValue = option => extractProperty(option, "value")
|
export let getOptionValue = option => extractProperty(option, "value")
|
||||||
|
export let getOptionTitle = option => extractProperty(option, "label")
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const onChange = e => {
|
const onChange = e => {
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
{direction}
|
{direction}
|
||||||
{getOptionLabel}
|
{getOptionLabel}
|
||||||
{getOptionValue}
|
{getOptionValue}
|
||||||
|
{getOptionTitle}
|
||||||
on:change={onChange}
|
on:change={onChange}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
|
@ -8,12 +8,14 @@
|
||||||
export let secondary = false
|
export let secondary = false
|
||||||
export let overBackground = false
|
export let overBackground = false
|
||||||
export let target
|
export let target
|
||||||
|
export let download
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
on:click
|
on:click
|
||||||
{href}
|
{href}
|
||||||
{target}
|
{target}
|
||||||
|
{download}
|
||||||
class:spectrum-Link--primary={primary}
|
class:spectrum-Link--primary={primary}
|
||||||
class:spectrum-Link--secondary={secondary}
|
class:spectrum-Link--secondary={secondary}
|
||||||
class:spectrum-Link--overBackground={overBackground}
|
class:spectrum-Link--overBackground={overBackground}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
export let align = "right"
|
export let align = "right"
|
||||||
export let portalTarget
|
export let portalTarget
|
||||||
export let dataCy
|
export let dataCy
|
||||||
|
export let maxWidth
|
||||||
|
|
||||||
export let direction = "bottom"
|
export let direction = "bottom"
|
||||||
export let showTip = false
|
export let showTip = false
|
||||||
|
@ -45,7 +46,7 @@
|
||||||
<Portal target={portalTarget}>
|
<Portal target={portalTarget}>
|
||||||
<div
|
<div
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
use:positionDropdown={{ anchor, align }}
|
use:positionDropdown={{ anchor, align, maxWidth }}
|
||||||
use:clickOutside={hide}
|
use:clickOutside={hide}
|
||||||
on:keydown={handleEscape}
|
on:keydown={handleEscape}
|
||||||
class={"spectrum-Popover is-open " + (tooltipClasses || "")}
|
class={"spectrum-Popover is-open " + (tooltipClasses || "")}
|
||||||
|
|
|
@ -15,14 +15,24 @@
|
||||||
|
|
||||||
{#each attachments as attachment}
|
{#each attachments as attachment}
|
||||||
{#if isImage(attachment.extension)}
|
{#if isImage(attachment.extension)}
|
||||||
<Link quiet target="_blank" href={attachment.url}>
|
<Link
|
||||||
|
quiet
|
||||||
|
target="_blank"
|
||||||
|
download={attachment.name}
|
||||||
|
href={attachment.url}
|
||||||
|
>
|
||||||
<div class="center" title={attachment.name}>
|
<div class="center" title={attachment.name}>
|
||||||
<img src={attachment.url} alt={attachment.extension} />
|
<img src={attachment.url} alt={attachment.extension} />
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="file" title={attachment.name}>
|
<div class="file" title={attachment.name}>
|
||||||
<Link quiet target="_blank" href={attachment.url}>
|
<Link
|
||||||
|
quiet
|
||||||
|
target="_blank"
|
||||||
|
download={attachment.name}
|
||||||
|
href={attachment.url}
|
||||||
|
>
|
||||||
{attachment.extension}
|
{attachment.extension}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -74,11 +74,11 @@ filterTests(["smoke", "all"], () => {
|
||||||
.contains("Update role")
|
.contains("Update role")
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
})
|
})
|
||||||
cy.reload({ timeout: 5000 })
|
cy.reload()
|
||||||
cy.wait(1000)
|
cy.wait(1000)
|
||||||
}
|
}
|
||||||
// Confirm roles exist within Configure roles table
|
// Confirm roles exist within Configure roles table
|
||||||
cy.get(interact.SPECTRUM_TABLE, { timeout: 2000 })
|
cy.get(interact.SPECTRUM_TABLE, { timeout: 20000 })
|
||||||
.eq(0)
|
.eq(0)
|
||||||
.within(assginedRoles => {
|
.within(assginedRoles => {
|
||||||
expect(assginedRoles).to.contain("Admin")
|
expect(assginedRoles).to.contain("Admin")
|
||||||
|
@ -180,7 +180,7 @@ filterTests(["smoke", "all"], () => {
|
||||||
cy.reload()
|
cy.reload()
|
||||||
|
|
||||||
// Confirm details have been saved
|
// 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.SPECTRUM_TEXTFIELD_INPUT).should('have.value', "bb")
|
||||||
})
|
})
|
||||||
cy.get(interact.FIELD, { timeout: 1000 }).eq(2).within(() => {
|
cy.get(interact.FIELD, { timeout: 1000 }).eq(2).within(() => {
|
||||||
|
|
|
@ -102,7 +102,7 @@ filterTests(['all'], () => {
|
||||||
|
|
||||||
cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 6000 })
|
cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 6000 })
|
||||||
cy.wait(500)
|
cy.wait(500)
|
||||||
cy.get(interact.APP_TABLE_STATUS, { timeout: 1000 }).eq(0).contains("Unpublished")
|
cy.get(interact.APP_TABLE_STATUS, { timeout: 10000 }).eq(0).contains("Unpublished")
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -10,7 +10,7 @@ filterTests(['smoke', 'all'], () => {
|
||||||
|
|
||||||
it("should add a current user binding", () => {
|
it("should add a current user binding", () => {
|
||||||
cy.searchAndAddComponent("Paragraph").then(() => {
|
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"
|
const paramName = "foo"
|
||||||
cy.createScreen(`/test/:${paramName}`)
|
cy.createScreen(`/test/:${paramName}`)
|
||||||
cy.searchAndAddComponent("Paragraph").then(componentId => {
|
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
|
// 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
|
// is check that we were able to bind to the property, and that the
|
||||||
// component exists on the page
|
// 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(`[data-cy="setting-${setting}"] [data-cy=text-binding-button]`).click()
|
||||||
|
cy.get(".category-list li").contains(bindingCategories[0])
|
||||||
cy.get(".drawer").within(() => {
|
cy.get(".drawer").within(() => {
|
||||||
if (clickOption) {
|
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} }}`)
|
cy.get("textarea").should("have.value", `{{ ${bindingText} }}`)
|
||||||
} else {
|
} else {
|
||||||
cy.get("textarea").type(bindingText)
|
cy.get("textarea").type(bindingText)
|
||||||
|
|
|
@ -20,7 +20,7 @@ filterTests(["all"], () => {
|
||||||
//Use the tree to delete a selected component
|
//Use the tree to delete a selected component
|
||||||
const deleteSelectedComponent = () => {
|
const deleteSelectedComponent = () => {
|
||||||
cy.get(
|
cy.get(
|
||||||
".nav-items-container .nav-item.selected .actions > div > .icon"
|
".nav-item.selected .actions > div > .icon"
|
||||||
).click({
|
).click({
|
||||||
force: true,
|
force: true,
|
||||||
})
|
})
|
||||||
|
@ -91,7 +91,7 @@ filterTests(["all"], () => {
|
||||||
cy.searchAndAddComponent("Paragraph").then(componentId => {
|
cy.searchAndAddComponent("Paragraph").then(componentId => {
|
||||||
cy.get("[data-cy=setting-_instanceName] input").type(componentId).blur()
|
cy.get("[data-cy=setting-_instanceName] input").type(componentId).blur()
|
||||||
cy.get(
|
cy.get(
|
||||||
".nav-items-container .nav-item.selected .actions > div > .icon"
|
".nav-item.selected .actions > div > .icon"
|
||||||
).click({
|
).click({
|
||||||
force: true,
|
force: true,
|
||||||
})
|
})
|
||||||
|
@ -145,7 +145,7 @@ filterTests(["all"], () => {
|
||||||
return testFieldFocusOnCreate(label)
|
return testFieldFocusOnCreate(label)
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
cy.get(".nav-items-container .nav-item")
|
cy.get(".nav-item")
|
||||||
.contains(formId)
|
.contains(formId)
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
deleteSelectedComponent()
|
deleteSelectedComponent()
|
||||||
|
@ -195,7 +195,7 @@ filterTests(["all"], () => {
|
||||||
return testFocusOnCreate(label)
|
return testFocusOnCreate(label)
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
cy.get(".nav-items-container .nav-item")
|
cy.get(".nav-item")
|
||||||
.contains(providerId)
|
.contains(providerId)
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
deleteSelectedComponent()
|
deleteSelectedComponent()
|
||||||
|
@ -218,7 +218,7 @@ filterTests(["all"], () => {
|
||||||
.find(".component-placeholder")
|
.find(".component-placeholder")
|
||||||
.should("not.exist")
|
.should("not.exist")
|
||||||
cy.getComponent(imageId).find(`img[alt=${imageId}]`).should("exist")
|
cy.getComponent(imageId).find(`img[alt=${imageId}]`).should("exist")
|
||||||
cy.get(".nav-items-container .nav-item")
|
cy.get(".nav-item")
|
||||||
.contains(imageId)
|
.contains(imageId)
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
deleteSelectedComponent()
|
deleteSelectedComponent()
|
||||||
|
@ -242,7 +242,7 @@ filterTests(["all"], () => {
|
||||||
cy.getComponent(markdownId)
|
cy.getComponent(markdownId)
|
||||||
.find(".editor-preview-full h1")
|
.find(".editor-preview-full h1")
|
||||||
.contains("Hi")
|
.contains("Hi")
|
||||||
cy.get(".nav-items-container .nav-item")
|
cy.get(".nav-item")
|
||||||
.contains(markdownId)
|
.contains(markdownId)
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
deleteSelectedComponent()
|
deleteSelectedComponent()
|
||||||
|
@ -265,7 +265,7 @@ filterTests(["all"], () => {
|
||||||
.find(".component-placeholder")
|
.find(".component-placeholder")
|
||||||
.should("not.exist")
|
.should("not.exist")
|
||||||
cy.getComponent(iconId).find("i.ri-save-fill").should("exist")
|
cy.getComponent(iconId).find("i.ri-save-fill").should("exist")
|
||||||
cy.get(".nav-items-container .nav-item")
|
cy.get(".nav-item")
|
||||||
.contains(iconId)
|
.contains(iconId)
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
deleteSelectedComponent()
|
deleteSelectedComponent()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import filterTests from "../../support/filterTests"
|
import filterTests from "../../support/filterTests"
|
||||||
|
|
||||||
filterTests(['all'], () => {
|
filterTests(['all'], () => {
|
||||||
context("Datasource Wizard", () => {
|
xcontext("Datasource Wizard", () => {
|
||||||
if (Cypress.env("TEST_ENV")) {
|
if (Cypress.env("TEST_ENV")) {
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.login()
|
cy.login()
|
||||||
|
|
|
@ -175,7 +175,10 @@ filterTests(["all"], () => {
|
||||||
cy.get("@query").its("response.statusCode").should("eq", 200)
|
cy.get("@query").its("response.statusCode").should("eq", 200)
|
||||||
cy.get("@query").its("response.body").should("not.be.empty")
|
cy.get("@query").its("response.body").should("not.be.empty")
|
||||||
// Save query
|
// Save query
|
||||||
|
cy.intercept("POST", "**/queries").as("saveQuery")
|
||||||
cy.get(".spectrum-Button").contains("Save Query").click({ force: true })
|
cy.get(".spectrum-Button").contains("Save Query").click({ force: true })
|
||||||
|
cy.wait("@saveQuery")
|
||||||
|
cy.get("@saveQuery").its("response.statusCode").should("eq", 200)
|
||||||
cy.get(".nav-item").should("contain", queryName)
|
cy.get(".nav-item").should("contain", queryName)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import filterTests from "../../support/filterTests"
|
import filterTests from "../../support/filterTests"
|
||||||
|
|
||||||
filterTests(["all"], () => {
|
filterTests(["all"], () => {
|
||||||
context("Oracle Datasource Testing", () => {
|
xcontext("Oracle Datasource Testing", () => {
|
||||||
if (Cypress.env("TEST_ENV")) {
|
if (Cypress.env("TEST_ENV")) {
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.login()
|
cy.login()
|
||||||
|
|
|
@ -162,7 +162,7 @@ filterTests(["all"], () => {
|
||||||
switchSchema("randomText")
|
switchSchema("randomText")
|
||||||
|
|
||||||
// No tables displayed
|
// 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
|
// Previously created query should be visible
|
||||||
cy.get(".spectrum-Table").should("contain", queryName)
|
cy.get(".spectrum-Table").should("contain", queryName)
|
||||||
|
@ -173,7 +173,7 @@ filterTests(["all"], () => {
|
||||||
switchSchema("1")
|
switchSchema("1")
|
||||||
|
|
||||||
// Confirm tables exist - Check for specific one
|
// 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")
|
cy.get(".spectrum-Table")
|
||||||
.eq(0)
|
.eq(0)
|
||||||
.find(".spectrum-Table-row")
|
.find(".spectrum-Table-row")
|
||||||
|
@ -187,7 +187,7 @@ filterTests(["all"], () => {
|
||||||
switchSchema("public")
|
switchSchema("public")
|
||||||
|
|
||||||
// Confirm tables exist - again
|
// 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")
|
cy.get(".spectrum-Table")
|
||||||
.eq(0)
|
.eq(0)
|
||||||
.find(".spectrum-Table-row")
|
.find(".spectrum-Table-row")
|
||||||
|
@ -252,7 +252,8 @@ filterTests(["all"], () => {
|
||||||
.contains("Delete Query")
|
.contains("Delete Query")
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
// Confirm deletion
|
// Confirm deletion
|
||||||
cy.reload({ timeout: 5000 })
|
cy.reload()
|
||||||
|
cy.get(".nav-item", { timeout: 30000 }).contains(datasource).click({ force: true })
|
||||||
cy.get(".nav-item", { timeout: 1000 }).should("not.contain", queryRename)
|
cy.get(".nav-item", { timeout: 1000 }).should("not.contain", queryRename)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ filterTests(["smoke", "all"], () => {
|
||||||
// Select REST data source
|
// Select REST data source
|
||||||
cy.selectExternalDatasource(datasource)
|
cy.selectExternalDatasource(datasource)
|
||||||
// Enter incorrect api & attempt to send query
|
// 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.intercept("**/preview").as("queryError")
|
||||||
cy.get("input").clear().type("random text")
|
cy.get("input").clear().type("random text")
|
||||||
cy.get(".spectrum-Button").contains("Send").click({ force: true })
|
cy.get(".spectrum-Button").contains("Send").click({ force: true })
|
||||||
|
|
|
@ -48,6 +48,7 @@ filterTests(['smoke', 'all'], () => {
|
||||||
cy.get(interact.AREA_LABEL_REVERT).click({ force: true })
|
cy.get(interact.AREA_LABEL_REVERT).click({ force: true })
|
||||||
})
|
})
|
||||||
cy.get(interact.SPECTRUM_DIALOG_GRID).within(() => {
|
cy.get(interact.SPECTRUM_DIALOG_GRID).within(() => {
|
||||||
|
cy.get("input").type("Cypress Tests")
|
||||||
// Click Revert
|
// Click Revert
|
||||||
cy.get(interact.SPECTRUM_BUTTON).contains("Revert").click({ force: true })
|
cy.get(interact.SPECTRUM_BUTTON).contains("Revert").click({ force: true })
|
||||||
cy.wait(2000) // Wait for app to finish reverting
|
cy.wait(2000) // Wait for app to finish reverting
|
||||||
|
|
|
@ -4,7 +4,7 @@ Cypress.on("uncaught:exception", () => {
|
||||||
|
|
||||||
// ACCOUNTS & USERS
|
// ACCOUNTS & USERS
|
||||||
Cypress.Commands.add("login", (email, password) => {
|
Cypress.Commands.add("login", (email, password) => {
|
||||||
cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 10000 })
|
cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 30000 })
|
||||||
cy.url()
|
cy.url()
|
||||||
.should("include", "/builder/")
|
.should("include", "/builder/")
|
||||||
.then(url => {
|
.then(url => {
|
||||||
|
@ -33,7 +33,7 @@ Cypress.Commands.add("login", (email, password) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("logOut", () => {
|
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(".user-dropdown .avatar > .icon").click({ force: true })
|
||||||
cy.get(".spectrum-Popover[data-cy='user-menu']").within(() => {
|
cy.get(".spectrum-Popover[data-cy='user-menu']").within(() => {
|
||||||
cy.get("li[data-cy='user-logout']").click({ force: true })
|
cy.get("li[data-cy='user-logout']").click({ force: true })
|
||||||
|
@ -43,7 +43,7 @@ Cypress.Commands.add("logOut", () => {
|
||||||
|
|
||||||
Cypress.Commands.add("logoutNoAppGrid", () => {
|
Cypress.Commands.add("logoutNoAppGrid", () => {
|
||||||
// Logs user out when app grid is not present
|
// 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(".avatar > .icon").click({ force: true })
|
||||||
cy.get(".spectrum-Popover[data-cy='user-menu']").within(() => {
|
cy.get(".spectrum-Popover[data-cy='user-menu']").within(() => {
|
||||||
cy.get(".spectrum-Menu-item").contains("Log out").click({ force: true })
|
cy.get(".spectrum-Menu-item").contains("Log out").click({ force: true })
|
||||||
|
@ -68,11 +68,14 @@ Cypress.Commands.add("createUser", (email, permission) => {
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Add user and wait for modal to change
|
// Add user
|
||||||
cy.get(".spectrum-Button").contains("Add user").click({ force: true })
|
cy.get(".spectrum-Button").contains("Add users").click({ force: true })
|
||||||
cy.get(".spectrum-ActionButton").contains("Add email").should("not.exist")
|
cy.get(".spectrum-ActionButton").contains("Add email").should("not.exist")
|
||||||
})
|
})
|
||||||
// Onboarding modal
|
// Onboarding modal
|
||||||
|
cy.get(".spectrum-Dialog-grid", { timeout: 5000 }).contains(
|
||||||
|
"Choose your onboarding"
|
||||||
|
)
|
||||||
cy.get(".spectrum-Dialog-grid").within(() => {
|
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||||
cy.get(".onboarding-type").eq(1).click()
|
cy.get(".onboarding-type").eq(1).click()
|
||||||
cy.get(".spectrum-Button").contains("Done").click({ force: true })
|
cy.get(".spectrum-Button").contains("Done").click({ force: true })
|
||||||
|
@ -163,7 +166,7 @@ Cypress.Commands.add("createApp", (name, addDefaultTable) => {
|
||||||
const shouldCreateDefaultTable =
|
const shouldCreateDefaultTable =
|
||||||
typeof addDefaultTable != "boolean" ? true : addDefaultTable
|
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.url({ timeout: 30000 }).should("include", "/apps")
|
||||||
cy.get(`[data-cy="create-app-btn"]`, { timeout: 5000 }).click({ force: true })
|
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 => {
|
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.wait(2000)
|
||||||
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
|
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
|
||||||
.its("body")
|
.its("body")
|
||||||
|
@ -254,7 +257,7 @@ Cypress.Commands.add("deleteApp", name => {
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("deleteAllApps", () => {
|
Cypress.Commands.add("deleteAllApps", () => {
|
||||||
cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 5000 })
|
cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 30000 })
|
||||||
cy.wait(500)
|
cy.wait(500)
|
||||||
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`, {
|
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`, {
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
|
@ -351,7 +354,7 @@ Cypress.Commands.add("alterAppVersion", (appId, version) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("importApp", (exportFilePath, name) => {
|
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`)
|
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
|
||||||
.its("body")
|
.its("body")
|
||||||
|
@ -386,7 +389,7 @@ Cypress.Commands.add("importApp", (exportFilePath, name) => {
|
||||||
|
|
||||||
// Filters visible with 1 or more
|
// Filters visible with 1 or more
|
||||||
Cypress.Commands.add("searchForApplication", appName => {
|
Cypress.Commands.add("searchForApplication", appName => {
|
||||||
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 30000 })
|
||||||
cy.wait(2000)
|
cy.wait(2000)
|
||||||
|
|
||||||
// No app filter functionality if only 1 app exists
|
// No app filter functionality if only 1 app exists
|
||||||
|
@ -409,7 +412,7 @@ Cypress.Commands.add("searchForApplication", appName => {
|
||||||
|
|
||||||
// Assumes there are no others
|
// Assumes there are no others
|
||||||
Cypress.Commands.add("applicationInAppTable", appName => {
|
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(".appTable", { timeout: 5000 }).within(() => {
|
||||||
cy.get(".title").contains(appName).should("exist")
|
cy.get(".title").contains(appName).should("exist")
|
||||||
})
|
})
|
||||||
|
@ -448,17 +451,14 @@ Cypress.Commands.add("createTable", (tableName, initialTable) => {
|
||||||
.contains("Continue")
|
.contains("Continue")
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
})
|
})
|
||||||
cy.get(".spectrum-Modal", { timeout: 10000 }).should(
|
cy.get(".spectrum-Modal").contains("Create Table", { timeout: 10000 })
|
||||||
"not.contain",
|
|
||||||
"Add data source"
|
|
||||||
)
|
|
||||||
cy.get(".spectrum-Modal", { timeout: 2000 }).within(() => {
|
cy.get(".spectrum-Modal", { timeout: 2000 }).within(() => {
|
||||||
cy.get("input", { timeout: 2000 }).first().type(tableName).blur()
|
cy.get("input", { timeout: 2000 }).first().type(tableName).blur()
|
||||||
cy.get(".spectrum-ButtonGroup").contains("Create").click()
|
cy.get(".spectrum-ButtonGroup").contains("Create").click()
|
||||||
})
|
})
|
||||||
// Ensure modal has closed and table is created
|
// Ensure modal has closed and table is created
|
||||||
cy.get(".spectrum-Modal").should("not.exist")
|
cy.get(".spectrum-Modal", { timeout: 2000 }).should("not.exist")
|
||||||
cy.get(".spectrum-Tabs-content", { timeout: 1000 }).should(
|
cy.get(".spectrum-Tabs-content", { timeout: 2000 }).should(
|
||||||
"contain",
|
"contain",
|
||||||
tableName
|
tableName
|
||||||
)
|
)
|
||||||
|
@ -637,30 +637,32 @@ Cypress.Commands.add(
|
||||||
(datasourceNames, accessLevelLabel) => {
|
(datasourceNames, accessLevelLabel) => {
|
||||||
cy.contains("Design").click()
|
cy.contains("Design").click()
|
||||||
cy.get(".spectrum-Button").contains("Add screen").click({ force: true })
|
cy.get(".spectrum-Button").contains("Add screen").click({ force: true })
|
||||||
cy.get(".spectrum-Modal").within(() => {
|
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||||
cy.get(".item").contains("Autogenerated screens").click()
|
cy.get("[data-cy='autogenerated-screens']").click()
|
||||||
|
cy.intercept("**/api/datasources").as("autoScreens")
|
||||||
cy.get(".spectrum-Button").contains("Continue").click({ force: true })
|
cy.get(".spectrum-Button").contains("Continue").click({ force: true })
|
||||||
|
cy.wait("@autoScreens")
|
||||||
|
cy.wait(5000)
|
||||||
})
|
})
|
||||||
cy.get(".spectrum-Modal [data-cy='data-source-modal']", {
|
cy.get("[data-cy='autogenerated-screens']").should("not.exist")
|
||||||
timeout: 500,
|
cy.get("[data-cy='data-source-modal']", { timeout: 10000 }).within(() => {
|
||||||
}).within(() => {
|
|
||||||
for (let i = 0; i < datasourceNames.length; i++) {
|
for (let i = 0; i < datasourceNames.length; i++) {
|
||||||
cy.wait(500)
|
cy.get(".data-source-entry")
|
||||||
cy.get(".data-source-entry").contains(datasourceNames[i]).click()
|
.contains(datasourceNames[i], { timeout: 20000 })
|
||||||
//Ensure the check mark is visible
|
.click({ force: true })
|
||||||
|
// Ensure the check mark is visible
|
||||||
cy.get(".data-source-entry")
|
cy.get(".data-source-entry")
|
||||||
.contains(datasourceNames[i])
|
.contains(datasourceNames[i])
|
||||||
.get(".data-source-check")
|
.get(".data-source-check", { timeout: 20000 })
|
||||||
.should("exist")
|
.should("exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
cy.get(".spectrum-Button").contains("Confirm").click({ force: true })
|
cy.get(".spectrum-Button").contains("Confirm").click({ force: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
cy.get(".spectrum-Modal").within(() => {
|
cy.get(".spectrum-Modal", { timeout: 10000 }).within(() => {
|
||||||
if (accessLevelLabel) {
|
if (accessLevelLabel) {
|
||||||
cy.get(".spectrum-Picker-label").click()
|
cy.get(".spectrum-Picker-label", { timeout: 10000 }).click()
|
||||||
cy.wait(500)
|
|
||||||
cy.contains(accessLevelLabel).click()
|
cy.contains(accessLevelLabel).click()
|
||||||
}
|
}
|
||||||
cy.get(".spectrum-Button").contains("Done").click({ force: true })
|
cy.get(".spectrum-Button").contains("Done").click({ force: true })
|
||||||
|
@ -742,8 +744,15 @@ Cypress.Commands.add("deleteAllScreens", () => {
|
||||||
Cypress.Commands.add("navigateToFrontend", () => {
|
Cypress.Commands.add("navigateToFrontend", () => {
|
||||||
// Clicks on Design tab and then the Home nav item
|
// Clicks on Design tab and then the Home nav item
|
||||||
cy.wait(500)
|
cy.wait(500)
|
||||||
|
cy.intercept("**/preview").as("preview")
|
||||||
cy.contains("Design").click()
|
cy.contains("Design").click()
|
||||||
cy.get(".spectrum-Search", { timeout: 2000 }).type("/")
|
cy.wait("@preview")
|
||||||
|
cy.get("@preview").then(res => {
|
||||||
|
if (res.statusCode != 200) {
|
||||||
|
cy.reload()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
cy.get(".spectrum-Search", { timeout: 20000 }).type("/")
|
||||||
cy.get(".nav-item", { timeout: 2000 }).contains("home").click({ force: true })
|
cy.get(".nav-item", { timeout: 2000 }).contains("home").click({ force: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -908,8 +917,9 @@ Cypress.Commands.add("createRestQuery", (method, restUrl, queryPrettyName) => {
|
||||||
Cypress.Commands.add("closeModal", () => {
|
Cypress.Commands.add("closeModal", () => {
|
||||||
cy.get(".spectrum-Modal", { timeout: 2000 }).within(() => {
|
cy.get(".spectrum-Modal", { timeout: 2000 }).within(() => {
|
||||||
cy.get(".close-icon").click()
|
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", () => {
|
Cypress.Commands.add("expandBudibaseConnection", () => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "1.2.44-alpha.2",
|
"version": "1.3.12-alpha.3",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -69,10 +69,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "1.2.44-alpha.2",
|
"@budibase/bbui": "1.3.12-alpha.3",
|
||||||
"@budibase/client": "1.2.44-alpha.2",
|
"@budibase/client": "1.3.12-alpha.3",
|
||||||
"@budibase/frontend-core": "1.2.44-alpha.2",
|
"@budibase/frontend-core": "1.3.12-alpha.3",
|
||||||
"@budibase/string-templates": "1.2.44-alpha.2",
|
"@budibase/string-templates": "1.3.12-alpha.3",
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
"@spectrum-css/page": "^3.0.1",
|
"@spectrum-css/page": "^3.0.1",
|
||||||
"@spectrum-css/vars": "^3.0.1",
|
"@spectrum-css/vars": "^3.0.1",
|
||||||
|
@ -96,7 +96,7 @@
|
||||||
"@babel/runtime": "^7.13.10",
|
"@babel/runtime": "^7.13.10",
|
||||||
"@rollup/plugin-replace": "^2.4.2",
|
"@rollup/plugin-replace": "^2.4.2",
|
||||||
"@roxi/routify": "2.18.5",
|
"@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/jest-dom": "^5.11.10",
|
||||||
"@testing-library/svelte": "^3.0.0",
|
"@testing-library/svelte": "^3.0.0",
|
||||||
"babel-jest": "^26.6.3",
|
"babel-jest": "^26.6.3",
|
||||||
|
@ -118,7 +118,7 @@
|
||||||
"ts-node": "^10.4.0",
|
"ts-node": "^10.4.0",
|
||||||
"tsconfig-paths": "4.0.0",
|
"tsconfig-paths": "4.0.0",
|
||||||
"typescript": "^4.5.5",
|
"typescript": "^4.5.5",
|
||||||
"vite": "^2.1.5"
|
"vite": "^3.0.8"
|
||||||
},
|
},
|
||||||
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072"
|
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072"
|
||||||
}
|
}
|
||||||
|
|
|
@ -299,7 +299,10 @@ const getProviderContextBindings = (asset, dataProviders) => {
|
||||||
schema = {}
|
schema = {}
|
||||||
const values = context.values || []
|
const values = context.values || []
|
||||||
values.forEach(value => {
|
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") {
|
} else if (context.type === "schema") {
|
||||||
// Schema contexts are generated dynamically depending on their data
|
// Schema contexts are generated dynamically depending on their data
|
||||||
|
@ -359,6 +362,12 @@ const getProviderContextBindings = (asset, dataProviders) => {
|
||||||
providerId,
|
providerId,
|
||||||
// Table ID is used by JSON fields to know what table the field is in
|
// Table ID is used by JSON fields to know what table the field is in
|
||||||
tableId: table?._id,
|
tableId: table?._id,
|
||||||
|
category: component._instanceName,
|
||||||
|
icon: def.icon,
|
||||||
|
display: {
|
||||||
|
name: fieldSchema.name || key,
|
||||||
|
type: fieldSchema.type,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -385,6 +394,9 @@ const getUserBindings = () => {
|
||||||
// datasource options, based on bindable properties
|
// datasource options, based on bindable properties
|
||||||
fieldSchema,
|
fieldSchema,
|
||||||
providerId: "user",
|
providerId: "user",
|
||||||
|
category: "Current User",
|
||||||
|
icon: "User",
|
||||||
|
display: fieldSchema,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return bindings
|
return bindings
|
||||||
|
@ -401,11 +413,17 @@ const getDeviceBindings = () => {
|
||||||
type: "context",
|
type: "context",
|
||||||
runtimeBinding: `${safeDevice}.${makePropSafe("mobile")}`,
|
runtimeBinding: `${safeDevice}.${makePropSafe("mobile")}`,
|
||||||
readableBinding: `Device.Mobile`,
|
readableBinding: `Device.Mobile`,
|
||||||
|
category: "Device",
|
||||||
|
icon: "DevicePhone",
|
||||||
|
display: { type: "boolean", name: "mobile" },
|
||||||
})
|
})
|
||||||
bindings.push({
|
bindings.push({
|
||||||
type: "context",
|
type: "context",
|
||||||
runtimeBinding: `${safeDevice}.${makePropSafe("tablet")}`,
|
runtimeBinding: `${safeDevice}.${makePropSafe("tablet")}`,
|
||||||
readableBinding: `Device.Tablet`,
|
readableBinding: `Device.Tablet`,
|
||||||
|
category: "Device",
|
||||||
|
icon: "DevicePhone",
|
||||||
|
display: { type: "boolean", name: "tablet" },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return bindings
|
return bindings
|
||||||
|
@ -429,6 +447,8 @@ const getSelectedRowsBindings = asset => {
|
||||||
"selectedRows"
|
"selectedRows"
|
||||||
)}`,
|
)}`,
|
||||||
readableBinding: `${table._instanceName}.Selected rows`,
|
readableBinding: `${table._instanceName}.Selected rows`,
|
||||||
|
category: "Selected rows",
|
||||||
|
icon: "ViewRow",
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -460,6 +480,9 @@ const getStateBindings = () => {
|
||||||
type: "context",
|
type: "context",
|
||||||
runtimeBinding: `${safeState}.${makePropSafe(key)}`,
|
runtimeBinding: `${safeState}.${makePropSafe(key)}`,
|
||||||
readableBinding: `State.${key}`,
|
readableBinding: `State.${key}`,
|
||||||
|
category: "State",
|
||||||
|
icon: "AutomatedSegment",
|
||||||
|
display: { name: key },
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
return bindings
|
return bindings
|
||||||
|
@ -482,11 +505,17 @@ const getUrlBindings = asset => {
|
||||||
type: "context",
|
type: "context",
|
||||||
runtimeBinding: `${safeURL}.${makePropSafe(param)}`,
|
runtimeBinding: `${safeURL}.${makePropSafe(param)}`,
|
||||||
readableBinding: `URL.${param}`,
|
readableBinding: `URL.${param}`,
|
||||||
|
category: "URL",
|
||||||
|
icon: "RailTop",
|
||||||
|
display: { type: "string" },
|
||||||
}))
|
}))
|
||||||
const queryParamsBinding = {
|
const queryParamsBinding = {
|
||||||
type: "context",
|
type: "context",
|
||||||
runtimeBinding: makePropSafe("query"),
|
runtimeBinding: makePropSafe("query"),
|
||||||
readableBinding: "Query params",
|
readableBinding: "Query params",
|
||||||
|
category: "URL",
|
||||||
|
icon: "RailTop",
|
||||||
|
display: { type: "object" },
|
||||||
}
|
}
|
||||||
return urlParamBindings.concat([queryParamsBinding])
|
return urlParamBindings.concat([queryParamsBinding])
|
||||||
}
|
}
|
||||||
|
@ -497,6 +526,9 @@ const getRoleBindings = () => {
|
||||||
type: "context",
|
type: "context",
|
||||||
runtimeBinding: `trim "${role._id}"`,
|
runtimeBinding: `trim "${role._id}"`,
|
||||||
readableBinding: `Role.${role.name}`,
|
readableBinding: `Role.${role.name}`,
|
||||||
|
category: "Role",
|
||||||
|
icon: "UserGroup",
|
||||||
|
display: { type: "string", name: role.name },
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -518,6 +550,7 @@ export const getEventContextBindings = (
|
||||||
// Check if any context bindings are provided by the component for this
|
// Check if any context bindings are provided by the component for this
|
||||||
// setting
|
// setting
|
||||||
const component = findComponent(asset.props, componentId)
|
const component = findComponent(asset.props, componentId)
|
||||||
|
const def = store.actions.components.getDefinition(component?._component)
|
||||||
const settings = getComponentSettings(component?._component)
|
const settings = getComponentSettings(component?._component)
|
||||||
const eventSetting = settings.find(setting => setting.key === settingKey)
|
const eventSetting = settings.find(setting => setting.key === settingKey)
|
||||||
if (eventSetting?.context?.length) {
|
if (eventSetting?.context?.length) {
|
||||||
|
@ -527,6 +560,8 @@ export const getEventContextBindings = (
|
||||||
runtimeBinding: `${makePropSafe("eventContext")}.${makePropSafe(
|
runtimeBinding: `${makePropSafe("eventContext")}.${makePropSafe(
|
||||||
contextEntry.key
|
contextEntry.key
|
||||||
)}`,
|
)}`,
|
||||||
|
category: component._instanceName,
|
||||||
|
icon: def.icon,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -548,6 +583,8 @@ export const getEventContextBindings = (
|
||||||
bindings.push({
|
bindings.push({
|
||||||
readableBinding: `Action ${idx + 1}.${contextValue.label}`,
|
readableBinding: `Action ${idx + 1}.${contextValue.label}`,
|
||||||
runtimeBinding: `actions.${idx}.${contextValue.value}`,
|
runtimeBinding: `actions.${idx}.${contextValue.value}`,
|
||||||
|
category: "Actions",
|
||||||
|
icon: "JourneyAction",
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ function prepareData(config) {
|
||||||
ds => ds.source === config.type
|
ds => ds.source === config.type
|
||||||
).length
|
).length
|
||||||
|
|
||||||
let baseName = IntegrationNames[config.type]
|
let baseName = IntegrationNames[config.type] || config.name
|
||||||
let name =
|
let name =
|
||||||
existingTypeCount === 0 ? baseName : `${baseName}-${existingTypeCount + 1}`
|
existingTypeCount === 0 ? baseName : `${baseName}-${existingTypeCount + 1}`
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ import {
|
||||||
makeComponentUnique,
|
makeComponentUnique,
|
||||||
} from "../componentUtils"
|
} from "../componentUtils"
|
||||||
import { Helpers } from "@budibase/bbui"
|
import { Helpers } from "@budibase/bbui"
|
||||||
import { DefaultAppTheme, LAYOUT_NAMES } from "../../constants"
|
|
||||||
import { Utils } from "@budibase/frontend-core"
|
import { Utils } from "@budibase/frontend-core"
|
||||||
|
|
||||||
const INITIAL_FRONTEND_STATE = {
|
const INITIAL_FRONTEND_STATE = {
|
||||||
|
@ -91,13 +90,21 @@ export const getFrontendStore = () => {
|
||||||
|
|
||||||
// Fetch component definitions.
|
// Fetch component definitions.
|
||||||
// Allow errors to propagate.
|
// 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
|
// Reset store state
|
||||||
store.update(state => ({
|
store.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
libraries: application.componentLibraries,
|
libraries: application.componentLibraries,
|
||||||
components,
|
components,
|
||||||
|
customComponents,
|
||||||
clientFeatures: {
|
clientFeatures: {
|
||||||
...INITIAL_FRONTEND_STATE.clientFeatures,
|
...INITIAL_FRONTEND_STATE.clientFeatures,
|
||||||
...components.features,
|
...components.features,
|
||||||
|
@ -117,6 +124,7 @@ export const getFrontendStore = () => {
|
||||||
version: application.version,
|
version: application.version,
|
||||||
revertableVersion: application.revertableVersion,
|
revertableVersion: application.revertableVersion,
|
||||||
navigation: application.navigation || {},
|
navigation: application.navigation || {},
|
||||||
|
usedPlugins: application.usedPlugins || [],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Initialise backend stores
|
// Initialise backend stores
|
||||||
|
@ -125,35 +133,6 @@ export const getFrontendStore = () => {
|
||||||
await integrations.init()
|
await integrations.init()
|
||||||
await queries.init()
|
await queries.init()
|
||||||
await tables.init()
|
await tables.init()
|
||||||
|
|
||||||
// Add navigation settings to old apps
|
|
||||||
if (!application.navigation) {
|
|
||||||
const layout = layouts.find(x => x._id === LAYOUT_NAMES.MASTER.PRIVATE)
|
|
||||||
const customTheme = application.customTheme
|
|
||||||
let navigationSettings = {
|
|
||||||
navigation: "Top",
|
|
||||||
title: application.name,
|
|
||||||
navWidth: "Large",
|
|
||||||
navBackground:
|
|
||||||
customTheme?.navBackground || DefaultAppTheme.navBackground,
|
|
||||||
navTextColor:
|
|
||||||
customTheme?.navTextColor || DefaultAppTheme.navTextColor,
|
|
||||||
}
|
|
||||||
if (layout) {
|
|
||||||
navigationSettings.hideLogo = layout.props.hideLogo
|
|
||||||
navigationSettings.hideTitle = layout.props.hideTitle
|
|
||||||
navigationSettings.title = layout.props.title || application.name
|
|
||||||
navigationSettings.logoUrl = layout.props.logoUrl
|
|
||||||
navigationSettings.links = layout.props.links
|
|
||||||
navigationSettings.navigation = layout.props.navigation || "Top"
|
|
||||||
navigationSettings.sticky = layout.props.sticky
|
|
||||||
navigationSettings.navWidth = layout.props.width || "Large"
|
|
||||||
if (navigationSettings.navigation === "None") {
|
|
||||||
navigationSettings.navigation = "Top"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await store.actions.navigation.save(navigationSettings)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
save: async theme => {
|
save: async theme => {
|
||||||
|
@ -219,9 +198,18 @@ export const getFrontendStore = () => {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
save: async screen => {
|
save: async screen => {
|
||||||
|
const state = get(store)
|
||||||
const creatingNewScreen = screen._id === undefined
|
const creatingNewScreen = screen._id === undefined
|
||||||
const savedScreen = await API.saveScreen(screen)
|
const savedScreen = await API.saveScreen(screen)
|
||||||
const routesResponse = await API.fetchAppRoutes()
|
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 => {
|
store.update(state => {
|
||||||
// Update screen object
|
// Update screen object
|
||||||
const idx = state.screens.findIndex(x => x._id === savedScreen._id)
|
const idx = state.screens.findIndex(x => x._id === savedScreen._id)
|
||||||
|
@ -240,6 +228,9 @@ export const getFrontendStore = () => {
|
||||||
// Update routes
|
// Update routes
|
||||||
state.routes = routesResponse.routes
|
state.routes = routesResponse.routes
|
||||||
|
|
||||||
|
// Update used plugins
|
||||||
|
state.usedPlugins = usedPlugins
|
||||||
|
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
return savedScreen
|
return savedScreen
|
||||||
|
@ -398,9 +389,6 @@ export const getFrontendStore = () => {
|
||||||
if (!componentName) {
|
if (!componentName) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
if (!componentName.startsWith("@budibase")) {
|
|
||||||
componentName = `@budibase/standard-components/${componentName}`
|
|
||||||
}
|
|
||||||
return get(store).components[componentName]
|
return get(store).components[componentName]
|
||||||
},
|
},
|
||||||
createInstance: (componentName, presetProps) => {
|
createInstance: (componentName, presetProps) => {
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="automations-list">
|
<div class="automations-list">
|
||||||
{#each $automationStore.automations as automation, idx}
|
{#each $automationStore.automations.sort(aut => aut.name) as automation, idx}
|
||||||
<NavItem
|
<NavItem
|
||||||
border={idx > 0}
|
border={idx > 0}
|
||||||
icon="ShareAndroid"
|
icon="ShareAndroid"
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
$: {
|
$: {
|
||||||
let fields = {}
|
let fields = {}
|
||||||
|
|
||||||
for (const [key, type] of Object.entries(block?.inputs?.fields)) {
|
for (const [key, type] of Object.entries(block?.inputs?.fields ?? {})) {
|
||||||
fields = {
|
fields = {
|
||||||
...fields,
|
...fields,
|
||||||
[key]: {
|
[key]: {
|
||||||
|
|
|
@ -467,6 +467,7 @@
|
||||||
options={relationshipOptions}
|
options={relationshipOptions}
|
||||||
getOptionLabel={option => option.name}
|
getOptionLabel={option => option.name}
|
||||||
getOptionValue={option => option.value}
|
getOptionValue={option => option.value}
|
||||||
|
getOptionTitle={option => option.alt}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<Input
|
<Input
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
customQueryIconColor,
|
customQueryIconColor,
|
||||||
customQueryText,
|
customQueryText,
|
||||||
} from "helpers/data/utils"
|
} from "helpers/data/utils"
|
||||||
import ICONS from "./icons"
|
import { getIcon } from "./icons"
|
||||||
import { notifications } from "@budibase/bbui"
|
import { notifications } from "@budibase/bbui"
|
||||||
|
|
||||||
let openDataSources = []
|
let openDataSources = []
|
||||||
|
@ -124,7 +124,7 @@
|
||||||
>
|
>
|
||||||
<div class="datasource-icon" slot="icon">
|
<div class="datasource-icon" slot="icon">
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={ICONS[datasource.source]}
|
this={getIcon(datasource.source, datasource.schema)}
|
||||||
height="18"
|
height="18"
|
||||||
width="18"
|
width="18"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
<script>
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
import { Heading, Detail } from "@budibase/bbui"
|
||||||
|
import { getIcon } from "../icons"
|
||||||
|
|
||||||
|
export let integration
|
||||||
|
export let integrationType
|
||||||
|
export let schema
|
||||||
|
|
||||||
|
let dispatcher = createEventDispatcher()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class:selected={integration.type === integrationType}
|
||||||
|
on:click={() => dispatcher("selected", integrationType)}
|
||||||
|
class="item hoverable"
|
||||||
|
>
|
||||||
|
<div class="item-body" class:with-type={!!schema.type}>
|
||||||
|
<svelte:component
|
||||||
|
this={getIcon(integrationType, schema)}
|
||||||
|
height="20"
|
||||||
|
width="20"
|
||||||
|
/>
|
||||||
|
<div class="text">
|
||||||
|
<Heading size="XXS">{schema.friendlyName}</Heading>
|
||||||
|
{#if schema.type}
|
||||||
|
<Detail size="S">{schema.type || ""}</Detail>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.item {
|
||||||
|
cursor: pointer;
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--spectrum-alias-grid-margin-xsmall);
|
||||||
|
padding: var(--spectrum-alias-item-padding-s)
|
||||||
|
var(--spectrum-alias-item-padding-m);
|
||||||
|
background: var(--spectrum-alias-background-color-secondary);
|
||||||
|
transition: background 0.13s ease-out;
|
||||||
|
border: solid var(--spectrum-alias-border-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-width: 2px;
|
||||||
|
}
|
||||||
|
.item:hover,
|
||||||
|
.item.selected {
|
||||||
|
background: var(--spectrum-alias-background-color-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.item-body.with-type {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.item-body.with-type :global(svg) {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,52 @@
|
||||||
|
<script>
|
||||||
|
export let width = "100"
|
||||||
|
export let height = "100"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
{width}
|
||||||
|
{height}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 18.43 17.62"
|
||||||
|
xml:space="preserve"
|
||||||
|
>
|
||||||
|
<style type="text/css">
|
||||||
|
.custom-st0 {
|
||||||
|
fill: var(
|
||||||
|
--spectrum-heading-xxs-text-color,
|
||||||
|
var(--spectrum-alias-heading-text-color)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<ellipse
|
||||||
|
id="Ellipse_9514"
|
||||||
|
class="custom-st0"
|
||||||
|
cx="9.14"
|
||||||
|
cy="2.88"
|
||||||
|
rx="8"
|
||||||
|
ry="2.5"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
id="Path_8355"
|
||||||
|
class="custom-st0"
|
||||||
|
d="M7.64,12.88c0-0.48,0.06-0.95,0.17-1.41c-2.53-0.16-5.92-0.76-6.67-1.95v4.36
|
||||||
|
c0,1.34,3.39,2.43,7.63,2.49C8.03,15.36,7.63,14.13,7.64,12.88z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
id="Path_8356"
|
||||||
|
class="custom-st0"
|
||||||
|
d="M13.64,6.88c1.25,0,2.47,0.39,3.48,1.12c0.01-0.04,0.02-0.08,0.02-0.12V4.51
|
||||||
|
c-1.22,1.55-5.53,2-8,2s-7.11-0.58-8-2v3.36c0,1.28,3.09,2.33,7.06,2.48C9.18,8.24,11.3,6.88,13.64,6.88z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
id="Path_8353"
|
||||||
|
class="custom-st0"
|
||||||
|
d="M10.32,14.42c-0.56-0.56-0.56-1.47,0-2.03l1.24-1.24l-0.01-0.01c-0.25-0.25-0.25-0.65,0-0.9l0,0
|
||||||
|
L12,9.79c0.25-0.25,0.65-0.25,0.9,0c0,0,0,0,0,0l0,0l0.45,0.45l1.53-1.53c0.09-0.09,0.24-0.09,0.34,0l0.34,0.34
|
||||||
|
c0.09,0.09,0.09,0.24,0,0.34l-1.53,1.53l1.35,1.35l1.53-1.53c0.09-0.09,0.24-0.09,0.34,0l0.34,0.34c0.09,0.09,0.09,0.24,0,0.34
|
||||||
|
l-1.53,1.53l0.45,0.45c0.25,0.25,0.25,0.65,0,0.9l0,0l-0.45,0.45c-0.25,0.25-0.65,0.25-0.9,0l0,0l-0.01-0.01l-1.24,1.24
|
||||||
|
c-0.56,0.56-1.47,0.56-2.03,0l0,0L10.32,14.42z"
|
||||||
|
/>
|
||||||
|
</svg>
|
|
@ -15,8 +15,9 @@ import GoogleSheets from "./GoogleSheets.svelte"
|
||||||
import Firebase from "./Firebase.svelte"
|
import Firebase from "./Firebase.svelte"
|
||||||
import Redis from "./Redis.svelte"
|
import Redis from "./Redis.svelte"
|
||||||
import Snowflake from "./Snowflake.svelte"
|
import Snowflake from "./Snowflake.svelte"
|
||||||
|
import Custom from "./Custom.svelte"
|
||||||
|
|
||||||
export default {
|
const ICONS = {
|
||||||
BUDIBASE: Budibase,
|
BUDIBASE: Budibase,
|
||||||
POSTGRES: Postgres,
|
POSTGRES: Postgres,
|
||||||
DYNAMODB: DynamoDB,
|
DYNAMODB: DynamoDB,
|
||||||
|
@ -34,4 +35,15 @@ export default {
|
||||||
FIRESTORE: Firebase,
|
FIRESTORE: Firebase,
|
||||||
REDIS: Redis,
|
REDIS: Redis,
|
||||||
SNOWFLAKE: Snowflake,
|
SNOWFLAKE: Snowflake,
|
||||||
|
CUSTOM: Custom,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ICONS
|
||||||
|
|
||||||
|
export function getIcon(integrationType, schema) {
|
||||||
|
if (schema?.custom || !ICONS[integrationType]) {
|
||||||
|
return ICONS.CUSTOM
|
||||||
|
} else {
|
||||||
|
return ICONS[integrationType]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
import { createRestDatasource } from "builderStore/datasource"
|
import { createRestDatasource } from "builderStore/datasource"
|
||||||
import { goto } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
import ImportRestQueriesModal from "./ImportRestQueriesModal.svelte"
|
import ImportRestQueriesModal from "./ImportRestQueriesModal.svelte"
|
||||||
|
import DatasourceCard from "../_components/DatasourceCard.svelte"
|
||||||
|
|
||||||
export let modal
|
export let modal
|
||||||
let integrations = {}
|
let integrations = {}
|
||||||
|
@ -27,6 +28,9 @@
|
||||||
let importModal
|
let importModal
|
||||||
|
|
||||||
$: showImportButton = false
|
$: showImportButton = false
|
||||||
|
$: customIntegrations = Object.entries(integrations).filter(
|
||||||
|
entry => entry[1].custom
|
||||||
|
)
|
||||||
|
|
||||||
checkShowImport()
|
checkShowImport()
|
||||||
|
|
||||||
|
@ -49,6 +53,9 @@
|
||||||
schema: selected.datasource,
|
schema: selected.datasource,
|
||||||
auth: selected.auth,
|
auth: selected.auth,
|
||||||
}
|
}
|
||||||
|
if (selected.friendlyName) {
|
||||||
|
integration.name = selected.friendlyName
|
||||||
|
}
|
||||||
checkShowImport()
|
checkShowImport()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,36 +157,39 @@
|
||||||
<Layout noPadding gap="XS">
|
<Layout noPadding gap="XS">
|
||||||
<Body size="S">Connect to an external data source</Body>
|
<Body size="S">Connect to an external data source</Body>
|
||||||
<div class="item-list">
|
<div class="item-list">
|
||||||
{#each Object.entries(integrations).filter(([key]) => key !== IntegrationTypes.INTERNAL) as [integrationType, schema]}
|
{#each Object.entries(integrations).filter(([key, val]) => key !== IntegrationTypes.INTERNAL && !val.custom) as [integrationType, schema]}
|
||||||
<div
|
<DatasourceCard
|
||||||
class:selected={integration.type === integrationType}
|
on:selected={evt => selectIntegration(evt.detail)}
|
||||||
on:click={() => selectIntegration(integrationType)}
|
{schema}
|
||||||
class="item hoverable"
|
bind:integrationType
|
||||||
>
|
{integration}
|
||||||
<div class="item-body" class:with-type={!!schema.type}>
|
/>
|
||||||
<svelte:component
|
|
||||||
this={ICONS[integrationType]}
|
|
||||||
height="20"
|
|
||||||
width="20"
|
|
||||||
/>
|
|
||||||
<div class="text">
|
|
||||||
<Heading size="XXS">{schema.friendlyName}</Heading>
|
|
||||||
{#if schema.type}
|
|
||||||
<Detail size="S">{schema.type || ""}</Detail>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
|
{#if customIntegrations.length > 0}
|
||||||
|
<Layout noPadding gap="XS">
|
||||||
|
<Body size="S">Custom data source</Body>
|
||||||
|
<div class="item-list">
|
||||||
|
{#each customIntegrations as [integrationType, schema]}
|
||||||
|
<DatasourceCard
|
||||||
|
on:selected={evt => selectIntegration(evt.detail)}
|
||||||
|
{schema}
|
||||||
|
bind:integrationType
|
||||||
|
{integration}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
{/if}
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.item-list {
|
.item-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
grid-template-columns: repeat(2, minmax(150px, 1fr));
|
||||||
grid-gap: var(--spectrum-alias-grid-baseline);
|
grid-gap: var(--spectrum-alias-grid-baseline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,14 @@
|
||||||
let datasource = cloneDeep(integration)
|
let datasource = cloneDeep(integration)
|
||||||
let skipFetch = false
|
let skipFetch = false
|
||||||
|
|
||||||
|
$: name =
|
||||||
|
IntegrationNames[datasource.type] || datasource.name || datasource.type
|
||||||
|
|
||||||
async function saveDatasource() {
|
async function saveDatasource() {
|
||||||
try {
|
try {
|
||||||
|
if (!datasource.name) {
|
||||||
|
datasource.name = name
|
||||||
|
}
|
||||||
const resp = await save(datasource, skipFetch)
|
const resp = await save(datasource, skipFetch)
|
||||||
$goto(`./datasource/${resp._id}`)
|
$goto(`./datasource/${resp._id}`)
|
||||||
notifications.success(`Datasource updated successfully.`)
|
notifications.success(`Datasource updated successfully.`)
|
||||||
|
@ -32,7 +38,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title={`Connect to ${IntegrationNames[datasource.type]}`}
|
title={`Connect to ${name}`}
|
||||||
onConfirm={() => saveDatasource()}
|
onConfirm={() => saveDatasource()}
|
||||||
onCancel={() => modal.show()}
|
onCancel={() => modal.show()}
|
||||||
confirmText={datasource.plus
|
confirmText={datasource.plus
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
export let scrollable = false
|
export let scrollable = false
|
||||||
export let highlighted = false
|
export let highlighted = false
|
||||||
export let rightAlignIcon = false
|
export let rightAlignIcon = false
|
||||||
|
export let id
|
||||||
|
|
||||||
const scrollApi = getContext("scroll")
|
const scrollApi = getContext("scroll")
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
@ -58,6 +59,7 @@
|
||||||
on:click={onClick}
|
on:click={onClick}
|
||||||
ondragover="return false"
|
ondragover="return false"
|
||||||
ondragenter="return false"
|
ondragenter="return false"
|
||||||
|
{id}
|
||||||
>
|
>
|
||||||
<div class="nav-item-content" bind:this={contentRef}>
|
<div class="nav-item-content" bind:this={contentRef}>
|
||||||
{#if withArrow}
|
{#if withArrow}
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
Body,
|
Body,
|
||||||
Layout,
|
Layout,
|
||||||
Button,
|
Button,
|
||||||
|
ActionButton,
|
||||||
|
Icon,
|
||||||
|
Popover,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { createEventDispatcher, onMount } from "svelte"
|
import { createEventDispatcher, onMount } from "svelte"
|
||||||
import {
|
import {
|
||||||
|
@ -45,9 +48,25 @@
|
||||||
let jsValue = initialValueJS ? value : null
|
let jsValue = initialValueJS ? value : null
|
||||||
let hbsValue = initialValueJS ? null : value
|
let hbsValue = initialValueJS ? null : value
|
||||||
|
|
||||||
|
let selectedCategory = null
|
||||||
|
|
||||||
|
let popover
|
||||||
|
let popoverAnchor
|
||||||
|
let hoverTarget
|
||||||
|
|
||||||
$: usingJS = mode === "JavaScript"
|
$: usingJS = mode === "JavaScript"
|
||||||
$: searchRgx = new RegExp(search, "ig")
|
$: searchRgx = new RegExp(search, "ig")
|
||||||
$: categories = Object.entries(groupBy("category", bindings))
|
$: categories = Object.entries(groupBy("category", bindings))
|
||||||
|
|
||||||
|
$: bindingIcons = bindings?.reduce((acc, ele) => {
|
||||||
|
if (ele.icon) {
|
||||||
|
acc[ele.category] = acc[ele.category] || ele.icon
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
$: categoryIcons = { ...bindingIcons, Helpers: "MagicWand" }
|
||||||
|
|
||||||
$: filteredCategories = categories
|
$: filteredCategories = categories
|
||||||
.map(([name, categoryBindings]) => ({
|
.map(([name, categoryBindings]) => ({
|
||||||
name,
|
name,
|
||||||
|
@ -55,10 +74,19 @@
|
||||||
return binding.readableBinding.match(searchRgx)
|
return binding.readableBinding.match(searchRgx)
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
.filter(category => category.bindings?.length > 0)
|
.filter(category => {
|
||||||
|
return (
|
||||||
|
category.bindings?.length > 0 &&
|
||||||
|
(!selectedCategory ? true : selectedCategory === category.name)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
$: filteredHelpers = helpers?.filter(helper => {
|
$: filteredHelpers = helpers?.filter(helper => {
|
||||||
return helper.label.match(searchRgx) || helper.description.match(searchRgx)
|
return helper.label.match(searchRgx) || helper.description.match(searchRgx)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$: categoryNames = [...categories.map(cat => cat[0]), "Helpers"]
|
||||||
|
|
||||||
$: codeMirrorHints = bindings?.map(x => `$("${x.readableBinding}")`)
|
$: codeMirrorHints = bindings?.map(x => `$("${x.readableBinding}")`)
|
||||||
|
|
||||||
const updateValue = val => {
|
const updateValue = val => {
|
||||||
|
@ -140,58 +168,163 @@
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<span class="detailPopover">
|
||||||
|
<Popover
|
||||||
|
align="right-side"
|
||||||
|
bind:this={popover}
|
||||||
|
anchor={popoverAnchor}
|
||||||
|
maxWidth={300}
|
||||||
|
>
|
||||||
|
<Layout gap="S">
|
||||||
|
<div class="helper">
|
||||||
|
{#if hoverTarget.title}
|
||||||
|
<div class="helper__name">{hoverTarget.title}</div>
|
||||||
|
{/if}
|
||||||
|
{#if hoverTarget.description}
|
||||||
|
<div class="helper__description">
|
||||||
|
{@html hoverTarget.description}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if hoverTarget.example}
|
||||||
|
<pre class="helper__example">{hoverTarget.example}</pre>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
</Popover>
|
||||||
|
</span>
|
||||||
|
|
||||||
<DrawerContent>
|
<DrawerContent>
|
||||||
<svelte:fragment slot="sidebar">
|
<svelte:fragment slot="sidebar">
|
||||||
<div class="container">
|
<Layout noPadding gap="S">
|
||||||
<section>
|
{#if selectedCategory}
|
||||||
|
<div>
|
||||||
|
<ActionButton
|
||||||
|
secondary
|
||||||
|
icon={"ArrowLeft"}
|
||||||
|
on:click={() => {
|
||||||
|
selectedCategory = null
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</ActionButton>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if !selectedCategory}
|
||||||
<div class="heading">Search</div>
|
<div class="heading">Search</div>
|
||||||
<Search placeholder="Search" bind:value={search} />
|
<Search placeholder="Search" bind:value={search} />
|
||||||
</section>
|
{/if}
|
||||||
{#each filteredCategories as category}
|
|
||||||
{#if category.bindings?.length}
|
{#if !selectedCategory && !search}
|
||||||
<section>
|
<ul class="category-list">
|
||||||
<div class="heading">{category.name}</div>
|
{#each categoryNames as categoryName}
|
||||||
|
<li
|
||||||
|
on:click={() => {
|
||||||
|
selectedCategory = categoryName
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name={categoryIcons[categoryName]} />
|
||||||
|
<span class="category-name">{categoryName} </span>
|
||||||
|
<span class="category-chevron"><Icon name="ChevronRight" /></span>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if selectedCategory || search}
|
||||||
|
{#each filteredCategories as category}
|
||||||
|
{#if category.bindings?.length}
|
||||||
|
<div class="cat-heading">
|
||||||
|
<Icon name={categoryIcons[category.name]} />{category.name}
|
||||||
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
{#each category.bindings as binding}
|
{#each category.bindings as binding}
|
||||||
<li on:click={() => addBinding(binding)}>
|
<li
|
||||||
<span class="binding__label">{binding.readableBinding}</span>
|
class="binding"
|
||||||
{#if binding.type}
|
on:mouseenter={e => {
|
||||||
<span class="binding__type">{binding.type}</span>
|
popoverAnchor = e.target
|
||||||
{/if}
|
if (!binding.description) {
|
||||||
{#if binding.description}
|
return
|
||||||
<br />
|
}
|
||||||
<div class="binding__description">
|
hoverTarget = {
|
||||||
{binding.description || ""}
|
title: binding.display.name || binding.fieldSchema.name,
|
||||||
</div>
|
description: binding.description,
|
||||||
|
}
|
||||||
|
popover.show()
|
||||||
|
e.stopPropagation()
|
||||||
|
}}
|
||||||
|
on:mouseleave={() => {
|
||||||
|
popover.hide()
|
||||||
|
popoverAnchor = null
|
||||||
|
hoverTarget = null
|
||||||
|
}}
|
||||||
|
on:focus={() => {}}
|
||||||
|
on:blur={() => {}}
|
||||||
|
on:click={() => addBinding(binding)}
|
||||||
|
>
|
||||||
|
<span class="binding__label">
|
||||||
|
{#if binding.display?.name}
|
||||||
|
{binding.display.name}
|
||||||
|
{:else if binding.fieldSchema?.name}
|
||||||
|
{binding.fieldSchema?.name}
|
||||||
|
{:else}
|
||||||
|
{binding.readableBinding}
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{#if binding.display?.type || binding.fieldSchema?.type}
|
||||||
|
<span class="binding__typeWrap">
|
||||||
|
<span class="binding__type">
|
||||||
|
{binding.display?.type || binding.fieldSchema?.type}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
{/if}
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
{#if selectedCategory === "Helpers" || search}
|
||||||
|
{#if filteredHelpers?.length}
|
||||||
|
<div class="heading">Helpers</div>
|
||||||
|
<ul class="helpers">
|
||||||
|
{#each filteredHelpers as helper}
|
||||||
|
<li
|
||||||
|
class="binding"
|
||||||
|
on:click={() => addHelper(helper, usingJS)}
|
||||||
|
on:mouseenter={e => {
|
||||||
|
popoverAnchor = e.target
|
||||||
|
if (!helper.displayText && helper.description) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hoverTarget = {
|
||||||
|
title: helper.displayText,
|
||||||
|
description: helper.description,
|
||||||
|
example: getHelperExample(helper, usingJS),
|
||||||
|
}
|
||||||
|
popover.show()
|
||||||
|
e.stopPropagation()
|
||||||
|
}}
|
||||||
|
on:mouseleave={() => {
|
||||||
|
popover.hide()
|
||||||
|
popoverAnchor = null
|
||||||
|
hoverTarget = null
|
||||||
|
}}
|
||||||
|
on:focus={() => {}}
|
||||||
|
on:blur={() => {}}
|
||||||
|
>
|
||||||
|
<span class="binding__label">{helper.displayText}</span>
|
||||||
|
<span class="binding__typeWrap">
|
||||||
|
<span class="binding__type">function</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
|
||||||
{#if filteredHelpers?.length}
|
|
||||||
<section>
|
|
||||||
<div class="heading">Helpers</div>
|
|
||||||
<ul>
|
|
||||||
{#each filteredHelpers as helper}
|
|
||||||
<li on:click={() => addHelper(helper, usingJS)}>
|
|
||||||
<div class="helper">
|
|
||||||
<div class="helper__name">{helper.displayText}</div>
|
|
||||||
<div class="helper__description">
|
|
||||||
{@html helper.description}
|
|
||||||
</div>
|
|
||||||
<pre class="helper__example">{getHelperExample(
|
|
||||||
helper,
|
|
||||||
usingJS
|
|
||||||
)}</pre>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</Layout>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<Tabs selected={mode} on:select={onChangeMode}>
|
<Tabs selected={mode} on:select={onChangeMode}>
|
||||||
|
@ -241,6 +374,35 @@
|
||||||
</DrawerContent>
|
</DrawerContent>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
ul.helpers li * {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
ul.category-list li {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
ul.category-list .category-name {
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
ul.category-list .category-chevron {
|
||||||
|
flex: 1;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
ul.category-list .category-chevron :global(div.icon),
|
||||||
|
.cat-heading :global(div.icon) {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
li.binding {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
li.binding .binding__typeWrap {
|
||||||
|
flex: 1;
|
||||||
|
text-align: right;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
.main :global(textarea) {
|
.main :global(textarea) {
|
||||||
min-height: 202px !important;
|
min-height: 202px !important;
|
||||||
}
|
}
|
||||||
|
@ -251,23 +413,20 @@
|
||||||
padding: var(--spacing-s) var(--spacing-xl);
|
padding: var(--spacing-s) var(--spacing-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.heading,
|
||||||
margin: calc(-1 * var(--spacing-xl));
|
.cat-heading {
|
||||||
}
|
|
||||||
.heading {
|
|
||||||
font-size: var(--font-size-s);
|
font-size: var(--font-size-s);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--spectrum-global-color-gray-600);
|
color: var(--spectrum-global-color-gray-600);
|
||||||
padding: var(--spacing-xl) 0 var(--spacing-m) 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
section {
|
.cat-heading {
|
||||||
padding: 0 var(--spacing-xl) var(--spacing-xl) var(--spacing-xl);
|
display: flex;
|
||||||
}
|
gap: var(--spacing-m);
|
||||||
section:not(:first-child) {
|
align-items: center;
|
||||||
border-top: var(--border-light);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -278,7 +437,7 @@
|
||||||
font-size: var(--font-size-s);
|
font-size: var(--font-size-s);
|
||||||
padding: var(--spacing-m);
|
padding: var(--spacing-m);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: var(--border-light);
|
background-color: var(--spectrum-global-color-gray-200);
|
||||||
transition: background-color 130ms ease-in-out, color 130ms ease-in-out,
|
transition: background-color 130ms ease-in-out, color 130ms ease-in-out,
|
||||||
border-color 130ms ease-in-out;
|
border-color 130ms ease-in-out;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
@ -292,22 +451,14 @@
|
||||||
li:hover {
|
li:hover {
|
||||||
color: var(--spectrum-global-color-gray-900);
|
color: var(--spectrum-global-color-gray-900);
|
||||||
background-color: var(--spectrum-global-color-gray-50);
|
background-color: var(--spectrum-global-color-gray-50);
|
||||||
border-color: var(--spectrum-global-color-gray-500);
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
li:hover :global(*) {
|
|
||||||
color: var(--spectrum-global-color-gray-900) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.binding__label {
|
.binding__label {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
.binding__description {
|
|
||||||
color: var(--spectrum-global-color-gray-700);
|
|
||||||
margin: 0.5rem 0 0 0;
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
.binding__type {
|
.binding__type {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
background-color: var(--spectrum-global-color-gray-200);
|
background-color: var(--spectrum-global-color-gray-200);
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
}
|
}
|
||||||
return bindings?.map(binding => ({
|
return bindings?.map(binding => ({
|
||||||
...binding,
|
...binding,
|
||||||
category: "Bindable Values",
|
|
||||||
type: null,
|
type: null,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,9 @@
|
||||||
class="add-button"
|
class="add-button"
|
||||||
data-cy={`new-${isExternal ? "datasource" : "table"}`}
|
data-cy={`new-${isExternal ? "datasource" : "table"}`}
|
||||||
>
|
>
|
||||||
<Icon hoverable name="AddCircle" on:click={modal.show} />
|
{#if modal}
|
||||||
|
<Icon hoverable name="AddCircle" on:click={modal.show} />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|
|
@ -85,6 +85,7 @@
|
||||||
? [$store.componentToPaste?._id]
|
? [$store.componentToPaste?._id]
|
||||||
: [],
|
: [],
|
||||||
isBudibaseEvent: true,
|
isBudibaseEvent: true,
|
||||||
|
usedPlugins: $store.usedPlugins,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh the preview when required
|
// Refresh the preview when required
|
||||||
|
@ -183,7 +184,7 @@
|
||||||
$goto("./navigation")
|
$goto("./navigation")
|
||||||
}
|
}
|
||||||
} else if (type === "request-add-component") {
|
} else if (type === "request-add-component") {
|
||||||
$goto(`./components/${$selectedComponent?._id}/new`)
|
toggleAddComponent()
|
||||||
} else if (type === "highlight-setting") {
|
} else if (type === "highlight-setting") {
|
||||||
store.actions.settings.highlight(data.setting)
|
store.actions.settings.highlight(data.setting)
|
||||||
|
|
||||||
|
@ -227,9 +228,8 @@
|
||||||
if (isAddingComponent) {
|
if (isAddingComponent) {
|
||||||
$goto(`../${$selectedScreen._id}/components/${$selectedComponent?._id}`)
|
$goto(`../${$selectedScreen._id}/components/${$selectedComponent?._id}`)
|
||||||
} else {
|
} else {
|
||||||
$goto(
|
const id = $selectedComponent?._id || $selectedScreen?.props?._id
|
||||||
`../${$selectedScreen._id}/components/${$selectedComponent?._id}/new`
|
$goto(`../${$selectedScreen._id}/components/${id}/new`)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,20 @@
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import { ActionMenu, MenuItem, Icon } from "@budibase/bbui"
|
import { ActionMenu, MenuItem, Icon } from "@budibase/bbui"
|
||||||
|
|
||||||
|
export let component
|
||||||
|
|
||||||
$: noPaste = !$store.componentToPaste
|
$: noPaste = !$store.componentToPaste
|
||||||
|
|
||||||
const keyboardEvent = (key, ctrlKey = false) => {
|
const keyboardEvent = (key, ctrlKey = false) => {
|
||||||
document.dispatchEvent(new KeyboardEvent("keydown", { key, ctrlKey }))
|
document.dispatchEvent(
|
||||||
|
new CustomEvent("component-menu", {
|
||||||
|
detail: {
|
||||||
|
key,
|
||||||
|
ctrlKey,
|
||||||
|
id: component?._id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import { selectedComponent, selectedScreen, store } from "builderStore"
|
||||||
|
import { findComponent } from "builderStore/componentUtils"
|
||||||
|
import { goto, isActive } from "@roxi/routify"
|
||||||
|
import { notifications } from "@budibase/bbui"
|
||||||
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
|
|
||||||
|
let confirmDeleteDialog
|
||||||
|
let componentToDelete
|
||||||
|
|
||||||
|
const keyHandlers = {
|
||||||
|
["^ArrowUp"]: async component => {
|
||||||
|
await store.actions.components.moveUp(component)
|
||||||
|
},
|
||||||
|
["^ArrowDown"]: async component => {
|
||||||
|
await store.actions.components.moveDown(component)
|
||||||
|
},
|
||||||
|
["^c"]: component => {
|
||||||
|
store.actions.components.copy(component, false)
|
||||||
|
},
|
||||||
|
["^x"]: component => {
|
||||||
|
store.actions.components.copy(component, true)
|
||||||
|
},
|
||||||
|
["^v"]: async component => {
|
||||||
|
await store.actions.components.paste(component, "inside")
|
||||||
|
},
|
||||||
|
["^d"]: async component => {
|
||||||
|
store.actions.components.copy(component)
|
||||||
|
await store.actions.components.paste(component, "below")
|
||||||
|
},
|
||||||
|
["^Enter"]: () => {
|
||||||
|
$goto("./new")
|
||||||
|
},
|
||||||
|
["Delete"]: component => {
|
||||||
|
// Don't show confirmation for the screen itself
|
||||||
|
if (component?._id === $selectedScreen.props._id) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
componentToDelete = component
|
||||||
|
confirmDeleteDialog.show()
|
||||||
|
},
|
||||||
|
["ArrowUp"]: () => {
|
||||||
|
store.actions.components.selectPrevious()
|
||||||
|
},
|
||||||
|
["ArrowDown"]: () => {
|
||||||
|
store.actions.components.selectNext()
|
||||||
|
},
|
||||||
|
["Escape"]: () => {
|
||||||
|
if (!$isActive("/new")) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
$goto("./")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleKeyAction = async (event, component, key, ctrlKey = false) => {
|
||||||
|
if (!component || !key) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// Delete and backspace are the same
|
||||||
|
if (key === "Backspace") {
|
||||||
|
key = "Delete"
|
||||||
|
}
|
||||||
|
// Prefix key with a caret for ctrl modifier
|
||||||
|
if (ctrlKey) {
|
||||||
|
key = "^" + key
|
||||||
|
}
|
||||||
|
const handler = keyHandlers[key]
|
||||||
|
if (!handler) {
|
||||||
|
return false
|
||||||
|
} else if (event) {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
}
|
||||||
|
return handler(component)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
notifications.error("Error handling key press")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleKeyPress = async e => {
|
||||||
|
// Ignore repeating events
|
||||||
|
if (e.repeat) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Ignore events when typing
|
||||||
|
const activeTag = document.activeElement?.tagName.toLowerCase()
|
||||||
|
if (["input", "textarea"].indexOf(activeTag) !== -1 && e.key !== "Escape") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Key events are always for the selected component
|
||||||
|
return await handleKeyAction(
|
||||||
|
e,
|
||||||
|
$selectedComponent,
|
||||||
|
e.key,
|
||||||
|
e.ctrlKey || e.metaKey
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleComponentMenu = async e => {
|
||||||
|
// Menu events can be for any component
|
||||||
|
const { id, key, ctrlKey } = e.detail
|
||||||
|
const component = findComponent($selectedScreen.props, id)
|
||||||
|
return await handleKeyAction(null, component, key, ctrlKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
document.addEventListener("keydown", handleKeyPress)
|
||||||
|
document.addEventListener("component-menu", handleComponentMenu)
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("keydown", handleKeyPress)
|
||||||
|
document.removeEventListener("component-menu", handleComponentMenu)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ConfirmDialog
|
||||||
|
bind:this={confirmDeleteDialog}
|
||||||
|
title="Confirm Deletion"
|
||||||
|
body={`Are you sure you want to delete "${componentToDelete?._instanceName}"?`}
|
||||||
|
okText="Delete Component"
|
||||||
|
onOk={() => store.actions.components.delete(componentToDelete)}
|
||||||
|
/>
|
|
@ -2,62 +2,15 @@
|
||||||
import Panel from "components/design/Panel.svelte"
|
import Panel from "components/design/Panel.svelte"
|
||||||
import ComponentTree from "./ComponentTree.svelte"
|
import ComponentTree from "./ComponentTree.svelte"
|
||||||
import { dndStore } from "./dndStore.js"
|
import { dndStore } from "./dndStore.js"
|
||||||
import { goto, isActive } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
import { store, selectedScreen, selectedComponent } from "builderStore"
|
import { store, selectedScreen } from "builderStore"
|
||||||
import NavItem from "components/common/NavItem.svelte"
|
import NavItem from "components/common/NavItem.svelte"
|
||||||
import ScreenslotDropdownMenu from "./ScreenslotDropdownMenu.svelte"
|
import ScreenslotDropdownMenu from "./ScreenslotDropdownMenu.svelte"
|
||||||
import { setContext, onMount } from "svelte"
|
|
||||||
import { get } from "svelte/store"
|
|
||||||
import DNDPositionIndicator from "./DNDPositionIndicator.svelte"
|
import DNDPositionIndicator from "./DNDPositionIndicator.svelte"
|
||||||
import { DropPosition } from "./dndStore"
|
import { DropPosition } from "./dndStore"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
|
||||||
import { notifications, Button } from "@budibase/bbui"
|
import { notifications, Button } from "@budibase/bbui"
|
||||||
|
import ComponentKeyHandler from "./ComponentKeyHandler.svelte"
|
||||||
let scrollRef
|
import ComponentScrollWrapper from "./ComponentScrollWrapper.svelte"
|
||||||
let confirmDeleteDialog
|
|
||||||
|
|
||||||
const scrollTo = bounds => {
|
|
||||||
if (!bounds) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const sidebarWidth = 259
|
|
||||||
const navItemHeight = 32
|
|
||||||
const { scrollLeft, scrollTop, offsetHeight } = scrollRef
|
|
||||||
let scrollBounds = scrollRef.getBoundingClientRect()
|
|
||||||
let newOffsets = {}
|
|
||||||
|
|
||||||
// Calculate left offset
|
|
||||||
const offsetX = bounds.left + bounds.width + scrollLeft - 36
|
|
||||||
if (offsetX > sidebarWidth) {
|
|
||||||
newOffsets.left = offsetX - sidebarWidth
|
|
||||||
} else {
|
|
||||||
newOffsets.left = 0
|
|
||||||
}
|
|
||||||
if (newOffsets.left === scrollLeft) {
|
|
||||||
delete newOffsets.left
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate top offset
|
|
||||||
const offsetY = bounds.top - scrollBounds?.top + scrollTop
|
|
||||||
if (offsetY > scrollTop + offsetHeight - 2 * navItemHeight) {
|
|
||||||
newOffsets.top = offsetY - offsetHeight + 2 * navItemHeight
|
|
||||||
} else if (offsetY < scrollTop + navItemHeight) {
|
|
||||||
newOffsets.top = offsetY - navItemHeight
|
|
||||||
} else {
|
|
||||||
delete newOffsets.top
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip if offset is unchanged
|
|
||||||
if (newOffsets.left == null && newOffsets.top == null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Smoothly scroll to the offset
|
|
||||||
scrollRef.scroll({
|
|
||||||
...newOffsets,
|
|
||||||
behavior: "smooth",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const onDrop = async () => {
|
const onDrop = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -67,95 +20,15 @@
|
||||||
notifications.error("Error saving component")
|
notifications.error("Error saving component")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set scroll context so components can invoke scrolling when selected
|
|
||||||
setContext("scroll", {
|
|
||||||
scrollTo,
|
|
||||||
})
|
|
||||||
|
|
||||||
const deleteComponent = async () => {
|
|
||||||
await store.actions.components.delete(get(selectedComponent))
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleKeyPress = async e => {
|
|
||||||
// Ignore repeating events
|
|
||||||
if (e.repeat) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Ignore events when typing
|
|
||||||
const activeTag = document.activeElement?.tagName.toLowerCase()
|
|
||||||
if (["input", "textarea"].indexOf(activeTag) !== -1 && e.key !== "Escape") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const component = get(selectedComponent)
|
|
||||||
try {
|
|
||||||
if (e.ctrlKey || e.metaKey) {
|
|
||||||
if (e.key === "ArrowUp") {
|
|
||||||
e.preventDefault()
|
|
||||||
await store.actions.components.moveUp(component)
|
|
||||||
} else if (e.key === "ArrowDown") {
|
|
||||||
e.preventDefault()
|
|
||||||
await store.actions.components.moveDown(component)
|
|
||||||
} else if (e.key === "c") {
|
|
||||||
e.preventDefault()
|
|
||||||
await store.actions.components.copy(component, false)
|
|
||||||
} else if (e.key === "x") {
|
|
||||||
e.preventDefault()
|
|
||||||
store.actions.components.copy(component, true)
|
|
||||||
} else if (e.key === "v") {
|
|
||||||
e.preventDefault()
|
|
||||||
await store.actions.components.paste(component, "inside")
|
|
||||||
} else if (e.key === "d") {
|
|
||||||
e.preventDefault()
|
|
||||||
await store.actions.components.copy(component)
|
|
||||||
await store.actions.components.paste(component, "below")
|
|
||||||
} else if (e.key === "Enter") {
|
|
||||||
e.preventDefault()
|
|
||||||
$goto("./new")
|
|
||||||
}
|
|
||||||
} else if (e.key === "Backspace" || e.key === "Delete") {
|
|
||||||
// Don't show confirmation for the screen itself
|
|
||||||
if (component._id === get(selectedScreen).props._id) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
e.preventDefault()
|
|
||||||
confirmDeleteDialog.show()
|
|
||||||
} else if (e.key === "ArrowUp") {
|
|
||||||
e.preventDefault()
|
|
||||||
await store.actions.components.selectPrevious()
|
|
||||||
} else if (e.key === "ArrowDown") {
|
|
||||||
e.preventDefault()
|
|
||||||
await store.actions.components.selectNext()
|
|
||||||
} else if (e.key === "Escape" && $isActive("./new")) {
|
|
||||||
e.preventDefault()
|
|
||||||
$goto("./")
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
notifications.error("Error handling key press")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
document.addEventListener("keydown", handleKeyPress)
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("keydown", handleKeyPress)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Panel title="Components" showExpandIcon borderRight>
|
<Panel title="Components" showExpandIcon borderRight>
|
||||||
<div class="add-component">
|
<div class="add-component">
|
||||||
<Button on:click={() => $goto("./new")} cta>Add component</Button>
|
<Button on:click={() => $goto("./new")} cta>Add component</Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-items-container" bind:this={scrollRef}>
|
<ComponentScrollWrapper>
|
||||||
<ul>
|
<ul>
|
||||||
<li
|
<li>
|
||||||
on:click={() => {
|
|
||||||
$store.selectedComponentId = $selectedScreen?.props._id
|
|
||||||
}}
|
|
||||||
id={`component-${$selectedScreen?.props._id}`}
|
|
||||||
>
|
|
||||||
<NavItem
|
<NavItem
|
||||||
text="Screen"
|
text="Screen"
|
||||||
indentLevel={0}
|
indentLevel={0}
|
||||||
|
@ -164,6 +37,10 @@
|
||||||
scrollable
|
scrollable
|
||||||
icon="WebPage"
|
icon="WebPage"
|
||||||
on:drop={onDrop}
|
on:drop={onDrop}
|
||||||
|
on:click={() => {
|
||||||
|
$store.selectedComponentId = $selectedScreen?.props._id
|
||||||
|
}}
|
||||||
|
id={`component-${$selectedScreen?.props._id}`}
|
||||||
>
|
>
|
||||||
<ScreenslotDropdownMenu component={$selectedScreen?.props} />
|
<ScreenslotDropdownMenu component={$selectedScreen?.props} />
|
||||||
</NavItem>
|
</NavItem>
|
||||||
|
@ -187,15 +64,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</ComponentScrollWrapper>
|
||||||
</Panel>
|
</Panel>
|
||||||
<ConfirmDialog
|
<ComponentKeyHandler />
|
||||||
bind:this={confirmDeleteDialog}
|
|
||||||
title="Confirm Deletion"
|
|
||||||
body={`Are you sure you want to delete "${$selectedComponent?._instanceName}"?`}
|
|
||||||
okText="Delete Component"
|
|
||||||
onOk={deleteComponent}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.add-component {
|
.add-component {
|
||||||
|
@ -205,12 +76,6 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
.nav-items-container {
|
|
||||||
padding: var(--spacing-xl) 0;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
overflow: auto;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
ul {
|
ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
<script>
|
||||||
|
import { setContext } from "svelte"
|
||||||
|
import { dndStore } from "./dndStore"
|
||||||
|
import { notifications } from "@budibase/bbui"
|
||||||
|
|
||||||
|
let scrollRef
|
||||||
|
|
||||||
|
const scrollTo = bounds => {
|
||||||
|
if (!bounds) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const sidebarWidth = 259
|
||||||
|
const navItemHeight = 32
|
||||||
|
const { scrollLeft, scrollTop, offsetHeight } = scrollRef
|
||||||
|
let scrollBounds = scrollRef.getBoundingClientRect()
|
||||||
|
let newOffsets = {}
|
||||||
|
|
||||||
|
// Calculate left offset
|
||||||
|
const offsetX = bounds.left + bounds.width + scrollLeft - 36
|
||||||
|
if (offsetX > sidebarWidth) {
|
||||||
|
newOffsets.left = offsetX - sidebarWidth
|
||||||
|
} else {
|
||||||
|
newOffsets.left = 0
|
||||||
|
}
|
||||||
|
if (newOffsets.left === scrollLeft) {
|
||||||
|
delete newOffsets.left
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate top offset
|
||||||
|
const offsetY = bounds.top - scrollBounds?.top + scrollTop
|
||||||
|
if (offsetY > scrollTop + offsetHeight - 2 * navItemHeight) {
|
||||||
|
newOffsets.top = offsetY - offsetHeight + 2 * navItemHeight
|
||||||
|
} else if (offsetY < scrollTop + navItemHeight) {
|
||||||
|
newOffsets.top = offsetY - navItemHeight
|
||||||
|
} else {
|
||||||
|
delete newOffsets.top
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if offset is unchanged
|
||||||
|
if (newOffsets.left == null && newOffsets.top == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smoothly scroll to the offset
|
||||||
|
scrollRef.scroll({
|
||||||
|
...newOffsets,
|
||||||
|
behavior: "smooth",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set scroll context so components can invoke scrolling when selected
|
||||||
|
setContext("scroll", {
|
||||||
|
scrollTo,
|
||||||
|
})
|
||||||
|
|
||||||
|
const onDrop = async () => {
|
||||||
|
try {
|
||||||
|
await dndStore.actions.drop()
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
notifications.error("Error saving component")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
bind:this={scrollRef}
|
||||||
|
on:drop={onDrop}
|
||||||
|
ondragover="return false"
|
||||||
|
ondragenter="return false"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
padding: var(--spacing-xl) 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: auto;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -68,7 +68,8 @@
|
||||||
closedNodes = closedNodes
|
closedNodes = closedNodes
|
||||||
}
|
}
|
||||||
|
|
||||||
const onDrop = async () => {
|
const onDrop = async e => {
|
||||||
|
e.stopPropagation()
|
||||||
try {
|
try {
|
||||||
await dndStore.actions.drop()
|
await dndStore.actions.drop()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
{#if $selectedComponent}
|
{#if $selectedComponent}
|
||||||
{#key $selectedComponent._id}
|
{#key $selectedComponent._id}
|
||||||
<Panel {title} icon={componentDefinition.icon} borderLeft>
|
<Panel {title} icon={componentDefinition?.icon} borderLeft>
|
||||||
<ComponentSettingsSection
|
<ComponentSettingsSection
|
||||||
{componentInstance}
|
{componentInstance}
|
||||||
{componentDefinition}
|
{componentDefinition}
|
||||||
|
|
|
@ -24,7 +24,11 @@
|
||||||
$: currentDefinition = store.actions.components.getDefinition(
|
$: currentDefinition = store.actions.components.getDefinition(
|
||||||
$selectedComponent?._component
|
$selectedComponent?._component
|
||||||
)
|
)
|
||||||
$: enrichedStructure = enrichStructure(structure, $store.components)
|
$: enrichedStructure = enrichStructure(
|
||||||
|
structure,
|
||||||
|
$store.components,
|
||||||
|
$store.customComponents
|
||||||
|
)
|
||||||
$: filteredStructure = filterStructure(
|
$: filteredStructure = filterStructure(
|
||||||
enrichedStructure,
|
enrichedStructure,
|
||||||
section,
|
section,
|
||||||
|
@ -46,8 +50,25 @@
|
||||||
|
|
||||||
// Parses the structure in the manifest and returns an enriched structure with
|
// Parses the structure in the manifest and returns an enriched structure with
|
||||||
// explicit categories
|
// explicit categories
|
||||||
const enrichStructure = (structure, definitions) => {
|
const enrichStructure = (structure, definitions, customComponents) => {
|
||||||
let enrichedStructure = []
|
let enrichedStructure = []
|
||||||
|
|
||||||
|
// Add custom components category
|
||||||
|
if (customComponents?.length) {
|
||||||
|
enrichedStructure.push({
|
||||||
|
name: "Plugins",
|
||||||
|
isCategory: true,
|
||||||
|
children: customComponents
|
||||||
|
.map(x => ({
|
||||||
|
...definitions[x],
|
||||||
|
name: definitions[x].friendlyName || definitions[x].name,
|
||||||
|
}))
|
||||||
|
.sort((a, b) => {
|
||||||
|
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
structure.forEach(item => {
|
structure.forEach(item => {
|
||||||
if (typeof item === "string") {
|
if (typeof item === "string") {
|
||||||
const def = definitions[`@budibase/standard-components/${item}`]
|
const def = definitions[`@budibase/standard-components/${item}`]
|
||||||
|
@ -65,6 +86,7 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return enrichedStructure
|
return enrichedStructure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +247,7 @@
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
height: 100%;
|
height: calc(100% - 60px);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|
|
@ -44,7 +44,11 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateInput(email, index) {
|
function validateInput(input, index) {
|
||||||
|
if (input.email) {
|
||||||
|
input.email = input.email.trim()
|
||||||
|
}
|
||||||
|
const email = input.email
|
||||||
if (email) {
|
if (email) {
|
||||||
const res = emailValidator(email)
|
const res = emailValidator(email)
|
||||||
if (res === true) {
|
if (res === true) {
|
||||||
|
@ -61,7 +65,7 @@
|
||||||
const onConfirm = () => {
|
const onConfirm = () => {
|
||||||
let valid = true
|
let valid = true
|
||||||
userData.forEach((input, index) => {
|
userData.forEach((input, index) => {
|
||||||
valid = validateInput(input.email, index) && valid
|
valid = validateInput(input, index) && valid
|
||||||
})
|
})
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
return false
|
return false
|
||||||
|
@ -95,7 +99,7 @@
|
||||||
bind:dropdownValue={input.role}
|
bind:dropdownValue={input.role}
|
||||||
options={Constants.BudibaseRoleOptions}
|
options={Constants.BudibaseRoleOptions}
|
||||||
error={input.error}
|
error={input.error}
|
||||||
on:blur={() => validateInput(input.email, index)}
|
on:blur={() => validateInput(input, index)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
<script>
|
||||||
|
import { Body, ModalContent, Table } from "@budibase/bbui"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
|
export let userData
|
||||||
|
export let deleteUsersResponse
|
||||||
|
|
||||||
|
let successCount
|
||||||
|
let failureCount
|
||||||
|
let title
|
||||||
|
let unsuccessfulUsers
|
||||||
|
let message
|
||||||
|
|
||||||
|
const setTitle = () => {
|
||||||
|
if (successCount) {
|
||||||
|
title = `${successCount} users deleted`
|
||||||
|
} else {
|
||||||
|
title = "Oops!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setMessage = () => {
|
||||||
|
if (successCount) {
|
||||||
|
message = "However there was a problem deleting some users."
|
||||||
|
} else {
|
||||||
|
message = "There was a problem deleting some users."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setUsers = () => {
|
||||||
|
unsuccessfulUsers = deleteUsersResponse.unsuccessful.map(user => {
|
||||||
|
return {
|
||||||
|
email: user.email,
|
||||||
|
reason: user.reason,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
successCount = deleteUsersResponse.successful.length
|
||||||
|
failureCount = deleteUsersResponse.unsuccessful.length
|
||||||
|
setTitle()
|
||||||
|
setMessage()
|
||||||
|
setUsers()
|
||||||
|
})
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
email: {},
|
||||||
|
reason: {},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ModalContent
|
||||||
|
size="M"
|
||||||
|
{title}
|
||||||
|
confirmText="Close"
|
||||||
|
showCloseIcon={false}
|
||||||
|
showCancelButton={false}
|
||||||
|
>
|
||||||
|
<Body size="XS">
|
||||||
|
{message}
|
||||||
|
</Body>
|
||||||
|
<Table
|
||||||
|
{schema}
|
||||||
|
data={unsuccessfulUsers}
|
||||||
|
allowEditColumns={false}
|
||||||
|
allowEditRows={false}
|
||||||
|
allowSelectRows={false}
|
||||||
|
/>
|
||||||
|
</ModalContent>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
|
@ -62,7 +62,7 @@
|
||||||
csvString = e.target.result
|
csvString = e.target.result
|
||||||
files = fileArray
|
files = fileArray
|
||||||
|
|
||||||
userEmails = csvString.split("\n")
|
userEmails = csvString.split(/\r?\n/)
|
||||||
})
|
})
|
||||||
reader.readAsText(fileArray[0])
|
reader.readAsText(fileArray[0])
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
<script>
|
||||||
|
import { Body, ModalContent, Table } from "@budibase/bbui"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
|
export let inviteUsersResponse
|
||||||
|
|
||||||
|
let hasSuccess
|
||||||
|
let hasFailure
|
||||||
|
let title
|
||||||
|
let failureMessage
|
||||||
|
|
||||||
|
let unsuccessfulUsers
|
||||||
|
|
||||||
|
const setTitle = () => {
|
||||||
|
if (hasSuccess) {
|
||||||
|
title = "Users invited!"
|
||||||
|
} else if (hasFailure) {
|
||||||
|
title = "Oops!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setFailureMessage = () => {
|
||||||
|
if (hasSuccess) {
|
||||||
|
failureMessage = "However there was a problem inviting some users."
|
||||||
|
} else {
|
||||||
|
failureMessage = "There was a problem inviting users."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setUsers = () => {
|
||||||
|
unsuccessfulUsers = inviteUsersResponse.unsuccessful.map(user => {
|
||||||
|
return {
|
||||||
|
email: user.email,
|
||||||
|
reason: user.reason,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
hasSuccess = inviteUsersResponse.successful.length
|
||||||
|
hasFailure = inviteUsersResponse.unsuccessful.length
|
||||||
|
setTitle()
|
||||||
|
setFailureMessage()
|
||||||
|
setUsers()
|
||||||
|
})
|
||||||
|
|
||||||
|
const failedSchema = {
|
||||||
|
email: {},
|
||||||
|
reason: {},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ModalContent showCancelButton={false} {title} confirmText="Done">
|
||||||
|
{#if hasSuccess}
|
||||||
|
<Body size="XS">
|
||||||
|
Your users should now receive an email invite to get access to their
|
||||||
|
Budibase account
|
||||||
|
</Body>
|
||||||
|
{/if}
|
||||||
|
{#if hasFailure}
|
||||||
|
<Body size="XS">
|
||||||
|
{failureMessage}
|
||||||
|
</Body>
|
||||||
|
<Table
|
||||||
|
schema={failedSchema}
|
||||||
|
data={unsuccessfulUsers}
|
||||||
|
allowEditColumns={false}
|
||||||
|
allowEditRows={false}
|
||||||
|
allowSelectRows={false}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</ModalContent>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
|
@ -2,24 +2,78 @@
|
||||||
import { Body, ModalContent, Table, Icon } from "@budibase/bbui"
|
import { Body, ModalContent, Table, Icon } from "@budibase/bbui"
|
||||||
import PasswordCopyRenderer from "./PasswordCopyRenderer.svelte"
|
import PasswordCopyRenderer from "./PasswordCopyRenderer.svelte"
|
||||||
import { parseToCsv } from "helpers/data/utils"
|
import { parseToCsv } from "helpers/data/utils"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
export let userData
|
export let userData
|
||||||
|
export let createUsersResponse
|
||||||
|
|
||||||
$: mappedData = userData.map(user => {
|
let hasSuccess
|
||||||
return {
|
let hasFailure
|
||||||
email: user.email,
|
let title
|
||||||
password: user.password,
|
let failureMessage
|
||||||
|
|
||||||
|
let userDataIndex
|
||||||
|
let successfulUsers
|
||||||
|
let unsuccessfulUsers
|
||||||
|
|
||||||
|
const setTitle = () => {
|
||||||
|
if (hasSuccess) {
|
||||||
|
title = "Users created!"
|
||||||
|
} else if (hasFailure) {
|
||||||
|
title = "Oops!"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setFailureMessage = () => {
|
||||||
|
if (hasSuccess) {
|
||||||
|
failureMessage = "However there was a problem creating some users."
|
||||||
|
} else {
|
||||||
|
failureMessage = "There was a problem creating some users."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setUsers = () => {
|
||||||
|
userDataIndex = userData.reduce((prev, current) => {
|
||||||
|
prev[current.email] = current
|
||||||
|
return prev
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
successfulUsers = createUsersResponse.successful.map(user => {
|
||||||
|
return {
|
||||||
|
email: user.email,
|
||||||
|
password: userDataIndex[user.email].password,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
unsuccessfulUsers = createUsersResponse.unsuccessful.map(user => {
|
||||||
|
return {
|
||||||
|
email: user.email,
|
||||||
|
reason: user.reason,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
hasSuccess = createUsersResponse.successful.length
|
||||||
|
hasFailure = createUsersResponse.unsuccessful.length
|
||||||
|
setTitle()
|
||||||
|
setFailureMessage()
|
||||||
|
setUsers()
|
||||||
})
|
})
|
||||||
|
|
||||||
const schema = {
|
const successSchema = {
|
||||||
email: {},
|
email: {},
|
||||||
password: {},
|
password: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const failedSchema = {
|
||||||
|
email: {},
|
||||||
|
reason: {},
|
||||||
|
}
|
||||||
|
|
||||||
const downloadCsvFile = () => {
|
const downloadCsvFile = () => {
|
||||||
const fileName = "passwords.csv"
|
const fileName = "passwords.csv"
|
||||||
const content = parseToCsv(["email", "password"], mappedData)
|
const content = parseToCsv(["email", "password"], successfulUsers)
|
||||||
|
|
||||||
download(fileName, content)
|
download(fileName, content)
|
||||||
}
|
}
|
||||||
|
@ -43,35 +97,51 @@
|
||||||
|
|
||||||
<ModalContent
|
<ModalContent
|
||||||
size="M"
|
size="M"
|
||||||
title="Accounts created!"
|
{title}
|
||||||
confirmText="Done"
|
confirmText="Done"
|
||||||
showCancelButton={false}
|
showCancelButton={false}
|
||||||
cancelText="Cancel"
|
cancelText="Cancel"
|
||||||
showCloseIcon={false}
|
showCloseIcon={false}
|
||||||
>
|
>
|
||||||
<Body size="XS">
|
{#if hasFailure}
|
||||||
All your new users can be accessed through the autogenerated passwords. Take
|
<Body size="XS">
|
||||||
note of these passwords or download the CSV file.
|
{failureMessage}
|
||||||
</Body>
|
</Body>
|
||||||
|
<Table
|
||||||
|
schema={failedSchema}
|
||||||
|
data={unsuccessfulUsers}
|
||||||
|
allowEditColumns={false}
|
||||||
|
allowEditRows={false}
|
||||||
|
allowSelectRows={false}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{#if hasSuccess}
|
||||||
|
<Body size="XS">
|
||||||
|
All your new users can be accessed through the autogenerated passwords.
|
||||||
|
Take note of these passwords or download the CSV file.
|
||||||
|
</Body>
|
||||||
|
|
||||||
<div class="container" on:click={downloadCsvFile}>
|
<div class="container" on:click={downloadCsvFile}>
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<Icon name="Download" />
|
<Icon name="Download" />
|
||||||
|
|
||||||
<div style="margin-left: var(--spacing-m)">
|
<div style="margin-left: var(--spacing-m)">
|
||||||
<Body size="XS">Passwords CSV</Body>
|
<Body size="XS">Passwords CSV</Body>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<Table
|
<Table
|
||||||
{schema}
|
schema={successSchema}
|
||||||
data={mappedData}
|
data={successfulUsers}
|
||||||
allowEditColumns={false}
|
allowEditColumns={false}
|
||||||
allowEditRows={false}
|
allowEditRows={false}
|
||||||
allowSelectRows={false}
|
allowSelectRows={false}
|
||||||
customRenderers={[{ column: "password", component: PasswordCopyRenderer }]}
|
customRenderers={[
|
||||||
/>
|
{ column: "password", component: PasswordCopyRenderer },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
Table,
|
Table,
|
||||||
Layout,
|
Layout,
|
||||||
Modal,
|
Modal,
|
||||||
ModalContent,
|
|
||||||
Search,
|
Search,
|
||||||
notifications,
|
notifications,
|
||||||
Pagination,
|
Pagination,
|
||||||
|
@ -23,6 +22,8 @@
|
||||||
import { goto } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
import OnboardingTypeModal from "./_components/OnboardingTypeModal.svelte"
|
import OnboardingTypeModal from "./_components/OnboardingTypeModal.svelte"
|
||||||
import PasswordModal from "./_components/PasswordModal.svelte"
|
import PasswordModal from "./_components/PasswordModal.svelte"
|
||||||
|
import InvitedModal from "./_components/InvitedModal.svelte"
|
||||||
|
import DeletionFailureModal from "./_components/DeletionFailureModal.svelte"
|
||||||
import ImportUsersModal from "./_components/ImportUsersModal.svelte"
|
import ImportUsersModal from "./_components/ImportUsersModal.svelte"
|
||||||
import { createPaginationStore } from "helpers/pagination"
|
import { createPaginationStore } from "helpers/pagination"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
|
@ -33,7 +34,8 @@
|
||||||
inviteConfirmationModal,
|
inviteConfirmationModal,
|
||||||
onboardingTypeModal,
|
onboardingTypeModal,
|
||||||
passwordModal,
|
passwordModal,
|
||||||
importUsersModal
|
importUsersModal,
|
||||||
|
deletionFailureModal
|
||||||
let pageInfo = createPaginationStore()
|
let pageInfo = createPaginationStore()
|
||||||
let prevEmail = undefined,
|
let prevEmail = undefined,
|
||||||
searchEmail = undefined
|
searchEmail = undefined
|
||||||
|
@ -55,6 +57,9 @@
|
||||||
apps: {},
|
apps: {},
|
||||||
}
|
}
|
||||||
$: userData = []
|
$: userData = []
|
||||||
|
$: createUsersResponse = { successful: [], unsuccessful: [] }
|
||||||
|
$: deleteUsersResponse = { successful: [], unsuccessful: [] }
|
||||||
|
$: inviteUsersResponse = { successful: [], unsuccessful: [] }
|
||||||
$: page = $pageInfo.page
|
$: page = $pageInfo.page
|
||||||
$: fetchUsers(page, searchEmail)
|
$: fetchUsers(page, searchEmail)
|
||||||
$: {
|
$: {
|
||||||
|
@ -92,8 +97,7 @@
|
||||||
admin: user.role === Constants.BudibaseRoles.Admin,
|
admin: user.role === Constants.BudibaseRoles.Admin,
|
||||||
}))
|
}))
|
||||||
try {
|
try {
|
||||||
const res = await users.invite(payload)
|
inviteUsersResponse = await users.invite(payload)
|
||||||
notifications.success(res.message)
|
|
||||||
inviteConfirmationModal.show()
|
inviteConfirmationModal.show()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Error inviting user")
|
notifications.error("Error inviting user")
|
||||||
|
@ -116,8 +120,9 @@
|
||||||
newUsers.push(user)
|
newUsers.push(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newUsers.length)
|
if (!newUsers.length) {
|
||||||
notifications.info("Duplicated! There is no new users to add.")
|
notifications.info("Duplicated! There is no new users to add.")
|
||||||
|
}
|
||||||
return { ...userData, users: newUsers }
|
return { ...userData, users: newUsers }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,12 +144,14 @@
|
||||||
userData = await removingDuplicities({ groups, users })
|
userData = await removingDuplicities({ groups, users })
|
||||||
if (!userData.users.length) return
|
if (!userData.users.length) return
|
||||||
|
|
||||||
return createUser()
|
return createUsers()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createUser() {
|
async function createUsers() {
|
||||||
try {
|
try {
|
||||||
await users.create(await removingDuplicities(userData))
|
createUsersResponse = await users.create(
|
||||||
|
await removingDuplicities(userData)
|
||||||
|
)
|
||||||
notifications.success("Successfully created user")
|
notifications.success("Successfully created user")
|
||||||
await groups.actions.init()
|
await groups.actions.init()
|
||||||
passwordModal.show()
|
passwordModal.show()
|
||||||
|
@ -157,7 +164,7 @@
|
||||||
if (onboardingType === "emailOnboarding") {
|
if (onboardingType === "emailOnboarding") {
|
||||||
createUserFlow()
|
createUserFlow()
|
||||||
} else {
|
} else {
|
||||||
await createUser()
|
await createUsers()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,8 +183,15 @@
|
||||||
notifications.error("You cannot delete yourself")
|
notifications.error("You cannot delete yourself")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await users.bulkDelete(ids)
|
deleteUsersResponse = await users.bulkDelete(ids)
|
||||||
notifications.success(`Successfully deleted ${selectedRows.length} rows`)
|
if (deleteUsersResponse.unsuccessful?.length) {
|
||||||
|
deletionFailureModal.show()
|
||||||
|
} else {
|
||||||
|
notifications.success(
|
||||||
|
`Successfully deleted ${selectedRows.length} users`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
selectedRows = []
|
selectedRows = []
|
||||||
await fetchUsers(page, searchEmail)
|
await fetchUsers(page, searchEmail)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -267,16 +281,7 @@
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal bind:this={inviteConfirmationModal}>
|
<Modal bind:this={inviteConfirmationModal}>
|
||||||
<ModalContent
|
<InvitedModal {inviteUsersResponse} />
|
||||||
showCancelButton={false}
|
|
||||||
title="Invites sent!"
|
|
||||||
confirmText="Done"
|
|
||||||
>
|
|
||||||
<Body size="S"
|
|
||||||
>Your users should now recieve an email invite to get access to their
|
|
||||||
Budibase account</Body
|
|
||||||
></ModalContent
|
|
||||||
>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal bind:this={onboardingTypeModal}>
|
<Modal bind:this={onboardingTypeModal}>
|
||||||
|
@ -284,7 +289,11 @@
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal bind:this={passwordModal}>
|
<Modal bind:this={passwordModal}>
|
||||||
<PasswordModal userData={userData.users} />
|
<PasswordModal {createUsersResponse} userData={userData.users} />
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal bind:this={deletionFailureModal}>
|
||||||
|
<DeletionFailureModal {deleteUsersResponse} />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal bind:this={importUsersModal}>
|
<Modal bind:this={importUsersModal}>
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
import { redirect } from "@roxi/routify"
|
import { redirect } from "@roxi/routify"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
// Only admins allowed here
|
// Only admins allowed here
|
||||||
$: {
|
$: {
|
||||||
|
@ -33,11 +34,12 @@
|
||||||
})
|
})
|
||||||
let loading = false
|
let loading = false
|
||||||
|
|
||||||
async function uploadLogo(file) {
|
async function uploadLogo() {
|
||||||
try {
|
try {
|
||||||
let data = new FormData()
|
let data = new FormData()
|
||||||
data.append("file", file)
|
data.append("file", $values.logo)
|
||||||
await API.uploadLogo(data)
|
await API.uploadPlugin(data)
|
||||||
|
notifications.success("Plugin uploaded successfully")
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Error uploading logo")
|
notifications.error("Error uploading logo")
|
||||||
}
|
}
|
||||||
|
@ -71,6 +73,11 @@
|
||||||
}
|
}
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
const plugins = await API.getPlugins()
|
||||||
|
console.log(plugins)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $auth.isAdmin}
|
{#if $auth.isAdmin}
|
||||||
|
@ -106,6 +113,7 @@
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<button on:click={uploadLogo}>Upload</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -63,10 +63,14 @@ export function createUsersStore() {
|
||||||
|
|
||||||
return body
|
return body
|
||||||
})
|
})
|
||||||
await API.createUsers({ users: mappedUsers, groups: data.groups })
|
const response = await API.createUsers({
|
||||||
|
users: mappedUsers,
|
||||||
|
groups: data.groups,
|
||||||
|
})
|
||||||
|
|
||||||
// re-search from first page
|
// re-search from first page
|
||||||
await search()
|
await search()
|
||||||
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
async function del(id) {
|
async function del(id) {
|
||||||
|
@ -79,7 +83,7 @@ export function createUsersStore() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bulkDelete(userIds) {
|
async function bulkDelete(userIds) {
|
||||||
await API.deleteUsers(userIds)
|
return API.deleteUsers(userIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function save(user) {
|
async function save(user) {
|
||||||
|
|
|
@ -1,15 +1,23 @@
|
||||||
import { svelte } from "@sveltejs/vite-plugin-svelte"
|
import { svelte } from "@sveltejs/vite-plugin-svelte"
|
||||||
import replace from "@rollup/plugin-replace"
|
import replace from "@rollup/plugin-replace"
|
||||||
|
import { defineConfig, loadEnv } from "vite"
|
||||||
|
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
export default ({ mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
const isProduction = mode === "production"
|
const isProduction = mode === "production"
|
||||||
|
const env = loadEnv(mode, process.cwd())
|
||||||
return {
|
return {
|
||||||
server: {
|
server: {
|
||||||
fs: {
|
fs: {
|
||||||
strict: false,
|
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/",
|
base: "/builder/",
|
||||||
build: {
|
build: {
|
||||||
|
@ -79,4 +87,4 @@ export default ({ mode }) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
|
@ -967,6 +967,11 @@
|
||||||
debug "^3.1.0"
|
debug "^3.1.0"
|
||||||
lodash.once "^4.1.1"
|
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":
|
"@hapi/hoek@^9.0.0":
|
||||||
version "9.2.1"
|
version "9.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17"
|
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17"
|
||||||
|
@ -1215,10 +1220,10 @@
|
||||||
estree-walker "^1.0.1"
|
estree-walker "^1.0.1"
|
||||||
picomatch "^2.2.2"
|
picomatch "^2.2.2"
|
||||||
|
|
||||||
"@rollup/pluginutils@^4.1.1":
|
"@rollup/pluginutils@^4.2.1":
|
||||||
version "4.1.1"
|
version "4.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.1.tgz#1d4da86dd4eded15656a57d933fda2b9a08d47ec"
|
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
|
||||||
integrity sha512-clDjivHqWGXi7u+0d2r2sBi4Ie6VLEAzWMIkvJLnDmxoOhBYOTfzGbOQBA32THHm11/LiJbd01tJUpJsbshSWQ==
|
integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
estree-walker "^2.0.1"
|
estree-walker "^2.0.1"
|
||||||
picomatch "^2.2.2"
|
picomatch "^2.2.2"
|
||||||
|
@ -1350,17 +1355,17 @@
|
||||||
resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-4.3.0.tgz#03ddf67d3aa8a9a4cb0edbbd259465c9ced7e70d"
|
resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-4.3.0.tgz#03ddf67d3aa8a9a4cb0edbbd259465c9ced7e70d"
|
||||||
integrity sha512-ZQ2XAhgu4G9yBeXQNDAz07Z8oZNnMt5o9vzf/mpBA7Teb/JI+8qXp2wt8D245SzmtNlFkG/bzRYvQc0scgZeCQ==
|
integrity sha512-ZQ2XAhgu4G9yBeXQNDAz07Z8oZNnMt5o9vzf/mpBA7Teb/JI+8qXp2wt8D245SzmtNlFkG/bzRYvQc0scgZeCQ==
|
||||||
|
|
||||||
"@sveltejs/vite-plugin-svelte@1.0.0-next.19":
|
"@sveltejs/vite-plugin-svelte@1.0.1":
|
||||||
version "1.0.0-next.19"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.19.tgz#9646abc2cd1982146db4bb341aafdb5f32f19dd2"
|
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.1.tgz#7f468f03c933fcdfc60d4773671c73f33b9ef4d6"
|
||||||
integrity sha512-q9hHkMzodScwDq64pNaWhekpj97vWg3wO9T0rqd8bC2EsrBQs2uD1qMJvDqlNd63AbO2uSJMGo+TQ0Xt2xgyYg==
|
integrity sha512-PorCgUounn0VXcpeJu+hOweZODKmGuLHsLomwqSj+p26IwjjGffmYQfVHtiTWq+NqaUuuHWWG7vPge6UFw4Aeg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@rollup/pluginutils" "^4.1.1"
|
"@rollup/pluginutils" "^4.2.1"
|
||||||
debug "^4.3.2"
|
debug "^4.3.4"
|
||||||
kleur "^4.1.4"
|
deepmerge "^4.2.2"
|
||||||
magic-string "^0.25.7"
|
kleur "^4.1.5"
|
||||||
require-relative "^0.8.7"
|
magic-string "^0.26.2"
|
||||||
svelte-hmr "^0.14.7"
|
svelte-hmr "^0.14.12"
|
||||||
|
|
||||||
"@testing-library/dom@^7.0.3":
|
"@testing-library/dom@^7.0.3":
|
||||||
version "7.31.2"
|
version "7.31.2"
|
||||||
|
@ -2493,6 +2498,13 @@ debug@^3.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
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:
|
decamelize@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||||
|
@ -2655,113 +2667,132 @@ error-ex@^1.3.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-arrayish "^0.2.1"
|
is-arrayish "^0.2.1"
|
||||||
|
|
||||||
esbuild-android-arm64@0.13.14:
|
esbuild-android-64@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.14.tgz#c85083ece26be3d67e6c720e088968a98409e023"
|
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
|
||||||
integrity sha512-Q+Xhfp827r+ma8/DJgpMRUbDZfefsk13oePFEXEIJ4gxFbNv5+vyiYXYuKm43/+++EJXpnaYmEnu4hAKbAWYbA==
|
integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==
|
||||||
|
|
||||||
esbuild-darwin-64@0.13.14:
|
esbuild-android-arm64@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.14.tgz#8e4e237ad847cc54a1d3a5caee26a746b9f0b81f"
|
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771"
|
||||||
integrity sha512-YmOhRns6QBNSjpVdTahi/yZ8dscx9ai7a6OY6z5ACgOuQuaQ2Qk2qgJ0/siZ6LgD0gJFMV8UINFV5oky5TFNQQ==
|
integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==
|
||||||
|
|
||||||
esbuild-darwin-arm64@0.13.14:
|
esbuild-darwin-64@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.14.tgz#b3b5ebd40b2cb06ee0f6fb342dd4bdcca54ad273"
|
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25"
|
||||||
integrity sha512-Lp00VTli2jqZghSa68fx3fEFCPsO1hK59RMo1PRap5RUjhf55OmaZTZYnCDI0FVlCtt+gBwX5qwFt4lc6tI1xg==
|
integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==
|
||||||
|
|
||||||
esbuild-freebsd-64@0.13.14:
|
esbuild-darwin-arm64@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.14.tgz#175ecb2fa8141428cf70ea2d5f4c27534bad53e0"
|
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73"
|
||||||
integrity sha512-BKosI3jtvTfnmsCW37B1TyxMUjkRWKqopR0CE9AF2ratdpkxdR24Vpe3gLKNyWiZ7BE96/SO5/YfhbPUzY8wKw==
|
integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==
|
||||||
|
|
||||||
esbuild-freebsd-arm64@0.13.14:
|
esbuild-freebsd-64@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.14.tgz#a7d64e41d1fa581f8db7775e5200f18e67d70c4d"
|
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d"
|
||||||
integrity sha512-yd2uh0yf+fWv5114+SYTl4/1oDWtr4nN5Op+PGxAkMqHfYfLjFKpcxwCo/QOS/0NWqPVE8O41IYZlFhbEN2B8Q==
|
integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==
|
||||||
|
|
||||||
esbuild-linux-32@0.13.14:
|
esbuild-freebsd-arm64@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.14.tgz#14bdd4f6b6cfd35c65c835894651ba335c2117da"
|
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48"
|
||||||
integrity sha512-a8rOnS1oWSfkkYWXoD2yXNV4BdbDKA7PNVQ1klqkY9SoSApL7io66w5H44mTLsfyw7G6Z2vLlaLI2nz9MMAowA==
|
integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==
|
||||||
|
|
||||||
esbuild-linux-64@0.13.14:
|
esbuild-linux-32@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.14.tgz#7fd56851b2982fdd0cd8447ee9858c2c5711708a"
|
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5"
|
||||||
integrity sha512-yPZSoMs9W2MC3Dw+6kflKt5FfQm6Dicex9dGIr1OlHRsn3Hm7yGMUTctlkW53KknnZdOdcdd5upxvbxqymczVQ==
|
integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==
|
||||||
|
|
||||||
esbuild-linux-arm64@0.13.14:
|
esbuild-linux-64@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.14.tgz#a55634d70679ba509adeafd68eebb9fd1ec5af6c"
|
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652"
|
||||||
integrity sha512-Lvo391ln9PzC334e+jJ2S0Rt0cxP47eoH5gFyv/E8HhOnEJTvm7A+RRnMjjHnejELacTTfYgFGQYPjLsi/jObQ==
|
integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
|
||||||
|
|
||||||
esbuild-linux-arm@0.13.14:
|
esbuild-linux-arm64@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.14.tgz#bb96a99677e608b31ff61f37564326d38e846ca2"
|
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b"
|
||||||
integrity sha512-8chZE4pkKRvJ/M/iwsNQ1KqsRg2RyU5eT/x2flNt/f8F2TVrDreR7I0HEeCR50wLla3B1C3wTIOzQBmjuc6uWg==
|
integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==
|
||||||
|
|
||||||
esbuild-linux-mips64le@0.13.14:
|
esbuild-linux-arm@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.14.tgz#6a55362a8fd1e593dea2ecc41877beed8b8184b9"
|
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59"
|
||||||
integrity sha512-MZhgxbmrWbpY3TOE029O6l5tokG9+Yoj2hW7vdit/d/VnmneqeGrSHADuDL6qXM8L5jaCiaivb4VhsyVCpdAbQ==
|
integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==
|
||||||
|
|
||||||
esbuild-linux-ppc64le@0.13.14:
|
esbuild-linux-mips64le@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.14.tgz#9e0048587ece0a7f184ab147f20d077098045e7f"
|
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34"
|
||||||
integrity sha512-un7KMwS7fX1Un6BjfSZxTT8L5cV/8Uf4SAhM7WYy2XF8o8TI+uRxxD03svZnRNIPsN2J5cl6qV4n7Iwz+yhhVw==
|
integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==
|
||||||
|
|
||||||
esbuild-netbsd-64@0.13.14:
|
esbuild-linux-ppc64le@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.14.tgz#dcab16a4bbcfa16e2e8535dadc5f64fdc891c63b"
|
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e"
|
||||||
integrity sha512-5ekKx/YbOmmlTeNxBjh38Uh5TGn5C4uyqN17i67k18pS3J+U2hTVD7rCxcFcRS1AjNWumkVL3jWqYXadFwMS0Q==
|
integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==
|
||||||
|
|
||||||
esbuild-openbsd-64@0.13.14:
|
esbuild-linux-riscv64@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.14.tgz#3c7453b155ebb68dc34d5aec3bd6505337bdda08"
|
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8"
|
||||||
integrity sha512-9bzvwewHjct2Cv5XcVoE1yW5YTW12Sk838EYfA46abgnhxGoFSD1mFcaztp5HHC43AsF+hQxbSFG/RilONARUA==
|
integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==
|
||||||
|
|
||||||
esbuild-sunos-64@0.13.14:
|
esbuild-linux-s390x@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.14.tgz#85addf5fef6b5db154a955d4f2e88953359d75ce"
|
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6"
|
||||||
integrity sha512-mjMrZB76M6FmoiTvj/RGWilrioR7gVwtFBRVugr9qLarXMIU1W/pQx+ieEOtflrW61xo8w1fcxyHsVVGRvoQ0w==
|
integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==
|
||||||
|
|
||||||
esbuild-windows-32@0.13.14:
|
esbuild-netbsd-64@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.14.tgz#f77f98f30a5c636c44db2428ecdf9bcbbaedb1a7"
|
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81"
|
||||||
integrity sha512-GZa6mrx2rgfbH/5uHg0Rdw50TuOKbdoKCpEBitzmG5tsXBdce+cOL+iFO5joZc6fDVCLW3Y6tjxmSXRk/v20Hg==
|
integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==
|
||||||
|
|
||||||
esbuild-windows-64@0.13.14:
|
esbuild-openbsd-64@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.14.tgz#bc778674c40d65150d12385e0f23eb3a0badbd0d"
|
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b"
|
||||||
integrity sha512-Lsgqah24bT7ClHjLp/Pj3A9wxjhIAJyWQcrOV4jqXAFikmrp2CspA8IkJgw7HFjx6QrJuhpcKVbCAe/xw0i2yw==
|
integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==
|
||||||
|
|
||||||
esbuild-windows-arm64@0.13.14:
|
esbuild-sunos-64@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.14.tgz#91a8dad35ab2c4dd27cd83860742955b25a354d7"
|
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da"
|
||||||
integrity sha512-KP8FHVlWGhM7nzYtURsGnskXb/cBCPTfj0gOKfjKq2tHtYnhDZywsUG57nk7TKhhK0fL11LcejHG3LRW9RF/9A==
|
integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==
|
||||||
|
|
||||||
esbuild@^0.13.2:
|
esbuild-windows-32@0.14.54:
|
||||||
version "0.13.14"
|
version "0.14.54"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.14.tgz#98a3f7f42809abdc2b57c84565d0f713382dc1a5"
|
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31"
|
||||||
integrity sha512-xu4D+1ji9x53ocuomcY+KOrwAnWzhBu/wTEjpdgZ8I1c8i5vboYIeigMdzgY1UowYBKa2vZgVgUB32bu7gkxeg==
|
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:
|
optionalDependencies:
|
||||||
esbuild-android-arm64 "0.13.14"
|
"@esbuild/linux-loong64" "0.14.54"
|
||||||
esbuild-darwin-64 "0.13.14"
|
esbuild-android-64 "0.14.54"
|
||||||
esbuild-darwin-arm64 "0.13.14"
|
esbuild-android-arm64 "0.14.54"
|
||||||
esbuild-freebsd-64 "0.13.14"
|
esbuild-darwin-64 "0.14.54"
|
||||||
esbuild-freebsd-arm64 "0.13.14"
|
esbuild-darwin-arm64 "0.14.54"
|
||||||
esbuild-linux-32 "0.13.14"
|
esbuild-freebsd-64 "0.14.54"
|
||||||
esbuild-linux-64 "0.13.14"
|
esbuild-freebsd-arm64 "0.14.54"
|
||||||
esbuild-linux-arm "0.13.14"
|
esbuild-linux-32 "0.14.54"
|
||||||
esbuild-linux-arm64 "0.13.14"
|
esbuild-linux-64 "0.14.54"
|
||||||
esbuild-linux-mips64le "0.13.14"
|
esbuild-linux-arm "0.14.54"
|
||||||
esbuild-linux-ppc64le "0.13.14"
|
esbuild-linux-arm64 "0.14.54"
|
||||||
esbuild-netbsd-64 "0.13.14"
|
esbuild-linux-mips64le "0.14.54"
|
||||||
esbuild-openbsd-64 "0.13.14"
|
esbuild-linux-ppc64le "0.14.54"
|
||||||
esbuild-sunos-64 "0.13.14"
|
esbuild-linux-riscv64 "0.14.54"
|
||||||
esbuild-windows-32 "0.13.14"
|
esbuild-linux-s390x "0.14.54"
|
||||||
esbuild-windows-64 "0.13.14"
|
esbuild-netbsd-64 "0.14.54"
|
||||||
esbuild-windows-arm64 "0.13.14"
|
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:
|
escalade@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
|
@ -3519,6 +3550,13 @@ is-core-module@^2.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
has "^1.0.3"
|
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:
|
is-data-descriptor@^0.1.4:
|
||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
|
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"
|
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
||||||
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
|
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
|
||||||
|
|
||||||
kleur@^4.1.4:
|
kleur@^4.1.5:
|
||||||
version "4.1.4"
|
version "4.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d"
|
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
|
||||||
integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==
|
integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
|
||||||
|
|
||||||
lazy-ass@1.6.0, lazy-ass@^1.6.0:
|
lazy-ass@1.6.0, lazy-ass@^1.6.0:
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
|
@ -4425,6 +4463,13 @@ magic-string@^0.25.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
sourcemap-codec "^1.4.4"
|
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:
|
make-dir@^3.0.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
|
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"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
|
||||||
integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==
|
integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==
|
||||||
|
|
||||||
nanoid@^3.1.30:
|
nanoid@^3.3.4:
|
||||||
version "3.1.30"
|
version "3.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
|
||||||
integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==
|
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
|
||||||
|
|
||||||
nanomatch@^1.2.9:
|
nanomatch@^1.2.9:
|
||||||
version "1.2.13"
|
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"
|
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
||||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||||
|
|
||||||
path-parse@^1.0.6:
|
path-parse@^1.0.6, path-parse@^1.0.7:
|
||||||
version "1.0.7"
|
version "1.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
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"
|
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
||||||
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
|
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
|
||||||
|
|
||||||
postcss@^8.3.8:
|
postcss@^8.4.16:
|
||||||
version "8.3.11"
|
version "8.4.16"
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.11.tgz#c3beca7ea811cd5e1c4a3ec6d2e7599ef1f8f858"
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
|
||||||
integrity sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==
|
integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid "^3.1.30"
|
nanoid "^3.3.4"
|
||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
source-map-js "^0.6.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
posthog-js@1.4.5:
|
posthog-js@1.4.5:
|
||||||
version "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"
|
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
|
||||||
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
|
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:
|
resolve-cwd@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
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"
|
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
|
||||||
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
|
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"
|
version "1.20.0"
|
||||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
|
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
|
||||||
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
|
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"
|
is-core-module "^2.2.0"
|
||||||
path-parse "^1.0.6"
|
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:
|
restore-cursor@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
|
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
|
||||||
|
@ -5262,7 +5311,14 @@ rollup-pluginutils@^2.8.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
estree-walker "^0.6.1"
|
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"
|
version "2.60.0"
|
||||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.60.0.tgz#4ee60ab7bdd0356763f87d7099f413e5460fc193"
|
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.60.0.tgz#4ee60ab7bdd0356763f87d7099f413e5460fc193"
|
||||||
integrity sha512-cHdv9GWd58v58rdseC8e8XIaPUo8a9cgZpnCMMDGZFDZKEODOiPPEQFXLriWr/TjXzhPPmG5bkAztPsOARIcGQ==
|
integrity sha512-cHdv9GWd58v58rdseC8e8XIaPUo8a9cgZpnCMMDGZFDZKEODOiPPEQFXLriWr/TjXzhPPmG5bkAztPsOARIcGQ==
|
||||||
|
@ -5475,10 +5531,10 @@ snapdragon@^0.8.1:
|
||||||
source-map-resolve "^0.5.0"
|
source-map-resolve "^0.5.0"
|
||||||
use "^3.1.0"
|
use "^3.1.0"
|
||||||
|
|
||||||
source-map-js@^0.6.2:
|
source-map-js@^1.0.2:
|
||||||
version "0.6.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||||
integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
|
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||||
|
|
||||||
source-map-resolve@^0.5.0:
|
source-map-resolve@^0.5.0:
|
||||||
version "0.5.3"
|
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"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||||
|
|
||||||
sourcemap-codec@^1.4.4:
|
sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8:
|
||||||
version "1.4.8"
|
version "1.4.8"
|
||||||
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
||||||
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
||||||
|
@ -5707,15 +5763,20 @@ supports-hyperlinks@^2.0.0:
|
||||||
has-flag "^4.0.0"
|
has-flag "^4.0.0"
|
||||||
supports-color "^7.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:
|
svelte-dnd-action@^0.9.8:
|
||||||
version "0.9.12"
|
version "0.9.12"
|
||||||
resolved "https://registry.yarnpkg.com/svelte-dnd-action/-/svelte-dnd-action-0.9.12.tgz#78cf33097986488c6d069eca517af473cd998730"
|
resolved "https://registry.yarnpkg.com/svelte-dnd-action/-/svelte-dnd-action-0.9.12.tgz#78cf33097986488c6d069eca517af473cd998730"
|
||||||
integrity sha512-GlXIB3/56IMR5A0+qUx+FX7Q7n8uCAIeuYdgSBmn9iOlxWc+mgM8P1kNwAKCMSTdQ4IQETVQILNgWVY1KIFzsg==
|
integrity sha512-GlXIB3/56IMR5A0+qUx+FX7Q7n8uCAIeuYdgSBmn9iOlxWc+mgM8P1kNwAKCMSTdQ4IQETVQILNgWVY1KIFzsg==
|
||||||
|
|
||||||
svelte-hmr@^0.14.7:
|
svelte-hmr@^0.14.12:
|
||||||
version "0.14.7"
|
version "0.14.12"
|
||||||
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.14.7.tgz#7fa8261c7b225d9409f0a86f3b9ea5c3ca6f6607"
|
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.14.12.tgz#a127aec02f1896500b10148b2d4d21ddde39973f"
|
||||||
integrity sha512-pDrzgcWSoMaK6AJkBWkmgIsecW0GChxYZSZieIYfCP0v2oPyx2CYU/zm7TBIcjLVUPP714WxmViE9Thht4etog==
|
integrity sha512-4QSW/VvXuqVcFZ+RhxiR8/newmwOCTlbYIezvkeN6302YFRE8cXy0naamHcjz8Y9Ce3ITTZtrHrIL0AGfyo61w==
|
||||||
|
|
||||||
svelte-jester@^1.3.2:
|
svelte-jester@^1.3.2:
|
||||||
version "1.8.2"
|
version "1.8.2"
|
||||||
|
@ -6081,15 +6142,15 @@ verror@1.10.0:
|
||||||
core-util-is "1.0.2"
|
core-util-is "1.0.2"
|
||||||
extsprintf "^1.2.0"
|
extsprintf "^1.2.0"
|
||||||
|
|
||||||
vite@^2.1.5:
|
vite@^3.0.8:
|
||||||
version "2.6.14"
|
version "3.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-2.6.14.tgz#35c09a15e4df823410819a2a239ab11efb186271"
|
resolved "https://registry.yarnpkg.com/vite/-/vite-3.0.8.tgz#aa095ad8e3e5da46d9ec7e878f262678965d6531"
|
||||||
integrity sha512-2HA9xGyi+EhY2MXo0+A2dRsqsAG3eFNEVIo12olkWhOmc8LfiM+eMdrXf+Ruje9gdXgvSqjLI9freec1RUM5EA==
|
integrity sha512-AOZ4eN7mrkJiOLuw8IA7piS4IdOQyQCA81GxGsAQvAZzMRi9ZwGB3TOaYsj4uLAWK46T5L4AfQ6InNGlxX30IQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild "^0.13.2"
|
esbuild "^0.14.47"
|
||||||
postcss "^8.3.8"
|
postcss "^8.4.16"
|
||||||
resolve "^1.20.0"
|
resolve "^1.22.1"
|
||||||
rollup "^2.57.0"
|
rollup ">=2.75.6 <2.77.0 || ~2.77.0"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/cli",
|
"name": "@budibase/cli",
|
||||||
"version": "1.2.44-alpha.2",
|
"version": "1.3.12-alpha.3",
|
||||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -26,14 +26,18 @@
|
||||||
"outputPath": "build"
|
"outputPath": "build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/backend-core": "1.2.41-alpha.5",
|
"@budibase/backend-core": "1.3.12-alpha.3",
|
||||||
|
"@budibase/string-templates": "1.3.12-alpha.3",
|
||||||
|
"@budibase/types": "1.3.12-alpha.3",
|
||||||
"axios": "0.21.2",
|
"axios": "0.21.2",
|
||||||
"chalk": "4.1.0",
|
"chalk": "4.1.0",
|
||||||
"cli-progress": "3.11.2",
|
"cli-progress": "3.11.2",
|
||||||
"commander": "7.1.0",
|
"commander": "7.1.0",
|
||||||
"docker-compose": "0.23.6",
|
"docker-compose": "0.23.6",
|
||||||
"dotenv": "16.0.1",
|
"dotenv": "16.0.1",
|
||||||
|
"download": "^8.0.0",
|
||||||
"inquirer": "8.0.0",
|
"inquirer": "8.0.0",
|
||||||
|
"joi": "^17.6.0",
|
||||||
"lookpath": "1.1.0",
|
"lookpath": "1.1.0",
|
||||||
"node-fetch": "2",
|
"node-fetch": "2",
|
||||||
"pkg": "5.7.0",
|
"pkg": "5.7.0",
|
||||||
|
|
|
@ -3,6 +3,7 @@ exports.CommandWords = {
|
||||||
HOSTING: "hosting",
|
HOSTING: "hosting",
|
||||||
ANALYTICS: "analytics",
|
ANALYTICS: "analytics",
|
||||||
HELP: "help",
|
HELP: "help",
|
||||||
|
PLUGIN: "plugins",
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.InitTypes = {
|
exports.InitTypes = {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
process.env.NO_JS = "1"
|
|
@ -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)
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue