Merge remote-tracking branch 'origin/develop' into feature/automation-collaboration

This commit is contained in:
Dean 2023-07-17 11:24:50 +01:00
commit e2311b8a55
83 changed files with 1500 additions and 890 deletions

View File

@ -28,3 +28,4 @@ BB_ADMIN_USER_PASSWORD=
# A path that is watched for plugin bundles. Any bundles found are imported automatically/ # A path that is watched for plugin bundles. Any bundles found are imported automatically/
PLUGINS_DIR= PLUGINS_DIR=
ROLLING_LOG_MAX_SIZE=

View File

@ -22,6 +22,16 @@ server {
proxy_pass http://127.0.0.1:4001; proxy_pass http://127.0.0.1:4001;
} }
location /embed {
rewrite /embed/(.*) /app/$1 break;
proxy_pass http://127.0.0.1:4001;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header x-budibase-embed "true";
add_header x-budibase-embed "true";
add_header Content-Security-Policy "frame-ancestors *";
}
location = / { location = / {
proxy_pass http://127.0.0.1:4001; proxy_pass http://127.0.0.1:4001;
} }

View File

@ -1,5 +1,5 @@
{ {
"version": "2.8.3-alpha.1", "version": "2.8.10-alpha.2",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

12
nx.json
View File

@ -1,9 +1,13 @@
{ {
"tasksRunnerOptions": { "tasksRunnerOptions": {
"default": { "default": {
"runner": "nx/tasks-runners/default", "runner": "nx-cloud",
"options": { "options": {
"cacheableOperations": ["build", "test"] "cacheableOperations": [
"build",
"test"
],
"accessToken": "YWNiYzc5NTEtMzMzZC00NDhjLTgyNjktZTllMjI1MzM4OGQxfHJlYWQtd3JpdGU="
} }
} }
}, },
@ -11,7 +15,9 @@
"dev:builder": { "dev:builder": {
"dependsOn": [ "dependsOn": [
{ {
"projects": ["@budibase/string-templates"], "projects": [
"@budibase/string-templates"
],
"target": "build" "target": "build"
} }
] ]

View File

@ -3,7 +3,7 @@
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"@esbuild-plugins/tsconfig-paths": "^0.1.2", "@esbuild-plugins/tsconfig-paths": "^0.1.2",
"@nx/js": "16.2.1", "@nx/js": "16.4.3",
"@rollup/plugin-json": "^4.0.2", "@rollup/plugin-json": "^4.0.2",
"@typescript-eslint/parser": "5.45.0", "@typescript-eslint/parser": "5.45.0",
"esbuild": "^0.17.18", "esbuild": "^0.17.18",
@ -13,9 +13,11 @@
"husky": "^8.0.3", "husky": "^8.0.3",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"kill-port": "^1.6.1", "kill-port": "^1.6.1",
"lerna": "7.0.2", "lerna": "7.1.1",
"madge": "^6.0.0", "madge": "^6.0.0",
"minimist": "^1.2.8", "minimist": "^1.2.8",
"nx": "16.4.3",
"nx-cloud": "16.0.5",
"prettier": "2.8.8", "prettier": "2.8.8",
"prettier-plugin-svelte": "^2.3.0", "prettier-plugin-svelte": "^2.3.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
@ -44,15 +46,15 @@
"restore": "yarn run clean && yarn run bootstrap && yarn run build", "restore": "yarn run clean && yarn run bootstrap && yarn run build",
"nuke": "yarn run nuke:packages && yarn run nuke:docker", "nuke": "yarn run nuke:packages && yarn run nuke:docker",
"nuke:packages": "yarn run restore", "nuke:packages": "yarn run restore",
"nuke:docker": "lerna run --stream --parallel dev:stack:nuke", "nuke:docker": "lerna run --stream dev:stack:nuke",
"clean": "lerna clean", "clean": "lerna clean",
"kill-builder": "kill-port 3000", "kill-builder": "kill-port 3000",
"kill-server": "kill-port 4001 4002", "kill-server": "kill-port 4001 4002",
"kill-all": "yarn run kill-builder && yarn run kill-server", "kill-all": "yarn run kill-builder && yarn run kill-server",
"dev": "yarn run kill-all && lerna run --stream --parallel dev:builder --stream", "dev": "yarn run kill-all && lerna run --stream dev:builder --stream",
"dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && lerna run --stream --parallel dev:builder --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker", "dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && lerna run --stream dev:builder --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker",
"dev:server": "yarn run kill-server && yarn build --projects=@budibase/client && lerna run --stream --parallel dev:builder --scope @budibase/worker --scope @budibase/server", "dev:server": "yarn run kill-server && yarn build --projects=@budibase/client && lerna run --stream dev:builder --scope @budibase/worker --scope @budibase/server",
"dev:built": "yarn run kill-all && cd packages/server && yarn dev:stack:up && cd ../../ && lerna run --stream --parallel dev:built", "dev:built": "yarn run kill-all && cd packages/server && yarn dev:stack:up && cd ../../ && lerna run --stream dev:built",
"dev:docker": "yarn build:docker:pre && docker-compose -f hosting/docker-compose.build.yaml -f hosting/docker-compose.dev.yaml --env-file hosting/.env up --build --scale proxy-service=0", "dev:docker": "yarn build:docker:pre && docker-compose -f hosting/docker-compose.build.yaml -f hosting/docker-compose.dev.yaml --env-file hosting/.env up --build --scale proxy-service=0",
"test": "lerna run --stream test --stream", "test": "lerna run --stream test --stream",
"lint:eslint": "eslint packages qa-core --max-warnings=0", "lint:eslint": "eslint packages qa-core --max-warnings=0",
@ -106,6 +108,5 @@
"@budibase/string-templates": "0.0.0", "@budibase/string-templates": "0.0.0",
"@budibase/types": "0.0.0" "@budibase/types": "0.0.0"
}, },
"dependencies": { "dependencies": {}
}
} }

View File

@ -51,6 +51,7 @@
"pouchdb": "7.3.0", "pouchdb": "7.3.0",
"pouchdb-find": "7.2.2", "pouchdb-find": "7.2.2",
"redlock": "4.2.0", "redlock": "4.2.0",
"rotating-file-stream": "3.1.0",
"sanitize-s3-objectkey": "0.0.1", "sanitize-s3-objectkey": "0.0.1",
"semver": "7.3.7", "semver": "7.3.7",
"tar-fs": "2.1.1", "tar-fs": "2.1.1",

View File

@ -433,6 +433,9 @@ export class QueryBuilder<T> {
if (!value) { if (!value) {
return null return null
} }
if (typeof value === "boolean") {
return `(*:* AND !${key}:${value})`
}
return `!${key}:${builder.preprocess(value, allPreProcessingOpts)}` return `!${key}:${builder.preprocess(value, allPreProcessingOpts)}`
}) })
} }

View File

@ -47,7 +47,10 @@ function httpLogging() {
return process.env.HTTP_LOGGING return process.env.HTTP_LOGGING
} }
function findVersion() { function getPackageJsonFields(): {
VERSION: string
SERVICE_NAME: string
} {
function findFileInAncestors( function findFileInAncestors(
fileName: string, fileName: string,
currentDir: string currentDir: string
@ -69,10 +72,14 @@ function findVersion() {
try { try {
const packageJsonFile = findFileInAncestors("package.json", process.cwd()) const packageJsonFile = findFileInAncestors("package.json", process.cwd())
const content = readFileSync(packageJsonFile!, "utf-8") const content = readFileSync(packageJsonFile!, "utf-8")
return JSON.parse(content).version const parsedContent = JSON.parse(content)
return {
VERSION: parsedContent.version,
SERVICE_NAME: parsedContent.name,
}
} catch { } catch {
// throwing an error here is confusing/causes backend-core to be hard to import // throwing an error here is confusing/causes backend-core to be hard to import
return undefined return { VERSION: "", SERVICE_NAME: "" }
} }
} }
@ -154,13 +161,14 @@ const environment = {
ENABLE_SSO_MAINTENANCE_MODE: selfHosted ENABLE_SSO_MAINTENANCE_MODE: selfHosted
? process.env.ENABLE_SSO_MAINTENANCE_MODE ? process.env.ENABLE_SSO_MAINTENANCE_MODE
: false, : false,
VERSION: findVersion(), ...getPackageJsonFields(),
DISABLE_PINO_LOGGER: process.env.DISABLE_PINO_LOGGER, DISABLE_PINO_LOGGER: process.env.DISABLE_PINO_LOGGER,
_set(key: any, value: any) { _set(key: any, value: any) {
process.env[key] = value process.env[key] = value
// @ts-ignore // @ts-ignore
environment[key] = value environment[key] = value
}, },
ROLLING_LOG_MAX_SIZE: process.env.ROLLING_LOG_MAX_SIZE || "10M",
} }
// clean up any environment variable edge cases // clean up any environment variable edge cases

View File

@ -1,6 +1,7 @@
export * as correlation from "./correlation/correlation" export * as correlation from "./correlation/correlation"
export { logger } from "./pino/logger" export { logger } from "./pino/logger"
export * from "./alerts" export * from "./alerts"
export * as system from "./system"
// turn off or on context logging i.e. tenantId, appId etc // turn off or on context logging i.e. tenantId, appId etc
export let LOG_CONTEXT = true export let LOG_CONTEXT = true

View File

@ -1,10 +1,15 @@
import env from "../../environment"
import pino, { LoggerOptions } from "pino" import pino, { LoggerOptions } from "pino"
import pinoPretty from "pino-pretty"
import { IdentityType } from "@budibase/types"
import env from "../../environment"
import * as context from "../../context" import * as context from "../../context"
import * as correlation from "../correlation" import * as correlation from "../correlation"
import { IdentityType } from "@budibase/types"
import { LOG_CONTEXT } from "../index" import { LOG_CONTEXT } from "../index"
import { localFileDestination } from "../system"
// LOGGER // LOGGER
let pinoInstance: pino.Logger | undefined let pinoInstance: pino.Logger | undefined
@ -16,22 +21,27 @@ if (!env.DISABLE_PINO_LOGGER) {
return { level: label.toUpperCase() } return { level: label.toUpperCase() }
}, },
bindings: () => { bindings: () => {
return {} return {
service: env.SERVICE_NAME,
}
}, },
}, },
timestamp: () => `,"timestamp":"${new Date(Date.now()).toISOString()}"`, timestamp: () => `,"timestamp":"${new Date(Date.now()).toISOString()}"`,
} }
const destinations: pino.DestinationStream[] = []
if (env.isDev()) { if (env.isDev()) {
pinoOptions.transport = { destinations.push(pinoPretty({ singleLine: true }))
target: "pino-pretty",
options: {
singleLine: true,
},
}
} }
pinoInstance = pino(pinoOptions) if (env.SELF_HOSTED) {
destinations.push(localFileDestination())
}
pinoInstance = destinations.length
? pino(pinoOptions, pino.multistream(destinations))
: pino(pinoOptions)
// CONSOLE OVERRIDES // CONSOLE OVERRIDES

View File

@ -0,0 +1,81 @@
import fs from "fs"
import path from "path"
import * as rfs from "rotating-file-stream"
import env from "../environment"
import { budibaseTempDir } from "../objectStore"
const logsFileName = `budibase.log`
const budibaseLogsHistoryFileName = "budibase-logs-history.txt"
const logsPath = path.join(budibaseTempDir(), "systemlogs")
function getFullPath(fileName: string) {
return path.join(logsPath, fileName)
}
export function getSingleFileMaxSizeInfo(totalMaxSize: string) {
const regex = /(\d+)([A-Za-z])/
const match = totalMaxSize?.match(regex)
if (!match) {
console.warn(`totalMaxSize does not have a valid value`, {
totalMaxSize,
})
return undefined
}
const size = +match[1]
const unit = match[2]
if (size === 1) {
switch (unit) {
case "B":
return { size: `${size}B`, totalHistoryFiles: 1 }
case "K":
return { size: `${(size * 1000) / 2}B`, totalHistoryFiles: 1 }
case "M":
return { size: `${(size * 1000) / 2}K`, totalHistoryFiles: 1 }
case "G":
return { size: `${(size * 1000) / 2}M`, totalHistoryFiles: 1 }
default:
return undefined
}
}
if (size % 2 === 0) {
return { size: `${size / 2}${unit}`, totalHistoryFiles: 1 }
}
return { size: `1${unit}`, totalHistoryFiles: size - 1 }
}
export function localFileDestination() {
const fileInfo = getSingleFileMaxSizeInfo(env.ROLLING_LOG_MAX_SIZE)
const outFile = rfs.createStream(logsFileName, {
// As we have a rolling size, we want to half the max size
size: fileInfo?.size,
path: logsPath,
maxFiles: fileInfo?.totalHistoryFiles || 1,
immutable: true,
history: budibaseLogsHistoryFileName,
initialRotation: false,
})
return outFile
}
export function getLogReadStream() {
const streams = []
const historyFile = getFullPath(budibaseLogsHistoryFileName)
if (fs.existsSync(historyFile)) {
const fileContent = fs.readFileSync(historyFile, "utf-8")
const historyFiles = fileContent.split("\n")
for (const historyFile of historyFiles.filter(x => x)) {
streams.push(fs.readFileSync(historyFile))
}
}
streams.push(fs.readFileSync(getFullPath(logsFileName)))
const combinedContent = Buffer.concat(streams)
return combinedContent
}

View File

@ -0,0 +1,61 @@
import { getSingleFileMaxSizeInfo } from "../system"
describe("system", () => {
describe("getSingleFileMaxSizeInfo", () => {
it.each([
["100B", "50B"],
["200K", "100K"],
["20M", "10M"],
["4G", "2G"],
])(
"Halving even number (%s) returns halved size and 1 history file (%s)",
(totalValue, expectedMaxSize) => {
const result = getSingleFileMaxSizeInfo(totalValue)
expect(result).toEqual({
size: expectedMaxSize,
totalHistoryFiles: 1,
})
}
)
it.each([
["5B", "1B", 4],
["17K", "1K", 16],
["21M", "1M", 20],
["3G", "1G", 2],
])(
"Halving an odd number (%s) returns as many files as size (-1) (%s)",
(totalValue, expectedMaxSize, totalHistoryFiles) => {
const result = getSingleFileMaxSizeInfo(totalValue)
expect(result).toEqual({
size: expectedMaxSize,
totalHistoryFiles,
})
}
)
it.each([
["1B", "1B"],
["1K", "500B"],
["1M", "500K"],
["1G", "500M"],
])(
"Halving '%s' returns halved unit (%s)",
(totalValue, expectedMaxSize) => {
const result = getSingleFileMaxSizeInfo(totalValue)
expect(result).toEqual({
size: expectedMaxSize,
totalHistoryFiles: 1,
})
}
)
it.each([[undefined], [""], ["50"], ["wrongvalue"]])(
"Halving wrongly formatted value ('%s') returns undefined",
totalValue => {
const result = getSingleFileMaxSizeInfo(totalValue!)
expect(result).toBeUndefined()
}
)
})
})

View File

@ -1,6 +1,7 @@
<script> <script>
import "@spectrum-css/button/dist/index-vars.css" import "@spectrum-css/button/dist/index-vars.css"
import Tooltip from "../Tooltip/Tooltip.svelte" import AbsTooltip from "../Tooltip/AbsTooltip.svelte"
import { createEventDispatcher } from "svelte"
export let type export let type
export let disabled = false export let disabled = false
@ -16,48 +17,53 @@
export let tooltip = undefined export let tooltip = undefined
export let newStyles = true export let newStyles = true
export let id export let id
const dispatch = createEventDispatcher()
</script> </script>
<button <AbsTooltip text={tooltip}>
{id} <button
{type} {id}
class:spectrum-Button--cta={cta} {type}
class:spectrum-Button--primary={primary} class:spectrum-Button--cta={cta}
class:spectrum-Button--secondary={secondary} class:spectrum-Button--primary={primary}
class:spectrum-Button--warning={warning} class:spectrum-Button--secondary={secondary}
class:spectrum-Button--overBackground={overBackground} class:spectrum-Button--warning={warning}
class:spectrum-Button--quiet={quiet} class:spectrum-Button--overBackground={overBackground}
class:new-styles={newStyles} class:spectrum-Button--quiet={quiet}
class:active class:new-styles={newStyles}
class:disabled class:active
class="spectrum-Button spectrum-Button--size{size.toUpperCase()}" class:is-disabled={disabled}
{disabled} class="spectrum-Button spectrum-Button--size{size.toUpperCase()}"
on:click|preventDefault on:click|preventDefault={() => {
> if (!disabled) {
{#if icon} dispatch("click")
<svg }
class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}" }}
focusable="false" >
aria-hidden="true" {#if icon}
aria-label={icon} <svg
> class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
<use xlink:href="#spectrum-icon-18-{icon}" /> focusable="false"
</svg> aria-hidden="true"
{/if} aria-label={icon}
{#if $$slots} >
<span class="spectrum-Button-label"><slot /></span> <use xlink:href="#spectrum-icon-18-{icon}" />
{/if} </svg>
{#if tooltip} {/if}
<div class="tooltip"> {#if $$slots}
<Tooltip textWrapping={true} direction={"bottom"} text={tooltip} /> <span class="spectrum-Button-label"><slot /></span>
</div> {/if}
{/if} </button>
</button> </AbsTooltip>
<style> <style>
button { button {
position: relative; position: relative;
} }
button.is-disabled {
cursor: default;
}
.spectrum-Button-label { .spectrum-Button-label {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
@ -66,23 +72,6 @@
.active { .active {
color: var(--spectrum-global-color-blue-600) !important; color: var(--spectrum-global-color-blue-600) !important;
} }
.tooltip {
position: absolute;
display: flex;
justify-content: center;
z-index: 100;
width: 160px;
text-align: center;
transform: translateX(-50%);
left: 50%;
top: 100%;
opacity: 0;
transition: opacity 130ms ease-out;
pointer-events: none;
}
button:hover .tooltip {
opacity: 1;
}
.spectrum-Button--primary.new-styles { .spectrum-Button--primary.new-styles {
background: var(--spectrum-global-color-gray-800); background: var(--spectrum-global-color-gray-800);
border-color: transparent; border-color: transparent;
@ -96,10 +85,10 @@
border-color: transparent; border-color: transparent;
color: var(--spectrum-global-color-gray-900); color: var(--spectrum-global-color-gray-900);
} }
.spectrum-Button--secondary.new-styles:not(.disabled):hover { .spectrum-Button--secondary.new-styles:not(.is-disabled):hover {
background: var(--spectrum-global-color-gray-300); background: var(--spectrum-global-color-gray-300);
} }
.spectrum-Button--secondary.new-styles.disabled { .spectrum-Button--secondary.new-styles.is-disabled {
color: var(--spectrum-global-color-gray-500); color: var(--spectrum-global-color-gray-500);
} }
</style> </style>

View File

@ -90,6 +90,6 @@
.spectrum-Popover { .spectrum-Popover {
min-width: var(--spectrum-global-dimension-size-2000); min-width: var(--spectrum-global-dimension-size-2000);
border-color: var(--spectrum-global-color-gray-300); border-color: var(--spectrum-global-color-gray-300);
overflow: visible; overflow: auto;
} }
</style> </style>

View File

@ -0,0 +1,157 @@
<script context="module">
export const TooltipPosition = {
Top: "top",
Right: "right",
Bottom: "bottom",
Left: "left",
}
export const TooltipType = {
Default: "default",
Info: "info",
Positive: "positive",
Negative: "negative",
}
</script>
<script>
import Portal from "svelte-portal"
import { fade } from "svelte/transition"
import "@spectrum-css/tooltip/dist/index-vars.css"
import { onDestroy } from "svelte"
export let position = TooltipPosition.Top
export let type = TooltipType.Default
export let text = ""
export let fixed = false
export let color = null
let wrapper
let hovered = false
let left
let top
let visible = false
let timeout
let interval
$: {
if (hovered || fixed) {
// Debounce showing by 200ms to avoid flashing tooltip
timeout = setTimeout(show, 200)
} else {
hide()
}
}
$: tooltipStyle = color ? `background:${color};` : null
$: tipStyle = color ? `border-top-color:${color};` : null
// Computes the position of the tooltip
const updateTooltipPosition = () => {
const node = wrapper?.children?.[0]
if (!node) {
left = null
top = null
return
}
const bounds = node.getBoundingClientRect()
// Determine where to render tooltip based on position prop
if (position === TooltipPosition.Top) {
left = bounds.left + bounds.width / 2
top = bounds.top
} else if (position === TooltipPosition.Right) {
left = bounds.left + bounds.width
top = bounds.top + bounds.height / 2
} else if (position === TooltipPosition.Bottom) {
left = bounds.left + bounds.width / 2
top = bounds.top + bounds.height
} else if (position === TooltipPosition.Left) {
left = bounds.left
top = bounds.top + bounds.height / 2
}
}
// Computes the position of the tooltip then shows it.
// We set up a poll to frequently update the position of the tooltip in case
// the target moves.
const show = () => {
updateTooltipPosition()
interval = setInterval(updateTooltipPosition, 100)
visible = true
}
// Hides the tooltip
const hide = () => {
clearTimeout(timeout)
clearInterval(interval)
visible = false
}
// Ensure we clean up interval and timeout
onDestroy(hide)
</script>
<div
bind:this={wrapper}
class="abs-tooltip"
on:focus={null}
on:mouseover={() => (hovered = true)}
on:mouseleave={() => (hovered = false)}
>
<slot />
</div>
{#if visible && text && left != null && top != null}
<Portal target=".spectrum">
<span
class="spectrum-Tooltip spectrum-Tooltip--{type} spectrum-Tooltip--{position} is-open"
style={`left:${left}px;top:${top}px;${tooltipStyle}`}
transition:fade|local={{ duration: 130 }}
>
<span class="spectrum-Tooltip-label">{text}</span>
<span class="spectrum-Tooltip-tip" style={tipStyle} />
</span>
</Portal>
{/if}
<style>
.abs-tooltip {
display: contents;
}
.spectrum-Tooltip {
position: absolute;
z-index: 9999;
pointer-events: none;
margin: 0;
max-width: 280px;
transition: top 130ms ease-out, left 130ms ease-out;
}
.spectrum-Tooltip-label {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
font-size: 12px;
font-weight: 600;
}
/* Colour overrides for default type */
.spectrum-Tooltip--default {
background: var(--spectrum-global-color-gray-500);
}
.spectrum-Tooltip--default .spectrum-Tooltip-tip {
border-top-color: var(--spectrum-global-color-gray-500);
}
/* Position styles */
.spectrum-Tooltip--top {
transform: translateX(-50%) translateY(calc(-100% - 8px));
}
.spectrum-Tooltip--right {
transform: translateX(8px) translateY(-50%);
}
.spectrum-Tooltip--bottom {
transform: translateX(-50%) translateY(8px);
}
.spectrum-Tooltip--left {
transform: translateX(calc(-100% - 8px)) translateY(-50%);
}
</style>

View File

@ -0,0 +1,39 @@
<script>
import AbsTooltip from "./AbsTooltip.svelte"
import { onDestroy } from "svelte"
export let text = null
export let condition = true
export let duration = 3000
export let position
export let type
let visible = false
let timeout
$: {
if (condition) {
showTooltip()
} else {
hideTooltip()
}
}
const showTooltip = () => {
visible = true
timeout = setTimeout(() => {
visible = false
}, duration)
}
const hideTooltip = () => {
visible = false
clearTimeout(timeout)
}
onDestroy(hideTooltip)
</script>
<AbsTooltip {position} {type} text={visible ? text : null} fixed={visible}>
<slot />
</AbsTooltip>

View File

@ -36,6 +36,12 @@ export { default as Layout } from "./Layout/Layout.svelte"
export { default as Page } from "./Layout/Page.svelte" export { default as Page } from "./Layout/Page.svelte"
export { default as Link } from "./Link/Link.svelte" export { default as Link } from "./Link/Link.svelte"
export { default as Tooltip } from "./Tooltip/Tooltip.svelte" export { default as Tooltip } from "./Tooltip/Tooltip.svelte"
export { default as TempTooltip } from "./Tooltip/TempTooltip.svelte"
export {
default as AbsTooltip,
TooltipPosition,
TooltipType,
} from "./Tooltip/AbsTooltip.svelte"
export { default as TooltipWrapper } from "./Tooltip/TooltipWrapper.svelte" export { default as TooltipWrapper } from "./Tooltip/TooltipWrapper.svelte"
export { default as Menu } from "./Menu/Menu.svelte" export { default as Menu } from "./Menu/Menu.svelte"
export { default as MenuSection } from "./Menu/Section.svelte" export { default as MenuSection } from "./Menu/Section.svelte"

View File

@ -127,8 +127,12 @@ export const selectedAutomation = derived(automationStore, $automationStore => {
export const userSelectedResourceMap = derived(userStore, $userStore => { export const userSelectedResourceMap = derived(userStore, $userStore => {
let map = {} let map = {}
$userStore.forEach(user => { $userStore.forEach(user => {
if (user.builderMetadata?.selectedResourceId) { const resource = user.builderMetadata?.selectedResourceId
map[user.builderMetadata?.selectedResourceId] = user if (resource) {
if (!map[resource]) {
map[resource] = []
}
map[resource].push(user)
} }
}) })
return map return map

View File

@ -17,7 +17,7 @@
$: text = getText(filters) $: text = getText(filters)
const getText = filters => { const getText = filters => {
const count = filters?.length const count = filters?.filter(filter => filter.field)?.length
return count ? `Filter (${count})` : "Filter" return count ? `Filter (${count})` : "Filter"
} }
</script> </script>

View File

@ -2,21 +2,13 @@
import { goto, url } from "@roxi/routify" import { goto, url } from "@roxi/routify"
import { tables } from "stores/backend" import { tables } from "stores/backend"
import { notifications } from "@budibase/bbui" import { notifications } from "@budibase/bbui"
import { import { Input, Label, ModalContent, Layout } from "@budibase/bbui"
Input,
Label,
ModalContent,
Toggle,
Divider,
Layout,
} from "@budibase/bbui"
import { datasources } from "stores/backend" import { datasources } from "stores/backend"
import TableDataImport from "../TableDataImport.svelte" import TableDataImport from "../TableDataImport.svelte"
import { import {
BUDIBASE_INTERNAL_DB_ID, BUDIBASE_INTERNAL_DB_ID,
BUDIBASE_DATASOURCE_TYPE, BUDIBASE_DATASOURCE_TYPE,
} from "constants/backend" } from "constants/backend"
import { buildAutoColumn, getAutoColumnInformation } from "builderStore/utils"
$: tableNames = $tables.list.map(table => table.name) $: tableNames = $tables.list.map(table => table.name)
$: selectedSource = $datasources.list.find( $: selectedSource = $datasources.list.find(
@ -43,28 +35,12 @@
} }
let error = "" let error = ""
let autoColumns = getAutoColumnInformation()
let schema = {} let schema = {}
let rows = [] let rows = []
let allValid = true let allValid = true
let displayColumn = null let displayColumn = null
function getAutoColumns() {
const selectedAutoColumns = {}
Object.entries(autoColumns).forEach(([subtype, column]) => {
if (column.enabled) {
selectedAutoColumns[column.name] = buildAutoColumn(
name,
column.name,
subtype
)
}
})
return selectedAutoColumns
}
function checkValid(evt) { function checkValid(evt) {
const tableName = evt.target.value const tableName = evt.target.value
if (tableNames.includes(tableName)) { if (tableNames.includes(tableName)) {
@ -77,7 +53,7 @@
async function saveTable() { async function saveTable() {
let newTable = { let newTable = {
name, name,
schema: { ...schema, ...getAutoColumns() }, schema: { ...schema },
rows, rows,
type: "internal", type: "internal",
sourceId: targetDatasourceId, sourceId: targetDatasourceId,
@ -118,21 +94,6 @@
bind:value={name} bind:value={name}
{error} {error}
/> />
<div class="autocolumns">
<Label extraSmall grey>Auto Columns</Label>
<div class="toggles">
<div class="toggle-1">
<Toggle text="Created by" bind:value={autoColumns.createdBy.enabled} />
<Toggle text="Created at" bind:value={autoColumns.createdAt.enabled} />
<Toggle text="Auto ID" bind:value={autoColumns.autoID.enabled} />
</div>
<div class="toggle-2">
<Toggle text="Updated by" bind:value={autoColumns.updatedBy.enabled} />
<Toggle text="Updated at" bind:value={autoColumns.updatedAt.enabled} />
</div>
</div>
<Divider />
</div>
<div> <div>
<Layout gap="XS" noPadding> <Layout gap="XS" noPadding>
<Label grey extraSmall <Label grey extraSmall
@ -148,24 +109,3 @@
</Layout> </Layout>
</div> </div>
</ModalContent> </ModalContent>
<style>
.autocolumns {
margin-bottom: -10px;
}
.toggles {
display: flex;
width: 100%;
margin-top: 6px;
}
.toggle-1 :global(> *) {
margin-bottom: 10px;
}
.toggle-2 :global(> *) {
margin-bottom: 10px;
margin-left: 20px;
}
</style>

View File

@ -0,0 +1,39 @@
<script>
import { Icon, Heading } from "@budibase/bbui"
export let showClose = false
export let onClose = () => {}
export let heading = ""
</script>
<section class="page">
<div class="closeButton">
{#if showClose}
<Icon hoverable name="Close" on:click={onClose} />
{/if}
</div>
<div class="heading">
<Heading weight="light">{heading}</Heading>
</div>
<slot />
</section>
<style>
.page {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.heading {
text-align: center;
}
.closeButton {
height: 38px;
display: flex;
justify-content: right;
width: 100%;
}
</style>

View File

@ -2,6 +2,7 @@
import { Icon } from "@budibase/bbui" import { Icon } from "@budibase/bbui"
import { createEventDispatcher, getContext } from "svelte" import { createEventDispatcher, getContext } from "svelte"
import { helpers } from "@budibase/shared-core" import { helpers } from "@budibase/shared-core"
import UserAvatars from "../../pages/builder/app/[application]/_components/UserAvatars.svelte"
export let icon export let icon
export let withArrow = false export let withArrow = false
@ -98,21 +99,25 @@
<Icon color={iconColor} size="S" name={icon} /> <Icon color={iconColor} size="S" name={icon} />
</div> </div>
{/if} {/if}
<div class="text" title={showTooltip ? text : null}>{text}</div> <div class="text" title={showTooltip ? text : null}>
{text}
{#if selectedBy}
<UserAvatars size="XS" users={selectedBy} />
{/if}
</div>
{#if withActions} {#if withActions}
<div class="actions"> <div class="actions">
<slot /> <slot />
</div> </div>
{/if} {/if}
{#if $$slots.right} {#if $$slots.right}
<div class="right"> <div class="right">
<slot name="right" /> <slot name="right" />
</div> </div>
{/if} {/if}
</div> </div>
{#if selectedBy}
<div class="selected-by-label">{helpers.getUserLabel(selectedBy)}</div>
{/if}
</div> </div>
<style> <style>
@ -136,13 +141,16 @@
} }
.nav-item.highlighted { .nav-item.highlighted {
background-color: var(--spectrum-global-color-gray-200); background-color: var(--spectrum-global-color-gray-200);
--avatars-background: var(--spectrum-global-color-gray-200);
} }
.nav-item.selected { .nav-item.selected {
background-color: var(--spectrum-global-color-gray-300); background-color: var(--spectrum-global-color-gray-300);
--avatars-background: var(--spectrum-global-color-gray-300);
color: var(--ink); color: var(--ink);
} }
.nav-item:hover { .nav-item:hover {
background-color: var(--spectrum-global-color-gray-300); background-color: var(--spectrum-global-color-gray-300);
--avatars-background: var(--spectrum-global-color-gray-300);
} }
.nav-item:hover .actions { .nav-item:hover .actions {
visibility: visible; visibility: visible;
@ -159,37 +167,6 @@
padding-left: var(--spacing-l); padding-left: var(--spacing-l);
} }
/* Selected user styles */
.nav-item.selectedBy:after {
content: "";
position: absolute;
width: calc(100% - 4px);
height: 28px;
border: 2px solid var(--selected-by-color);
left: 0;
top: 0;
border-radius: 2px;
pointer-events: none;
}
.selected-by-label {
position: absolute;
top: 0;
right: 0;
background: var(--selected-by-color);
padding: 2px 4px;
font-size: 12px;
color: white;
transform: translateY(calc(1px - 100%));
border-top-right-radius: 2px;
border-top-left-radius: 2px;
pointer-events: none;
opacity: 0;
transition: opacity 130ms ease-out;
}
.nav-item.selectedBy:hover .selected-by-label {
opacity: 1;
}
/* Needed to fully display the actions icon */ /* Needed to fully display the actions icon */
.nav-item.scrollable .nav-item-content { .nav-item.scrollable .nav-item-content {
padding-right: 1px; padding-right: 1px;
@ -245,6 +222,9 @@
color: var(--spectrum-global-color-gray-900); color: var(--spectrum-global-color-gray-900);
order: 2; order: 2;
width: 0; width: 0;
display: flex;
align-items: center;
gap: 8px;
} }
.scrollable .text { .scrollable .text {
flex: 0 0 auto; flex: 0 0 auto;

View File

@ -208,7 +208,9 @@
<div class="syntax-error"> <div class="syntax-error">
Current Handlebars syntax is invalid, please check the Current Handlebars syntax is invalid, please check the
guide guide
<a href="https://handlebarsjs.com/guide/">here</a> <a href="https://handlebarsjs.com/guide/" target="_blank"
>here</a
>
for more details. for more details.
</div> </div>
{:else} {:else}

View File

@ -10,6 +10,7 @@
Link, Link,
Modal, Modal,
StatusLight, StatusLight,
AbsTooltip,
} from "@budibase/bbui" } from "@budibase/bbui"
import RevertModal from "components/deploy/RevertModal.svelte" import RevertModal from "components/deploy/RevertModal.svelte"
import VersionModal from "components/deploy/VersionModal.svelte" import VersionModal from "components/deploy/VersionModal.svelte"
@ -250,15 +251,20 @@
<Link quiet on:click={unpublishApp}>Unpublish</Link> <Link quiet on:click={unpublishApp}>Unpublish</Link>
</span> </span>
<span class="revert-link"> <span class="revert-link">
<Link <AbsTooltip
disabled={!$isOnlyUser} text={$isOnlyUser
quiet ? null
secondary : "Unavailable - another user is editing this app"}
on:click={revertApp}
tooltip="Unavailable - another user is editing this app"
> >
Revert <Link
</Link> disabled={!$isOnlyUser}
quiet
secondary
on:click={revertApp}
>
Revert
</Link>
</AbsTooltip>
</span> </span>
{:else} {:else}
<span class="status-text unpublished">Not published</span> <span class="status-text unpublished">Not published</span>

View File

@ -16,6 +16,7 @@
makeStateBinding, makeStateBinding,
} from "builderStore/dataBinding" } from "builderStore/dataBinding"
import { currentAsset, store } from "builderStore" import { currentAsset, store } from "builderStore"
import { cloneDeep } from "lodash/fp"
const flipDurationMs = 150 const flipDurationMs = 150
const EVENT_TYPE_KEY = "##eventHandlerType" const EVENT_TYPE_KEY = "##eventHandlerType"
@ -29,6 +30,26 @@
let actionQuery let actionQuery
let selectedAction = actions?.length ? actions[0] : null let selectedAction = actions?.length ? actions[0] : null
const setUpdateActions = actions => {
return actions
? cloneDeep(actions)
.filter(action => {
return (
action[EVENT_TYPE_KEY] === "Update State" &&
action.parameters?.type === "set" &&
action.parameters.key
)
})
.reduce((acc, action) => {
acc[action.id] = action
return acc
}, {})
: []
}
// Snapshot original action state
let updateStateActions = setUpdateActions(actions)
$: { $: {
// Ensure parameters object is never null // Ensure parameters object is never null
if (selectedAction && !selectedAction.parameters) { if (selectedAction && !selectedAction.parameters) {
@ -125,8 +146,9 @@
actions = e.detail.items actions = e.detail.items
} }
const getAllBindings = (bindings, eventContextBindings, actions) => { const getAllBindings = (actionBindings, eventContextBindings, actions) => {
let allBindings = [] let allBindings = []
let cloneActionBindings = cloneDeep(actionBindings)
if (!actions) { if (!actions) {
return [] return []
} }
@ -144,11 +166,19 @@
.forEach(action => { .forEach(action => {
// Check we have a binding for this action, and generate one if not // Check we have a binding for this action, and generate one if not
const stateBinding = makeStateBinding(action.parameters.key) const stateBinding = makeStateBinding(action.parameters.key)
const hasKey = bindings.some(binding => { const hasKey = actionBindings.some(binding => {
return binding.runtimeBinding === stateBinding.runtimeBinding return binding.runtimeBinding === stateBinding.runtimeBinding
}) })
if (!hasKey) { if (!hasKey) {
bindings.push(stateBinding) let existing = updateStateActions[action.id]
if (existing) {
const existingBinding = makeStateBinding(existing.parameters.key)
cloneActionBindings = cloneActionBindings.filter(
binding =>
binding.runtimeBinding !== existingBinding.runtimeBinding
)
}
allBindings.push(stateBinding)
} }
}) })
// Get which indexes are asynchronous automations as we want to filter them out from the bindings // Get which indexes are asynchronous automations as we want to filter them out from the bindings
@ -164,15 +194,16 @@
.filter(index => index !== undefined) .filter(index => index !== undefined)
// Based on the above, filter out the asynchronous automations from the bindings // Based on the above, filter out the asynchronous automations from the bindings
if (asynchronousAutomationIndexes) { let contextBindings = asynchronousAutomationIndexes
allBindings = eventContextBindings ? eventContextBindings.filter((binding, index) => {
.filter((binding, index) => {
return !asynchronousAutomationIndexes.includes(index) return !asynchronousAutomationIndexes.includes(index)
}) })
.concat(bindings) : eventContextBindings
} else {
allBindings = eventContextBindings.concat(bindings) allBindings = contextBindings
} .concat(cloneActionBindings)
.concat(allBindings)
return allBindings return allBindings
} }
</script> </script>

View File

@ -70,8 +70,9 @@
} set` } set`
</script> </script>
<div class="action-count">{actionText}</div> <div class="action-editor">
<ActionButton on:click={openDrawer}>Define actions</ActionButton> <ActionButton on:click={openDrawer}>{actionText}</ActionButton>
</div>
<Drawer bind:this={drawer} title={"Actions"}> <Drawer bind:this={drawer} title={"Actions"}>
<svelte:fragment slot="description"> <svelte:fragment slot="description">
@ -89,9 +90,7 @@
</Drawer> </Drawer>
<style> <style>
.action-count { .action-editor :global(.spectrum-ActionButton) {
padding-top: 6px; width: 100%;
padding-bottom: var(--spacing-s);
font-weight: 600;
} }
</style> </style>

View File

@ -20,6 +20,7 @@
let drawer let drawer
let boundValue let boundValue
$: text = getText(value)
$: datasource = getDatasourceForProvider($currentAsset, componentInstance) $: datasource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchema($currentAsset, datasource) $: schema = getSchema($currentAsset, datasource)
$: options = allowCellEditing $: options = allowCellEditing
@ -31,6 +32,17 @@
allowLinks: true, allowLinks: true,
}) })
const getText = value => {
if (!value?.length) {
return "All columns"
}
let text = `${value.length} column`
if (value.length !== 1) {
text += "s"
}
return text
}
const getSchema = (asset, datasource) => { const getSchema = (asset, datasource) => {
const schema = getSchemaForDatasource(asset, datasource).schema const schema = getSchemaForDatasource(asset, datasource).schema
@ -76,7 +88,7 @@
</script> </script>
<div class="column-editor"> <div class="column-editor">
<ActionButton on:click={open}>Configure columns</ActionButton> <ActionButton on:click={open}>{text}</ActionButton>
</div> </div>
<Drawer bind:this={drawer} title="Columns"> <Drawer bind:this={drawer} title="Columns">
<Button cta slot="buttons" on:click={save}>Save</Button> <Button cta slot="buttons" on:click={save}>Save</Button>

View File

@ -12,25 +12,36 @@
export let componentInstance export let componentInstance
export let value = [] export let value = []
const convertOldColumnFormat = oldColumns => {
if (typeof oldColumns?.[0] === "string") {
value = oldColumns.map(field => ({ name: field, displayName: field }))
}
}
$: convertOldColumnFormat(value)
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
let drawer let drawer
let boundValue let boundValue
$: text = getText(value)
$: convertOldColumnFormat(value)
$: datasource = getDatasourceForProvider($currentAsset, componentInstance) $: datasource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchema($currentAsset, datasource) $: schema = getSchema($currentAsset, datasource)
$: options = Object.keys(schema || {}) $: options = Object.keys(schema || {})
$: sanitisedValue = getValidColumns(value, options) $: sanitisedValue = getValidColumns(value, options)
$: updateBoundValue(sanitisedValue) $: updateBoundValue(sanitisedValue)
const getText = value => {
if (!value?.length) {
return "All fields"
}
let text = `${value.length} field`
if (value.length !== 1) {
text += "s"
}
return text
}
const convertOldColumnFormat = oldColumns => {
if (typeof oldColumns?.[0] === "string") {
value = oldColumns.map(field => ({ name: field, displayName: field }))
}
}
const getSchema = (asset, datasource) => { const getSchema = (asset, datasource) => {
const schema = getSchemaForDatasource(asset, datasource).schema const schema = getSchemaForDatasource(asset, datasource).schema
@ -75,7 +86,10 @@
} }
</script> </script>
<ActionButton on:click={open}>Configure fields</ActionButton> <div class="field-configuration">
<ActionButton on:click={open}>{text}</ActionButton>
</div>
<Drawer bind:this={drawer} title="Form Fields"> <Drawer bind:this={drawer} title="Form Fields">
<svelte:fragment slot="description"> <svelte:fragment slot="description">
Configure the fields in your form. Configure the fields in your form.
@ -83,3 +97,9 @@
<Button cta slot="buttons" on:click={save}>Save</Button> <Button cta slot="buttons" on:click={save}>Save</Button>
<ColumnDrawer slot="body" bind:columns={boundValue} {options} {schema} /> <ColumnDrawer slot="body" bind:columns={boundValue} {options} {schema} />
</Drawer> </Drawer>
<style>
.field-configuration :global(.spectrum-ActionButton) {
width: 100%;
}
</style>

View File

@ -20,7 +20,7 @@
$: datasource = getDatasourceForProvider($currentAsset, componentInstance) $: datasource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchemaForDatasource($currentAsset, datasource)?.schema $: schema = getSchemaForDatasource($currentAsset, datasource)?.schema
$: schemaFields = Object.values(schema || {}) $: schemaFields = Object.values(schema || {})
$: text = getText(value) $: text = getText(value?.filter(filter => filter.field))
async function saveFilter() { async function saveFilter() {
dispatch("change", tempValue) dispatch("change", tempValue)

View File

@ -1,11 +1,8 @@
<script> <script>
import { Tooltip } from "@budibase/bbui"
export let text export let text
export let url export let url
export let active = false export let active = false
export let disabled = false export let disabled = false
export let tooltip = null
</script> </script>
<div class="side-nav-item"> <div class="side-nav-item">
@ -18,11 +15,6 @@
{text || ""} {text || ""}
</div> </div>
{/if} {/if}
{#if tooltip}
<div class="tooltip">
<Tooltip textWrapping direction="right" text={tooltip} />
</div>
{/if}
</div> </div>
<style> <style>
@ -45,17 +37,4 @@
pointer-events: none; pointer-events: none;
color: var(--spectrum-global-color-gray-500) !important; color: var(--spectrum-global-color-gray-500) !important;
} }
.tooltip {
position: absolute;
transform: translateY(-50%);
left: 100%;
top: 50%;
opacity: 0;
pointer-events: none;
transition: opacity 130ms ease-out;
z-index: 100;
}
.side-nav-item:hover .tooltip {
opacity: 1;
}
</style> </style>

View File

@ -15,8 +15,6 @@
import { createValidationStore } from "helpers/validation/yup" import { createValidationStore } from "helpers/validation/yup"
import * as appValidation from "helpers/validation/yup/app" import * as appValidation from "helpers/validation/yup/app"
import TemplateCard from "components/common/TemplateCard.svelte" import TemplateCard from "components/common/TemplateCard.svelte"
import createFromScratchScreen from "builderStore/store/screenTemplates/createFromScratchScreen"
import { Roles } from "constants/backend"
import { lowercase } from "helpers" import { lowercase } from "helpers"
export let template export let template
@ -142,21 +140,6 @@
// Create user // Create user
await auth.setInitInfo({}) await auth.setInitInfo({})
// Create a default home screen if no template was selected
if (template == null) {
let defaultScreenTemplate = createFromScratchScreen.create()
defaultScreenTemplate.routing.route = "/home"
defaultScreenTemplate.routing.roldId = Roles.BASIC
try {
await store.actions.screens.save(defaultScreenTemplate)
} catch (err) {
console.error("Could not create a default application screen", err)
notifications.warning(
"Encountered an issue creating the default screen."
)
}
}
$goto(`/builder/app/${createdApp.instance._id}`) $goto(`/builder/app/${createdApp.instance._id}`)
} catch (error) { } catch (error) {
creating = false creating = false

View File

@ -15,6 +15,7 @@
} }
onMount(() => { onMount(() => {
window.isBuilder = true
window.closePreview = () => { window.closePreview = () => {
store.update(state => ({ store.update(state => ({
...state, ...state,

View File

@ -1,9 +1,14 @@
<script> <script>
import { UserAvatar } from "@budibase/frontend-core" import { UserAvatar } from "@budibase/frontend-core"
import { TooltipPosition, Avatar } from "@budibase/bbui"
export let users = [] export let users = []
export let order = "ltr"
export let size = "S"
export let tooltipPosition = TooltipPosition.Top
$: uniqueUsers = unique(users) $: uniqueUsers = unique(users, order)
$: avatars = getAvatars(uniqueUsers, order)
const unique = users => { const unique = users => {
let uniqueUsers = {} let uniqueUsers = {}
@ -12,17 +17,51 @@
}) })
return Object.values(uniqueUsers) return Object.values(uniqueUsers)
} }
const getAvatars = (users, order) => {
const avatars = users.slice(0, 3)
if (users.length > 3) {
const overflow = {
_id: "overflow",
label: `+${users.length - 3}`,
}
if (order === "ltr") {
avatars.push(overflow)
} else {
avatars.unshift(overflow)
}
}
return avatars.map((user, idx) => ({
...user,
zIndex: order === "ltr" ? idx : uniqueUsers.length - idx,
}))
}
</script> </script>
<div class="avatars"> <div class="avatars">
{#each uniqueUsers as user} {#each avatars as user}
<UserAvatar {user} tooltipDirection="bottom" /> <span style="z-index:{user.zIndex};">
{#if user._id === "overflow"}
<Avatar
{size}
initials={user.label}
color="var(--spectrum-global-color-gray-500)"
/>
{:else}
<UserAvatar {size} {user} {tooltipPosition} />
{/if}
</span>
{/each} {/each}
</div> </div>
<style> <style>
.avatars { .avatars {
display: flex; display: flex;
gap: 4px; }
span:not(:first-of-type) {
margin-left: -6px;
}
.avatars :global(.spectrum-Avatar) {
border: 2px solid var(--avatars-background, var(--background));
} }
</style> </style>

View File

@ -15,6 +15,7 @@
Heading, Heading,
Modal, Modal,
notifications, notifications,
TooltipPosition,
} from "@budibase/bbui" } from "@budibase/bbui"
import AppActions from "components/deploy/AppActions.svelte" import AppActions from "components/deploy/AppActions.svelte"
import { API } from "api" import { API } from "api"
@ -172,7 +173,11 @@
</div> </div>
<div class="toprightnav"> <div class="toprightnav">
<span> <span>
<UserAvatars users={$userStore} /> <UserAvatars
users={$userStore}
order="rtl"
tooltipPosition={TooltipPosition.Bottom}
/>
</span> </span>
<AppActions {application} {loaded} /> <AppActions {application} {loaded} />
</div> </div>

View File

@ -2,11 +2,12 @@
import { Button, Modal } from "@budibase/bbui" import { Button, Modal } from "@budibase/bbui"
import ImportQueriesModal from "./RestImportQueriesModal.svelte" import ImportQueriesModal from "./RestImportQueriesModal.svelte"
export let datasourceId
let importQueriesModal let importQueriesModal
</script> </script>
<Modal bind:this={importQueriesModal}> <Modal bind:this={importQueriesModal}>
<ImportQueriesModal createDatasource={false} datasourceId={"todo"} /> <ImportQueriesModal createDatasource={false} {datasourceId} />
</Modal> </Modal>
<div class="button"> <div class="button">

View File

@ -7,14 +7,14 @@
} from "stores/backend" } from "stores/backend"
import { hasData } from "stores/selectors" import { hasData } from "stores/selectors"
import { Icon, notifications, Heading, Body } from "@budibase/bbui" import { notifications, Body, Icon, AbsTooltip } from "@budibase/bbui"
import { params, goto } from "@roxi/routify" import { params, goto } from "@roxi/routify"
import CreateExternalDatasourceModal from "./_components/CreateExternalDatasourceModal/index.svelte" import CreateExternalDatasourceModal from "./_components/CreateExternalDatasourceModal/index.svelte"
import CreateInternalTableModal from "./_components/CreateInternalTableModal.svelte" import CreateInternalTableModal from "./_components/CreateInternalTableModal.svelte"
import DatasourceOption from "./_components/DatasourceOption.svelte" import DatasourceOption from "./_components/DatasourceOption.svelte"
import IntegrationIcon from "components/backend/DatasourceNavigator/IntegrationIcon.svelte" import IntegrationIcon from "components/backend/DatasourceNavigator/IntegrationIcon.svelte"
import CreationPage from "components/common/CreationPage.svelte"
import ICONS from "components/backend/DatasourceNavigator/icons/index.js" import ICONS from "components/backend/DatasourceNavigator/icons/index.js"
import FontAwesomeIcon from "components/common/FontAwesomeIcon.svelte"
let internalTableModal let internalTableModal
let externalDatasourceModal let externalDatasourceModal
@ -46,25 +46,16 @@
bind:this={externalDatasourceModal} bind:this={externalDatasourceModal}
/> />
<div class="page"> <CreationPage
<div class="closeButton"> showClose={hasData($datasources, $tables)}
{#if hasData($datasources, $tables)} onClose={() => $goto("./table")}
<Icon hoverable name="Close" on:click={$goto("./table")} /> heading="Add new data source"
{/if} >
</div>
<div class="heading">
<Heading weight="light">Add new data source</Heading>
</div>
<div class="subHeading"> <div class="subHeading">
<Body>Get started with our Budibase DB</Body> <Body>Get started with our Budibase DB</Body>
<div <AbsTooltip text="Budibase DB is built with CouchDB">
role="tooltip" <Icon name="Info" size="S" />
title="Budibase DB is built with CouchDB" </AbsTooltip>
class="tooltip"
>
<FontAwesomeIcon name="fa-solid fa-circle-info" />
</div>
</div> </div>
<div class="options"> <div class="options">
@ -113,37 +104,19 @@
</DatasourceOption> </DatasourceOption>
{/each} {/each}
</div> </div>
</div> </CreationPage>
<style> <style>
.page {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.closeButton {
height: 38px;
display: flex;
justify-content: right;
width: 100%;
}
.heading {
margin-bottom: 12px;
}
.subHeading { .subHeading {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 24px; margin-top: 12px;
margin-bottom: 36px;
gap: 8px;
} }
.subHeading :global(p) {
.tooltip { color: var(--spectrum-global-color-gray-600) !important;
margin-left: 6px;
} }
.options { .options {
width: 100%; width: 100%;
display: grid; display: grid;

View File

@ -1,165 +0,0 @@
<script>
import { tables } from "stores/backend"
import { ModalContent, Body, Layout, Icon, Heading } from "@budibase/bbui"
import blankScreenPreview from "./blankScreenPreview.png"
import listScreenPreview from "./listScreenPreview.png"
export let onConfirm
export let onCancel
let listScreenModeKey = "autoCreate"
let blankScreenModeKey = "blankScreen"
let selectedScreenMode
const confirmScreenSelection = async () => {
await onConfirm(selectedScreenMode)
}
</script>
<div>
<ModalContent
title="Add screens"
confirmText="Continue"
cancelText="Cancel"
onConfirm={confirmScreenSelection}
{onCancel}
disabled={!selectedScreenMode}
size="M"
>
<Layout noPadding gap="S">
<div
class="screen-type item blankView"
class:selected={selectedScreenMode == blankScreenModeKey}
on:click={() => {
selectedScreenMode = blankScreenModeKey
}}
>
<div class="content screen-type-wrap">
<img
alt="blank screen preview"
class="preview"
src={blankScreenPreview}
/>
<div class="screen-type-text">
<Heading size="XS">Blank screen</Heading>
<Body size="S">Add an empty blank screen</Body>
</div>
</div>
<div
style="color: var(--spectrum-global-color-green-600); float: right"
>
<div
class={`checkmark-spacing ${
selectedScreenMode == blankScreenModeKey ? "visible" : ""
}`}
>
<Icon size="S" name="CheckmarkCircle" />
</div>
</div>
</div>
<div class="listViewTitle">
<Heading size="XS">Quickly create a screen from your data</Heading>
</div>
<div
class="screen-type item"
class:selected={selectedScreenMode == listScreenModeKey}
on:click={() => {
selectedScreenMode = listScreenModeKey
}}
class:disabled={!$tables.list.filter(table => table._id !== "ta_users")
.length}
>
<div class="content screen-type-wrap">
<img
alt="list screen preview"
class="preview"
src={listScreenPreview}
/>
<div class="screen-type-text">
<Heading size="XS">List view</Heading>
<Body size="S">
Create, edit and view your data in a list view screen with side
panel
</Body>
</div>
</div>
<div
style="color: var(--spectrum-global-color-green-600); float: right"
>
<div
class={`checkmark-spacing ${
selectedScreenMode == listScreenModeKey ? "visible" : ""
}`}
>
<Icon size="S" name="CheckmarkCircle" />
</div>
</div>
</div>
</Layout>
</ModalContent>
</div>
<style>
.screen-type-wrap {
display: flex;
flex-direction: row;
align-items: center;
}
.disabled {
opacity: 0.3;
pointer-events: none;
}
.checkmark-spacing {
margin-right: var(--spacing-m);
opacity: 0;
}
.content {
letter-spacing: 0px;
}
.item {
cursor: pointer;
grid-gap: var(--spectrum-alias-grid-margin-xsmall);
background: var(--spectrum-alias-background-color-secondary);
transition: 0.3s all;
border: 1px solid var(--spectrum-global-color-gray-300);
border-radius: 4px;
border-width: 1px;
display: flex;
justify-content: space-between;
align-items: center;
}
.item:hover,
.selected {
background: var(--spectrum-alias-background-color-tertiary);
}
.screen-type-wrap .screen-type-text {
padding-left: var(--spectrum-alias-item-padding-xl);
}
.screen-type-wrap .screen-type-text :global(h1) {
padding-bottom: var(--spacing-xs);
}
.screen-type-wrap :global(.spectrum-Icon) {
min-width: var(--spectrum-icon-size-m);
}
.screen-type-wrap :global(.spectrum-Heading) {
padding-bottom: var(--spectrum-alias-item-padding-s);
}
.preview {
width: 140px;
}
.listViewTitle {
margin-top: 35px;
}
.blankView {
margin-top: 10px;
}
.visible {
opacity: 1;
}
</style>

View File

@ -9,7 +9,7 @@
Helpers, Helpers,
notifications, notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import ScreenDetailsModal from "./ScreenDetailsModal.svelte" import ScreenDetailsModal from "components/design/ScreenDetailsModal.svelte"
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl" import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl"
import { makeComponentUnique } from "builderStore/componentUtils" import { makeComponentUnique } from "builderStore/componentUtils"

View File

@ -1,17 +1,16 @@
<script> <script>
import { Search, Layout, Select, Body, Button } from "@budibase/bbui" import { Search, Layout, Select, Body, Button } from "@budibase/bbui"
import Panel from "components/design/Panel.svelte" import Panel from "components/design/Panel.svelte"
import { goto } from "@roxi/routify"
import { roles } from "stores/backend" import { roles } from "stores/backend"
import { store, sortedScreens, userSelectedResourceMap } from "builderStore" import { store, sortedScreens, userSelectedResourceMap } from "builderStore"
import NavItem from "components/common/NavItem.svelte" import NavItem from "components/common/NavItem.svelte"
import ScreenDropdownMenu from "./ScreenDropdownMenu.svelte" import ScreenDropdownMenu from "./ScreenDropdownMenu.svelte"
import ScreenWizard from "./ScreenWizard.svelte"
import RoleIndicator from "./RoleIndicator.svelte" import RoleIndicator from "./RoleIndicator.svelte"
import { RoleUtils } from "@budibase/frontend-core" import { RoleUtils } from "@budibase/frontend-core"
let searchString let searchString
let accessRole = "all" let accessRole = "all"
let showNewScreenModal
$: filteredScreens = getFilteredScreens( $: filteredScreens = getFilteredScreens(
$sortedScreens, $sortedScreens,
@ -31,7 +30,7 @@
<Panel title="Screens" borderRight> <Panel title="Screens" borderRight>
<Layout paddingX="L" paddingY="XL" gap="S"> <Layout paddingX="L" paddingY="XL" gap="S">
<Button on:click={showNewScreenModal} cta>Add screen</Button> <Button on:click={() => $goto("../../new")} cta>Add screen</Button>
<Search <Search
placeholder="Search" placeholder="Search"
value={searchString} value={searchString}
@ -74,5 +73,3 @@
</Layout> </Layout>
{/if} {/if}
</Panel> </Panel>
<ScreenWizard bind:showModal={showNewScreenModal} />

View File

@ -1,6 +1,5 @@
<script> <script>
import ScreenDetailsModal from "./ScreenDetailsModal.svelte" import ScreenDetailsModal from "components/design/ScreenDetailsModal.svelte"
import NewScreenModal from "./NewScreenModal.svelte"
import DatasourceModal from "./DatasourceModal.svelte" import DatasourceModal from "./DatasourceModal.svelte"
import ScreenRoleModal from "./ScreenRoleModal.svelte" import ScreenRoleModal from "./ScreenRoleModal.svelte"
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl" import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl"
@ -11,11 +10,11 @@
import { tables } from "stores/backend" import { tables } from "stores/backend"
import { Roles } from "constants/backend" import { Roles } from "constants/backend"
import { capitalise } from "helpers" import { capitalise } from "helpers"
import { goto } from "@roxi/routify"
let pendingScreen let pendingScreen
// Modal refs // Modal refs
let newScreenModal
let screenDetailsModal let screenDetailsModal
let datasourceModal let datasourceModal
let screenAccessRoleModal let screenAccessRoleModal
@ -26,16 +25,6 @@
let blankScreenUrl = null let blankScreenUrl = null
let screenMode = null let screenMode = null
// External handler to show the screen wizard
export const showModal = () => {
selectedTemplates = null
blankScreenUrl = null
screenMode = null
pendingScreen = null
screenAccessRole = Roles.BASIC
newScreenModal.show()
}
// Creates an array of screens, checking and sanitising their URLs // Creates an array of screens, checking and sanitising their URLs
const createScreens = async ({ screens, screenAccessRole }) => { const createScreens = async ({ screens, screenAccessRole }) => {
if (!screens?.length) { if (!screens?.length) {
@ -43,6 +32,8 @@
} }
try { try {
let screenId
for (let screen of screens) { for (let screen of screens) {
// Check we aren't clashing with an existing URL // Check we aren't clashing with an existing URL
if (hasExistingUrl(screen.routing.route)) { if (hasExistingUrl(screen.routing.route)) {
@ -64,7 +55,8 @@
screen.routing.roleId = screenAccessRole screen.routing.roleId = screenAccessRole
// Create the screen // Create the screen
await store.actions.screens.save(screen) const response = await store.actions.screens.save(screen)
screenId = response._id
// Add link in layout for list screens // Add link in layout for list screens
if (screen.props._instanceName.endsWith("List")) { if (screen.props._instanceName.endsWith("List")) {
@ -74,7 +66,10 @@
) )
} }
} }
$goto(`./${screenId}`)
} catch (error) { } catch (error) {
console.log(error)
notifications.error("Error creating screens") notifications.error("Error creating screens")
} }
} }
@ -104,18 +99,24 @@
} }
// Handler for NewScreenModal // Handler for NewScreenModal
const confirmScreenSelection = async mode => { export const show = mode => {
selectedTemplates = null
blankScreenUrl = null
screenMode = mode screenMode = mode
pendingScreen = null
screenAccessRole = Roles.BASIC
if (mode === "autoCreate") { if (mode === "table") {
datasourceModal.show() datasourceModal.show()
} else { } else if (mode === "blank") {
let templates = getTemplates($store, $tables.list) let templates = getTemplates($store, $tables.list)
const blankScreenTemplate = templates.find( const blankScreenTemplate = templates.find(
t => t.id === "createFromScratch" t => t.id === "createFromScratch"
) )
pendingScreen = blankScreenTemplate.create() pendingScreen = blankScreenTemplate.create()
screenDetailsModal.show() screenDetailsModal.show()
} else {
throw new Error("Invalid mode provided")
} }
} }
@ -155,7 +156,7 @@
// Submit screen config for creation. // Submit screen config for creation.
const confirmScreenCreation = async () => { const confirmScreenCreation = async () => {
if (screenMode === "blankScreen") { if (screenMode === "blank") {
confirmBlankScreenCreation({ confirmBlankScreenCreation({
screenUrl: blankScreenUrl, screenUrl: blankScreenUrl,
screenAccessRole, screenAccessRole,
@ -166,7 +167,7 @@
} }
const roleSelectBack = () => { const roleSelectBack = () => {
if (screenMode === "blankScreen") { if (screenMode === "blank") {
screenDetailsModal.show() screenDetailsModal.show()
} else { } else {
datasourceModal.show() datasourceModal.show()
@ -174,14 +175,9 @@
} }
</script> </script>
<Modal bind:this={newScreenModal}>
<NewScreenModal onConfirm={confirmScreenSelection} />
</Modal>
<Modal bind:this={datasourceModal}> <Modal bind:this={datasourceModal}>
<DatasourceModal <DatasourceModal
onConfirm={confirmScreenDatasources} onConfirm={confirmScreenDatasources}
onCancel={() => newScreenModal.show()}
initalScreens={!selectedTemplates ? [] : [...selectedTemplates]} initalScreens={!selectedTemplates ? [] : [...selectedTemplates]}
/> />
</Modal> </Modal>
@ -198,7 +194,6 @@
<Modal bind:this={screenDetailsModal}> <Modal bind:this={screenDetailsModal}>
<ScreenDetailsModal <ScreenDetailsModal
onConfirm={confirmScreenBlank} onConfirm={confirmScreenBlank}
onCancel={() => newScreenModal.show()}
initialUrl={blankScreenUrl} initialUrl={blankScreenUrl}
/> />
</Modal> </Modal>

View File

@ -40,14 +40,14 @@
</script> </script>
<ModalContent <ModalContent
title="Autogenerated screens" title="Access"
confirmText="Done" confirmText="Done"
cancelText="Back" cancelText="Back"
{onConfirm} {onConfirm}
{onCancel} {onCancel}
disabled={!!error} disabled={!!error}
> >
Select which level of access you want your screens to have Select the level of access required to see these screens
<Select <Select
bind:value={screenAccessRole} bind:value={screenAccessRole}
on:change={onChangeRole} on:change={onChangeRole}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -1,67 +1,12 @@
<script> <script>
import { store, selectedScreen } from "builderStore"
import { onMount } from "svelte"
import { redirect } from "@roxi/routify" import { redirect } from "@roxi/routify"
import { Layout, Button, Detail, notifications } from "@budibase/bbui" import { store as frontendStore } from "builderStore"
import Logo from "assets/bb-space-man.svg"
import createFromScratchScreen from "builderStore/store/screenTemplates/createFromScratchScreen"
import { Roles } from "constants/backend"
let loaded = false $: {
if ($frontendStore.screens.length > 0) {
const createFirstScreen = async () => { $redirect(`./${$frontendStore.screens[0]._id}`)
let screen = createFromScratchScreen.create()
screen.routing.route = "/home"
screen.routing.roldId = Roles.BASIC
screen.routing.homeScreen = true
try {
const savedScreen = await store.actions.screens.save(screen)
notifications.success("Screen created successfully")
$redirect(`./${savedScreen._id}`)
} catch (err) {
console.error("Could not create screen", err)
notifications.error("Error creating screen")
}
}
onMount(() => {
if ($selectedScreen) {
$redirect(`./${$selectedScreen._id}`)
} else if ($store.screens?.length) {
$redirect(`./${$store.screens[0]._id}`)
} else { } else {
loaded = true $redirect("./new")
} }
}) }
</script> </script>
{#if loaded}
<div class="centered">
<Layout gap="S" justifyItems="center">
<img class="img-size" alt="logo" src={Logo} />
<div class="new-screen-text">
<Detail size="L">LETS BRING THIS APP TO LIFE</Detail>
</div>
<Button on:click={createFirstScreen} size="M" cta icon="Add">
Create first screen
</Button>
</Layout>
</div>
{/if}
<style>
.centered {
width: 100%;
height: 100%;
display: grid;
place-items: center;
}
.new-screen-text {
width: 150px;
text-align: center;
font-weight: 600;
}
.img-size {
width: 170px;
}
</style>

View File

@ -0,0 +1,104 @@
<script>
import { Body } from "@budibase/bbui"
import CreationPage from "components/common/CreationPage.svelte"
import blankImage from "./blank.png"
import tableImage from "./table.png"
import CreateScreenModal from "./_components/CreateScreenModal.svelte"
import { store } from "builderStore"
import { goto } from "@roxi/routify"
let createScreenModal
$: hasScreens = $store.screens?.length
</script>
<div class="page">
<CreationPage
showClose={$store.screens.length > 0}
onClose={() => $goto(`./${$store.screens[0]._id}`)}
heading={hasScreens ? "Create new screen" : "Create your first screen"}
>
<div class="subHeading">
<Body>Start from scratch or create screens from your data</Body>
</div>
<div class="cards">
<div class="card" on:click={() => createScreenModal.show("blank")}>
<div class="image">
<img alt="" src={blankImage} />
</div>
<div class="text">
<Body size="S">Blank screen</Body>
<Body size="XS">Add an empty blank screen</Body>
</div>
</div>
<div class="card" on:click={() => createScreenModal.show("table")}>
<div class="image">
<img alt="" src={tableImage} />
</div>
<div class="text">
<Body size="S">Table</Body>
<Body size="XS">View, edit and delete rows on a table</Body>
</div>
</div>
</div>
</CreationPage>
</div>
<CreateScreenModal bind:this={createScreenModal} />
<style>
.page {
padding: 28px 40px 40px 40px;
}
.subHeading :global(p) {
text-align: center;
margin-top: 12px;
margin-bottom: 36px;
color: var(--spectrum-global-color-gray-600);
}
.cards {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 24px;
}
.card {
max-width: 235px;
transition: filter 150ms;
}
.card:hover {
filter: brightness(1.1);
cursor: pointer;
}
.image {
border-radius: 4px 4px 0 0;
width: 100%;
max-height: 127px;
overflow: hidden;
}
.image img {
width: 100%;
}
.text {
border: 1px solid var(--grey-4);
border-radius: 0 0 4px 4px;
padding: 8px 16px 13px 16px;
}
.text :global(p:nth-child(1)) {
margin-bottom: 6px;
}
.text :global(p:nth-child(2)) {
color: var(--grey-6);
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,6 +1,6 @@
<script> <script>
import { Content, SideNav, SideNavItem } from "components/portal/page" import { Content, SideNav, SideNavItem } from "components/portal/page"
import { Page, Layout } from "@budibase/bbui" import { Page, Layout, AbsTooltip, TooltipPosition } from "@budibase/bbui"
import { url, isActive } from "@roxi/routify" import { url, isActive } from "@roxi/routify"
import DeleteModal from "components/deploy/DeleteModal.svelte" import DeleteModal from "components/deploy/DeleteModal.svelte"
import { isOnlyUser } from "builderStore" import { isOnlyUser } from "builderStore"
@ -45,16 +45,20 @@
active={$isActive("./version")} active={$isActive("./version")}
/> />
<div class="delete-action"> <div class="delete-action">
<SideNavItem <AbsTooltip
text="Delete app" position={TooltipPosition.Bottom}
on:click={() => { text={$isOnlyUser
deleteModal.show()
}}
disabled={!$isOnlyUser}
tooltip={$isOnlyUser
? null ? null
: "Unavailable - another user is editing this app"} : "Unavailable - another user is editing this app"}
/> >
<SideNavItem
text="Delete app"
disabled={!$isOnlyUser}
on:click={() => {
deleteModal.show()
}}
/>
</AbsTooltip>
</div> </div>
</SideNav> </SideNav>
<slot /> <slot />

View File

@ -6,6 +6,8 @@
Heading, Heading,
Body, Body,
Modal, Modal,
AbsTooltip,
TooltipPosition,
} from "@budibase/bbui" } from "@budibase/bbui"
import ConfirmDialog from "components/common/ConfirmDialog.svelte" import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import CreateRestoreModal from "./CreateRestoreModal.svelte" import CreateRestoreModal from "./CreateRestoreModal.svelte"
@ -46,16 +48,18 @@
</div> </div>
{#if row.type !== "restore"} {#if row.type !== "restore"}
<MenuItem <AbsTooltip
on:click={restoreDialog.show} position={TooltipPosition.Left}
icon="Revert" text="Unavailable - another user is editing this app"
disabled={!$isOnlyUser}
tooltip={$isOnlyUser
? null
: "Unavailable - another user is editing this app"}
> >
Restore <MenuItem
</MenuItem> on:click={restoreDialog.show}
icon="Revert"
disabled={!$isOnlyUser}
>
Restore
</MenuItem>
</AbsTooltip>
<MenuItem on:click={deleteDialog.show} icon="Delete">Delete</MenuItem> <MenuItem on:click={deleteDialog.show} icon="Delete">Delete</MenuItem>
<MenuItem on:click={downloadExport} icon="Download">Download</MenuItem> <MenuItem on:click={downloadExport} icon="Download">Download</MenuItem>
{/if} {/if}

View File

@ -0,0 +1,40 @@
<script>
import { Layout, Body, Button } from "@budibase/bbui"
import { downloadStream } from "@budibase/frontend-core"
import Spinner from "components/common/Spinner.svelte"
import { API } from "api"
let loading = false
async function download() {
loading = true
try {
await downloadStream(await API.getSystemLogs())
} finally {
loading = false
}
}
</script>
<Layout noPadding>
<Body>Download your latest logs to share with the Budibase team</Body>
<div class="download-button">
<Button cta on:click={download} disabled={loading}>
<div class="button-content">
{#if loading}
<Spinner size="10" />
{/if}
Download system logs
</div>
</Button>
</div>
</Layout>
<style>
.button-content {
display: flex;
align-items: center;
gap: var(--spacing-m);
}
</style>

View File

@ -7,8 +7,6 @@
import { API } from "api" import { API } from "api"
import { store, automationStore } from "builderStore" import { store, automationStore } from "builderStore"
import { auth, admin } from "stores/portal" import { auth, admin } from "stores/portal"
import createFromScratchScreen from "builderStore/store/screenTemplates/createFromScratchScreen"
import { Roles } from "constants/backend"
let name = "My first app" let name = "My first app"
let url = "my-first-app" let url = "my-first-app"
@ -38,11 +36,6 @@
// Create user // Create user
await auth.setInitInfo({}) await auth.setInitInfo({})
let defaultScreenTemplate = createFromScratchScreen.create()
defaultScreenTemplate.routing.route = "/home"
defaultScreenTemplate.routing.roldId = Roles.BASIC
await store.actions.screens.save(defaultScreenTemplate)
appId = createdApp.instance._id appId = createdApp.instance._id
return createdApp return createdApp
} }

View File

@ -85,6 +85,13 @@ export const menu = derived([admin, auth], ([$admin, $auth]) => {
title: "Audit Logs", title: "Audit Logs",
href: "/builder/portal/account/auditLogs", href: "/builder/portal/account/auditLogs",
}) })
if (!$admin.cloud) {
accountSubPages.push({
title: "System Logs",
href: "/builder/portal/account/systemLogs",
})
}
} }
if ($admin.cloud && $auth?.user?.accountPortalAccess) { if ($admin.cloud && $auth?.user?.accountPortalAccess) {
accountSubPages.push({ accountSubPages.push({

View File

@ -3223,6 +3223,46 @@
"key": "allowManualEntry", "key": "allowManualEntry",
"defaultValue": false "defaultValue": false
}, },
{
"type": "boolean",
"label": "Play sound on scan",
"key": "beepOnScan",
"defaultValue": false
},
{
"type": "select",
"label": "Sound pitch",
"key": "beepFrequency",
"dependsOn": "beepOnScan",
"defaultValue": 2637,
"options": [
{
"label": "Low",
"value": 2096
},
{
"label": "Regular",
"value": 2637
},
{
"label": "High",
"value": 3136
},
{ "label": "Custom", "value": "custom" }
]
},
{
"type": "number",
"label": "Sound frequency (Hz)",
"key": "customFrequency",
"defaultValue": 1046,
"min": 20,
"max": 8000,
"dependsOn": {
"setting": "beepFrequency",
"value": "custom"
}
},
{ {
"type": "validation/string", "type": "validation/string",
"label": "Validation", "label": "Validation",
@ -4541,6 +4581,16 @@
"setting": "clickBehaviour", "setting": "clickBehaviour",
"value": "details" "value": "details"
} }
},
{
"type": "boolean",
"label": "Hide notifications",
"key": "notificationOverride",
"defaultValue": false,
"dependsOn": {
"setting": "clickBehaviour",
"value": "details"
}
} }
] ]
}, },
@ -5178,6 +5228,16 @@
"value": "View", "value": "View",
"invert": true "invert": true
} }
},
{
"type": "boolean",
"label": "Hide notifications",
"key": "notificationOverride",
"defaultValue": false,
"dependsOn": {
"setting": "showSaveButton",
"value": true
}
} }
] ]
} }

View File

@ -30,6 +30,7 @@
export let sidePanelShowDelete export let sidePanelShowDelete
export let sidePanelSaveLabel export let sidePanelSaveLabel
export let sidePanelDeleteLabel export let sidePanelDeleteLabel
export let notificationOverride
const { fetchDatasourceSchema, API } = getContext("sdk") const { fetchDatasourceSchema, API } = getContext("sdk")
const stateKey = `ID_${generate()}` const stateKey = `ID_${generate()}`
@ -253,6 +254,7 @@
fields: sidePanelFields || normalFields, fields: sidePanelFields || normalFields,
title: editTitle, title: editTitle,
labelPosition: "left", labelPosition: "left",
notificationOverride,
}} }}
/> />
</BlockComponent> </BlockComponent>
@ -277,6 +279,7 @@
fields: sidePanelFields || normalFields, fields: sidePanelFields || normalFields,
title: "Create Row", title: "Create Row",
labelPosition: "left", labelPosition: "left",
notificationOverride,
}} }}
/> />
</BlockComponent> </BlockComponent>

View File

@ -19,6 +19,7 @@
export let rowId export let rowId
export let actionUrl export let actionUrl
export let noRowsMessage export let noRowsMessage
export let notificationOverride
const { fetchDatasourceSchema } = getContext("sdk") const { fetchDatasourceSchema } = getContext("sdk")
@ -87,6 +88,7 @@
showDeleteButton, showDeleteButton,
schema, schema,
repeaterId, repeaterId,
notificationOverride,
} }
const fetchSchema = async () => { const fetchSchema = async () => {

View File

@ -17,6 +17,7 @@
export let showDeleteButton export let showDeleteButton
export let schema export let schema
export let repeaterId export let repeaterId
export let notificationOverride
const FieldTypeToComponentMap = { const FieldTypeToComponentMap = {
string: "stringfield", string: "stringfield",
@ -47,6 +48,7 @@
parameters: { parameters: {
providerId: formId, providerId: formId,
tableId: dataSource?.tableId, tableId: dataSource?.tableId,
notificationOverride,
}, },
}, },
{ {

View File

@ -8,6 +8,10 @@
export let disabled = false export let disabled = false
export let allowManualEntry = false export let allowManualEntry = false
export let scanButtonText = "Scan code" export let scanButtonText = "Scan code"
export let beepOnScan = false
export let beepFrequency = 2637
export let customFrequency = 1046
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
let videoEle let videoEle
@ -21,8 +25,13 @@
fps: 25, fps: 25,
qrbox: { width: 250, height: 250 }, qrbox: { width: 250, height: 250 },
} }
const audioCtx = new (window.AudioContext || window.webkitAudioContext)()
const onScanSuccess = decodedText => { const onScanSuccess = decodedText => {
if (value != decodedText) { if (value != decodedText) {
if (beepOnScan) {
beep()
}
dispatch("change", decodedText) dispatch("change", decodedText)
} }
} }
@ -84,6 +93,27 @@
} }
camModal.hide() camModal.hide()
} }
const beep = () => {
const oscillator = audioCtx.createOscillator()
const gainNode = audioCtx.createGain()
oscillator.connect(gainNode)
gainNode.connect(audioCtx.destination)
const frequency =
beepFrequency === "custom" ? customFrequency : beepFrequency
oscillator.frequency.value = frequency
oscillator.type = "square"
const duration = 420
const endTime = audioCtx.currentTime + duration / 1000
gainNode.gain.setValueAtTime(1, audioCtx.currentTime)
gainNode.gain.exponentialRampToValueAtTime(0.001, endTime)
oscillator.start()
oscillator.stop(endTime)
}
</script> </script>
<div class="scanner-video-wrapper"> <div class="scanner-video-wrapper">

View File

@ -11,6 +11,9 @@
export let onChange export let onChange
export let allowManualEntry export let allowManualEntry
export let scanButtonText export let scanButtonText
export let beepOnScan
export let beepFrequency
export let customFrequency
let fieldState let fieldState
let fieldApi let fieldApi
@ -42,6 +45,9 @@
disabled={fieldState.disabled} disabled={fieldState.disabled}
{allowManualEntry} {allowManualEntry}
scanButtonText={scanText} scanButtonText={scanText}
{beepOnScan}
{beepFrequency}
{customFrequency}
/> />
{/if} {/if}
</Field> </Field>

View File

@ -1,6 +1,6 @@
<script> <script>
import { Heading, Select, ActionButton } from "@budibase/bbui" import { Heading, Select, ActionButton } from "@budibase/bbui"
import { devToolsStore } from "../../stores" import { devToolsStore, appStore } from "../../stores"
import { getContext } from "svelte" import { getContext } from "svelte"
const context = getContext("context") const context = getContext("context")
@ -45,27 +45,41 @@
icon="Code" icon="Code"
on:click={() => devToolsStore.actions.setVisible(!$devToolsStore.visible)} on:click={() => devToolsStore.actions.setVisible(!$devToolsStore.visible)}
> >
{$devToolsStore.visible ? "Close" : "Open"} DevTools DevTools
</ActionButton>
{/if}
{#if window.parent.isBuilder}
<ActionButton
quiet
icon="LinkOut"
on:click={() => {
window.parent.closePreview?.()
window.open(`/${$appStore.appId}`, "_blank")
}}
>
Fullscreen
</ActionButton>
<ActionButton
quiet
icon="Close"
on:click={() => window.parent.closePreview?.()}
>
Close
</ActionButton> </ActionButton>
{/if} {/if}
<ActionButton
quiet
icon="Close"
on:click={() => window.parent.closePreview?.()}
>
Close preview
</ActionButton>
</div> </div>
<style> <style>
.dev-preview-header { .dev-preview-header {
flex: 0 0 60px; flex: 0 0 60px;
display: grid;
align-items: center;
background-color: black; background-color: black;
padding: 0 var(--spacing-xl); padding: 0 var(--spacing-xl);
grid-template-columns: 1fr auto auto auto; display: flex;
grid-gap: var(--spacing-xl); align-items: center;
gap: var(--spacing-xl);
}
.dev-preview-header :global(.spectrum-Heading) {
flex: 1 1 auto;
} }
.dev-preview-header.mobile { .dev-preview-header.mobile {
grid-template-columns: 1fr auto auto; grid-template-columns: 1fr auto auto;

View File

@ -30,6 +30,7 @@ import { buildBackupsEndpoints } from "./backups"
import { buildEnvironmentVariableEndpoints } from "./environmentVariables" import { buildEnvironmentVariableEndpoints } from "./environmentVariables"
import { buildEventEndpoints } from "./events" import { buildEventEndpoints } from "./events"
import { buildAuditLogsEndpoints } from "./auditLogs" import { buildAuditLogsEndpoints } from "./auditLogs"
import { buildLogsEndpoints } from "./logs"
/** /**
* Random identifier to uniquely identify a session in a tab. This is * Random identifier to uniquely identify a session in a tab. This is
@ -277,5 +278,6 @@ export const createAPIClient = config => {
...buildEnvironmentVariableEndpoints(API), ...buildEnvironmentVariableEndpoints(API),
...buildEventEndpoints(API), ...buildEventEndpoints(API),
...buildAuditLogsEndpoints(API), ...buildAuditLogsEndpoints(API),
...buildLogsEndpoints(API),
} }
} }

View File

@ -0,0 +1,14 @@
export const buildLogsEndpoints = API => ({
/**
* Gets a stream for the system logs.
*/
getSystemLogs: async () => {
return await API.get({
url: "/api/system/logs",
json: false,
parseResponse: async response => {
return response
},
})
},
})

View File

@ -1,60 +1,23 @@
<script> <script>
import { Avatar, Tooltip } from "@budibase/bbui" import { Avatar, AbsTooltip, TooltipPosition } from "@budibase/bbui"
import { helpers } from "@budibase/shared-core" import { helpers } from "@budibase/shared-core"
export let user export let user
export let size export let size = "S"
export let tooltipDirection = "top" export let tooltipPosition = TooltipPosition.Top
export let showTooltip = true export let showTooltip = true
$: tooltipStyle = getTooltipStyle(tooltipDirection)
const getTooltipStyle = direction => {
if (!direction) {
return ""
}
if (direction === "top") {
return "transform: translateX(-50%) translateY(-100%);"
} else if (direction === "bottom") {
return "transform: translateX(-50%) translateY(100%);"
}
}
</script> </script>
{#if user} {#if user}
<div class="user-avatar"> <AbsTooltip
text={showTooltip ? helpers.getUserLabel(user) : null}
position={tooltipPosition}
color={helpers.getUserColor(user)}
>
<Avatar <Avatar
{size} {size}
initials={helpers.getUserInitials(user)} initials={helpers.getUserInitials(user)}
color={helpers.getUserColor(user)} color={helpers.getUserColor(user)}
/> />
{#if showTooltip} </AbsTooltip>
<div class="tooltip" style={tooltipStyle}>
<Tooltip
direction={tooltipDirection}
textWrapping
text={helpers.getUserLabel(user)}
size="S"
/>
</div>
{/if}
</div>
{/if} {/if}
<style>
.user-avatar {
position: relative;
}
.tooltip {
position: absolute;
top: 0;
left: 50%;
white-space: nowrap;
pointer-events: none;
opacity: 0;
transition: opacity 130ms ease-out;
}
.user-avatar:hover .tooltip {
opacity: 1;
}
</style>

View File

@ -1,16 +0,0 @@
<script>
import { ActionButton } from "@budibase/bbui"
import { getContext } from "svelte"
const { config, dispatch } = getContext("grid")
</script>
<ActionButton
icon="TableColumnAddRight"
quiet
size="M"
on:click={() => dispatch("add-column")}
disabled={!$config.allowSchemaChanges}
>
Add column
</ActionButton>

View File

@ -1,18 +0,0 @@
<script>
import { ActionButton } from "@budibase/bbui"
import { getContext } from "svelte"
const { dispatch, columns, stickyColumn, config, loaded } = getContext("grid")
</script>
<ActionButton
icon="TableRowAddBottom"
quiet
size="M"
on:click={() => dispatch("add-row-inline")}
disabled={!loaded ||
!$config.allowAddRows ||
(!$columns.length && !$stickyColumn)}
>
Add row
</ActionButton>

View File

@ -71,6 +71,7 @@
contentLines, contentLines,
gridFocused, gridFocused,
error, error,
canAddRows,
} = context } = context
// Keep config store up to date with props // Keep config store up to date with props
@ -143,7 +144,7 @@
<HeaderRow /> <HeaderRow />
<GridBody /> <GridBody />
</div> </div>
{#if allowAddRows} {#if $canAddRows}
<NewRow /> <NewRow />
{/if} {/if}
<div class="overlays"> <div class="overlays">

View File

@ -9,7 +9,7 @@
renderedRows, renderedRows,
renderedColumns, renderedColumns,
rowVerticalInversionIndex, rowVerticalInversionIndex,
config, canAddRows,
hoveredRowId, hoveredRowId,
dispatch, dispatch,
isDragging, isDragging,
@ -43,7 +43,7 @@
invertY={idx >= $rowVerticalInversionIndex} invertY={idx >= $rowVerticalInversionIndex}
/> />
{/each} {/each}
{#if $config.allowAddRows && $renderedColumns.length} {#if $canAddRows}
<div <div
class="blank" class="blank"
class:highlighted={$hoveredRowId === BlankRowID} class:highlighted={$hoveredRowId === BlankRowID}

View File

@ -2,7 +2,7 @@
import { getContext } from "svelte" import { getContext } from "svelte"
import GridScrollWrapper from "./GridScrollWrapper.svelte" import GridScrollWrapper from "./GridScrollWrapper.svelte"
import HeaderCell from "../cells/HeaderCell.svelte" import HeaderCell from "../cells/HeaderCell.svelte"
import { Icon } from "@budibase/bbui" import { Icon, TempTooltip, TooltipType } from "@budibase/bbui"
const { const {
renderedColumns, renderedColumns,
@ -11,10 +11,13 @@
hiddenColumnsWidth, hiddenColumnsWidth,
width, width,
config, config,
hasNonAutoColumn,
tableId,
loading,
} = getContext("grid") } = getContext("grid")
$: columnsWidth = $renderedColumns.reduce( $: columnsWidth = $renderedColumns.reduce(
(total, col) => (total += col.width), (total, col) => total + col.width,
0 0
) )
$: end = $hiddenColumnsWidth + columnsWidth - 1 - $scroll.left $: end = $hiddenColumnsWidth + columnsWidth - 1 - $scroll.left
@ -30,13 +33,21 @@
</div> </div>
</GridScrollWrapper> </GridScrollWrapper>
{#if $config.allowSchemaChanges} {#if $config.allowSchemaChanges}
<div {#key $tableId}
class="add" <TempTooltip
style="left:{left}px" text="Click here to create your first column"
on:click={() => dispatch("add-column")} type={TooltipType.Info}
> condition={!$hasNonAutoColumn && !$loading}
<Icon name="Add" /> >
</div> <div
class="add"
style="left:{left}px;"
on:click={() => dispatch("add-column")}
>
<Icon name="Add" />
</div>
</TempTooltip>
{/key}
{/if} {/if}
</div> </div>

View File

@ -1,6 +1,6 @@
<script> <script>
import { getContext, onDestroy, onMount, tick } from "svelte" import { getContext, onDestroy, onMount, tick } from "svelte"
import { Icon, Button } from "@budibase/bbui" import { Icon, Button, TempTooltip, TooltipType } from "@budibase/bbui"
import GridScrollWrapper from "./GridScrollWrapper.svelte" import GridScrollWrapper from "./GridScrollWrapper.svelte"
import DataCell from "../cells/DataCell.svelte" import DataCell from "../cells/DataCell.svelte"
import { fade } from "svelte/transition" import { fade } from "svelte/transition"
@ -27,7 +27,8 @@
rowVerticalInversionIndex, rowVerticalInversionIndex,
columnHorizontalInversionIndex, columnHorizontalInversionIndex,
selectedRows, selectedRows,
config, loading,
canAddRows,
} = getContext("grid") } = getContext("grid")
let visible = false let visible = false
@ -40,6 +41,7 @@
$: $tableId, (visible = false) $: $tableId, (visible = false)
$: invertY = shouldInvertY(offset, $rowVerticalInversionIndex, $renderedRows) $: invertY = shouldInvertY(offset, $rowVerticalInversionIndex, $renderedRows)
$: selectedRowCount = Object.values($selectedRows).length $: selectedRowCount = Object.values($selectedRows).length
$: hasNoRows = !$rows.length
const shouldInvertY = (offset, inversionIndex, rows) => { const shouldInvertY = (offset, inversionIndex, rows) => {
if (offset === 0) { if (offset === 0) {
@ -147,16 +149,22 @@
</script> </script>
<!-- New row FAB --> <!-- New row FAB -->
{#if !visible && !selectedRowCount && $config.allowAddRows && firstColumn} <TempTooltip
<div text="Click here to create your first row"
class="new-row-fab" condition={hasNoRows && !$loading}
on:click={() => dispatch("add-row-inline")} type={TooltipType.Info}
transition:fade|local={{ duration: 130 }} >
class:offset={!$stickyColumn} {#if !visible && !selectedRowCount && $canAddRows}
> <div
<Icon name="Add" size="S" /> class="new-row-fab"
</div> on:click={() => dispatch("add-row-inline")}
{/if} transition:fade|local={{ duration: 130 }}
class:offset={!$stickyColumn}
>
<Icon name="Add" size="S" />
</div>
{/if}
</TempTooltip>
<!-- Only show new row functionality if we have any columns --> <!-- Only show new row functionality if we have any columns -->
{#if visible} {#if visible}

View File

@ -13,11 +13,10 @@
rows, rows,
selectedRows, selectedRows,
stickyColumn, stickyColumn,
renderedColumns,
renderedRows, renderedRows,
focusedCellId, focusedCellId,
hoveredRowId, hoveredRowId,
config, canAddRows,
selectedCellMap, selectedCellMap,
focusedRow, focusedRow,
scrollLeft, scrollLeft,
@ -93,7 +92,7 @@
{/if} {/if}
</div> </div>
{/each} {/each}
{#if $config.allowAddRows && ($renderedColumns.length || $stickyColumn)} {#if $canAddRows}
<div <div
class="row new" class="row new"
on:mouseenter={$isDragging on:mouseenter={$isDragging

View File

@ -16,6 +16,7 @@
config, config,
menu, menu,
gridFocused, gridFocused,
canAddRows,
} = getContext("grid") } = getContext("grid")
const ignoredOriginSelectors = [ const ignoredOriginSelectors = [
@ -45,7 +46,7 @@
e.preventDefault() e.preventDefault()
focusFirstCell() focusFirstCell()
} else if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { } else if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
if ($config.allowAddRows) { if ($canAddRows) {
e.preventDefault() e.preventDefault()
dispatch("add-row-inline") dispatch("add-row-inline")
} }
@ -99,7 +100,7 @@
} }
break break
case "Enter": case "Enter":
if ($config.allowAddRows) { if ($canAddRows) {
dispatch("add-row-inline") dispatch("add-row-inline")
} }
} }

View File

@ -17,6 +17,7 @@
focusedCellAPI, focusedCellAPI,
focusedRowId, focusedRowId,
notifications, notifications,
canAddRows,
} = getContext("grid") } = getContext("grid")
$: style = makeStyle($menu) $: style = makeStyle($menu)
@ -93,7 +94,7 @@
</MenuItem> </MenuItem>
<MenuItem <MenuItem
icon="Duplicate" icon="Duplicate"
disabled={isNewRow || !$config.allowAddRows} disabled={isNewRow || !$canAddRows}
on:click={duplicate} on:click={duplicate}
> >
Duplicate row Duplicate row

View File

@ -83,6 +83,21 @@ export const deriveStores = context => {
await saveChanges() await saveChanges()
} }
// Derive if we have any normal columns
const hasNonAutoColumn = derived(
[columns, stickyColumn],
([$columns, $stickyColumn]) => {
let allCols = $columns || []
if ($stickyColumn) {
allCols = [...allCols, $stickyColumn]
}
const normalCols = allCols.filter(column => {
return !column.schema?.autocolumn
})
return normalCols.length > 0
}
)
// Persists column changes by saving metadata against table schema // Persists column changes by saving metadata against table schema
const saveChanges = async () => { const saveChanges = async () => {
const $columns = get(columns) const $columns = get(columns)
@ -128,6 +143,7 @@ export const deriveStores = context => {
} }
return { return {
hasNonAutoColumn,
columns: { columns: {
...columns, ...columns,
actions: { actions: {

View File

@ -70,6 +70,8 @@ export const deriveStores = context => {
rowHeight, rowHeight,
stickyColumn, stickyColumn,
width, width,
hasNonAutoColumn,
config,
} = context } = context
// Derive the row that contains the selected cell // Derive the row that contains the selected cell
@ -112,7 +114,16 @@ export const deriveStores = context => {
return ($stickyColumn?.width || 0) + $width + GutterWidth < 1100 return ($stickyColumn?.width || 0) + $width + GutterWidth < 1100
}) })
// Derive if we're able to add rows
const canAddRows = derived(
[config, hasNonAutoColumn],
([$config, $hasNonAutoColumn]) => {
return $config.allowAddRows && $hasNonAutoColumn
}
)
return { return {
canAddRows,
focusedRow, focusedRow,
contentLines, contentLines,
compact, compact,

View File

@ -11,3 +11,26 @@ export function downloadText(filename, text) {
URL.revokeObjectURL(url) URL.revokeObjectURL(url)
} }
export async function downloadStream(streamResponse) {
const blob = await streamResponse.blob()
const contentDisposition = streamResponse.headers.get("Content-Disposition")
const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(
contentDisposition
)
const filename = matches[1].replace(/['"]/g, "")
const resBlob = new Blob([blob])
const blobUrl = URL.createObjectURL(resBlob)
const link = document.createElement("a")
link.href = blobUrl
link.download = filename
link.click()
URL.revokeObjectURL(blobUrl)
}

View File

@ -5,4 +5,4 @@ export * as RoleUtils from "./roles"
export * as Utils from "./utils" export * as Utils from "./utils"
export { memo, derivedMemo } from "./memo" export { memo, derivedMemo } from "./memo"
export { createWebsocket } from "./websocket" export { createWebsocket } from "./websocket"
export { downloadText } from "./download" export * from "./download"

View File

@ -27,11 +27,14 @@ const DEFAULT_SCHEMA = "dbo"
import { ConfidentialClientApplication } from "@azure/msal-node" import { ConfidentialClientApplication } from "@azure/msal-node"
import { utils } from "@budibase/shared-core"
enum MSSQLConfigAuthType { enum MSSQLConfigAuthType {
ACTIVE_DIRECTORY = "Active Directory", AZURE_ACTIVE_DIRECTORY = "Azure Active Directory",
NTLM = "NTLM",
} }
interface MSSQLConfig { interface BasicMSSQLConfig {
user: string user: string
password: string password: string
server: string server: string
@ -40,13 +43,30 @@ interface MSSQLConfig {
schema: string schema: string
encrypt?: boolean encrypt?: boolean
authType?: MSSQLConfigAuthType authType?: MSSQLConfigAuthType
adConfig?: { }
interface AzureADMSSQLConfig extends BasicMSSQLConfig {
authType: MSSQLConfigAuthType.AZURE_ACTIVE_DIRECTORY
adConfig: {
clientId: string clientId: string
clientSecret: string clientSecret: string
tenantId: string tenantId: string
} }
} }
interface NTLMMSSQLConfig extends BasicMSSQLConfig {
authType: MSSQLConfigAuthType.NTLM
ntlmConfig: {
domain?: string
trustServerCertificate?: boolean
}
}
type MSSQLConfig =
| (BasicMSSQLConfig & { authType?: undefined })
| AzureADMSSQLConfig
| NTLMMSSQLConfig
const SCHEMA: Integration = { const SCHEMA: Integration = {
docs: "https://github.com/tediousjs/node-mssql", docs: "https://github.com/tediousjs/node-mssql",
plus: true, plus: true,
@ -93,13 +113,18 @@ const SCHEMA: Integration = {
authType: { authType: {
type: DatasourceFieldType.SELECT, type: DatasourceFieldType.SELECT,
display: "Advanced auth", display: "Advanced auth",
config: { options: [MSSQLConfigAuthType.ACTIVE_DIRECTORY] }, config: {
options: [
MSSQLConfigAuthType.AZURE_ACTIVE_DIRECTORY,
MSSQLConfigAuthType.NTLM,
],
},
}, },
adConfig: { adConfig: {
type: DatasourceFieldType.FIELD_GROUP, type: DatasourceFieldType.FIELD_GROUP,
default: true, default: true,
display: "Configure Active Directory", display: "Configure Active Directory",
hidden: "'{{authType}}' !== 'Active Directory'", hidden: `'{{authType}}' !== '${MSSQLConfigAuthType.AZURE_ACTIVE_DIRECTORY}'`,
config: { config: {
openByDefault: true, openByDefault: true,
nestedFields: true, nestedFields: true,
@ -122,6 +147,28 @@ const SCHEMA: Integration = {
}, },
}, },
}, },
ntlmConfig: {
type: DatasourceFieldType.FIELD_GROUP,
default: true,
display: "Configure NTLM",
hidden: `'{{authType}}' !== '${MSSQLConfigAuthType.NTLM}'`,
config: {
openByDefault: true,
nestedFields: true,
},
fields: {
domain: {
type: DatasourceFieldType.STRING,
required: false,
display: "Domain",
},
trustServerCertificate: {
type: DatasourceFieldType.BOOLEAN,
required: false,
display: "Trust server certificate",
},
},
},
}, },
query: { query: {
create: { create: {
@ -199,26 +246,43 @@ class SqlServerIntegration extends Sql implements DatasourcePlus {
} }
delete clientCfg.encrypt delete clientCfg.encrypt
if (this.config.authType === MSSQLConfigAuthType.ACTIVE_DIRECTORY) { switch (this.config.authType) {
const { clientId, tenantId, clientSecret } = this.config.adConfig! case MSSQLConfigAuthType.AZURE_ACTIVE_DIRECTORY:
const clientApp = new ConfidentialClientApplication({ const { clientId, tenantId, clientSecret } = this.config.adConfig
auth: { const clientApp = new ConfidentialClientApplication({
clientId, auth: {
authority: `https://login.microsoftonline.com/${tenantId}`, clientId,
clientSecret, authority: `https://login.microsoftonline.com/${tenantId}`,
}, clientSecret,
}) },
})
const response = await clientApp.acquireTokenByClientCredential({ const response = await clientApp.acquireTokenByClientCredential({
scopes: ["https://database.windows.net/.default"], scopes: ["https://database.windows.net/.default"],
}) })
clientCfg.authentication = { clientCfg.authentication = {
type: "azure-active-directory-access-token", type: "azure-active-directory-access-token",
options: { options: {
token: response!.accessToken, token: response!.accessToken,
}, },
} }
break
case MSSQLConfigAuthType.NTLM:
const { domain, trustServerCertificate } = this.config.ntlmConfig
clientCfg.authentication = {
type: "ntlm",
options: {
domain,
},
}
clientCfg.options ??= {}
clientCfg.options.trustServerCertificate = trustServerCertificate
break
case undefined:
break
default:
utils.unreachable(this.config)
} }
const pool = new sqlServer.ConnectionPool(clientCfg) const pool = new sqlServer.ConnectionPool(clientCfg)

View File

@ -0,0 +1,13 @@
import { UserCtx } from "@budibase/types"
import { installation, logging } from "@budibase/backend-core"
export async function getLogs(ctx: UserCtx) {
const logReadStream = logging.system.getLogReadStream()
const { installId } = await installation.getInstall()
const fileName = `${installId}-${Date.now()}.log`
ctx.set("content-disposition", `attachment; filename=${fileName}`)
ctx.body = logReadStream
}

View File

@ -16,6 +16,9 @@ import licenseRoutes from "./global/license"
import migrationRoutes from "./system/migrations" import migrationRoutes from "./system/migrations"
import accountRoutes from "./system/accounts" import accountRoutes from "./system/accounts"
import restoreRoutes from "./system/restore" import restoreRoutes from "./system/restore"
import systemLogRoutes from "./system/logs"
import env from "../../environment"
export const routes: Router[] = [ export const routes: Router[] = [
configRoutes, configRoutes,
@ -38,3 +41,7 @@ export const routes: Router[] = [
eventRoutes, eventRoutes,
pro.scim, pro.scim,
] ]
if (env.SELF_HOSTED) {
routes.push(systemLogRoutes)
}

View File

@ -0,0 +1,9 @@
import Router from "@koa/router"
import { middleware } from "@budibase/backend-core"
import * as controller from "../../controllers/system/logs"
const router: Router = new Router()
router.get("/api/system/logs", middleware.adminOnly, controller.getLogs)
export default router

View File

@ -1,4 +1,5 @@
import { logging } from "@budibase/backend-core" import { logging } from "@budibase/backend-core"
logging.LOG_CONTEXT = false logging.LOG_CONTEXT = false
jest.retryTimes(2)
jest.setTimeout(60000) jest.setTimeout(60000)

375
yarn.lock
View File

@ -4487,21 +4487,21 @@
path-to-regexp "1.x" path-to-regexp "1.x"
urijs "^1.19.2" urijs "^1.19.2"
"@lerna/child-process@7.0.2": "@lerna/child-process@7.1.1":
version "7.0.2" version "7.1.1"
resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.0.2.tgz#0ea270dcc34cbece6b5319f1d4a24733060883bd" resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.1.1.tgz#60eddd6dc4b6ba0fd51851c78b6dbdc4e1614220"
integrity sha512-15lMrNBL/pvREMJPSfIPieaBtyyapDco/TNjALLEL53JGzO9g8rj5PipfE9h5ILx8aq/GaP17XCh209aVCun/w== integrity sha512-mR8PaTkckYPLmEBG2VsVsJq2UuzEvjXevOB1rKLKUZ/dPCGcottVhbiEzTxickc+s7Y/1dTTLn/1BKj3B1a5BA==
dependencies: dependencies:
chalk "^4.1.0" chalk "^4.1.0"
execa "^5.0.0" execa "^5.0.0"
strong-log-transformer "^2.1.0" strong-log-transformer "^2.1.0"
"@lerna/create@7.0.2": "@lerna/create@7.1.1":
version "7.0.2" version "7.1.1"
resolved "https://registry.yarnpkg.com/@lerna/create/-/create-7.0.2.tgz#a9335b889b54a456b3efc953f109aaf1d134b40a" resolved "https://registry.yarnpkg.com/@lerna/create/-/create-7.1.1.tgz#2af94afb01971c1b594c06347b6998607aefe5c4"
integrity sha512-1arGpEpWbWmci1MyaGKvP2SqCAPFWpLqZp0swckianX1kx1mso9B16BWFvcHhU57zCD0Co/z+jX+02UEzZGP7Q== integrity sha512-1PY2OgwGxp7b91JzLKEhONVl69mCt1IyYEc6pzKy3Sv+UOdeK2QFq1SX/85hNOR3iitiyZ75bNWUTcBly1ZlZg==
dependencies: dependencies:
"@lerna/child-process" "7.0.2" "@lerna/child-process" "7.1.1"
dedent "0.7.0" dedent "0.7.0"
fs-extra "^11.1.1" fs-extra "^11.1.1"
init-package-json "5.0.0" init-package-json "5.0.0"
@ -4694,13 +4694,6 @@
read-package-json-fast "^3.0.0" read-package-json-fast "^3.0.0"
which "^3.0.0" which "^3.0.0"
"@nrwl/devkit@16.2.1":
version "16.2.1"
resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-16.2.1.tgz#2da4dbe5826b0721cae547635554b6e411a069f6"
integrity sha512-yeNEccQzDuL+/thbS2XTq8MtD0KDrI92gXIPSrS/Q6QnDNJGz6T2kRe/mJWrcfrDFm/L61MsAlGXobElhceNMw==
dependencies:
"@nx/devkit" "16.2.1"
"@nrwl/devkit@16.2.2": "@nrwl/devkit@16.2.2":
version "16.2.2" version "16.2.2"
resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-16.2.2.tgz#fd7d0a19b4be3ba35cc0d3dd9e4154f9812f432f" resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-16.2.2.tgz#fd7d0a19b4be3ba35cc0d3dd9e4154f9812f432f"
@ -4708,19 +4701,26 @@
dependencies: dependencies:
"@nx/devkit" "16.2.2" "@nx/devkit" "16.2.2"
"@nrwl/js@16.2.1": "@nrwl/devkit@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nrwl/js/-/js-16.2.1.tgz#6eacfa1f0658ca1e288da86b6c38be4846f2779a" resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-16.4.3.tgz#036be0d478ef7156e55c1cfb4d13da080983503d"
integrity sha512-+XCgHocQzqn/wQauzTuWv/ioyuuiC3FfE3H+wg2FgfYuJLYuGyGx4qDhuiGvaaLqOK1dJQxLBsZW9Gjk77qe1g== integrity sha512-sDGv3RX5DHBMFFiHdd91e4YFXb87X5jsKkEg5Y2jmFtz/OilBKA9yoRhZ8t+iLBOmbgUngC5ZYPHc+Ykd2U3nA==
dependencies: dependencies:
"@nx/js" "16.2.1" "@nx/devkit" "16.4.3"
"@nrwl/tao@16.2.1": "@nrwl/js@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-16.2.1.tgz#08bb3dae81e958777268747c385c32a608452c3e" resolved "https://registry.yarnpkg.com/@nrwl/js/-/js-16.4.3.tgz#2f0d7c824a4abe6d55912c0e4abcc18a390aab25"
integrity sha512-mhLkMxGFbnR4hu9UbjMvzdePDXmUpV33mImt1myewP/cY9YZdzv5ntqT+9U+zzVg7Q2ZGosiGQE+IYRm6yeWog== integrity sha512-hA26y1aZfnjIJVlVtEMdK8BFFtKTZVZ6VWOgtUhCMmbtxpE4tpTssRItzq5dkQi9Et/dry8ClWqRWPqlW7LqxQ==
dependencies: dependencies:
nx "16.2.1" "@nx/js" "16.4.3"
"@nrwl/nx-cloud@16.0.5":
version "16.0.5"
resolved "https://registry.yarnpkg.com/@nrwl/nx-cloud/-/nx-cloud-16.0.5.tgz#c963480a71c4afa964fbbe9e4d6bbf222764e9cd"
integrity sha512-1p82ym8WE9ziejwgPslstn19iV/VkHfHfKr/5YOnfCHQS+NxUf92ogcYhHXtqWLblVZ9Zs4W4pkSXK4e04wCmQ==
dependencies:
nx-cloud "16.0.5"
"@nrwl/tao@16.3.2": "@nrwl/tao@16.3.2":
version "16.3.2" version "16.3.2"
@ -4729,24 +4729,19 @@
dependencies: dependencies:
nx "16.3.2" nx "16.3.2"
"@nrwl/workspace@16.2.1": "@nrwl/tao@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nrwl/workspace/-/workspace-16.2.1.tgz#22e83a4b545e563fd157c407b387113a6bc639bc" resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-16.4.3.tgz#8a72e8c1c903d8d7e1d9a298c28f03a000a925d8"
integrity sha512-k9CUsGNBC5gTnTMcPDHFkxIPzzhhm47DhmNV3xueuwSAyZbvnekCtmkFRdwe4jtOFjB6+MpTsol9p37vKfXVLA== integrity sha512-h+/UdXtdVuH9K2+Rx1HK5AHXGtgXNIqdLIH1KRL+74fiQ+JNO2Xuz9wqiD+rZ5tmtL/4hZpePCMkTz2FusKvbA==
dependencies: dependencies:
"@nx/workspace" "16.2.1" nx "16.4.3"
"@nx/devkit@16.2.1": "@nrwl/workspace@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/devkit/-/devkit-16.2.1.tgz#f937604149272b46927cad5645ecc444973f97f2" resolved "https://registry.yarnpkg.com/@nrwl/workspace/-/workspace-16.4.3.tgz#5d8bd06d2d1650318237dd9eb786e6695ebe7802"
integrity sha512-OrnFkU+lrSP/MdQW6C07aMlLyMp98oZMyfZ6h721T66zvuDfchhG2RXLX/Rb2t1lgZ+oMBKwvxxUKMRpHKPekA== integrity sha512-8FKqGgB8IFeMebCZeaD2Ta0/wSqWMFEX0FWCj1piuJNosgfImdQZUgYfLorsAjyR69GBP+59OkmsxDClZcCPMw==
dependencies: dependencies:
"@nrwl/devkit" "16.2.1" "@nx/workspace" "16.4.3"
ejs "^3.1.7"
ignore "^5.0.4"
semver "7.3.4"
tmp "~0.2.1"
tslib "^2.3.0"
"@nx/devkit@16.2.2", "@nx/devkit@>=16.1.3 < 17": "@nx/devkit@16.2.2", "@nx/devkit@>=16.1.3 < 17":
version "16.2.2" version "16.2.2"
@ -4760,10 +4755,22 @@
tmp "~0.2.1" tmp "~0.2.1"
tslib "^2.3.0" tslib "^2.3.0"
"@nx/js@16.2.1": "@nx/devkit@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/js/-/js-16.2.1.tgz#41c8c2d610131fa064bbb2b6bb25bd67ed06be79" resolved "https://registry.yarnpkg.com/@nx/devkit/-/devkit-16.4.3.tgz#a5e691f1fd49b5b0d8bb0a4347b3501b11e33056"
integrity sha512-WpK8yqVCrkCRTbNxSuRuQFpBXyX+doynixVv8yuB8HKPfE/wx6252eUMT43DWQJ+stmd5IhoH4THGyqpf+aaHg== integrity sha512-5LHtia3Ioy4uwWDIpnCbslFwxNdRJ2cWWmzq4oDINZnUMzNsjatVowKkOUBeG4Xh++6Dvui/VSdKZ6J0MUoQzw==
dependencies:
"@nrwl/devkit" "16.4.3"
ejs "^3.1.7"
ignore "^5.0.4"
semver "7.5.3"
tmp "~0.2.1"
tslib "^2.3.0"
"@nx/js@16.4.3":
version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/js/-/js-16.4.3.tgz#3d5aa519cd5f769c69a14b1c6418ab2bb43de519"
integrity sha512-0Uia5j+BOybXN0Fpfl1729m+bot+6g0tKKR3WLh715QCuXoKPtprTYgp/GEi+dUCUNrmHzCAduztMvsH0RY9Kw==
dependencies: dependencies:
"@babel/core" "^7.15.0" "@babel/core" "^7.15.0"
"@babel/plugin-proposal-class-properties" "^7.14.5" "@babel/plugin-proposal-class-properties" "^7.14.5"
@ -4772,124 +4779,131 @@
"@babel/preset-env" "^7.15.0" "@babel/preset-env" "^7.15.0"
"@babel/preset-typescript" "^7.15.0" "@babel/preset-typescript" "^7.15.0"
"@babel/runtime" "^7.14.8" "@babel/runtime" "^7.14.8"
"@nrwl/js" "16.2.1" "@nrwl/js" "16.4.3"
"@nx/devkit" "16.2.1" "@nx/devkit" "16.4.3"
"@nx/workspace" "16.2.1" "@nx/workspace" "16.4.3"
"@phenomnomnominal/tsquery" "~5.0.1" "@phenomnomnominal/tsquery" "~5.0.1"
babel-plugin-const-enum "^1.0.1" babel-plugin-const-enum "^1.0.1"
babel-plugin-macros "^2.8.0" babel-plugin-macros "^2.8.0"
babel-plugin-transform-typescript-metadata "^0.3.1" babel-plugin-transform-typescript-metadata "^0.3.1"
chalk "^4.1.0" chalk "^4.1.0"
detect-port "^1.5.1"
fast-glob "3.2.7" fast-glob "3.2.7"
fs-extra "^11.1.0" fs-extra "^11.1.0"
ignore "^5.0.4" ignore "^5.0.4"
js-tokens "^4.0.0" js-tokens "^4.0.0"
minimatch "3.0.5" minimatch "3.0.5"
semver "7.5.3"
source-map-support "0.5.19" source-map-support "0.5.19"
tslib "^2.3.0" tslib "^2.3.0"
"@nx/nx-darwin-arm64@16.2.1":
version "16.2.1"
resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-16.2.1.tgz#8a144a6fd38a2a7179c583c1fc344b2a0de27996"
integrity sha512-xK/dL5T2R8zrcD8/13PeaYH/LBcYeaELIZkXGdGbtQ8WeFHjPJLBfuWo/7Se7KSWIXLIJEeYrVZwyxuei1dOTA==
"@nx/nx-darwin-arm64@16.3.2": "@nx/nx-darwin-arm64@16.3.2":
version "16.3.2" version "16.3.2"
resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-16.3.2.tgz#83b6e78b27d2d7da8f7626560f52070c8735d28a" resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-16.3.2.tgz#83b6e78b27d2d7da8f7626560f52070c8735d28a"
integrity sha512-YfYVNfsJBzBcBnJUU4AcA6A4QMkgnVlETfp4KGL36Otq542mRY1ISGHdox63ocI5AKh5gay5AaGcR4wR9PU9Vg== integrity sha512-YfYVNfsJBzBcBnJUU4AcA6A4QMkgnVlETfp4KGL36Otq542mRY1ISGHdox63ocI5AKh5gay5AaGcR4wR9PU9Vg==
"@nx/nx-darwin-x64@16.2.1": "@nx/nx-darwin-arm64@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/nx-darwin-x64/-/nx-darwin-x64-16.2.1.tgz#f878b9257bb5ed939c5095b72f1f37fe01bab950" resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-16.4.3.tgz#08e63921c4e4dfc9eb9da612140c62ca8c190059"
integrity sha512-J1ZBqy8FtIhvZopcc96JWZY2InZClQ+XHWHnAmX8S1f79hcLUiatpu90FZhvfXmfOfLlpkKsa8aje/kjpnnWhA== integrity sha512-iVr3KTHXqGWx34mLxKjdDT1m6px9NME7zqSoKZW9DQuxDt3G7NN4PkK6+n2YqVNNSOmYml/Oo5iVtQ2TUCJDFA==
"@nx/nx-darwin-x64@16.3.2": "@nx/nx-darwin-x64@16.3.2":
version "16.3.2" version "16.3.2"
resolved "https://registry.yarnpkg.com/@nx/nx-darwin-x64/-/nx-darwin-x64-16.3.2.tgz#0ae2a64356542c5fb73ca8038ce10ec4512e7fcb" resolved "https://registry.yarnpkg.com/@nx/nx-darwin-x64/-/nx-darwin-x64-16.3.2.tgz#0ae2a64356542c5fb73ca8038ce10ec4512e7fcb"
integrity sha512-bJtpozz0zSRVRrcQ76GrlT3TWEGTymLYWrVG51bH5KZ46t6/a4EQBI3uL3vubMmOZ0jR4ywybOcPBBhxmBJ68w== integrity sha512-bJtpozz0zSRVRrcQ76GrlT3TWEGTymLYWrVG51bH5KZ46t6/a4EQBI3uL3vubMmOZ0jR4ywybOcPBBhxmBJ68w==
"@nx/nx-darwin-x64@16.4.3":
version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/nx-darwin-x64/-/nx-darwin-x64-16.4.3.tgz#e1e591f38bd103cf110487bd8c35daf17f8636c7"
integrity sha512-Km1N7Rek4VZW9rFMpV/gwmW0YHCoeV/5/tbYOYjSPJY6n2GB/vVoqE1DTf69muIk32436aK+qYRpd98bXC8GKg==
"@nx/nx-freebsd-x64@16.3.2": "@nx/nx-freebsd-x64@16.3.2":
version "16.3.2" version "16.3.2"
resolved "https://registry.yarnpkg.com/@nx/nx-freebsd-x64/-/nx-freebsd-x64-16.3.2.tgz#202adf4d6070f47ed46450f006ecd50851147c74" resolved "https://registry.yarnpkg.com/@nx/nx-freebsd-x64/-/nx-freebsd-x64-16.3.2.tgz#202adf4d6070f47ed46450f006ecd50851147c74"
integrity sha512-ZvufI0bWqT67nLbBo6ejrIGxypdoedRQTP/tudWbs/4isvxLe1uVku1BfKCTQUsJG367SqNOU1H5kzI/MRr3ow== integrity sha512-ZvufI0bWqT67nLbBo6ejrIGxypdoedRQTP/tudWbs/4isvxLe1uVku1BfKCTQUsJG367SqNOU1H5kzI/MRr3ow==
"@nx/nx-linux-arm-gnueabihf@16.2.1": "@nx/nx-freebsd-x64@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-16.2.1.tgz#a0f7d2c1f90d78bf30c7beae7c5481a71fb2652a" resolved "https://registry.yarnpkg.com/@nx/nx-freebsd-x64/-/nx-freebsd-x64-16.4.3.tgz#dc7fd8dbb87d7eb613b3f7302b0e3cba233277fd"
integrity sha512-rnujPmWlnkEvzkWARuW85cizVx6uGwQ/gA84tK3cHZQf9ly172WbDtsMtYRS9/CjvysMqDV0zBd7o/YhwpXNZg== integrity sha512-i6gc7oiDekYY2DS20COoeIrUqSQt0A3V+xUbrMGTInbHMux8QlfY5LGPRHGzqRlvnmUbrpgN0TdwBB9KOgaWmw==
"@nx/nx-linux-arm-gnueabihf@16.3.2": "@nx/nx-linux-arm-gnueabihf@16.3.2":
version "16.3.2" version "16.3.2"
resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-16.3.2.tgz#62314a82566e3647866b9dd4167a2d0e1397f001" resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-16.3.2.tgz#62314a82566e3647866b9dd4167a2d0e1397f001"
integrity sha512-IQL4kxdiZLvifar7+SIum3glRuVsxtE0dL8RvteSDXrxDQnaTUrjILC+VGhalRmk7ngBbGKNrhWOeeL7390CzQ== integrity sha512-IQL4kxdiZLvifar7+SIum3glRuVsxtE0dL8RvteSDXrxDQnaTUrjILC+VGhalRmk7ngBbGKNrhWOeeL7390CzQ==
"@nx/nx-linux-arm64-gnu@16.2.1": "@nx/nx-linux-arm-gnueabihf@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-16.2.1.tgz#30dead7a96437c7cbb041a45e1c0e7292fb2151a" resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-16.4.3.tgz#5a2aa53297eff9b3d0cef5b0280d67400e61e80d"
integrity sha512-ZcuQN8eaxEI+93ut6UrDrZMPsk61LGlS6yaWPgrv3blKMfcU2+DYBDQ3ois7o5t0bnVad5QYSNhIvnMF2iU+hQ== integrity sha512-hozcDrzbv3X0oWYYbJfSybVmKviko78wjjxvdwYS2H9eqNN6sNBZ5+LL+duUazCeGGHj1fRipvb9E3rJxiKWEw==
"@nx/nx-linux-arm64-gnu@16.3.2": "@nx/nx-linux-arm64-gnu@16.3.2":
version "16.3.2" version "16.3.2"
resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-16.3.2.tgz#02826400aa55b8f44bac83332dd29647d0e95001" resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-16.3.2.tgz#02826400aa55b8f44bac83332dd29647d0e95001"
integrity sha512-f6AWgPVu3mfUEoOBa0rY2/7QY0Or9eR0KtLFpcPh7RUpxPw2EXzIbjD/0RGipdpspSrgiMKbZpsUjo6mXBFsQA== integrity sha512-f6AWgPVu3mfUEoOBa0rY2/7QY0Or9eR0KtLFpcPh7RUpxPw2EXzIbjD/0RGipdpspSrgiMKbZpsUjo6mXBFsQA==
"@nx/nx-linux-arm64-musl@16.2.1": "@nx/nx-linux-arm64-gnu@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-16.2.1.tgz#31219ecf98f9fe78b5fc06c9e0102d99e335bc5b" resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-16.4.3.tgz#2129426f8258e193f9997adafcda570e23d94435"
integrity sha512-mMOvkYyBLU4j+mSHobtrj/pIDYXFGIX3Q9FMWxZ5Xz15m0DsbypZ/8v6NWpJaBY4VX6rJhCc+D/pZH+QBT8+/g== integrity sha512-LrlSKCZtFl8TiIFuLjkSNN/yzQ8phZ6+0jgsuumrIE8t02y+WLcZ4dSGlCo4nwVX/MDCtTbc9LPI+rIoBvO/pQ==
"@nx/nx-linux-arm64-musl@16.3.2": "@nx/nx-linux-arm64-musl@16.3.2":
version "16.3.2" version "16.3.2"
resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-16.3.2.tgz#a0a81520e0904aa026a7ab0a8a3bf3facec9f14c" resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-16.3.2.tgz#a0a81520e0904aa026a7ab0a8a3bf3facec9f14c"
integrity sha512-AvrWcYz7021E3b5P9/0i26p60XMZfw86Epks51L6AhlflarlOH4AcEChc7APMtb1ELAIbDWx2S6oIDRbQ7rtVA== integrity sha512-AvrWcYz7021E3b5P9/0i26p60XMZfw86Epks51L6AhlflarlOH4AcEChc7APMtb1ELAIbDWx2S6oIDRbQ7rtVA==
"@nx/nx-linux-x64-gnu@16.2.1": "@nx/nx-linux-arm64-musl@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-16.2.1.tgz#fb89cbbdf852e27093e0296ff7414ccccdf3591a" resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-16.4.3.tgz#0285f71f94b5a2eb40f15033457f937e0362770d"
integrity sha512-Kyn4dxFTj2PCRv+39tKU8BzDRE6/ru5v435uvodx03GS650F7+OMr4DN57jG4MQWhf//OUX8zPkvbKhsmxjndA== integrity sha512-3ahS0k330T339FdVBQhr3EGrghAaezqdVpbOwG2pyiZRwvLVgnDkPF/d4EkGd3ZAsOLazcPkPH/fKxPPf8HP2g==
"@nx/nx-linux-x64-gnu@16.3.2": "@nx/nx-linux-x64-gnu@16.3.2":
version "16.3.2" version "16.3.2"
resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-16.3.2.tgz#e79b5c142ec8d9bfb458ea5803bc4b62abbcf296" resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-16.3.2.tgz#e79b5c142ec8d9bfb458ea5803bc4b62abbcf296"
integrity sha512-K2pWGAcbCNm6b7UZI9cc8z4Rb540QcuepBXD7akjPjWerzXriT6VCn4i9mVKsCg2mwSfknTJJVJ1PZwJSmTl/Q== integrity sha512-K2pWGAcbCNm6b7UZI9cc8z4Rb540QcuepBXD7akjPjWerzXriT6VCn4i9mVKsCg2mwSfknTJJVJ1PZwJSmTl/Q==
"@nx/nx-linux-x64-musl@16.2.1": "@nx/nx-linux-x64-gnu@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-16.2.1.tgz#82b1b5ba04ef6e4d49527841239bf9e0fb1456e3" resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-16.4.3.tgz#defea39bbd5f494c28369bd403f909d5ec905ac0"
integrity sha512-q8iFxLosSLiWkRWsbrioXV/qMG8TgsbqcM0VGz2FFLNMJ9DXvav/E/+8YbgEeHOjvA1MDeRaspIpDF7OMgJYGw== integrity sha512-Nbo+FLBYZRhJUB367Eg9f0mH7Q+X67H+QAF+wU2oK3StSGQNQbLnr7Q0yfmX912WdYDe7gWhEpqWTLZ7rv65mg==
"@nx/nx-linux-x64-musl@16.3.2": "@nx/nx-linux-x64-musl@16.3.2":
version "16.3.2" version "16.3.2"
resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-16.3.2.tgz#900aee8f171638b9fb44378e2ac0548cb4aa99a7" resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-16.3.2.tgz#900aee8f171638b9fb44378e2ac0548cb4aa99a7"
integrity sha512-sY1QDuQlqyYiRPJZanrtV07tU0DOXiCrWb0pDsGiO0qHuUSmW5Vw17GWEY4z3rt0/5U8fJ+/9WQrneviOmsOKg== integrity sha512-sY1QDuQlqyYiRPJZanrtV07tU0DOXiCrWb0pDsGiO0qHuUSmW5Vw17GWEY4z3rt0/5U8fJ+/9WQrneviOmsOKg==
"@nx/nx-win32-arm64-msvc@16.2.1": "@nx/nx-linux-x64-musl@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-16.2.1.tgz#8a19a0c2db565f6d07a2d0b4a8b8fc6c8c86fadd" resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-16.4.3.tgz#c78db85f3234d2b899c7acaa7b5e2ef2c8591eb6"
integrity sha512-PpGiYzrMivDY1i10Zwf5Hmnv6oAQ8ACf6ehDgyQ3tByMMXHgyUZJLykfPaoWjoLh0s8wOvMV74WZO+K1LcIxTA== integrity sha512-RG31ewe3GRmwSMBgWF0yeJ1zu8s42xywpwK8swgGHpUp+Z6JN8dkUqi7UfHGbjeaOIDg4w45/7OJyrE7dlqHCg==
"@nx/nx-win32-arm64-msvc@16.3.2": "@nx/nx-win32-arm64-msvc@16.3.2":
version "16.3.2" version "16.3.2"
resolved "https://registry.yarnpkg.com/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-16.3.2.tgz#88db772b3535648e147b1a0206b1a1fe875fa9a5" resolved "https://registry.yarnpkg.com/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-16.3.2.tgz#88db772b3535648e147b1a0206b1a1fe875fa9a5"
integrity sha512-wBfohT2hjrLKn9WFHvG0MFVk7uYhgYNiptnTLdTouziHgFyZ08vyl7XYBq55BwHPMQ5iswVoEfjn/5ZBfCPscg== integrity sha512-wBfohT2hjrLKn9WFHvG0MFVk7uYhgYNiptnTLdTouziHgFyZ08vyl7XYBq55BwHPMQ5iswVoEfjn/5ZBfCPscg==
"@nx/nx-win32-x64-msvc@16.2.1": "@nx/nx-win32-arm64-msvc@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-16.2.1.tgz#848b4c5b118f6a6f92f59c1643297e938c439242" resolved "https://registry.yarnpkg.com/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-16.4.3.tgz#d131273e8267eb98a7640f79a94049b5f12d572e"
integrity sha512-m5oHCaSKdyydM1n1W9V0m2oxBL8PiF54dZB0+PlKB2fhf1zxiyq8i1hL2hXbKA90IOYcUt5/b7761/BzN5njAw== integrity sha512-5HXY8S0vGUculndAhWqBrqkrQxY6M3v3Ac/3rr8O238JkdkhRiHilnGbwS2MIQpU7dou3wROO6wKT7+TyFv+cA==
"@nx/nx-win32-x64-msvc@16.3.2": "@nx/nx-win32-x64-msvc@16.3.2":
version "16.3.2" version "16.3.2"
resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-16.3.2.tgz#2195faaf1fc465c7a89bfdd62323fdd2a5d91f15" resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-16.3.2.tgz#2195faaf1fc465c7a89bfdd62323fdd2a5d91f15"
integrity sha512-QC0sWrfQm0/WdvvM//7UAgm+otbak6bznZ0zawTeqmLBh1hLjNeweyzSVKQEtZtlzDMKpzCVuuwkJq+VKBLvmw== integrity sha512-QC0sWrfQm0/WdvvM//7UAgm+otbak6bznZ0zawTeqmLBh1hLjNeweyzSVKQEtZtlzDMKpzCVuuwkJq+VKBLvmw==
"@nx/workspace@16.2.1": "@nx/nx-win32-x64-msvc@16.4.3":
version "16.2.1" version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/workspace/-/workspace-16.2.1.tgz#52cc54bf9970fe6769459b1315eb7f6db8ec387f" resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-16.4.3.tgz#cc8d87dbada3965b3156440277951b742b6c0de3"
integrity sha512-gGJNKsH2KFtxlBBL0AqPu0vo322wGfCPDK19OxgwTQMWDruMZ9jjAe3XU4a+FbCGmK1CmZUlbFo4HueD9hVkig== integrity sha512-9vdA5t5xuWCQ9JFJZFjzYGz9w5wtZ7zfKcx2HdBvg2nDWUzK5Z3khwsakTSsc7Ff7Hnd0i0l5T3Ls6Hk42Haww==
"@nx/workspace@16.4.3":
version "16.4.3"
resolved "https://registry.yarnpkg.com/@nx/workspace/-/workspace-16.4.3.tgz#0466ba261511bdbd41afedfe51ee002e6dee8f86"
integrity sha512-vgZo4ywYhRheFA2xUzf6Jt00tJ4B2iYFRqFCw4bNDOrn01wnpDUcMof4M0VE3P0OHMKktygDxuARzO20tGMRQQ==
dependencies: dependencies:
"@nrwl/workspace" "16.2.1" "@nrwl/workspace" "16.4.3"
"@nx/devkit" "16.2.1" "@nx/devkit" "16.4.3"
"@parcel/watcher" "2.0.4" "@parcel/watcher" "2.0.4"
chalk "^4.1.0" chalk "^4.1.0"
chokidar "^3.5.1" chokidar "^3.5.1"
@ -4901,7 +4915,7 @@
ignore "^5.0.4" ignore "^5.0.4"
minimatch "3.0.5" minimatch "3.0.5"
npm-run-path "^4.0.1" npm-run-path "^4.0.1"
nx "16.2.1" nx "16.4.3"
open "^8.4.0" open "^8.4.0"
rxjs "^7.8.0" rxjs "^7.8.0"
tmp "~0.2.1" tmp "~0.2.1"
@ -7403,6 +7417,14 @@
resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==
"@yarnpkg/parsers@3.0.0-rc.46":
version "3.0.0-rc.46"
resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz#03f8363111efc0ea670e53b0282cd3ef62de4e01"
integrity sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==
dependencies:
js-yaml "^3.10.0"
tslib "^2.4.0"
"@yarnpkg/parsers@^3.0.0-rc.18": "@yarnpkg/parsers@^3.0.0-rc.18":
version "3.0.0-rc.42" version "3.0.0-rc.42"
resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.42.tgz#3814e90a81bb1f9c06cc83c6a009139c55efe94d" resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.42.tgz#3814e90a81bb1f9c06cc83c6a009139c55efe94d"
@ -7563,6 +7585,11 @@ add-stream@^1.0.0:
resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa"
integrity sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ== integrity sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==
address@^1.0.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e"
integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==
after-all-results@^2.0.0: after-all-results@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/after-all-results/-/after-all-results-2.0.0.tgz#6ac2fc202b500f88da8f4f5530cfa100f4c6a2d0" resolved "https://registry.yarnpkg.com/after-all-results/-/after-all-results-2.0.0.tgz#6ac2fc202b500f88da8f4f5530cfa100f4c6a2d0"
@ -8166,6 +8193,15 @@ axios@0.24.0:
dependencies: dependencies:
follow-redirects "^1.14.4" follow-redirects "^1.14.4"
axios@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.3.tgz#8274250dada2edf53814ed7db644b9c2866c1e35"
integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
axios@^0.21.1, axios@^0.21.4: axios@^0.21.1, axios@^0.21.4:
version "0.21.4" version "0.21.4"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
@ -10782,6 +10818,14 @@ detect-node@^2.0.4:
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
detect-port@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.5.1.tgz#451ca9b6eaf20451acb0799b8ab40dff7718727b"
integrity sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==
dependencies:
address "^1.0.1"
debug "4"
detective-amd@^3.1.0: detective-amd@^3.1.0:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-3.1.2.tgz#bf55eb5291c218b76d6224a3d07932ef13a9a357" resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-3.1.2.tgz#bf55eb5291c218b76d6224a3d07932ef13a9a357"
@ -17501,13 +17545,13 @@ leaflet@^1.7.1:
resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.3.tgz#52ec436954964e2d3d39e0d433da4b2500d74414" resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.3.tgz#52ec436954964e2d3d39e0d433da4b2500d74414"
integrity sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ== integrity sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ==
lerna@7.0.2: lerna@7.1.1:
version "7.0.2" version "7.1.1"
resolved "https://registry.yarnpkg.com/lerna/-/lerna-7.0.2.tgz#45e844bfca72d12cc285c9a00f6f2cfb341bed05" resolved "https://registry.yarnpkg.com/lerna/-/lerna-7.1.1.tgz#6703062e6c4ddefdaf41e8890e9200690924fd71"
integrity sha512-omFpf1pTiaObC2YOC7K+euaDwhQA9CyKN1kXxmlSwaSkh8b8QTs4SC8jp3oNeXfcHpVS1ttuuz98AvQvJD46wA== integrity sha512-rjivAl3bYu2+lWOi90vy0tYFgwBYPMiNkR/DuEWZC08wle5dsbOZ/SlXeLk9+kzbF89Bt5P6p+qF78A2tJsWPA==
dependencies: dependencies:
"@lerna/child-process" "7.0.2" "@lerna/child-process" "7.1.1"
"@lerna/create" "7.0.2" "@lerna/create" "7.1.1"
"@npmcli/run-script" "6.0.2" "@npmcli/run-script" "6.0.2"
"@nx/devkit" ">=16.1.3 < 17" "@nx/devkit" ">=16.1.3 < 17"
"@octokit/plugin-enterprise-rest" "6.0.1" "@octokit/plugin-enterprise-rest" "6.0.1"
@ -19348,6 +19392,11 @@ node-int64@^0.4.0:
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
node-machine-id@^1.1.12:
version "1.1.12"
resolved "https://registry.yarnpkg.com/node-machine-id/-/node-machine-id-1.1.12.tgz#37904eee1e59b320bb9c5d6c0a59f3b469cb6267"
integrity sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==
node-mocks-http@^1.11.0: node-mocks-http@^1.11.0:
version "1.12.1" version "1.12.1"
resolved "https://registry.yarnpkg.com/node-mocks-http/-/node-mocks-http-1.12.1.tgz#838e176019daf177caff6bb8534e3a32646e7531" resolved "https://registry.yarnpkg.com/node-mocks-http/-/node-mocks-http-1.12.1.tgz#838e176019daf177caff6bb8534e3a32646e7531"
@ -19739,55 +19788,21 @@ nwsapi@^2.2.2:
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.4.tgz#fd59d5e904e8e1f03c25a7d5a15cfa16c714a1e5" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.4.tgz#fd59d5e904e8e1f03c25a7d5a15cfa16c714a1e5"
integrity sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g== integrity sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g==
nx@16.2.1: nx-cloud@16.0.5:
version "16.2.1" version "16.0.5"
resolved "https://registry.yarnpkg.com/nx/-/nx-16.2.1.tgz#8571a4663c79dc9d60c98599b19146b58c59b473" resolved "https://registry.yarnpkg.com/nx-cloud/-/nx-cloud-16.0.5.tgz#fa0b0185d254405ec47fcbcdbbd8b12ff1add096"
integrity sha512-O+yGcYIQtYKYagbIuOQFk1P8ki5PHn0BZjdZpsa4K8UZ4pCaRWzlwWwwUL91FUJe6tdhic5710DwAAakbGKP7Q== integrity sha512-13P7r0aKikjBtmdZrNorwXzVPeVIV4MLEwqGY+DEG6doLBtI5KqEQk/d5B5l2dCF2BEi/LXEmLYCmf9gwbOJ+Q==
dependencies: dependencies:
"@nrwl/tao" "16.2.1" "@nrwl/nx-cloud" "16.0.5"
"@parcel/watcher" "2.0.4" axios "1.1.3"
"@yarnpkg/lockfile" "^1.1.0"
"@yarnpkg/parsers" "^3.0.0-rc.18"
"@zkochan/js-yaml" "0.0.6"
axios "^1.0.0"
chalk "^4.1.0" chalk "^4.1.0"
cli-cursor "3.1.0"
cli-spinners "2.6.1"
cliui "^7.0.2"
dotenv "~10.0.0" dotenv "~10.0.0"
enquirer "~2.3.6"
fast-glob "3.2.7"
figures "3.2.0"
flat "^5.0.2"
fs-extra "^11.1.0" fs-extra "^11.1.0"
glob "7.1.4" node-machine-id "^1.1.12"
ignore "^5.0.4" open "~8.4.0"
js-yaml "4.1.0" strip-json-comments "^3.1.1"
jsonc-parser "3.2.0" tar "6.1.11"
lines-and-columns "~2.0.3" yargs-parser ">=21.1.1"
minimatch "3.0.5"
npm-run-path "^4.0.1"
open "^8.4.0"
semver "7.3.4"
string-width "^4.2.3"
strong-log-transformer "^2.1.0"
tar-stream "~2.2.0"
tmp "~0.2.1"
tsconfig-paths "^4.1.2"
tslib "^2.3.0"
v8-compile-cache "2.3.0"
yargs "^17.6.2"
yargs-parser "21.1.1"
optionalDependencies:
"@nx/nx-darwin-arm64" "16.2.1"
"@nx/nx-darwin-x64" "16.2.1"
"@nx/nx-linux-arm-gnueabihf" "16.2.1"
"@nx/nx-linux-arm64-gnu" "16.2.1"
"@nx/nx-linux-arm64-musl" "16.2.1"
"@nx/nx-linux-x64-gnu" "16.2.1"
"@nx/nx-linux-x64-musl" "16.2.1"
"@nx/nx-win32-arm64-msvc" "16.2.1"
"@nx/nx-win32-x64-msvc" "16.2.1"
nx@16.3.2, "nx@>=16.1.3 < 17": nx@16.3.2, "nx@>=16.1.3 < 17":
version "16.3.2" version "16.3.2"
@ -19840,6 +19855,57 @@ nx@16.3.2, "nx@>=16.1.3 < 17":
"@nx/nx-win32-arm64-msvc" "16.3.2" "@nx/nx-win32-arm64-msvc" "16.3.2"
"@nx/nx-win32-x64-msvc" "16.3.2" "@nx/nx-win32-x64-msvc" "16.3.2"
nx@16.4.3:
version "16.4.3"
resolved "https://registry.yarnpkg.com/nx/-/nx-16.4.3.tgz#0bd8e408eeb9f09f9fca334689bf3d13f361254f"
integrity sha512-bq3wc7WI/j/mmz4MbrhDVE+DLJ6ywvmAoUjxNRcVAhPi+rT7bDaztVZceDbxxVFW55wfOIjcYwhS9fGQMSBBpQ==
dependencies:
"@nrwl/tao" "16.4.3"
"@parcel/watcher" "2.0.4"
"@yarnpkg/lockfile" "^1.1.0"
"@yarnpkg/parsers" "3.0.0-rc.46"
"@zkochan/js-yaml" "0.0.6"
axios "^1.0.0"
chalk "^4.1.0"
cli-cursor "3.1.0"
cli-spinners "2.6.1"
cliui "^7.0.2"
dotenv "~10.0.0"
enquirer "~2.3.6"
fast-glob "3.2.7"
figures "3.2.0"
flat "^5.0.2"
fs-extra "^11.1.0"
glob "7.1.4"
ignore "^5.0.4"
js-yaml "4.1.0"
jsonc-parser "3.2.0"
lines-and-columns "~2.0.3"
minimatch "3.0.5"
npm-run-path "^4.0.1"
open "^8.4.0"
semver "7.5.3"
string-width "^4.2.3"
strong-log-transformer "^2.1.0"
tar-stream "~2.2.0"
tmp "~0.2.1"
tsconfig-paths "^4.1.2"
tslib "^2.3.0"
v8-compile-cache "2.3.0"
yargs "^17.6.2"
yargs-parser "21.1.1"
optionalDependencies:
"@nx/nx-darwin-arm64" "16.4.3"
"@nx/nx-darwin-x64" "16.4.3"
"@nx/nx-freebsd-x64" "16.4.3"
"@nx/nx-linux-arm-gnueabihf" "16.4.3"
"@nx/nx-linux-arm64-gnu" "16.4.3"
"@nx/nx-linux-arm64-musl" "16.4.3"
"@nx/nx-linux-x64-gnu" "16.4.3"
"@nx/nx-linux-x64-musl" "16.4.3"
"@nx/nx-win32-arm64-msvc" "16.4.3"
"@nx/nx-win32-x64-msvc" "16.4.3"
oauth-sign@~0.9.0: oauth-sign@~0.9.0:
version "0.9.0" version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
@ -20043,7 +20109,7 @@ open@^7.3.1:
is-docker "^2.0.0" is-docker "^2.0.0"
is-wsl "^2.1.1" is-wsl "^2.1.1"
open@^8.0.0, open@^8.4.0: open@^8.0.0, open@^8.4.0, open@~8.4.0:
version "8.4.2" version "8.4.2"
resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9"
integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==
@ -23131,6 +23197,11 @@ rollup@^3.18.0:
optionalDependencies: optionalDependencies:
fsevents "~2.3.2" fsevents "~2.3.2"
rotating-file-stream@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/rotating-file-stream/-/rotating-file-stream-3.1.0.tgz#6cf50e1671de82a396de6d31d39a6f2445f45fba"
integrity sha512-TkMF6cP1/QDcon9D71mjxHoflNuznNOrY5JJQfuxkKklZRmoow/lWBLNxXVjb6KcjAU8BDCV145buLgOx9Px1Q==
rrweb-cssom@^0.6.0: rrweb-cssom@^0.6.0:
version "0.6.0" version "0.6.0"
resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1"
@ -23377,6 +23448,13 @@ semver@7.3.7, semver@^7.3.4:
dependencies: dependencies:
lru-cache "^6.0.0" lru-cache "^6.0.0"
semver@7.5.3, semver@^7.5.3:
version "7.5.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e"
integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==
dependencies:
lru-cache "^6.0.0"
semver@7.x, semver@^7.3.2, semver@^7.3.7, semver@^7.3.8: semver@7.x, semver@^7.3.2, semver@^7.3.7, semver@^7.3.8:
version "7.3.8" version "7.3.8"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
@ -23403,13 +23481,6 @@ semver@^7.2.1, semver@^7.3.5:
dependencies: dependencies:
lru-cache "^6.0.0" lru-cache "^6.0.0"
semver@^7.5.3:
version "7.5.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e"
integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==
dependencies:
lru-cache "^6.0.0"
semver@~2.3.1: semver@~2.3.1:
version "2.3.2" version "2.3.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-2.3.2.tgz#b9848f25d6cf36333073ec9ef8856d42f1233e52" resolved "https://registry.yarnpkg.com/semver/-/semver-2.3.2.tgz#b9848f25d6cf36333073ec9ef8856d42f1233e52"
@ -26952,7 +27023,7 @@ yargs-parser@20.2.4:
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
yargs-parser@21.1.1, yargs-parser@^21.0.0, yargs-parser@^21.0.1, yargs-parser@^21.1.1: yargs-parser@21.1.1, yargs-parser@>=21.1.1, yargs-parser@^21.0.0, yargs-parser@^21.0.1, yargs-parser@^21.1.1:
version "21.1.1" version "21.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==