Merge remote-tracking branch 'origin/master' into automation-branching-ux-updates

This commit is contained in:
Dean 2024-11-27 09:11:57 +00:00
commit c52a29df29
33 changed files with 582 additions and 233 deletions

View File

@ -27,9 +27,8 @@
"extends": "plugin:svelte/recommended", "extends": "plugin:svelte/recommended",
"parser": "svelte-eslint-parser", "parser": "svelte-eslint-parser",
"parserOptions": { "parserOptions": {
"parser": "@babel/eslint-parser", "parser": "@typescript-eslint/parser",
"ecmaVersion": 2019, "ecmaVersion": 2019,
"sourceType": "module",
"allowImportExportEverywhere": true "allowImportExportEverywhere": true
} }
}, },

View File

@ -12,12 +12,12 @@ metadata:
type: Opaque type: Opaque
data: data:
{{- if $existingSecret }} {{- if $existingSecret }}
internalApiKey: {{ index $existingSecret.data "internalApiKey" }} internalApiKey: {{ index $existingSecret.data "internalApiKey" | quote }}
jwtSecret: {{ index $existingSecret.data "jwtSecret" }} jwtSecret: {{ index $existingSecret.data "jwtSecret" | quote }}
objectStoreAccess: {{ index $existingSecret.data "objectStoreAccess" }} objectStoreAccess: {{ index $existingSecret.data "objectStoreAccess" | quote }}
objectStoreSecret: {{ index $existingSecret.data "objectStoreSecret" }} objectStoreSecret: {{ index $existingSecret.data "objectStoreSecret" | quote }}
bbEncryptionKey: {{ index $existingSecret.data "bbEncryptionKey" }} bbEncryptionKey: {{ index $existingSecret.data "bbEncryptionKey" | quote }}
apiEncryptionKey: {{ index $existingSecret.data "apiEncryptionKey" }} apiEncryptionKey: {{ index $existingSecret.data "apiEncryptionKey" | quote }}
{{- else }} {{- else }}
internalApiKey: {{ template "budibase.defaultsecret" .Values.globals.internalApiKey }} internalApiKey: {{ template "budibase.defaultsecret" .Values.globals.internalApiKey }}
jwtSecret: {{ template "budibase.defaultsecret" .Values.globals.jwtSecret }} jwtSecret: {{ template "budibase.defaultsecret" .Values.globals.jwtSecret }}

View File

@ -3,7 +3,7 @@
import AbsTooltip from "../Tooltip/AbsTooltip.svelte" import AbsTooltip from "../Tooltip/AbsTooltip.svelte"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
export let type export let type = undefined
export let disabled = false export let disabled = false
export let size = "M" export let size = "M"
export let cta = false export let cta = false
@ -16,8 +16,8 @@
export let active = false export let active = false
export let tooltip = undefined export let tooltip = undefined
export let newStyles = true export let newStyles = true
export let id export let id = undefined
export let ref export let ref = undefined
export let reverse = false export let reverse = false
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()

View File

@ -2,13 +2,6 @@
import CoreDatePicker from "./DatePicker/DatePicker.svelte" import CoreDatePicker from "./DatePicker/DatePicker.svelte"
import Icon from "../../Icon/Icon.svelte" import Icon from "../../Icon/Icon.svelte"
export let value = null
export let disabled = false
export let readonly = false
export let error = null
export let appendTo = undefined
export let ignoreTimezones = false
let fromDate let fromDate
let toDate let toDate
</script> </script>

View File

@ -10,7 +10,7 @@
export let disabled = false export let disabled = false
export let updateOnChange = true export let updateOnChange = true
export let quiet = false export let quiet = false
export let inputRef export let inputRef = undefined
export let helpText = null export let helpText = null
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()

View File

@ -17,18 +17,18 @@
export let getOptionIcon = option => option?.icon export let getOptionIcon = option => option?.icon
export let getOptionColour = option => option?.colour export let getOptionColour = option => option?.colour
export let useOptionIconImage = false export let useOptionIconImage = false
export let isOptionEnabled export let isOptionEnabled = undefined
export let quiet = false export let quiet = false
export let autoWidth = false export let autoWidth = false
export let sort = false export let sort = false
export let tooltip = "" export let tooltip = ""
export let autocomplete = false export let autocomplete = false
export let customPopoverHeight export let customPopoverHeight = undefined
export let align export let align = undefined
export let footer = null export let footer = null
export let tag = null export let tag = null
export let helpText = null export let helpText = null
export let compare export let compare = undefined
export let onOptionMouseenter = () => {} export let onOptionMouseenter = () => {}
export let onOptionMouseleave = () => {} export let onOptionMouseleave = () => {}

View File

@ -43,7 +43,7 @@
export let showHeaderBorder = true export let showHeaderBorder = true
export let placeholderText = "No rows found" export let placeholderText = "No rows found"
export let snippets = [] export let snippets = []
export let defaultSortColumn export let defaultSortColumn = undefined
export let defaultSortOrder = "Ascending" export let defaultSortOrder = "Ascending"
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()

View File

@ -1,9 +1,9 @@
<script> <script lang="ts">
import "@spectrum-css/typography/dist/index-vars.css" import "@spectrum-css/typography/dist/index-vars.css"
// Sizes // Sizes
export let size = "M" export let size = "M"
export let textAlign export let textAlign = undefined
export let noPadding = false export let noPadding = false
export let weight = "default" // light, heavy, default export let weight = "default" // light, heavy, default
</script> </script>

View File

@ -6,4 +6,4 @@ release/
dist/ dist/
routify routify
.routify/ .routify/
svelte.config.js .rollup.cache

View File

@ -4,13 +4,14 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "routify -b && vite build --emptyOutDir", "build": "routify -b && NODE_OPTIONS=\"--max_old_space_size=4096\" vite build --emptyOutDir",
"start": "routify -c rollup", "start": "routify -c rollup",
"dev": "routify -c dev:vite", "dev": "routify -c dev:vite",
"dev:vite": "vite --host 0.0.0.0", "dev:vite": "vite --host 0.0.0.0",
"rollup": "rollup -c -w", "rollup": "rollup -c -w",
"test": "vitest run", "test": "vitest run",
"test:watch": "vitest" "test:watch": "vitest",
"check:types": "yarn svelte-check"
}, },
"jest": { "jest": {
"globals": { "globals": {
@ -88,6 +89,7 @@
"@babel/plugin-transform-runtime": "^7.13.10", "@babel/plugin-transform-runtime": "^7.13.10",
"@babel/preset-env": "^7.13.12", "@babel/preset-env": "^7.13.12",
"@rollup/plugin-replace": "^5.0.3", "@rollup/plugin-replace": "^5.0.3",
"@rollup/plugin-typescript": "8.3.0",
"@roxi/routify": "2.18.12", "@roxi/routify": "2.18.12",
"@sveltejs/vite-plugin-svelte": "1.4.0", "@sveltejs/vite-plugin-svelte": "1.4.0",
"@testing-library/jest-dom": "6.4.2", "@testing-library/jest-dom": "6.4.2",
@ -97,6 +99,7 @@
"jest": "29.7.0", "jest": "29.7.0",
"jsdom": "^21.1.1", "jsdom": "^21.1.1",
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"svelte-check": "^4.1.0",
"svelte-jester": "^1.3.2", "svelte-jester": "^1.3.2",
"vite": "^4.5.0", "vite": "^4.5.0",
"vite-plugin-static-copy": "^0.17.0", "vite-plugin-static-copy": "^0.17.0",

View File

@ -8,7 +8,7 @@ import { get } from "svelte/store"
import { auth, navigation } from "./stores/portal" import { auth, navigation } from "./stores/portal"
export const API = createAPIClient({ export const API = createAPIClient({
attachHeaders: headers => { attachHeaders: (headers: Record<string, string>) => {
// Attach app ID header from store // Attach app ID header from store
let appId = get(appStore).appId let appId = get(appStore).appId
if (appId) { if (appId) {
@ -16,13 +16,13 @@ export const API = createAPIClient({
} }
// Add csrf token if authenticated // Add csrf token if authenticated
const user = get(auth).user const user: any = get(auth).user
if (user?.csrfToken) { if (user?.csrfToken) {
headers["x-csrf-token"] = user.csrfToken headers["x-csrf-token"] = user.csrfToken
} }
}, },
onError: error => { onError: (error: any) => {
const { url, message, status, method, handled } = error || {} const { url, message, status, method, handled } = error || {}
// Log any errors that we haven't manually handled // Log any errors that we haven't manually handled
@ -45,14 +45,14 @@ export const API = createAPIClient({
} }
} }
}, },
onMigrationDetected: appId => { onMigrationDetected: (appId: string) => {
const updatingUrl = `/builder/app/updating/${appId}` const updatingUrl = `/builder/app/updating/${appId}`
if (window.location.pathname === updatingUrl) { if (window.location.pathname === updatingUrl) {
return return
} }
get(navigation).goto( get(navigation)?.goto(
`${updatingUrl}?returnUrl=${encodeURIComponent(window.location.pathname)}` `${updatingUrl}?returnUrl=${encodeURIComponent(window.location.pathname)}`
) )
}, },

View File

@ -114,7 +114,7 @@
$: schemaFields = search.getFields( $: schemaFields = search.getFields(
$tables.list, $tables.list,
Object.values(schema || {}), Object.values(schema || {}),
{ allowLinks: true } { allowLinks: false }
) )
$: queryLimit = tableId?.includes("datasource") ? "∞" : "1000" $: queryLimit = tableId?.includes("datasource") ? "∞" : "1000"
$: isTrigger = $memoBlock?.type === AutomationStepType.TRIGGER $: isTrigger = $memoBlock?.type === AutomationStepType.TRIGGER

View File

@ -1141,10 +1141,11 @@ export const buildFormSchema = (component, asset) => {
const fieldSetting = settings.find( const fieldSetting = settings.find(
setting => setting.key === "field" && setting.type.startsWith("field/") setting => setting.key === "field" && setting.type.startsWith("field/")
) )
if (fieldSetting && component.field) { if (fieldSetting) {
const type = fieldSetting.type.split("field/")[1] const type = fieldSetting.type.split("field/")[1]
if (type) { const key = component.field || component._instanceName
schema[component.field] = { type } if (type && key) {
schema[key] = { type }
} }
} }
component._children?.forEach(child => { component._children?.forEach(child => {

8
packages/builder/src/index.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
declare module "api" {
const API: {
getPlugins: () => Promise<any>
createPlugin: (plugin: object) => Promise<any>
uploadPlugin: (plugin: FormData) => Promise<any>
deletePlugin: (id: string) => Promise<void>
}
}

View File

@ -117,7 +117,4 @@
align-items: center; align-items: center;
margin-bottom: 12px; margin-bottom: 12px;
} }
.tabs {
}
</style> </style>

View File

@ -50,8 +50,6 @@
border-radius: 4px; border-radius: 4px;
pointer-events: none; pointer-events: none;
} }
.indicator.above {
}
.indicator.below { .indicator.below {
margin-top: 32px; margin-top: 32px;
} }

View File

@ -1,4 +1,4 @@
<script> <script lang="ts">
import { import {
Layout, Layout,
Heading, Heading,
@ -42,23 +42,25 @@
{ column: "edit", component: EditPluginRenderer }, { column: "edit", component: EditPluginRenderer },
] ]
let modal let modal: any
let searchTerm = "" let searchTerm: any = ""
let filter = "all" let filter: any = "all"
let filterOptions = [ let filterOptions = [
{ label: "All plugins", value: "all" }, { label: "All plugins", value: "all" },
{ label: "Components", value: "component" }, { label: "Components", value: "component" },
] ]
const searchPlaceholder: any = "Search"
if (!$admin.cloud) { if (!$admin.cloud) {
filterOptions.push({ label: "Datasources", value: "datasource" }) filterOptions.push({ label: "Datasources", value: "datasource" })
} }
$: filteredPlugins = $plugins $: filteredPlugins = $plugins
.filter(plugin => { .filter((plugin: any) => {
return filter === "all" || plugin.schema.type === filter return filter === "all" || plugin.schema.type === filter
}) })
.filter(plugin => { .filter((plugin: any) => {
return ( return (
!searchTerm || !searchTerm ||
plugin?.name?.toLowerCase().includes(searchTerm.toLowerCase()) plugin?.name?.toLowerCase().includes(searchTerm.toLowerCase())
@ -85,8 +87,8 @@
<Button <Button
on:click={() => on:click={() =>
window window
.open("https://github.com/Budibase/plugins", "_blank") ?.open("https://github.com/Budibase/plugins", "_blank")
.focus()} ?.focus()}
secondary secondary
> >
GitHub repo GitHub repo
@ -98,12 +100,12 @@
<div class="select"> <div class="select">
<Select <Select
bind:value={filter} bind:value={filter}
placeholder={null} placeholder={undefined}
options={filterOptions} options={filterOptions}
autoWidth autoWidth
/> />
</div> </div>
<Search bind:value={searchTerm} placeholder="Search" /> <Search bind:value={searchTerm} placeholder={searchPlaceholder} />
</div> </div>
{/if} {/if}
</div> </div>

View File

@ -16,6 +16,7 @@ import {
AutomationTriggerStepId, AutomationTriggerStepId,
AutomationEventType, AutomationEventType,
AutomationStepType, AutomationStepType,
AutomationActionStepId,
} from "@budibase/types" } from "@budibase/types"
import { ActionStepID } from "constants/backend/automations" import { ActionStepID } from "constants/backend/automations"
import { FIELDS } from "constants/backend" import { FIELDS } from "constants/backend"
@ -474,9 +475,13 @@ const automationActions = store => ({
.getPathSteps(block.pathTo, automation) .getPathSteps(block.pathTo, automation)
.slice(0, -1) .slice(0, -1)
// Current step will always be the last step of the path
const currentBlock = store.actions
.getPathSteps(block.pathTo, automation)
.at(-1)
// Extract all outputs from all previous steps as available bindingsx§x // Extract all outputs from all previous steps as available bindingsx§x
let bindings = [] let bindings = []
const addBinding = (name, value, icon, idx, isLoopBlock, bindingName) => { const addBinding = (name, value, icon, idx, isLoopBlock, bindingName) => {
if (!name) return if (!name) return
const runtimeBinding = determineRuntimeBinding( const runtimeBinding = determineRuntimeBinding(
@ -527,9 +532,24 @@ const automationActions = store => ({
runtimeName = `loop.${name}` runtimeName = `loop.${name}`
} else if (idx === 0) { } else if (idx === 0) {
runtimeName = `trigger.${name}` runtimeName = `trigger.${name}`
} else { } else if (
runtimeName = `steps.${pathSteps[idx]?.id}.${name}` currentBlock?.stepId === AutomationActionStepId.EXECUTE_SCRIPT
) {
const stepId = pathSteps[idx].id
if (!stepId) {
notifications.error("Error generating binding: Step ID not found.")
return null
} }
runtimeName = `steps["${stepId}"].${name}`
} else {
const stepId = pathSteps[idx].id
if (!stepId) {
notifications.error("Error generating binding: Step ID not found.")
return null
}
runtimeName = `steps.${stepId}.${name}`
}
return runtimeName return runtimeName
} }
@ -645,7 +665,6 @@ const automationActions = store => ({
console.error("Loop block missing.") console.error("Loop block missing.")
} }
} }
Object.entries(schema).forEach(([name, value]) => { Object.entries(schema).forEach(([name, value]) => {
addBinding(name, value, icon, blockIdx, isLoopBlock, bindingName) addBinding(name, value, icon, blockIdx, isLoopBlock, bindingName)
}) })

View File

@ -1,13 +1,20 @@
import { writable } from "svelte/store" import { writable } from "svelte/store"
type GotoFuncType = (path: string) => void
interface Store {
initialisated: boolean
goto: GotoFuncType
}
export function createNavigationStore() { export function createNavigationStore() {
const store = writable({ const store = writable<Store>({
initialisated: false, initialisated: false,
goto: undefined, goto: undefined as any,
}) })
const { set, subscribe } = store const { set, subscribe } = store
const init = gotoFunc => { const init = (gotoFunc: GotoFuncType) => {
if (typeof gotoFunc !== "function") { if (typeof gotoFunc !== "function") {
throw new Error( throw new Error(
`gotoFunc must be a function, found a "${typeof gotoFunc}" instead` `gotoFunc must be a function, found a "${typeof gotoFunc}" instead`

View File

@ -1,16 +1,21 @@
import { writable } from "svelte/store" import { writable } from "svelte/store"
import { PluginSource } from "constants/index"
import { API } from "api" import { API } from "api"
import { PluginSource } from "constants"
interface Plugin {
_id: string
}
export function createPluginsStore() { export function createPluginsStore() {
const { subscribe, set, update } = writable([]) const { subscribe, set, update } = writable<Plugin[]>([])
async function load() { async function load() {
const plugins = await API.getPlugins() const plugins = await API.getPlugins()
set(plugins) set(plugins)
} }
async function deletePlugin(pluginId) { async function deletePlugin(pluginId: string) {
await API.deletePlugin(pluginId) await API.deletePlugin(pluginId)
update(state => { update(state => {
state = state.filter(existing => existing._id !== pluginId) state = state.filter(existing => existing._id !== pluginId)
@ -18,8 +23,8 @@ export function createPluginsStore() {
}) })
} }
async function createPlugin(source, url, auth = null) { async function createPlugin(source: string, url: string, auth = null) {
let pluginData = { let pluginData: any = {
source, source,
url, url,
} }
@ -46,7 +51,7 @@ export function createPluginsStore() {
}) })
} }
async function uploadPlugin(file) { async function uploadPlugin(file: File) {
if (!file) { if (!file) {
return return
} }

View File

@ -0,0 +1,7 @@
const { vitePreprocess } = require("@sveltejs/vite-plugin-svelte")
const config = {
preprocess: vitePreprocess(),
}
module.exports = config

View File

@ -9,15 +9,9 @@
"noImplicitAny": true, "noImplicitAny": true,
"esModuleInterop": true, "esModuleInterop": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"incremental": true "incremental": true,
"skipLibCheck": true
}, },
"include": [ "include": ["./src/**/*"],
"./src/**/*" "exclude": ["node_modules", "**/*.json", "**/*.spec.ts", "**/*.spec.js"]
],
"exclude": [
"node_modules",
"**/*.json",
"**/*.spec.ts",
"**/*.spec.js"
]
} }

View File

@ -3,6 +3,7 @@ import replace from "@rollup/plugin-replace"
import { defineConfig, loadEnv } from "vite" import { defineConfig, loadEnv } from "vite"
import { viteStaticCopy } from "vite-plugin-static-copy" import { viteStaticCopy } from "vite-plugin-static-copy"
import path from "path" import path from "path"
import typescript from "@rollup/plugin-typescript"
const ignoredWarnings = [ const ignoredWarnings = [
"unused-export-let", "unused-export-let",
@ -35,7 +36,7 @@ export default defineConfig(({ mode }) => {
// Copy fonts to an additional path so that svelte's automatic // Copy fonts to an additional path so that svelte's automatic
// prefixing of the base URL path can still resolve assets // prefixing of the base URL path can still resolve assets
copyFonts("builder/fonts"), copyFonts("builder/fonts"),
] ]
return { return {
test: { test: {
@ -61,6 +62,7 @@ export default defineConfig(({ mode }) => {
sourcemap: !isProduction, sourcemap: !isProduction,
}, },
plugins: [ plugins: [
typescript({ outDir: "../server/builder/dist" }),
svelte({ svelte({
hot: !isProduction, hot: !isProduction,
emitCss: true, emitCss: true,

View File

@ -3096,7 +3096,6 @@
"name": "Text Field", "name": "Text Field",
"icon": "Text", "icon": "Text",
"styles": ["size"], "styles": ["size"],
"requiredAncestors": ["form"],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -3106,8 +3105,7 @@
{ {
"type": "field/string", "type": "field/string",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -3226,13 +3224,22 @@
} }
] ]
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "string"
}
] ]
}
}, },
"numberfield": { "numberfield": {
"name": "Number Field", "name": "Number Field",
"icon": "123", "icon": "123",
"styles": ["size"], "styles": ["size"],
"requiredAncestors": ["form"],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -3242,8 +3249,7 @@
{ {
"type": "field/number", "type": "field/number",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -3328,13 +3334,22 @@
} }
] ]
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "number"
}
] ]
}
}, },
"bigintfield": { "bigintfield": {
"name": "BigInt Field", "name": "BigInt Field",
"icon": "TagBold", "icon": "TagBold",
"styles": ["size"], "styles": ["size"],
"requiredAncestors": ["form"],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -3344,8 +3359,7 @@
{ {
"type": "field/bigint", "type": "field/bigint",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -3414,13 +3428,22 @@
} }
] ]
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "number"
}
] ]
}
}, },
"passwordfield": { "passwordfield": {
"name": "Password Field", "name": "Password Field",
"icon": "LockClosed", "icon": "LockClosed",
"styles": ["size"], "styles": ["size"],
"requiredAncestors": ["form"],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -3430,8 +3453,7 @@
{ {
"type": "field/string", "type": "field/string",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -3500,13 +3522,22 @@
} }
] ]
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "string"
}
] ]
}
}, },
"optionsfield": { "optionsfield": {
"name": "Options Picker", "name": "Options Picker",
"icon": "Menu", "icon": "Menu",
"styles": ["size"], "styles": ["size"],
"requiredAncestors": ["form"],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -3516,8 +3547,7 @@
{ {
"type": "field/options", "type": "field/options",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -3714,13 +3744,22 @@
} }
] ]
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "string"
}
] ]
}
}, },
"multifieldselect": { "multifieldselect": {
"name": "Multi-select Picker", "name": "Multi-select Picker",
"icon": "ViewList", "icon": "ViewList",
"styles": ["size"], "styles": ["size"],
"requiredAncestors": ["form"],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -3730,8 +3769,7 @@
{ {
"type": "field/array", "type": "field/array",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -3922,13 +3960,22 @@
} }
] ]
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "array"
}
] ]
}
}, },
"booleanfield": { "booleanfield": {
"name": "Checkbox", "name": "Checkbox",
"icon": "SelectBox", "icon": "SelectBox",
"editable": true, "editable": true,
"requiredAncestors": ["form"],
"size": { "size": {
"width": 400, "width": 400,
"height": 60 "height": 60
@ -3937,8 +3984,7 @@
{ {
"type": "field/boolean", "type": "field/boolean",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -4047,13 +4093,22 @@
} }
] ]
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "boolean"
}
] ]
}
}, },
"longformfield": { "longformfield": {
"name": "Long Form Field", "name": "Long Form Field",
"icon": "TextAlignLeft", "icon": "TextAlignLeft",
"styles": ["size"], "styles": ["size"],
"requiredAncestors": ["form"],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -4063,8 +4118,7 @@
{ {
"type": "field/longform", "type": "field/longform",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -4171,13 +4225,22 @@
} }
] ]
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "string"
}
] ]
}
}, },
"datetimefield": { "datetimefield": {
"name": "Date Picker", "name": "Date Picker",
"icon": "Date", "icon": "Date",
"styles": ["size"], "styles": ["size"],
"requiredAncestors": ["form"],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -4187,8 +4250,7 @@
{ {
"type": "field/datetime", "type": "field/datetime",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -4291,7 +4353,17 @@
} }
] ]
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "datetime"
}
] ]
}
}, },
"codescanner": { "codescanner": {
"name": "Barcode/QR Scanner", "name": "Barcode/QR Scanner",
@ -4305,8 +4377,7 @@
{ {
"type": "field/barcodeqr", "type": "field/barcodeqr",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -4451,7 +4522,17 @@
} }
] ]
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "string"
}
] ]
}
}, },
"signaturesinglefield": { "signaturesinglefield": {
"name": "Signature", "name": "Signature",
@ -4924,7 +5005,6 @@
"icon": "Brackets", "icon": "Brackets",
"styles": ["size"], "styles": ["size"],
"editable": true, "editable": true,
"requiredAncestors": ["form"],
"size": { "size": {
"width": 400, "width": 400,
"height": 100 "height": 100
@ -4933,8 +5013,7 @@
{ {
"type": "field/json", "type": "field/json",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -5014,7 +5093,17 @@
} }
] ]
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "string"
}
] ]
}
}, },
"s3upload": { "s3upload": {
"name": "S3 File Upload", "name": "S3 File Upload",
@ -5029,8 +5118,7 @@
{ {
"type": "field/s3", "type": "field/s3",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -5075,7 +5163,17 @@
"label": "Validation", "label": "Validation",
"key": "validation" "key": "validation"
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "array"
}
] ]
}
}, },
"dataprovider": { "dataprovider": {
"name": "Data Provider", "name": "Data Provider",
@ -7643,7 +7741,6 @@
"name": "User List Field", "name": "User List Field",
"icon": "UserGroup", "icon": "UserGroup",
"styles": ["size"], "styles": ["size"],
"requiredAncestors": ["form"],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -7653,8 +7750,7 @@
{ {
"type": "field/bb_reference", "type": "field/bb_reference",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -7744,14 +7840,23 @@
} }
] ]
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "array"
}
] ]
}
}, },
"bbreferencesinglefield": { "bbreferencesinglefield": {
"devComment": "As bb reference is only used for user subtype for now, we are using user for icon and labels", "devComment": "As bb reference is only used for user subtype for now, we are using user for icon and labels",
"name": "User Field", "name": "User Field",
"icon": "User", "icon": "User",
"styles": ["size"], "styles": ["size"],
"requiredAncestors": ["form"],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -7761,8 +7866,7 @@
{ {
"type": "field/bb_reference_single", "type": "field/bb_reference_single",
"label": "Field", "label": "Field",
"key": "field", "key": "field"
"required": true
}, },
{ {
"type": "text", "type": "text",
@ -7852,6 +7956,16 @@
} }
] ]
} }
],
"context": {
"type": "static",
"values": [
{
"label": "Value",
"key": "value",
"type": "string"
}
] ]
} }
}
} }

View File

@ -18,8 +18,6 @@
export let palette export let palette
export let c1, c2, c3, c4, c5 export let c1, c2, c3, c4, c5
// Area specific props
export let area
export let stacked export let stacked
export let gradient export let gradient

View File

@ -1,7 +1,10 @@
<script> <script>
import Placeholder from "../Placeholder.svelte"
import { getContext, onDestroy } from "svelte" import { getContext, onDestroy } from "svelte"
import { writable } from "svelte/store"
import { Icon } from "@budibase/bbui" import { Icon } from "@budibase/bbui"
import { memo } from "@budibase/frontend-core"
import Placeholder from "../Placeholder.svelte"
import InnerForm from "./InnerForm.svelte"
export let label export let label
export let field export let field
@ -20,26 +23,39 @@
const formContext = getContext("form") const formContext = getContext("form")
const formStepContext = getContext("form-step") const formStepContext = getContext("form-step")
const fieldGroupContext = getContext("field-group") const fieldGroupContext = getContext("field-group")
const { styleable, builderStore } = getContext("sdk") const { styleable, builderStore, Provider } = getContext("sdk")
const component = getContext("component") const component = getContext("component")
// Register field with form // Register field with form
const formApi = formContext?.formApi const formApi = formContext?.formApi
const labelPos = fieldGroupContext?.labelPosition || "above" const labelPos = fieldGroupContext?.labelPosition || "above"
let formField
let touched = false let touched = false
let labelNode let labelNode
$: formStep = formStepContext ? $formStepContext || 1 : 1 // Memoize values required to register the field to avoid loops
$: formField = formApi?.registerField( const formStep = formStepContext || writable(1)
field, const fieldInfo = memo({
field: field || $component.name,
type, type,
defaultValue, defaultValue,
disabled, disabled,
readonly, readonly,
validation, validation,
formStep formStep: $formStep || 1,
) })
$: fieldInfo.set({
field: field || $component.name,
type,
defaultValue,
disabled,
readonly,
validation,
formStep: $formStep || 1,
})
$: registerField($fieldInfo)
$: schemaType = $: schemaType =
fieldSchema?.type !== "formula" && fieldSchema?.type !== "bigint" fieldSchema?.type !== "formula" && fieldSchema?.type !== "bigint"
? fieldSchema?.type ? fieldSchema?.type
@ -58,6 +74,18 @@
// Determine label class from position // Determine label class from position
$: labelClass = labelPos === "above" ? "" : `spectrum-FieldLabel--${labelPos}` $: labelClass = labelPos === "above" ? "" : `spectrum-FieldLabel--${labelPos}`
const registerField = info => {
formField = formApi?.registerField(
info.field,
info.type,
info.defaultValue,
info.disabled,
info.readonly,
info.validation,
info.formStep
)
}
const updateLabel = e => { const updateLabel = e => {
if (touched) { if (touched) {
builderStore.actions.updateProp("label", e.target.textContent) builderStore.actions.updateProp("label", e.target.textContent)
@ -71,14 +99,27 @@
}) })
</script> </script>
<div <Provider data={{ value: fieldState?.value }}>
{#if !formContext}
<InnerForm
{disabled}
{readonly}
currentStep={writable(1)}
provideContext={false}
>
<svelte:self {...$$props} bind:fieldState bind:fieldApi bind:fieldSchema>
<slot />
</svelte:self>
</InnerForm>
{:else}
<div
class="spectrum-Form-item" class="spectrum-Form-item"
class:span-2={span === 2} class:span-2={span === 2}
class:span-3={span === 3} class:span-3={span === 3}
class:span-6={span === 6 || !span} class:span-6={span === 6 || !span}
use:styleable={$component.styles} use:styleable={$component.styles}
class:above={labelPos === "above"} class:above={labelPos === "above"}
> >
{#key $component.editing} {#key $component.editing}
<label <label
bind:this={labelNode} bind:this={labelNode}
@ -94,9 +135,7 @@
</label> </label>
{/key} {/key}
<div class="spectrum-Form-itemField"> <div class="spectrum-Form-itemField">
{#if !formContext} {#if !fieldState}
<Placeholder text="Form components need to be wrapped in a form" />
{:else if !fieldState}
<Placeholder /> <Placeholder />
{:else if schemaType && schemaType !== type && !["options", "longform"].includes(type)} {:else if schemaType && schemaType !== type && !["options", "longform"].includes(type)}
<Placeholder <Placeholder
@ -116,7 +155,9 @@
{/if} {/if}
{/if} {/if}
</div> </div>
</div> </div>
{/if}
</Provider>
<style> <style>
:global(.form-block .spectrum-Form-item.span-2) { :global(.form-block .spectrum-Form-item.span-2) {

View File

@ -5,7 +5,6 @@
import { writable } from "svelte/store" import { writable } from "svelte/store"
export let dataSource export let dataSource
export let theme
export let size export let size
export let disabled = false export let disabled = false
export let readonly = false export let readonly = false
@ -113,11 +112,9 @@
{#key resetKey} {#key resetKey}
<InnerForm <InnerForm
{dataSource} {dataSource}
{theme}
{size} {size}
{disabled} {disabled}
{readonly} {readonly}
{actionType}
{schema} {schema}
{definition} {definition}
{initialValues} {initialValues}

View File

@ -14,6 +14,10 @@
export let disableSchemaValidation = false export let disableSchemaValidation = false
export let editAutoColumns = false export let editAutoColumns = false
// For internal use only, to disable context when being used with standalone
// fields
export let provideContext = true
// We export this store so that when we remount the inner form we can still // We export this store so that when we remount the inner form we can still
// persist what step we're on // persist what step we're on
export let currentStep export let currentStep
@ -442,8 +446,14 @@
] ]
</script> </script>
<Provider {actions} data={dataContext}> {#if provideContext}
<Provider {actions} data={dataContext}>
<div use:styleable={$component.styles} class={size}> <div use:styleable={$component.styles} class={size}>
<slot /> <slot />
</div> </div>
</Provider> </Provider>
{:else}
<div use:styleable={$component.styles} class={size}>
<slot />
</div>
{/if}

View File

@ -61,6 +61,9 @@ export async function run({
inputs: ServerLogStepInputs inputs: ServerLogStepInputs
appId: string appId: string
}): Promise<ServerLogStepOutputs> { }): Promise<ServerLogStepOutputs> {
if (typeof inputs.text !== "string") {
inputs.text = JSON.stringify(inputs.text)
}
const message = `App ${appId} - ${inputs.text}` const message = `App ${appId} - ${inputs.text}`
console.log(message) console.log(message)
return { return {

View File

@ -1,50 +1,123 @@
import { getConfig, afterAll as _afterAll, runStep, actions } from "./utilities" import { createAutomationBuilder } from "./utilities/AutomationTestBuilder"
import * as automation from "../index"
import * as setup from "./utilities"
import { Table } from "@budibase/types"
describe("test the execute script action", () => { describe("Execute Script Automations", () => {
let config = getConfig() let config = setup.getConfig(),
table: Table
beforeAll(async () => { beforeEach(async () => {
await automation.init()
await config.init() await config.init()
}) table = await config.createTable()
afterAll(_afterAll) await config.createRow()
it("should be able to execute a script", async () => {
const res = await runStep(config, actions.EXECUTE_SCRIPT.stepId, {
code: "return 1 + 1",
})
expect(res.value).toEqual(2)
expect(res.success).toEqual(true)
}) })
it("should handle a null value", async () => { afterAll(setup.afterAll)
const res = await runStep(config, actions.EXECUTE_SCRIPT.stepId, {
code: null, it("should execute a basic script and return the result", async () => {
}) const builder = createAutomationBuilder({
expect(res.response.message).toEqual("Invalid inputs") name: "Basic Script Execution",
expect(res.success).toEqual(false)
}) })
it("should be able to get a value from context", async () => { const results = await builder
const res = await runStep( .appAction({ fields: {} })
config, .executeScript({ code: "return 2 + 2" })
actions.EXECUTE_SCRIPT.stepId, .run()
expect(results.steps[0].outputs.value).toEqual(4)
})
it("should access bindings from previous steps", async () => {
const builder = createAutomationBuilder({
name: "Access Bindings",
})
const results = await builder
.appAction({ fields: { data: [1, 2, 3] } })
.executeScript(
{ {
code: "return steps.map(d => d.value)", code: "return trigger.fields.data.map(x => x * 2)",
}, },
{ { stepId: "binding-script-step" }
steps: [{ value: 0 }, { value: 1 }],
}
) )
expect(res.value).toEqual([0, 1]) .run()
expect(res.response).toBeUndefined()
expect(res.success).toEqual(true) expect(results.steps[0].outputs.value).toEqual([2, 4, 6])
}) })
it("should be able to handle an error gracefully", async () => { it("should handle script execution errors gracefully", async () => {
const res = await runStep(config, actions.EXECUTE_SCRIPT.stepId, { const builder = createAutomationBuilder({
code: "return something.map(x => x.name)", name: "Handle Script Errors",
}) })
expect(res.response).toEqual("ReferenceError: something is not defined")
expect(res.success).toEqual(false) const results = await builder
.appAction({ fields: {} })
.executeScript({ code: "return nonexistentVariable.map(x => x)" })
.run()
expect(results.steps[0].outputs.response).toContain(
"ReferenceError: nonexistentVariable is not defined"
)
expect(results.steps[0].outputs.success).toEqual(false)
})
it("should handle conditional logic in scripts", async () => {
const builder = createAutomationBuilder({
name: "Conditional Script Logic",
})
const results = await builder
.appAction({ fields: { value: 10 } })
.executeScript({
code: `
if (trigger.fields.value > 5) {
return "Value is greater than 5";
} else {
return "Value is 5 or less";
}
`,
})
.run()
expect(results.steps[0].outputs.value).toEqual("Value is greater than 5")
})
it("should use multiple steps and validate script execution", async () => {
const builder = createAutomationBuilder({
name: "Multi-Step Script Execution",
})
const results = await builder
.appAction({ fields: {} })
.serverLog(
{ text: "Starting multi-step automation" },
{ stepId: "start-log-step" }
)
.createRow(
{ row: { name: "Test Row", value: 42, tableId: table._id } },
{ stepId: "abc123" }
)
.executeScript(
{
code: `
const createdRow = steps['abc123'];
return createdRow.row.value * 2;
`,
},
{ stepId: "ScriptingStep1" }
)
.serverLog({
text: `Final result is {{ steps.ScriptingStep1.value }}`,
})
.run()
expect(results.steps[0].outputs.message).toContain(
"Starting multi-step automation"
)
expect(results.steps[1].outputs.row.value).toEqual(42)
expect(results.steps[2].outputs.value).toEqual(84)
expect(results.steps[3].outputs.message).toContain("Final result is 84")
}) })
}) })

View File

@ -34,6 +34,7 @@ import {
SearchFilters, SearchFilters,
Branch, Branch,
FilterStepInputs, FilterStepInputs,
ExecuteScriptStepInputs,
} from "@budibase/types" } from "@budibase/types"
import TestConfiguration from "../../../tests/utilities/TestConfiguration" import TestConfiguration from "../../../tests/utilities/TestConfiguration"
import * as setup from "../utilities" import * as setup from "../utilities"
@ -201,6 +202,18 @@ class BaseStepBuilder {
) )
} }
executeScript(
input: ExecuteScriptStepInputs,
opts?: { stepName?: string; stepId?: string }
): this {
return this.step(
AutomationActionStepId.EXECUTE_SCRIPT,
BUILTIN_ACTION_DEFINITIONS.EXECUTE_SCRIPT,
input,
opts
)
}
filter(input: FilterStepInputs): this { filter(input: FilterStepInputs): this {
return this.step( return this.step(
AutomationActionStepId.FILTER, AutomationActionStepId.FILTER,

View File

@ -385,7 +385,7 @@ class Orchestrator {
stepIdx: number, stepIdx: number,
pathIdx?: number pathIdx?: number
): Promise<number> { ): Promise<number> {
await processObject(loopStep.inputs, this.processContext(this.context)) await processObject(loopStep.inputs, this.mergeContexts(this.context))
const iterations = getLoopIterations(loopStep) const iterations = getLoopIterations(loopStep)
let stepToLoopIndex = stepIdx + 1 let stepToLoopIndex = stepIdx + 1
let pathStepIdx = (pathIdx || stepIdx) + 1 let pathStepIdx = (pathIdx || stepIdx) + 1
@ -573,14 +573,14 @@ class Orchestrator {
for (const [field, value] of Object.entries(filters[filterKey])) { for (const [field, value] of Object.entries(filters[filterKey])) {
const fromContext = processStringSync( const fromContext = processStringSync(
field, field,
this.processContext(this.context) this.mergeContexts(this.context)
) )
toFilter[field] = fromContext toFilter[field] = fromContext
if (typeof value === "string" && findHBSBlocks(value).length > 0) { if (typeof value === "string" && findHBSBlocks(value).length > 0) {
const processedVal = processStringSync( const processedVal = processStringSync(
value, value,
this.processContext(this.context) this.mergeContexts(this.context)
) )
filters[filterKey][field] = processedVal filters[filterKey][field] = processedVal
@ -637,7 +637,7 @@ class Orchestrator {
const stepFn = await this.getStepFunctionality(step.stepId) const stepFn = await this.getStepFunctionality(step.stepId)
let inputs = await processObject( let inputs = await processObject(
originalStepInput, originalStepInput,
this.processContext(this.context) this.mergeContexts(this.context)
) )
inputs = automationUtils.cleanInputValues(inputs, step.schema.inputs) inputs = automationUtils.cleanInputValues(inputs, step.schema.inputs)
@ -645,7 +645,7 @@ class Orchestrator {
inputs: inputs, inputs: inputs,
appId: this.appId, appId: this.appId,
emitter: this.emitter, emitter: this.emitter,
context: this.context, context: this.mergeContexts(this.context),
}) })
this.handleStepOutput(step, outputs, loopIteration) this.handleStepOutput(step, outputs, loopIteration)
} }
@ -665,8 +665,8 @@ class Orchestrator {
return null return null
} }
private processContext(context: AutomationContext) { private mergeContexts(context: AutomationContext) {
const processContext = { const mergeContexts = {
...context, ...context,
steps: { steps: {
...context.steps, ...context.steps,
@ -674,7 +674,7 @@ class Orchestrator {
...context.stepsByName, ...context.stepsByName,
}, },
} }
return processContext return mergeContexts
} }
private handleStepOutput( private handleStepOutput(

View File

@ -8225,6 +8225,13 @@ chokidar@3.5.3, chokidar@^3.5.2, chokidar@^3.5.3:
optionalDependencies: optionalDependencies:
fsevents "~2.3.2" fsevents "~2.3.2"
chokidar@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41"
integrity sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==
dependencies:
readdirp "^4.0.1"
chownr@^1.1.1: chownr@^1.1.1:
version "1.1.4" version "1.1.4"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
@ -11176,6 +11183,11 @@ fd-slicer@~1.1.0:
dependencies: dependencies:
pend "~1.2.0" pend "~1.2.0"
fdir@^6.2.0:
version "6.4.2"
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.2.tgz#ddaa7ce1831b161bc3657bb99cb36e1622702689"
integrity sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==
fecha@^4.2.0: fecha@^4.2.0:
version "4.2.3" version "4.2.3"
resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd"
@ -15893,6 +15905,11 @@ mongodb@6.7.0:
bson "^6.7.0" bson "^6.7.0"
mongodb-connection-string-url "^3.0.0" mongodb-connection-string-url "^3.0.0"
mri@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
ms@2.1.2: ms@2.1.2:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
@ -18840,6 +18857,11 @@ readdirp@^3.6.0, readdirp@~3.6.0:
dependencies: dependencies:
picomatch "^2.2.1" picomatch "^2.2.1"
readdirp@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a"
integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==
readline-sync@^1.4.9: readline-sync@^1.4.9:
version "1.4.10" version "1.4.10"
resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b" resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b"
@ -19424,6 +19446,13 @@ rxjs@^7.5.5:
dependencies: dependencies:
tslib "^2.1.0" tslib "^2.1.0"
sade@^1.7.4:
version "1.8.1"
resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701"
integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==
dependencies:
mri "^1.1.0"
safe-array-concat@^1.1.2: safe-array-concat@^1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb"
@ -20259,7 +20288,16 @@ string-range@~1.2, string-range@~1.2.1:
resolved "https://registry.yarnpkg.com/string-range/-/string-range-1.2.2.tgz#a893ed347e72299bc83befbbf2a692a8d239d5dd" resolved "https://registry.yarnpkg.com/string-range/-/string-range-1.2.2.tgz#a893ed347e72299bc83befbbf2a692a8d239d5dd"
integrity sha512-tYft6IFi8SjplJpxCUxyqisD3b+R2CSkomrtJYCkvuf1KuCAWgz7YXt4O0jip7efpfCemwHEzTEAO8EuOYgh3w== integrity sha512-tYft6IFi8SjplJpxCUxyqisD3b+R2CSkomrtJYCkvuf1KuCAWgz7YXt4O0jip7efpfCemwHEzTEAO8EuOYgh3w==
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: "string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
version "4.2.3" version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -20351,7 +20389,7 @@ stringify-object@^3.2.1:
is-obj "^1.0.1" is-obj "^1.0.1"
is-regexp "^1.0.0" is-regexp "^1.0.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: "strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1" version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@ -20365,6 +20403,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
dependencies: dependencies:
ansi-regex "^4.1.0" ansi-regex "^4.1.0"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1: strip-ansi@^7.0.1:
version "7.0.1" version "7.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
@ -20570,6 +20615,17 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
svelte-check@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-4.1.0.tgz#4389c1c88aa24f3d06fe0df94f9075a55017256d"
integrity sha512-AflEZYqI578KuDZcpcorPSf597LStxlkN7XqXi38u09zlHODVKd7c+7OuubGzbhgGRUqNTdQCZ+Ga96iRXEf2g==
dependencies:
"@jridgewell/trace-mapping" "^0.3.25"
chokidar "^4.0.1"
fdir "^6.2.0"
picocolors "^1.0.0"
sade "^1.7.4"
svelte-dnd-action@^0.9.8: svelte-dnd-action@^0.9.8:
version "0.9.22" version "0.9.22"
resolved "https://registry.yarnpkg.com/svelte-dnd-action/-/svelte-dnd-action-0.9.22.tgz#003eee9dddb31d8c782f6832aec8b1507fff194d" resolved "https://registry.yarnpkg.com/svelte-dnd-action/-/svelte-dnd-action-0.9.22.tgz#003eee9dddb31d8c782f6832aec8b1507fff194d"
@ -22170,7 +22226,7 @@ worker-farm@1.7.0:
dependencies: dependencies:
errno "~0.1.7" errno "~0.1.7"
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0" version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@ -22188,6 +22244,15 @@ wrap-ansi@^5.1.0:
string-width "^3.0.0" string-width "^3.0.0"
strip-ansi "^5.0.0" strip-ansi "^5.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^8.1.0: wrap-ansi@^8.1.0:
version "8.1.0" version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"