Merge branch 'develop' into api-tets-public-api-key-generation

This commit is contained in:
Pedro Silva 2023-03-09 15:42:50 +00:00
commit 781ff726f0
154 changed files with 3246 additions and 1283 deletions

View File

@ -2,10 +2,11 @@
name: Bug report name: Bug report
about: Create a report to help us improve about: Create a report to help us improve
title: '' title: ''
labels: bug labels: bug, linear
assignees: '' assignees: ''
--- ---
**Checklist** **Checklist**
- [ ] I have searched budibase discussions and github issues to check if my issue already exists - [ ] I have searched budibase discussions and github issues to check if my issue already exists

View File

@ -1,65 +0,0 @@
name: Budibase Deploy Preprod
on:
workflow_dispatch:
env:
INTERCOM_TOKEN: ${{ secrets.INTERCOM_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: eu-west-1
- name: Get the latest budibase release version
id: version
run: |
release_version=$(cat lerna.json | jq -r '.version')
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
- name: Pull values.yaml from budibase-infra
run: |
curl -H "Authorization: token ${{ secrets.GH_PERSONAL_TOKEN }}" \
-H 'Accept: application/vnd.github.v3.raw' \
-o values.preprod.yaml \
-L https://api.github.com/repos/budibase/budibase-infra/contents/kubernetes/budibase-preprod/values.yaml
wc -l values.preprod.yaml
- name: Deploy to Preprod Environment
uses: glopezep/helm@v1.7.1
with:
release: budibase-preprod
namespace: budibase
chart: charts/budibase
token: ${{ github.token }}
helm: helm3
values: |
globals:
appVersion: v${{ env.RELEASE_VERSION }}
ingress:
enabled: true
nginx: true
value-files: >-
[
"values.preprod.yaml"
]
env:
KUBECONFIG_FILE: '${{ secrets.PREPROD_KUBECONFIG }}'
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v4.0.0
with:
webhook-url: ${{ secrets.PROD_DEPLOY_WEBHOOK_URL }}
content: "Preprod Deployment Complete: ${{ env.RELEASE_VERSION }} deployed to Budibase Pre-prod."
embed-title: ${{ env.RELEASE_VERSION }}

View File

@ -1,88 +0,0 @@
name: Budibase Deploy Release
on:
workflow_dispatch:
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: eu-west-1
- name: Fail if branch is not develop
if: github.ref != 'refs/heads/develop'
run: |
echo "Ref is not develop, you must run this job from develop."
exit 1
- name: Get the latest budibase release version
id: version
run: |
release_version=$(cat lerna.json | jq -r '.version')
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
- name: Pull values.yaml from budibase-infra
run: |
curl -H "Authorization: token ${{ secrets.GH_PERSONAL_TOKEN }}" \
-H 'Accept: application/vnd.github.v3.raw' \
-o values.release.yaml \
-L https://api.github.com/repos/budibase/budibase-infra/contents/kubernetes/budibase-release/values.yaml
wc -l values.release.yaml
- name: Deploy to Release Environment
uses: glopezep/helm@v1.7.1
with:
release: budibase-release
namespace: budibase
chart: charts/budibase
token: ${{ github.token }}
helm: helm3
values: |
globals:
appVersion: develop
ingress:
enabled: true
nginx: true
value-files: >-
[
"values.release.yaml"
]
env:
KUBECONFIG_FILE: '${{ secrets.RELEASE_KUBECONFIG }}'
- name: Re roll app-service
uses: actions-hub/kubectl@master
env:
KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG_BASE64 }}
with:
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
uses: tsickert/discord-webhook@v4.0.0
with:
webhook-url: ${{ secrets.PROD_DEPLOY_WEBHOOK_URL }}
content: "Release Env Deployment Complete: ${{ env.RELEASE_VERSION }} deployed to Budibase Release Env."
embed-title: ${{ env.RELEASE_VERSION }}

View File

@ -117,4 +117,4 @@ jobs:
with: with:
repository: budibase/budibase-deploys repository: budibase/budibase-deploys
event: budicloud-qa-deploy event: budicloud-qa-deploy
github_pat: ${{ secrets.GH_ACCESS_TOKEN }} github_pat: ${{ secrets.GH_ACCESS_TOKEN }}

View File

@ -35,9 +35,8 @@ env:
PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }} PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }}
jobs: jobs:
release: release-images:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Fail if branch is not master - name: Fail if branch is not master
if: github.ref != 'refs/heads/master' if: github.ref != 'refs/heads/master'
@ -57,14 +56,6 @@ jobs:
- run: yarn lint - run: yarn lint
- run: yarn build - run: yarn build
- run: yarn build:sdk - run: yarn build:sdk
- run: yarn test
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: eu-west-1
- name: Publish budibase packages to NPM - name: Publish budibase packages to NPM
env: env:
@ -90,46 +81,59 @@ jobs:
DOCKER_USER: ${{ secrets.DOCKER_USERNAME }} DOCKER_USER: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }} DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }}
BUDIBASE_RELEASE_VERSION: ${{ steps.previoustag.outputs.tag }} BUDIBASE_RELEASE_VERSION: ${{ steps.previoustag.outputs.tag }}
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: eu-west-1
- name: Pull values.yaml from budibase-infra release-helm-chart:
run: | needs: [release-images]
curl -H "Authorization: token ${{ secrets.GH_PERSONAL_TOKEN }}" \ runs-on: ubuntu-latest
-H 'Accept: application/vnd.github.v3.raw' \ steps:
-o values.preprod.yaml \ - uses: actions/checkout@v2
-L https://api.github.com/repos/budibase/budibase-infra/contents/kubernetes/budibase-preprod/values.yaml - name: Setup Helm
wc -l values.preprod.yaml uses: azure/setup-helm@v1
id: helm-install
- name: Deploy to Preprod Environment - name: 'Get Previous tag'
uses: glopezep/helm@v1.7.1 id: previoustag
with: uses: "WyriHaximus/github-action-get-previous-tag@v1"
release: budibase-preprod
namespace: budibase # due to helm repo index issue: https://github.com/helm/helm/issues/7363
chart: charts/budibase # we need to create new package in a different dir, merge the index and move the package back
token: ${{ github.token }} - name: Build and release helm chart
helm: helm3 run: |
values: | git config user.name "Budibase Helm Bot"
globals: git config user.email "<>"
appVersion: ${{ steps.previoustag.outputs.tag }} git reset --hard
ingress: git pull
enabled: true mkdir sync
nginx: true echo "Packaging chart to sync dir"
value-files: >- helm package charts/budibase --version 0.0.0-master --app-version "$RELEASE_VERSION" --destination sync
[ echo "Packaging successful"
"values.preprod.yaml" git checkout gh-pages
] echo "Indexing helm repo"
helm repo index --merge docs/index.yaml sync
mv -f sync/* docs
rm -rf sync
echo "Pushing new helm release"
git add -A
git commit -m "Helm Release: ${{ env.RELEASE_VERSION }}"
git push
env: env:
KUBECONFIG_FILE: '${{ secrets.PREPROD_KUBECONFIG }}' RELEASE_VERSION: ${{ steps.previoustag.outputs.tag }}
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v4.0.0 # Trigger deploy to new EKS preprod environment
trigger-deploy-to-preprod-env:
needs: [release-helm-chart]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: 'Get Previous tag'
id: previoustag
uses: "WyriHaximus/github-action-get-previous-tag@v1"
- uses: passeidireto/trigger-external-workflow-action@main
env:
PAYLOAD_VERSION: ${{ steps.previoustag.outputs.tag }}
with: with:
webhook-url: ${{ secrets.PROD_DEPLOY_WEBHOOK_URL }} repository: budibase/budibase-deploys
content: "Preprod Deployment Complete: ${{ steps.previoustag.outputs.tag }} deployed to Budibase Pre-prod." event: budicloud-preprod-deploy
embed-title: ${{ steps.previoustag.outputs.tag }} github_pat: ${{ secrets.GH_ACCESS_TOKEN }}

View File

@ -16,9 +16,13 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
node-version: 14.x
fetch_depth: 0 fetch_depth: 0
- name: Use Node.js 14.x
uses: actions/setup-node@v1
with:
node-version: 14.x
- name: Get the latest budibase release version - name: Get the latest budibase release version
id: version id: version
run: | run: |

View File

@ -1,31 +0,0 @@
name: Budibase Nightly Tests
on:
workflow_dispatch:
schedule:
- cron: "0 5 * * *" # every day at 5AM
jobs:
nightly:
runs-on: [self-hosted, qa]
steps:
- uses: actions/checkout@v2
- name: Use Node.js 14.x
uses: actions/setup-node@v1
with:
node-version: 14.x
- name: QA Core Integration Tests
run: |
cd qa-core
yarn
yarn api:test:ci
env:
BUDIBASE_HOST: budicloud.qa.budibase.net
BUDIBASE_ACCOUNTS_URL: https://account-portal.budicloud.qa.budibase.net
- name: Cypress Discord Notify
run: yarn test:notify
env:
WEBHOOK_URL: ${{ secrets.BUDI_QA_WEBHOOK }}
GITHUB_RUN_URL: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID

View File

@ -51,6 +51,14 @@ spec:
value: {{ tpl .Values.services.proxy.upstreams.minio . | quote }} value: {{ tpl .Values.services.proxy.upstreams.minio . | quote }}
- name: COUCHDB_UPSTREAM_URL - name: COUCHDB_UPSTREAM_URL
value: {{ .Values.services.couchdb.url | default (tpl .Values.services.proxy.upstreams.couchdb .) | quote }} value: {{ .Values.services.couchdb.url | default (tpl .Values.services.proxy.upstreams.couchdb .) | quote }}
{{ if .Values.services.proxy.proxyRateLimitWebhooksPerSecond }}
- name: PROXY_RATE_LIMIT_WEBHOOKS_PER_SECOND
value: {{ .Values.services.proxy.proxyRateLimitWebhooksPerSecond | quote }}
{{ end }}
{{ if .Values.services.proxy.proxyRateLimitApiPerSecond }}
- name: PROXY_RATE_LIMIT_API_PER_SECOND
value: {{ .Values.services.proxy.proxyRateLimitApiPerSecond | quote }}
{{ end }}
- name: RESOLVER - name: RESOLVER
{{ if .Values.services.proxy.resolver }} {{ if .Values.services.proxy.resolver }}
value: {{ .Values.services.proxy.resolver }} value: {{ .Values.services.proxy.resolver }}

View File

@ -245,7 +245,7 @@ couchdb:
## The CouchDB image ## The CouchDB image
image: image:
repository: couchdb repository: couchdb
tag: 3.2.1 tag: 3.1.1
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
## Experimental integration with Lucene-powered fulltext search ## Experimental integration with Lucene-powered fulltext search

View File

@ -52,4 +52,14 @@ So this command will actually run the application in dev mode. It creates .env f
The dev version will be available on port 10000 i.e. The dev version will be available on port 10000 i.e.
http://127.0.0.1:10000/builder/admin http://127.0.0.1:10000/builder/admin
### File descriptor issues with Vite and Chrome in Linux
If your dev environment stalls forever, with some network requests stuck in flight, it's likely that Chrome is trying to open more file descriptors than your system allows.
To fix this, apply the following tweaks.
Debian based distros:
Add `* - nofile 65536` to `/etc/security/limits.conf`.
Arch:
Add `DefaultLimitNOFILE=65536` to `/etc/systemd/system.conf`.

View File

@ -6,8 +6,7 @@ services:
minio-service: minio-service:
container_name: budi-minio-dev container_name: budi-minio-dev
restart: on-failure restart: on-failure
# Last version that supports the "fs" backend image: minio/minio
image: minio/minio:RELEASE.2022-10-24T18-35-07Z
volumes: volumes:
- minio_data:/data - minio_data:/data
ports: ports:
@ -69,4 +68,4 @@ volumes:
minio_data: minio_data:
driver: local driver: local
redis_data: redis_data:
driver: local driver: local

View File

@ -55,12 +55,12 @@ http {
set $csp_style "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://rsms.me https://maxcdn.bootstrapcdn.com"; set $csp_style "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://rsms.me https://maxcdn.bootstrapcdn.com";
set $csp_object "object-src 'none'"; set $csp_object "object-src 'none'";
set $csp_base_uri "base-uri 'self'"; set $csp_base_uri "base-uri 'self'";
set $csp_connect "connect-src 'self' https://*.budibase.net https://api-iam.intercom.io https://api-iam.intercom.io https://api-ping.intercom.io https://app.posthog.com wss://nexus-websocket-a.intercom.io wss://nexus-websocket-b.intercom.io https://nexus-websocket-a.intercom.io https://nexus-websocket-b.intercom.io https://uploads.intercomcdn.com https://uploads.intercomusercontent.com https://*.s3.*.amazonaws.com https://s3.*.amazonaws.com https://api.github.com"; set $csp_connect "connect-src 'self' https://*.budibase.net https://api-iam.intercom.io https://api-iam.intercom.io https://api-ping.intercom.io https://app.posthog.com wss://nexus-websocket-a.intercom.io wss://nexus-websocket-b.intercom.io https://nexus-websocket-a.intercom.io https://nexus-websocket-b.intercom.io https://uploads.intercomcdn.com https://uploads.intercomusercontent.com https://*.s3.amazonaws.com https://*.s3.us-east-2.amazonaws.com https://*.s3.us-east-1.amazonaws.com https://*.s3.us-west-1.amazonaws.com https://*.s3.us-west-2.amazonaws.com https://*.s3.af-south-1.amazonaws.com https://*.s3.ap-east-1.amazonaws.com https://*.s3.ap-southeast-3.amazonaws.com https://*.s3.ap-south-1.amazonaws.com https://*.s3.ap-northeast-3.amazonaws.com https://*.s3.ap-northeast-2.amazonaws.com https://*.s3.ap-southeast-1.amazonaws.com https://*.s3.ap-southeast-2.amazonaws.com https://*.s3.ap-northeast-1.amazonaws.com https://*.s3.ca-central-1.amazonaws.com https://*.s3.cn-north-1.amazonaws.com https://*.s3.cn-northwest-1.amazonaws.com https://*.s3.eu-central-1.amazonaws.com https://*.s3.eu-west-1.amazonaws.com https://*.s3.eu-west-2.amazonaws.com https://*.s3.eu-south-1.amazonaws.com https://*.s3.eu-west-3.amazonaws.com https://*.s3.eu-north-1.amazonaws.com https://*.s3.sa-east-1.amazonaws.com https://*.s3.me-south-1.amazonaws.com https://*.s3.us-gov-east-1.amazonaws.com https://*.s3.us-gov-west-1.amazonaws.com https://api.github.com";
set $csp_font "font-src 'self' data: https://cdn.jsdelivr.net https://fonts.gstatic.com https://rsms.me https://maxcdn.bootstrapcdn.com https://js.intercomcdn.com https://fonts.intercomcdn.com"; set $csp_font "font-src 'self' data: https://cdn.jsdelivr.net https://fonts.gstatic.com https://rsms.me https://maxcdn.bootstrapcdn.com https://js.intercomcdn.com https://fonts.intercomcdn.com";
set $csp_frame "frame-src 'self' https:"; set $csp_frame "frame-src 'self' https:";
set $csp_img "img-src http: https: data: blob:"; set $csp_img "img-src http: https: data: blob:";
set $csp_manifest "manifest-src 'self'"; set $csp_manifest "manifest-src 'self'";
set $csp_media "media-src 'self' https://js.intercomcdn.com"; set $csp_media "media-src 'self' https://js.intercomcdn.com https://cdn.budi.live";
set $csp_worker "worker-src 'none'"; set $csp_worker "worker-src 'none'";
error_page 502 503 504 /error.html; error_page 502 503 504 /error.html;

View File

@ -1,5 +1,5 @@
{ {
"version": "2.3.18-alpha.29", "version": "2.4.8-alpha.4",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/backend-core", "name": "@budibase/backend-core",
"version": "2.3.18-alpha.29", "version": "2.4.8-alpha.4",
"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",
@ -22,9 +22,9 @@
"test:watch": "jest --watchAll" "test:watch": "jest --watchAll"
}, },
"dependencies": { "dependencies": {
"@budibase/nano": "10.1.1", "@budibase/nano": "10.1.2",
"@budibase/pouchdb-replication-stream": "1.2.10", "@budibase/pouchdb-replication-stream": "1.2.10",
"@budibase/types": "2.3.18-alpha.29", "@budibase/types": "2.4.8-alpha.4",
"@shopify/jest-koa-mocks": "5.0.1", "@shopify/jest-koa-mocks": "5.0.1",
"@techpass/passport-openidconnect": "0.3.2", "@techpass/passport-openidconnect": "0.3.2",
"aws-cloudfront-sign": "2.2.0", "aws-cloudfront-sign": "2.2.0",

View File

@ -28,6 +28,7 @@ import * as events from "../events"
import * as configs from "../configs" import * as configs from "../configs"
import { clearCookie, getCookie } from "../utils" import { clearCookie, getCookie } from "../utils"
import { ssoSaveUserNoOp } from "../middleware/passport/sso/sso" import { ssoSaveUserNoOp } from "../middleware/passport/sso/sso"
import env from "../environment"
const refresh = require("passport-oauth2-refresh") const refresh = require("passport-oauth2-refresh")
export { export {
@ -52,7 +53,7 @@ export const jwt = require("jsonwebtoken")
_passport.use(new LocalStrategy(local.options, local.authenticate)) _passport.use(new LocalStrategy(local.options, local.authenticate))
if (jwtPassport.options.secretOrKey) { if (jwtPassport.options.secretOrKey) {
_passport.use(new JwtStrategy(jwtPassport.options, jwtPassport.authenticate)) _passport.use(new JwtStrategy(jwtPassport.options, jwtPassport.authenticate))
} else { } else if (!env.DISABLE_JWT_WARNING) {
logAlert("No JWT Secret supplied, cannot configure JWT strategy") logAlert("No JWT Secret supplied, cannot configure JWT strategy")
} }

View File

@ -1,10 +1,13 @@
import { structures, DBTestConfiguration } from "../../../tests" import {
structures,
DBTestConfiguration,
expectFunctionWasCalledTimesWith,
} from "../../../tests"
import { Writethrough } from "../writethrough" import { Writethrough } from "../writethrough"
import { getDB } from "../../db" import { getDB } from "../../db"
import tk from "timekeeper" import tk from "timekeeper"
const START_DATE = Date.now() tk.freeze(Date.now())
tk.freeze(START_DATE)
const DELAY = 5000 const DELAY = 5000
@ -17,34 +20,99 @@ describe("writethrough", () => {
const writethrough = new Writethrough(db, DELAY) const writethrough = new Writethrough(db, DELAY)
const writethrough2 = new Writethrough(db2, DELAY) const writethrough2 = new Writethrough(db2, DELAY)
const docId = structures.uuid()
beforeEach(() => {
jest.clearAllMocks()
})
describe("put", () => { describe("put", () => {
let first: any let current: any
it("should be able to store, will go to DB", async () => { it("should be able to store, will go to DB", async () => {
await config.doInTenant(async () => { await config.doInTenant(async () => {
const response = await writethrough.put({ _id: "test", value: 1 }) const response = await writethrough.put({
_id: docId,
value: 1,
})
const output = await db.get(response.id) const output = await db.get(response.id)
first = output current = output
expect(output.value).toBe(1) expect(output.value).toBe(1)
}) })
}) })
it("second put shouldn't update DB", async () => { it("second put shouldn't update DB", async () => {
await config.doInTenant(async () => { await config.doInTenant(async () => {
const response = await writethrough.put({ ...first, value: 2 }) const response = await writethrough.put({ ...current, value: 2 })
const output = await db.get(response.id) const output = await db.get(response.id)
expect(first._rev).toBe(output._rev) expect(current._rev).toBe(output._rev)
expect(output.value).toBe(1) expect(output.value).toBe(1)
}) })
}) })
it("should put it again after delay period", async () => { it("should put it again after delay period", async () => {
await config.doInTenant(async () => { await config.doInTenant(async () => {
tk.freeze(START_DATE + DELAY + 1) tk.freeze(Date.now() + DELAY + 1)
const response = await writethrough.put({ ...first, value: 3 }) const response = await writethrough.put({ ...current, value: 3 })
const output = await db.get(response.id) const output = await db.get(response.id)
expect(response.rev).not.toBe(first._rev) expect(response.rev).not.toBe(current._rev)
expect(output.value).toBe(3) expect(output.value).toBe(3)
current = output
})
})
it("should handle parallel DB updates ignoring conflicts", async () => {
await config.doInTenant(async () => {
tk.freeze(Date.now() + DELAY + 1)
const responses = await Promise.all([
writethrough.put({ ...current, value: 4 }),
writethrough.put({ ...current, value: 4 }),
writethrough.put({ ...current, value: 4 }),
])
const newRev = responses.map(x => x.rev).find(x => x !== current._rev)
expect(newRev).toBeDefined()
expect(responses.map(x => x.rev)).toEqual(
expect.arrayContaining([current._rev, current._rev, newRev])
)
expectFunctionWasCalledTimesWith(
console.warn,
2,
"bb-warn: Ignoring redlock conflict in write-through cache"
)
const output = await db.get(current._id)
expect(output.value).toBe(4)
expect(output._rev).toBe(newRev)
current = output
})
})
it("should handle updates with documents falling behind", async () => {
await config.doInTenant(async () => {
tk.freeze(Date.now() + DELAY + 1)
const id = structures.uuid()
await writethrough.put({ _id: id, value: 1 })
const doc = await writethrough.get(id)
// Updating document
tk.freeze(Date.now() + DELAY + 1)
await writethrough.put({ ...doc, value: 2 })
// Update with the old rev value
tk.freeze(Date.now() + DELAY + 1)
const res = await writethrough.put({
...doc,
value: 3,
})
expect(res.ok).toBe(true)
const output = await db.get(id)
expect(output.value).toBe(3)
expect(output._rev).toBe(res.rev)
}) })
}) })
}) })
@ -52,8 +120,8 @@ describe("writethrough", () => {
describe("get", () => { describe("get", () => {
it("should be able to retrieve", async () => { it("should be able to retrieve", async () => {
await config.doInTenant(async () => { await config.doInTenant(async () => {
const response = await writethrough.get("test") const response = await writethrough.get(docId)
expect(response.value).toBe(3) expect(response.value).toBe(4)
}) })
}) })
}) })

View File

@ -1,7 +1,8 @@
import BaseCache from "./base" import BaseCache from "./base"
import { getWritethroughClient } from "../redis/init" import { getWritethroughClient } from "../redis/init"
import { logWarn } from "../logging" import { logWarn } from "../logging"
import { Database } from "@budibase/types" import { Database, Document, LockName, LockType } from "@budibase/types"
import * as locks from "../redis/redlockImpl"
const DEFAULT_WRITE_RATE_MS = 10000 const DEFAULT_WRITE_RATE_MS = 10000
let CACHE: BaseCache | null = null let CACHE: BaseCache | null = null
@ -27,44 +28,62 @@ function makeCacheItem(doc: any, lastWrite: number | null = null): CacheItem {
return { doc, lastWrite: lastWrite || Date.now() } return { doc, lastWrite: lastWrite || Date.now() }
} }
export async function put( async function put(
db: Database, db: Database,
doc: any, doc: Document,
writeRateMs: number = DEFAULT_WRITE_RATE_MS writeRateMs: number = DEFAULT_WRITE_RATE_MS
) { ) {
const cache = await getCache() const cache = await getCache()
const key = doc._id const key = doc._id
let cacheItem: CacheItem | undefined = await cache.get(makeCacheKey(db, key)) let cacheItem: CacheItem | undefined
if (key) {
cacheItem = await cache.get(makeCacheKey(db, key))
}
const updateDb = !cacheItem || cacheItem.lastWrite < Date.now() - writeRateMs const updateDb = !cacheItem || cacheItem.lastWrite < Date.now() - writeRateMs
let output = doc let output = doc
if (updateDb) { if (updateDb) {
const writeDb = async (toWrite: any) => { const lockResponse = await locks.doWithLock(
// doc should contain the _id and _rev {
const response = await db.put(toWrite) type: LockType.TRY_ONCE,
output = { name: LockName.PERSIST_WRITETHROUGH,
...doc, resource: key,
_id: response.id, ttl: 1000,
_rev: response.rev, },
} async () => {
} const writeDb = async (toWrite: any) => {
try { // doc should contain the _id and _rev
await writeDb(doc) const response = await db.put(toWrite, { force: true })
} catch (err: any) { output = {
if (err.status !== 409) { ...doc,
throw err _id: response.id,
} else { _rev: response.rev,
// Swallow 409s but log them }
logWarn(`Ignoring conflict in write-through cache`) }
try {
await writeDb(doc)
} catch (err: any) {
if (err.status !== 409) {
throw err
} else {
// Swallow 409s but log them
logWarn(`Ignoring conflict in write-through cache`)
}
}
} }
)
if (!lockResponse.executed) {
logWarn(`Ignoring redlock conflict in write-through cache`)
} }
} }
// if we are updating the DB then need to set the lastWrite to now // if we are updating the DB then need to set the lastWrite to now
cacheItem = makeCacheItem(output, updateDb ? null : cacheItem?.lastWrite) cacheItem = makeCacheItem(output, updateDb ? null : cacheItem?.lastWrite)
await cache.store(makeCacheKey(db, key), cacheItem) if (output._id) {
await cache.store(makeCacheKey(db, output._id), cacheItem)
}
return { ok: true, id: output._id, rev: output._rev } return { ok: true, id: output._id, rev: output._rev }
} }
export async function get(db: Database, id: string): Promise<any> { async function get(db: Database, id: string): Promise<any> {
const cache = await getCache() const cache = await getCache()
const cacheKey = makeCacheKey(db, id) const cacheKey = makeCacheKey(db, id)
let cacheItem: CacheItem = await cache.get(cacheKey) let cacheItem: CacheItem = await cache.get(cacheKey)
@ -76,11 +95,7 @@ export async function get(db: Database, id: string): Promise<any> {
return cacheItem.doc return cacheItem.doc
} }
export async function remove( async function remove(db: Database, docOrId: any, rev?: any): Promise<void> {
db: Database,
docOrId: any,
rev?: any
): Promise<void> {
const cache = await getCache() const cache = await getCache()
if (!docOrId) { if (!docOrId) {
throw new Error("No ID/Rev provided.") throw new Error("No ID/Rev provided.")

View File

@ -42,7 +42,9 @@ export async function getConfig<T extends Config>(
} }
} }
export async function save(config: Config) { export async function save(
config: Config
): Promise<{ id: string; rev: string }> {
const db = context.getGlobalDB() const db = context.getGlobalDB()
return db.put(config) return db.put(config)
} }

View File

@ -94,6 +94,7 @@ const environment = {
SMTP_HOST: process.env.SMTP_HOST, SMTP_HOST: process.env.SMTP_HOST,
SMTP_PORT: parseInt(process.env.SMTP_PORT || ""), SMTP_PORT: parseInt(process.env.SMTP_PORT || ""),
SMTP_FROM_ADDRESS: process.env.SMTP_FROM_ADDRESS, SMTP_FROM_ADDRESS: process.env.SMTP_FROM_ADDRESS,
DISABLE_JWT_WARNING: process.env.DISABLE_JWT_WARNING,
/** /**
* Enable to allow an admin user to login using a password. * Enable to allow an admin user to login using a password.
* This can be useful to prevent lockout when configuring SSO. * This can be useful to prevent lockout when configuring SSO.

View File

@ -24,7 +24,7 @@ const getClient = async (type: LockType): Promise<Redlock> => {
} }
} }
export const OPTIONS = { const OPTIONS = {
TRY_ONCE: { TRY_ONCE: {
// immediately throws an error if the lock is already held // immediately throws an error if the lock is already held
retryCount: 0, retryCount: 0,
@ -56,14 +56,29 @@ export const OPTIONS = {
}, },
} }
export const newRedlock = async (opts: Options = {}) => { const newRedlock = async (opts: Options = {}) => {
let options = { ...OPTIONS.DEFAULT, ...opts } let options = { ...OPTIONS.DEFAULT, ...opts }
const redisWrapper = await getLockClient() const redisWrapper = await getLockClient()
const client = redisWrapper.getClient() const client = redisWrapper.getClient()
return new Redlock([client], options) return new Redlock([client], options)
} }
export const doWithLock = async (opts: LockOptions, task: any) => { type SuccessfulRedlockExecution<T> = {
executed: true
result: T
}
type UnsuccessfulRedlockExecution = {
executed: false
}
type RedlockExecution<T> =
| SuccessfulRedlockExecution<T>
| UnsuccessfulRedlockExecution
export const doWithLock = async <T>(
opts: LockOptions,
task: () => Promise<T>
): Promise<RedlockExecution<T>> => {
const redlock = await getClient(opts.type) const redlock = await getClient(opts.type)
let lock let lock
try { try {
@ -73,8 +88,8 @@ export const doWithLock = async (opts: LockOptions, task: any) => {
let name: string = `lock:${prefix}_${opts.name}` let name: string = `lock:${prefix}_${opts.name}`
// add additional unique name if required // add additional unique name if required
if (opts.nameSuffix) { if (opts.resource) {
name = name + `_${opts.nameSuffix}` name = name + `_${opts.resource}`
} }
// create the lock // create the lock
@ -83,7 +98,7 @@ export const doWithLock = async (opts: LockOptions, task: any) => {
// perform locked task // perform locked task
// need to await to ensure completion before unlocking // need to await to ensure completion before unlocking
const result = await task() const result = await task()
return result return { executed: true, result }
} catch (e: any) { } catch (e: any) {
console.warn("lock error") console.warn("lock error")
// lock limit exceeded // lock limit exceeded
@ -92,7 +107,7 @@ export const doWithLock = async (opts: LockOptions, task: any) => {
// don't throw for try-once locks, they will always error // don't throw for try-once locks, they will always error
// due to retry count (0) exceeded // due to retry count (0) exceeded
console.warn(e) console.warn(e)
return return { executed: false }
} else { } else {
console.error(e) console.error(e)
throw e throw e

View File

@ -4,4 +4,6 @@ export { generator } from "./structures"
export * as testEnv from "./testEnv" export * as testEnv from "./testEnv"
export * as testContainerUtils from "./testContainerUtils" export * as testContainerUtils from "./testContainerUtils"
export * from "./jestUtils"
export { default as DBTestConfiguration } from "./DBTestConfiguration" export { default as DBTestConfiguration } from "./DBTestConfiguration"

View File

@ -0,0 +1,9 @@
export function expectFunctionWasCalledTimesWith(
jestFunction: any,
times: number,
argument: any
) {
expect(
jestFunction.mock.calls.filter((call: any) => call[0] === argument).length
).toBe(times)
}

View File

@ -1,5 +1,12 @@
import { structures } from ".."
import { newid } from "../../../src/newid" import { newid } from "../../../src/newid"
export function id() { export function id() {
return `db_${newid()}` return `db_${newid()}`
} }
export function rev() {
return `${structures.generator.character({
numeric: true,
})}-${structures.uuid().replace(/-/, "")}`
}

View File

@ -475,10 +475,10 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/nano@10.1.1": "@budibase/nano@10.1.2":
version "10.1.1" version "10.1.2"
resolved "https://registry.yarnpkg.com/@budibase/nano/-/nano-10.1.1.tgz#36ccda4d9bb64b5ee14dd2b27a295b40739b1038" resolved "https://registry.yarnpkg.com/@budibase/nano/-/nano-10.1.2.tgz#10fae5a1ab39be6a81261f40e7b7ec6d21cbdd4a"
integrity sha512-kbMIzMkjVtl+xI0UPwVU0/pn8/ccxTyfzwBz6Z+ZiN2oUSb0fJCe0qwA6o8dxwSa8nZu4MbGAeMJl3CJndmWtA== integrity sha512-1w+YN2n/M5aZ9hBKCP4NEjdQbT8BfCLRizkdvm0Je665eEHw3aE1hvo8mon9Ro9QuDdxj1DfDMMFnym6/QUwpQ==
dependencies: dependencies:
"@types/tough-cookie" "^4.0.2" "@types/tough-cookie" "^4.0.2"
axios "^1.1.3" axios "^1.1.3"

View File

@ -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": "2.3.18-alpha.29", "version": "2.4.8-alpha.4",
"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,8 @@
], ],
"dependencies": { "dependencies": {
"@adobe/spectrum-css-workflow-icons": "1.2.1", "@adobe/spectrum-css-workflow-icons": "1.2.1",
"@budibase/string-templates": "2.3.18-alpha.29", "@budibase/shared-core": "2.4.8-alpha.4",
"@budibase/string-templates": "2.4.8-alpha.4",
"@spectrum-css/accordion": "3.0.24", "@spectrum-css/accordion": "3.0.24",
"@spectrum-css/actionbutton": "1.0.1", "@spectrum-css/actionbutton": "1.0.1",
"@spectrum-css/actiongroup": "1.0.1", "@spectrum-css/actiongroup": "1.0.1",

View File

@ -113,6 +113,9 @@
.spectrum-ActionButton--quiet { .spectrum-ActionButton--quiet {
padding: 0 8px; padding: 0 8px;
} }
.spectrum-ActionButton--quiet.is-selected {
color: var(--spectrum-global-color-gray-900);
}
.is-selected:not(.emphasized) .spectrum-Icon { .is-selected:not(.emphasized) .spectrum-Icon {
color: var(--spectrum-global-color-gray-900); color: var(--spectrum-global-color-gray-900);
} }

View File

@ -31,6 +31,7 @@ export default function positionDropdown(element, opts) {
styles.top = anchorBounds.top styles.top = anchorBounds.top
} else if (window.innerHeight - anchorBounds.bottom < 100) { } else if (window.innerHeight - anchorBounds.bottom < 100) {
styles.top = anchorBounds.top - elementBounds.height - offset styles.top = anchorBounds.top - elementBounds.height - offset
styles.maxHeight = 240
} else { } else {
styles.top = anchorBounds.bottom + offset styles.top = anchorBounds.bottom + offset
styles.maxHeight = window.innerHeight - anchorBounds.bottom - 20 styles.maxHeight = window.innerHeight - anchorBounds.bottom - 20

View File

@ -29,6 +29,14 @@
visible = false visible = false
} }
export function toggle() {
if (visible) {
hide()
} else {
show()
}
}
export function cancel() { export function cancel() {
if (!visible) { if (!visible) {
return return
@ -61,7 +69,7 @@
} }
} }
setContext(Context.Modal, { show, hide, cancel }) setContext(Context.Modal, { show, hide, toggle, cancel })
onMount(() => { onMount(() => {
document.addEventListener("keydown", handleKey) document.addEventListener("keydown", handleKey)

View File

@ -1,3 +1,6 @@
import { helpers } from "@budibase/shared-core"
export const deepGet = helpers.deepGet
/** /**
* Generates a DOM safe UUID. * Generates a DOM safe UUID.
* Starting with a letter is important to make it DOM safe. * Starting with a letter is important to make it DOM safe.
@ -41,30 +44,6 @@ export const hashString = string => {
return hash.toString() return hash.toString()
} }
/**
* Gets a key within an object. The key supports dot syntax for retrieving deep
* fields - e.g. "a.b.c".
* Exact matches of keys with dots in them take precedence over nested keys of
* the same path - e.g. getting "a.b" from { "a.b": "foo", a: { b: "bar" } }
* will return "foo" over "bar".
* @param obj the object
* @param key the key
* @return {*|null} the value or null if a value was not found for this key
*/
export const deepGet = (obj, key) => {
if (!obj || !key) {
return null
}
if (Object.prototype.hasOwnProperty.call(obj, key)) {
return obj[key]
}
const split = key.split(".")
for (let i = 0; i < split.length; i++) {
obj = obj?.[split[i]]
}
return obj
}
/** /**
* Sets a key within an object. The key supports dot syntax for retrieving deep * Sets a key within an object. The key supports dot syntax for retrieving deep
* fields - e.g. "a.b.c". * fields - e.g. "a.b.c".

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "2.3.18-alpha.29", "version": "2.4.8-alpha.4",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -58,10 +58,11 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "2.3.18-alpha.29", "@budibase/bbui": "2.4.8-alpha.4",
"@budibase/client": "2.3.18-alpha.29", "@budibase/client": "2.4.8-alpha.4",
"@budibase/frontend-core": "2.3.18-alpha.29", "@budibase/frontend-core": "2.4.8-alpha.4",
"@budibase/string-templates": "2.3.18-alpha.29", "@budibase/shared-core": "2.4.8-alpha.4",
"@budibase/string-templates": "2.4.8-alpha.4",
"@fortawesome/fontawesome-svg-core": "^6.2.1", "@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-brands-svg-icons": "^6.2.1", "@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1", "@fortawesome/free-solid-svg-icons": "^6.2.1",

View File

@ -73,14 +73,14 @@
<Tabs noHorizPadding selected="Input"> <Tabs noHorizPadding selected="Input">
<Tab title="Input"> <Tab title="Input">
<TextArea <TextArea
minHeight="80px" minHeight="160px"
disabled disabled
value={textArea(filteredResults?.[idx]?.inputs, "No input")} value={textArea(filteredResults?.[idx]?.inputs, "No input")}
/> />
</Tab> </Tab>
<Tab title="Output"> <Tab title="Output">
<TextArea <TextArea
minHeight="100px" minHeight="160px"
disabled disabled
value={textArea(filteredResults?.[idx]?.outputs, "No output")} value={textArea(filteredResults?.[idx]?.outputs, "No output")}
/> />
@ -98,8 +98,9 @@
<style> <style>
.container { .container {
padding: 0 30px 0 30px; padding: 0 30px 30px 30px;
height: 100%; height: 100%;
overflow: auto;
} }
.tabs { .tabs {

View File

@ -0,0 +1,333 @@
<script>
import {
Context,
Icon,
Input,
ModalContent,
Detail,
notifications,
} from "@budibase/bbui"
import { API } from "api"
import { goto } from "@roxi/routify"
import {
store,
sortedScreens,
automationStore,
themeStore,
} from "builderStore"
import { datasources, queries, tables, views } from "stores/backend"
import { getContext } from "svelte"
import { Constants } from "@budibase/frontend-core"
const modalContext = getContext(Context.Modal)
const commands = [
{
type: "Access",
name: "Invite users and manage app access",
description: "",
icon: "User",
action: () =>
store.update(state => ({ ...state, builderSidePanel: true })),
},
{
type: "Navigate",
name: "Portal",
description: "",
icon: "Compass",
action: () => $goto("../../portal"),
},
{
type: "Navigate",
name: "Data",
description: "",
icon: "Compass",
action: () => $goto("./data"),
},
{
type: "Navigate",
name: "Design",
description: "",
icon: "Compass",
action: () => $goto("./design"),
},
{
type: "Navigate",
name: "Automations",
description: "",
icon: "Compass",
action: () => $goto("./automate"),
},
{
type: "Publish",
name: "App",
description: "Deploy your application",
icon: "Box",
action: deployApp,
},
{
type: "Preview",
name: "App",
description: "",
icon: "Play",
action: () => window.open(`/${$store.appId}`),
},
{
type: "Preview",
name: "Published App",
icon: "Play",
action: () => window.open(`/app${$store.url}`),
},
{
type: "Support",
name: "Raise Github Discussion",
icon: "Help",
action: () =>
window.open(`https://github.com/Budibase/budibase/discussions/new`),
},
{
type: "Support",
name: "Raise A Bug",
icon: "Bug",
action: () =>
window.open(
`https://github.com/Budibase/budibase/issues/new?assignees=&labels=bug&template=bug_report.md&title=`
),
},
...$datasources?.list.map(datasource => ({
type: "Datasource",
name: `${datasource.name}`,
icon: "Data",
action: () => $goto(`./data/datasource/${datasource._id}`),
})),
...$tables?.list.map(table => ({
type: "Table",
name: table.name,
icon: "Table",
action: () => $goto(`./data/table/${table._id}`),
})),
...$views?.list.map(view => ({
type: "View",
name: view.name,
icon: "Remove",
action: () => $goto(`./data/view/${view.name}`),
})),
...$queries?.list.map(query => ({
type: "Query",
name: query.name,
icon: "SQLQuery",
action: () => $goto(`./data/query/${query._id}`),
})),
...$sortedScreens.map(screen => ({
type: "Screen",
name: screen.routing.route,
icon: "WebPage",
action: () => $goto(`./design/${screen._id}/components`),
})),
...$automationStore?.automations.map(automation => ({
type: "Automation",
name: automation.name,
icon: "ShareAndroid",
action: () => $goto(`./automate/${automation._id}`),
})),
...Constants.Themes.map(theme => ({
type: "Change Builder Theme",
name: theme.name,
icon: "ColorPalette",
action: () =>
themeStore.update(state => {
state.theme = theme.class
return state
}),
})),
]
let search
let selected = null
$: enrichedCommands = commands.map(cmd => ({
...cmd,
searchValue: `${cmd.type} ${cmd.name}`.toLowerCase(),
}))
$: results = filterResults(enrichedCommands, search)
$: categories = groupResults(results)
const filterResults = (commands, search) => {
if (!search) {
selected = null
return commands
}
selected = 0
search = search.toLowerCase()
return commands
.filter(cmd => cmd.searchValue.includes(search))
.map((cmd, idx) => ({
...cmd,
idx,
}))
}
const groupResults = results => {
let categories = {}
results?.forEach(result => {
if (!categories[result.type]) {
categories[result.type] = []
}
categories[result.type].push(result)
})
return Object.entries(categories)
}
const onKeyDown = e => {
if (e.key === "ArrowDown") {
e.preventDefault()
if (selected === null) {
selected = 0
return
}
if (selected < results.length - 1) {
selected += 1
}
} else if (e.key === "ArrowUp") {
e.preventDefault()
if (selected === null) {
selected = results.length - 1
return
}
if (selected > 0) {
selected -= 1
}
} else if (e.key === "Enter") {
if (selected == null) {
return
}
runAction(results[selected])
} else if (e.key === "Escape") {
modalContext.hide()
}
}
async function deployApp() {
try {
await API.deployAppChanges()
notifications.success("Application published successfully")
} catch (error) {
notifications.error("Error publishing app")
}
}
const runAction = command => {
if (!command) {
return
}
command.action()
modalContext.hide()
}
</script>
<svelte:window on:keydown={onKeyDown} />
<ModalContent
size="L"
showCancelButton={false}
showConfirmButton={false}
showCloseIcon={false}
>
<div class="content">
<div class="title">
<Icon size="XL" name="Search" />
<Input bind:value={search} quiet placeholder="Search for command" />
</div>
<div class="commands">
{#each categories as [name, results], catIdx}
<div class="category">
<Detail>{name}</Detail>
<div class="options">
{#each results as command, cmdIdx}
<div
class="command"
on:click={() => runAction(command)}
class:selected={command.idx === selected}
>
<Icon size="M" name={command.icon} />
<strong>{command.type}:&nbsp;</strong>
<div class="name">
{command.name}
</div>
</div>
{/each}
</div>
</div>
{/each}
</div>
</div>
</ModalContent>
<style>
.content {
margin: -40px;
overflow: hidden;
}
.title {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
padding: var(--spacing-xl) var(--spacing-xl) var(--spacing-l)
var(--spacing-xl);
border-bottom: var(--border-dark);
gap: var(--spacing-m);
border-bottom-width: 2px;
}
.title :global(.spectrum-Textfield-input) {
border-bottom: none;
font-size: 20px;
}
.commands {
height: 378px;
overflow: scroll;
}
.category {
padding: var(--spacing-m) var(--spacing-xl);
border-bottom: var(--border-light);
}
.category:last-of-type {
border-bottom: none;
}
.category :global(.spectrum-Detail) {
color: var(--spectrum-global-color-gray-600);
}
.options {
padding-top: var(--spacing-m);
margin: 0 calc(-1 * var(--spacing-xl));
}
.command {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
padding: var(--spacing-s) var(--spacing-xl);
cursor: pointer;
overflow: hidden;
transition: color 130ms ease-out, background-color 130ms ease-out;
}
.command:hover,
.selected {
color: var(--spectrum-global-color-gray-900);
background-color: var(--spectrum-global-color-gray-300);
}
.command strong {
margin-left: var(--spacing-m);
}
.name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
footer {
display: flex;
justify-content: center;
}
</style>

View File

@ -24,7 +24,10 @@
let updateModal let updateModal
$: appId = $store.appId $: appId = $store.appId
$: updateAvailable = clientPackage.version !== $store.version $: updateAvailable =
clientPackage.version &&
$store.version &&
clientPackage.version !== $store.version
$: revertAvailable = $store.revertableVersion != null $: revertAvailable = $store.revertableVersion != null
const refreshAppPackage = async () => { const refreshAppPackage = async () => {

View File

@ -14,10 +14,11 @@
export let borderRight = false export let borderRight = false
let wide = false let wide = false
$: customHeaderContent = $$slots["panel-header-content"]
</script> </script>
<div class="panel" class:wide class:borderLeft class:borderRight> <div class="panel" class:wide class:borderLeft class:borderRight>
<div class="header"> <div class="header" class:custom={customHeaderContent}>
{#if showBackButton} {#if showBackButton}
<Icon name="ArrowLeft" hoverable on:click={onClickBackButton} /> <Icon name="ArrowLeft" hoverable on:click={onClickBackButton} />
{/if} {/if}
@ -43,6 +44,13 @@
<Icon name="Close" hoverable on:click={onClickCloseButton} /> <Icon name="Close" hoverable on:click={onClickCloseButton} />
{/if} {/if}
</div> </div>
{#if customHeaderContent}
<span class="custom-content-wrap">
<slot name="panel-header-content" />
</span>
{/if}
<div class="body"> <div class="body">
<slot /> <slot />
</div> </div>
@ -116,4 +124,10 @@
justify-content: flex-start; justify-content: flex-start;
align-items: stretch; align-items: stretch;
} }
.header.custom {
border: none;
}
.custom-content-wrap {
border-bottom: var(--border-light);
}
</style> </style>

View File

@ -20,6 +20,12 @@
x => x =>
x._id !== BUDIBASE_INTERNAL_DB_ID && x.type !== BUDIBASE_DATASOURCE_TYPE x._id !== BUDIBASE_INTERNAL_DB_ID && x.type !== BUDIBASE_DATASOURCE_TYPE
) )
// Ensure query params exist so they can be bound
$: {
if (!parameters.queryParams) {
parameters.queryParams = {}
}
}
function fetchQueryDefinition(query) { function fetchQueryDefinition(query) {
const source = $datasources.list.find( const source = $datasources.list.find(

View File

@ -8,6 +8,7 @@
getSchemaForDatasource, getSchemaForDatasource,
} from "builderStore/dataBinding" } from "builderStore/dataBinding"
import { currentAsset } from "builderStore" import { currentAsset } from "builderStore"
import { getFields } from "helpers/searchFields"
export let componentInstance export let componentInstance
export let value = [] export let value = []
@ -21,9 +22,14 @@
$: datasource = getDatasourceForProvider($currentAsset, componentInstance) $: datasource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchema($currentAsset, datasource) $: schema = getSchema($currentAsset, datasource)
$: options = Object.keys(schema || {}) $: options = allowCellEditing
? Object.keys(schema || {})
: enrichedSchemaFields?.map(field => field.name)
$: sanitisedValue = getValidColumns(value, options) $: sanitisedValue = getValidColumns(value, options)
$: updateBoundValue(sanitisedValue) $: updateBoundValue(sanitisedValue)
$: enrichedSchemaFields = getFields(Object.values(schema) || [], {
allowLinks: true,
})
const getSchema = (asset, datasource) => { const getSchema = (asset, datasource) => {
const schema = getSchemaForDatasource(asset, datasource).schema const schema = getSchemaForDatasource(asset, datasource).schema

View File

@ -120,7 +120,7 @@
const cleanUrl = inputUrl => const cleanUrl = inputUrl =>
url url
?.replace(/(http)|(https)|[{}:]/g, "") ?.replace(/(https)|(http)|[{}:]/g, "")
?.replaceAll(".", "_") ?.replaceAll(".", "_")
?.replaceAll("/", " ") ?.replaceAll("/", " ")
?.trim() || inputUrl ?.trim() || inputUrl

View File

@ -1,8 +1,9 @@
import { get } from "svelte/store" import { get } from "svelte/store"
import { store } from "builderStore" import { store } from "builderStore"
import { users, auth } from "stores/portal" import { auth } from "stores/portal"
import analytics from "analytics" import analytics from "analytics"
import { OnboardingData, OnboardingDesign, OnboardingPublish } from "./steps" import { OnboardingData, OnboardingDesign, OnboardingPublish } from "./steps"
import { API } from "api"
const ONBOARDING_EVENT_PREFIX = "onboarding" const ONBOARDING_EVENT_PREFIX = "onboarding"
export const TOUR_STEP_KEYS = { export const TOUR_STEP_KEYS = {
@ -83,8 +84,7 @@ const getTours = () => {
// Mark the users onboarding as complete // Mark the users onboarding as complete
// Clear all tour related state // Clear all tour related state
if (get(auth).user) { if (get(auth).user) {
await users.save({ await API.updateSelf({
...get(auth).user,
onboardedAt: new Date().toISOString(), onboardedAt: new Date().toISOString(),
}) })
@ -114,8 +114,7 @@ const getTours = () => {
onComplete: async () => { onComplete: async () => {
// Push the onboarding forward // Push the onboarding forward
if (get(auth).user) { if (get(auth).user) {
await users.save({ await API.updateSelf({
...get(auth).user,
onboardedAt: new Date().toISOString(), onboardedAt: new Date().toISOString(),
}) })

View File

@ -13,6 +13,7 @@
await auth.updateSelf($values) await auth.updateSelf($values)
notifications.success("Information updated successfully") notifications.success("Information updated successfully")
} catch (error) { } catch (error) {
console.error(error)
notifications.error("Failed to update information") notifications.error("Failed to update information")
} }
} }

View File

@ -18,7 +18,7 @@
return list.map(item => { return list.map(item => {
return { return {
...item, ...item,
selected: selected.find(x => x === item._id) != null, selected: selected?.find(x => x === item._id) != null,
} }
}) })
} }

View File

@ -346,8 +346,15 @@
onMount(() => { onMount(() => {
rendered = true rendered = true
searchFocus = true
}) })
function handleKeyDown(evt) {
if (evt.key === "Enter" && queryIsEmail && !inviting) {
onInviteUser()
}
}
const userTitle = user => { const userTitle = user => {
if (user.admin?.global) { if (user.admin?.global) {
return "Admin" return "Admin"
@ -370,6 +377,8 @@
} }
</script> </script>
<svelte:window on:keydown={handleKeyDown} />
<div <div
id="builder-side-panel-container" id="builder-side-panel-container"
class:open={$store.builderSidePanel} class:open={$store.builderSidePanel}
@ -403,6 +412,7 @@
autocomplete="off" autocomplete="off"
disabled={inviting} disabled={inviting}
value={query} value={query}
autofocus
on:input={e => { on:input={e => {
query = e.target.value.trim() query = e.target.value.trim()
}} }}
@ -661,6 +671,7 @@
align-items: center; align-items: center;
gap: var(--spacing-m); gap: var(--spacing-m);
color: var(--spectrum-global-color-gray-900); color: var(--spectrum-global-color-gray-900);
overflow: hidden;
} }
.auth-entity .user-email { .auth-entity .user-email {
@ -741,11 +752,11 @@
} }
.builder-side-panel-header { .builder-side-panel-header {
height: 58px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
flex: 0 0 58px;
} }
.invite-header { .invite-header {

View File

@ -10,6 +10,7 @@
Tabs, Tabs,
Tab, Tab,
Heading, Heading,
Modal,
notifications, notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
@ -18,6 +19,7 @@
import { isActive, goto, layout, redirect } from "@roxi/routify" import { isActive, goto, layout, redirect } from "@roxi/routify"
import { capitalise } from "helpers" import { capitalise } from "helpers"
import { onMount, onDestroy } from "svelte" import { onMount, onDestroy } from "svelte"
import CommandPalette from "components/commandPalette/CommandPalette.svelte"
import TourWrap from "components/portal/onboarding/TourWrap.svelte" import TourWrap from "components/portal/onboarding/TourWrap.svelte"
import TourPopover from "components/portal/onboarding/TourPopover.svelte" import TourPopover from "components/portal/onboarding/TourPopover.svelte"
import BuilderSidePanel from "./_components/BuilderSidePanel.svelte" import BuilderSidePanel from "./_components/BuilderSidePanel.svelte"
@ -25,12 +27,9 @@
export let application export let application
// Get Package and set store
let promise = getPackage() let promise = getPackage()
// let betaAccess = false
// Sync once when you load the app
let hasSynced = false let hasSynced = false
let commandPaletteModal
$: selected = capitalise( $: selected = capitalise(
$layout.children.find(layout => $isActive(layout.path))?.title ?? "data" $layout.children.find(layout => $isActive(layout.path))?.title ?? "data"
@ -50,7 +49,6 @@
$redirect("../../") $redirect("../../")
} }
} }
// Handles navigation between frontend, backend, automation. // Handles navigation between frontend, backend, automation.
// This remembers your last place on each of the sections // This remembers your last place on each of the sections
// e.g. if one of your screens is selected on front end, then // e.g. if one of your screens is selected on front end, then
@ -67,6 +65,14 @@
}) })
} }
// Event handler for the command palette
const handleKeyDown = e => {
if (e.key === "k" && (e.ctrlKey || e.metaKey)) {
e.preventDefault()
commandPaletteModal.toggle()
}
}
const initTour = async () => { const initTour = async () => {
// Check if onboarding is enabled. // Check if onboarding is enabled.
if (isEnabled(TENANT_FEATURE_FLAGS.ONBOARDING_TOUR)) { if (isEnabled(TENANT_FEATURE_FLAGS.ONBOARDING_TOUR)) {
@ -120,89 +126,91 @@
}) })
</script> </script>
{#await promise} <TourPopover />
<!-- This should probably be some kind of loading state? -->
<div class="loading" />
{:then _}
<TourPopover />
{#if $store.builderSidePanel} {#if $store.builderSidePanel}
<BuilderSidePanel /> <BuilderSidePanel />
{/if} {/if}
<div class="root"> <div class="root">
<div class="top-nav"> <div class="top-nav">
<div class="topleftnav"> <div class="topleftnav">
<ActionMenu> <ActionMenu>
<div slot="control"> <div slot="control">
<Icon size="M" hoverable name="ShowMenu" /> <Icon size="M" hoverable name="ShowMenu" />
</div> </div>
<MenuItem on:click={() => $goto("../../portal/apps")}> <MenuItem on:click={() => $goto("../../portal/apps")}>
Exit to portal Exit to portal
</MenuItem> </MenuItem>
<MenuItem <MenuItem
on:click={() => $goto(`../../portal/overview/${application}`)} on:click={() => $goto(`../../portal/overview/${application}`)}
> >
Overview Overview
</MenuItem> </MenuItem>
<MenuItem <MenuItem
on:click={() => on:click={() => $goto(`../../portal/overview/${application}/access`)}
$goto(`../../portal/overview/${application}/access`)} >
> Access
Access </MenuItem>
</MenuItem> <MenuItem
<MenuItem on:click={() =>
on:click={() => $goto(`../../portal/overview/${application}/automation-history`)}
$goto(`../../portal/overview/${application}/automation-history`)} >
> Automation history
Automation history </MenuItem>
</MenuItem> <MenuItem
<MenuItem on:click={() => $goto(`../../portal/overview/${application}/backups`)}
on:click={() => >
$goto(`../../portal/overview/${application}/backups`)} Backups
> </MenuItem>
Backups
</MenuItem>
<MenuItem <MenuItem
on:click={() => on:click={() =>
$goto(`../../portal/overview/${application}/name-and-url`)} $goto(`../../portal/overview/${application}/name-and-url`)}
> >
Name and URL Name and URL
</MenuItem> </MenuItem>
<MenuItem <MenuItem
on:click={() => on:click={() => $goto(`../../portal/overview/${application}/version`)}
$goto(`../../portal/overview/${application}/version`)} >
> Version
Version </MenuItem>
</MenuItem> </ActionMenu>
</ActionMenu> <Heading size="XS">{$store.name}</Heading>
<Heading size="XS">{$store.name || "App"}</Heading> </div>
</div> <div class="topcenternav">
<div class="topcenternav"> <Tabs {selected} size="M">
<Tabs {selected} size="M"> {#each $layout.children as { path, title }}
{#each $layout.children as { path, title }} <TourWrap tourStepKey={`builder-${title}-section`}>
<TourWrap tourStepKey={`builder-${title}-section`}> <Tab
<Tab quiet
quiet selected={$isActive(path)}
selected={$isActive(path)} on:click={topItemNavigate(path)}
on:click={topItemNavigate(path)} title={capitalise(title)}
title={capitalise(title)} id={`builder-${title}-tab`}
id={`builder-${title}-tab`} />
/> </TourWrap>
</TourWrap> {/each}
{/each} </Tabs>
</Tabs> </div>
</div> <div class="toprightnav">
<div class="toprightnav"> <AppActions {application} />
<AppActions {application} />
</div>
</div> </div>
<slot />
</div> </div>
{:catch error} {#await promise}
<p>Something went wrong: {error.message}</p> <!-- This should probably be some kind of loading state? -->
{/await} <div class="loading" />
{:then _}
<slot />
{:catch error}
<p>Something went wrong: {error.message}</p>
{/await}
</div>
<svelte:window on:keydown={handleKeyDown} />
<Modal bind:this={commandPaletteModal}>
<CommandPalette />
</Modal>
<style> <style>
.loading { .loading {

View File

@ -34,8 +34,8 @@
{#if duplicates?.length} {#if duplicates?.length}
<div class="alert-wrap"> <div class="alert-wrap">
<Banner type="warning" showCloseButton={false}> <Banner type="warning" showCloseButton={false}>
{`Schema Invalid - There are duplicate auto column types defined in this schema. {`Schema Invalid - There are duplicate auto column types defined in this schema.
Please delete the duplicate entries where appropriate: - Please delete the duplicate entries where appropriate: -
${invalidColumnText.join(", ")}`} ${invalidColumnText.join(", ")}`}
</Banner> </Banner>
</div> </div>

View File

@ -10,6 +10,8 @@
getBindableProperties, getBindableProperties,
getComponentBindableProperties, getComponentBindableProperties,
} from "builderStore/dataBinding" } from "builderStore/dataBinding"
import { ActionButton } from "@budibase/bbui"
import { capitalise } from "helpers"
$: componentInstance = $selectedComponent $: componentInstance = $selectedComponent
$: componentDefinition = store.actions.components.getDefinition( $: componentDefinition = store.actions.components.getDefinition(
@ -25,32 +27,69 @@
) )
$: isScreen = $selectedComponent?._id === $selectedScreen?.props._id $: isScreen = $selectedComponent?._id === $selectedScreen?.props._id
$: title = isScreen ? "Screen" : $selectedComponent?._instanceName $: title = isScreen ? "Screen" : $selectedComponent?._instanceName
let section = "settings"
const tabs = ["settings", "styles", "conditions"]
$: id = $selectedComponent?._id
$: id, (section = tabs[0])
</script> </script>
{#if $selectedComponent} {#if $selectedComponent}
{#key $selectedComponent._id} {#key $selectedComponent._id}
<Panel {title} icon={componentDefinition?.icon} borderLeft> <Panel {title} icon={componentDefinition?.icon} borderLeft>
{#if componentDefinition?.info} <span slot="panel-header-content">
<ComponentInfoSection {componentDefinition} /> <div class="settings-tabs">
{#each tabs as tab}
<ActionButton
size="M"
quiet
selected={section === tab}
on:click={() => {
section = tab
}}
>
{capitalise(tab)}
</ActionButton>
{/each}
</div>
</span>
{#if section == "settings"}
{#if componentDefinition?.info}
<ComponentInfoSection {componentDefinition} />
{/if}
<ComponentSettingsSection
{componentInstance}
{componentDefinition}
{bindings}
{componentBindings}
{isScreen}
/>
{/if}
{#if section == "styles"}
<DesignSection {componentInstance} {componentDefinition} {bindings} />
<CustomStylesSection
{componentInstance}
{componentDefinition}
{bindings}
/>
{/if}
{#if section == "conditions"}
<ConditionalUISection
{componentInstance}
{componentDefinition}
{bindings}
/>
{/if} {/if}
<ComponentSettingsSection
{componentInstance}
{componentDefinition}
{bindings}
{componentBindings}
{isScreen}
/>
<DesignSection {componentInstance} {componentDefinition} {bindings} />
<CustomStylesSection
{componentInstance}
{componentDefinition}
{bindings}
/>
<ConditionalUISection
{componentInstance}
{componentDefinition}
{bindings}
/>
</Panel> </Panel>
{/key} {/key}
{/if} {/if}
<style>
.settings-tabs {
display: flex;
gap: var(--spacing-s);
padding: 0 var(--spacing-l);
padding-bottom: var(--spacing-l);
}
</style>

View File

@ -1,11 +1,11 @@
<script> <script>
import { Button } from "@budibase/bbui" import { Button } from "@budibase/bbui"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { auth, admin } from "stores/portal" import { auth, admin, licensing } from "stores/portal"
import { isEnabled, TENANT_FEATURE_FLAGS } from "helpers/featureFlags" import { isEnabled, TENANT_FEATURE_FLAGS } from "helpers/featureFlags"
</script> </script>
{#if isEnabled(TENANT_FEATURE_FLAGS.LICENSING)} {#if isEnabled(TENANT_FEATURE_FLAGS.LICENSING) && !$licensing.isEnterprisePlan}
{#if $admin.cloud && $auth?.user?.accountPortalAccess} {#if $admin.cloud && $auth?.user?.accountPortalAccess}
<Button <Button
cta cta

View File

@ -154,9 +154,14 @@ export function createAuthStore() {
await setInitInfo({}) await setInitInfo({})
}, },
updateSelf: async fields => { updateSelf: async fields => {
const newUser = { ...get(auth).user, ...fields } await API.updateSelf({ ...fields })
await API.updateSelf(newUser) // Refetch to enrich after update.
setUser(newUser) try {
const user = await API.fetchBuilderSelf()
setUser(user)
} catch (error) {
setUser(null)
}
}, },
forgotPassword: async email => { forgotPassword: async email => {
const tenantId = get(store).tenantId const tenantId = get(store).tenantId

View File

@ -12,6 +12,7 @@ export const createLicensingStore = () => {
// the top level license // the top level license
license: undefined, license: undefined,
isFreePlan: true, isFreePlan: true,
isEnterprisePlan: true,
// features // features
groupsEnabled: false, groupsEnabled: false,
backupsEnabled: false, backupsEnabled: false,
@ -53,7 +54,9 @@ export const createLicensingStore = () => {
}, },
setLicense: () => { setLicense: () => {
const license = get(auth).user.license const license = get(auth).user.license
const isFreePlan = license?.plan.type === Constants.PlanType.FREE const planType = license?.plan.type
const isEnterprisePlan = planType === Constants.PlanType.ENTERPRISE
const isFreePlan = planType === Constants.PlanType.FREE
const groupsEnabled = license.features.includes( const groupsEnabled = license.features.includes(
Constants.Features.USER_GROUPS Constants.Features.USER_GROUPS
) )
@ -74,6 +77,7 @@ export const createLicensingStore = () => {
return { return {
...state, ...state,
license, license,
isEnterprisePlan,
isFreePlan, isFreePlan,
groupsEnabled, groupsEnabled,
backupsEnabled, backupsEnabled,

View File

@ -6,3 +6,4 @@ docker-error.log
envoy.yaml envoy.yaml
*.tar.gz *.tar.gz
prebuilds/ prebuilds/
dist/

View File

@ -1,16 +1,19 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "2.3.18-alpha.29", "version": "2.4.8-alpha.4",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "dist/index.js",
"bin": { "bin": {
"budi": "src/index.js" "budi": "dist/index.js"
}, },
"author": "Budibase", "author": "Budibase",
"license": "GPL-3.0", "license": "GPL-3.0",
"scripts": { "scripts": {
"prebuild": "rm -rf prebuilds 2> /dev/null && cp -r node_modules/leveldown/prebuilds prebuilds", "prebuild": "rm -rf prebuilds 2> /dev/null && cp -r node_modules/leveldown/prebuilds prebuilds",
"build": "yarn prebuild && renamer --find .node --replace .fake 'prebuilds/**' && pkg . --out-path build && yarn postbuild", "rename": "renamer --find .node --replace .fake 'prebuilds/**'",
"tsc": "tsc -p tsconfig.build.json",
"pkg": "pkg . --out-path build --no-bytecode --public --public-packages \"*\" -C GZip",
"build": "yarn prebuild && yarn rename && yarn tsc && yarn pkg && yarn postbuild",
"postbuild": "rm -rf prebuilds 2> /dev/null" "postbuild": "rm -rf prebuilds 2> /dev/null"
}, },
"pkg": { "pkg": {
@ -26,21 +29,21 @@
"outputPath": "build" "outputPath": "build"
}, },
"dependencies": { "dependencies": {
"@budibase/backend-core": "2.3.18-alpha.29", "@budibase/backend-core": "2.4.8-alpha.4",
"@budibase/string-templates": "2.3.18-alpha.29", "@budibase/string-templates": "2.4.8-alpha.4",
"@budibase/types": "2.3.18-alpha.29", "@budibase/types": "2.4.8-alpha.4",
"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.12",
"dotenv": "16.0.1", "dotenv": "16.0.1",
"download": "8.0.0", "download": "8.0.0",
"find-free-port": "^2.0.0", "find-free-port": "^2.0.0",
"inquirer": "8.0.0", "inquirer": "8.0.0",
"joi": "17.6.0", "joi": "17.6.0",
"lookpath": "1.1.0", "lookpath": "1.1.0",
"node-fetch": "2", "node-fetch": "2.6.7",
"pkg": "5.8.0", "pkg": "5.8.0",
"posthog-node": "1.0.7", "posthog-node": "1.0.7",
"pouchdb": "7.3.0", "pouchdb": "7.3.0",
@ -50,8 +53,15 @@
"yaml": "^2.1.1" "yaml": "^2.1.1"
}, },
"devDependencies": { "devDependencies": {
"@swc/core": "^1.3.25",
"@swc/jest": "^0.2.24",
"@types/jest": "^29.4.0",
"@types/node-fetch": "2.6.1",
"@types/pouchdb": "^6.4.0",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
"eslint": "^7.20.0", "eslint": "^7.20.0",
"renamer": "^4.0.0" "renamer": "^4.0.0",
"ts-node": "^10.9.1",
"typescript": "4.7.3"
} }
} }

View File

@ -1,32 +0,0 @@
const PostHog = require("posthog-node")
const { POSTHOG_TOKEN, AnalyticsEvents } = require("../constants")
const ConfigManager = require("../structures/ConfigManager")
class AnalyticsClient {
constructor() {
this.client = new PostHog(POSTHOG_TOKEN)
this.configManager = new ConfigManager()
}
capture(event) {
if (this.configManager.config.analyticsDisabled) return
this.client.capture(event)
}
enable() {
this.configManager.removeKey("analyticsDisabled")
this.client.capture({ event: AnalyticsEvents.OptIn, distinctId: "cli" })
}
disable() {
this.client.capture({ event: AnalyticsEvents.OptOut, distinctId: "cli" })
this.configManager.setValue("analyticsDisabled", true)
}
status() {
return this.configManager.config.analyticsDisabled ? "disabled" : "enabled"
}
}
module.exports = AnalyticsClient

View File

@ -0,0 +1,33 @@
import PostHog from "posthog-node"
import { POSTHOG_TOKEN, AnalyticsEvent } from "../constants"
import { ConfigManager } from "../structures/ConfigManager"
export class AnalyticsClient {
client: PostHog
configManager: ConfigManager
constructor() {
this.client = new PostHog(POSTHOG_TOKEN, {})
this.configManager = new ConfigManager()
}
capture(event: { distinctId: string; event: string; properties?: any }) {
if (this.configManager.config.analyticsDisabled) return
this.client.capture(event)
}
enable() {
this.configManager.removeKey("analyticsDisabled")
this.client.capture({ event: AnalyticsEvent.OptIn, distinctId: "cli" })
}
disable() {
this.client.capture({ event: AnalyticsEvent.OptOut, distinctId: "cli" })
this.configManager.setValue("analyticsDisabled", true)
}
status() {
return this.configManager.config.analyticsDisabled ? "disabled" : "enabled"
}
}

View File

@ -1,7 +1,7 @@
const Command = require("../structures/Command") import { Command } from "../structures/Command"
const { CommandWords } = require("../constants") import { CommandWord } from "../constants"
const { success, error } = require("../utils") import { success, error } from "../utils"
const AnalyticsClient = require("./Client") import { AnalyticsClient } from "./Client"
const client = new AnalyticsClient() const client = new AnalyticsClient()
@ -14,11 +14,10 @@ async function optOut() {
"Successfully opted out of Budibase analytics. You can opt in at any time by running 'budi analytics opt-in'" "Successfully opted out of Budibase analytics. You can opt in at any time by running 'budi analytics opt-in'"
) )
) )
} catch (err) { } catch (err: any) {
console.log( console.log(
error( error(
"Error opting out of Budibase analytics. Please try again later.", `Error opting out of Budibase analytics. Please try again later - ${err}`
err
) )
) )
} }
@ -50,7 +49,7 @@ async function status() {
} }
} }
const command = new Command(`${CommandWords.ANALYTICS}`) export default new Command(`${CommandWord.ANALYTICS}`)
.addHelp("Control the analytics you send to Budibase.") .addHelp("Control the analytics you send to Budibase.")
.addSubOption("--optin", "Opt in to sending analytics to Budibase", optIn) .addSubOption("--optin", "Opt in to sending analytics to Budibase", optIn)
.addSubOption("--optout", "Opt out of sending analytics to Budibase.", optOut) .addSubOption("--optout", "Opt out of sending analytics to Budibase.", optOut)
@ -59,5 +58,3 @@ const command = new Command(`${CommandWords.ANALYTICS}`)
"Check whether you are currently opted in to Budibase analytics.", "Check whether you are currently opted in to Budibase analytics.",
status status
) )
exports.command = command

View File

@ -1,28 +1,30 @@
const Command = require("../structures/Command") import { Command } from "../structures/Command"
const { CommandWords } = require("../constants") import { CommandWord } from "../constants"
const fs = require("fs") import fs from "fs"
const { join } = require("path") import { join } from "path"
const { getAllDbs } = require("../core/db") import { getAllDbs } from "../core/db"
const tar = require("tar") import { progressBar, httpCall } from "../utils"
const { progressBar, httpCall } = require("../utils") import {
const {
TEMP_DIR, TEMP_DIR,
COUCH_DIR, COUCH_DIR,
MINIO_DIR, MINIO_DIR,
getConfig, getConfig,
replication, replication,
getPouches, getPouches,
} = require("./utils") } from "./utils"
const { exportObjects, importObjects } = require("./objectStore") import { exportObjects, importObjects } from "./objectStore"
const tar = require("tar")
async function exportBackup(opts) { type BackupOpts = { env?: string; import?: string; export?: string }
async function exportBackup(opts: BackupOpts) {
const envFile = opts.env || undefined const envFile = opts.env || undefined
let filename = opts["export"] || opts let filename = opts["export"] || (opts as string)
if (typeof filename !== "string") { if (typeof filename !== "string") {
filename = `backup-${new Date().toISOString()}.tar.gz` filename = `backup-${new Date().toISOString()}.tar.gz`
} }
const config = await getConfig(envFile) const config = await getConfig(envFile)
const dbList = await getAllDbs(config["COUCH_DB_URL"]) const dbList = (await getAllDbs(config["COUCH_DB_URL"])) as string[]
const { Remote, Local } = getPouches(config) const { Remote, Local } = getPouches(config)
if (fs.existsSync(TEMP_DIR)) { if (fs.existsSync(TEMP_DIR)) {
fs.rmSync(TEMP_DIR, { recursive: true }) fs.rmSync(TEMP_DIR, { recursive: true })
@ -55,9 +57,9 @@ async function exportBackup(opts) {
console.log(`Generated export file - ${filename}`) console.log(`Generated export file - ${filename}`)
} }
async function importBackup(opts) { async function importBackup(opts: BackupOpts) {
const envFile = opts.env || undefined const envFile = opts.env || undefined
const filename = opts["import"] || opts const filename = opts["import"] || (opts as string)
const config = await getConfig(envFile) const config = await getConfig(envFile)
if (!filename || !fs.existsSync(filename)) { if (!filename || !fs.existsSync(filename)) {
console.error("Cannot import without specifying a valid file to import") console.error("Cannot import without specifying a valid file to import")
@ -99,7 +101,7 @@ async function importBackup(opts) {
fs.rmSync(TEMP_DIR, { recursive: true }) fs.rmSync(TEMP_DIR, { recursive: true })
} }
async function pickOne(opts) { async function pickOne(opts: BackupOpts) {
if (opts["import"]) { if (opts["import"]) {
return importBackup(opts) return importBackup(opts)
} else if (opts["export"]) { } else if (opts["export"]) {
@ -107,7 +109,7 @@ async function pickOne(opts) {
} }
} }
const command = new Command(`${CommandWords.BACKUPS}`) export default new Command(`${CommandWord.BACKUPS}`)
.addHelp( .addHelp(
"Allows building backups of Budibase, as well as importing a backup to a new instance." "Allows building backups of Budibase, as well as importing a backup to a new instance."
) )
@ -126,5 +128,3 @@ const command = new Command(`${CommandWords.BACKUPS}`)
"Provide an environment variable file to configure the CLI.", "Provide an environment variable file to configure the CLI.",
pickOne pickOne
) )
exports.command = command

View File

@ -1,8 +1,8 @@
const { objectStore } = require("@budibase/backend-core") import { objectStore } from "@budibase/backend-core"
const fs = require("fs") import fs from "fs"
const { join } = require("path") import { join } from "path"
const { TEMP_DIR, MINIO_DIR } = require("./utils") import { TEMP_DIR, MINIO_DIR } from "./utils"
const { progressBar } = require("../utils") import { progressBar } from "../utils"
const { const {
ObjectStoreBuckets, ObjectStoreBuckets,
ObjectStore, ObjectStore,
@ -13,10 +13,10 @@ const {
const bucketList = Object.values(ObjectStoreBuckets) const bucketList = Object.values(ObjectStoreBuckets)
exports.exportObjects = async () => { export async function exportObjects() {
const path = join(TEMP_DIR, MINIO_DIR) const path = join(TEMP_DIR, MINIO_DIR)
fs.mkdirSync(path) fs.mkdirSync(path)
let fullList = [] let fullList: any[] = []
let errorCount = 0 let errorCount = 0
for (let bucket of bucketList) { for (let bucket of bucketList) {
const client = ObjectStore(bucket) const client = ObjectStore(bucket)
@ -26,7 +26,7 @@ exports.exportObjects = async () => {
errorCount++ errorCount++
continue continue
} }
const list = await client.listObjectsV2().promise() const list = (await client.listObjectsV2().promise()) as { Contents: any[] }
fullList = fullList.concat(list.Contents.map(el => ({ ...el, bucket }))) fullList = fullList.concat(list.Contents.map(el => ({ ...el, bucket })))
} }
if (errorCount === bucketList.length) { if (errorCount === bucketList.length) {
@ -48,7 +48,7 @@ exports.exportObjects = async () => {
bar.stop() bar.stop()
} }
exports.importObjects = async () => { export async function importObjects() {
const path = join(TEMP_DIR, MINIO_DIR) const path = join(TEMP_DIR, MINIO_DIR)
const buckets = fs.readdirSync(path) const buckets = fs.readdirSync(path)
let total = 0 let total = 0

View File

@ -1,12 +1,13 @@
const dotenv = require("dotenv") import dotenv from "dotenv"
const fs = require("fs") import fs from "fs"
const { string } = require("../questions") import { string } from "../questions"
const { getPouch } = require("../core/db") import { getPouch } from "../core/db"
const { env: environment } = require("@budibase/backend-core") import { env as environment } from "@budibase/backend-core"
import PouchDB from "pouchdb"
exports.TEMP_DIR = ".temp" export const TEMP_DIR = ".temp"
exports.COUCH_DIR = "couchdb" export const COUCH_DIR = "couchdb"
exports.MINIO_DIR = "minio" export const MINIO_DIR = "minio"
const REQUIRED = [ const REQUIRED = [
{ value: "MAIN_PORT", default: "10000" }, { value: "MAIN_PORT", default: "10000" },
@ -19,7 +20,7 @@ const REQUIRED = [
{ value: "MINIO_SECRET_KEY" }, { value: "MINIO_SECRET_KEY" },
] ]
exports.checkURLs = config => { export function checkURLs(config: Record<string, string>) {
const mainPort = config["MAIN_PORT"], const mainPort = config["MAIN_PORT"],
username = config["COUCH_DB_USER"], username = config["COUCH_DB_USER"],
password = config["COUCH_DB_PASSWORD"] password = config["COUCH_DB_PASSWORD"]
@ -34,23 +35,23 @@ exports.checkURLs = config => {
return config return config
} }
exports.askQuestions = async () => { export async function askQuestions() {
console.log( console.log(
"*** NOTE: use a .env file to load these parameters repeatedly ***" "*** NOTE: use a .env file to load these parameters repeatedly ***"
) )
let config = {} let config: Record<string, string> = {}
for (let property of REQUIRED) { for (let property of REQUIRED) {
config[property.value] = await string(property.value, property.default) config[property.value] = await string(property.value, property.default)
} }
return config return config
} }
exports.loadEnvironment = path => { export function loadEnvironment(path: string) {
if (!fs.existsSync(path)) { if (!fs.existsSync(path)) {
throw "Unable to file specified .env file" throw "Unable to file specified .env file"
} }
const env = fs.readFileSync(path, "utf8") const env = fs.readFileSync(path, "utf8")
const config = exports.checkURLs(dotenv.parse(env)) const config = checkURLs(dotenv.parse(env))
for (let required of REQUIRED) { for (let required of REQUIRED) {
if (!config[required.value]) { if (!config[required.value]) {
throw `Cannot find "${required.value}" property in .env file` throw `Cannot find "${required.value}" property in .env file`
@ -60,12 +61,12 @@ exports.loadEnvironment = path => {
} }
// true is the default value passed by commander // true is the default value passed by commander
exports.getConfig = async (envFile = true) => { export async function getConfig(envFile: boolean | string = true) {
let config let config
if (envFile !== true) { if (envFile !== true) {
config = exports.loadEnvironment(envFile) config = loadEnvironment(envFile as string)
} else { } else {
config = await exports.askQuestions() config = await askQuestions()
} }
// fill out environment // fill out environment
for (let key of Object.keys(config)) { for (let key of Object.keys(config)) {
@ -74,12 +75,16 @@ exports.getConfig = async (envFile = true) => {
return config return config
} }
exports.replication = async (from, to) => { export async function replication(
from: PouchDB.Database,
to: PouchDB.Database
) {
const pouch = getPouch() const pouch = getPouch()
try { try {
await pouch.replicate(from, to, { await pouch.replicate(from, to, {
batch_size: 1000, batch_size: 1000,
batch_limit: 5, batches_limit: 5,
// @ts-ignore
style: "main_only", style: "main_only",
}) })
} catch (err) { } catch (err) {
@ -87,7 +92,7 @@ exports.replication = async (from, to) => {
} }
} }
exports.getPouches = config => { export function getPouches(config: Record<string, string>) {
const Remote = getPouch(config["COUCH_DB_URL"]) const Remote = getPouch(config["COUCH_DB_URL"])
const Local = getPouch() const Local = getPouch()
return { Remote, Local } return { Remote, Local }

View File

@ -1,25 +0,0 @@
const { Event } = require("@budibase/types")
exports.CommandWords = {
BACKUPS: "backups",
HOSTING: "hosting",
ANALYTICS: "analytics",
HELP: "help",
PLUGIN: "plugins",
}
exports.InitTypes = {
QUICK: "quick",
DIGITAL_OCEAN: "do",
}
exports.AnalyticsEvents = {
OptOut: "analytics:opt:out",
OptIn: "analytics:opt:in",
SelfHostInit: "hosting:init",
PluginInit: Event.PLUGIN_INIT,
}
exports.POSTHOG_TOKEN = "phc_yGOn4i7jWKaCTapdGR6lfA4AvmuEQ2ijn5zAVSFYPlS"
exports.GENERATED_USER_EMAIL = "admin@admin.com"

View File

@ -0,0 +1,4 @@
export { CommandWord, InitType, AnalyticsEvent } from "@budibase/types"
export const POSTHOG_TOKEN = "phc_yGOn4i7jWKaCTapdGR6lfA4AvmuEQ2ijn5zAVSFYPlS"
export const GENERATED_USER_EMAIL = "admin@admin.com"

View File

@ -1,12 +1,12 @@
const PouchDB = require("pouchdb") import PouchDB from "pouchdb"
const { checkSlashesInUrl } = require("../utils") import { checkSlashesInUrl } from "../utils"
const fetch = require("node-fetch") import fetch from "node-fetch"
/** /**
* Fully qualified URL including username and password, or nothing for local * Fully qualified URL including username and password, or nothing for local
*/ */
exports.getPouch = (url = undefined) => { export function getPouch(url?: string) {
let POUCH_DB_DEFAULTS = {} let POUCH_DB_DEFAULTS
if (!url) { if (!url) {
POUCH_DB_DEFAULTS = { POUCH_DB_DEFAULTS = {
prefix: undefined, prefix: undefined,
@ -19,11 +19,12 @@ exports.getPouch = (url = undefined) => {
} }
const replicationStream = require("pouchdb-replication-stream") const replicationStream = require("pouchdb-replication-stream")
PouchDB.plugin(replicationStream.plugin) PouchDB.plugin(replicationStream.plugin)
// @ts-ignore
PouchDB.adapter("writableStream", replicationStream.adapters.writableStream) PouchDB.adapter("writableStream", replicationStream.adapters.writableStream)
return PouchDB.defaults(POUCH_DB_DEFAULTS) return PouchDB.defaults(POUCH_DB_DEFAULTS) as PouchDB.Static
} }
exports.getAllDbs = async url => { export async function getAllDbs(url: string) {
const response = await fetch( const response = await fetch(
checkSlashesInUrl(encodeURI(`${url}/_all_dbs`)), checkSlashesInUrl(encodeURI(`${url}/_all_dbs`)),
{ {

View File

@ -1,2 +1,3 @@
process.env.NO_JS = "1" process.env.NO_JS = "1"
process.env.JS_BCRYPT = "1" process.env.JS_BCRYPT = "1"
process.env.DISABLE_JWT_WARNING = "1"

View File

@ -1,11 +0,0 @@
const AnalyticsClient = require("./analytics/Client")
const client = new AnalyticsClient()
exports.captureEvent = (event, properties) => {
client.capture({
distinctId: "cli",
event,
properties,
})
}

View File

@ -0,0 +1,11 @@
import { AnalyticsClient } from "./analytics/Client"
const client = new AnalyticsClient()
export function captureEvent(event: string, properties: any) {
client.capture({
distinctId: "cli",
event,
properties,
})
}

View File

@ -1,21 +1,21 @@
const util = require("util") import util from "util"
const exec = util.promisify(require("child_process").exec) const runCommand = util.promisify(require("child_process").exec)
exports.exec = async (command, dir = "./") => { export async function exec(command: string, dir = "./") {
const { stdout } = await exec(command, { cwd: dir }) const { stdout } = await runCommand(command, { cwd: dir })
return stdout return stdout
} }
exports.utilityInstalled = async utilName => { export async function utilityInstalled(utilName: string) {
try { try {
await exports.exec(`${utilName} --version`) await exec(`${utilName} --version`)
return true return true
} catch (err) { } catch (err) {
return false return false
} }
} }
exports.runPkgCommand = async (command, dir = "./") => { export async function runPkgCommand(command: string, dir = "./") {
const yarn = await exports.utilityInstalled("yarn") const yarn = await exports.utilityInstalled("yarn")
const npm = await exports.utilityInstalled("npm") const npm = await exports.utilityInstalled("npm")
if (!yarn && !npm) { if (!yarn && !npm) {

View File

@ -2,15 +2,16 @@ const { success } = require("../utils")
const { updateDockerComposeService } = require("./utils") const { updateDockerComposeService } = require("./utils")
const randomString = require("randomstring") const randomString = require("randomstring")
const { GENERATED_USER_EMAIL } = require("../constants") const { GENERATED_USER_EMAIL } = require("../constants")
import { DockerCompose } from "./types"
exports.generateUser = async (password, silent) => { export async function generateUser(password: string | null, silent: boolean) {
const email = GENERATED_USER_EMAIL const email = GENERATED_USER_EMAIL
if (!password) { if (!password) {
password = randomString.generate({ length: 6 }) password = randomString.generate({ length: 6 })
} }
updateDockerComposeService(service => { updateDockerComposeService((service: DockerCompose) => {
service.environment["BB_ADMIN_USER_EMAIL"] = email service.environment["BB_ADMIN_USER_EMAIL"] = email
service.environment["BB_ADMIN_USER_PASSWORD"] = password service.environment["BB_ADMIN_USER_PASSWORD"] = password as string
}) })
if (!silent) { if (!silent) {
console.log( console.log(

View File

@ -1,14 +1,14 @@
const Command = require("../structures/Command") import { Command } from "../structures/Command"
const { CommandWords } = require("../constants") import { CommandWord } from "../constants"
const { init } = require("./init") import { init } from "./init"
const { start } = require("./start") import { start } from "./start"
const { stop } = require("./stop") import { stop } from "./stop"
const { status } = require("./status") import { status } from "./status"
const { update } = require("./update") import { update } from "./update"
const { generateUser } = require("./genUser") import { generateUser } from "./genUser"
const { watchPlugins } = require("./watch") import { watchPlugins } from "./watch"
const command = new Command(`${CommandWords.HOSTING}`) export default new Command(`${CommandWord.HOSTING}`)
.addHelp("Controls self hosting on the Budibase platform.") .addHelp("Controls self hosting on the Budibase platform.")
.addSubOption( .addSubOption(
"--init [type]", "--init [type]",
@ -46,5 +46,3 @@ const command = new Command(`${CommandWords.HOSTING}`)
generateUser generateUser
) )
.addSubOption("--single", "Specify this with init to use the single image.") .addSubOption("--single", "Specify this with init to use the single image.")
exports.command = command

View File

@ -1,24 +1,25 @@
const { InitTypes, AnalyticsEvents } = require("../constants") import { InitType, AnalyticsEvent } from "../constants"
const { confirmation } = require("../questions") import { confirmation } from "../questions"
const { captureEvent } = require("../events") import { captureEvent } from "../events"
const makeFiles = require("./makeFiles") import * as makeFiles from "./makeFiles"
const axios = require("axios") import { parseEnv } from "../utils"
const { parseEnv } = require("../utils") import { checkDockerConfigured, downloadDockerCompose } from "./utils"
const { checkDockerConfigured, downloadFiles } = require("./utils") import { watchPlugins } from "./watch"
const { watchPlugins } = require("./watch") import { generateUser } from "./genUser"
const { generateUser } = require("./genUser") import fetch from "node-fetch"
const DO_USER_DATA_URL = "http://169.254.169.254/metadata/v1/user-data" const DO_USER_DATA_URL = "http://169.254.169.254/metadata/v1/user-data"
async function getInitConfig(type, isQuick, port) { async function getInitConfig(type: string, isQuick: boolean, port: number) {
const config = isQuick ? makeFiles.QUICK_CONFIG : {} const config: any = isQuick ? makeFiles.QUICK_CONFIG : {}
if (type === InitTypes.DIGITAL_OCEAN) { if (type === InitType.DIGITAL_OCEAN) {
try { try {
const output = await axios.get(DO_USER_DATA_URL) const output = await fetch(DO_USER_DATA_URL)
const response = parseEnv(output.data) const data = await output.text()
const response = parseEnv(data)
for (let [key, value] of Object.entries(makeFiles.ConfigMap)) { for (let [key, value] of Object.entries(makeFiles.ConfigMap)) {
if (response[key]) { if (response[key]) {
config[value] = response[key] config[value as string] = response[key]
} }
} }
} catch (err) { } catch (err) {
@ -32,7 +33,7 @@ async function getInitConfig(type, isQuick, port) {
return config return config
} }
exports.init = async opts => { export async function init(opts: any) {
let type, isSingle, watchDir, genUser, port, silent let type, isSingle, watchDir, genUser, port, silent
if (typeof opts === "string") { if (typeof opts === "string") {
type = opts type = opts
@ -44,7 +45,7 @@ exports.init = async opts => {
port = opts["port"] port = opts["port"]
silent = opts["silent"] silent = opts["silent"]
} }
const isQuick = type === InitTypes.QUICK || type === InitTypes.DIGITAL_OCEAN const isQuick = type === InitType.QUICK || type === InitType.DIGITAL_OCEAN
await checkDockerConfigured() await checkDockerConfigured()
if (!isQuick) { if (!isQuick) {
const shouldContinue = await confirmation( const shouldContinue = await confirmation(
@ -55,12 +56,12 @@ exports.init = async opts => {
return return
} }
} }
captureEvent(AnalyticsEvents.SelfHostInit, { captureEvent(AnalyticsEvent.SelfHostInit, {
type, type,
}) })
const config = await getInitConfig(type, isQuick, port) const config = await getInitConfig(type, isQuick, port)
if (!isSingle) { if (!isSingle) {
await downloadFiles() await downloadDockerCompose()
await makeFiles.makeEnv(config, silent) await makeFiles.makeEnv(config, silent)
} else { } else {
await makeFiles.makeSingleCompose(config, silent) await makeFiles.makeSingleCompose(config, silent)

View File

@ -1,15 +1,15 @@
const { number } = require("../questions") import { number } from "../questions"
const { success, stringifyToDotEnv } = require("../utils") import { success, stringifyToDotEnv } from "../utils"
const fs = require("fs") import fs from "fs"
const path = require("path") import path from "path"
import yaml from "yaml"
import { getAppService } from "./utils"
const randomString = require("randomstring") const randomString = require("randomstring")
const yaml = require("yaml")
const { getAppService } = require("./utils")
const SINGLE_IMAGE = "budibase/budibase:latest" const SINGLE_IMAGE = "budibase/budibase:latest"
const VOL_NAME = "budibase_data" const VOL_NAME = "budibase_data"
const COMPOSE_PATH = path.resolve("./docker-compose.yaml") export const COMPOSE_PATH = path.resolve("./docker-compose.yaml")
const ENV_PATH = path.resolve("./.env") export const ENV_PATH = path.resolve("./.env")
function getSecrets(opts = { single: false }) { function getSecrets(opts = { single: false }) {
const secrets = [ const secrets = [
@ -19,7 +19,7 @@ function getSecrets(opts = { single: false }) {
"REDIS_PASSWORD", "REDIS_PASSWORD",
"INTERNAL_API_KEY", "INTERNAL_API_KEY",
] ]
const obj = {} const obj: Record<string, string> = {}
secrets.forEach(secret => (obj[secret] = randomString.generate())) secrets.forEach(secret => (obj[secret] = randomString.generate()))
// setup couch creds separately // setup couch creds separately
if (opts && opts.single) { if (opts && opts.single) {
@ -32,7 +32,7 @@ function getSecrets(opts = { single: false }) {
return obj return obj
} }
function getSingleCompose(port) { function getSingleCompose(port: number) {
const singleComposeObj = { const singleComposeObj = {
version: "3", version: "3",
services: { services: {
@ -53,7 +53,7 @@ function getSingleCompose(port) {
return yaml.stringify(singleComposeObj) return yaml.stringify(singleComposeObj)
} }
function getEnv(port) { function getEnv(port: number) {
const partOne = stringifyToDotEnv({ const partOne = stringifyToDotEnv({
MAIN_PORT: port, MAIN_PORT: port,
}) })
@ -77,19 +77,21 @@ function getEnv(port) {
].join("\n") ].join("\n")
} }
exports.ENV_PATH = ENV_PATH export const ConfigMap = {
exports.COMPOSE_PATH = COMPOSE_PATH
module.exports.ConfigMap = {
MAIN_PORT: "port", MAIN_PORT: "port",
} }
module.exports.QUICK_CONFIG = { export const QUICK_CONFIG = {
key: "budibase", key: "budibase",
port: 10000, port: 10000,
} }
async function make(path, contentsFn, inputs = {}, silent) { async function make(
path: string,
contentsFn: Function,
inputs: any = {},
silent: boolean
) {
const port = const port =
inputs.port || inputs.port ||
(await number( (await number(
@ -107,15 +109,15 @@ async function make(path, contentsFn, inputs = {}, silent) {
} }
} }
module.exports.makeEnv = async (inputs = {}, silent) => { export async function makeEnv(inputs: any = {}, silent: boolean) {
return make(ENV_PATH, getEnv, inputs, silent) return make(ENV_PATH, getEnv, inputs, silent)
} }
module.exports.makeSingleCompose = async (inputs = {}, silent) => { export async function makeSingleCompose(inputs: any = {}, silent: boolean) {
return make(COMPOSE_PATH, getSingleCompose, inputs, silent) return make(COMPOSE_PATH, getSingleCompose, inputs, silent)
} }
module.exports.getEnvProperty = property => { export function getEnvProperty(property: string) {
const props = fs.readFileSync(ENV_PATH, "utf8").split(property) const props = fs.readFileSync(ENV_PATH, "utf8").split(property)
if (props[0].charAt(0) === "=") { if (props[0].charAt(0) === "=") {
property = props[0] property = props[0]
@ -125,7 +127,7 @@ module.exports.getEnvProperty = property => {
return property.split("=")[1].split("\n")[0] return property.split("=")[1].split("\n")[0]
} }
module.exports.getComposeProperty = property => { export function getComposeProperty(property: string) {
const { service } = getAppService(COMPOSE_PATH) const { service } = getAppService(COMPOSE_PATH)
if (property === "port" && Array.isArray(service.ports)) { if (property === "port" && Array.isArray(service.ports)) {
const port = service.ports[0] const port = service.ports[0]

View File

@ -1,14 +1,10 @@
const { import { checkDockerConfigured, checkInitComplete, handleError } from "./utils"
checkDockerConfigured, import { info, success } from "../utils"
checkInitComplete, import * as makeFiles from "./makeFiles"
handleError, import compose from "docker-compose"
} = require("./utils") import fs from "fs"
const { info, success } = require("../utils")
const makeFiles = require("./makeFiles")
const compose = require("docker-compose")
const fs = require("fs")
exports.start = async () => { export async function start() {
await checkDockerConfigured() await checkDockerConfigured()
checkInitComplete() checkInitComplete()
console.log( console.log(

View File

@ -1,12 +1,8 @@
const { import { checkDockerConfigured, checkInitComplete, handleError } from "./utils"
checkDockerConfigured, import { info } from "../utils"
checkInitComplete, import compose from "docker-compose"
handleError,
} = require("./utils")
const { info } = require("../utils")
const compose = require("docker-compose")
exports.status = async () => { export async function status() {
await checkDockerConfigured() await checkDockerConfigured()
checkInitComplete() checkInitComplete()
console.log(info("Budibase status")) console.log(info("Budibase status"))

View File

@ -1,12 +1,8 @@
const { import { checkDockerConfigured, checkInitComplete, handleError } from "./utils"
checkDockerConfigured, import { info, success } from "../utils"
checkInitComplete, import compose from "docker-compose"
handleError,
} = require("./utils")
const { info, success } = require("../utils")
const compose = require("docker-compose")
exports.stop = async () => { export async function stop() {
await checkDockerConfigured() await checkDockerConfigured()
checkInitComplete() checkInitComplete()
console.log(info("Stopping services, this may take a moment.")) console.log(info("Stopping services, this may take a moment."))

View File

@ -0,0 +1,4 @@
export interface DockerCompose {
environment: Record<string, string>
volumes: string[]
}

View File

@ -1,20 +1,20 @@
const { import {
checkDockerConfigured, checkDockerConfigured,
checkInitComplete, checkInitComplete,
downloadFiles, downloadDockerCompose,
handleError, handleError,
getServices, getServices,
} = require("./utils") } from "./utils"
const { confirmation } = require("../questions") import { confirmation } from "../questions"
const compose = require("docker-compose") import compose from "docker-compose"
const { COMPOSE_PATH } = require("./makeFiles") import { COMPOSE_PATH } from "./makeFiles"
const { info, success } = require("../utils") import { info, success } from "../utils"
const { start } = require("./start") import { start } from "./start"
const BB_COMPOSE_SERVICES = ["app-service", "worker-service", "proxy-service"] const BB_COMPOSE_SERVICES = ["app-service", "worker-service", "proxy-service"]
const BB_SINGLE_SERVICE = ["budibase"] const BB_SINGLE_SERVICE = ["budibase"]
exports.update = async () => { export async function update() {
const { services } = getServices(COMPOSE_PATH) const { services } = getServices(COMPOSE_PATH)
const isSingle = Object.keys(services).length === 1 const isSingle = Object.keys(services).length === 1
await checkDockerConfigured() await checkDockerConfigured()
@ -23,7 +23,7 @@ exports.update = async () => {
!isSingle && !isSingle &&
(await confirmation("Do you wish to update you docker-compose.yaml?")) (await confirmation("Do you wish to update you docker-compose.yaml?"))
) { ) {
await downloadFiles() await downloadDockerCompose()
} }
await handleError(async () => { await handleError(async () => {
const status = await compose.ps() const status = await compose.ps()

View File

@ -1,24 +1,24 @@
const { lookpath } = require("lookpath") import { lookpath } from "lookpath"
const fs = require("fs") import fs from "fs"
const makeFiles = require("./makeFiles") import * as makeFiles from "./makeFiles"
const { logErrorToFile, downloadFile, error } = require("../utils") import { logErrorToFile, downloadFile, error } from "../utils"
const yaml = require("yaml") import yaml from "yaml"
import { DockerCompose } from "./types"
const ERROR_FILE = "docker-error.log" const ERROR_FILE = "docker-error.log"
const FILE_URLS = [ const COMPOSE_URL =
"https://raw.githubusercontent.com/Budibase/budibase/master/hosting/docker-compose.yaml", "https://raw.githubusercontent.com/Budibase/budibase/master/hosting/docker-compose.yaml"
]
exports.downloadFiles = async () => { export async function downloadDockerCompose() {
const promises = [] const fileName = COMPOSE_URL.split("/").slice(-1)[0]
for (let url of FILE_URLS) { try {
const fileName = url.split("/").slice(-1)[0] await downloadFile(COMPOSE_URL, `./${fileName}`)
promises.push(downloadFile(url, `./${fileName}`)) } catch (err) {
console.error(error(`Failed to retrieve compose file - ${err}`))
} }
await Promise.all(promises)
} }
exports.checkDockerConfigured = async () => { export async function checkDockerConfigured() {
const error = const error =
"docker/docker-compose has not been installed, please follow instructions at: https://docs.budibase.com/docs/docker-compose" "docker/docker-compose has not been installed, please follow instructions at: https://docs.budibase.com/docs/docker-compose"
const docker = await lookpath("docker") const docker = await lookpath("docker")
@ -28,7 +28,7 @@ exports.checkDockerConfigured = async () => {
} }
} }
exports.checkInitComplete = () => { export function checkInitComplete() {
if ( if (
!fs.existsSync(makeFiles.ENV_PATH) && !fs.existsSync(makeFiles.ENV_PATH) &&
!fs.existsSync(makeFiles.COMPOSE_PATH) !fs.existsSync(makeFiles.COMPOSE_PATH)
@ -37,10 +37,10 @@ exports.checkInitComplete = () => {
} }
} }
exports.handleError = async func => { export async function handleError(func: Function) {
try { try {
await func() await func()
} catch (err) { } catch (err: any) {
if (err && err.err) { if (err && err.err) {
logErrorToFile(ERROR_FILE, err.err) logErrorToFile(ERROR_FILE, err.err)
} }
@ -48,14 +48,14 @@ exports.handleError = async func => {
} }
} }
exports.getServices = path => { export function getServices(path: string) {
const dockerYaml = fs.readFileSync(path, "utf8") const dockerYaml = fs.readFileSync(path, "utf8")
const parsedYaml = yaml.parse(dockerYaml) const parsedYaml = yaml.parse(dockerYaml)
return { yaml: parsedYaml, services: parsedYaml.services } return { yaml: parsedYaml, services: parsedYaml.services }
} }
exports.getAppService = path => { export function getAppService(path: string) {
const { yaml, services } = exports.getServices(path), const { yaml, services } = getServices(path),
serviceList = Object.keys(services) serviceList = Object.keys(services)
let service let service
if (services["app-service"]) { if (services["app-service"]) {
@ -66,14 +66,17 @@ exports.getAppService = path => {
return { yaml, service } return { yaml, service }
} }
exports.updateDockerComposeService = updateFn => { export function updateDockerComposeService(
// eslint-disable-next-line no-unused-vars
updateFn: (service: DockerCompose) => void
) {
const opts = ["docker-compose.yaml", "docker-compose.yml"] const opts = ["docker-compose.yaml", "docker-compose.yml"]
const dockerFilePath = opts.find(name => fs.existsSync(name)) const dockerFilePath = opts.find(name => fs.existsSync(name))
if (!dockerFilePath) { if (!dockerFilePath) {
console.log(error("Unable to locate docker-compose YAML.")) console.log(error("Unable to locate docker-compose YAML."))
return return
} }
const { yaml: parsedYaml, service } = exports.getAppService(dockerFilePath) const { yaml: parsedYaml, service } = getAppService(dockerFilePath)
if (!service) { if (!service) {
console.log( console.log(
error( error(

View File

@ -1,9 +1,10 @@
const { resolve } = require("path") import { resolve } from "path"
const fs = require("fs") import fs from "fs"
const { error, success } = require("../utils") import { error, success } from "../utils"
const { updateDockerComposeService } = require("./utils") import { updateDockerComposeService } from "./utils"
import { DockerCompose } from "./types"
exports.watchPlugins = async (pluginPath, silent) => { export async function watchPlugins(pluginPath: string, silent: boolean) {
const PLUGIN_PATH = "/plugins" const PLUGIN_PATH = "/plugins"
// get absolute path // get absolute path
pluginPath = resolve(pluginPath) pluginPath = resolve(pluginPath)
@ -15,7 +16,7 @@ exports.watchPlugins = async (pluginPath, silent) => {
) )
return return
} }
updateDockerComposeService(service => { updateDockerComposeService((service: DockerCompose) => {
// set environment variable // set environment variable
service.environment["PLUGINS_DIR"] = PLUGIN_PATH service.environment["PLUGINS_DIR"] = PLUGIN_PATH
// add volumes to parsed yaml // add volumes to parsed yaml

View File

@ -1,10 +1,10 @@
#!/usr/bin/env node #!/usr/bin/env node
require("./prebuilds") import "./prebuilds"
require("./environment") import "./environment"
import { getCommands } from "./options"
import { Command } from "commander"
import { getHelpDescription } from "./utils"
const json = require("../package.json") const json = require("../package.json")
const { getCommands } = require("./options")
const { Command } = require("commander")
const { getHelpDescription } = require("./utils")
// add hosting config // add hosting config
async function init() { async function init() {

View File

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

View File

@ -0,0 +1,8 @@
import analytics from "./analytics"
import hosting from "./hosting"
import backups from "./backups"
import plugins from "./plugins"
export function getCommands() {
return [hosting, analytics, backups, plugins]
}

View File

@ -1,18 +1,22 @@
const Command = require("../structures/Command") import { Command } from "../structures/Command"
const { CommandWords, AnalyticsEvents, InitTypes } = require("../constants") import { CommandWord, AnalyticsEvent, InitType } from "../constants"
const { getSkeleton, fleshOutSkeleton } = require("./skeleton") import { getSkeleton, fleshOutSkeleton } from "./skeleton"
const questions = require("../questions") import * as questions from "../questions"
const fs = require("fs") import fs from "fs"
const { PLUGIN_TYPE_ARR } = require("@budibase/types") import { PluginType, PLUGIN_TYPE_ARR } from "@budibase/types"
const { plugins } = require("@budibase/backend-core") import { plugins } from "@budibase/backend-core"
const { runPkgCommand } = require("../exec") import { runPkgCommand } from "../exec"
const { join } = require("path") import { join } from "path"
const { success, error, info, moveDirectory } = require("../utils") import { success, error, info, moveDirectory } from "../utils"
const { captureEvent } = require("../events") import { captureEvent } from "../events"
import { GENERATED_USER_EMAIL } from "../constants"
import { init as hostingInit } from "../hosting/init"
import { start as hostingStart } from "../hosting/start"
const fp = require("find-free-port") const fp = require("find-free-port")
const { GENERATED_USER_EMAIL } = require("../constants")
const { init: hostingInit } = require("../hosting/init") type PluginOpts = {
const { start: hostingStart } = require("../hosting/start") init?: PluginType
}
function checkInPlugin() { function checkInPlugin() {
if (!fs.existsSync("package.json")) { if (!fs.existsSync("package.json")) {
@ -27,7 +31,7 @@ function checkInPlugin() {
} }
} }
async function askAboutTopLevel(name) { async function askAboutTopLevel(name: string) {
const files = fs.readdirSync(process.cwd()) const files = fs.readdirSync(process.cwd())
// we are in an empty git repo, don't ask // we are in an empty git repo, don't ask
if (files.find(file => file === ".git")) { if (files.find(file => file === ".git")) {
@ -45,8 +49,8 @@ async function askAboutTopLevel(name) {
} }
} }
async function init(opts) { async function init(opts: PluginOpts) {
const type = opts["init"] || opts const type = opts["init"] || (opts as PluginType)
if (!type || !PLUGIN_TYPE_ARR.includes(type)) { if (!type || !PLUGIN_TYPE_ARR.includes(type)) {
console.log( console.log(
error( error(
@ -82,7 +86,7 @@ async function init(opts) {
} else { } else {
console.log(info(`Plugin created in directory "${name}"`)) console.log(info(`Plugin created in directory "${name}"`))
} }
captureEvent(AnalyticsEvents.PluginInit, { captureEvent(AnalyticsEvent.PluginInit, {
type, type,
name, name,
description, description,
@ -109,7 +113,7 @@ async function verify() {
version = pkgJson.version version = pkgJson.version
plugins.validate(schemaJson) plugins.validate(schemaJson)
return { name, version } return { name, version }
} catch (err) { } catch (err: any) {
if (err && err.message && err.message.includes("not valid JSON")) { if (err && err.message && err.message.includes("not valid JSON")) {
console.log(error(`schema.json is not valid JSON: ${err.message}`)) console.log(error(`schema.json is not valid JSON: ${err.message}`))
} else { } else {
@ -120,7 +124,7 @@ async function verify() {
async function build() { async function build() {
const verified = await verify() const verified = await verify()
if (!verified.name) { if (!verified?.name) {
return return
} }
console.log(success("Verified!")) console.log(success("Verified!"))
@ -132,7 +136,7 @@ async function build() {
async function watch() { async function watch() {
const verified = await verify() const verified = await verify()
if (!verified.name) { if (!verified?.name) {
return return
} }
const output = join("dist", `${verified.name}-${verified.version}.tar.gz`) const output = join("dist", `${verified.name}-${verified.version}.tar.gz`)
@ -150,7 +154,7 @@ async function dev() {
const [port] = await fp(10000) const [port] = await fp(10000)
const password = "admin" const password = "admin"
await hostingInit({ await hostingInit({
init: InitTypes.QUICK, init: InitType.QUICK,
single: true, single: true,
watchPluginDir: pluginDir, watchPluginDir: pluginDir,
genUser: password, genUser: password,
@ -168,7 +172,7 @@ async function dev() {
console.log(success("Password: ") + info(password)) console.log(success("Password: ") + info(password))
} }
const command = new Command(`${CommandWords.PLUGIN}`) export default new Command(`${CommandWord.PLUGIN}`)
.addHelp( .addHelp(
"Custom plugins for Budibase, init, build and verify your components and datasources with this tool." "Custom plugins for Budibase, init, build and verify your components and datasources with this tool."
) )
@ -192,5 +196,3 @@ const command = new Command(`${CommandWords.PLUGIN}`)
"Run a development environment which automatically watches the current directory.", "Run a development environment which automatically watches the current directory.",
dev dev
) )
exports.command = command

View File

@ -1,21 +1,21 @@
const fetch = require("node-fetch") import fetch from "node-fetch"
import fs from "fs"
import os from "os"
import { join } from "path"
import { processStringSync } from "@budibase/string-templates"
const download = require("download") const download = require("download")
const fs = require("fs")
const os = require("os")
const { join } = require("path")
const tar = require("tar") const tar = require("tar")
const { processStringSync } = require("@budibase/string-templates")
const HBS_FILES = ["package.json.hbs", "schema.json.hbs", "README.md.hbs"] const HBS_FILES = ["package.json.hbs", "schema.json.hbs", "README.md.hbs"]
async function getSkeletonUrl(type) { async function getSkeletonUrl(type: string) {
const resp = await fetch( const resp = await fetch(
"https://api.github.com/repos/budibase/budibase-skeleton/releases/latest" "https://api.github.com/repos/budibase/budibase-skeleton/releases/latest"
) )
if (resp.status >= 300) { if (resp.status >= 300) {
throw new Error("Failed to retrieve skeleton metadata") throw new Error("Failed to retrieve skeleton metadata")
} }
const json = await resp.json() const json = (await resp.json()) as { assets: any[] }
for (let asset of json["assets"]) { for (let asset of json["assets"]) {
if (asset.name && asset.name.includes(type)) { if (asset.name && asset.name.includes(type)) {
return asset["browser_download_url"] return asset["browser_download_url"]
@ -24,7 +24,7 @@ async function getSkeletonUrl(type) {
throw new Error("No skeleton found in latest release.") throw new Error("No skeleton found in latest release.")
} }
exports.getSkeleton = async (type, name) => { export async function getSkeleton(type: string, name: string) {
const url = await getSkeletonUrl(type) const url = await getSkeletonUrl(type)
const tarballFile = join(os.tmpdir(), "skeleton.tar.gz") const tarballFile = join(os.tmpdir(), "skeleton.tar.gz")
@ -40,7 +40,12 @@ exports.getSkeleton = async (type, name) => {
fs.rmSync(tarballFile) fs.rmSync(tarballFile)
} }
exports.fleshOutSkeleton = async (type, name, description, version) => { export async function fleshOutSkeleton(
type: string,
name: string,
description: string,
version: string
) {
for (let file of HBS_FILES) { for (let file of HBS_FILES) {
const oldFile = join(name, file), const oldFile = join(name, file),
newFile = join(name, file.substring(0, file.length - 4)) newFile = join(name, file.substring(0, file.length - 4))

View File

@ -1,7 +1,8 @@
const os = require("os") import os from "os"
const { join } = require("path") import { join } from "path"
const fs = require("fs") import fs from "fs"
const { error } = require("./utils") import { error } from "./utils"
const PREBUILDS = "prebuilds" const PREBUILDS = "prebuilds"
const ARCH = `${os.platform()}-${os.arch()}` const ARCH = `${os.platform()}-${os.arch()}`
const PREBUILD_DIR = join(process.execPath, "..", PREBUILDS, ARCH) const PREBUILD_DIR = join(process.execPath, "..", PREBUILDS, ARCH)
@ -26,8 +27,8 @@ function checkForBinaries() {
} }
} }
function cleanup(evt) { function cleanup(evt?: number) {
if (!isNaN(evt)) { if (evt && !isNaN(evt)) {
return return
} }
if (evt) { if (evt) {

View File

@ -1,6 +1,6 @@
const inquirer = require("inquirer") const inquirer = require("inquirer")
exports.confirmation = async question => { export async function confirmation(question: string) {
const config = { const config = {
type: "confirm", type: "confirm",
message: question, message: question,
@ -10,8 +10,8 @@ exports.confirmation = async question => {
return (await inquirer.prompt(config)).confirmation return (await inquirer.prompt(config)).confirmation
} }
exports.string = async (question, defaultString = null) => { export async function string(question: string, defaultString?: string) {
const config = { const config: any = {
type: "input", type: "input",
name: "string", name: "string",
message: question, message: question,
@ -22,12 +22,12 @@ exports.string = async (question, defaultString = null) => {
return (await inquirer.prompt(config)).string return (await inquirer.prompt(config)).string
} }
exports.number = async (question, defaultNumber) => { export async function number(question: string, defaultNumber?: number) {
const config = { const config: any = {
type: "input", type: "input",
name: "number", name: "number",
message: question, message: question,
validate: value => { validate: (value: string) => {
let valid = !isNaN(parseFloat(value)) let valid = !isNaN(parseFloat(value))
return valid || "Please enter a number" return valid || "Please enter a number"
}, },

View File

@ -1,19 +1,31 @@
const { import {
getSubHelpDescription, getSubHelpDescription,
getHelpDescription, getHelpDescription,
error, error,
capitaliseFirstLetter, capitaliseFirstLetter,
} = require("../utils") } from "../utils"
class Command { type CommandOpt = {
constructor(command, func = null) { command: string
help: string
func?: Function
extras: any[]
}
export class Command {
command: string
opts: CommandOpt[]
func?: Function
help?: string
constructor(command: string, func?: Function) {
// if there are options, need to just get the command name // if there are options, need to just get the command name
this.command = command this.command = command
this.opts = [] this.opts = []
this.func = func this.func = func
} }
convertToCommander(lookup) { convertToCommander(lookup: string) {
const parts = lookup.toLowerCase().split("-") const parts = lookup.toLowerCase().split("-")
// camel case, separate out first // camel case, separate out first
const first = parts.shift() const first = parts.shift()
@ -22,21 +34,26 @@ class Command {
.join("") .join("")
} }
addHelp(help) { addHelp(help: string) {
this.help = help this.help = help
return this return this
} }
addSubOption(command, help, func, extras = []) { addSubOption(
command: string,
help: string,
func?: Function,
extras: any[] = []
) {
this.opts.push({ command, help, func, extras }) this.opts.push({ command, help, func, extras })
return this return this
} }
configure(program) { configure(program: any) {
const thisCmd = this const thisCmd = this
let command = program.command(thisCmd.command) let command = program.command(thisCmd.command)
if (this.help) { if (this.help) {
command = command.description(getHelpDescription(thisCmd.help)) command = command.description(getHelpDescription(thisCmd.help!))
} }
for (let opt of thisCmd.opts) { for (let opt of thisCmd.opts) {
command = command.option(opt.command, getSubHelpDescription(opt.help)) command = command.option(opt.command, getSubHelpDescription(opt.help))
@ -45,7 +62,7 @@ class Command {
"--help", "--help",
getSubHelpDescription(`Get help with ${this.command} options`) getSubHelpDescription(`Get help with ${this.command} options`)
) )
command.action(async options => { command.action(async (options: Record<string, string>) => {
try { try {
let executed = false, let executed = false,
found = false found = false
@ -53,7 +70,7 @@ class Command {
let lookup = opt.command.split(" ")[0].replace("--", "") let lookup = opt.command.split(" ")[0].replace("--", "")
// need to handle how commander converts watch-plugin-dir to watchPluginDir // need to handle how commander converts watch-plugin-dir to watchPluginDir
lookup = this.convertToCommander(lookup) lookup = this.convertToCommander(lookup)
found = !executed && options[lookup] found = !executed && !!options[lookup]
if (found && opt.func) { if (found && opt.func) {
const input = const input =
Object.keys(options).length > 1 ? options : options[lookup] Object.keys(options).length > 1 ? options : options[lookup]
@ -69,11 +86,9 @@ class Command {
console.log(error(`Unknown ${this.command} option.`)) console.log(error(`Unknown ${this.command} option.`))
command.help() command.help()
} }
} catch (err) { } catch (err: any) {
console.log(error(err)) console.log(error(err))
} }
}) })
} }
} }
module.exports = Command

View File

@ -3,7 +3,9 @@ const path = require("path")
const os = require("os") const os = require("os")
const { error } = require("../utils") const { error } = require("../utils")
class ConfigManager { export class ConfigManager {
path: string
constructor() { constructor() {
this.path = path.join(os.homedir(), ".budibase.json") this.path = path.join(os.homedir(), ".budibase.json")
if (!fs.existsSync(this.path)) { if (!fs.existsSync(this.path)) {
@ -24,26 +26,24 @@ class ConfigManager {
} }
} }
set config(json) { set config(json: any) {
fs.writeFileSync(this.path, JSON.stringify(json)) fs.writeFileSync(this.path, JSON.stringify(json))
} }
getValue(key) { getValue(key: string) {
return this.config[key] return this.config[key]
} }
setValue(key, value) { setValue(key: string, value: any) {
this.config = { this.config = {
...this.config, ...this.config,
[key]: value, [key]: value,
} }
} }
removeKey(key) { removeKey(key: string) {
const updated = { ...this.config } const updated = { ...this.config }
delete updated[key] delete updated[key]
this.config = updated this.config = updated
} }
} }
module.exports = ConfigManager

View File

@ -1,106 +0,0 @@
const chalk = require("chalk")
const fs = require("fs")
const axios = require("axios")
const path = require("path")
const progress = require("cli-progress")
const { join } = require("path")
exports.downloadFile = async (url, filePath) => {
filePath = path.resolve(filePath)
const writer = fs.createWriteStream(filePath)
const response = await axios({
url,
method: "GET",
responseType: "stream",
})
response.data.pipe(writer)
return new Promise((resolve, reject) => {
writer.on("finish", resolve)
writer.on("error", reject)
})
}
exports.httpCall = async (url, method) => {
const response = await axios({
url,
method,
})
return response.data
}
exports.getHelpDescription = string => {
return chalk.cyan(string)
}
exports.getSubHelpDescription = string => {
return chalk.green(string)
}
exports.error = error => {
return chalk.red(`Error - ${error}`)
}
exports.success = success => {
return chalk.green(success)
}
exports.info = info => {
return chalk.cyan(info)
}
exports.logErrorToFile = (file, error) => {
fs.writeFileSync(path.resolve(`./${file}`), `Budibase Error\n${error}`)
}
exports.parseEnv = env => {
const lines = env.toString().split("\n")
let result = {}
for (const line of lines) {
const match = line.match(/^([^=:#]+?)[=:](.*)/)
if (match) {
result[match[1].trim()] = match[2].trim()
}
}
return result
}
exports.progressBar = total => {
const bar = new progress.SingleBar({}, progress.Presets.shades_classic)
bar.start(total, 0)
return bar
}
exports.checkSlashesInUrl = url => {
return url.replace(/(https?:\/\/)|(\/)+/g, "$1$2")
}
exports.moveDirectory = (oldPath, newPath) => {
const files = fs.readdirSync(oldPath)
// check any file exists already
for (let file of files) {
if (fs.existsSync(join(newPath, file))) {
throw new Error(
"Unable to remove top level directory - some skeleton files already exist."
)
}
}
for (let file of files) {
fs.renameSync(join(oldPath, file), join(newPath, file))
}
fs.rmdirSync(oldPath)
}
exports.capitaliseFirstLetter = str => {
return str.charAt(0).toUpperCase() + str.slice(1)
}
exports.stringifyToDotEnv = json => {
let str = ""
for (let [key, value] of Object.entries(json)) {
str += `${key}=${value}\n`
}
return str
}

112
packages/cli/src/utils.ts Normal file
View File

@ -0,0 +1,112 @@
import chalk from "chalk"
import fs from "fs"
import path from "path"
import { join } from "path"
import fetch from "node-fetch"
const progress = require("cli-progress")
export function downloadFile(url: string, filePath: string) {
return new Promise((resolve, reject) => {
filePath = path.resolve(filePath)
fetch(url, {
method: "GET",
})
.then(response => {
const writer = fs.createWriteStream(filePath)
if (response.body) {
response.body.pipe(writer)
response.body.on("end", resolve)
response.body.on("error", reject)
} else {
throw new Error(
`Unable to retrieve docker-compose file - ${response.status}`
)
}
})
.catch(err => {
throw err
})
})
}
export async function httpCall(url: string, method: string) {
const response = await fetch(url, {
method,
})
return response.body
}
export function getHelpDescription(str: string) {
return chalk.cyan(str)
}
export function getSubHelpDescription(str: string) {
return chalk.green(str)
}
export function error(err: string | number) {
process.exitCode = -1
return chalk.red(`Error - ${err}`)
}
export function success(str: string) {
return chalk.green(str)
}
export function info(str: string) {
return chalk.cyan(str)
}
export function logErrorToFile(file: string, error: string) {
fs.writeFileSync(path.resolve(`./${file}`), `Budibase Error\n${error}`)
}
export function parseEnv(env: string) {
const lines = env.toString().split("\n")
let result: Record<string, string> = {}
for (const line of lines) {
const match = line.match(/^([^=:#]+?)[=:](.*)/)
if (match) {
result[match[1].trim()] = match[2].trim()
}
}
return result
}
export function progressBar(total: number) {
const bar = new progress.SingleBar({}, progress.Presets.shades_classic)
bar.start(total, 0)
return bar
}
export function checkSlashesInUrl(url: string) {
return url.replace(/(https?:\/\/)|(\/)+/g, "$1$2")
}
export function moveDirectory(oldPath: string, newPath: string) {
const files = fs.readdirSync(oldPath)
// check any file exists already
for (let file of files) {
if (fs.existsSync(join(newPath, file))) {
throw new Error(
"Unable to remove top level directory - some skeleton files already exist."
)
}
}
for (let file of files) {
fs.renameSync(join(oldPath, file), join(newPath, file))
}
fs.rmdirSync(oldPath)
}
export function capitaliseFirstLetter(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
export function stringifyToDotEnv(json: Record<string, string | number>) {
let str = ""
for (let [key, value] of Object.entries(json)) {
str += `${key}=${value}\n`
}
return str
}

3
packages/cli/start.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
dir="$(dirname -- "$(readlink -f "${BASH_SOURCE}")")"
${dir}/node_modules/ts-node/dist/bin.js ${dir}/src/index.ts $@

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"lib": ["es2020"],
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"incremental": true,
"types": [ "node", "jest" ],
"outDir": "dist",
"skipLibCheck": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist",
"**/*.spec.ts",
"**/*.spec.js"
]
}

View File

@ -0,0 +1,30 @@
{
"extends": "./tsconfig.build.json",
"compilerOptions": {
"composite": true,
"declaration": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@budibase/types": ["../types/src"],
"@budibase/backend-core": ["../backend-core/src"],
"@budibase/backend-core/*": ["../backend-core/*"],
}
},
"ts-node": {
"require": ["tsconfig-paths/register"],
"swc": true
},
"references": [
{ "path": "../types" },
{ "path": "../backend-core" },
],
"include": [
"src/**/*",
"package.json"
],
"exclude": [
"node_modules",
"dist"
]
}

View File

@ -9,6 +9,13 @@
dependencies: dependencies:
"@babel/highlight" "^7.10.4" "@babel/highlight" "^7.10.4"
"@babel/code-frame@^7.12.13":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
dependencies:
"@babel/highlight" "^7.18.6"
"@babel/generator@7.18.2": "@babel/generator@7.18.2":
version "7.18.2" version "7.18.2"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d"
@ -33,7 +40,7 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
"@babel/highlight@^7.10.4": "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6":
version "7.18.6" version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
@ -71,6 +78,13 @@
"@babel/helper-validator-identifier" "^7.19.1" "@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@cspotcode/source-map-support@^0.8.0":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
dependencies:
"@jridgewell/trace-mapping" "0.3.9"
"@eslint/eslintrc@^0.4.3": "@eslint/eslintrc@^0.4.3":
version "0.4.3" version "0.4.3"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
@ -112,6 +126,50 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@jest/create-cache-key-function@^27.4.2":
version "27.5.1"
resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-27.5.1.tgz#7448fae15602ea95c828f5eceed35c202a820b31"
integrity sha512-dmH1yW+makpTSURTy8VzdUwFnfQh1G8R+DxO2Ho2FFmBbKFEVm+3jWdvFhE2VqB/LATCTokkP0dotjyQyw5/AQ==
dependencies:
"@jest/types" "^27.5.1"
"@jest/expect-utils@^29.4.3":
version "29.4.3"
resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.4.3.tgz#95ce4df62952f071bcd618225ac7c47eaa81431e"
integrity sha512-/6JWbkxHOP8EoS8jeeTd9dTfc9Uawi+43oLKHfp6zzux3U2hqOOVnV3ai4RpDYHOccL6g+5nrxpoc8DmJxtXVQ==
dependencies:
jest-get-type "^29.4.3"
"@jest/schemas@^29.4.3":
version "29.4.3"
resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788"
integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==
dependencies:
"@sinclair/typebox" "^0.25.16"
"@jest/types@^27.5.1":
version "27.5.1"
resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80"
integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==
dependencies:
"@types/istanbul-lib-coverage" "^2.0.0"
"@types/istanbul-reports" "^3.0.0"
"@types/node" "*"
"@types/yargs" "^16.0.0"
chalk "^4.0.0"
"@jest/types@^29.4.3":
version "29.4.3"
resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.3.tgz#9069145f4ef09adf10cec1b2901b2d390031431f"
integrity sha512-bPYfw8V65v17m2Od1cv44FH+SiKW7w2Xu7trhcdTLUmSv85rfKsP+qXSjO4KGJr4dtPSzl/gvslZBXctf1qGEA==
dependencies:
"@jest/schemas" "^29.4.3"
"@types/istanbul-lib-coverage" "^2.0.0"
"@types/istanbul-reports" "^3.0.0"
"@types/node" "*"
"@types/yargs" "^17.0.8"
chalk "^4.0.0"
"@jridgewell/gen-mapping@^0.3.0": "@jridgewell/gen-mapping@^0.3.0":
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
@ -121,7 +179,7 @@
"@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9" "@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/resolve-uri@3.1.0": "@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3":
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
@ -136,6 +194,14 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
"@jridgewell/trace-mapping@0.3.9":
version "0.3.9"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
dependencies:
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping@^0.3.9": "@jridgewell/trace-mapping@^0.3.9":
version "0.3.17" version "0.3.17"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985"
@ -182,11 +248,332 @@
resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
"@sinclair/typebox@^0.25.16":
version "0.25.24"
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718"
integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==
"@sindresorhus/is@^0.7.0": "@sindresorhus/is@^0.7.0":
version "0.7.0" version "0.7.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==
"@swc/core-darwin-arm64@1.3.37":
version "1.3.37"
resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.37.tgz#a92e075ae35f18a64aaf3823ea175f03564f8da1"
integrity sha512-iIyVqqioUpVeT/hbBVfkrsjfCyL4idNH+LVKGmoTAWaTTSB0+UNhNuA7Wh2CqIHWh1Mv7IlumitWPcqsVDdoEw==
"@swc/core-darwin-x64@1.3.37":
version "1.3.37"
resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.37.tgz#a3cc06c87140a2ca0b8e7ef1f3d5cc34dd080429"
integrity sha512-dao5nXPWKxtaxqak4ZkRyBoApNIelW/glantQhPhj0FjMjuIQc+v03ldJ8XDByWOG+6xuVUTheANCtEccxoQBw==
"@swc/core-linux-arm-gnueabihf@1.3.37":
version "1.3.37"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.37.tgz#f7d8f8523830c6be653f608839d4bd5598457f1f"
integrity sha512-/mVrc8H/f062CUkqKGmBiil2VIYu4mKawHxERfeP1y38X5K/OwjG5s9MgO9TVxy+Ly6vejwj70kRhSa3hVp1Bw==
"@swc/core-linux-arm64-gnu@1.3.37":
version "1.3.37"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.37.tgz#b162febd9de14fb08000c722b063be2bb5aefa6b"
integrity sha512-eRQ3KaZI0j5LidTfOIi/kUVOOMuVmw1HCdt/Z1TAUKoHMLVxY8xcJ3pEE3/+ednI60EmHpwpJRs6LelXyL6uzQ==
"@swc/core-linux-arm64-musl@1.3.37":
version "1.3.37"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.37.tgz#3b1a628e880fbb1a5e2a7a46d42e8aa878c6bfdd"
integrity sha512-w2BRLODyxNQY2rfHZMZ5ir6QrrnGBPlnIslTrgKmVbn1OjZoxUCtuqhrYnCmybaAc4DOkeH02TqynEFXrm+EMw==
"@swc/core-linux-x64-gnu@1.3.37":
version "1.3.37"
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.37.tgz#ed443ad77dc90e415267d02a38e4113047b2d3d8"
integrity sha512-CfoH8EsZJZ9kunjMUjBNYD5fFuO86zw+K/o4wEw72Yg6ZEiqPmeIlCKU8tpTv4sK+CbhUXrmVzMB5tqsb2jALQ==
"@swc/core-linux-x64-musl@1.3.37":
version "1.3.37"
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.37.tgz#de607a4985458bd6e8b0e40f0d62d0e26bd8df1e"
integrity sha512-9YPrHYNdoG7PK11gV51GfL45biI2dic+YTqHUDKyykemsD7Ot1zUFX7Ty//pdvpKcKSff6SrHbfFACD5ziNirA==
"@swc/core-win32-arm64-msvc@1.3.37":
version "1.3.37"
resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.37.tgz#d5851a47d7df183929b9746d56f76c282f940e6a"
integrity sha512-h17Ek8/wCDje6BrXOvCXBM80oBRmTSMMdLyt87whTl5xqYlWYYs9oQIzZndNRTlNpTgjGO8Ns2eo4kwVxIkBIA==
"@swc/core-win32-ia32-msvc@1.3.37":
version "1.3.37"
resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.37.tgz#06ad7016f61b56aec4abf60eab3a91b786f9e294"
integrity sha512-1BR175E1olGy/zdt94cgdb6ps/lBNissAOaxyBk8taFpcjy3zpdP30yAoH0GIsC6isnZ5JfArbOJNRXXO5tE0Q==
"@swc/core-win32-x64-msvc@1.3.37":
version "1.3.37"
resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.37.tgz#60139a7089003a7447a4efef9704ae8fde21995e"
integrity sha512-1siDQ7dccQ1pesJmgAL3BUBbRPtfbNInOWnZOkiie/DfFqGQ117QKnCVyjUvwFKfTQx1+3UUTDmMSlRd00SlXg==
"@swc/core@^1.3.25":
version "1.3.37"
resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.3.37.tgz#644653fa7deb20c7c342e7fd019c7abc44ecf1bf"
integrity sha512-VOFlEQ1pReOM73N9A7R8rt561GU8Rxsq833jiimWDUB2sXEN3V6n6wFTgYmZuMz2T4/R0cQA1nV48KkaT4gkFw==
optionalDependencies:
"@swc/core-darwin-arm64" "1.3.37"
"@swc/core-darwin-x64" "1.3.37"
"@swc/core-linux-arm-gnueabihf" "1.3.37"
"@swc/core-linux-arm64-gnu" "1.3.37"
"@swc/core-linux-arm64-musl" "1.3.37"
"@swc/core-linux-x64-gnu" "1.3.37"
"@swc/core-linux-x64-musl" "1.3.37"
"@swc/core-win32-arm64-msvc" "1.3.37"
"@swc/core-win32-ia32-msvc" "1.3.37"
"@swc/core-win32-x64-msvc" "1.3.37"
"@swc/jest@^0.2.24":
version "0.2.24"
resolved "https://registry.yarnpkg.com/@swc/jest/-/jest-0.2.24.tgz#35d9377ede049613cd5fdd6c24af2b8dcf622875"
integrity sha512-fwgxQbM1wXzyKzl1+IW0aGrRvAA8k0Y3NxFhKigbPjOJ4mCKnWEcNX9HQS3gshflcxq8YKhadabGUVfdwjCr6Q==
dependencies:
"@jest/create-cache-key-function" "^27.4.2"
jsonc-parser "^3.2.0"
"@tsconfig/node10@^1.0.7":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
"@tsconfig/node12@^1.0.7":
version "1.0.11"
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
"@tsconfig/node14@^1.0.0":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
"@tsconfig/node16@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
"@types/debug@*":
version "4.1.7"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82"
integrity sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==
dependencies:
"@types/ms" "*"
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44"
integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==
"@types/istanbul-lib-report@*":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686"
integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==
dependencies:
"@types/istanbul-lib-coverage" "*"
"@types/istanbul-reports@^3.0.0":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff"
integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==
dependencies:
"@types/istanbul-lib-report" "*"
"@types/jest@^29.4.0":
version "29.4.0"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.4.0.tgz#a8444ad1704493e84dbf07bb05990b275b3b9206"
integrity sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==
dependencies:
expect "^29.0.0"
pretty-format "^29.0.0"
"@types/ms@*":
version "0.7.31"
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
"@types/node-fetch@2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975"
integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==
dependencies:
"@types/node" "*"
form-data "^3.0.0"
"@types/node@*":
version "18.14.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.4.tgz#0e64ec0b35a772e1e3d849f9a0ff61782d0cb647"
integrity sha512-VhCw7I7qO2X49+jaKcAUwi3rR+hbxT5VcYF493+Z5kMLI0DL568b7JI4IDJaxWFH0D/xwmGJNoXisyX+w7GH/g==
"@types/pouchdb-adapter-cordova-sqlite@*":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-cordova-sqlite/-/pouchdb-adapter-cordova-sqlite-1.0.1.tgz#49e5ee6df7cc0c23196fcb340f43a560e74eb1d6"
integrity sha512-nqlXpW1ho3KBg1mUQvZgH2755y3z/rw4UA7ZJCPMRTHofxGMY8izRVw5rHBL4/7P615or0J2udpRYxgkT3D02g==
dependencies:
"@types/pouchdb-core" "*"
"@types/pouchdb-adapter-fruitdown@*":
version "6.1.3"
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-fruitdown/-/pouchdb-adapter-fruitdown-6.1.3.tgz#9b140ad9645cc56068728acf08ec19ac0046658e"
integrity sha512-Wz1Z1JLOW1hgmFQjqnSkmyyfH7by/iWb4abKn684WMvQfmxx6BxKJpJ4+eulkVPQzzgMMSgU1MpnQOm9FgRkbw==
dependencies:
"@types/pouchdb-core" "*"
"@types/pouchdb-adapter-http@*":
version "6.1.3"
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-http/-/pouchdb-adapter-http-6.1.3.tgz#6e592d5f48deb6274a21ddac1498dd308096bcf3"
integrity sha512-9Z4TLbF/KJWy/D2sWRPBA+RNU0odQimfdvlDX+EY7rGcd3aVoH8qjD/X0Xcd/0dfBH5pKrNIMFFQgW/TylRCmA==
dependencies:
"@types/pouchdb-core" "*"
"@types/pouchdb-adapter-idb@*":
version "6.1.4"
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-idb/-/pouchdb-adapter-idb-6.1.4.tgz#cb9a18864585d600820cd325f007614c5c3989cd"
integrity sha512-KIAXbkF4uYUz0ZwfNEFLtEkK44mEWopAsD76UhucH92XnJloBysav+TjI4FFfYQyTjoW3S1s6V+Z14CUJZ0F6w==
dependencies:
"@types/pouchdb-core" "*"
"@types/pouchdb-adapter-leveldb@*":
version "6.1.3"
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-leveldb/-/pouchdb-adapter-leveldb-6.1.3.tgz#17c7e75d75b992050bca15991e97fba575c61bb3"
integrity sha512-ex8NFqQGFwEpFi7AaZ5YofmuemfZNsL3nTFZBUCAKYMBkazQij1pe2ILLStSvJr0XS0qxgXjCEW19T5Wqiiskg==
dependencies:
"@types/pouchdb-core" "*"
"@types/pouchdb-adapter-localstorage@*":
version "6.1.3"
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-localstorage/-/pouchdb-adapter-localstorage-6.1.3.tgz#0dde02ba6b9d6073a295a20196563942ba9a54bd"
integrity sha512-oor040tye1KKiGLWYtIy7rRT7C2yoyX3Tf6elEJRpjOA7Ja/H8lKc4LaSh9ATbptIcES6MRqZDxtp7ly9hsW3Q==
dependencies:
"@types/pouchdb-core" "*"
"@types/pouchdb-adapter-memory@*":
version "6.1.3"
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-memory/-/pouchdb-adapter-memory-6.1.3.tgz#9eabdbc890fcf58960ee8b68b8685f837e75c844"
integrity sha512-gVbsIMzDzgZYThFVT4eVNsmuZwVm/4jDxP1sjlgc3qtDIxbtBhGgyNfcskwwz9Zu5Lv1avkDsIWvcxQhnvRlHg==
dependencies:
"@types/pouchdb-core" "*"
"@types/pouchdb-adapter-node-websql@*":
version "6.1.3"
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-node-websql/-/pouchdb-adapter-node-websql-6.1.3.tgz#aa18bc68af8cf509acd12c400010dcd5fab2243d"
integrity sha512-F/P+os6Jsa7CgHtH64+Z0HfwIcj0hIRB5z8gNhF7L7dxPWoAfkopK5H2gydrP3sQrlGyN4WInF+UJW/Zu1+FKg==
dependencies:
"@types/pouchdb-adapter-websql" "*"
"@types/pouchdb-core" "*"
"@types/pouchdb-adapter-websql@*":
version "6.1.4"
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-websql/-/pouchdb-adapter-websql-6.1.4.tgz#359fbe42ccac0ac90b492ddb8c32fafd0aa96d79"
integrity sha512-zMJQCtXC40hBsIDRn0GhmpeGMK0f9l/OGWfLguvczROzxxcOD7REI+e6SEmX7gJKw5JuMvlfuHzkQwjmvSJbtg==
dependencies:
"@types/pouchdb-core" "*"
"@types/pouchdb-browser@*":
version "6.1.3"
resolved "https://registry.yarnpkg.com/@types/pouchdb-browser/-/pouchdb-browser-6.1.3.tgz#8f33d6ef58d6817d1f6d36979148a1c7f63244d8"
integrity sha512-EdYowrWxW9SWBMX/rux2eq7dbHi5Zeyzz+FF/IAsgQKnUxgeCO5VO2j4zTzos0SDyJvAQU+EYRc11r7xGn5tvA==
dependencies:
"@types/pouchdb-adapter-http" "*"
"@types/pouchdb-adapter-idb" "*"
"@types/pouchdb-adapter-websql" "*"
"@types/pouchdb-core" "*"
"@types/pouchdb-mapreduce" "*"
"@types/pouchdb-replication" "*"
"@types/pouchdb-core@*":
version "7.0.10"
resolved "https://registry.yarnpkg.com/@types/pouchdb-core/-/pouchdb-core-7.0.10.tgz#d1ea1549e7fad6cb579f71459b1bc27252e06a5a"
integrity sha512-mKhjLlWWXyV3PTTjDhzDV1kc2dolO7VYFa75IoKM/hr8Er9eo8RIbS7mJLfC8r/C3p6ihZu9yZs1PWC1LQ0SOA==
dependencies:
"@types/debug" "*"
"@types/pouchdb-find" "*"
"@types/pouchdb-find@*":
version "7.3.0"
resolved "https://registry.yarnpkg.com/@types/pouchdb-find/-/pouchdb-find-7.3.0.tgz#b917030e9f4bf6e56bf8c3b9fe4b2a25e989009a"
integrity sha512-sFPli5tBjGX9UfXioik1jUzPdcN84eV82n0lmEFuoPepWqkLjQcyri0eOa++HYOaNPyMDhKFBqEALEZivK2dRg==
dependencies:
"@types/pouchdb-core" "*"
"@types/pouchdb-http@*":
version "6.1.3"
resolved "https://registry.yarnpkg.com/@types/pouchdb-http/-/pouchdb-http-6.1.3.tgz#09576c0d409da1f8dee34ec5b768415e2472ea52"
integrity sha512-0e9E5SqNOyPl/3FnEIbENssB4FlJsNYuOy131nxrZk36S+y1R/6qO7ZVRypWpGTqBWSuVd7gCsq2UDwO/285+w==
dependencies:
"@types/pouchdb-adapter-http" "*"
"@types/pouchdb-core" "*"
"@types/pouchdb-mapreduce@*":
version "6.1.7"
resolved "https://registry.yarnpkg.com/@types/pouchdb-mapreduce/-/pouchdb-mapreduce-6.1.7.tgz#9ab32d1e0f234f1bf6d1e4c5d7e216e9e23ac0a3"
integrity sha512-WzBwm7tmO9QhfRzVaWT4v6JQSS/fG2OoUDrWrhX87rPe2Pn6laPvdK5li6myNRxCoI/l5e8Jd+oYBAFnaiFucA==
dependencies:
"@types/pouchdb-core" "*"
"@types/pouchdb-node@*":
version "6.1.4"
resolved "https://registry.yarnpkg.com/@types/pouchdb-node/-/pouchdb-node-6.1.4.tgz#5214c0169fcfd2237d373380bbd65a934feb5dfb"
integrity sha512-wnTCH8X1JOPpNOfVhz8HW0AvmdHh6pt40MuRj0jQnK7QEHsHS79WujsKTKSOF8QXtPwpvCNSsI7ut7H7tfxxJQ==
dependencies:
"@types/pouchdb-adapter-http" "*"
"@types/pouchdb-adapter-leveldb" "*"
"@types/pouchdb-core" "*"
"@types/pouchdb-mapreduce" "*"
"@types/pouchdb-replication" "*"
"@types/pouchdb-replication@*":
version "6.4.4"
resolved "https://registry.yarnpkg.com/@types/pouchdb-replication/-/pouchdb-replication-6.4.4.tgz#743406c90f13a988fa3e346ea74ce40acd170d00"
integrity sha512-BsE5LKpjJK4iAf6Fx5kyrMw+33V+Ip7uWldUnU2BYrrvtR+MLD22dcImm7DZN1st2wPPb91i0XEnQzvP0w1C/Q==
dependencies:
"@types/pouchdb-core" "*"
"@types/pouchdb-find" "*"
"@types/pouchdb@^6.4.0":
version "6.4.0"
resolved "https://registry.yarnpkg.com/@types/pouchdb/-/pouchdb-6.4.0.tgz#f9c41ca64b23029f9bf2eb4bf6956e6431cb79f8"
integrity sha512-eGCpX+NXhd5VLJuJMzwe3L79fa9+IDTrAG3CPaf4s/31PD56hOrhDJTSmRELSXuiqXr6+OHzzP0PldSaWsFt7w==
dependencies:
"@types/pouchdb-adapter-cordova-sqlite" "*"
"@types/pouchdb-adapter-fruitdown" "*"
"@types/pouchdb-adapter-http" "*"
"@types/pouchdb-adapter-idb" "*"
"@types/pouchdb-adapter-leveldb" "*"
"@types/pouchdb-adapter-localstorage" "*"
"@types/pouchdb-adapter-memory" "*"
"@types/pouchdb-adapter-node-websql" "*"
"@types/pouchdb-adapter-websql" "*"
"@types/pouchdb-browser" "*"
"@types/pouchdb-core" "*"
"@types/pouchdb-http" "*"
"@types/pouchdb-mapreduce" "*"
"@types/pouchdb-node" "*"
"@types/pouchdb-replication" "*"
"@types/stack-utils@^2.0.0":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
"@types/yargs-parser@*":
version "21.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b"
integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==
"@types/yargs@^16.0.0":
version "16.0.5"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.5.tgz#12cc86393985735a283e387936398c2f9e5f88e3"
integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==
dependencies:
"@types/yargs-parser" "*"
"@types/yargs@^17.0.8":
version "17.0.22"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a"
integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==
dependencies:
"@types/yargs-parser" "*"
abort-controller@3.0.0: abort-controller@3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
@ -221,11 +608,21 @@ acorn-jsx@^5.3.1:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
acorn-walk@^8.1.1:
version "8.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
acorn@^7.4.0: acorn@^7.4.0:
version "7.4.1" version "7.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
acorn@^8.4.1:
version "8.8.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
agent-base@6: agent-base@6:
version "6.0.2" version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
@ -289,6 +686,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
dependencies: dependencies:
color-convert "^2.0.1" color-convert "^2.0.1"
ansi-styles@^5.0.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
aproba@^1.0.3: aproba@^1.0.3:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
@ -309,6 +711,11 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0" delegates "^1.0.0"
readable-stream "^2.0.6" readable-stream "^2.0.6"
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
argparse@^1.0.7: argparse@^1.0.7:
version "1.0.10" version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@ -351,6 +758,11 @@ astral-regex@^2.0.0:
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
at-least-node@^1.0.0: at-least-node@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
@ -519,6 +931,11 @@ chownr@^2.0.0:
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
ci-info@^3.2.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91"
integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==
cli-cursor@^3.1.0: cli-cursor@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
@ -588,6 +1005,13 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
command-line-args@^5.2.0: command-line-args@^5.2.0:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e"
@ -658,6 +1082,11 @@ core-util-is@~1.0.0:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-spawn@^7.0.2: cross-spawn@^7.0.2:
version "7.0.3" version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@ -774,6 +1203,11 @@ deferred-leveldown@~5.3.0:
abstract-leveldown "~6.2.1" abstract-leveldown "~6.2.1"
inherits "^2.0.3" inherits "^2.0.3"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
delegates@^1.0.0: delegates@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
@ -784,6 +1218,16 @@ detect-libc@^1.0.3:
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
diff-sequences@^29.4.3:
version "29.4.3"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2"
integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==
diff@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
dir-glob@^3.0.1: dir-glob@^3.0.1:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@ -791,10 +1235,12 @@ dir-glob@^3.0.1:
dependencies: dependencies:
path-type "^4.0.0" path-type "^4.0.0"
docker-compose@0.23.6: docker-compose@0.23.12:
version "0.23.6" version "0.23.12"
resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.23.6.tgz#bd21e17d599f17fcf7a4b5d607cff0358a9c378b" resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.23.12.tgz#fa883b98be08f6926143d06bf9e522ef7ed3210c"
integrity sha512-y3Q8MkwG862rNqkvEQG59/7Fi2/fzs3NYDCvqUAAD+z0WGs2qcJ9hRcn34hWgWv9ouPkFqe3Vwca0h+4bIIRWw== integrity sha512-KFbSMqQBuHjTGZGmYDOCO0L4SaML3BsWTId5oSUyaBa22vALuFHNv+UdDWs3HcMylHWKsxCbLB7hnM/nCosWZw==
dependencies:
yaml "^1.10.2"
doctrine@^3.0.0: doctrine@^3.0.0:
version "3.0.0" version "3.0.0"
@ -888,6 +1334,11 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
escape-string-regexp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
escape-string-regexp@^4.0.0: escape-string-regexp@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
@ -1017,6 +1468,17 @@ expand-template@^2.0.3:
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
expect@^29.0.0:
version "29.4.3"
resolved "https://registry.yarnpkg.com/expect/-/expect-29.4.3.tgz#5e47757316df744fe3b8926c3ae8a3ebdafff7fe"
integrity sha512-uC05+Q7eXECFpgDrHdXA4k2rpMyStAYPItEDLyQDo5Ta7fVkJnNA/4zh/OIVkVVNZ1oOK1PipQoyNjuZ6sz6Dg==
dependencies:
"@jest/expect-utils" "^29.4.3"
jest-get-type "^29.4.3"
jest-matcher-utils "^29.4.3"
jest-message-util "^29.4.3"
jest-util "^29.4.3"
ext-list@^2.0.0: ext-list@^2.0.0:
version "2.2.2" version "2.2.2"
resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37"
@ -1191,6 +1653,15 @@ follow-redirects@^1.14.0:
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5"
integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
form-data@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
from2@^2.1.1, from2@^2.3.0: from2@^2.1.1, from2@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
@ -1348,7 +1819,7 @@ got@^8.3.1:
url-parse-lax "^3.0.0" url-parse-lax "^3.0.0"
url-to-options "^1.0.1" url-to-options "^1.0.1"
graceful-fs@^4.1.10, graceful-fs@^4.1.6, graceful-fs@^4.2.0: graceful-fs@^4.1.10, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9:
version "4.2.10" version "4.2.10"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
@ -1604,6 +2075,58 @@ isurl@^1.0.0-alpha5:
has-to-string-tag-x "^1.2.0" has-to-string-tag-x "^1.2.0"
is-object "^1.0.1" is-object "^1.0.1"
jest-diff@^29.4.3:
version "29.4.3"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.4.3.tgz#42f4eb34d0bf8c0fb08b0501069b87e8e84df347"
integrity sha512-YB+ocenx7FZ3T5O9lMVMeLYV4265socJKtkwgk/6YUz/VsEzYDkiMuMhWzZmxm3wDRQvayJu/PjkjjSkjoHsCA==
dependencies:
chalk "^4.0.0"
diff-sequences "^29.4.3"
jest-get-type "^29.4.3"
pretty-format "^29.4.3"
jest-get-type@^29.4.3:
version "29.4.3"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5"
integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==
jest-matcher-utils@^29.4.3:
version "29.4.3"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.4.3.tgz#ea68ebc0568aebea4c4213b99f169ff786df96a0"
integrity sha512-TTciiXEONycZ03h6R6pYiZlSkvYgT0l8aa49z/DLSGYjex4orMUcafuLXYyyEDWB1RKglq00jzwY00Ei7yFNVg==
dependencies:
chalk "^4.0.0"
jest-diff "^29.4.3"
jest-get-type "^29.4.3"
pretty-format "^29.4.3"
jest-message-util@^29.4.3:
version "29.4.3"
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.4.3.tgz#65b5280c0fdc9419503b49d4f48d4999d481cb5b"
integrity sha512-1Y8Zd4ZCN7o/QnWdMmT76If8LuDv23Z1DRovBj/vcSFNlGCJGoO8D1nJDw1AdyAGUk0myDLFGN5RbNeJyCRGCw==
dependencies:
"@babel/code-frame" "^7.12.13"
"@jest/types" "^29.4.3"
"@types/stack-utils" "^2.0.0"
chalk "^4.0.0"
graceful-fs "^4.2.9"
micromatch "^4.0.4"
pretty-format "^29.4.3"
slash "^3.0.0"
stack-utils "^2.0.3"
jest-util@^29.4.3:
version "29.4.3"
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.3.tgz#851a148e23fc2b633c55f6dad2e45d7f4579f496"
integrity sha512-ToSGORAz4SSSoqxDSylWX8JzkOQR7zoBtNRsA7e+1WUX5F8jrOwaNpuh1YfJHJKDHXLHmObv5eOjejUd+/Ws+Q==
dependencies:
"@jest/types" "^29.4.3"
"@types/node" "*"
chalk "^4.0.0"
ci-info "^3.2.0"
graceful-fs "^4.2.9"
picomatch "^2.2.3"
joi@17.6.0: joi@17.6.0:
version "17.6.0" version "17.6.0"
resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2" resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2"
@ -1663,6 +2186,11 @@ json-stringify-safe@^5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
jsonc-parser@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76"
integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==
jsonfile@^6.0.1: jsonfile@^6.0.1:
version "6.1.0" version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
@ -1857,6 +2385,11 @@ make-dir@^2.1.0:
pify "^4.0.1" pify "^4.0.1"
semver "^5.6.0" semver "^5.6.0"
make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
md5@^2.3.0: md5@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f"
@ -1879,11 +2412,18 @@ micromatch@^4.0.4:
braces "^3.0.2" braces "^3.0.2"
picomatch "^2.3.1" picomatch "^2.3.1"
mime-db@^1.28.0: mime-db@1.52.0, mime-db@^1.28.0:
version "1.52.0" version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mimic-fn@^2.1.0: mimic-fn@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
@ -1991,7 +2531,7 @@ node-abi@^2.21.0:
dependencies: dependencies:
semver "^5.4.1" semver "^5.4.1"
node-fetch@2, node-fetch@2.6.7, node-fetch@^2.6.6: node-fetch@2.6.7, node-fetch@^2.6.6:
version "2.6.7" version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@ -2137,7 +2677,7 @@ pend@~1.2.0:
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
picomatch@^2.3.1: picomatch@^2.2.3, picomatch@^2.3.1:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
@ -2300,6 +2840,15 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA== integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==
pretty-format@^29.0.0, pretty-format@^29.4.3:
version "29.4.3"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.3.tgz#25500ada21a53c9e8423205cf0337056b201244c"
integrity sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==
dependencies:
"@jest/schemas" "^29.4.3"
ansi-styles "^5.0.0"
react-is "^18.0.0"
printj@^1.3.0: printj@^1.3.0:
version "1.3.1" version "1.3.1"
resolved "https://registry.yarnpkg.com/printj/-/printj-1.3.1.tgz#9af6b1d55647a1587ac44f4c1654a4b95b8e12cb" resolved "https://registry.yarnpkg.com/printj/-/printj-1.3.1.tgz#9af6b1d55647a1587ac44f4c1654a4b95b8e12cb"
@ -2374,6 +2923,11 @@ rc@^1.2.7:
minimist "^1.2.0" minimist "^1.2.0"
strip-json-comments "~2.0.1" strip-json-comments "~2.0.1"
react-is@^18.0.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
readable-stream@1.1.14, readable-stream@^1.0.27-1: readable-stream@1.1.14, readable-stream@^1.0.27-1:
version "1.1.14" version "1.1.14"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
@ -2656,6 +3210,13 @@ sprintf-js@~1.0.2:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
stack-utils@^2.0.3:
version "2.0.6"
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f"
integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==
dependencies:
escape-string-regexp "^2.0.0"
stream-meter@^1.0.4: stream-meter@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/stream-meter/-/stream-meter-1.0.4.tgz#52af95aa5ea760a2491716704dbff90f73afdd1d" resolved "https://registry.yarnpkg.com/stream-meter/-/stream-meter-1.0.4.tgz#52af95aa5ea760a2491716704dbff90f73afdd1d"
@ -2911,6 +3472,25 @@ trim-repeated@^1.0.0:
dependencies: dependencies:
escape-string-regexp "^1.0.2" escape-string-regexp "^1.0.2"
ts-node@^10.9.1:
version "10.9.1"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
dependencies:
"@cspotcode/source-map-support" "^0.8.0"
"@tsconfig/node10" "^1.0.7"
"@tsconfig/node12" "^1.0.7"
"@tsconfig/node14" "^1.0.0"
"@tsconfig/node16" "^1.0.2"
acorn "^8.4.1"
acorn-walk "^8.1.1"
arg "^4.1.0"
create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"
tslib@^1.9.0: tslib@^1.9.0:
version "1.14.1" version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
@ -2940,6 +3520,11 @@ type-fest@^0.21.3:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
typescript@4.7.3:
version "4.7.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d"
integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==
typical@^4.0.0: typical@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4"
@ -3015,6 +3600,11 @@ uuid@8.3.2, uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
v8-compile-cache@^2.0.3: v8-compile-cache@^2.0.3:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
@ -3101,6 +3691,11 @@ yallist@^4.0.0:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
yaml@^1.10.2:
version "1.10.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yaml@^2.1.1: yaml@^2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.1.tgz#1e06fb4ca46e60d9da07e4f786ea370ed3c3cfec" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.1.tgz#1e06fb4ca46e60d9da07e4f786ea370ed3c3cfec"
@ -3131,3 +3726,8 @@ yauzl@^2.4.2:
dependencies: dependencies:
buffer-crc32 "~0.2.3" buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0" fd-slicer "~1.1.0"
yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "2.3.18-alpha.29", "version": "2.4.8-alpha.4",
"license": "MPL-2.0", "license": "MPL-2.0",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
@ -19,9 +19,11 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "2.3.18-alpha.29", "@budibase/bbui": "2.4.8-alpha.4",
"@budibase/frontend-core": "2.3.18-alpha.29", "@budibase/frontend-core": "2.4.8-alpha.4",
"@budibase/string-templates": "2.3.18-alpha.29", "@budibase/shared-core": "2.4.8-alpha.4",
"@budibase/string-templates": "2.4.8-alpha.4",
"@budibase/types": "2.4.8-alpha.4",
"@spectrum-css/button": "^3.0.3", "@spectrum-css/button": "^3.0.3",
"@spectrum-css/card": "^3.0.3", "@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3", "@spectrum-css/divider": "^1.0.3",

View File

@ -20,6 +20,19 @@ const ignoredWarnings = [
"a11y-click-events-have-key-events", "a11y-click-events-have-key-events",
] ]
const devPaths = production
? []
: [
{
find: "@budibase/shared-core",
replacement: path.resolve("../shared-core/dist/mjs/src/index"),
},
{
find: "@budibase/types",
replacement: path.resolve("../types/dist/mjs/index"),
},
]
export default { export default {
input: "src/index.js", input: "src/index.js",
output: [ output: [
@ -69,6 +82,7 @@ export default {
find: "sdk", find: "sdk",
replacement: path.resolve("./src/sdk"), replacement: path.resolve("./src/sdk"),
}, },
...devPaths,
], ],
}), }),
svelte({ svelte({

View File

@ -57,6 +57,9 @@
const onFieldChange = (expression, field) => { const onFieldChange = (expression, field) => {
// Update the field type // Update the field type
expression.type = schemaFields.find(x => x.name === field)?.type expression.type = schemaFields.find(x => x.name === field)?.type
expression.externalType = schemaFields.find(
x => x.name === field
)?.externalType
// Ensure a valid operator is set // Ensure a valid operator is set
const validOperators = LuceneUtils.getValidOperatorsForType( const validOperators = LuceneUtils.getValidOperatorsForType(

View File

@ -283,27 +283,32 @@
if (mapInstance) { if (mapInstance) {
mapInstance.remove() mapInstance.remove()
} }
mapInstance = L.map(embeddedMapId, mapOptions)
mapMarkerGroup.addTo(mapInstance)
candidateMarkerGroup.addTo(mapInstance)
// Add attribution try {
const cleanAttribution = sanitizeHtml(attribution, { mapInstance = L.map(embeddedMapId, mapOptions)
allowedTags: ["a"], mapMarkerGroup.addTo(mapInstance)
allowedAttributes: { candidateMarkerGroup.addTo(mapInstance)
a: ["href", "target"],
},
})
L.tileLayer(tileURL, {
attribution: "&copy; " + cleanAttribution,
zoom,
}).addTo(mapInstance)
// Add click handler // Add attribution
mapInstance.on("click", handleMapClick) const cleanAttribution = sanitizeHtml(attribution, {
allowedTags: ["a"],
allowedAttributes: {
a: ["href", "target"],
},
})
L.tileLayer(tileURL, {
attribution: "&copy; " + cleanAttribution,
zoom,
}).addTo(mapInstance)
// Reset view // Add click handler
resetView() mapInstance.on("click", handleMapClick)
// Reset view
resetView()
} catch (e) {
console.log("There was a problem with the map", e)
}
} }
const handleMapClick = e => { const handleMapClick = e => {

View File

@ -81,6 +81,7 @@ export const getRelationshipSchemaAdditions = async schema => {
Object.keys(linkSchema || {}).forEach(linkKey => { Object.keys(linkSchema || {}).forEach(linkKey => {
relationshipAdditions[`${fieldKey}.${linkKey}`] = { relationshipAdditions[`${fieldKey}.${linkKey}`] = {
type: linkSchema[linkKey].type, type: linkSchema[linkKey].type,
externalType: linkSchema[linkKey].externalType,
} }
}) })
} }

View File

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

View File

@ -194,8 +194,7 @@ export const buildUserEndpoints = API => ({
}, },
/** /**
* Retrieves the invitation associated with a provided code. * Retrieves all user invitations for the current tenant.
* @param code The unique code for the target invite
*/ */
getUserInvites: async () => { getUserInvites: async () => {
return await API.get({ return await API.get({

View File

@ -1,56 +1,7 @@
/** /**
* Operator options for lucene queries * Operator options for lucene queries
*/ */
export const OperatorOptions = { export { OperatorOptions, SqlNumberTypeRangeMap } from "@budibase/shared-core"
Equals: {
value: "equal",
label: "Equals",
},
NotEquals: {
value: "notEqual",
label: "Not equals",
},
Empty: {
value: "empty",
label: "Is empty",
},
NotEmpty: {
value: "notEmpty",
label: "Is not empty",
},
StartsWith: {
value: "string",
label: "Starts with",
},
Like: {
value: "fuzzy",
label: "Like",
},
MoreThan: {
value: "rangeLow",
label: "More than or equal to",
},
LessThan: {
value: "rangeHigh",
label: "Less than or equal to",
},
Contains: {
value: "contains",
label: "Contains",
},
NotContains: {
value: "notContains",
label: "Does not contain",
},
In: {
value: "oneOf",
label: "Is in",
},
ContainsAny: {
value: "containsAny",
label: "Has any",
},
}
// Cookie names // Cookie names
export const Cookies = { export const Cookies = {
@ -127,27 +78,6 @@ export const Roles = {
PUBLIC: "PUBLIC", PUBLIC: "PUBLIC",
BUILDER: "BUILDER", BUILDER: "BUILDER",
} }
/**
* Maximum minimum range for SQL number values
*/
export const SqlNumberTypeRangeMap = {
integer: {
max: 2147483647,
min: -2147483648,
},
int: {
max: 2147483647,
min: -2147483648,
},
smallint: {
max: 32767,
min: -32768,
},
mediumint: {
max: 8388607,
min: -8388608,
},
}
export const Themes = [ export const Themes = [
{ {

View File

@ -1,13 +1,11 @@
import { writable, derived, get } from "svelte/store" import { writable, derived, get } from "svelte/store"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import { import { LuceneUtils } from "../utils"
buildLuceneQuery,
luceneLimit,
runLuceneQuery,
luceneSort,
} from "../utils/lucene"
import { convertJSONSchemaToTableSchema } from "../utils/json" import { convertJSONSchemaToTableSchema } from "../utils/json"
const { buildLuceneQuery, luceneLimit, runLuceneQuery, luceneSort } =
LuceneUtils
/** /**
* Parent class which handles the implementation of fetching data from an * Parent class which handles the implementation of fetching data from an
* internal table or datasource plus. * internal table or datasource plus.

View File

@ -1,4 +1,4 @@
export * as LuceneUtils from "./lucene" export { dataFilters as LuceneUtils } from "@budibase/shared-core"
export * as JSONUtils from "./json" export * as JSONUtils from "./json"
export * as CookieUtils from "./cookies" export * as CookieUtils from "./cookies"
export * as RoleUtils from "./roles" export * as RoleUtils from "./roles"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/sdk", "name": "@budibase/sdk",
"version": "2.3.18-alpha.29", "version": "2.4.8-alpha.4",
"description": "Budibase Public API SDK", "description": "Budibase Public API SDK",
"author": "Budibase", "author": "Budibase",
"license": "MPL-2.0", "license": "MPL-2.0",

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