Merge branch 'feature/day-pass-pricing' of github.com:Budibase/budibase into feature/day-pass-pricing

This commit is contained in:
Dean 2022-09-01 15:33:16 +01:00
commit 6ed4fede00
160 changed files with 4933 additions and 1771 deletions

View File

@ -68,16 +68,28 @@ jobs:
] ]
env: env:
KUBECONFIG_FILE: '${{ secrets.RELEASE_KUBECONFIG }}' KUBECONFIG_FILE: '${{ secrets.RELEASE_KUBECONFIG }}'
- name: Set the base64 kubeconfig
run: echo 'RELEASE_KUBECONFIG=${{ secrets.RELEASE_KUBECONFIG }}' | base64
- name: Re roll the services - name: Re roll app-service
uses: actions-hub/kubectl@master uses: actions-hub/kubectl@master
env: env:
KUBE_CONFIG: ${{ env.RELEASE_KUBECONFIG }} KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG_BASE64 }}
with: with:
args: rollout restart deployment proxy-service -n budibase && kubectl rollout restart deployment app-service -n budibase && kubectl rollout restart deployment worker-service -n budibase args: rollout restart deployment app-service -n budibase
- name: Re roll proxy-service
uses: actions-hub/kubectl@master
env:
KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG_BASE64 }}
with:
args: rollout restart deployment proxy-service -n budibase
- name: Re roll worker-service
uses: actions-hub/kubectl@master
env:
KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG_BASE64 }}
with:
args: rollout restart deployment worker-service -n budibase
- name: Discord Webhook Action - name: Discord Webhook Action
uses: tsickert/discord-webhook@v4.0.0 uses: tsickert/discord-webhook@v4.0.0

View File

@ -121,15 +121,26 @@ jobs:
env: env:
KUBECONFIG_FILE: '${{ secrets.RELEASE_KUBECONFIG }}' KUBECONFIG_FILE: '${{ secrets.RELEASE_KUBECONFIG }}'
- name: Set the base64 kubeconfig - name: Re roll app-service
run: echo 'RELEASE_KUBECONFIG=${{ secrets.RELEASE_KUBECONFIG }}' | base64
- name: Re roll the services
uses: actions-hub/kubectl@master uses: actions-hub/kubectl@master
env: env:
KUBE_CONFIG: ${{ env.RELEASE_KUBECONFIG }} KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG_BASE64 }}
with: with:
args: rollout restart deployment proxy-service -n budibase && kubectl rollout restart deployment app-service -n budibase && kubectl rollout restart deployment worker-service -n budibase args: rollout restart deployment app-service -n budibase
- name: Re roll proxy-service
uses: actions-hub/kubectl@master
env:
KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG_BASE64 }}
with:
args: rollout restart deployment proxy-service -n budibase
- name: Re roll worker-service
uses: actions-hub/kubectl@master
env:
KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG_BASE64 }}
with:
args: rollout restart deployment worker-service -n budibase
- name: Discord Webhook Action - name: Discord Webhook Action
uses: tsickert/discord-webhook@v4.0.0 uses: tsickert/discord-webhook@v4.0.0

View File

@ -1,4 +1,4 @@
name: Budibase Smoke Test name: Budibase Nightly Tests
on: on:
workflow_dispatch: workflow_dispatch:
@ -6,7 +6,7 @@ on:
- cron: "0 5 * * *" # every day at 5AM - cron: "0 5 * * *" # every day at 5AM
jobs: jobs:
release: nightly:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -43,6 +43,18 @@ jobs:
name: Test Reports name: Test Reports
path: packages/builder/cypress/reports/testReport.html path: packages/builder/cypress/reports/testReport.html
# TODO: enable once running in QA test env
# - 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: Upload test results HTML
# uses: aws-actions/configure-aws-credentials@v1
# run: aws s3 cp packages/builder/cypress/reports/testReport.html s3://{{ secrets.BUDI_QA_REPORTS_BUCKET_NAME }}/$GITHUB_RUN_ID/index.html
- name: Cypress Discord Notify - name: Cypress Discord Notify
run: yarn test:e2e:ci:notify run: yarn test:e2e:ci:notify
env: env:

View File

@ -4,7 +4,7 @@
"singleQuote": false, "singleQuote": false,
"trailingComma": "es5", "trailingComma": "es5",
"arrowParens": "avoid", "arrowParens": "avoid",
"jsxBracketSameLine": false, "bracketSameLine": false,
"plugins": ["prettier-plugin-svelte"], "plugins": ["prettier-plugin-svelte"],
"svelteSortOrder": "options-scripts-markup-styles" "svelteSortOrder": "options-scripts-markup-styles"
} }

View File

@ -130,6 +130,22 @@ spec:
- name: BB_ADMIN_USER_PASSWORD - name: BB_ADMIN_USER_PASSWORD
value: { { .Values.globals.bbAdminUserPassword | quote } } value: { { .Values.globals.bbAdminUserPassword | quote } }
{{ end }} {{ end }}
{{ if .Values.services.apps.nodeDebug }}
- name: NODE_DEBUG
value: {{ .Values.services.apps.nodeDebug | quote }}
{{ end }}
{{ if .Values.globals.elasticApmEnabled }}
- name: ELASTIC_APM_ENABLED
value: {{ .Values.globals.elasticApmEnabled | quote }}
{{ end }}
{{ if .Values.globals.elasticApmSecretToken }}
- name: ELASTIC_APM_SECRET_TOKEN
value: {{ .Values.globals.elasticApmSecretToken | quote }}
{{ end }}
{{ if .Values.globals.elasticApmServerUrl }}
- name: ELASTIC_APM_SERVER_URL
value: {{ .Values.globals.elasticApmServerUrl | quote }}
{{ end }}
image: budibase/apps:{{ .Values.globals.appVersion }} image: budibase/apps:{{ .Values.globals.appVersion }}
imagePullPolicy: Always imagePullPolicy: Always

View File

@ -27,6 +27,8 @@ spec:
spec: spec:
containers: containers:
- env: - env:
- name: BUDIBASE_ENVIRONMENT
value: {{ .Values.globals.budibaseEnv }}
- name: DEPLOYMENT_ENVIRONMENT - name: DEPLOYMENT_ENVIRONMENT
value: "kubernetes" value: "kubernetes"
- name: CLUSTER_PORT - name: CLUSTER_PORT
@ -125,6 +127,19 @@ spec:
value: {{ .Values.globals.google.secret | quote }} value: {{ .Values.globals.google.secret | quote }}
- name: TENANT_FEATURE_FLAGS - name: TENANT_FEATURE_FLAGS
value: {{ .Values.globals.tenantFeatureFlags | quote }} value: {{ .Values.globals.tenantFeatureFlags | quote }}
{{ if .Values.globals.elasticApmEnabled }}
- name: ELASTIC_APM_ENABLED
value: {{ .Values.globals.elasticApmEnabled | quote }}
{{ end }}
{{ if .Values.globals.elasticApmSecretToken }}
- name: ELASTIC_APM_SECRET_TOKEN
value: {{ .Values.globals.elasticApmSecretToken | quote }}
{{ end }}
{{ if .Values.globals.elasticApmServerUrl }}
- name: ELASTIC_APM_SERVER_URL
value: {{ .Values.globals.elasticApmServerUrl | quote }}
{{ end }}
image: budibase/worker:{{ .Values.globals.appVersion }} image: budibase/worker:{{ .Values.globals.appVersion }}
imagePullPolicy: Always imagePullPolicy: Always
livenessProbe: livenessProbe:

View File

@ -114,6 +114,10 @@ globals:
smtp: smtp:
enabled: false enabled: false
# elasticApmEnabled:
# elasticApmSecretToken:
# elasticApmServerUrl:
services: services:
budibaseVersion: latest budibaseVersion: latest
dns: cluster.local dns: cluster.local
@ -126,6 +130,7 @@ services:
port: 4002 port: 4002
replicaCount: 1 replicaCount: 1
logLevel: info logLevel: info
# nodeDebug: "" # set the value of NODE_DEBUG
worker: worker:
port: 4003 port: 4003

View File

@ -15,7 +15,10 @@ http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" ' '$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"'; '"$http_user_agent" "$http_x_forwarded_for" '
'response_time=$upstream_response_time proxy_host=$proxy_host upstream_addr=$upstream_addr';
access_log /var/log/nginx/access.log main;
map $http_upgrade $connection_upgrade { map $http_upgrade $connection_upgrade {
default "upgrade"; default "upgrade";
@ -62,6 +65,10 @@ http {
proxy_pass http://{{ address }}:4001; proxy_pass http://{{ address }}:4001;
} }
location /preview {
proxy_pass http://{{ address }}:4001;
}
location /builder { location /builder {
proxy_pass http://{{ address }}:3000; proxy_pass http://{{ address }}:3000;
rewrite ^/builder(.*)$ /builder/$1 break; rewrite ^/builder(.*)$ /builder/$1 break;

View File

@ -33,7 +33,10 @@ http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" ' '$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"'; '"$http_user_agent" "$http_x_forwarded_for" '
'response_time=$upstream_response_time proxy_host=$proxy_host upstream_addr=$upstream_addr';
access_log /var/log/nginx/access.log main;
map $http_upgrade $connection_upgrade { map $http_upgrade $connection_upgrade {
default "upgrade"; default "upgrade";
@ -85,6 +88,10 @@ http {
proxy_pass http://$apps:4002; proxy_pass http://$apps:4002;
} }
location /preview {
proxy_pass http://$apps:4002;
}
location = / { location = / {
proxy_pass http://$apps:4002; proxy_pass http://$apps:4002;
} }
@ -94,6 +101,7 @@ http {
proxy_pass http://$watchtower:8080; proxy_pass http://$watchtower:8080;
} }
{{/if}} {{/if}}
location ~ ^/(builder|app_) { location ~ ^/(builder|app_) {
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Connection $connection_upgrade; proxy_set_header Connection $connection_upgrade;

View File

@ -1,5 +1,5 @@
{ {
"version": "1.2.41-alpha.5", "version": "1.2.58-alpha.5",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/backend-core", "name": "@budibase/backend-core",
"version": "1.2.41-alpha.5", "version": "1.2.58-alpha.5",
"description": "Budibase backend core libraries used in server and worker", "description": "Budibase backend core libraries used in server and worker",
"main": "dist/src/index.js", "main": "dist/src/index.js",
"types": "dist/src/index.d.ts", "types": "dist/src/index.d.ts",
@ -20,7 +20,7 @@
"test:watch": "jest --watchAll" "test:watch": "jest --watchAll"
}, },
"dependencies": { "dependencies": {
"@budibase/types": "1.2.41-alpha.5", "@budibase/types": "1.2.58-alpha.5",
"@techpass/passport-openidconnect": "0.3.2", "@techpass/passport-openidconnect": "0.3.2",
"aws-sdk": "2.1030.0", "aws-sdk": "2.1030.0",
"bcrypt": "5.0.1", "bcrypt": "5.0.1",

View File

@ -1,11 +1,11 @@
const passport = require("koa-passport") const passport = require("koa-passport")
const LocalStrategy = require("passport-local").Strategy const LocalStrategy = require("passport-local").Strategy
const JwtStrategy = require("passport-jwt").Strategy const JwtStrategy = require("passport-jwt").Strategy
const { getGlobalDB } = require("./tenancy") import { getGlobalDB } from "./tenancy"
const refresh = require("passport-oauth2-refresh") const refresh = require("passport-oauth2-refresh")
const { Configs } = require("./constants") import { Configs } from "./constants"
const { getScopedConfig } = require("./db/utils") import { getScopedConfig } from "./db/utils"
const { import {
jwt, jwt,
local, local,
authenticated, authenticated,
@ -13,7 +13,6 @@ const {
oidc, oidc,
auditLog, auditLog,
tenancy, tenancy,
appTenancy,
authError, authError,
ssoCallbackUrl, ssoCallbackUrl,
csrf, csrf,
@ -22,32 +21,36 @@ const {
builderOnly, builderOnly,
builderOrAdmin, builderOrAdmin,
joiValidator, joiValidator,
} = require("./middleware") } from "./middleware"
import { invalidateUser } from "./cache/user"
const { invalidateUser } = require("./cache/user") import { User } from "@budibase/types"
// Strategies // Strategies
passport.use(new LocalStrategy(local.options, local.authenticate)) passport.use(new LocalStrategy(local.options, local.authenticate))
passport.use(new JwtStrategy(jwt.options, jwt.authenticate)) passport.use(new JwtStrategy(jwt.options, jwt.authenticate))
passport.serializeUser((user, done) => done(null, user)) passport.serializeUser((user: User, done: any) => done(null, user))
passport.deserializeUser(async (user, done) => { passport.deserializeUser(async (user: User, done: any) => {
const db = getGlobalDB() const db = getGlobalDB()
try { try {
const user = await db.get(user._id) const dbUser = await db.get(user._id)
return done(null, user) return done(null, dbUser)
} catch (err) { } catch (err) {
console.error(`User not found`, err) console.error(`User not found`, err)
return done(null, false, { message: "User not found" }) return done(null, false, { message: "User not found" })
} }
}) })
async function refreshOIDCAccessToken(db, chosenConfig, refreshToken) { async function refreshOIDCAccessToken(
db: any,
chosenConfig: any,
refreshToken: string
) {
const callbackUrl = await oidc.getCallbackUrl(db, chosenConfig) const callbackUrl = await oidc.getCallbackUrl(db, chosenConfig)
let enrichedConfig let enrichedConfig: any
let strategy let strategy: any
try { try {
enrichedConfig = await oidc.fetchStrategyConfig(chosenConfig, callbackUrl) enrichedConfig = await oidc.fetchStrategyConfig(chosenConfig, callbackUrl)
@ -70,22 +73,28 @@ async function refreshOIDCAccessToken(db, chosenConfig, refreshToken) {
refresh.requestNewAccessToken( refresh.requestNewAccessToken(
Configs.OIDC, Configs.OIDC,
refreshToken, refreshToken,
(err, accessToken, refreshToken, params) => { (err: any, accessToken: string, refreshToken: any, params: any) => {
resolve({ err, accessToken, refreshToken, params }) resolve({ err, accessToken, refreshToken, params })
} }
) )
}) })
} }
async function refreshGoogleAccessToken(db, config, refreshToken) { async function refreshGoogleAccessToken(
db: any,
config: any,
refreshToken: any
) {
let callbackUrl = await google.getCallbackUrl(db, config) let callbackUrl = await google.getCallbackUrl(db, config)
let strategy let strategy
try { try {
strategy = await google.strategyFactory(config, callbackUrl) strategy = await google.strategyFactory(config, callbackUrl)
} catch (err) { } catch (err: any) {
console.error(err) console.error(err)
throw new Error("Error constructing OIDC refresh strategy", err) throw new Error(
`Error constructing OIDC refresh strategy: message=${err.message}`
)
} }
refresh.use(strategy) refresh.use(strategy)
@ -94,14 +103,18 @@ async function refreshGoogleAccessToken(db, config, refreshToken) {
refresh.requestNewAccessToken( refresh.requestNewAccessToken(
Configs.GOOGLE, Configs.GOOGLE,
refreshToken, refreshToken,
(err, accessToken, refreshToken, params) => { (err: any, accessToken: string, refreshToken: string, params: any) => {
resolve({ err, accessToken, refreshToken, params }) resolve({ err, accessToken, refreshToken, params })
} }
) )
}) })
} }
async function refreshOAuthToken(refreshToken, configType, configId) { async function refreshOAuthToken(
refreshToken: string,
configType: string,
configId: string
) {
const db = getGlobalDB() const db = getGlobalDB()
const config = await getScopedConfig(db, { const config = await getScopedConfig(db, {
@ -113,7 +126,7 @@ async function refreshOAuthToken(refreshToken, configType, configId) {
let refreshResponse let refreshResponse
if (configType === Configs.OIDC) { if (configType === Configs.OIDC) {
// configId - retrieved from cookie. // configId - retrieved from cookie.
chosenConfig = config.configs.filter(c => c.uuid === configId)[0] chosenConfig = config.configs.filter((c: any) => c.uuid === configId)[0]
if (!chosenConfig) { if (!chosenConfig) {
throw new Error("Invalid OIDC configuration") throw new Error("Invalid OIDC configuration")
} }
@ -134,7 +147,7 @@ async function refreshOAuthToken(refreshToken, configType, configId) {
return refreshResponse return refreshResponse
} }
async function updateUserOAuth(userId, oAuthConfig) { async function updateUserOAuth(userId: string, oAuthConfig: any) {
const details = { const details = {
accessToken: oAuthConfig.accessToken, accessToken: oAuthConfig.accessToken,
refreshToken: oAuthConfig.refreshToken, refreshToken: oAuthConfig.refreshToken,
@ -162,14 +175,13 @@ async function updateUserOAuth(userId, oAuthConfig) {
} }
} }
module.exports = { export = {
buildAuthMiddleware: authenticated, buildAuthMiddleware: authenticated,
passport, passport,
google, google,
oidc, oidc,
jwt: require("jsonwebtoken"), jwt: require("jsonwebtoken"),
buildTenancyMiddleware: tenancy, buildTenancyMiddleware: tenancy,
buildAppTenancyMiddleware: appTenancy,
auditLog, auditLog,
authError, authError,
buildCsrfMiddleware: csrf, buildCsrfMiddleware: csrf,

View File

@ -18,6 +18,7 @@ export enum ViewName {
LINK = "by_link", LINK = "by_link",
ROUTING = "screen_routes", ROUTING = "screen_routes",
AUTOMATION_LOGS = "automation_logs", AUTOMATION_LOGS = "automation_logs",
ACCOUNT_BY_EMAIL = "account_by_email",
} }
export const DeprecatedViews = { export const DeprecatedViews = {
@ -27,12 +28,6 @@ export const DeprecatedViews = {
], ],
} }
export type GlobalViewName =
| ViewName.USER_BY_EMAIL
| ViewName.BY_API_KEY
| ViewName.USER_BY_BUILDERS
| ViewName.USER_BY_APP
export enum DocumentType { export enum DocumentType {
USER = "us", USER = "us",
GROUP = "gr", GROUP = "gr",
@ -47,6 +42,7 @@ export enum DocumentType {
MIGRATIONS = "migrations", MIGRATIONS = "migrations",
DEV_INFO = "devinfo", DEV_INFO = "devinfo",
AUTOMATION_LOG = "log_au", AUTOMATION_LOG = "log_au",
ACCOUNT_METADATA = "acc_metadata",
} }
export const StaticDatabases = { export const StaticDatabases = {

View File

@ -1,13 +1,8 @@
import { import { DocumentType, ViewName, DeprecatedViews, SEPARATOR } from "./utils"
DocumentType,
ViewName,
GlobalViewName,
DeprecatedViews,
SEPARATOR,
} from "./utils"
import { getGlobalDB } from "../tenancy" import { getGlobalDB } from "../tenancy"
import PouchDB from "pouchdb" import PouchDB from "pouchdb"
import { Document } from "@budibase/types" import { StaticDatabases } from "./constants"
import { doWithDB } from "./"
const DESIGN_DB = "_design/database" const DESIGN_DB = "_design/database"
@ -65,11 +60,39 @@ export const createNewUserEmailView = async () => {
await db.put(designDoc) await db.put(designDoc)
} }
export const createAccountEmailView = async () => {
await doWithDB(
StaticDatabases.PLATFORM_INFO.name,
async (db: PouchDB.Database) => {
let designDoc
try {
designDoc = await db.get<DesignDocument>(DESIGN_DB)
} catch (err) {
// no design doc, make one
designDoc = DesignDoc()
}
const view = {
// if using variables in a map function need to inject them before use
map: `function(doc) {
if (doc._id.startsWith("${DocumentType.ACCOUNT_METADATA}${SEPARATOR}")) {
emit(doc.email.toLowerCase(), doc._id)
}
}`,
}
designDoc.views = {
...designDoc.views,
[ViewName.ACCOUNT_BY_EMAIL]: view,
}
await db.put(designDoc)
}
)
}
export const createUserAppView = async () => { export const createUserAppView = async () => {
const db = getGlobalDB() const db = getGlobalDB() as PouchDB.Database
let designDoc let designDoc
try { try {
designDoc = await db.get("_design/database") designDoc = await db.get<DesignDocument>("_design/database")
} catch (err) { } catch (err) {
// no design doc, make one // no design doc, make one
designDoc = DesignDoc() designDoc = DesignDoc()
@ -137,35 +160,73 @@ export const createUserBuildersView = async () => {
await db.put(designDoc) await db.put(designDoc)
} }
export const queryGlobalView = async <T extends Document>( export interface QueryViewOptions {
viewName: GlobalViewName, arrayResponse?: boolean
params: PouchDB.Query.Options<T, T>, }
db?: PouchDB.Database
): Promise<T[] | T | undefined> => {
const CreateFuncByName = {
[ViewName.USER_BY_EMAIL]: createNewUserEmailView,
[ViewName.BY_API_KEY]: createApiKeyView,
[ViewName.USER_BY_BUILDERS]: createUserBuildersView,
[ViewName.USER_BY_APP]: createUserAppView,
}
// can pass DB in if working with something specific
if (!db) {
db = getGlobalDB() as PouchDB.Database
}
export const queryView = async <T>(
viewName: ViewName,
params: PouchDB.Query.Options<T, T>,
db: PouchDB.Database,
CreateFuncByName: any,
opts?: QueryViewOptions
): Promise<T[] | T | undefined> => {
try { try {
const response = await db.query<T, T>(`database/${viewName}`, params) let response = await db.query<T, T>(`database/${viewName}`, params)
const rows = response.rows const rows = response.rows
const docs = rows.map(row => (params.include_docs ? row.doc : row.value)) const docs = rows.map(row => (params.include_docs ? row.doc : row.value))
return docs.length <= 1 ? docs[0] : docs
// if arrayResponse has been requested, always return array regardless of length
if (opts?.arrayResponse) {
return docs
} else {
// return the single document if there is only one
return docs.length <= 1 ? docs[0] : docs
}
} catch (err: any) { } catch (err: any) {
if (err != null && err.name === "not_found") { if (err != null && err.name === "not_found") {
const createFunc = CreateFuncByName[viewName] const createFunc = CreateFuncByName[viewName]
await removeDeprecated(db, viewName) await removeDeprecated(db, viewName)
await createFunc() await createFunc()
return queryGlobalView(viewName, params) return queryView(viewName, params, db, CreateFuncByName, opts)
} else { } else {
throw err throw err
} }
} }
} }
export const queryPlatformView = async <T>(
viewName: ViewName,
params: PouchDB.Query.Options<T, T>,
opts?: QueryViewOptions
): Promise<T[] | T | undefined> => {
const CreateFuncByName = {
[ViewName.ACCOUNT_BY_EMAIL]: exports.createAccountEmailView,
}
return doWithDB(
StaticDatabases.PLATFORM_INFO.name,
async (db: PouchDB.Database) => {
return queryView(viewName, params, db, CreateFuncByName, opts)
}
)
}
export const queryGlobalView = async <T>(
viewName: ViewName,
params: PouchDB.Query.Options<T, T>,
db?: PouchDB.Database,
opts?: QueryViewOptions
): Promise<T[] | T | undefined> => {
const CreateFuncByName = {
[ViewName.USER_BY_EMAIL]: exports.createNewUserEmailView,
[ViewName.BY_API_KEY]: exports.createApiKeyView,
[ViewName.USER_BY_BUILDERS]: exports.createUserBuildersView,
[ViewName.USER_BY_APP]: exports.createUserAppView,
}
// can pass DB in if working with something specific
if (!db) {
db = getGlobalDB() as PouchDB.Database
}
return queryView(viewName, params, db, CreateFuncByName, opts)
}

View File

@ -8,4 +8,5 @@ import { processors } from "./processors"
export const shutdown = () => { export const shutdown = () => {
processors.shutdown() processors.shutdown()
console.log("Events shutdown")
} }

View File

@ -65,7 +65,7 @@ async function checkApiKey(apiKey: string, populateUser?: Function) {
* The tenancy modules should not be used here and it should be assumed that the tenancy context * The tenancy modules should not be used here and it should be assumed that the tenancy context
* has not yet been populated. * has not yet been populated.
*/ */
module.exports = ( export = (
noAuthPatterns = [], noAuthPatterns = [],
opts: { publicAllowed: boolean; populateUser?: Function } = { opts: { publicAllowed: boolean; populateUser?: Function } = {
publicAllowed: false, publicAllowed: false,

View File

@ -13,7 +13,8 @@ const adminOnly = require("./adminOnly")
const builderOrAdmin = require("./builderOrAdmin") const builderOrAdmin = require("./builderOrAdmin")
const builderOnly = require("./builderOnly") const builderOnly = require("./builderOnly")
const joiValidator = require("./joi-validator") const joiValidator = require("./joi-validator")
module.exports = {
const pkg = {
google, google,
oidc, oidc,
jwt, jwt,
@ -33,3 +34,5 @@ module.exports = {
builderOrAdmin, builderOrAdmin,
joiValidator, joiValidator,
} }
export = pkg

View File

@ -13,10 +13,13 @@ function validate(schema, property) {
params = ctx.request[property] params = ctx.request[property]
} }
schema = schema.append({ // not all schemas have the append property e.g. array schemas
createdAt: Joi.any().optional(), if (schema.append) {
updatedAt: Joi.any().optional(), schema = schema.append({
}) createdAt: Joi.any().optional(),
updatedAt: Joi.any().optional(),
})
}
const { error } = schema.validate(params) const { error } = schema.validate(params)
if (error) { if (error) {

View File

@ -66,15 +66,13 @@ const PUBLIC_BUCKETS = [ObjectStoreBuckets.APPS, ObjectStoreBuckets.GLOBAL]
* @constructor * @constructor
*/ */
export const ObjectStore = (bucket: any) => { export const ObjectStore = (bucket: any) => {
AWS.config.update({
accessKeyId: env.MINIO_ACCESS_KEY,
secretAccessKey: env.MINIO_SECRET_KEY,
region: env.AWS_REGION,
})
const config: any = { const config: any = {
s3ForcePathStyle: true, s3ForcePathStyle: true,
signatureVersion: "v4", signatureVersion: "v4",
apiVersion: "2006-03-01", apiVersion: "2006-03-01",
accessKeyId: env.MINIO_ACCESS_KEY,
secretAccessKey: env.MINIO_SECRET_KEY,
region: env.AWS_REGION,
} }
if (bucket) { if (bucket) {
config.params = { config.params = {

View File

@ -3,17 +3,27 @@ const { v4: uuidv4 } = require("uuid")
const { logWarn } = require("../logging") const { logWarn } = require("../logging")
const env = require("../environment") const env = require("../environment")
interface Session { interface CreateSession {
key: string
userId: string
sessionId: string sessionId: string
lastAccessedAt: string tenantId: string
createdAt: string
csrfToken?: string csrfToken?: string
value: string
} }
type SessionKey = { key: string }[] interface Session extends CreateSession {
userId: string
lastAccessedAt: string
createdAt: string
// make optional attributes required
csrfToken: string
}
interface SessionKey {
key: string
}
interface ScannedSession {
value: Session
}
// a week in seconds // a week in seconds
const EXPIRY_SECONDS = 86400 * 7 const EXPIRY_SECONDS = 86400 * 7
@ -22,14 +32,14 @@ function makeSessionID(userId: string, sessionId: string) {
return `${userId}/${sessionId}` return `${userId}/${sessionId}`
} }
export async function getSessionsForUser(userId: string) { export async function getSessionsForUser(userId: string): Promise<Session[]> {
if (!userId) { if (!userId) {
console.trace("Cannot get sessions for undefined userId") console.trace("Cannot get sessions for undefined userId")
return [] return []
} }
const client = await redis.getSessionClient() const client = await redis.getSessionClient()
const sessions = await client.scan(userId) const sessions: ScannedSession[] = await client.scan(userId)
return sessions.map((session: Session) => session.value) return sessions.map(session => session.value)
} }
export async function invalidateSessions( export async function invalidateSessions(
@ -39,33 +49,32 @@ export async function invalidateSessions(
try { try {
const reason = opts?.reason || "unknown" const reason = opts?.reason || "unknown"
let sessionIds: string[] = opts.sessionIds || [] let sessionIds: string[] = opts.sessionIds || []
let sessions: SessionKey let sessionKeys: SessionKey[]
// If no sessionIds, get all the sessions for the user // If no sessionIds, get all the sessions for the user
if (sessionIds.length === 0) { if (sessionIds.length === 0) {
sessions = await getSessionsForUser(userId) const sessions = await getSessionsForUser(userId)
sessions.forEach( sessionKeys = sessions.map(session => ({
(session: any) => key: makeSessionID(session.userId, session.sessionId),
(session.key = makeSessionID(session.userId, session.sessionId)) }))
)
} else { } else {
// use the passed array of sessionIds // use the passed array of sessionIds
sessionIds = Array.isArray(sessionIds) ? sessionIds : [sessionIds] sessionIds = Array.isArray(sessionIds) ? sessionIds : [sessionIds]
sessions = sessionIds.map((sessionId: string) => ({ sessionKeys = sessionIds.map(sessionId => ({
key: makeSessionID(userId, sessionId), key: makeSessionID(userId, sessionId),
})) }))
} }
if (sessions && sessions.length > 0) { if (sessionKeys && sessionKeys.length > 0) {
const client = await redis.getSessionClient() const client = await redis.getSessionClient()
const promises = [] const promises = []
for (let session of sessions) { for (let sessionKey of sessionKeys) {
promises.push(client.delete(session.key)) promises.push(client.delete(sessionKey.key))
} }
if (!env.isTest()) { if (!env.isTest()) {
logWarn( logWarn(
`Invalidating sessions for ${userId} (reason: ${reason}) - ${sessions `Invalidating sessions for ${userId} (reason: ${reason}) - ${sessionKeys
.map(session => session.key) .map(sessionKey => sessionKey.key)
.join(", ")}` .join(", ")}`
) )
} }
@ -76,22 +85,26 @@ export async function invalidateSessions(
} }
} }
export async function createASession(userId: string, session: Session) { export async function createASession(
userId: string,
createSession: CreateSession
) {
// invalidate all other sessions // invalidate all other sessions
await invalidateSessions(userId, { reason: "creation" }) await invalidateSessions(userId, { reason: "creation" })
const client = await redis.getSessionClient() const client = await redis.getSessionClient()
const sessionId = session.sessionId const sessionId = createSession.sessionId
if (!session.csrfToken) { const csrfToken = createSession.csrfToken ? createSession.csrfToken : uuidv4()
session.csrfToken = uuidv4() const key = makeSessionID(userId, sessionId)
}
session = { const session: Session = {
...session, ...createSession,
csrfToken,
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
lastAccessedAt: new Date().toISOString(), lastAccessedAt: new Date().toISOString(),
userId, userId,
} }
await client.store(makeSessionID(userId, sessionId), session, EXPIRY_SECONDS) await client.store(key, session, EXPIRY_SECONDS)
} }
export async function updateSessionTTL(session: Session) { export async function updateSessionTTL(session: Session) {
@ -106,7 +119,10 @@ export async function endSession(userId: string, sessionId: string) {
await client.delete(makeSessionID(userId, sessionId)) await client.delete(makeSessionID(userId, sessionId))
} }
export async function getSession(userId: string, sessionId: string) { export async function getSession(
userId: string,
sessionId: string
): Promise<Session> {
if (!userId || !sessionId) { if (!userId || !sessionId) {
throw new Error(`Invalid session details - ${userId} - ${sessionId}`) throw new Error(`Invalid session details - ${userId} - ${sessionId}`)
} }

View File

@ -0,0 +1,7 @@
export const getAccount = jest.fn()
export const getAccountByTenantId = jest.fn()
jest.mock("../../../src/cloud/accounts", () => ({
getAccount,
getAccountByTenantId,
}))

View File

@ -1,2 +0,0 @@
exports.MOCK_DATE = new Date("2020-01-01T00:00:00.000Z")
exports.MOCK_DATE_TIMESTAMP = 1577836800000

View File

@ -0,0 +1,2 @@
export const MOCK_DATE = new Date("2020-01-01T00:00:00.000Z")
export const MOCK_DATE_TIMESTAMP = 1577836800000

View File

@ -1,9 +0,0 @@
const posthog = require("./posthog")
const events = require("./events")
const date = require("./date")
module.exports = {
posthog,
date,
events,
}

View File

@ -0,0 +1,4 @@
import "./posthog"
import "./events"
export * as accounts from "./accounts"
export * as date from "./date"

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": "1.2.41-alpha.5", "version": "1.2.58-alpha.5",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",
@ -38,7 +38,7 @@
], ],
"dependencies": { "dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1", "@adobe/spectrum-css-workflow-icons": "^1.2.1",
"@budibase/string-templates": "1.2.41-alpha.5", "@budibase/string-templates": "1.2.58-alpha.5",
"@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2", "@spectrum-css/avatar": "^3.0.2",

View File

@ -67,6 +67,13 @@
// If time only set date component to 2000-01-01 // If time only set date component to 2000-01-01
if (timeOnly) { if (timeOnly) {
// Classic flackpickr causing issues.
// When selecting a value for the first time for a "time only" field,
// the time is always offset by 1 hour for some reason (regardless of time
// zone) so we need to correct it.
if (!value && newValue) {
newValue = new Date(dates[0].getTime() + 60 * 60 * 1000).toISOString()
}
newValue = `2000-01-01T${newValue.split("T")[1]}` newValue = `2000-01-01T${newValue.split("T")[1]}`
} }

View File

@ -139,7 +139,13 @@
<div class="title"> <div class="title">
<div class="filename"> <div class="filename">
{#if selectedUrl} {#if selectedUrl}
<Link href={selectedUrl}>{selectedImage.name}</Link> <Link
target="_blank"
download={selectedImage.name}
href={selectedUrl}
>
{selectedImage.name}
</Link>
{:else} {:else}
{selectedImage.name} {selectedImage.name}
{/if} {/if}

View File

@ -10,6 +10,7 @@
export let disabled = false export let disabled = false
export let getOptionLabel = option => option export let getOptionLabel = option => option
export let getOptionValue = option => option export let getOptionValue = option => option
export let getOptionTitle = option => option
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const onChange = e => dispatch("change", e.target.value) const onChange = e => dispatch("change", e.target.value)
@ -19,7 +20,7 @@
{#if options && Array.isArray(options)} {#if options && Array.isArray(options)}
{#each options as option} {#each options as option}
<div <div
title={getOptionLabel(option)} title={getOptionTitle(option)}
class="spectrum-Radio spectrum-FieldGroup-item spectrum-Radio--emphasized" class="spectrum-Radio spectrum-FieldGroup-item spectrum-Radio--emphasized"
class:is-invalid={!!error} class:is-invalid={!!error}
> >

View File

@ -12,6 +12,7 @@
export let direction = "vertical" export let direction = "vertical"
export let getOptionLabel = option => extractProperty(option, "label") export let getOptionLabel = option => extractProperty(option, "label")
export let getOptionValue = option => extractProperty(option, "value") export let getOptionValue = option => extractProperty(option, "value")
export let getOptionTitle = option => extractProperty(option, "label")
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const onChange = e => { const onChange = e => {
@ -35,6 +36,7 @@
{direction} {direction}
{getOptionLabel} {getOptionLabel}
{getOptionValue} {getOptionValue}
{getOptionTitle}
on:change={onChange} on:change={onChange}
/> />
</Field> </Field>

View File

@ -8,12 +8,14 @@
export let secondary = false export let secondary = false
export let overBackground = false export let overBackground = false
export let target export let target
export let download
</script> </script>
<a <a
on:click on:click
{href} {href}
{target} {target}
{download}
class:spectrum-Link--primary={primary} class:spectrum-Link--primary={primary}
class:spectrum-Link--secondary={secondary} class:spectrum-Link--secondary={secondary}
class:spectrum-Link--overBackground={overBackground} class:spectrum-Link--overBackground={overBackground}

View File

@ -15,14 +15,24 @@
{#each attachments as attachment} {#each attachments as attachment}
{#if isImage(attachment.extension)} {#if isImage(attachment.extension)}
<Link quiet target="_blank" href={attachment.url}> <Link
quiet
target="_blank"
download={attachment.name}
href={attachment.url}
>
<div class="center" title={attachment.name}> <div class="center" title={attachment.name}>
<img src={attachment.url} alt={attachment.extension} /> <img src={attachment.url} alt={attachment.extension} />
</div> </div>
</Link> </Link>
{:else} {:else}
<div class="file" title={attachment.name}> <div class="file" title={attachment.name}>
<Link quiet target="_blank" href={attachment.url}> <Link
quiet
target="_blank"
download={attachment.name}
href={attachment.url}
>
{attachment.extension} {attachment.extension}
</Link> </Link>
</div> </div>

View File

@ -0,0 +1,178 @@
import filterTests from "../../support/filterTests"
// const interact = require("../support/interact")
filterTests(["smoke", "all"], () => {
context("Auth Configuration", () => {
before(() => {
cy.login()
})
after(() => {
cy.get(".spectrum-SideNav li").contains("Auth").click()
cy.location().should(loc => {
expect(loc.pathname).to.eq("/builder/portal/manage/auth")
})
cy.get("[data-cy=new-scope-input]").clear()
cy.get("div.content").scrollTo("bottom")
cy.get("[data-cy=oidc-active]").click()
cy.get("[data-cy=oidc-active]").should('not.be.checked')
cy.intercept("POST", "/api/global/configs").as("updateAuth")
cy.get("button[data-cy=oidc-save]").contains("Save").click({force: true})
cy.wait("@updateAuth")
cy.get("@updateAuth").its("response.statusCode").should("eq", 200)
cy.get(".spectrum-Toast-content")
.contains("Settings saved")
.should("be.visible")
})
it("Should allow updating of the OIDC config", () => {
cy.get(".spectrum-SideNav li").contains("Auth").click()
cy.location().should(loc => {
expect(loc.pathname).to.eq("/builder/portal/manage/auth")
})
cy.get("div.content").scrollTo("bottom")
cy.get(".spectrum-Toast .spectrum-ClearButton").click()
cy.get("input[data-cy=configUrl]").type("http://budi-auth.com/v2")
cy.get("input[data-cy=clientID]").type("34ac6a13-f24a-4b52-c70d-fa544ffd11b2")
cy.get("input[data-cy=clientSecret]").type("12A8Q~4nS_DWhOOJ2vWIRsNyDVsdtXPD.Zxa9df_")
cy.get("button[data-cy=oidc-save]").should("not.be.disabled");
cy.intercept("POST", "/api/global/configs").as("updateAuth")
cy.get("button[data-cy=oidc-save]").contains("Save").click({force: true})
cy.wait("@updateAuth")
cy.get("@updateAuth").its("response.statusCode").should("eq", 200)
cy.get(".spectrum-Toast-content")
.contains("Settings saved")
.should("be.visible")
})
it("Should display default scopes in advanced config.", () => {
cy.get(".spectrum-SideNav li").contains("Auth").click()
cy.location().should(loc => {
expect(loc.pathname).to.eq("/builder/portal/manage/auth")
})
cy.get("div.content").scrollTo("bottom")
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
cy.get(".spectrum-Tags-item").contains("openid")
cy.get(".spectrum-Tags-item").contains("openid").find(".spectrum-ClearButton").should("not.exist")
cy.get(".spectrum-Tags-item").contains("offline_access")
cy.get(".spectrum-Tags-item").contains("email")
cy.get(".spectrum-Tags-item").contains("profile")
})
it("Add a new scopes", () => {
cy.get(".spectrum-SideNav li").contains("Auth").click()
cy.location().should(loc => {
expect(loc.pathname).to.eq("/builder/portal/manage/auth")
})
cy.get("div.content").scrollTo("bottom")
cy.get("[data-cy=new-scope-input]").type("Sample{enter}")
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 5)
cy.get(".spectrum-Tags-item").contains("Sample")
cy.get(".auth-form input.spectrum-Textfield-input").type("Another ")
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 6)
cy.get(".spectrum-Tags-item").contains("Another")
cy.get("button[data-cy=oidc-save]").should("not.be.disabled");
cy.intercept("POST", "/api/global/configs").as("updateAuth")
cy.get("button[data-cy=oidc-save]").contains("Save").click({force: true})
cy.wait("@updateAuth")
cy.get("@updateAuth").its("response.statusCode").should("eq", 200)
cy.reload()
cy.get("div.content").scrollTo("bottom")
cy.get(".spectrum-Tags-item").contains("openid")
cy.get(".spectrum-Tags-item").contains("offline_access")
cy.get(".spectrum-Tags-item").contains("email")
cy.get(".spectrum-Tags-item").contains("profile")
cy.get(".spectrum-Tags-item").contains("Sample")
cy.get(".spectrum-Tags-item").contains("Another")
})
it("Should allow the removal of auth scopes", () => {
cy.get(".spectrum-SideNav li").contains("Auth").click()
cy.location().should(loc => {
expect(loc.pathname).to.eq("/builder/portal/manage/auth")
})
cy.get("div.content").scrollTo("bottom")
cy.get(".spectrum-Tags-item").contains("offline_access").parent().find(".spectrum-ClearButton").click()
cy.get(".spectrum-Tags-item").contains("profile").parent().find(".spectrum-ClearButton").click()
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
cy.get(".spectrum-Tags-item").contains("offline_access").should("not.exist")
cy.get(".spectrum-Tags-item").contains("profile").should("not.exist")
cy.get("button[data-cy=oidc-save]").should("not.be.disabled");
cy.intercept("POST", "/api/global/configs").as("updateAuth")
cy.get("button[data-cy=oidc-save]").contains("Save").click({force: true})
cy.wait("@updateAuth")
cy.get("@updateAuth").its("response.statusCode").should("eq", 200)
cy.get(".spectrum-Toast-content")
.contains("Settings saved")
.should("be.visible")
cy.reload()
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
cy.get(".spectrum-Tags-item").contains("offline_access").should("not.exist")
cy.get(".spectrum-Tags-item").contains("profile").should("not.exist")
})
it("Should allow auth scopes to be reset to the core defaults.", () => {
cy.get(".spectrum-SideNav li").contains("Auth").click()
cy.get("div.content").scrollTo("bottom")
cy.get("[data-cy=restore-oidc-default-scopes]").click({force: true})
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
cy.get(".spectrum-Tags-item").contains("openid")
cy.get(".spectrum-Tags-item").contains("offline_access")
cy.get(".spectrum-Tags-item").contains("email")
cy.get(".spectrum-Tags-item").contains("profile")
})
it("Should not allow invalid characters in the auth scopes", () => {
cy.get("[data-cy=new-scope-input]").type("thisIsInvalid\\{enter}")
cy.get(".spectrum-Form-itemField .error").contains("Auth scopes cannot contain spaces, double quotes or backslashes")
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
cy.get("[data-cy=new-scope-input]").clear()
cy.get("[data-cy=new-scope-input]").type("alsoInvalid\"{enter}")
cy.get(".spectrum-Form-itemField .error").contains("Auth scopes cannot contain spaces, double quotes or backslashes")
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
cy.get("[data-cy=new-scope-input]").clear()
})
it("Should not allow duplicate auth scopes", () => {
cy.get("[data-cy=new-scope-input]").type("offline_access{enter}")
cy.get(".spectrum-Form-itemField .error").contains("Auth scope already exists")
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
})
})
})

View File

@ -102,7 +102,7 @@ filterTests(['all'], () => {
cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 6000 }) cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 6000 })
cy.wait(500) cy.wait(500)
cy.get(interact.APP_TABLE_STATUS, { timeout: 1000 }).eq(0).contains("Unpublished") cy.get(interact.APP_TABLE_STATUS, { timeout: 10000 }).eq(0).contains("Unpublished")
}) })
}) })

View File

@ -175,7 +175,10 @@ filterTests(["all"], () => {
cy.get("@query").its("response.statusCode").should("eq", 200) cy.get("@query").its("response.statusCode").should("eq", 200)
cy.get("@query").its("response.body").should("not.be.empty") cy.get("@query").its("response.body").should("not.be.empty")
// Save query // Save query
cy.intercept("POST", "**/queries").as("saveQuery")
cy.get(".spectrum-Button").contains("Save Query").click({ force: true }) cy.get(".spectrum-Button").contains("Save Query").click({ force: true })
cy.wait("@saveQuery")
cy.get("@saveQuery").its("response.statusCode").should("eq", 200)
cy.get(".nav-item").should("contain", queryName) cy.get(".nav-item").should("contain", queryName)
}) })

View File

@ -252,7 +252,8 @@ filterTests(["all"], () => {
.contains("Delete Query") .contains("Delete Query")
.click({ force: true }) .click({ force: true })
// Confirm deletion // Confirm deletion
cy.reload({ timeout: 5000 }) cy.reload()
cy.get(".nav-item", { timeout: 30000 }).contains(datasource).click({ force: true })
cy.get(".nav-item", { timeout: 1000 }).should("not.contain", queryRename) cy.get(".nav-item", { timeout: 1000 }).should("not.contain", queryRename)
}) })

View File

@ -48,6 +48,7 @@ filterTests(['smoke', 'all'], () => {
cy.get(interact.AREA_LABEL_REVERT).click({ force: true }) cy.get(interact.AREA_LABEL_REVERT).click({ force: true })
}) })
cy.get(interact.SPECTRUM_DIALOG_GRID).within(() => { cy.get(interact.SPECTRUM_DIALOG_GRID).within(() => {
cy.get("input").type("Cypress Tests")
// Click Revert // Click Revert
cy.get(interact.SPECTRUM_BUTTON).contains("Revert").click({ force: true }) cy.get(interact.SPECTRUM_BUTTON).contains("Revert").click({ force: true })
cy.wait(2000) // Wait for app to finish reverting cy.wait(2000) // Wait for app to finish reverting

View File

@ -448,10 +448,7 @@ Cypress.Commands.add("createTable", (tableName, initialTable) => {
.contains("Continue") .contains("Continue")
.click({ force: true }) .click({ force: true })
}) })
cy.get(".spectrum-Modal", { timeout: 10000 }).should( cy.get(".spectrum-Modal").contains("Create Table", { timeout: 10000 })
"not.contain",
"Add data source"
)
cy.get(".spectrum-Modal", { timeout: 2000 }).within(() => { cy.get(".spectrum-Modal", { timeout: 2000 }).within(() => {
cy.get("input", { timeout: 2000 }).first().type(tableName).blur() cy.get("input", { timeout: 2000 }).first().type(tableName).blur()
cy.get(".spectrum-ButtonGroup").contains("Create").click() cy.get(".spectrum-ButtonGroup").contains("Create").click()
@ -742,8 +739,15 @@ Cypress.Commands.add("deleteAllScreens", () => {
Cypress.Commands.add("navigateToFrontend", () => { Cypress.Commands.add("navigateToFrontend", () => {
// Clicks on Design tab and then the Home nav item // Clicks on Design tab and then the Home nav item
cy.wait(500) cy.wait(500)
cy.intercept("**/preview").as("preview")
cy.contains("Design").click() cy.contains("Design").click()
cy.get(".spectrum-Search", { timeout: 2000 }).type("/") cy.wait("@preview")
cy.get("@preview").then(res => {
if (res.statusCode != 200) {
cy.reload()
}
})
cy.get(".spectrum-Search", { timeout: 20000 }).type("/")
cy.get(".nav-item", { timeout: 2000 }).contains("home").click({ force: true }) cy.get(".nav-item", { timeout: 2000 }).contains("home").click({ force: true })
}) })

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "1.2.41-alpha.5", "version": "1.2.58-alpha.5",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -13,11 +13,11 @@
"cy:setup:ci": "node ./cypress/setup.js", "cy:setup:ci": "node ./cypress/setup.js",
"cy:open": "cypress open", "cy:open": "cypress open",
"cy:run": "cypress run", "cy:run": "cypress run",
"cy:run:ci": "cypress run --headed --browser chrome --spec cypress/integration/createApp.spec.js", "cy:run:ci": "cypress run --headed --browser chrome --spec cypress/integration/createApp.spec.js",
"cy:run:ci:record": "xvfb-run cypress run --headed --browser chrome --record", "cy:run:ci:record": "xvfb-run cypress run --headed --browser chrome --record",
"cy:test": "start-server-and-test cy:setup http://localhost:4100/builder cy:run", "cy:test": "start-server-and-test cy:setup http://localhost:4100/builder cy:run",
"cy:ci": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci", "cy:ci": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci",
"cy:ci:record": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci:record && npm run cy:ci:report", "cy:ci:record": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci:record; npm run cy:ci:report",
"cy:ci:report": "mochawesome-merge cypress/reports/*.json > cypress/reports/testReport.json && marge cypress/reports/testReport.json --reportDir cypress/reports --inline", "cy:ci:report": "mochawesome-merge cypress/reports/*.json > cypress/reports/testReport.json && marge cypress/reports/testReport.json --reportDir cypress/reports --inline",
"cy:ci:notify": "node scripts/cypressResultsWebhook", "cy:ci:notify": "node scripts/cypressResultsWebhook",
"cy:debug": "start-server-and-test cy:setup http://localhost:4100/builder cy:open", "cy:debug": "start-server-and-test cy:setup http://localhost:4100/builder cy:open",
@ -69,10 +69,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "1.2.41-alpha.5", "@budibase/bbui": "1.2.58-alpha.5",
"@budibase/client": "1.2.41-alpha.5", "@budibase/client": "1.2.58-alpha.5",
"@budibase/frontend-core": "1.2.41-alpha.5", "@budibase/frontend-core": "1.2.58-alpha.5",
"@budibase/string-templates": "1.2.41-alpha.5", "@budibase/string-templates": "1.2.58-alpha.5",
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",

View File

@ -5,7 +5,6 @@ const path = require("path")
const fs = require("fs") const fs = require("fs")
const WEBHOOK_URL = process.env.CYPRESS_WEBHOOK_URL const WEBHOOK_URL = process.env.CYPRESS_WEBHOOK_URL
const OUTCOME = process.env.CYPRESS_OUTCOME
const DASHBOARD_URL = process.env.CYPRESS_DASHBOARD_URL const DASHBOARD_URL = process.env.CYPRESS_DASHBOARD_URL
const GIT_SHA = process.env.GITHUB_SHA const GIT_SHA = process.env.GITHUB_SHA
const GITHUB_ACTIONS_RUN_URL = process.env.GITHUB_ACTIONS_RUN_URL const GITHUB_ACTIONS_RUN_URL = process.env.GITHUB_ACTIONS_RUN_URL
@ -35,6 +34,8 @@ async function discordCypressResultsNotification(report) {
skipped, skipped,
} = report.stats } = report.stats
const OUTCOME = failures > 0 ? "failure" : "success"
const options = { const options = {
method: "POST", method: "POST",
headers: { headers: {
@ -114,7 +115,7 @@ async function discordCypressResultsNotification(report) {
} }
const response = await fetch(WEBHOOK_URL, options) const response = await fetch(WEBHOOK_URL, options)
if (response.status >= 400) { if (response.status >= 201) {
const text = await response.text() const text = await response.text()
console.error( console.error(
`Error sending discord webhook. \nStatus: ${response.status}. \nResponse Body: ${text}. \nRequest Body: ${options.body}` `Error sending discord webhook. \nStatus: ${response.status}. \nResponse Body: ${text}. \nRequest Body: ${options.body}`

View File

@ -502,11 +502,11 @@ const getRoleBindings = () => {
} }
/** /**
* Gets all bindable properties exposed in a button actions flow up until * Gets all bindable properties exposed in an event action flow up until
* the specified action ID, as well as context provided for the action * the specified action ID, as well as context provided for the action
* setting as a whole by the component. * setting as a whole by the component.
*/ */
export const getButtonContextBindings = ( export const getEventContextBindings = (
asset, asset,
componentId, componentId,
settingKey, settingKey,
@ -520,10 +520,7 @@ export const getButtonContextBindings = (
const component = findComponent(asset.props, componentId) const component = findComponent(asset.props, componentId)
const settings = getComponentSettings(component?._component) const settings = getComponentSettings(component?._component)
const eventSetting = settings.find(setting => setting.key === settingKey) const eventSetting = settings.find(setting => setting.key === settingKey)
if (!eventSetting) { if (eventSetting?.context?.length) {
return bindings
}
if (eventSetting.context?.length) {
eventSetting.context.forEach(contextEntry => { eventSetting.context.forEach(contextEntry => {
bindings.push({ bindings.push({
readableBinding: contextEntry.label, readableBinding: contextEntry.label,

View File

@ -19,7 +19,6 @@ import {
makeComponentUnique, makeComponentUnique,
} from "../componentUtils" } from "../componentUtils"
import { Helpers } from "@budibase/bbui" import { Helpers } from "@budibase/bbui"
import { DefaultAppTheme, LAYOUT_NAMES } from "../../constants"
import { Utils } from "@budibase/frontend-core" import { Utils } from "@budibase/frontend-core"
const INITIAL_FRONTEND_STATE = { const INITIAL_FRONTEND_STATE = {
@ -40,6 +39,7 @@ const INITIAL_FRONTEND_STATE = {
devicePreview: false, devicePreview: false,
messagePassing: false, messagePassing: false,
continueIfAction: false, continueIfAction: false,
showNotificationAction: false,
}, },
errors: [], errors: [],
hasAppPackage: false, hasAppPackage: false,
@ -124,35 +124,6 @@ export const getFrontendStore = () => {
await integrations.init() await integrations.init()
await queries.init() await queries.init()
await tables.init() await tables.init()
// Add navigation settings to old apps
if (!application.navigation) {
const layout = layouts.find(x => x._id === LAYOUT_NAMES.MASTER.PRIVATE)
const customTheme = application.customTheme
let navigationSettings = {
navigation: "Top",
title: application.name,
navWidth: "Large",
navBackground:
customTheme?.navBackground || DefaultAppTheme.navBackground,
navTextColor:
customTheme?.navTextColor || DefaultAppTheme.navTextColor,
}
if (layout) {
navigationSettings.hideLogo = layout.props.hideLogo
navigationSettings.hideTitle = layout.props.hideTitle
navigationSettings.title = layout.props.title || application.name
navigationSettings.logoUrl = layout.props.logoUrl
navigationSettings.links = layout.props.links
navigationSettings.navigation = layout.props.navigation || "Top"
navigationSettings.sticky = layout.props.sticky
navigationSettings.navWidth = layout.props.width || "Large"
if (navigationSettings.navigation === "None") {
navigationSettings.navigation = "Top"
}
}
await store.actions.navigation.save(navigationSettings)
}
}, },
theme: { theme: {
save: async theme => { save: async theme => {

View File

@ -14,7 +14,7 @@
$: { $: {
let fields = {} let fields = {}
for (const [key, type] of Object.entries(block?.inputs?.fields)) { for (const [key, type] of Object.entries(block?.inputs?.fields ?? {})) {
fields = { fields = {
...fields, ...fields,
[key]: { [key]: {

View File

@ -467,6 +467,7 @@
options={relationshipOptions} options={relationshipOptions}
getOptionLabel={option => option.name} getOptionLabel={option => option.name}
getOptionValue={option => option.value} getOptionValue={option => option.value}
getOptionTitle={option => option.alt}
/> />
{/if} {/if}
<Input <Input

View File

@ -11,7 +11,7 @@
} from "@budibase/bbui" } from "@budibase/bbui"
import { getAvailableActions } from "./index" import { getAvailableActions } from "./index"
import { generate } from "shortid" import { generate } from "shortid"
import { getButtonContextBindings } from "builderStore/dataBinding" import { getEventContextBindings } from "builderStore/dataBinding"
import { currentAsset, store } from "builderStore" import { currentAsset, store } from "builderStore"
const flipDurationMs = 150 const flipDurationMs = 150
@ -41,14 +41,14 @@
}, {}) }, {})
// These are ephemeral bindings which only exist while executing actions // These are ephemeral bindings which only exist while executing actions
$: buttonContextBindings = getButtonContextBindings( $: eventContexBindings = getEventContextBindings(
$currentAsset, $currentAsset,
$store.selectedComponentId, $store.selectedComponentId,
key, key,
actions, actions,
selectedAction?.id selectedAction?.id
) )
$: allBindings = buttonContextBindings.concat(bindings) $: allBindings = eventContexBindings.concat(bindings)
// Assign a unique ID to each action // Assign a unique ID to each action
$: { $: {

View File

@ -0,0 +1,61 @@
<script>
import { Select, Label, Checkbox } from "@budibase/bbui"
import { onMount } from "svelte"
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
export let parameters
export let bindings = []
const types = [
{
label: "Success",
value: "success",
},
{
label: "Warning",
value: "warning",
},
{
label: "Error",
value: "error",
},
{
label: "Info",
value: "info",
},
]
onMount(() => {
if (!parameters.type) {
parameters.type = "success"
}
if (parameters.autoDismiss == null) {
parameters.autoDismiss = true
}
})
</script>
<div class="root">
<Label>Type</Label>
<Select bind:value={parameters.type} options={types} placeholder={null} />
<Label>Message</Label>
<DrawerBindableInput
{bindings}
value={parameters.message}
on:change={e => (parameters.message = e.detail)}
/>
<Label />
<Checkbox text="Auto dismiss" bind:value={parameters.autoDismiss} />
</div>
<style>
.root {
display: grid;
column-gap: var(--spacing-l);
row-gap: var(--spacing-s);
grid-template-columns: 60px 1fr;
align-items: center;
max-width: 400px;
margin: 0 auto;
}
</style>

View File

@ -15,3 +15,4 @@ export { default as S3Upload } from "./S3Upload.svelte"
export { default as ExportData } from "./ExportData.svelte" export { default as ExportData } from "./ExportData.svelte"
export { default as ContinueIf } from "./ContinueIf.svelte" export { default as ContinueIf } from "./ContinueIf.svelte"
export { default as UpdateFieldValue } from "./UpdateFieldValue.svelte" export { default as UpdateFieldValue } from "./UpdateFieldValue.svelte"
export { default as ShowNotification } from "./ShowNotification.svelte"

View File

@ -110,6 +110,12 @@
"type": "logic", "type": "logic",
"component": "ContinueIf", "component": "ContinueIf",
"dependsOnFeature": "continueIfAction" "dependsOnFeature": "continueIfAction"
},
{
"name": "Show Notification",
"type": "application",
"component": "ShowNotification",
"dependsOnFeature": "showNotificationAction"
} }
] ]
} }

View File

@ -8,7 +8,6 @@
selectedLayout, selectedLayout,
currentAsset, currentAsset,
} from "builderStore" } from "builderStore"
import iframeTemplate from "./iframeTemplate"
import ConfirmDialog from "components/common/ConfirmDialog.svelte" import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import { import {
ProgressCircle, ProgressCircle,
@ -40,12 +39,6 @@
BUDIBASE: "type", BUDIBASE: "type",
} }
// Construct iframe template
$: template = iframeTemplate.replace(
/\{\{ CLIENT_LIB_PATH }}/,
$store.clientLibPath
)
const placeholderScreen = new Screen() const placeholderScreen = new Screen()
.name("Screen Placeholder") .name("Screen Placeholder")
.route("/") .route("/")
@ -190,7 +183,7 @@
$goto("./navigation") $goto("./navigation")
} }
} else if (type === "request-add-component") { } else if (type === "request-add-component") {
$goto(`./components/${$selectedComponent?._id}/new`) toggleAddComponent()
} else if (type === "highlight-setting") { } else if (type === "highlight-setting") {
store.actions.settings.highlight(data.setting) store.actions.settings.highlight(data.setting)
@ -234,9 +227,8 @@
if (isAddingComponent) { if (isAddingComponent) {
$goto(`../${$selectedScreen._id}/components/${$selectedComponent?._id}`) $goto(`../${$selectedScreen._id}/components/${$selectedComponent?._id}`)
} else { } else {
$goto( const id = $selectedComponent?._id || $selectedScreen?.props?._id
`../${$selectedScreen._id}/components/${$selectedComponent?._id}/new` $goto(`../${$selectedScreen._id}/components/${id}/new`)
)
} }
} }
@ -298,7 +290,7 @@
<iframe <iframe
title="componentPreview" title="componentPreview"
bind:this={iframe} bind:this={iframe}
srcdoc={template} src="/preview"
class:hidden={loading || error} class:hidden={loading || error}
class:tablet={$store.previewDevice === "tablet"} class:tablet={$store.previewDevice === "tablet"}
class:mobile={$store.previewDevice === "mobile"} class:mobile={$store.previewDevice === "mobile"}

View File

@ -1,104 +0,0 @@
export default `
<html>
<head>
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600;700&display=swap"
rel="stylesheet"
/>
<link
href="https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css"
rel="stylesheet"
/>
<style>
html, body {
padding: 0;
margin: 0;
}
html {
height: 100%;
width: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
}
body {
flex: 1 1 auto;
overflow: hidden;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
</style>
<script src='{{ CLIENT_LIB_PATH }}'></script>
<script>
function receiveMessage(event) {
if (!event.data) {
return
}
// Parse received message
// If parsing fails, just ignore and wait for the next message
let parsed
try {
parsed = JSON.parse(event.data)
} catch (error) {
console.error("Client received invalid JSON")
// Ignore
}
if (!parsed || !parsed.isBudibaseEvent) {
return
}
// Extract data from message
const {
selectedComponentId,
layout,
screen,
appId,
theme,
customTheme,
previewDevice,
navigation,
hiddenComponentIds
} = parsed
// Set some flags so the app knows we're in the builder
window["##BUDIBASE_IN_BUILDER##"] = true
window["##BUDIBASE_APP_ID##"] = appId
window["##BUDIBASE_PREVIEW_LAYOUT##"] = layout
window["##BUDIBASE_PREVIEW_SCREEN##"] = screen
window["##BUDIBASE_SELECTED_COMPONENT_ID##"] = selectedComponentId
window["##BUDIBASE_PREVIEW_ID##"] = Math.random()
window["##BUDIBASE_PREVIEW_THEME##"] = theme
window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"] = customTheme
window["##BUDIBASE_PREVIEW_DEVICE##"] = previewDevice
window["##BUDIBASE_PREVIEW_NAVIGATION##"] = navigation
window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"] = hiddenComponentIds
// Initialise app
try {
if (window.loadBudibase) {
window.loadBudibase()
document.documentElement.classList.add("loaded")
} else {
throw "The client library couldn't be loaded"
}
} catch (error) {
window.parent.postMessage({ type: "error", error })
}
}
window.addEventListener("message", receiveMessage)
window.parent.postMessage({ type: "ready" })
</script>
</head>
<body/>
</html>
`

View File

@ -2,9 +2,18 @@
import { store } from "builderStore" import { store } from "builderStore"
import { ActionMenu, MenuItem, Icon } from "@budibase/bbui" import { ActionMenu, MenuItem, Icon } from "@budibase/bbui"
export let component
$: noPaste = !$store.componentToPaste $: noPaste = !$store.componentToPaste
const keyboardEvent = (key, ctrlKey = false) => { const keyboardEvent = (key, ctrlKey = false) => {
// Ensure this component is selected first
if (component._id !== $store.selectedComponentId) {
store.update(state => {
state.selectedComponentId = component._id
return state
})
}
document.dispatchEvent(new KeyboardEvent("keydown", { key, ctrlKey })) document.dispatchEvent(new KeyboardEvent("keydown", { key, ctrlKey }))
} }
</script> </script>

View File

@ -112,7 +112,7 @@
Constants.OperatorOptions.NotEmpty.value, Constants.OperatorOptions.NotEmpty.value,
] ]
condition.noValue = noValueOptions.includes(newOperator) condition.noValue = noValueOptions.includes(newOperator)
if (condition.noValue) { if (condition.noValue || newOperator === "oneOf") {
condition.referenceValue = null condition.referenceValue = null
condition.valueType = "string" condition.valueType = "string"
} }
@ -206,7 +206,7 @@
on:change={e => onOperatorChange(condition, e.detail)} on:change={e => onOperatorChange(condition, e.detail)}
/> />
<Select <Select
disabled={condition.noValue} disabled={condition.noValue || condition.operator === "oneOf"}
options={valueTypeOptions} options={valueTypeOptions}
bind:value={condition.valueType} bind:value={condition.valueType}
placeholder={null} placeholder={null}

View File

@ -18,6 +18,8 @@
Body, Body,
Select, Select,
Toggle, Toggle,
Tag,
Tags,
} from "@budibase/bbui" } from "@budibase/bbui"
import { onMount } from "svelte" import { onMount } from "svelte"
import { API } from "api" import { API } from "api"
@ -29,6 +31,8 @@
OIDC: "oidc", OIDC: "oidc",
} }
const HasSpacesRegex = /[\\"\s]/
// Some older google configs contain a manually specified value - retain the functionality to edit the field // Some older google configs contain a manually specified value - retain the functionality to edit the field
// When there is no value or we are in the cloud - prohibit editing the field, must use platform url to change // When there is no value or we are in the cloud - prohibit editing the field, must use platform url to change
$: googleCallbackUrl = undefined $: googleCallbackUrl = undefined
@ -145,7 +149,6 @@
async function save(docs) { async function save(docs) {
let calls = [] let calls = []
// Only if the user has provided an image, upload it // Only if the user has provided an image, upload it
if (image) { if (image) {
let data = new FormData() let data = new FormData()
@ -157,7 +160,6 @@
}) })
) )
} }
docs.forEach(element => { docs.forEach(element => {
// Delete unsupported fields // Delete unsupported fields
delete element.createdAt delete element.createdAt
@ -199,7 +201,6 @@
} }
} }
}) })
if (calls.length) { if (calls.length) {
Promise.all(calls) Promise.all(calls)
.then(data => { .then(data => {
@ -215,6 +216,21 @@
} }
} }
let defaultScopes = ["profile", "email", "offline_access"]
const refreshScopes = idx => {
providers.oidc.config.configs[idx]["scopes"] =
providers.oidc.config.configs[idx]["scopes"]
}
let scopesFields = [
{
editing: true,
inputText: null,
error: null,
},
]
onMount(async () => { onMount(async () => {
try { try {
await organisation.init() await organisation.init()
@ -276,7 +292,7 @@
if (!oidcDoc?._id) { if (!oidcDoc?._id) {
providers.oidc = { providers.oidc = {
type: ConfigTypes.OIDC, type: ConfigTypes.OIDC,
config: { configs: [{ activated: true }] }, config: { configs: [{ activated: true, scopes: defaultScopes }] },
} }
} else { } else {
originalOidcDoc = cloneDeep(oidcDoc) originalOidcDoc = cloneDeep(oidcDoc)
@ -345,6 +361,7 @@
size="s" size="s"
cta cta
on:click={() => save([providers.oidc])} on:click={() => save([providers.oidc])}
dataCy={"oidc-save"}
> >
Save Save
</Button> </Button>
@ -362,6 +379,7 @@
bind:value={providers.oidc.config.configs[0][field.name]} bind:value={providers.oidc.config.configs[0][field.name]}
readonly={field.readonly} readonly={field.readonly}
placeholder={field.placeholder} placeholder={field.placeholder}
dataCy={field.name}
/> />
</div> </div>
{/each} {/each}
@ -392,15 +410,132 @@
<div class="form-row"> <div class="form-row">
<Label size="L">Activated</Label> <Label size="L">Activated</Label>
<Toggle <Toggle
dataCy={"oidc-active"}
text="" text=""
bind:value={providers.oidc.config.configs[0].activated} bind:value={providers.oidc.config.configs[0].activated}
/> />
</div> </div>
</Layout> </Layout>
<span class="advanced-config">
<Layout gap="XS" noPadding>
<Heading size="XS">
<div class="auth-scopes">
<div>Advanced</div>
<Button
secondary
newStyles
size="S"
on:click={() => {
providers.oidc.config.configs[0]["scopes"] = [...defaultScopes]
}}
dataCy={"restore-oidc-default-scopes"}
>
Restore Defaults
</Button>
</div>
</Heading>
<Body size="S">
Changes to your authentication scopes will only take effect when you
next log in. Please refer to your vendor documentation before
modification.
</Body>
<div class="auth-form">
<span class="add-new">
<Label size="L">{"Auth Scopes"}</Label>
<Input
dataCy={"new-scope-input"}
error={scopesFields[0].error}
placeholder={"New Scope"}
bind:value={scopesFields[0].inputText}
on:keyup={e => {
if (!scopesFields[0].inputText) {
scopesFields[0].error = null
}
if (
e.key === "Enter" ||
e.keyCode === 13 ||
e.code == "Space" ||
e.keyCode == 32
) {
let scopes = providers.oidc.config.configs[0]["scopes"]
? providers.oidc.config.configs[0]["scopes"]
: [...defaultScopes]
let update = scopesFields[0].inputText.trim()
if (HasSpacesRegex.test(update)) {
scopesFields[0].error =
"Auth scopes cannot contain spaces, double quotes or backslashes"
return
} else if (scopes.indexOf(update) > -1) {
scopesFields[0].error = "Auth scope already exists"
return
} else if (!update.length) {
scopesFields[0].inputText = null
scopesFields[0].error = null
return
} else {
scopesFields[0].error = null
scopes.push(update)
providers.oidc.config.configs[0]["scopes"] = scopes
scopesFields[0].inputText = null
}
}
}}
/>
</span>
<div class="tag-wrap">
<span />
<Tags>
<Tag closable={false}>openid</Tag>
{#each providers.oidc.config.configs[0]["scopes"] || [...defaultScopes] as tag, idx}
<Tag
closable={scopesFields[0].editing}
on:click={() => {
let idxScopes = providers.oidc.config.configs[0]["scopes"]
if (idxScopes.length == 1) {
idxScopes.pop()
} else {
idxScopes.splice(idx, 1)
refreshScopes(0)
}
}}
>
{tag}
</Tag>
{/each}
</Tags>
</div>
</div>
</Layout>
</span>
{/if} {/if}
</Layout> </Layout>
<style> <style>
.auth-scopes {
display: flex;
justify-content: space-between;
align-items: center;
}
.advanced-config :global(.spectrum-Tags-item) {
margin-left: 0px;
margin-top: var(--spacing-m);
margin-right: var(--spacing-m);
}
.auth-form > * {
display: grid;
grid-gap: var(--spacing-l);
grid-template-columns: 100px 1fr;
}
.advanced-config .auth-form .tag-wrap {
padding: 0px 5px 5px 0px;
}
.form-row { .form-row {
display: grid; display: grid;
grid-template-columns: 100px 1fr; grid-template-columns: 100px 1fr;

View File

@ -44,7 +44,11 @@
] ]
} }
function validateInput(email, index) { function validateInput(input, index) {
if (input.email) {
input.email = input.email.trim()
}
const email = input.email
if (email) { if (email) {
const res = emailValidator(email) const res = emailValidator(email)
if (res === true) { if (res === true) {
@ -95,7 +99,7 @@
bind:dropdownValue={input.role} bind:dropdownValue={input.role}
options={Constants.BudibaseRoleOptions} options={Constants.BudibaseRoleOptions}
error={input.error} error={input.error}
on:blur={() => validateInput(input.email, index)} on:blur={() => validateInput(input, index)}
/> />
</div> </div>
<div class="icon"> <div class="icon">

View File

@ -0,0 +1,73 @@
<script>
import { Body, ModalContent, Table } from "@budibase/bbui"
import { onMount } from "svelte"
export let userData
export let deleteUsersResponse
let successCount
let failureCount
let title
let unsuccessfulUsers
let message
const setTitle = () => {
if (successCount) {
title = `${successCount} users deleted`
} else {
title = "Oops!"
}
}
const setMessage = () => {
if (successCount) {
message = "However there was a problem deleting some users."
} else {
message = "There was a problem deleting some users."
}
}
const setUsers = () => {
unsuccessfulUsers = deleteUsersResponse.unsuccessful.map(user => {
return {
email: user.email,
reason: user.reason,
}
})
}
onMount(() => {
successCount = deleteUsersResponse.successful.length
failureCount = deleteUsersResponse.unsuccessful.length
setTitle()
setMessage()
setUsers()
})
const schema = {
email: {},
reason: {},
}
</script>
<ModalContent
size="M"
{title}
confirmText="Close"
showCloseIcon={false}
showCancelButton={false}
>
<Body size="XS">
{message}
</Body>
<Table
{schema}
data={unsuccessfulUsers}
allowEditColumns={false}
allowEditRows={false}
allowSelectRows={false}
/>
</ModalContent>
<style>
</style>

View File

@ -62,7 +62,7 @@
csvString = e.target.result csvString = e.target.result
files = fileArray files = fileArray
userEmails = csvString.split("\n") userEmails = csvString.split(/\r?\n/)
}) })
reader.readAsText(fileArray[0]) reader.readAsText(fileArray[0])
} }

View File

@ -0,0 +1,75 @@
<script>
import { Body, ModalContent, Table } from "@budibase/bbui"
import { onMount } from "svelte"
export let inviteUsersResponse
let hasSuccess
let hasFailure
let title
let failureMessage
let unsuccessfulUsers
const setTitle = () => {
if (hasSuccess) {
title = "Users invited!"
} else if (hasFailure) {
title = "Oops!"
}
}
const setFailureMessage = () => {
if (hasSuccess) {
failureMessage = "However there was a problem inviting some users."
} else {
failureMessage = "There was a problem inviting users."
}
}
const setUsers = () => {
unsuccessfulUsers = inviteUsersResponse.unsuccessful.map(user => {
return {
email: user.email,
reason: user.reason,
}
})
}
onMount(() => {
hasSuccess = inviteUsersResponse.successful.length
hasFailure = inviteUsersResponse.unsuccessful.length
setTitle()
setFailureMessage()
setUsers()
})
const failedSchema = {
email: {},
reason: {},
}
</script>
<ModalContent showCancelButton={false} {title} confirmText="Done">
{#if hasSuccess}
<Body size="XS">
Your users should now receive an email invite to get access to their
Budibase account
</Body>
{/if}
{#if hasFailure}
<Body size="XS">
{failureMessage}
</Body>
<Table
schema={failedSchema}
data={unsuccessfulUsers}
allowEditColumns={false}
allowEditRows={false}
allowSelectRows={false}
/>
{/if}
</ModalContent>
<style>
</style>

View File

@ -2,24 +2,78 @@
import { Body, ModalContent, Table, Icon } from "@budibase/bbui" import { Body, ModalContent, Table, Icon } from "@budibase/bbui"
import PasswordCopyRenderer from "./PasswordCopyRenderer.svelte" import PasswordCopyRenderer from "./PasswordCopyRenderer.svelte"
import { parseToCsv } from "helpers/data/utils" import { parseToCsv } from "helpers/data/utils"
import { onMount } from "svelte"
export let userData export let userData
export let createUsersResponse
$: mappedData = userData.map(user => { let hasSuccess
return { let hasFailure
email: user.email, let title
password: user.password, let failureMessage
let userDataIndex
let successfulUsers
let unsuccessfulUsers
const setTitle = () => {
if (hasSuccess) {
title = "Users created!"
} else if (hasFailure) {
title = "Oops!"
} }
}
const setFailureMessage = () => {
if (hasSuccess) {
failureMessage = "However there was a problem creating some users."
} else {
failureMessage = "There was a problem creating some users."
}
}
const setUsers = () => {
userDataIndex = userData.reduce((prev, current) => {
prev[current.email] = current
return prev
}, {})
successfulUsers = createUsersResponse.successful.map(user => {
return {
email: user.email,
password: userDataIndex[user.email].password,
}
})
unsuccessfulUsers = createUsersResponse.unsuccessful.map(user => {
return {
email: user.email,
reason: user.reason,
}
})
}
onMount(() => {
hasSuccess = createUsersResponse.successful.length
hasFailure = createUsersResponse.unsuccessful.length
setTitle()
setFailureMessage()
setUsers()
}) })
const schema = { const successSchema = {
email: {}, email: {},
password: {}, password: {},
} }
const failedSchema = {
email: {},
reason: {},
}
const downloadCsvFile = () => { const downloadCsvFile = () => {
const fileName = "passwords.csv" const fileName = "passwords.csv"
const content = parseToCsv(["email", "password"], mappedData) const content = parseToCsv(["email", "password"], successfulUsers)
download(fileName, content) download(fileName, content)
} }
@ -42,36 +96,52 @@
</script> </script>
<ModalContent <ModalContent
size="S" size="M"
title="Accounts created!" {title}
confirmText="Done" confirmText="Done"
showCancelButton={false} showCancelButton={false}
cancelText="Cancel" cancelText="Cancel"
showCloseIcon={false} showCloseIcon={false}
> >
<Body size="XS"> {#if hasFailure}
All your new users can be accessed through the autogenerated passwords. Take <Body size="XS">
note of these passwords or download the CSV file. {failureMessage}
</Body> </Body>
<Table
schema={failedSchema}
data={unsuccessfulUsers}
allowEditColumns={false}
allowEditRows={false}
allowSelectRows={false}
/>
{/if}
{#if hasSuccess}
<Body size="XS">
All your new users can be accessed through the autogenerated passwords.
Take note of these passwords or download the CSV file.
</Body>
<div class="container" on:click={downloadCsvFile}> <div class="container" on:click={downloadCsvFile}>
<div class="inner"> <div class="inner">
<Icon name="Download" /> <Icon name="Download" />
<div style="margin-left: var(--spacing-m)"> <div style="margin-left: var(--spacing-m)">
<Body size="XS">Passwords CSV</Body> <Body size="XS">Passwords CSV</Body>
</div>
</div> </div>
</div> </div>
</div>
<Table <Table
{schema} schema={successSchema}
data={mappedData} data={successfulUsers}
allowEditColumns={false} allowEditColumns={false}
allowEditRows={false} allowEditRows={false}
allowSelectRows={false} allowSelectRows={false}
customRenderers={[{ column: "password", component: PasswordCopyRenderer }]} customRenderers={[
/> { column: "password", component: PasswordCopyRenderer },
]}
/>
{/if}
</ModalContent> </ModalContent>
<style> <style>

View File

@ -7,7 +7,6 @@
Table, Table,
Layout, Layout,
Modal, Modal,
ModalContent,
Search, Search,
notifications, notifications,
Pagination, Pagination,
@ -23,6 +22,8 @@
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import OnboardingTypeModal from "./_components/OnboardingTypeModal.svelte" import OnboardingTypeModal from "./_components/OnboardingTypeModal.svelte"
import PasswordModal from "./_components/PasswordModal.svelte" import PasswordModal from "./_components/PasswordModal.svelte"
import InvitedModal from "./_components/InvitedModal.svelte"
import DeletionFailureModal from "./_components/DeletionFailureModal.svelte"
import ImportUsersModal from "./_components/ImportUsersModal.svelte" import ImportUsersModal from "./_components/ImportUsersModal.svelte"
import { createPaginationStore } from "helpers/pagination" import { createPaginationStore } from "helpers/pagination"
import { get } from "svelte/store" import { get } from "svelte/store"
@ -33,7 +34,8 @@
inviteConfirmationModal, inviteConfirmationModal,
onboardingTypeModal, onboardingTypeModal,
passwordModal, passwordModal,
importUsersModal importUsersModal,
deletionFailureModal
let pageInfo = createPaginationStore() let pageInfo = createPaginationStore()
let prevEmail = undefined, let prevEmail = undefined,
searchEmail = undefined searchEmail = undefined
@ -55,6 +57,9 @@
apps: {}, apps: {},
} }
$: userData = [] $: userData = []
$: createUsersResponse = { successful: [], unsuccessful: [] }
$: deleteUsersResponse = { successful: [], unsuccessful: [] }
$: inviteUsersResponse = { successful: [], unsuccessful: [] }
$: page = $pageInfo.page $: page = $pageInfo.page
$: fetchUsers(page, searchEmail) $: fetchUsers(page, searchEmail)
$: { $: {
@ -92,8 +97,7 @@
admin: user.role === Constants.BudibaseRoles.Admin, admin: user.role === Constants.BudibaseRoles.Admin,
})) }))
try { try {
const res = await users.invite(payload) inviteUsersResponse = await users.invite(payload)
notifications.success(res.message)
inviteConfirmationModal.show() inviteConfirmationModal.show()
} catch (error) { } catch (error) {
notifications.error("Error inviting user") notifications.error("Error inviting user")
@ -116,8 +120,9 @@
newUsers.push(user) newUsers.push(user)
} }
if (!newUsers.length) if (!newUsers.length) {
notifications.info("Duplicated! There is no new users to add.") notifications.info("Duplicated! There is no new users to add.")
}
return { ...userData, users: newUsers } return { ...userData, users: newUsers }
} }
@ -139,12 +144,14 @@
userData = await removingDuplicities({ groups, users }) userData = await removingDuplicities({ groups, users })
if (!userData.users.length) return if (!userData.users.length) return
return createUser() return createUsers()
} }
async function createUser() { async function createUsers() {
try { try {
await users.create(await removingDuplicities(userData)) createUsersResponse = await users.create(
await removingDuplicities(userData)
)
notifications.success("Successfully created user") notifications.success("Successfully created user")
await groups.actions.init() await groups.actions.init()
passwordModal.show() passwordModal.show()
@ -157,7 +164,7 @@
if (onboardingType === "emailOnboarding") { if (onboardingType === "emailOnboarding") {
createUserFlow() createUserFlow()
} else { } else {
await createUser() await createUsers()
} }
} }
@ -176,8 +183,15 @@
notifications.error("You cannot delete yourself") notifications.error("You cannot delete yourself")
return return
} }
await users.bulkDelete(ids) deleteUsersResponse = await users.bulkDelete(ids)
notifications.success(`Successfully deleted ${selectedRows.length} rows`) if (deleteUsersResponse.unsuccessful?.length) {
deletionFailureModal.show()
} else {
notifications.success(
`Successfully deleted ${selectedRows.length} users`
)
}
selectedRows = [] selectedRows = []
await fetchUsers(page, searchEmail) await fetchUsers(page, searchEmail)
} catch (error) { } catch (error) {
@ -267,16 +281,7 @@
</Modal> </Modal>
<Modal bind:this={inviteConfirmationModal}> <Modal bind:this={inviteConfirmationModal}>
<ModalContent <InvitedModal {inviteUsersResponse} />
showCancelButton={false}
title="Invites sent!"
confirmText="Done"
>
<Body size="S"
>Your users should now recieve an email invite to get access to their
Budibase account</Body
></ModalContent
>
</Modal> </Modal>
<Modal bind:this={onboardingTypeModal}> <Modal bind:this={onboardingTypeModal}>
@ -284,7 +289,11 @@
</Modal> </Modal>
<Modal bind:this={passwordModal}> <Modal bind:this={passwordModal}>
<PasswordModal userData={userData.users} /> <PasswordModal {createUsersResponse} userData={userData.users} />
</Modal>
<Modal bind:this={deletionFailureModal}>
<DeletionFailureModal {deleteUsersResponse} />
</Modal> </Modal>
<Modal bind:this={importUsersModal}> <Modal bind:this={importUsersModal}>

View File

@ -63,10 +63,14 @@ export function createUsersStore() {
return body return body
}) })
await API.createUsers({ users: mappedUsers, groups: data.groups }) const response = await API.createUsers({
users: mappedUsers,
groups: data.groups,
})
// re-search from first page // re-search from first page
await search() await search()
return response
} }
async function del(id) { async function del(id) {
@ -79,7 +83,7 @@ export function createUsersStore() {
} }
async function bulkDelete(userIds) { async function bulkDelete(userIds) {
await API.deleteUsers(userIds) return API.deleteUsers(userIds)
} }
async function save(user) { async function save(user) {

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "1.2.41-alpha.5", "version": "1.2.58-alpha.5",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "src/index.js",
"bin": { "bin": {
@ -26,7 +26,7 @@
"outputPath": "build" "outputPath": "build"
}, },
"dependencies": { "dependencies": {
"@budibase/backend-core": "1.2.41-alpha.5", "@budibase/backend-core": "1.2.58-alpha.5",
"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",

View File

@ -8,7 +8,8 @@
"devicePreview": true, "devicePreview": true,
"messagePassing": true, "messagePassing": true,
"rowSelection": true, "rowSelection": true,
"continueIfAction": true "continueIfAction": true,
"showNotificationAction": true
}, },
"layout": { "layout": {
"name": "Layout", "name": "Layout",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "1.2.41-alpha.5", "version": "1.2.58-alpha.5",
"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,9 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "1.2.41-alpha.5", "@budibase/bbui": "1.2.58-alpha.5",
"@budibase/frontend-core": "1.2.41-alpha.5", "@budibase/frontend-core": "1.2.58-alpha.5",
"@budibase/string-templates": "1.2.41-alpha.5", "@budibase/string-templates": "1.2.58-alpha.5",
"@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

@ -77,6 +77,7 @@
{direction} {direction}
on:change={handleChange} on:change={handleChange}
getOptionLabel={flatOptions ? x => x : x => x.label} getOptionLabel={flatOptions ? x => x : x => x.label}
getOptionTitle={flatOptions ? x => x : x => x.label}
getOptionValue={flatOptions ? x => x : x => x.value} getOptionValue={flatOptions ? x => x : x => x.value}
/> />
{/if} {/if}

View File

@ -62,10 +62,14 @@ const createNotificationStore = () => {
subscribe: store.subscribe, subscribe: store.subscribe,
actions: { actions: {
send, send,
info: msg => send(msg, "info", "Info"), info: (msg, autoDismiss) =>
success: msg => send(msg, "success", "CheckmarkCircle"), send(msg, "info", "Info", autoDismiss ?? true),
warning: msg => send(msg, "warning", "Alert"), success: (msg, autoDismiss) =>
error: msg => send(msg, "error", "Alert", false), send(msg, "success", "CheckmarkCircle", autoDismiss ?? true),
warning: (msg, autoDismiss) =>
send(msg, "warning", "Alert", autoDismiss ?? true),
error: (msg, autoDismiss) =>
send(msg, "error", "Alert", autoDismiss ?? false),
blockNotifications, blockNotifications,
dismiss, dismiss,
}, },

View File

@ -300,6 +300,14 @@ const continueIfHandler = action => {
} }
} }
const showNotificationHandler = action => {
const { message, type, autoDismiss } = action.parameters
if (!message || !type) {
return
}
notificationStore.actions[type]?.(message, autoDismiss)
}
const handlerMap = { const handlerMap = {
["Save Row"]: saveRowHandler, ["Save Row"]: saveRowHandler,
["Duplicate Row"]: duplicateRowHandler, ["Duplicate Row"]: duplicateRowHandler,
@ -318,6 +326,7 @@ const handlerMap = {
["Upload File to S3"]: s3UploadHandler, ["Upload File to S3"]: s3UploadHandler,
["Export Data"]: exportDataHandler, ["Export Data"]: exportDataHandler,
["Continue if / Stop if"]: continueIfHandler, ["Continue if / Stop if"]: continueIfHandler,
["Show Notification"]: showNotificationHandler,
} }
const confirmTextMap = { const confirmTextMap = {

View File

@ -1,12 +1,12 @@
{ {
"name": "@budibase/frontend-core", "name": "@budibase/frontend-core",
"version": "1.2.41-alpha.5", "version": "1.2.58-alpha.5",
"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": "1.2.41-alpha.5", "@budibase/bbui": "1.2.58-alpha.5",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"svelte": "^3.46.2" "svelte": "^3.46.2"
} }

View File

@ -72,7 +72,7 @@ const cleanupQuery = query => {
continue continue
} }
for (let [key, value] of Object.entries(query[filterField])) { for (let [key, value] of Object.entries(query[filterField])) {
if (!value || value === "") { if (value == null || value === "") {
delete query[filterField][key] delete query[filterField][key]
} }
} }
@ -119,7 +119,7 @@ export const buildLuceneQuery = filter => {
return return
} }
} }
if (type === "number" && !Array.isArray(value)) { if (type === "number" && typeof value === "string") {
if (operator === "oneOf") { if (operator === "oneOf") {
value = value.split(",").map(item => parseFloat(item)) value = value.split(",").map(item => parseFloat(item))
} else if (!isHbs) { } else if (!isHbs) {
@ -186,7 +186,7 @@ export const runLuceneQuery = (docs, query) => {
return docs return docs
} }
// make query consistent first // Make query consistent first
query = cleanupQuery(query) query = cleanupQuery(query)
// Iterates over a set of filters and evaluates a fail function against a doc // Iterates over a set of filters and evaluates a fail function against a doc
@ -218,7 +218,12 @@ export const runLuceneQuery = (docs, query) => {
// Process a range match // Process a range match
const rangeMatch = match("range", (docValue, testValue) => { const rangeMatch = match("range", (docValue, testValue) => {
return !docValue || docValue < testValue.low || docValue > testValue.high return (
docValue == null ||
docValue === "" ||
docValue < testValue.low ||
docValue > testValue.high
)
}) })
// Process an equal match (fails if the value is different) // Process an equal match (fails if the value is different)

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "1.2.41-alpha.5", "version": "1.2.58-alpha.5",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -77,11 +77,11 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@apidevtools/swagger-parser": "10.0.3", "@apidevtools/swagger-parser": "10.0.3",
"@budibase/backend-core": "1.2.41-alpha.5", "@budibase/backend-core": "1.2.58-alpha.5",
"@budibase/client": "1.2.41-alpha.5", "@budibase/client": "1.2.58-alpha.5",
"@budibase/pro": "1.2.41-alpha.5", "@budibase/pro": "1.2.58-alpha.5",
"@budibase/string-templates": "1.2.41-alpha.5", "@budibase/string-templates": "1.2.58-alpha.5",
"@budibase/types": "1.2.41-alpha.5", "@budibase/types": "1.2.58-alpha.5",
"@bull-board/api": "3.7.0", "@bull-board/api": "3.7.0",
"@bull-board/koa": "3.9.4", "@bull-board/koa": "3.9.4",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",
@ -93,12 +93,13 @@
"arangojs": "7.2.0", "arangojs": "7.2.0",
"aws-sdk": "2.1030.0", "aws-sdk": "2.1030.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"bull": "3.29.3", "bull": "4.8.5",
"chmodr": "1.2.0", "chmodr": "1.2.0",
"csvtojson": "2.0.10", "csvtojson": "2.0.10",
"curlconverter": "3.21.0", "curlconverter": "3.21.0",
"dotenv": "8.2.0", "dotenv": "8.2.0",
"download": "8.0.0", "download": "8.0.0",
"elastic-apm-node": "3.38.0",
"fix-path": "3.0.0", "fix-path": "3.0.0",
"form-data": "4.0.0", "form-data": "4.0.0",
"fs-extra": "8.1.0", "fs-extra": "8.1.0",

View File

@ -47,7 +47,14 @@ import { checkAppMetadata } from "../../automations/logging"
import { getUniqueRows } from "../../utilities/usageQuota/rows" import { getUniqueRows } from "../../utilities/usageQuota/rows"
import { quotas } from "@budibase/pro" import { quotas } from "@budibase/pro"
import { errors, events, migrations } from "@budibase/backend-core" import { errors, events, migrations } from "@budibase/backend-core"
import { App, MigrationType } from "@budibase/types" import {
App,
Layout,
Screen,
MigrationType,
AppNavigation,
} from "@budibase/types"
import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts"
const URL_REGEX_SLASH = /\/|\\/g const URL_REGEX_SLASH = /\/|\\/g
@ -243,27 +250,19 @@ const performAppCreate = async (ctx: any) => {
} }
const instance = await createInstance(instanceConfig) const instance = await createInstance(instanceConfig)
const appId = instance._id const appId = instance._id
const db = context.getAppDB() const db = context.getAppDB()
let _rev
try { let newApplication: App = {
// if template there will be an existing doc
const existing = await db.get(DocumentType.APP_METADATA)
_rev = existing._rev
} catch (err) {
// nothing to do
}
const newApplication: App = {
_id: DocumentType.APP_METADATA, _id: DocumentType.APP_METADATA,
_rev, _rev: undefined,
appId: instance._id, appId,
type: "app", type: "app",
version: packageJson.version, version: packageJson.version,
componentLibraries: ["@budibase/standard-components"], componentLibraries: ["@budibase/standard-components"],
name: name, name: name,
url: url, url: url,
template: ctx.request.body.template, template: templateKey,
instance: instance, instance,
tenantId: getTenantId(), tenantId: getTenantId(),
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
@ -285,6 +284,36 @@ const performAppCreate = async (ctx: any) => {
buttonBorderRadius: "16px", buttonBorderRadius: "16px",
}, },
} }
// If we used a template or imported an app there will be an existing doc.
// Fetch and migrate some metadata from the existing app.
try {
const existing: App = await db.get(DocumentType.APP_METADATA)
const keys: (keyof App)[] = [
"_rev",
"navigation",
"theme",
"customTheme",
"icon",
]
keys.forEach(key => {
if (existing[key]) {
// @ts-ignore
newApplication[key] = existing[key]
}
})
// Migrate navigation settings and screens if required
if (existing && !existing.navigation) {
const navigation = await migrateAppNavigation()
if (navigation) {
newApplication.navigation = navigation
}
}
} catch (err) {
// Nothing to do
}
const response = await db.put(newApplication, { force: true }) const response = await db.put(newApplication, { force: true })
newApplication._rev = response.rev newApplication._rev = response.rev
@ -566,3 +595,55 @@ const updateAppPackage = async (appPackage: any, appId: any) => {
return newAppPackage return newAppPackage
}) })
} }
const migrateAppNavigation = async () => {
const db = context.getAppDB()
const existing: App = await db.get(DocumentType.APP_METADATA)
const layouts: Layout[] = await getLayouts()
const screens: Screen[] = await getScreens()
// Migrate all screens, removing custom layouts
for (let screen of screens) {
if (!screen.layoutId) {
return
}
const layout = layouts.find(layout => layout._id === screen.layoutId)
screen.layoutId = undefined
screen.showNavigation = layout?.props.navigation !== "None"
screen.width = layout?.props.width || "Large"
await db.put(screen)
}
// Migrate layout navigation settings
const { name, customTheme } = existing
const layout = layouts?.find(
(layout: Layout) => layout._id === BASE_LAYOUT_PROP_IDS.PRIVATE
)
if (layout) {
let navigationSettings: any = {
navigation: "Top",
title: name,
navWidth: "Large",
navBackground:
customTheme?.navBackground || "var(--spectrum-global-color-gray-50)",
navTextColor:
customTheme?.navTextColor || "var(--spectrum-global-color-gray-800)",
}
if (layout) {
navigationSettings.hideLogo = layout.props.hideLogo
navigationSettings.hideTitle = layout.props.hideTitle
navigationSettings.title = layout.props.title || name
navigationSettings.logoUrl = layout.props.logoUrl
navigationSettings.links = layout.props.links
navigationSettings.navigation = layout.props.navigation || "Top"
navigationSettings.sticky = layout.props.sticky
navigationSettings.navWidth = layout.props.width || "Large"
if (navigationSettings.navigation === "None") {
navigationSettings.navigation = "Top"
}
}
return navigationSettings
} else {
return null
}
}

View File

@ -3,32 +3,36 @@ const { getComponentLibraryManifest } = require("../../utilities/fileSystem")
const { getAppDB } = require("@budibase/backend-core/context") const { getAppDB } = require("@budibase/backend-core/context")
exports.fetchAppComponentDefinitions = async function (ctx) { exports.fetchAppComponentDefinitions = async function (ctx) {
const db = getAppDB() try {
const app = await db.get(DocumentType.APP_METADATA) const db = getAppDB()
const app = await db.get(DocumentType.APP_METADATA)
let componentManifests = await Promise.all( let componentManifests = await Promise.all(
app.componentLibraries.map(async library => { app.componentLibraries.map(async library => {
let manifest = await getComponentLibraryManifest(library) let manifest = await getComponentLibraryManifest(library)
return { return {
manifest, manifest,
library, library,
} }
}) })
) )
const definitions = {} const definitions = {}
for (let { manifest, library } of componentManifests) { for (let { manifest, library } of componentManifests) {
for (let key of Object.keys(manifest)) { for (let key of Object.keys(manifest)) {
if (key === "features") { if (key === "features") {
definitions[key] = manifest[key] definitions[key] = manifest[key]
} else { } else {
const fullComponentName = `${library}/${key}`.toLowerCase() const fullComponentName = `${library}/${key}`.toLowerCase()
definitions[fullComponentName] = { definitions[fullComponentName] = {
component: fullComponentName, component: fullComponentName,
...manifest[key], ...manifest[key],
}
} }
} }
} }
ctx.body = definitions
} catch (err) {
console.error(`component-definitions=failed`, err)
} }
ctx.body = definitions
} }

View File

@ -375,6 +375,7 @@ exports.exportRows = async ctx => {
const table = await db.get(ctx.params.tableId) const table = await db.get(ctx.params.tableId)
const rowIds = ctx.request.body.rows const rowIds = ctx.request.body.rows
let format = ctx.query.format let format = ctx.query.format
const { columns } = ctx.request.body
let response = ( let response = (
await db.allDocs({ await db.allDocs({
include_docs: true, include_docs: true,
@ -382,7 +383,20 @@ exports.exportRows = async ctx => {
}) })
).rows.map(row => row.doc) ).rows.map(row => row.doc)
let rows = await outputProcessing(table, response) let result = await outputProcessing(table, response)
let rows = []
// Filter data to only specified columns if required
if (columns && columns.length) {
for (let i = 0; i < result.length; i++) {
rows[i] = {}
for (let column of columns) {
rows[i][column] = result[i][column]
}
}
} else {
rows = result
}
let headers = Object.keys(rows[0]) let headers = Object.keys(rows[0])
const exporter = exporters[format] const exporter = exporters[format]

View File

@ -128,6 +128,22 @@ export const serveApp = async function (ctx: any) {
} }
} }
export const serveBuilderPreview = async function (ctx: any) {
const db = getAppDB({ skip_setup: true })
const appInfo = await db.get(DocumentType.APP_METADATA)
if (!env.isJest()) {
let appId = getAppId()
const previewHbs = loadHandlebarsFile(`${__dirname}/templates/preview.hbs`)
ctx.body = await processString(previewHbs, {
clientLibPath: clientLibraryPath(appId, appInfo.version, ctx),
})
} else {
// just return the app info for jest to assert on
ctx.body = { ...appInfo, builderPreview: true }
}
}
export const serveClientLibrary = async function (ctx: any) { export const serveClientLibrary = async function (ctx: any) {
return send(ctx, "budibase-client.js", { return send(ctx, "budibase-client.js", {
root: join(NODE_MODULES_PATH, "@budibase", "client", "dist"), root: join(NODE_MODULES_PATH, "@budibase", "client", "dist"),

View File

@ -0,0 +1,103 @@
<html lang="en">
<head>
<title>Budibase Builder Preview</title>
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600;700&display=swap"
rel="stylesheet"
/>
<link
href="https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css"
rel="stylesheet"
/>
<style>
html, body {
padding: 0;
margin: 0;
}
html {
height: 100%;
width: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
}
body {
flex: 1 1 auto;
overflow: hidden;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
</style>
<script src='{{ clientLibPath }}'></script>
<script>
function receiveMessage(event) {
if (!event.data) {
return
}
// Parse received message
// If parsing fails, just ignore and wait for the next message
let parsed
try {
parsed = JSON.parse(event.data)
} catch (error) {
console.error("Client received invalid JSON")
// Ignore
}
if (!parsed || !parsed.isBudibaseEvent) {
return
}
// Extract data from message
const {
selectedComponentId,
layout,
screen,
appId,
theme,
customTheme,
previewDevice,
navigation,
hiddenComponentIds
} = parsed
// Set some flags so the app knows we're in the builder
window["##BUDIBASE_IN_BUILDER##"] = true
window["##BUDIBASE_APP_ID##"] = appId
window["##BUDIBASE_PREVIEW_LAYOUT##"] = layout
window["##BUDIBASE_PREVIEW_SCREEN##"] = screen
window["##BUDIBASE_SELECTED_COMPONENT_ID##"] = selectedComponentId
window["##BUDIBASE_PREVIEW_ID##"] = Math.random()
window["##BUDIBASE_PREVIEW_THEME##"] = theme
window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"] = customTheme
window["##BUDIBASE_PREVIEW_DEVICE##"] = previewDevice
window["##BUDIBASE_PREVIEW_NAVIGATION##"] = navigation
window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"] = hiddenComponentIds
// Initialise app
try {
if (window.loadBudibase) {
window.loadBudibase()
document.documentElement.classList.add("loaded")
} else {
throw "The client library couldn't be loaded"
}
} catch (error) {
window.parent.postMessage({ type: "error", error })
}
}
window.addEventListener("message", receiveMessage)
window.parent.postMessage({ type: "ready" })
</script>
</head>
<body></body>
</html>

View File

@ -56,6 +56,7 @@ router
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE), authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
controller.deleteObjects controller.deleteObjects
) )
.get("/preview", authorized(BUILDER), controller.serveBuilderPreview)
.get("/:appId/:path*", controller.serveApp) .get("/:appId/:path*", controller.serveApp)
.get("/app/:appUrl/:path*", controller.serveApp) .get("/app/:appUrl/:path*", controller.serveApp)
.post( .post(

View File

@ -17,6 +17,7 @@ const {
checkBuilderEndpoint, checkBuilderEndpoint,
} = require("./utilities/TestFunctions") } = require("./utilities/TestFunctions")
const setup = require("./utilities") const setup = require("./utilities")
const { basicScreen, basicLayout } = setup.structures
const { AppStatus } = require("../../../db/utils") const { AppStatus } = require("../../../db/utils")
const { events } = require("@budibase/backend-core") const { events } = require("@budibase/backend-core")
@ -81,6 +82,31 @@ describe("/applications", () => {
body: { name: "My App" }, body: { name: "My App" },
}) })
}) })
it("migrates navigation settings from old apps", async () => {
const res = await request
.post("/api/applications")
.field("name", "Old App")
.field("useTemplate", "true")
.set(config.defaultHeaders())
.attach("templateFile", "src/api/routes/tests/data/old-app.txt")
.expect("Content-Type", /json/)
.expect(200)
expect(res.body._id).toBeDefined()
expect(res.body.navigation).toBeDefined()
expect(res.body.navigation.hideLogo).toBe(true)
expect(res.body.navigation.title).toBe("Custom Title")
expect(res.body.navigation.hideLogo).toBe(true)
expect(res.body.navigation.navigation).toBe("Left")
expect(res.body.navigation.navBackground).toBe(
"var(--spectrum-global-color-blue-600)"
)
expect(res.body.navigation.navTextColor).toBe(
"var(--spectrum-global-color-gray-50)"
)
expect(events.app.created).toBeCalledTimes(1)
expect(events.app.fileImported).toBeCalledTimes(1)
})
}) })
describe("fetch", () => { describe("fetch", () => {

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,12 @@ const setup = require("./utilities")
const { basicRow } = setup.structures const { basicRow } = setup.structures
const { doInAppContext } = require("@budibase/backend-core/context") const { doInAppContext } = require("@budibase/backend-core/context")
const { doInTenant } = require("@budibase/backend-core/tenancy") const { doInTenant } = require("@budibase/backend-core/tenancy")
const { quotas, QuotaUsageType, StaticQuotaName, MonthlyQuotaName } = require("@budibase/pro") const {
quotas,
QuotaUsageType,
StaticQuotaName,
MonthlyQuotaName,
} = require("@budibase/pro")
describe("/rows", () => { describe("/rows", () => {
let request = setup.getRequest() let request = setup.getRequest()
@ -23,23 +28,30 @@ describe("/rows", () => {
await request await request
.get(`/api/${table._id}/rows/${id}`) .get(`/api/${table._id}/rows/${id}`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(status) .expect(status)
const getRowUsage = async () => { const getRowUsage = async () => {
return config.doInContext(null, () => quotas.getCurrentUsageValue(QuotaUsageType.STATIC, StaticQuotaName.ROWS)) return config.doInContext(null, () =>
quotas.getCurrentUsageValue(QuotaUsageType.STATIC, StaticQuotaName.ROWS)
)
} }
const getQueryUsage = async () => { const getQueryUsage = async () => {
return config.doInContext(null, () => quotas.getCurrentUsageValue(QuotaUsageType.MONTHLY, MonthlyQuotaName.QUERIES)) return config.doInContext(null, () =>
quotas.getCurrentUsageValue(
QuotaUsageType.MONTHLY,
MonthlyQuotaName.QUERIES
)
)
} }
const assertRowUsage = async (expected) => { const assertRowUsage = async expected => {
const usage = await getRowUsage() const usage = await getRowUsage()
expect(usage).toBe(expected) expect(usage).toBe(expected)
} }
const assertQueryUsage = async (expected) => { const assertQueryUsage = async expected => {
const usage = await getQueryUsage() const usage = await getQueryUsage()
expect(usage).toBe(expected) expect(usage).toBe(expected)
} }
@ -76,10 +88,12 @@ describe("/rows", () => {
name: "Updated Name", name: "Updated Name",
}) })
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.res.statusMessage).toEqual(`${table.name} updated successfully.`) expect(res.res.statusMessage).toEqual(
`${table.name} updated successfully.`
)
expect(res.body.name).toEqual("Updated Name") expect(res.body.name).toEqual("Updated Name")
// await assertRowUsage(rowUsage) // await assertRowUsage(rowUsage)
// await assertQueryUsage(queryUsage + 1) // await assertQueryUsage(queryUsage + 1)
@ -92,7 +106,7 @@ describe("/rows", () => {
const res = await request const res = await request
.get(`/api/${table._id}/rows/${existing._id}`) .get(`/api/${table._id}/rows/${existing._id}`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body).toEqual({ expect(res.body).toEqual({
@ -110,7 +124,7 @@ describe("/rows", () => {
const newRow = { const newRow = {
tableId: table._id, tableId: table._id,
name: "Second Contact", name: "Second Contact",
status: "new" status: "new",
} }
await config.createRow() await config.createRow()
await config.createRow(newRow) await config.createRow(newRow)
@ -119,7 +133,7 @@ describe("/rows", () => {
const res = await request const res = await request
.get(`/api/${table._id}/rows`) .get(`/api/${table._id}/rows`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body.length).toBe(2) expect(res.body.length).toBe(2)
@ -135,17 +149,36 @@ describe("/rows", () => {
await request await request
.get(`/api/${table._id}/rows/not-a-valid-id`) .get(`/api/${table._id}/rows/not-a-valid-id`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(404) .expect(404)
await assertQueryUsage(queryUsage) // no change await assertQueryUsage(queryUsage) // no change
}) })
it("row values are coerced", async () => { it("row values are coerced", async () => {
const str = {type:"string", constraints: { type: "string", presence: false }} const str = {
const attachment = {type:"attachment", constraints: { type: "array", presence: false }} type: "string",
const bool = {type:"boolean", constraints: { type: "boolean", presence: false }} constraints: { type: "string", presence: false },
const number = {type:"number", constraints: { type: "number", presence: false }} }
const datetime = {type:"datetime", constraints: { type: "string", presence: false, datetime: {earliest:"", latest: ""} }} const attachment = {
type: "attachment",
constraints: { type: "array", presence: false },
}
const bool = {
type: "boolean",
constraints: { type: "boolean", presence: false },
}
const number = {
type: "number",
constraints: { type: "number", presence: false },
}
const datetime = {
type: "datetime",
constraints: {
type: "string",
presence: false,
datetime: { earliest: "", latest: "" },
},
}
table = await config.createTable({ table = await config.createTable({
name: "TestTable2", name: "TestTable2",
@ -171,9 +204,9 @@ describe("/rows", () => {
boolUndefined: bool, boolUndefined: bool,
boolString: bool, boolString: bool,
boolBool: bool, boolBool: bool,
attachmentNull : attachment, attachmentNull: attachment,
attachmentUndefined : attachment, attachmentUndefined: attachment,
attachmentEmpty : attachment, attachmentEmpty: attachment,
}, },
}) })
@ -198,9 +231,9 @@ describe("/rows", () => {
boolString: "true", boolString: "true",
boolBool: true, boolBool: true,
tableId: table._id, tableId: table._id,
attachmentNull : null, attachmentNull: null,
attachmentUndefined : undefined, attachmentUndefined: undefined,
attachmentEmpty : "", attachmentEmpty: "",
} }
const id = (await config.createRow(row))._id const id = (await config.createRow(row))._id
@ -218,7 +251,9 @@ describe("/rows", () => {
expect(saved.datetimeEmptyString).toBe(null) expect(saved.datetimeEmptyString).toBe(null)
expect(saved.datetimeNull).toBe(null) expect(saved.datetimeNull).toBe(null)
expect(saved.datetimeUndefined).toBe(undefined) expect(saved.datetimeUndefined).toBe(undefined)
expect(saved.datetimeString).toBe(new Date(row.datetimeString).toISOString()) expect(saved.datetimeString).toBe(
new Date(row.datetimeString).toISOString()
)
expect(saved.datetimeDate).toBe(row.datetimeDate.toISOString()) expect(saved.datetimeDate).toBe(row.datetimeDate.toISOString())
expect(saved.boolNull).toBe(null) expect(saved.boolNull).toBe(null)
expect(saved.boolEmpty).toBe(null) expect(saved.boolEmpty).toBe(null)
@ -247,10 +282,12 @@ describe("/rows", () => {
name: "Updated Name", name: "Updated Name",
}) })
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.res.statusMessage).toEqual(`${table.name} updated successfully.`) expect(res.res.statusMessage).toEqual(
`${table.name} updated successfully.`
)
expect(res.body.name).toEqual("Updated Name") expect(res.body.name).toEqual("Updated Name")
expect(res.body.description).toEqual(existing.description) expect(res.body.description).toEqual(existing.description)
@ -292,16 +329,14 @@ describe("/rows", () => {
const res = await request const res = await request
.delete(`/api/${table._id}/rows`) .delete(`/api/${table._id}/rows`)
.send({ .send({
rows: [ rows: [createdRow],
createdRow
]
}) })
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body[0]._id).toEqual(createdRow._id) expect(res.body[0]._id).toEqual(createdRow._id)
await assertRowUsage(rowUsage -1) await assertRowUsage(rowUsage - 1)
await assertQueryUsage(queryUsage +1) await assertQueryUsage(queryUsage + 1)
}) })
}) })
@ -314,9 +349,9 @@ describe("/rows", () => {
.post(`/api/${table._id}/rows/validate`) .post(`/api/${table._id}/rows/validate`)
.send({ name: "ivan" }) .send({ name: "ivan" })
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body.valid).toBe(true) expect(res.body.valid).toBe(true)
expect(Object.keys(res.body.errors)).toEqual([]) expect(Object.keys(res.body.errors)).toEqual([])
await assertRowUsage(rowUsage) await assertRowUsage(rowUsage)
@ -331,9 +366,9 @@ describe("/rows", () => {
.post(`/api/${table._id}/rows/validate`) .post(`/api/${table._id}/rows/validate`)
.send({ name: 1 }) .send({ name: 1 })
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body.valid).toBe(false) expect(res.body.valid).toBe(false)
expect(Object.keys(res.body.errors)).toEqual(["name"]) expect(Object.keys(res.body.errors)).toEqual(["name"])
await assertRowUsage(rowUsage) await assertRowUsage(rowUsage)
@ -351,19 +386,16 @@ describe("/rows", () => {
const res = await request const res = await request
.delete(`/api/${table._id}/rows`) .delete(`/api/${table._id}/rows`)
.send({ .send({
rows: [ rows: [row1, row2],
row1,
row2,
]
}) })
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body.length).toEqual(2) expect(res.body.length).toEqual(2)
await loadRow(row1._id, 404) await loadRow(row1._id, 404)
await assertRowUsage(rowUsage - 2) await assertRowUsage(rowUsage - 2)
await assertQueryUsage(queryUsage +1) await assertQueryUsage(queryUsage + 1)
}) })
}) })
@ -376,12 +408,12 @@ describe("/rows", () => {
const res = await request const res = await request
.get(`/api/views/${table._id}`) .get(`/api/views/${table._id}`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body.length).toEqual(1) expect(res.body.length).toEqual(1)
expect(res.body[0]._id).toEqual(row._id) expect(res.body[0]._id).toEqual(row._id)
await assertRowUsage(rowUsage) await assertRowUsage(rowUsage)
await assertQueryUsage(queryUsage +1) await assertQueryUsage(queryUsage + 1)
}) })
it("should throw an error if view doesn't exist", async () => { it("should throw an error if view doesn't exist", async () => {
@ -406,7 +438,7 @@ describe("/rows", () => {
const res = await request const res = await request
.get(`/api/views/${view.name}`) .get(`/api/views/${view.name}`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body.length).toEqual(1) expect(res.body.length).toEqual(1)
expect(res.body[0]._id).toEqual(row._id) expect(res.body[0]._id).toEqual(row._id)
@ -418,21 +450,24 @@ describe("/rows", () => {
describe("fetchEnrichedRows", () => { describe("fetchEnrichedRows", () => {
it("should allow enriching some linked rows", async () => { it("should allow enriching some linked rows", async () => {
const { table, firstRow, secondRow } = await doInTenant(setup.structures.TENANT_ID, async () => { const { table, firstRow, secondRow } = await doInTenant(
const table = await config.createLinkedTable() setup.structures.TENANT_ID,
const firstRow = await config.createRow({ async () => {
name: "Test Contact", const table = await config.createLinkedTable()
description: "original description", const firstRow = await config.createRow({
tableId: table._id name: "Test Contact",
}) description: "original description",
const secondRow = await config.createRow({ tableId: table._id,
name: "Test 2", })
description: "og desc", const secondRow = await config.createRow({
link: [{_id: firstRow._id}], name: "Test 2",
tableId: table._id, description: "og desc",
}) link: [{ _id: firstRow._id }],
return { table, firstRow, secondRow } tableId: table._id,
}) })
return { table, firstRow, secondRow }
}
)
const rowUsage = await getRowUsage() const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage() const queryUsage = await getQueryUsage()
@ -440,7 +475,7 @@ describe("/rows", () => {
const resBasic = await request const resBasic = await request
.get(`/api/${table._id}/rows/${secondRow._id}`) .get(`/api/${table._id}/rows/${secondRow._id}`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(resBasic.body.link[0]._id).toBe(firstRow._id) expect(resBasic.body.link[0]._id).toBe(firstRow._id)
expect(resBasic.body.link[0].primaryDisplay).toBe("Test Contact") expect(resBasic.body.link[0].primaryDisplay).toBe("Test Contact")
@ -449,14 +484,14 @@ describe("/rows", () => {
const resEnriched = await request const resEnriched = await request
.get(`/api/${table._id}/${secondRow._id}/enrich`) .get(`/api/${table._id}/${secondRow._id}/enrich`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(resEnriched.body.link.length).toBe(1) expect(resEnriched.body.link.length).toBe(1)
expect(resEnriched.body.link[0]._id).toBe(firstRow._id) expect(resEnriched.body.link[0]._id).toBe(firstRow._id)
expect(resEnriched.body.link[0].name).toBe("Test Contact") expect(resEnriched.body.link[0].name).toBe("Test Contact")
expect(resEnriched.body.link[0].description).toBe("original description") expect(resEnriched.body.link[0].description).toBe("original description")
await assertRowUsage(rowUsage) await assertRowUsage(rowUsage)
await assertQueryUsage(queryUsage +2) await assertQueryUsage(queryUsage + 2)
}) })
}) })
@ -466,9 +501,11 @@ describe("/rows", () => {
const row = await config.createRow({ const row = await config.createRow({
name: "test", name: "test",
description: "test", description: "test",
attachment: [{ attachment: [
key: `${config.getAppId()}/attachments/test/thing.csv`, {
}], key: `${config.getAppId()}/attachments/test/thing.csv`,
},
],
tableId: table._id, tableId: table._id,
}) })
// the environment needs configured for this // the environment needs configured for this
@ -482,4 +519,49 @@ describe("/rows", () => {
}) })
}) })
}) })
describe("exportData", () => {
it("should allow exporting all columns", async () => {
const existing = await config.createRow()
const res = await request
.post(`/api/${table._id}/rows/exportRows?format=json`)
.set(config.defaultHeaders())
.send({
rows: [existing._id],
})
.expect("Content-Type", /json/)
.expect(200)
const results = JSON.parse(res.text)
expect(results.length).toEqual(1)
const row = results[0]
// Ensure all original columns were exported
expect(Object.keys(row).length).toBeGreaterThanOrEqual(
Object.keys(existing).length
)
Object.keys(existing).forEach(key => {
expect(row[key]).toEqual(existing[key])
})
})
it("should allow exporting only certain columns", async () => {
const existing = await config.createRow()
const res = await request
.post(`/api/${table._id}/rows/exportRows?format=json`)
.set(config.defaultHeaders())
.send({
rows: [existing._id],
columns: ["_id"],
})
.expect("Content-Type", /json/)
.expect(200)
const results = JSON.parse(res.text)
expect(results.length).toEqual(1)
const row = results[0]
// Ensure only the _id column was exported
expect(Object.keys(row).length).toEqual(1)
expect(row._id).toEqual(existing._id)
})
})
}) })

View File

@ -40,7 +40,6 @@ describe("/static", () => {
}) })
describe("/app", () => { describe("/app", () => {
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks() jest.clearAllMocks()
}) })
@ -60,7 +59,7 @@ describe("/static", () => {
it("should serve the app by url", async () => { it("should serve the app by url", async () => {
const headers = config.defaultHeaders() const headers = config.defaultHeaders()
delete headers[constants.Headers.APP_ID] delete headers[constants.Headers.APP_ID]
const res = await request const res = await request
.get(`/app${config.prodApp.url}`) .get(`/app${config.prodApp.url}`)
.set(headers) .set(headers)
@ -82,7 +81,7 @@ describe("/static", () => {
describe("/attachments", () => { describe("/attachments", () => {
describe("generateSignedUrls", () => { describe("generateSignedUrls", () => {
let datasource let datasource
beforeEach(async () => { beforeEach(async () => {
datasource = await config.createDatasource({ datasource = await config.createDatasource({
datasource: { datasource: {
@ -93,7 +92,7 @@ describe("/static", () => {
}, },
}) })
}) })
it("should be able to generate a signed upload URL", async () => { it("should be able to generate a signed upload URL", async () => {
const bucket = "foo" const bucket = "foo"
const key = "bar" const key = "bar"
@ -108,7 +107,7 @@ describe("/static", () => {
`https://${bucket}.s3.eu-west-1.amazonaws.com/${key}` `https://${bucket}.s3.eu-west-1.amazonaws.com/${key}`
) )
}) })
it("should handle an invalid datasource ID", async () => { it("should handle an invalid datasource ID", async () => {
const res = await request const res = await request
.post(`/api/attachments/foo/url`) .post(`/api/attachments/foo/url`)
@ -123,7 +122,7 @@ describe("/static", () => {
"The specified datasource could not be found" "The specified datasource could not be found"
) )
}) })
it("should require a bucket parameter", async () => { it("should require a bucket parameter", async () => {
const res = await request const res = await request
.post(`/api/attachments/${datasource._id}/url`) .post(`/api/attachments/${datasource._id}/url`)
@ -136,7 +135,7 @@ describe("/static", () => {
.expect(400) .expect(400)
expect(res.body.message).toEqual("bucket and key values are required") expect(res.body.message).toEqual("bucket and key values are required")
}) })
it("should require a key parameter", async () => { it("should require a key parameter", async () => {
const res = await request const res = await request
.post(`/api/attachments/${datasource._id}/url`) .post(`/api/attachments/${datasource._id}/url`)
@ -151,4 +150,17 @@ describe("/static", () => {
}) })
}) })
describe("/preview", () => {
beforeEach(() => {
jest.clearAllMocks()
})
it("should serve the builder preview", async () => {
const headers = config.defaultHeaders()
const res = await request.get(`/preview`).set(headers).expect(200)
expect(res.body.appId).toBe(config.appId)
expect(res.body.builderPreview).toBe(true)
})
})
}) })

View File

@ -1,6 +1,15 @@
// need to load environment first // need to load environment first
import { ExtendableContext } from "koa"
import * as env from "./environment" import * as env from "./environment"
// enable APM if configured
if (process.env.ELASTIC_APM_ENABLED) {
const apm = require("elastic-apm-node").start({
serviceName: process.env.SERVICE,
environment: process.env.BUDIBASE_ENVIRONMENT,
})
}
import { ExtendableContext } from "koa"
import db from "./db" import db from "./db"
db.init() db.init()
const Koa = require("koa") const Koa = require("koa")
@ -74,9 +83,7 @@ server.on("close", async () => {
return return
} }
shuttingDown = true shuttingDown = true
if (!env.isTest()) { console.log("Server Closed")
console.log("Server Closed")
}
await automations.shutdown() await automations.shutdown()
await redis.shutdown() await redis.shutdown()
await events.shutdown() await events.shutdown()
@ -158,3 +165,7 @@ process.on("uncaughtException", err => {
process.on("SIGTERM", () => { process.on("SIGTERM", () => {
shutdown() shutdown()
}) })
process.on("SIGINT", () => {
shutdown()
})

View File

@ -8,12 +8,14 @@ const Queue = env.isTest()
const { JobQueues } = require("../constants") const { JobQueues } = require("../constants")
const { utils } = require("@budibase/backend-core/redis") const { utils } = require("@budibase/backend-core/redis")
const { opts, redisProtocolUrl } = utils.getRedisOptions() const { opts, redisProtocolUrl } = utils.getRedisOptions()
const listeners = require("./listeners")
const CLEANUP_PERIOD_MS = 60 * 1000 const CLEANUP_PERIOD_MS = 60 * 1000
const queueConfig = redisProtocolUrl || { redis: opts } const queueConfig = redisProtocolUrl || { redis: opts }
let cleanupInternal = null let cleanupInternal = null
let automationQueue = new Queue(JobQueues.AUTOMATIONS, queueConfig) let automationQueue = new Queue(JobQueues.AUTOMATIONS, queueConfig)
listeners.addListeners(automationQueue)
async function cleanup() { async function cleanup() {
await automationQueue.clean(CLEANUP_PERIOD_MS, "completed") await automationQueue.clean(CLEANUP_PERIOD_MS, "completed")
@ -51,6 +53,7 @@ exports.shutdown = async () => {
await automationQueue.close() await automationQueue.close()
automationQueue = null automationQueue = null
} }
console.log("Bull shutdown")
} }
exports.queue = automationQueue exports.queue = automationQueue

View File

@ -0,0 +1,78 @@
import { Queue, Job, JobId } from "bull"
import { AutomationEvent } from "../definitions/automations"
import * as automation from "../threads/automation"
export const addListeners = (queue: Queue) => {
logging(queue)
handleStalled(queue)
}
const handleStalled = (queue: Queue) => {
queue.on("stalled", async (job: Job) => {
await automation.removeStalled(job as AutomationEvent)
})
}
const logging = (queue: Queue) => {
if (process.env.NODE_DEBUG?.includes("bull")) {
queue
.on("error", (error: any) => {
// An error occurred.
console.error(`automation-event=error error=${JSON.stringify(error)}`)
})
.on("waiting", (jobId: JobId) => {
// A Job is waiting to be processed as soon as a worker is idling.
console.log(`automation-event=waiting jobId=${jobId}`)
})
.on("active", (job: Job, jobPromise: any) => {
// A job has started. You can use `jobPromise.cancel()`` to abort it.
console.log(`automation-event=active jobId=${job.id}`)
})
.on("stalled", (job: Job) => {
// A job has been marked as stalled. This is useful for debugging job
// workers that crash or pause the event loop.
console.error(
`automation-event=stalled jobId=${job.id} job=${JSON.stringify(job)}`
)
})
.on("progress", (job: Job, progress: any) => {
// A job's progress was updated!
console.log(
`automation-event=progress jobId=${job.id} progress=${progress}`
)
})
.on("completed", (job: Job, result) => {
// A job successfully completed with a `result`.
console.log(
`automation-event=completed jobId=${job.id} result=${result}`
)
})
.on("failed", (job, err: any) => {
// A job failed with reason `err`!
console.log(`automation-event=failed jobId=${job.id} error=${err}`)
})
.on("paused", () => {
// The queue has been paused.
console.log(`automation-event=paused`)
})
.on("resumed", (job: Job) => {
// The queue has been resumed.
console.log(`automation-event=paused jobId=${job.id}`)
})
.on("cleaned", (jobs: Job[], type: string) => {
// Old jobs have been cleaned from the queue. `jobs` is an array of cleaned
// jobs, and `type` is the type of jobs cleaned.
console.log(
`automation-event=cleaned length=${jobs.length} type=${type}`
)
})
.on("drained", () => {
// Emitted every time the queue has processed all the waiting jobs (even if there can be some delayed jobs not yet processed)
console.log(`automation-event=drained`)
})
.on("removed", (job: Job) => {
// A job successfully removed.
console.log(`automation-event=removed jobId=${job.id}`)
})
}
}

View File

@ -125,6 +125,14 @@ const hasNullFilters = filters =>
exports.run = async function ({ inputs, appId }) { exports.run = async function ({ inputs, appId }) {
const { tableId, filters, sortColumn, sortOrder, limit } = inputs const { tableId, filters, sortColumn, sortOrder, limit } = inputs
if (!tableId) {
return {
success: false,
response: {
message: "You must select a table to query.",
},
}
}
const table = await getTable(appId, tableId) const table = await getTable(appId, tableId)
let sortType = FieldTypes.STRING let sortType = FieldTypes.STRING
if (table && table.schema && table.schema[sortColumn] && sortColumn) { if (table && table.schema && table.schema[sortColumn] && sortColumn) {

View File

@ -21,11 +21,13 @@ const WH_STEP_ID = definitions.WEBHOOK.stepId
const CRON_STEP_ID = definitions.CRON.stepId const CRON_STEP_ID = definitions.CRON.stepId
const Runner = new Thread(ThreadType.AUTOMATION) const Runner = new Thread(ThreadType.AUTOMATION)
const jobMessage = (job: any, message: string) => {
return `app=${job.data.event.appId} automation=${job.data.automation._id} jobId=${job.id} trigger=${job.data.automation.definition.trigger.event} : ${message}`
}
export async function processEvent(job: any) { export async function processEvent(job: any) {
try { try {
console.log( console.log(jobMessage(job, "running"))
`${job.data.automation.appId} automation ${job.data.automation._id} running`
)
// need to actually await these so that an error can be captured properly // need to actually await these so that an error can be captured properly
const tenantId = tenancy.getTenantIDFromAppID(job.data.event.appId) const tenantId = tenancy.getTenantIDFromAppID(job.data.event.appId)
return await tenancy.doInTenant(tenantId, async () => { return await tenancy.doInTenant(tenantId, async () => {
@ -34,9 +36,7 @@ export async function processEvent(job: any) {
}) })
} catch (err) { } catch (err) {
const errJson = JSON.stringify(err) const errJson = JSON.stringify(err)
console.error( console.error(jobMessage(job, `was unable to run - ${errJson}`))
`${job.data.automation.appId} automation ${job.data.automation._id} was unable to run - ${errJson}`
)
console.trace(err) console.trace(err)
return { err } return { err }
} }
@ -91,6 +91,7 @@ export async function disableAllCrons(appId: any) {
export async function disableCron(jobId: string, jobKey: string) { export async function disableCron(jobId: string, jobKey: string) {
await queue.removeRepeatableByKey(jobKey) await queue.removeRepeatableByKey(jobKey)
await queue.removeJobs(jobId) await queue.removeJobs(jobId)
console.log(`jobId=${jobId} disabled`)
} }
export async function clearMetadata() { export async function clearMetadata() {

View File

@ -103,11 +103,9 @@ class Table {
exports.init = endpoint => { exports.init = endpoint => {
let AWS = require("aws-sdk") let AWS = require("aws-sdk")
AWS.config.update({
region: AWS_REGION,
})
let docClientParams = { let docClientParams = {
correctClockSkew: true, correctClockSkew: true,
region: AWS_REGION,
} }
if (endpoint) { if (endpoint) {
docClientParams.endpoint = endpoint docClientParams.endpoint = endpoint

View File

@ -13,10 +13,7 @@ function isJest() {
} }
function isDev() { function isDev() {
return ( return process.env.NODE_ENV !== "production"
process.env.NODE_ENV !== "production" &&
process.env.BUDIBASE_ENVIRONMENT !== "production"
)
} }
function isCypress() { function isCypress() {

View File

@ -1,8 +1,10 @@
const { rowEmission, tableEmission } = require("./utils") const { rowEmission, tableEmission } = require("./utils")
const mainEmitter = require("./index") const mainEmitter = require("./index")
const env = require("../environment")
// max number of automations that can chain on top of each other // max number of automations that can chain on top of each other
const MAX_AUTOMATION_CHAIN = 5 // TODO: in future make this configurable at the automation level
const MAX_AUTOMATION_CHAIN = env.SELF_HOSTED ? 5 : 0
/** /**
* Special emitter which takes the count of automation runs which have occurred and blocks an * Special emitter which takes the count of automation runs which have occurred and blocks an

View File

@ -13,7 +13,8 @@ module DynamoModule {
region: string region: string
accessKeyId: string accessKeyId: string
secretAccessKey: string secretAccessKey: string
endpoint: string endpoint?: string
currentClockSkew?: boolean
} }
const SCHEMA: Integration = { const SCHEMA: Integration = {
@ -132,31 +133,20 @@ module DynamoModule {
constructor(config: DynamoDBConfig) { constructor(config: DynamoDBConfig) {
this.config = config this.config = config
if (this.config.endpoint && !this.config.endpoint.includes("localhost")) {
this.connect() // User is using a local dynamoDB endpoint, don't auth with remote
if (this.config?.endpoint?.includes("localhost")) {
// @ts-ignore
this.config = {}
} }
let options = {
correctClockSkew: true, this.config = {
region: this.config.region || AWS_REGION, ...this.config,
endpoint: config.endpoint ? config.endpoint : undefined, currentClockSkew: true,
region: config.region || AWS_REGION,
endpoint: config.endpoint || undefined,
} }
this.client = new AWS.DynamoDB.DocumentClient(options) this.client = new AWS.DynamoDB.DocumentClient(this.config)
}
end() {
this.disconnect()
}
connect() {
AWS.config.update(this.config)
}
disconnect() {
AWS.config.update({
secretAccessKey: undefined,
accessKeyId: undefined,
region: AWS_REGION,
})
} }
async create(query: { table: string; json: object }) { async create(query: { table: string; json: object }) {
@ -197,7 +187,7 @@ module DynamoModule {
const params = { const params = {
TableName: query.table, TableName: query.table,
} }
return new AWS.DynamoDB().describeTable(params).promise() return new AWS.DynamoDB(this.config).describeTable(params).promise()
} }
async get(query: { table: string; json: object }) { async get(query: { table: string; json: object }) {

View File

@ -93,8 +93,8 @@ module MongoDBModule {
json[field] = self.createObjectIds(json[field]) json[field] = self.createObjectIds(json[field])
} }
if ( if (
(field === "_id" || field?.startsWith("$")) && typeof json[field] === "string" &&
typeof json[field] === "string" json[field].toLowerCase().startsWith("objectid")
) { ) {
const id = json[field].match( const id = json[field].match(
/(?<=objectid\(['"]).*(?=['"]\))/gi /(?<=objectid\(['"]).*(?=['"]\))/gi

View File

@ -100,4 +100,52 @@ describe("DynamoDB Integration", () => {
Name: "John" Name: "John"
}) })
}) })
it("configures the dynamoDB constructor based on an empty endpoint parameter", async () => {
const config = {
region: "us-east-1",
accessKeyId: "test",
secretAccessKeyId: "test"
}
const integration = new DynamoDBIntegration.integration(config)
expect(integration.config).toEqual({
currentClockSkew: true,
...config
})
})
it("configures the dynamoDB constructor based on a localhost endpoint parameter", async () => {
const config = {
region: "us-east-1",
accessKeyId: "test",
secretAccessKeyId: "test",
endpoint: "localhost:8080"
}
const integration = new DynamoDBIntegration.integration(config)
expect(integration.config).toEqual({
region: "us-east-1",
currentClockSkew: true,
endpoint: "localhost:8080"
})
})
it("configures the dynamoDB constructor based on a remote endpoint parameter", async () => {
const config = {
region: "us-east-1",
accessKeyId: "test",
secretAccessKeyId: "test",
endpoint: "dynamodb.aws.foo.net"
}
const integration = new DynamoDBIntegration.integration(config)
expect(integration.config).toEqual({
currentClockSkew: true,
...config
})
})
}) })

View File

@ -103,16 +103,16 @@ describe("MongoDB Integration", () => {
restore() restore()
}) })
it("creates ObjectIds if the _id fields contains a match on ObjectId", async () => { it("creates ObjectIds if the field contains a match on ObjectId", async () => {
const query = { const query = {
json: { json: {
filter: { filter: {
_id: "ObjectId('ACBD12345678ABCD12345678')", _id: "ObjectId('ACBD12345678ABCD12345678')",
name: "ObjectId('name')" name: "ObjectId('BBBB12345678ABCD12345678')"
}, },
update: { update: {
_id: "ObjectId('FFFF12345678ABCD12345678')", _id: "ObjectId('FFFF12345678ABCD12345678')",
name: "ObjectId('updatedName')", name: "ObjectId('CCCC12345678ABCD12345678')",
}, },
options: { options: {
upsert: false, upsert: false,
@ -126,11 +126,11 @@ describe("MongoDB Integration", () => {
const args = config.integration.client.updateOne.mock.calls[0] const args = config.integration.client.updateOne.mock.calls[0]
expect(args[0]).toEqual({ expect(args[0]).toEqual({
_id: mongo.ObjectID.createFromHexString("ACBD12345678ABCD12345678"), _id: mongo.ObjectID.createFromHexString("ACBD12345678ABCD12345678"),
name: "ObjectId('name')", name: mongo.ObjectID.createFromHexString("BBBB12345678ABCD12345678"),
}) })
expect(args[1]).toEqual({ expect(args[1]).toEqual({
_id: mongo.ObjectID.createFromHexString("FFFF12345678ABCD12345678"), _id: mongo.ObjectID.createFromHexString("FFFF12345678ABCD12345678"),
name: "ObjectId('updatedName')", name: mongo.ObjectID.createFromHexString("CCCC12345678ABCD12345678"),
}) })
expect(args[2]).toEqual({ expect(args[2]).toEqual({
upsert: false upsert: false

View File

@ -13,10 +13,13 @@ function validate(schema, property) {
params = ctx.request[property] params = ctx.request[property]
} }
schema = schema.append({ // not all schemas have the append property e.g. array schemas
createdAt: Joi.any().optional(), if (schema.append) {
updatedAt: Joi.any().optional(), schema = schema.append({
}) createdAt: Joi.any().optional(),
updatedAt: Joi.any().optional(),
})
}
const { error } = schema.validate(params) const { error } = schema.validate(params)
if (error) { if (error) {

View File

@ -133,27 +133,34 @@ class Orchestrator {
return metadata return metadata
} }
async stopCron(reason: string) {
if (!this._repeat) {
return
}
logWarn(
`CRON disabled reason=${reason} - ${this._appId}/${this._automation._id}`
)
const automation = this._automation
const trigger = automation.definition.trigger
await disableCron(this._repeat?.jobId, this._repeat?.jobKey)
this.updateExecutionOutput(
trigger.id,
trigger.stepId,
{},
{
status: AutomationStatus.STOPPED_ERROR,
success: false,
}
)
await storeLog(automation, this.executionOutput)
}
async checkIfShouldStop(metadata: AutomationMetadata): Promise<boolean> { async checkIfShouldStop(metadata: AutomationMetadata): Promise<boolean> {
if (!metadata.errorCount || !this._repeat) { if (!metadata.errorCount || !this._repeat) {
return false return false
} }
const automation = this._automation
const trigger = automation.definition.trigger
if (metadata.errorCount >= MAX_AUTOMATION_RECURRING_ERRORS) { if (metadata.errorCount >= MAX_AUTOMATION_RECURRING_ERRORS) {
logWarn( await this.stopCron("errors")
`CRON disabled due to errors - ${this._appId}/${this._automation._id}`
)
await disableCron(this._repeat?.jobId, this._repeat?.jobKey)
this.updateExecutionOutput(
trigger.id,
trigger.stepId,
{},
{
status: AutomationStatus.STOPPED_ERROR,
success: false,
}
)
await storeLog(automation, this.executionOutput)
return true return true
} }
return false return false
@ -465,3 +472,15 @@ export function execute(input: AutomationEvent, callback: WorkerCallback) {
} }
}) })
} }
export const removeStalled = async (input: AutomationEvent) => {
const appId = input.data.event.appId
await doInAppContext(appId, async () => {
const automationOrchestrator = new Orchestrator(
input.data.automation,
input.data.event,
input.opts
)
await automationOrchestrator.stopCron("stalled")
})
}

View File

@ -106,5 +106,6 @@ export class Thread {
static async shutdown() { static async shutdown() {
await Thread.stopThreads() await Thread.stopThreads()
console.log("Threads shutdown")
} }
} }

View File

@ -287,13 +287,18 @@ exports.getComponentLibraryManifest = async library => {
} }
let resp let resp
let path
try { try {
// Try to load the manifest from the new file location // Try to load the manifest from the new file location
const path = join(appId, filename) path = join(appId, filename)
resp = await retrieve(ObjectStoreBuckets.APPS, path) resp = await retrieve(ObjectStoreBuckets.APPS, path)
} catch (error) { } catch (error) {
console.error(
`component-manifest-objectstore=failed appId=${appId} path=${path}`,
error
)
// Fallback to loading it from the old location for old apps // Fallback to loading it from the old location for old apps
const path = join(appId, "node_modules", library, "package", filename) path = join(appId, "node_modules", library, "package", filename)
resp = await retrieve(ObjectStoreBuckets.APPS, path) resp = await retrieve(ObjectStoreBuckets.APPS, path)
} }
if (typeof resp !== "string") { if (typeof resp !== "string") {

View File

@ -113,6 +113,10 @@ class InMemoryQueue {
async getJob() { async getJob() {
return {} return {}
} }
on() {
// do nothing
}
} }
module.exports = InMemoryQueue module.exports = InMemoryQueue

View File

@ -20,6 +20,7 @@ exports.shutdown = async () => {
if (devAppClient) await devAppClient.finish() if (devAppClient) await devAppClient.finish()
if (debounceClient) await debounceClient.finish() if (debounceClient) await debounceClient.finish()
if (flagClient) await flagClient.finish() if (flagClient) await flagClient.finish()
console.log("Redis shutdown")
} }
exports.doesUserHaveLock = async (devAppId, user) => { exports.doesUserHaveLock = async (devAppId, user) => {

View File

@ -1094,12 +1094,12 @@
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/backend-core@1.2.41-alpha.5": "@budibase/backend-core@1.2.58-alpha.5":
version "1.2.41-alpha.5" version "1.2.58-alpha.5"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.2.41-alpha.5.tgz#e03c9970f9eadcdfe9146ffe4d7f29174648675a" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.2.58-alpha.5.tgz#430699334629b3bd05a5066aa6660c1f68f047b2"
integrity sha512-kB+S3pu4F9wNMwzdBSF4auZEAqQ05VlhEA/cjLyb54zjxrJmpGv761I6vAM4BptvjpSNCGtiv6HrpGaOILRDDQ== integrity sha512-Z926rAp0eskXUr5UXGwh0S1OMDA3qzIi8o0GHLlO7KZAzk49RYzhRT/yo1fIst8Eq/9ZCaXLFxIeRkDmCJgILg==
dependencies: dependencies:
"@budibase/types" "1.2.41-alpha.5" "@budibase/types" "1.2.58-alpha.5"
"@techpass/passport-openidconnect" "0.3.2" "@techpass/passport-openidconnect" "0.3.2"
aws-sdk "2.1030.0" aws-sdk "2.1030.0"
bcrypt "5.0.1" bcrypt "5.0.1"
@ -1178,13 +1178,13 @@
svelte-flatpickr "^3.2.3" svelte-flatpickr "^3.2.3"
svelte-portal "^1.0.0" svelte-portal "^1.0.0"
"@budibase/pro@1.2.41-alpha.5": "@budibase/pro@1.2.58-alpha.5":
version "1.2.41-alpha.5" version "1.2.58-alpha.5"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.2.41-alpha.5.tgz#43362d0e9bca4dbb8ab93cfe3daf821b04f1ee95" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.2.58-alpha.5.tgz#1f739653e641ac32d20818517d35bef489ec2ae6"
integrity sha512-p/Vvs2xOWlQlLYJ8sF/PWI2ki/B0uy0sQPwUUld4qQRHXa+YevXJgnjZPa3v7q2NWQ1vxov2K8g8y6Z5mv9XAw== integrity sha512-owT7ipgZ588KAz6aHPJie5bLJX6EkhTRUvUQlyOJuLWt/hdXXnvbu+agxBQNaXE4gVzn916JJP0L0tbiMctjNA==
dependencies: dependencies:
"@budibase/backend-core" "1.2.41-alpha.5" "@budibase/backend-core" "1.2.58-alpha.5"
"@budibase/types" "1.2.41-alpha.5" "@budibase/types" "1.2.58-alpha.5"
"@koa/router" "8.0.8" "@koa/router" "8.0.8"
joi "17.6.0" joi "17.6.0"
node-fetch "^2.6.1" node-fetch "^2.6.1"
@ -1207,10 +1207,10 @@
svelte-apexcharts "^1.0.2" svelte-apexcharts "^1.0.2"
svelte-flatpickr "^3.1.0" svelte-flatpickr "^3.1.0"
"@budibase/types@1.2.41-alpha.5": "@budibase/types@1.2.58-alpha.5":
version "1.2.41-alpha.5" version "1.2.58-alpha.5"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.2.41-alpha.5.tgz#0ade282c2e4ec3e4a1a4212403a1d0c3da80521b" resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.2.58-alpha.5.tgz#8ea99745f296a1076f878a06b7c228bc60886cd3"
integrity sha512-Awya83t8bDeavc0XSglYVc4sStDheUKsAQWzTpgcRaB5l7NwhHQG5xlWsbFT8glolYY96ogLZJjLFTzIur3iDQ== integrity sha512-0JhHDGvfcMcjhBckP3RqbKCDsMhx7ZVTV8wvaqsOaG7PnBSst9dxyX7ETVVzfvYolIK1eV11itsgx0FpbYID2w==
"@bull-board/api@3.7.0": "@bull-board/api@3.7.0":
version "3.7.0" version "3.7.0"
@ -1304,6 +1304,20 @@
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
"@elastic/ecs-helpers@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@elastic/ecs-helpers/-/ecs-helpers-1.1.0.tgz#ee7e6f870f75a2222c5d7179b36a628f1db4779e"
integrity sha512-MDLb2aFeGjg46O5mLpdCzT5yOUDnXToJSrco2ShqGIXxNJaM8uJjX+4nd+hRYV4Vex8YJyDtOFEVBldQct6ndg==
dependencies:
fast-json-stringify "^2.4.1"
"@elastic/ecs-pino-format@^1.2.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@elastic/ecs-pino-format/-/ecs-pino-format-1.3.0.tgz#6e349a7da342b3c370d15361ba7f850bc2f783bc"
integrity sha512-U8D57gPECYoRCcwREsrXKBtqeyFFF/KAwHi4rG1u/oQhAg91Kzw8ZtUQJXD/DMDieLOqtbItFr2FRBWI3t3wog==
dependencies:
"@elastic/ecs-helpers" "^1.1.0"
"@elastic/elasticsearch@7.10.0": "@elastic/elasticsearch@7.10.0":
version "7.10.0" version "7.10.0"
resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.10.0.tgz#da105a9c1f14146f9f2cab4e7026cb7949121b8d" resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.10.0.tgz#da105a9c1f14146f9f2cab4e7026cb7949121b8d"
@ -2026,6 +2040,36 @@
semver "^7.3.5" semver "^7.3.5"
tar "^6.1.11" tar "^6.1.11"
"@msgpackr-extract/msgpackr-extract-darwin-arm64@2.1.2":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.1.2.tgz#9571b87be3a3f2c46de05585470bc4f3af2f6f00"
integrity sha512-TyVLn3S/+ikMDsh0gbKv2YydKClN8HaJDDpONlaZR+LVJmsxLFUgA+O7zu59h9+f9gX1aj/ahw9wqa6rosmrYQ==
"@msgpackr-extract/msgpackr-extract-darwin-x64@2.1.2":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.1.2.tgz#bfbc6936ede2955218f5621a675679a5fe8e6f4c"
integrity sha512-YPXtcVkhmVNoMGlqp81ZHW4dMxK09msWgnxtsDpSiZwTzUBG2N+No2bsr7WMtBKCVJMSD6mbAl7YhKUqkp/Few==
"@msgpackr-extract/msgpackr-extract-linux-arm64@2.1.2":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-2.1.2.tgz#22555e28382af2922e7450634c8a2f240bb9eb82"
integrity sha512-vHZ2JiOWF2+DN9lzltGbhtQNzDo8fKFGrf37UJrgqxU0yvtERrzUugnfnX1wmVfFhSsF8OxrfqiNOUc5hko1Zg==
"@msgpackr-extract/msgpackr-extract-linux-arm@2.1.2":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-2.1.2.tgz#ffb6ae1beea7ac572b6be6bf2a8e8162ebdd8be7"
integrity sha512-42R4MAFeIeNn+L98qwxAt360bwzX2Kf0ZQkBBucJ2Ircza3asoY4CDbgiu9VWklq8gWJVSJSJBwDI+c/THiWkA==
"@msgpackr-extract/msgpackr-extract-linux-x64@2.1.2":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-2.1.2.tgz#7caf62eebbfb1345de40f75e89666b3d4194755f"
integrity sha512-RjRoRxg7Q3kPAdUSC5EUUPlwfMkIVhmaRTIe+cqHbKrGZ4M6TyCA/b5qMaukQ/1CHWrqYY2FbKOAU8Hg0pQFzg==
"@msgpackr-extract/msgpackr-extract-win32-x64@2.1.2":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-2.1.2.tgz#f2d8b9ddd8d191205ed26ce54aba3dfc5ae3e7c9"
integrity sha512-rIZVR48zA8hGkHIK7ED6+ZiXsjRCcAVBJbm8o89OKAMTmEAQ2QvoOxoiu3w2isAaWwzgtQIOFIqHwvZDyLKCvw==
"@nodelib/fs.scandir@2.1.5": "@nodelib/fs.scandir@2.1.5":
version "2.1.5" version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@ -2047,7 +2091,7 @@
"@nodelib/fs.scandir" "2.1.5" "@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0" fastq "^1.6.0"
"@opentelemetry/api@^1.0.1": "@opentelemetry/api@^1.0.1", "@opentelemetry/api@^1.1.0":
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.1.0.tgz#563539048255bbe1a5f4f586a4a10a1bb737f44a" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.1.0.tgz#563539048255bbe1a5f4f586a4a10a1bb737f44a"
integrity sha512-hf+3bwuBwtXsugA2ULBc95qxrOqP2pOekLz34BJhcAKawt94vfeNyUKpYc0lZQ/3sCP6LqRa7UAdHA7i5UODzQ== integrity sha512-hf+3bwuBwtXsugA2ULBc95qxrOqP2pOekLz34BJhcAKawt94vfeNyUKpYc0lZQ/3sCP6LqRa7UAdHA7i5UODzQ==
@ -3327,6 +3371,11 @@ adal-node@^0.2.2:
uuid "^3.1.0" uuid "^3.1.0"
xpath.js "~1.1.0" xpath.js "~1.1.0"
after-all-results@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/after-all-results/-/after-all-results-2.0.0.tgz#6ac2fc202b500f88da8f4f5530cfa100f4c6a2d0"
integrity sha512-2zHEyuhSJOuCrmas9YV0YL/MFCWLxe1dS6k/ENhgYrb/JqyMnadLN4iIAc9kkZrbElMDyyAGH/0J18OPErOWLg==
agent-base@6, agent-base@^6.0.2: agent-base@6, agent-base@^6.0.2:
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"
@ -3334,6 +3383,15 @@ agent-base@6, agent-base@^6.0.2:
dependencies: dependencies:
debug "4" debug "4"
agentkeepalive@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717"
integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==
dependencies:
debug "^4.1.0"
depd "^1.1.2"
humanize-ms "^1.2.1"
airtable@0.10.1: airtable@0.10.1:
version "0.10.1" version "0.10.1"
resolved "https://registry.yarnpkg.com/airtable/-/airtable-0.10.1.tgz#0b311002bb44b39f19bf7c4bd2d47d75c733bf87" resolved "https://registry.yarnpkg.com/airtable/-/airtable-0.10.1.tgz#0b311002bb44b39f19bf7c4bd2d47d75c733bf87"
@ -3357,7 +3415,7 @@ ajv-keywords@^3.5.2:
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.5: ajv@^6.10.0, ajv@^6.10.2, ajv@^6.11.0, ajv@^6.12.3, ajv@^6.12.5:
version "6.12.6" version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@ -3666,11 +3724,30 @@ astral-regex@^1.0.0:
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
async-cache@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/async-cache/-/async-cache-1.1.0.tgz#4a9a5a89d065ec5d8e5254bd9ee96ba76c532b5a"
integrity sha512-YDQc4vBn5NFhY6g6HhVshyi3Fy9+SQ5ePnE7JLDJn1DoL+i7ER+vMwtTNOYk9leZkYMnOwpBCWqyLDPw8Aig8g==
dependencies:
lru-cache "^4.0.0"
async-limiter@~1.0.0: async-limiter@~1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
async-value-promise@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/async-value-promise/-/async-value-promise-1.1.1.tgz#68957819e3eace804f3b4b69477e2bd276c15378"
integrity sha512-c2RFDKjJle1rHa0YxN9Ysu97/QBu3Wa+NOejJxsX+1qVDJrkD3JL/GN1B3gaILAEXJXbu/4Z1lcoCHFESe/APA==
dependencies:
async-value "^1.2.2"
async-value@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/async-value/-/async-value-1.2.2.tgz#84517a1e7cb6b1a5b5e181fa31be10437b7fb125"
integrity sha512-8rwtYe32OAS1W9CTwvknoyts+mc3ta8N7Pi0h7AjkMaKvsFbr39K+gEfZ7Z81aPXQ1sK5M23lgLy1QfZpcpadQ==
async@^2.6.3: async@^2.6.3:
version "2.6.4" version "2.6.4"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
@ -3961,6 +4038,13 @@ base@^0.11.1:
mixin-deep "^1.2.0" mixin-deep "^1.2.0"
pascalcase "^0.1.1" pascalcase "^0.1.1"
basic-auth@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==
dependencies:
safe-buffer "5.1.2"
bcrypt-pbkdf@^1.0.0: bcrypt-pbkdf@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
@ -4006,6 +4090,11 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
binary-search@^1.3.3:
version "1.3.6"
resolved "https://registry.yarnpkg.com/binary-search/-/binary-search-1.3.6.tgz#e32426016a0c5092f0f3598836a1c7da3560565c"
integrity sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==
binascii@0.0.2: binascii@0.0.2:
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/binascii/-/binascii-0.0.2.tgz#a7f8a8801dbccf8b1756b743daa0fee9e2d9e0ee" resolved "https://registry.yarnpkg.com/binascii/-/binascii-0.0.2.tgz#a7f8a8801dbccf8b1756b743daa0fee9e2d9e0ee"
@ -4127,6 +4216,13 @@ braces@^3.0.2, braces@~3.0.2:
dependencies: dependencies:
fill-range "^7.0.1" fill-range "^7.0.1"
breadth-filter@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/breadth-filter/-/breadth-filter-2.0.0.tgz#7b3f8737f46ba1946aec19355ecf5df2bdb7e47c"
integrity sha512-thQShDXnFWSk2oVBixRCyrWsFoV5tfOpWKHmxwafHQDNxCfDBk539utpvytNjmlFrTMqz41poLwJvA1MW3z0MQ==
dependencies:
object.entries "^1.0.4"
browser-process-hrtime@^1.0.0: browser-process-hrtime@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
@ -4246,20 +4342,19 @@ buffer@^5.1.0, buffer@^5.2.0, buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0:
base64-js "^1.3.1" base64-js "^1.3.1"
ieee754 "^1.1.13" ieee754 "^1.1.13"
bull@3.29.3: bull@4.8.5:
version "3.29.3" version "4.8.5"
resolved "https://registry.yarnpkg.com/bull/-/bull-3.29.3.tgz#5b0059b172685b0d6f011d56214e1898ff3a7a0b" resolved "https://registry.yarnpkg.com/bull/-/bull-4.8.5.tgz#eebafddc3249d6d5e8ced1c42b8bfa8efcc274aa"
integrity sha512-MOqV1dKLy1YQgP9m3lFolyMxaU+1+o4afzYYf0H4wNM+x/S0I1QPQfkgGlLiH00EyFrvSmeubeCYFP47rTfpjg== integrity sha512-2Z630e4f6VsLJnWMAtfEHwIqJYmND4W3dcG48RIbXeWpvb4UnYtpe/zxEdslJu0PKrltB4IkFj5YtBsdeQRn8w==
dependencies: dependencies:
cron-parser "^2.13.0" cron-parser "^4.2.1"
debuglog "^1.0.0" debuglog "^1.0.0"
get-port "^5.1.1" get-port "^5.1.1"
ioredis "^4.27.0" ioredis "^4.28.5"
lodash "^4.17.21" lodash "^4.17.21"
msgpackr "^1.5.2"
p-timeout "^3.2.0" p-timeout "^3.2.0"
promise.prototype.finally "^3.1.2"
semver "^7.3.2" semver "^7.3.2"
util.promisify "^1.0.1"
uuid "^8.3.0" uuid "^8.3.0"
bytes@3.1.2, bytes@^3.0.0: bytes@3.1.2, bytes@^3.0.0:
@ -4726,6 +4821,11 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0:
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
console-log-level@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/console-log-level/-/console-log-level-1.4.1.tgz#9c5a6bb9ef1ef65b05aba83028b0ff894cdf630a"
integrity sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ==
consolidate@^0.16.0: consolidate@^0.16.0:
version "0.16.0" version "0.16.0"
resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.16.0.tgz#a11864768930f2f19431660a65906668f5fbdc16" resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.16.0.tgz#a11864768930f2f19431660a65906668f5fbdc16"
@ -4733,6 +4833,11 @@ consolidate@^0.16.0:
dependencies: dependencies:
bluebird "^3.7.2" bluebird "^3.7.2"
container-info@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/container-info/-/container-info-1.1.0.tgz#6fcb94e93eacd397c6316ca2834491ede44e55ee"
integrity sha512-eD2zLAmxGS2kmL4f1jY8BdOqnmpL6X70kvzTBW/9FIQnxoxiBJ4htMsTmtPLPWRs7NHYFvqKQ1VtppV08mdsQA==
content-disposition@^0.5.2, content-disposition@~0.5.2: content-disposition@^0.5.2, content-disposition@~0.5.2:
version "0.5.4" version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
@ -4757,6 +4862,11 @@ cookie@^0.4.1:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
cookie@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
cookiejar@^2.1.0: cookiejar@^2.1.0:
version "2.1.3" version "2.1.3"
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc" resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc"
@ -4826,13 +4936,12 @@ create-require@^1.1.0:
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cron-parser@^2.13.0: cron-parser@^4.2.1:
version "2.18.0" version "4.6.0"
resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.18.0.tgz#de1bb0ad528c815548371993f81a54e5a089edcf" resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-4.6.0.tgz#404c3fdbff10ae80eef6b709555d577ef2fd2e0d"
integrity sha512-s4odpheTyydAbTBQepsqd2rNWGa2iV3cyo8g7zbI2QQYGLVsfbhmwukayS1XHppe02Oy1fg7mg6xoaraVJeEcg== integrity sha512-guZNLMGUgg6z4+eGhmHGw7ft+v6OQeuHzd1gcLxCo9Yg/qoxmG3nindp2/uwGCLizEisf2H0ptqeVXeoCpP6FA==
dependencies: dependencies:
is-nan "^1.3.0" luxon "^3.0.1"
moment-timezone "^0.5.31"
cross-spawn@^4.0.0: cross-spawn@^4.0.0:
version "4.0.2" version "4.0.2"
@ -5380,6 +5489,60 @@ ejs@^3.1.6:
dependencies: dependencies:
jake "^10.8.5" jake "^10.8.5"
elastic-apm-http-client@11.0.1:
version "11.0.1"
resolved "https://registry.yarnpkg.com/elastic-apm-http-client/-/elastic-apm-http-client-11.0.1.tgz#15dbe99d56d62b3f732d1bd2b51bef6094b78801"
integrity sha512-5AOWlhs2WlZpI+DfgGqY/8Rk7KF8WeevaO8R961eBylavU6GWhLRNiJncohn5jsvrqhmeT19azBvy/oYRN7bJw==
dependencies:
agentkeepalive "^4.2.1"
breadth-filter "^2.0.0"
container-info "^1.0.1"
end-of-stream "^1.4.4"
fast-safe-stringify "^2.0.7"
fast-stream-to-buffer "^1.0.0"
object-filter-sequence "^1.0.0"
readable-stream "^3.4.0"
semver "^6.3.0"
stream-chopper "^3.0.1"
elastic-apm-node@3.38.0:
version "3.38.0"
resolved "https://registry.yarnpkg.com/elastic-apm-node/-/elastic-apm-node-3.38.0.tgz#4d0dc9279c0e23e09b3b30aa4a9f9aeccfa59cc0"
integrity sha512-/d6YuWFtsfkVRpFD0YJ2rYJVq0rI0PGqG/C+cW1JpMZ4IOU8dA9xzUkxbT0G3B8gpHNug07Xo6bJdQa2oUaFbQ==
dependencies:
"@elastic/ecs-pino-format" "^1.2.0"
"@opentelemetry/api" "^1.1.0"
after-all-results "^2.0.0"
async-cache "^1.1.0"
async-value-promise "^1.1.1"
basic-auth "^2.0.1"
cookie "^0.5.0"
core-util-is "^1.0.2"
elastic-apm-http-client "11.0.1"
end-of-stream "^1.4.4"
error-callsites "^2.0.4"
error-stack-parser "^2.0.6"
escape-string-regexp "^4.0.0"
fast-safe-stringify "^2.0.7"
http-headers "^3.0.2"
is-native "^1.0.1"
lru-cache "^6.0.0"
measured-reporting "^1.51.1"
monitor-event-loop-delay "^1.0.0"
object-filter-sequence "^1.0.0"
object-identity-map "^1.0.2"
original-url "^1.2.3"
pino "^6.11.2"
relative-microtime "^2.0.0"
require-in-the-middle "^5.0.3"
semver "^6.3.0"
set-cookie-serde "^1.0.0"
shallow-clone-shim "^2.0.0"
source-map "^0.8.0-beta.0"
sql-summary "^1.0.1"
traverse "^0.6.6"
unicode-byte-truncate "^1.0.0"
electron-to-chromium@^1.4.147: electron-to-chromium@^1.4.147:
version "1.4.150" version "1.4.150"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.150.tgz#89f0e12505462d5df7e56c5b91aff7e1dfdd33ec" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.150.tgz#89f0e12505462d5df7e56c5b91aff7e1dfdd33ec"
@ -5432,7 +5595,7 @@ encoding-down@^6.3.0:
level-codec "^9.0.0" level-codec "^9.0.0"
level-errors "^2.0.0" level-errors "^2.0.0"
end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1, end-of-stream@^1.4.4:
version "1.4.4" version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
@ -5471,6 +5634,11 @@ errno@~0.1.1, errno@~0.1.7:
dependencies: dependencies:
prr "~1.0.1" prr "~1.0.1"
error-callsites@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/error-callsites/-/error-callsites-2.0.4.tgz#44f09e6a201e9a1603ead81eacac5ba258fca76e"
integrity sha512-V877Ch4FC4FN178fDK1fsrHN4I1YQIBdtjKrHh3BUHMnh3SMvwUVrqkaOgDpUuevgSNna0RBq6Ox9SGlxYrigA==
error-ex@^1.3.1: error-ex@^1.3.1:
version "1.3.2" version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
@ -5483,6 +5651,13 @@ error-inject@^1.0.0:
resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37"
integrity sha512-JM8N6PytDbmIYm1IhPWlo8vr3NtfjhDY/1MhD/a5b/aad/USE8a0+NsqE9d5n+GVGmuNkPQWm4bFQWv18d8tMg== integrity sha512-JM8N6PytDbmIYm1IhPWlo8vr3NtfjhDY/1MhD/a5b/aad/USE8a0+NsqE9d5n+GVGmuNkPQWm4bFQWv18d8tMg==
error-stack-parser@^2.0.6:
version "2.1.4"
resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286"
integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==
dependencies:
stackframe "^1.3.4"
es-abstract@^1.17.5, es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.0, es-abstract@^1.20.1: es-abstract@^1.17.5, es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.0, es-abstract@^1.20.1:
version "1.20.1" version "1.20.1"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814"
@ -6109,6 +6284,16 @@ fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0:
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
fast-json-stringify@^2.4.1:
version "2.7.13"
resolved "https://registry.yarnpkg.com/fast-json-stringify/-/fast-json-stringify-2.7.13.tgz#277aa86c2acba4d9851bd6108ed657aa327ed8c0"
integrity sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA==
dependencies:
ajv "^6.11.0"
deepmerge "^4.2.2"
rfdc "^1.2.0"
string-similarity "^4.0.1"
fast-levenshtein@~2.0.6: fast-levenshtein@~2.0.6:
version "2.0.6" version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
@ -6124,6 +6309,13 @@ fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.0.8:
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884"
integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==
fast-stream-to-buffer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fast-stream-to-buffer/-/fast-stream-to-buffer-1.0.0.tgz#793340cc753e7ec9c7fb6d57a53a0b911cb0f588"
integrity sha512-bI/544WUQlD2iXBibQbOMSmG07Hay7YrpXlKaeGTPT7H7pC0eitt3usak5vUwEvCGK/O7rUAM3iyQValGU22TQ==
dependencies:
end-of-stream "^1.4.1"
fast-text-encoding@^1.0.0, fast-text-encoding@^1.0.3: fast-text-encoding@^1.0.0, fast-text-encoding@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53"
@ -6411,6 +6603,11 @@ formidable@^1.1.1, formidable@^1.2.0:
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.6.tgz#d2a51d60162bbc9b4a055d8457a7c75315d1a168" resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.6.tgz#d2a51d60162bbc9b4a055d8457a7c75315d1a168"
integrity sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ== integrity sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==
forwarded-parse@^2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/forwarded-parse/-/forwarded-parse-2.1.2.tgz#08511eddaaa2ddfd56ba11138eee7df117a09325"
integrity sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==
fragment-cache@^0.2.1: fragment-cache@^0.2.1:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
@ -7156,6 +7353,13 @@ http-errors@~1.6.2:
setprototypeof "1.1.0" setprototypeof "1.1.0"
statuses ">= 1.4.0 < 2" statuses ">= 1.4.0 < 2"
http-headers@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/http-headers/-/http-headers-3.0.2.tgz#5147771292f0b39d6778d930a3a59a76fc7ef44d"
integrity sha512-87E1I+2Wg4dxxz4rcxElo3dxO/w1ZtgL1yA0Sb6vH3qU16vRKq1NjWQv9SCY3ly2OQROcoxHZOUpmelS+k6wOw==
dependencies:
next-line "^1.1.0"
http-proxy-agent@^4.0.1: http-proxy-agent@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a"
@ -7187,6 +7391,13 @@ human-signals@^2.1.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
humanize-ms@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==
dependencies:
ms "^2.0.0"
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.5: iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.5:
version "0.4.24" version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@ -7397,7 +7608,7 @@ ioredis@4.28.0:
redis-parser "^3.0.0" redis-parser "^3.0.0"
standard-as-callback "^2.1.0" standard-as-callback "^2.1.0"
ioredis@^4.27.0: ioredis@^4.28.5:
version "4.28.5" version "4.28.5"
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.28.5.tgz#5c149e6a8d76a7f8fa8a504ffc85b7d5b6797f9f" resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.28.5.tgz#5c149e6a8d76a7f8fa8a504ffc85b7d5b6797f9f"
integrity sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A== integrity sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==
@ -7502,6 +7713,13 @@ is-core-module@^2.8.1:
dependencies: dependencies:
has "^1.0.3" has "^1.0.3"
is-core-module@^2.9.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed"
integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==
dependencies:
has "^1.0.3"
is-data-descriptor@^0.1.4: is-data-descriptor@^0.1.4:
version "0.1.4" version "0.1.4"
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
@ -7563,6 +7781,11 @@ is-extglob@^2.1.1:
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-finite@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3"
integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==
is-fullwidth-code-point@^2.0.0: is-fullwidth-code-point@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
@ -7605,13 +7828,20 @@ is-installed-globally@^0.4.0:
global-dirs "^3.0.0" global-dirs "^3.0.0"
is-path-inside "^3.0.2" is-path-inside "^3.0.2"
is-nan@^1.3.0: is-integer@^1.0.6:
version "1.3.2" version "1.0.7"
resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" resolved "https://registry.yarnpkg.com/is-integer/-/is-integer-1.0.7.tgz#6bde81aacddf78b659b6629d629cadc51a886d5c"
integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== integrity sha512-RPQc/s9yBHSvpi+hs9dYiJ2cuFeU6x3TyyIp8O2H6SKEltIvJOzRj9ToyvcStDvPR/pS4rxgr1oBFajQjZ2Szg==
dependencies: dependencies:
call-bind "^1.0.0" is-finite "^1.0.0"
define-properties "^1.1.3"
is-native@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-native/-/is-native-1.0.1.tgz#cd18cc162e8450d683b5babe79ac99c145449675"
integrity sha512-I4z9hx+4u3/zyvpvGtAR+n7SodJugE+i2jiS8yfq1A9QAZY0KldLQz0SBptLC9ti7kBlpghWUwTKE2BA62eCcw==
dependencies:
is-nil "^1.0.0"
to-source-code "^1.0.0"
is-natural-number@^4.0.1: is-natural-number@^4.0.1:
version "4.0.1" version "4.0.1"
@ -7623,6 +7853,11 @@ is-negative-zero@^2.0.2:
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==
is-nil@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-nil/-/is-nil-1.0.1.tgz#2daba29e0b585063875e7b539d071f5b15937969"
integrity sha512-m2Rm8PhUFDNNhgvwZJjJG74a9h5CHU0fkA8WT+WGlCjyEbZ2jPwgb+ZxHu4np284EqNVyOsgppReK4qy/TwEwg==
is-npm@^5.0.0: is-npm@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8"
@ -9628,7 +9863,7 @@ lowercase-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
lru-cache@^4.0.1, lru-cache@^4.1.3, lru-cache@^4.1.5: lru-cache@^4.0.0, lru-cache@^4.0.1, lru-cache@^4.1.3, lru-cache@^4.1.5:
version "4.1.5" version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
@ -9653,6 +9888,11 @@ ltgt@2.2.1, ltgt@^2.1.2, ltgt@~2.2.0:
resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5"
integrity sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA== integrity sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==
luxon@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.0.1.tgz#6901111d10ad06fd267ad4e4128a84bef8a77299"
integrity sha512-hF3kv0e5gwHQZKz4wtm4c+inDtyc7elkanAsBq+fundaCdUBNJB1dHEGUZIM6SfSBUlbVFduPwEtNjFK8wLtcw==
make-dir@^1.0.0: make-dir@^1.0.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
@ -9706,6 +9946,11 @@ map-visit@^1.0.0:
dependencies: dependencies:
object-visit "^1.0.0" object-visit "^1.0.0"
mapcap@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/mapcap/-/mapcap-1.0.0.tgz#e8e29d04a160eaf8c92ec4bcbd2c5d07ed037e5a"
integrity sha512-KcNlZSlFPx+r1jYZmxEbTVymG+dIctf10WmWkuhrhrblM+KMoF77HelwihL5cxYlORye79KoR4IlOOk99lUJ0g==
markdown-it@^12.2.0: markdown-it@^12.2.0:
version "12.3.2" version "12.3.2"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90"
@ -9738,6 +9983,24 @@ mdurl@^1.0.1:
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==
measured-core@^1.51.1:
version "1.51.1"
resolved "https://registry.yarnpkg.com/measured-core/-/measured-core-1.51.1.tgz#98989705c00bfb0d8a20e665a9f8d6e246a40518"
integrity sha512-DZQP9SEwdqqYRvT2slMK81D/7xwdxXosZZBtLVfPSo6y5P672FBTbzHVdN4IQyUkUpcVOR9pIvtUy5Ryl7NKyg==
dependencies:
binary-search "^1.3.3"
optional-js "^2.0.0"
measured-reporting@^1.51.1:
version "1.51.1"
resolved "https://registry.yarnpkg.com/measured-reporting/-/measured-reporting-1.51.1.tgz#6aeb209ad55edf3940e8afa75c8f97f541216b31"
integrity sha512-JCt+2u6XT1I5lG3SuYqywE0e62DJuAzBcfMzWGUhIYtPQV2Vm4HiYt/durqmzsAbZV181CEs+o/jMKWJKkYIWw==
dependencies:
console-log-level "^1.4.1"
mapcap "^1.0.0"
measured-core "^1.51.1"
optional-js "^2.0.0"
media-typer@0.3.0: media-typer@0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@ -9935,19 +10198,19 @@ mock-require@^3.0.3:
get-caller-file "^1.0.2" get-caller-file "^1.0.2"
normalize-path "^2.1.1" normalize-path "^2.1.1"
moment-timezone@^0.5.15, moment-timezone@^0.5.31: module-details-from-path@^1.0.3:
version "0.5.34" version "1.0.3"
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c" resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b"
integrity sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg== integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==
moment-timezone@^0.5.15:
version "0.5.37"
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.37.tgz#adf97f719c4e458fdb12e2b4e87b8bec9f4eef1e"
integrity sha512-uEDzDNFhfaywRl+vwXxffjjq1q0Vzr+fcQpQ1bU0kbzorfS7zVtZnCnGc8mhWmF39d4g4YriF6kwA75mJKE/Zg==
dependencies: dependencies:
moment ">= 2.9.0" moment ">= 2.9.0"
"moment@>= 2.9.0": "moment@>= 2.9.0", moment@^2.29.3:
version "2.29.3"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.3.tgz#edd47411c322413999f7a5940d526de183c031f3"
integrity sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==
moment@^2.29.3:
version "2.29.4" version "2.29.4"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
@ -9965,6 +10228,11 @@ mongodb@3.6.3:
optionalDependencies: optionalDependencies:
saslprep "^1.0.0" saslprep "^1.0.0"
monitor-event-loop-delay@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/monitor-event-loop-delay/-/monitor-event-loop-delay-1.0.0.tgz#b5ab78165a3bb93f2b275c50d01430c7f155d1f7"
integrity sha512-YRIr1exCIfBDLZle8WHOfSo7Xg3M+phcZfq9Fx1L6Abo+atGp7cge5pM7PjyBn4s1oZI/BRD4EMrzQBbPpVb5Q==
mri@1.1.4: mri@1.1.4:
version "1.1.4" version "1.1.4"
resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a" resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a"
@ -9980,11 +10248,32 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@^2.1.1, ms@^2.1.3: ms@^2.0.0, ms@^2.1.1, ms@^2.1.3:
version "2.1.3" version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
msgpackr-extract@^2.0.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-2.1.2.tgz#56272030f3e163e1b51964ef8b1cd5e7240c03ed"
integrity sha512-cmrmERQFb19NX2JABOGtrKdHMyI6RUyceaPBQ2iRz9GnDkjBWFjNJC0jyyoOfZl2U/LZE3tQCCQc4dlRyA8mcA==
dependencies:
node-gyp-build-optional-packages "5.0.3"
optionalDependencies:
"@msgpackr-extract/msgpackr-extract-darwin-arm64" "2.1.2"
"@msgpackr-extract/msgpackr-extract-darwin-x64" "2.1.2"
"@msgpackr-extract/msgpackr-extract-linux-arm" "2.1.2"
"@msgpackr-extract/msgpackr-extract-linux-arm64" "2.1.2"
"@msgpackr-extract/msgpackr-extract-linux-x64" "2.1.2"
"@msgpackr-extract/msgpackr-extract-win32-x64" "2.1.2"
msgpackr@^1.5.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.6.2.tgz#176cd9f6b4437dad87a839b37f23c2dfee408d9a"
integrity sha512-bqSQ0DYJbXbrJcrZFmMygUZmqQiDfI2ewFVWcrZY12w5XHWtPuW4WppDT/e63Uu311ajwkRRXSoF0uILroBeTA==
optionalDependencies:
msgpackr-extract "^2.0.2"
mssql@6.2.3: mssql@6.2.3:
version "6.2.3" version "6.2.3"
resolved "https://registry.yarnpkg.com/mssql/-/mssql-6.2.3.tgz#1d15bbe8c3057e32ee6e98596b6c323b097a6cba" resolved "https://registry.yarnpkg.com/mssql/-/mssql-6.2.3.tgz#1d15bbe8c3057e32ee6e98596b6c323b097a6cba"
@ -10099,6 +10388,11 @@ neo-async@^2.6.0, neo-async@^2.6.2:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
next-line@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/next-line/-/next-line-1.1.0.tgz#fcae57853052b6a9bae8208e40dd7d3c2d304603"
integrity sha512-+I10J3wKNoKddNxn0CNpoZ3eTZuqxjNM3b1GImVx22+ePI+Y15P8g/j3WsbP0fhzzrFzrtjOAoq5NCCucswXOQ==
nice-try@^1.0.4: nice-try@^1.0.4:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
@ -10131,6 +10425,11 @@ node-forge@^1.3.1:
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
node-gyp-build-optional-packages@5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17"
integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==
node-gyp-build@~4.1.0: node-gyp-build@~4.1.0:
version "4.1.1" version "4.1.1"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb"
@ -10303,11 +10602,23 @@ object-copy@^0.1.0:
define-property "^0.2.5" define-property "^0.2.5"
kind-of "^3.0.3" kind-of "^3.0.3"
object-filter-sequence@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/object-filter-sequence/-/object-filter-sequence-1.0.0.tgz#10bb05402fff100082b80d7e83991b10db411692"
integrity sha512-CsubGNxhIEChNY4cXYuA6KXafztzHqzLLZ/y3Kasf3A+sa3lL9thq3z+7o0pZqzEinjXT6lXDPAfVWI59dUyzQ==
object-hash@^3.0.0: object-hash@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9"
integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
object-identity-map@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/object-identity-map/-/object-identity-map-1.0.2.tgz#2b4213a4285ca3a8cd2e696782c9964f887524e7"
integrity sha512-a2XZDGyYTngvGS67kWnqVdpoaJWsY7C1GhPJvejWAFCsUioTAaiTu8oBad7c6cI4McZxr4CmvnZeycK05iav5A==
dependencies:
object.entries "^1.1.0"
object-inspect@^1.12.0, object-inspect@^1.9.0: object-inspect@^1.12.0, object-inspect@^1.9.0:
version "1.12.2" version "1.12.2"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
@ -10335,6 +10646,15 @@ object.assign@^4.1.0, object.assign@^4.1.2:
has-symbols "^1.0.1" has-symbols "^1.0.1"
object-keys "^1.1.1" object-keys "^1.1.1"
object.entries@^1.0.4, object.entries@^1.1.0:
version "1.1.5"
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861"
integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==
dependencies:
call-bind "^1.0.2"
define-properties "^1.1.3"
es-abstract "^1.19.1"
object.getownpropertydescriptors@^2.1.1: object.getownpropertydescriptors@^2.1.1:
version "2.1.4" version "2.1.4"
resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37"
@ -10458,6 +10778,11 @@ openapi-validator@^0.14.2:
path-parser "^6.1.0" path-parser "^6.1.0"
typeof "^1.0.0" typeof "^1.0.0"
optional-js@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/optional-js/-/optional-js-2.3.0.tgz#81d54c4719afa8845b988143643a5148f9d89490"
integrity sha512-B0LLi+Vg+eko++0z/b8zIv57kp7HKEzaPJo7LowJXMUKYdf+3XJGu/cw03h/JhIOsLnP+cG5QnTHAuicjA5fMw==
optionator@^0.8.1, optionator@^0.8.3: optionator@^0.8.1, optionator@^0.8.3:
version "0.8.3" version "0.8.3"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
@ -10475,6 +10800,13 @@ oracledb@5.3.0:
resolved "https://registry.yarnpkg.com/oracledb/-/oracledb-5.3.0.tgz#a15e6cd16757d8711a2c006a28bd7ecd3b8466f7" resolved "https://registry.yarnpkg.com/oracledb/-/oracledb-5.3.0.tgz#a15e6cd16757d8711a2c006a28bd7ecd3b8466f7"
integrity sha512-HMJzQ6lCf287ztvvehTEmjCWA21FQ3RMvM+mgoqd4i8pkREuqFWO+y3ovsGR9moJUg4T0xjcwS8rl4mggWPxmg== integrity sha512-HMJzQ6lCf287ztvvehTEmjCWA21FQ3RMvM+mgoqd4i8pkREuqFWO+y3ovsGR9moJUg4T0xjcwS8rl4mggWPxmg==
original-url@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/original-url/-/original-url-1.2.3.tgz#133aff4b2d27e38a98d736f7629c56262b7153e1"
integrity sha512-BYm+pKYLtS4mVe/mgT3YKGtWV5HzN/XKiaIu1aK4rsxyjuHeTW9N+xVBEpJcY1onB3nccfH0RbzUEoimMqFUHQ==
dependencies:
forwarded-parse "^2.1.0"
os-locale@^3.1.0: os-locale@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
@ -10958,7 +11290,7 @@ pino-std-serializers@^4.0.0:
resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz#1791ccd2539c091ae49ce9993205e2cd5dbba1e2" resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz#1791ccd2539c091ae49ce9993205e2cd5dbba1e2"
integrity sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q== integrity sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==
pino@^6.13.0: pino@^6.11.2, pino@^6.13.0:
version "6.14.0" version "6.14.0"
resolved "https://registry.yarnpkg.com/pino/-/pino-6.14.0.tgz#b745ea87a99a6c4c9b374e4f29ca7910d4c69f78" resolved "https://registry.yarnpkg.com/pino/-/pino-6.14.0.tgz#b745ea87a99a6c4c9b374e4f29ca7910d4c69f78"
integrity sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg== integrity sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==
@ -11354,15 +11686,6 @@ progress@^2.0.0:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
promise.prototype.finally@^3.1.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/promise.prototype.finally/-/promise.prototype.finally-3.1.3.tgz#d3186e58fcf4df1682a150f934ccc27b7893389c"
integrity sha512-EXRF3fC9/0gz4qkt/f5EP5iW4kj9oFpBICNpCNOb/52+8nlHIX07FPLbi/q4qYBQ1xZqivMzTpNQSnArVASolQ==
dependencies:
call-bind "^1.0.2"
define-properties "^1.1.3"
es-abstract "^1.19.1"
prompts@^2.0.1: prompts@^2.0.1:
version "2.4.2" version "2.4.2"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
@ -11579,7 +11902,7 @@ readable-stream@1.1.14, readable-stream@^1.0.27-1:
isarray "0.0.1" isarray "0.0.1"
string_decoder "~0.10.x" string_decoder "~0.10.x"
"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.0.1, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: "readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.0.1, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
version "3.6.0" version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@ -11796,6 +12119,11 @@ regjsparser@^0.8.2:
dependencies: dependencies:
jsesc "~0.5.0" jsesc "~0.5.0"
relative-microtime@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/relative-microtime/-/relative-microtime-2.0.0.tgz#cceed2af095ecd72ea32011279c79e5fcc7de29b"
integrity sha512-l18ha6HEZc+No/uK4GyAnNxgKW7nvEe35IaeN54sShMojtqik2a6GbTyuiezkjpPaqP874Z3lW5ysBo5irz4NA==
remove-trailing-separator@^1.0.1: remove-trailing-separator@^1.0.1:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
@ -11876,6 +12204,15 @@ require-from-string@^2.0.2:
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
require-in-the-middle@^5.0.3:
version "5.2.0"
resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.2.0.tgz#4b71e3cc7f59977100af9beb76bf2d056a5a6de2"
integrity sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==
dependencies:
debug "^4.1.1"
module-details-from-path "^1.0.3"
resolve "^1.22.1"
require-main-filename@^2.0.0: require-main-filename@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
@ -11948,6 +12285,15 @@ resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.9.0:
path-parse "^1.0.7" path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0" supports-preserve-symlinks-flag "^1.0.0"
resolve@^1.22.1:
version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
dependencies:
is-core-module "^2.9.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
responselike@1.0.2, responselike@^1.0.2: responselike@1.0.2, responselike@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
@ -11981,6 +12327,11 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
rfdc@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==
rimraf@2.6.3: rimraf@2.6.3:
version "2.6.3" version "2.6.3"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
@ -12036,7 +12387,7 @@ safe-buffer@*, safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-b
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.0, safe-buffer@~5.1.1: safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2" version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
@ -12191,6 +12542,11 @@ set-blocking@^2.0.0:
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
set-cookie-serde@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/set-cookie-serde/-/set-cookie-serde-1.0.0.tgz#bcf9c260ed2212ac4005a53eacbaaa37c07ac452"
integrity sha512-Vq8e5GsupfJ7okHIvEPcfs5neCo7MZ1ZuWrO3sllYi3DOWt6bSSCpADzqXjz3k0fXehnoFIrmmhty9IN6U6BXQ==
set-value@^2.0.0, set-value@^2.0.1: set-value@^2.0.0, set-value@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
@ -12211,6 +12567,11 @@ setprototypeof@1.2.0:
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
shallow-clone-shim@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shallow-clone-shim/-/shallow-clone-shim-2.0.0.tgz#b62bf55aed79f4c1430ea1dc4d293a193f52cf91"
integrity sha512-YRNymdiL3KGOoS67d73TEmk4tdPTO9GSMCoiphQsTcC9EtC+AOmMPjkyBkRoCJfW9ASsaZw1craaiw1dPN2D3Q==
shallow-clone@^3.0.0: shallow-clone@^3.0.0:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
@ -12480,6 +12841,13 @@ source-map@^0.7.3:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
source-map@^0.8.0-beta.0:
version "0.8.0-beta.0"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11"
integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==
dependencies:
whatwg-url "^7.0.0"
spark-md5@3.0.1: spark-md5@3.0.1:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.1.tgz#83a0e255734f2ab4e5c466e5a2cfc9ba2aa2124d" resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.1.tgz#83a0e255734f2ab4e5c466e5a2cfc9ba2aa2124d"
@ -12564,6 +12932,11 @@ 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==
sql-summary@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/sql-summary/-/sql-summary-1.0.1.tgz#a2dddb5435bae294eb11424a7330dc5bafe09c2b"
integrity sha512-IpCr2tpnNkP3Jera4ncexsZUp0enJBLr+pHCyTweMUBrbJsTgQeLWx1FXLhoBj/MvcnUQpkgOn2EY8FKOkUzww==
sqlstring@^2.3.2: sqlstring@^2.3.2:
version "2.3.3" version "2.3.3"
resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c" resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c"
@ -12603,6 +12976,11 @@ stack-utils@^2.0.3:
dependencies: dependencies:
escape-string-regexp "^2.0.0" escape-string-regexp "^2.0.0"
stackframe@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==
standard-as-callback@^2.1.0: standard-as-callback@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45"
@ -12636,6 +13014,13 @@ step@0.0.x:
resolved "https://registry.yarnpkg.com/step/-/step-0.0.6.tgz#143e7849a5d7d3f4a088fe29af94915216eeede2" resolved "https://registry.yarnpkg.com/step/-/step-0.0.6.tgz#143e7849a5d7d3f4a088fe29af94915216eeede2"
integrity sha512-qSSeQinUJk2w38vUFobjFoE307GqsozMC8VisOCkJLpklvKPT0ptPHwWOrENoag8rgLudvTkfP3bancwP93/Jw== integrity sha512-qSSeQinUJk2w38vUFobjFoE307GqsozMC8VisOCkJLpklvKPT0ptPHwWOrENoag8rgLudvTkfP3bancwP93/Jw==
stream-chopper@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/stream-chopper/-/stream-chopper-3.0.1.tgz#73791ae7bf954c297d6683aec178648efc61dd75"
integrity sha512-f7h+ly8baAE26iIjcp3VbnBkbIRGtrvV0X0xxFM/d7fwLTYnLzDPTXRKNxa2HZzohOrc96NTrR+FaV3mzOelNA==
dependencies:
readable-stream "^3.0.6"
stream-shift@^1.0.0: stream-shift@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
@ -12659,7 +13044,7 @@ string-length@^4.0.1:
char-regex "^1.0.2" char-regex "^1.0.2"
strip-ansi "^6.0.0" strip-ansi "^6.0.0"
string-similarity@^4.0.4: string-similarity@^4.0.1, string-similarity@^4.0.4:
version "4.0.4" version "4.0.4"
resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.4.tgz#42d01ab0b34660ea8a018da8f56a3309bb8b2a5b" resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.4.tgz#42d01ab0b34660ea8a018da8f56a3309bb8b2a5b"
integrity sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ== integrity sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==
@ -13314,6 +13699,13 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2" regex-not "^1.0.2"
safe-regex "^1.1.0" safe-regex "^1.1.0"
to-source-code@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/to-source-code/-/to-source-code-1.0.2.tgz#dd136bdb1e1dbd80bbeacf088992678e9070bfea"
integrity sha512-YzWtjmNIf3E75eZYa7m1SCyl0vgOGoTzdpH3svfa8SUm5rqTgl9hnDolrAGOghCF9P2gsITXQoMrlujOoz+RPw==
dependencies:
is-nil "^1.0.0"
toidentifier@1.0.1: toidentifier@1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
@ -13371,6 +13763,11 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
traverse@^0.6.6:
version "0.6.6"
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
integrity sha512-kdf4JKs8lbARxWdp7RKdNzoJBhGUcIalSYibuGyHJbmk40pOysQ0+QPvlkCOICOivDWU2IJo2rkrxyTK2AH4fw==
trim-repeated@^1.0.0: trim-repeated@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21"
@ -13569,6 +13966,14 @@ undici@^4.14.1:
resolved "https://registry.yarnpkg.com/undici/-/undici-4.16.0.tgz#469bb87b3b918818d3d7843d91a1d08da357d5ff" resolved "https://registry.yarnpkg.com/undici/-/undici-4.16.0.tgz#469bb87b3b918818d3d7843d91a1d08da357d5ff"
integrity sha512-tkZSECUYi+/T1i4u+4+lwZmQgLXd4BLGlrc7KZPcLIW7Jpq99+Xpc30ONv7nS6F5UNOxp/HBZSSL9MafUrvJbw== integrity sha512-tkZSECUYi+/T1i4u+4+lwZmQgLXd4BLGlrc7KZPcLIW7Jpq99+Xpc30ONv7nS6F5UNOxp/HBZSSL9MafUrvJbw==
unicode-byte-truncate@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unicode-byte-truncate/-/unicode-byte-truncate-1.0.0.tgz#aa6f0f3475193fe20c320ac9213e36e62e8764a7"
integrity sha512-GQgHk6DodEoKddKQdjnv7xKS9G09XCfHWX0R4RKht+EbUMSiVEmtWHGFO8HUm+6NvWik3E2/DG4MxTitOLL64A==
dependencies:
is-integer "^1.0.6"
unicode-substring "^0.1.0"
unicode-canonical-property-names-ecmascript@^2.0.0: unicode-canonical-property-names-ecmascript@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
@ -13592,6 +13997,11 @@ unicode-property-aliases-ecmascript@^2.0.0:
resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8"
integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==
unicode-substring@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/unicode-substring/-/unicode-substring-0.1.0.tgz#6120ce3c390385dbcd0f60c32b9065c4181d4b36"
integrity sha512-36Xaw9wXi7MB/3/EQZZHkZyyiRNa9i3k9YtPAz2KfqMVH2xutdXyMHn4Igarmnvr+wOrfWa/6njhY+jPpXN2EQ==
union-value@^1.0.0: union-value@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
@ -13733,7 +14143,7 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
util.promisify@^1.0.0, util.promisify@^1.0.1: util.promisify@^1.0.0:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.1.1.tgz#77832f57ced2c9478174149cae9b96e9918cd54b" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.1.1.tgz#77832f57ced2c9478174149cae9b96e9918cd54b"
integrity sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw== integrity sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw==

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "1.2.41-alpha.5", "version": "1.2.58-alpha.5",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs", "main": "src/index.cjs",
"module": "dist/bundle.mjs", "module": "dist/bundle.mjs",

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