Merge branch 'develop' into api-create-app

This commit is contained in:
Pedro Silva 2022-10-07 18:06:57 +01:00
commit 3abf993779
51 changed files with 1179 additions and 561 deletions

View File

@ -1,5 +1,5 @@
{ {
"version": "2.0.24-alpha.0", "version": "2.0.24-alpha.3",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

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

View File

@ -214,6 +214,31 @@ export = class RedisWrapper {
} }
} }
async bulkGet(keys: string[]) {
const db = this._db
const prefixedKeys = keys.map(key => addDbPrefix(db, key))
let response = await this.getClient().mget(prefixedKeys)
if (Array.isArray(response)) {
let final: any = {}
let count = 0
for (let result of response) {
if (result) {
let parsed
try {
parsed = JSON.parse(result)
} catch (err) {
parsed = result
}
final[keys[count]] = parsed
}
count++
}
return final
} else {
throw new Error(`Invalid response: ${response}`)
}
}
async store(key: string, value: any, expirySeconds: number | null = null) { async store(key: string, value: any, expirySeconds: number | null = null) {
const db = this._db const db = this._db
if (typeof value === "object") { if (typeof value === "object") {

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/bbui", "name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.", "description": "A UI solution used in the different Budibase projects.",
"version": "2.0.24-alpha.0", "version": "2.0.24-alpha.3",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",
@ -38,7 +38,7 @@
], ],
"dependencies": { "dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1", "@adobe/spectrum-css-workflow-icons": "^1.2.1",
"@budibase/string-templates": "2.0.24-alpha.0", "@budibase/string-templates": "2.0.24-alpha.3",
"@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2", "@spectrum-css/avatar": "^3.0.2",

View File

@ -1,5 +1,5 @@
import filterTests from "../support/filterTests" import filterTests from "../support/filterTests"
const interact = require('../support/interact') const interact = require("../support/interact")
filterTests(["smoke", "all"], () => { filterTests(["smoke", "all"], () => {
context("Query Level Transformers", () => { context("Query Level Transformers", () => {

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "2.0.24-alpha.0", "version": "2.0.24-alpha.3",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -71,10 +71,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "2.0.24-alpha.0", "@budibase/bbui": "2.0.24-alpha.3",
"@budibase/client": "2.0.24-alpha.0", "@budibase/client": "2.0.24-alpha.3",
"@budibase/frontend-core": "2.0.24-alpha.0", "@budibase/frontend-core": "2.0.24-alpha.3",
"@budibase/string-templates": "2.0.24-alpha.0", "@budibase/string-templates": "2.0.24-alpha.3",
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",

View File

@ -185,43 +185,42 @@ export const makeComponentUnique = component => {
// Replace component ID // Replace component ID
const oldId = component._id const oldId = component._id
const newId = Helpers.uuid() const newId = Helpers.uuid()
component._id = newId let definition = JSON.stringify(component)
if (component._children?.length) { // Replace all instances of this ID in HBS bindings
let children = JSON.stringify(component._children) definition = definition.replace(new RegExp(oldId, "g"), newId)
// Replace all instances of this ID in child HBS bindings // Replace all instances of this ID in JS bindings
children = children.replace(new RegExp(oldId, "g"), newId) const bindings = findHBSBlocks(definition)
bindings.forEach(binding => {
// JSON.stringify will have escaped double quotes, so we need
// to account for that
let sanitizedBinding = binding.replace(/\\"/g, '"')
// Replace all instances of this ID in child JS bindings // Check if this is a valid JS binding
const bindings = findHBSBlocks(children) let js = decodeJSBinding(sanitizedBinding)
bindings.forEach(binding => { if (js != null) {
// JSON.stringify will have escaped double quotes, so we need // Replace ID inside JS binding
// to account for that js = js.replace(new RegExp(oldId, "g"), newId)
let sanitizedBinding = binding.replace(/\\"/g, '"')
// Check if this is a valid JS binding // Create new valid JS binding
let js = decodeJSBinding(sanitizedBinding) let newBinding = encodeJSBinding(js)
if (js != null) {
// Replace ID inside JS binding
js = js.replace(new RegExp(oldId, "g"), newId)
// Create new valid JS binding // Replace escaped double quotes
let newBinding = encodeJSBinding(js) newBinding = newBinding.replace(/"/g, '\\"')
// Replace escaped double quotes // Insert new JS back into binding.
newBinding = newBinding.replace(/"/g, '\\"') // A single string replace here is better than a regex as
// the binding contains special characters, and we only need
// to replace a single instance.
definition = definition.replace(binding, newBinding)
}
})
// Insert new JS back into binding. // Recurse on all children
// A single string replace here is better than a regex as component = JSON.parse(definition)
// the binding contains special characters, and we only need return {
// to replace a single instance. ...component,
children = children.replace(binding, newBinding) _children: component._children?.map(makeComponentUnique),
}
})
// Recurse on all children
component._children = JSON.parse(children)
component._children.forEach(makeComponentUnique)
} }
} }

View File

@ -169,7 +169,12 @@ export const getComponentBindableProperties = (asset, componentId) => {
/** /**
* Gets all data provider components above a component. * Gets all data provider components above a component.
*/ */
export const getContextProviderComponents = (asset, componentId, type) => { export const getContextProviderComponents = (
asset,
componentId,
type,
options = { includeSelf: false }
) => {
if (!asset || !componentId) { if (!asset || !componentId) {
return [] return []
} }
@ -177,7 +182,9 @@ export const getContextProviderComponents = (asset, componentId, type) => {
// Get the component tree leading up to this component, ignoring the component // Get the component tree leading up to this component, ignoring the component
// itself // itself
const path = findComponentPath(asset.props, componentId) const path = findComponentPath(asset.props, componentId)
path.pop() if (!options?.includeSelf) {
path.pop()
}
// Filter by only data provider components // Filter by only data provider components
return path.filter(component => { return path.filter(component => {
@ -798,6 +805,17 @@ export const buildFormSchema = component => {
if (!component) { if (!component) {
return schema return schema
} }
// If this is a form block, simply use the fields setting
if (component._component.endsWith("formblock")) {
let schema = {}
component.fields?.forEach(field => {
schema[field] = { type: "string" }
})
return schema
}
// Otherwise find all field component children
const settings = getComponentSettings(component._component) const settings = getComponentSettings(component._component)
const fieldSetting = settings.find( const fieldSetting = settings.find(
setting => setting.key === "field" && setting.type.startsWith("field/") setting => setting.key === "field" && setting.type.startsWith("field/")

View File

@ -330,6 +330,16 @@ export const getFrontendStore = () => {
return state return state
}) })
}, },
sendEvent: (name, payload) => {
const { previewEventHandler } = get(store)
previewEventHandler?.(name, payload)
},
registerEventHandler: handler => {
store.update(state => {
state.previewEventHandler = handler
return state
})
},
}, },
layouts: { layouts: {
select: layoutId => { select: layoutId => {
@ -611,7 +621,7 @@ export const getFrontendStore = () => {
// Make new component unique if copying // Make new component unique if copying
if (!cut) { if (!cut) {
makeComponentUnique(componentToPaste) componentToPaste = makeComponentUnique(componentToPaste)
} }
newComponentId = componentToPaste._id newComponentId = componentToPaste._id
@ -891,6 +901,50 @@ export const getFrontendStore = () => {
component[name] = value component[name] = value
}) })
}, },
requestEjectBlock: componentId => {
store.actions.preview.sendEvent("eject-block", componentId)
},
handleEjectBlock: async (componentId, ejectedDefinition) => {
let nextSelectedComponentId
await store.actions.screens.patch(screen => {
const block = findComponent(screen.props, componentId)
const parent = findComponentParent(screen.props, componentId)
// Sanity check
if (!block || !parent?._children?.length) {
return false
}
// Attach block children back into ejected definition, using the
// _containsSlot flag to know where to insert them
const slotContainer = findAllMatchingComponents(
ejectedDefinition,
x => x._containsSlot
)[0]
if (slotContainer) {
delete slotContainer._containsSlot
slotContainer._children = [
...(slotContainer._children || []),
...(block._children || []),
]
}
// Replace block with ejected definition
ejectedDefinition = makeComponentUnique(ejectedDefinition)
const index = parent._children.findIndex(x => x._id === componentId)
parent._children[index] = ejectedDefinition
nextSelectedComponentId = ejectedDefinition._id
})
// Select new root component
if (nextSelectedComponentId) {
store.update(state => {
state.selectedComponentId = nextSelectedComponentId
return state
})
}
},
}, },
links: { links: {
save: async (url, title) => { save: async (url, title) => {

View File

@ -21,6 +21,7 @@
export let key export let key
export let actions export let actions
export let bindings = [] export let bindings = []
export let nested
$: showAvailableActions = !actions?.length $: showAvailableActions = !actions?.length
@ -187,6 +188,7 @@
this={selectedActionComponent} this={selectedActionComponent}
parameters={selectedAction.parameters} parameters={selectedAction.parameters}
bindings={allBindings} bindings={allBindings}
{nested}
/> />
</div> </div>
{/key} {/key}

View File

@ -12,6 +12,7 @@
export let value = [] export let value = []
export let name export let name
export let bindings export let bindings
export let nested
let drawer let drawer
let tmpValue let tmpValue
@ -90,6 +91,7 @@
eventType={name} eventType={name}
{bindings} {bindings}
{key} {key}
{nested}
/> />
</Drawer> </Drawer>

View File

@ -10,11 +10,13 @@
export let parameters export let parameters
export let bindings = [] export let bindings = []
export let nested
$: formComponents = getContextProviderComponents( $: formComponents = getContextProviderComponents(
$currentAsset, $currentAsset,
$store.selectedComponentId, $store.selectedComponentId,
"form" "form",
{ includeSelf: nested }
) )
$: schemaComponents = getContextProviderComponents( $: schemaComponents = getContextProviderComponents(
$currentAsset, $currentAsset,

View File

@ -0,0 +1,13 @@
<script>
import { ActionButton } from "@budibase/bbui"
const eject = () => {
document.dispatchEvent(
new KeyboardEvent("keydown", { key: "e", ctrlKey: true })
)
}
</script>
<div>
<ActionButton secondary on:click={eject}>Eject block</ActionButton>
</div>

View File

@ -20,6 +20,7 @@
export let componentBindings = [] export let componentBindings = []
export let nested = false export let nested = false
export let highlighted = false export let highlighted = false
export let info = null
$: nullishValue = value == null || value === "" $: nullishValue = value == null || value === ""
$: allBindings = getAllBindings(bindings, componentBindings, nested) $: allBindings = getAllBindings(bindings, componentBindings, nested)
@ -94,11 +95,15 @@
bindings={allBindings} bindings={allBindings}
name={key} name={key}
text={label} text={label}
{nested}
{key} {key}
{type} {type}
{...props} {...props}
/> />
</div> </div>
{#if info}
<div class="text">{@html info}</div>
{/if}
</div> </div>
<style> <style>
@ -123,4 +128,9 @@
.control { .control {
position: relative; position: relative;
} }
.text {
margin-top: var(--spectrum-global-dimension-size-65);
font-size: var(--spectrum-global-dimension-font-size-75);
color: var(--grey-6);
}
</style> </style>

View File

@ -4,6 +4,7 @@
export let value export let value
export let bindings export let bindings
export let placeholder
$: urlOptions = $store.screens $: urlOptions = $store.screens
.map(screen => screen.routing?.route) .map(screen => screen.routing?.route)
@ -13,6 +14,7 @@
<DrawerBindableCombobox <DrawerBindableCombobox
{value} {value}
{bindings} {bindings}
{placeholder}
on:change on:change
options={urlOptions} options={urlOptions}
appendBindingsAsOptions={false} appendBindingsAsOptions={false}

View File

@ -98,11 +98,21 @@
`./components/${$selectedComponent?._id}/new` `./components/${$selectedComponent?._id}/new`
) )
// Register handler to send custom to the preview
$: store.actions.preview.registerEventHandler((name, payload) => {
iframe?.contentWindow.postMessage(
JSON.stringify({
name,
payload,
isBudibaseEvent: true,
runtimeEvent: true,
})
)
})
// Update the iframe with the builder info to render the correct preview // Update the iframe with the builder info to render the correct preview
const refreshContent = message => { const refreshContent = message => {
if (iframe) { iframe?.contentWindow.postMessage(message)
iframe.contentWindow.postMessage(message)
}
} }
const receiveMessage = message => { const receiveMessage = message => {
@ -198,6 +208,9 @@
block: "center", block: "center",
}) })
} }
} else if (type === "eject-block") {
const { id, definition } = data
await store.actions.components.handleEjectBlock(id, definition)
} else if (type === "reload-plugin") { } else if (type === "reload-plugin") {
await store.actions.components.refreshDefinitions() await store.actions.components.refreshDefinitions()
} else { } else {

View File

@ -4,7 +4,9 @@
export let component export let component
$: definition = store.actions.components.getDefinition(component?._component)
$: noPaste = !$store.componentToPaste $: noPaste = !$store.componentToPaste
$: isBlock = definition?.block === true
const keyboardEvent = (key, ctrlKey = false) => { const keyboardEvent = (key, ctrlKey = false) => {
document.dispatchEvent( document.dispatchEvent(
@ -30,6 +32,15 @@
> >
Delete Delete
</MenuItem> </MenuItem>
{#if isBlock}
<MenuItem
icon="Export"
keyBind="Ctrl+E"
on:click={() => keyboardEvent("e", true)}
>
Eject block
</MenuItem>
{/if}
<MenuItem <MenuItem
icon="ChevronUp" icon="ChevronUp"
keyBind="Ctrl+!ArrowUp" keyBind="Ctrl+!ArrowUp"

View File

@ -7,7 +7,9 @@
import ConfirmDialog from "components/common/ConfirmDialog.svelte" import ConfirmDialog from "components/common/ConfirmDialog.svelte"
let confirmDeleteDialog let confirmDeleteDialog
let confirmEjectDialog
let componentToDelete let componentToDelete
let componentToEject
const keyHandlers = { const keyHandlers = {
["^ArrowUp"]: async component => { ["^ArrowUp"]: async component => {
@ -29,6 +31,10 @@
store.actions.components.copy(component) store.actions.components.copy(component)
await store.actions.components.paste(component, "below") await store.actions.components.paste(component, "below")
}, },
["^e"]: component => {
componentToEject = component
confirmEjectDialog.show()
},
["^Enter"]: () => { ["^Enter"]: () => {
$goto("./new") $goto("./new")
}, },
@ -124,3 +130,10 @@
okText="Delete Component" okText="Delete Component"
onOk={() => store.actions.components.delete(componentToDelete)} onOk={() => store.actions.components.delete(componentToDelete)}
/> />
<ConfirmDialog
bind:this={confirmEjectDialog}
title="Eject block"
body={`Ejecting a block breaks it down into multiple components and cannot be undone. Are you sure you want to eject "${componentToEject?._instanceName}"?`}
onOk={() => store.actions.components.requestEjectBlock(componentToEject?._id)}
okText="Eject block"
/>

View File

@ -4,6 +4,7 @@
import { store } from "builderStore" import { store } from "builderStore"
import PropertyControl from "components/design/settings/controls/PropertyControl.svelte" import PropertyControl from "components/design/settings/controls/PropertyControl.svelte"
import ResetFieldsButton from "components/design/settings/controls/ResetFieldsButton.svelte" import ResetFieldsButton from "components/design/settings/controls/ResetFieldsButton.svelte"
import EjectBlockButton from "components/design/settings/controls/EjectBlockButton.svelte"
import { getComponentForSetting } from "components/design/settings/componentSettings" import { getComponentForSetting } from "components/design/settings/componentSettings"
export let componentDefinition export let componentDefinition
@ -12,20 +13,29 @@
export let componentBindings export let componentBindings
export let isScreen = false export let isScreen = false
$: sections = getSections(componentDefinition) $: sections = getSections(componentInstance, componentDefinition, isScreen)
const getSections = definition => { const getSections = (instance, definition, isScreen) => {
const settings = definition?.settings ?? [] const settings = definition?.settings ?? []
const generalSettings = settings.filter(setting => !setting.section) const generalSettings = settings.filter(setting => !setting.section)
const customSections = settings.filter(setting => setting.section) const customSections = settings.filter(setting => setting.section)
return [ let sections = [
{ {
name: "General", name: "General",
info: componentDefinition?.info,
settings: generalSettings, settings: generalSettings,
}, },
...(customSections || []), ...(customSections || []),
] ]
// Filter out settings which shouldn't be rendered
sections.forEach(section => {
section.settings.forEach(setting => {
setting.visible = canRenderControl(instance, setting, isScreen)
})
section.visible = section.settings.some(setting => setting.visible)
})
return sections
} }
const updateSetting = async (key, value) => { const updateSetting = async (key, value) => {
@ -36,7 +46,7 @@
} }
} }
const canRenderControl = (setting, isScreen) => { const canRenderControl = (instance, setting, isScreen) => {
// Prevent rendering on click setting for screens // Prevent rendering on click setting for screens
if (setting?.type === "event" && isScreen) { if (setting?.type === "event" && isScreen) {
return false return false
@ -51,6 +61,7 @@
if (setting.dependsOn) { if (setting.dependsOn) {
let dependantSetting = setting.dependsOn let dependantSetting = setting.dependsOn
let dependantValue = null let dependantValue = null
let invert = !!setting.dependsOn.invert
if (typeof setting.dependsOn === "object") { if (typeof setting.dependsOn === "object") {
dependantSetting = setting.dependsOn.setting dependantSetting = setting.dependsOn.setting
dependantValue = setting.dependsOn.value dependantValue = setting.dependsOn.value
@ -62,7 +73,7 @@
// If no specific value is depended upon, check if a value exists at all // If no specific value is depended upon, check if a value exists at all
// for the dependent setting // for the dependent setting
if (dependantValue == null) { if (dependantValue == null) {
const currentValue = componentInstance[dependantSetting] const currentValue = instance[dependantSetting]
if (currentValue === false) { if (currentValue === false) {
return false return false
} }
@ -73,7 +84,11 @@
} }
// Otherwise check the value matches // Otherwise check the value matches
return componentInstance[dependantSetting] === dependantValue if (invert) {
return instance[dependantSetting] !== dependantValue
} else {
return instance[dependantSetting] === dependantValue
}
} }
return true return true
@ -81,60 +96,54 @@
</script> </script>
{#each sections as section, idx (section.name)} {#each sections as section, idx (section.name)}
<DetailSummary name={section.name} collapsible={false}> {#if section.visible}
{#if idx === 0 && !componentInstance._component.endsWith("/layout") && !isScreen} <DetailSummary name={section.name} collapsible={false}>
<PropertyControl {#if idx === 0 && !componentInstance._component.endsWith("/layout") && !isScreen}
control={Input}
label="Name"
key="_instanceName"
value={componentInstance._instanceName}
onChange={val => updateSetting("_instanceName", val)}
/>
{/if}
{#each section.settings as setting (setting.key)}
{#if canRenderControl(setting, isScreen)}
<PropertyControl <PropertyControl
type={setting.type} control={Input}
control={getComponentForSetting(setting)} label="Name"
label={setting.label} key="_instanceName"
key={setting.key} value={componentInstance._instanceName}
value={componentInstance[setting.key]} onChange={val => updateSetting("_instanceName", val)}
defaultValue={setting.defaultValue}
nested={setting.nested}
onChange={val => updateSetting(setting.key, val)}
highlighted={$store.highlightedSettingKey === setting.key}
props={{
// Generic settings
placeholder: setting.placeholder || null,
// Select settings
options: setting.options || [],
// Number fields
min: setting.min || null,
max: setting.max || null,
}}
{bindings}
{componentBindings}
{componentInstance}
{componentDefinition}
/> />
{/if} {/if}
{/each} {#each section.settings as setting (setting.key)}
{#if idx === 0 && componentDefinition?.component?.endsWith("/fieldgroup")} {#if setting.visible}
<ResetFieldsButton {componentInstance} /> <PropertyControl
{/if} type={setting.type}
{#if section?.info} control={getComponentForSetting(setting)}
<div class="text"> label={setting.label}
{@html section.info} key={setting.key}
</div> value={componentInstance[setting.key]}
{/if} defaultValue={setting.defaultValue}
</DetailSummary> nested={setting.nested}
{/each} onChange={val => updateSetting(setting.key, val)}
highlighted={$store.highlightedSettingKey === setting.key}
info={setting.info}
props={{
// Generic settings
placeholder: setting.placeholder || null,
<style> // Select settings
.text { options: setting.options || [],
font-size: var(--spectrum-global-dimension-font-size-75);
color: var(--grey-6); // Number fields
} min: setting.min || null,
</style> max: setting.max || null,
}}
{bindings}
{componentBindings}
{componentInstance}
{componentDefinition}
/>
{/if}
{/each}
{#if idx === 0 && componentDefinition?.component?.endsWith("/fieldgroup")}
<ResetFieldsButton {componentInstance} />
{/if}
{#if idx === 0 && componentDefinition?.block}
<EjectBlockButton />
{/if}
</DetailSummary>
{/if}
{/each}

View File

@ -5,7 +5,8 @@
"children": [ "children": [
"tableblock", "tableblock",
"cardsblock", "cardsblock",
"repeaterblock" "repeaterblock",
"formblock"
] ]
}, },
{ {

View File

@ -38,7 +38,7 @@
let duplicateScreen = Helpers.cloneDeep(screen) let duplicateScreen = Helpers.cloneDeep(screen)
delete duplicateScreen._id delete duplicateScreen._id
delete duplicateScreen._rev delete duplicateScreen._rev
makeComponentUnique(duplicateScreen.props) duplicateScreen.props = makeComponentUnique(duplicateScreen.props)
// Attach the new name and URL // Attach the new name and URL
duplicateScreen.routing.route = sanitizeUrl(screenUrl) duplicateScreen.routing.route = sanitizeUrl(screenUrl)

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "2.0.24-alpha.0", "version": "2.0.24-alpha.3",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "src/index.js",
"bin": { "bin": {
@ -26,9 +26,9 @@
"outputPath": "build" "outputPath": "build"
}, },
"dependencies": { "dependencies": {
"@budibase/backend-core": "2.0.24-alpha.0", "@budibase/backend-core": "2.0.24-alpha.3",
"@budibase/string-templates": "2.0.24-alpha.0", "@budibase/string-templates": "2.0.24-alpha.3",
"@budibase/types": "2.0.24-alpha.0", "@budibase/types": "2.0.24-alpha.3",
"axios": "0.21.2", "axios": "0.21.2",
"chalk": "4.1.0", "chalk": "4.1.0",
"cli-progress": "3.11.2", "cli-progress": "3.11.2",

View File

@ -3442,7 +3442,6 @@
}, },
"s3upload": { "s3upload": {
"name": "S3 File Upload", "name": "S3 File Upload",
"info": "This component can't be used with S3 datasources that use custom endpoints.",
"icon": "UploadToCloud", "icon": "UploadToCloud",
"styles": [ "styles": [
"size" "size"
@ -3463,7 +3462,8 @@
{ {
"type": "dataSource/s3", "type": "dataSource/s3",
"label": "S3 Datasource", "label": "S3 Datasource",
"key": "datasourceId" "key": "datasourceId",
"info": "This component can't be used with S3 datasources that use custom endpoints"
}, },
{ {
"type": "text", "type": "text",
@ -3501,7 +3501,6 @@
}, },
"dataprovider": { "dataprovider": {
"name": "Data Provider", "name": "Data Provider",
"info": "Pagination is only available for data stored in tables.",
"icon": "Data", "icon": "Data",
"illegalChildren": [ "illegalChildren": [
"section" "section"
@ -3547,7 +3546,8 @@
"type": "boolean", "type": "boolean",
"label": "Paginate", "label": "Paginate",
"key": "paginate", "key": "paginate",
"defaultValue": true "defaultValue": true,
"info": "Pagination is only available for data stored in tables"
} }
], ],
"context": { "context": {
@ -3589,7 +3589,6 @@
], ],
"hasChildren": true, "hasChildren": true,
"showEmptyState": false, "showEmptyState": false,
"info": "Row selection is only compatible with internal or SQL tables",
"settings": [ "settings": [
{ {
"type": "dataProvider", "type": "dataProvider",
@ -3646,7 +3645,8 @@
"type": "boolean", "type": "boolean",
"label": "Allow row selection", "label": "Allow row selection",
"key": "allowSelectRows", "key": "allowSelectRows",
"defaultValue": false "defaultValue": false,
"info": "Row selection is only compatible with internal or SQL tables"
}, },
{ {
"type": "boolean", "type": "boolean",
@ -3687,13 +3687,13 @@
"size" "size"
], ],
"hasChildren": false, "hasChildren": false,
"info": "Your data provider will be automatically filtered to the given date range.",
"settings": [ "settings": [
{ {
"type": "dataProvider", "type": "dataProvider",
"label": "Provider", "label": "Provider",
"key": "dataProvider", "key": "dataProvider",
"required": true "required": true,
"info": "Your data provider will be automatically filtered to the given date range."
}, },
{ {
"type": "field", "type": "field",
@ -3828,7 +3828,6 @@
"styles": [ "styles": [
"size" "size"
], ],
"info": "Only the first 3 search columns will be used.",
"settings": [ "settings": [
{ {
"type": "text", "type": "text",
@ -3845,7 +3844,8 @@
"type": "searchfield", "type": "searchfield",
"label": "Search Columns", "label": "Search Columns",
"key": "searchColumns", "key": "searchColumns",
"placeholder": "Choose search columns" "placeholder": "Choose search columns",
"info": "Only the first 5 search columns will be used"
}, },
{ {
"type": "filter", "type": "filter",
@ -3892,7 +3892,6 @@
{ {
"section": true, "section": true,
"name": "Table", "name": "Table",
"info": "Row selection is only compatible with internal or SQL tables",
"settings": [ "settings": [
{ {
"type": "number", "type": "number",
@ -3926,7 +3925,8 @@
{ {
"type": "boolean", "type": "boolean",
"label": "Allow row selection", "label": "Allow row selection",
"key": "allowSelectRows" "key": "allowSelectRows",
"info": "Row selection is only compatible with internal or SQL tables"
}, },
{ {
"type": "boolean", "type": "boolean",
@ -3993,7 +3993,6 @@
"styles": [ "styles": [
"size" "size"
], ],
"info": "Only the first 3 search columns will be used.",
"settings": [ "settings": [
{ {
"type": "text", "type": "text",
@ -4010,7 +4009,8 @@
"type": "searchfield", "type": "searchfield",
"label": "Search Columns", "label": "Search Columns",
"key": "searchColumns", "key": "searchColumns",
"placeholder": "Choose search columns" "placeholder": "Choose search columns",
"info": "Only the first 5 search columns will be used"
}, },
{ {
"type": "filter", "type": "filter",
@ -4157,6 +4157,7 @@
} }
}, },
"repeaterblock": { "repeaterblock": {
"block": true,
"name": "Repeater block", "name": "Repeater block",
"icon": "ViewList", "icon": "ViewList",
"illegalChildren": [ "illegalChildren": [
@ -4394,5 +4395,145 @@
"required": true "required": true
} }
] ]
},
"formblock": {
"name": "Form Block",
"icon": "Form",
"styles": ["size"],
"block": true,
"info": "Form blocks are only compatible with internal or SQL tables",
"settings": [
{
"type": "select",
"label": "Type",
"key": "actionType",
"options": ["Create", "Update", "View"],
"defaultValue": "Create"
},
{
"type": "table",
"label": "Table",
"key": "dataSource"
},
{
"type": "text",
"label": "Row ID",
"key": "rowId",
"nested": true,
"dependsOn": {
"setting": "actionType",
"value": "Create",
"invert": true
}
},
{
"type": "text",
"label": "Title",
"key": "title",
"nested": true
},
{
"type": "select",
"label": "Size",
"key": "size",
"options": [
{
"label": "Medium",
"value": "spectrum--medium"
},
{
"label": "Large",
"value": "spectrum--large"
}
],
"defaultValue": "spectrum--medium"
},
{
"section": true,
"name": "Fields",
"settings": [
{
"type": "multifield",
"label": "Fields",
"key": "fields"
},
{
"type": "select",
"label": "Field labels",
"key": "labelPosition",
"defaultValue": "left",
"options": [
{
"label": "Left",
"value": "left"
},
{
"label": "Above",
"value": "above"
}
]
},
{
"type": "boolean",
"label": "Disabled",
"key": "disabled",
"defaultValue": false,
"dependsOn": {
"setting": "actionType",
"value": "View",
"invert": true
}
}
]
},
{
"section": true,
"name": "Buttons",
"settings": [
{
"type": "boolean",
"label": "Show save button",
"key": "showSaveButton",
"defaultValue": true,
"dependsOn": {
"setting": "actionType",
"value": "View",
"invert": true
}
},
{
"type": "boolean",
"label": "Show delete button",
"key": "showDeleteButton",
"defaultValue": false,
"dependsOn": {
"setting": "actionType",
"value": "Update"
}
},
{
"type": "url",
"label": "Navigate after button press",
"key": "actionUrl",
"placeholder": "Choose a screen",
"dependsOn": {
"setting": "actionType",
"value": "View",
"invert": true
}
}
]
}
],
"context": [
{
"type": "form",
"suffix": "form"
},
{
"type": "schema",
"suffix": "repeater"
}
]
} }
} }

View File

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

View File

@ -1,5 +1,7 @@
import { createAPIClient } from "@budibase/frontend-core" import { createAPIClient } from "@budibase/frontend-core"
import { notificationStore, authStore, devToolsStore } from "../stores" import { notificationStore } from "../stores/notification.js"
import { authStore } from "../stores/auth.js"
import { devToolsStore } from "../stores/devTools.js"
import { get } from "svelte/store" import { get } from "svelte/store"
export const API = createAPIClient({ export const API = createAPIClient({

View File

@ -1,12 +1,92 @@
<script> <script>
import { getContext, setContext } from "svelte" import { getContext, onDestroy, onMount, setContext } from "svelte"
import { builderStore } from "stores/builder.js"
import { blockStore } from "stores/blocks.js"
const component = getContext("component") const component = getContext("component")
const { styleable } = getContext("sdk")
// We need to set a block context to know we're inside a block, but also let structureLookupMap = {}
// to be able to reference the actual component ID of the block from
// any depth const registerBlockComponent = (id, order, parentId, instance) => {
setContext("block", { id: $component.id }) // Ensure child array exists
if (!structureLookupMap[parentId]) {
structureLookupMap[parentId] = {}
}
// Add this instance in this order, overwriting any existing instance in
// this order in case of repeaters
structureLookupMap[parentId][order] = instance
}
const eject = () => {
// Start the new structure with the root component
let definition = structureLookupMap[$component.id][0]
// Copy styles from block to root component
definition._styles = {
...definition._styles,
normal: {
...definition._styles?.normal,
...$component.styles?.normal,
},
custom:
definition._styles?.custom || "" + $component.styles?.custom || "",
}
// Create component tree
attachChildren(definition, structureLookupMap)
builderStore.actions.ejectBlock($component.id, definition)
}
const attachChildren = (rootComponent, map) => {
// Transform map into children array
let id = rootComponent._id
const children = Object.entries(map[id] || {}).map(([order, instance]) => ({
order,
instance,
}))
if (!children.length) {
return
}
// Sort children by order
children.sort((a, b) => (a.order < b.order ? -1 : 1))
// Attach all children of this component
rootComponent._children = children.map(x => x.instance)
// Recurse for each child
rootComponent._children.forEach(child => {
attachChildren(child, map)
})
}
setContext("block", {
// We need to set a block context to know we're inside a block, but also
// to be able to reference the actual component ID of the block from
// any depth
id: $component.id,
// We register block components with their raw props so that we can eject
// blocks later on
registerComponent: registerBlockComponent,
})
onMount(() => {
// We register and unregister blocks to the block store when inside the
// builder preview to allow for block ejection
if ($builderStore.inBuilder) {
blockStore.actions.registerBlock($component.id, { eject })
}
})
onDestroy(() => {
if ($builderStore.inBuilder) {
blockStore.actions.unregisterBlock($component.id)
}
})
</script> </script>
<slot /> <div use:styleable={$component.styles}>
<slot />
</div>

View File

@ -1,17 +1,21 @@
<script> <script>
import { getContext } from "svelte" import { getContext } from "svelte"
import { generate } from "shortid" import { generate } from "shortid"
import { builderStore } from "../stores/builder.js"
import Component from "components/Component.svelte" import Component from "components/Component.svelte"
export let type export let type
export let props export let props
export let styles export let styles
export let context export let context
export let order = 0
export let containsSlot = false
// ID is only exposed as a prop so that it can be bound to from parent // ID is only exposed as a prop so that it can be bound to from parent
// block components // block components
export let id export let id
const component = getContext("component")
const block = getContext("block") const block = getContext("block")
const rand = generate() const rand = generate()
@ -21,13 +25,22 @@
$: instance = { $: instance = {
_component: `@budibase/standard-components/${type}`, _component: `@budibase/standard-components/${type}`,
_id: id, _id: id,
_instanceName: type[0].toUpperCase() + type.slice(1),
_styles: { _styles: {
normal: { ...styles,
...styles, normal: styles?.normal || {},
},
}, },
_containsSlot: containsSlot,
...props, ...props,
} }
// Register this block component if we're inside the builder so it can be
// ejected later
$: {
if ($builderStore.inBuilder) {
block.registerComponent(id, order ?? 0, $component?.id, instance)
}
}
</script> </script>
<Component {instance} isBlock> <Component {instance} isBlock>

View File

@ -2,7 +2,6 @@
import { getContext } from "svelte" import { getContext } from "svelte"
import Block from "components/Block.svelte" import Block from "components/Block.svelte"
import BlockComponent from "components/BlockComponent.svelte" import BlockComponent from "components/BlockComponent.svelte"
import { Heading } from "@budibase/bbui"
import { makePropSafe as safe } from "@budibase/string-templates" import { makePropSafe as safe } from "@budibase/string-templates"
import { enrichSearchColumns, enrichFilter } from "utils/blocks.js" import { enrichSearchColumns, enrichFilter } from "utils/blocks.js"
@ -31,9 +30,7 @@
export let cardButtonOnClick export let cardButtonOnClick
export let linkColumn export let linkColumn
const { fetchDatasourceSchema, styleable } = getContext("sdk") const { fetchDatasourceSchema } = getContext("sdk")
const context = getContext("context")
const component = getContext("component")
let formId let formId
let dataProviderId let dataProviderId
@ -84,163 +81,132 @@
{#if schemaLoaded} {#if schemaLoaded}
<Block> <Block>
<div class="card-list" use:styleable={$component.styles}> <BlockComponent
<BlockComponent type="form"
type="form" bind:id={formId}
bind:id={formId} props={{ dataSource, disableValidation: true }}
props={{ dataSource, disableValidation: true }} >
> {#if title || enrichedSearchColumns?.length || showTitleButton}
{#if title || enrichedSearchColumns?.length || showTitleButton}
<div class="header" class:mobile={$context.device.mobile}>
<div class="title">
<Heading>{title || ""}</Heading>
</div>
<div class="controls">
{#if enrichedSearchColumns?.length}
<div
class="search"
style="--cols:{enrichedSearchColumns?.length}"
>
{#each enrichedSearchColumns as column}
<BlockComponent
type={column.componentType}
props={{
field: column.name,
placeholder: column.name,
text: column.name,
autoWidth: true,
}}
/>
{/each}
</div>
{/if}
{#if showTitleButton}
<BlockComponent
type="button"
props={{
onClick: titleButtonAction,
text: titleButtonText,
type: "cta",
}}
/>
{/if}
</div>
</div>
{/if}
<BlockComponent <BlockComponent
type="dataprovider" type="container"
bind:id={dataProviderId}
props={{ props={{
dataSource, direction: "row",
filter: enrichedFilter, hAlign: "stretch",
sortColumn, vAlign: "middle",
sortOrder, gap: "M",
paginate, wrap: true,
limit,
}} }}
styles={{
normal: {
"margin-bottom": "20px",
},
}}
order={0}
> >
<BlockComponent <BlockComponent
type="repeater" type="heading"
bind:id={repeaterId} props={{
context="repeater" text: title,
}}
order={0}
/>
<BlockComponent
type="container"
props={{ props={{
dataProvider: `{{ literal ${safe(dataProviderId)} }}`,
direction: "row", direction: "row",
hAlign: "stretch", hAlign: "left",
vAlign: "top", vAlign: "middle",
gap: "M", gap: "M",
noRowsMessage: "No rows found", wrap: true,
}}
styles={{
display: "grid",
"grid-template-columns": `repeat(auto-fill, minmax(min(${cardWidth}px, 100%), 1fr))`,
}} }}
order={1}
> >
<BlockComponent {#if enrichedSearchColumns?.length}
type="spectrumcard" {#each enrichedSearchColumns as column, idx}
props={{ <BlockComponent
title: cardTitle, type={column.componentType}
subtitle: cardSubtitle, props={{
description: cardDescription, field: column.name,
imageURL: cardImageURL, placeholder: column.name,
horizontal: cardHorizontal, text: column.name,
showButton: showCardButton, autoWidth: true,
buttonText: cardButtonText, }}
buttonOnClick: cardButtonOnClick, order={idx}
linkURL: fullCardURL, styles={{
linkPeek: cardPeek, normal: {
}} width: "192px",
styles={{ },
width: "auto", }}
}} />
/> {/each}
{/if}
{#if showTitleButton}
<BlockComponent
type="button"
props={{
onClick: titleButtonAction,
text: titleButtonText,
type: "cta",
}}
order={enrichedSearchColumns?.length ?? 0}
/>
{/if}
</BlockComponent> </BlockComponent>
</BlockComponent> </BlockComponent>
{/if}
<BlockComponent
type="dataprovider"
bind:id={dataProviderId}
props={{
dataSource,
filter: enrichedFilter,
sortColumn,
sortOrder,
paginate,
limit,
}}
order={1}
>
<BlockComponent
type="repeater"
bind:id={repeaterId}
context="repeater"
props={{
dataProvider: `{{ literal ${safe(dataProviderId)} }}`,
direction: "row",
hAlign: "stretch",
vAlign: "top",
gap: "M",
noRowsMessage: "No rows found",
}}
styles={{
custom: `display: grid;\ngrid-template-columns: repeat(auto-fill, minmax(min(${cardWidth}px, 100%), 1fr));`,
}}
order={0}
>
<BlockComponent
type="spectrumcard"
props={{
title: cardTitle,
subtitle: cardSubtitle,
description: cardDescription,
imageURL: cardImageURL,
horizontal: cardHorizontal,
showButton: showCardButton,
buttonText: cardButtonText,
buttonOnClick: cardButtonOnClick,
linkURL: fullCardURL,
linkPeek: cardPeek,
}}
styles={{
normal: {
width: "auto",
},
}}
order={0}
/>
</BlockComponent>
</BlockComponent> </BlockComponent>
</div> </BlockComponent>
</Block> </Block>
{/if} {/if}
<style>
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
gap: 20px;
margin-bottom: 20px;
}
.title {
overflow: hidden;
}
.title :global(.spectrum-Heading) {
flex: 1 1 auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.controls {
flex: 0 1 auto;
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
gap: 20px;
}
.controls :global(.spectrum-InputGroup .spectrum-InputGroup-input) {
width: 100%;
}
.search {
flex: 0 1 auto;
gap: 10px;
max-width: 100%;
display: grid;
grid-template-columns: repeat(var(--cols), minmax(120px, 200px));
}
.search :global(.spectrum-InputGroup) {
min-width: 0;
}
/* Mobile styles */
.mobile {
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
}
.mobile .controls {
flex-direction: column-reverse;
justify-content: flex-start;
align-items: stretch;
}
.mobile .search {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
position: relative;
width: 100%;
}
</style>

View File

@ -0,0 +1,239 @@
<script>
import { getContext } from "svelte"
import BlockComponent from "../../BlockComponent.svelte"
import Block from "../../Block.svelte"
import Placeholder from "../Placeholder.svelte"
import { makePropSafe as safe } from "@budibase/string-templates"
export let actionType
export let dataSource
export let size
export let disabled
export let fields
export let labelPosition
export let title
export let showSaveButton
export let showDeleteButton
export let rowId
export let actionUrl
const { fetchDatasourceSchema, builderStore } = getContext("sdk")
const FieldTypeToComponentMap = {
string: "stringfield",
number: "numberfield",
options: "optionsfield",
array: "multifieldselect",
boolean: "booleanfield",
longform: "longformfield",
datetime: "datetimefield",
attachment: "attachmentfield",
link: "relationshipfield",
json: "jsonfield",
}
let schema
let formId
let providerId
let repeaterId
$: fetchSchema(dataSource)
$: onSave = [
{
"##eventHandlerType": "Save Row",
parameters: {
providerId: formId,
tableId: dataSource?.tableId,
},
},
{
"##eventHandlerType": "Close Screen Modal",
},
{
"##eventHandlerType": "Navigate To",
parameters: {
url: actionUrl,
},
},
]
$: onDelete = [
{
"##eventHandlerType": "Delete Row",
parameters: {
confirm: true,
tableId: dataSource?.tableId,
rowId: `{{ ${safe(repeaterId)}.${safe("_id")} }}`,
revId: `{{ ${safe(repeaterId)}.${safe("_rev")} }}`,
},
},
{
"##eventHandlerType": "Close Screen Modal",
},
{
"##eventHandlerType": "Navigate To",
parameters: {
url: actionUrl,
},
},
]
$: filter = [
{
field: "_id",
operator: "equal",
type: "string",
value: rowId,
valueType: "Binding",
},
]
// If we're using an "update" form, use the real data provider. If we're
// using a create form, we just want a fake array so that our repeater
// will actually render the form, but data doesn't matter.
$: dataProvider =
actionType !== "Create"
? `{{ literal ${safe(providerId)} }}`
: { rows: [{}] }
$: renderDeleteButton = showDeleteButton && actionType === "Update"
$: renderSaveButton = showSaveButton && actionType !== "View"
$: renderButtons = renderDeleteButton || renderSaveButton
$: renderHeader = renderButtons || title
const fetchSchema = async () => {
schema = (await fetchDatasourceSchema(dataSource)) || {}
}
const getComponentForField = field => {
if (!field || !schema?.[field]) {
return null
}
const type = schema[field].type
return FieldTypeToComponentMap[type]
}
</script>
<Block>
{#if fields?.length}
<BlockComponent
type="dataprovider"
context="provider"
bind:id={providerId}
props={{
dataSource,
filter,
limit: rowId ? 1 : $builderStore.inBuilder ? 1 : 0,
paginate: false,
}}
>
<BlockComponent
type="repeater"
context="repeater"
bind:id={repeaterId}
props={{
dataProvider,
noRowsMessage: "We couldn't find a row to display",
}}
>
<BlockComponent
type="form"
props={{
actionType: actionType === "Create" ? "Create" : "Update",
dataSource,
size,
disabled: disabled || actionType === "View",
}}
context="form"
bind:id={formId}
>
<BlockComponent
type="container"
props={{
direction: "column",
hAlign: "stretch",
vAlign: "top",
gap: "M",
}}
>
{#if renderHeader}
<BlockComponent
type="container"
props={{
direction: "row",
hAlign: "stretch",
vAlign: "center",
gap: "M",
wrap: true,
}}
order={0}
>
<BlockComponent
type="heading"
props={{ text: title || "" }}
order={0}
/>
{#if renderButtons}
<BlockComponent
type="container"
props={{
direction: "row",
hAlign: "stretch",
vAlign: "center",
gap: "M",
wrap: true,
}}
order={1}
>
{#if renderDeleteButton}
<BlockComponent
type="button"
props={{
text: "Delete",
onClick: onDelete,
quiet: true,
type: "secondary",
}}
order={0}
/>
{/if}
{#if renderSaveButton}
<BlockComponent
type="button"
props={{
text: "Save",
onClick: onSave,
type: "cta",
}}
order={1}
/>
{/if}
</BlockComponent>
{/if}
</BlockComponent>
{/if}
<BlockComponent
type="fieldgroup"
props={{ labelPosition }}
order={1}
>
{#each fields as field, idx}
{#if getComponentForField(field)}
<BlockComponent
type={getComponentForField(field)}
props={{
field,
label: field,
placeholder: field,
disabled,
}}
order={idx}
/>
{/if}
{/each}
</BlockComponent>
</BlockComponent>
</BlockComponent>
</BlockComponent>
</BlockComponent>
{:else}
<Placeholder
text="Choose your table and add some fields to your form to get started"
/>
{/if}
</Block>

View File

@ -17,45 +17,43 @@
export let vAlign export let vAlign
export let gap export let gap
let providerId
const component = getContext("component") const component = getContext("component")
const { styleable } = getContext("sdk")
let providerId
</script> </script>
<Block> <Block>
<div use:styleable={$component.styles}> <BlockComponent
<BlockComponent type="dataprovider"
type="dataprovider" context="provider"
context="provider" bind:id={providerId}
bind:id={providerId} props={{
props={{ dataSource,
dataSource, filter,
filter, sortColumn,
sortColumn, sortOrder,
sortOrder, limit,
limit, paginate,
paginate, }}
}} >
> {#if $component.empty}
{#if $component.empty} <Placeholder />
<Placeholder /> {:else}
{:else} <BlockComponent
<BlockComponent type="repeater"
type="repeater" context="repeater"
context="repeater" containsSlot
props={{ props={{
dataProvider: `{{ literal ${safe(providerId)} }}`, dataProvider: `{{ literal ${safe(providerId)} }}`,
noRowsMessage, noRowsMessage,
direction, direction,
hAlign, hAlign,
vAlign, vAlign,
gap, gap,
}} }}
> >
<slot /> <slot />
</BlockComponent> </BlockComponent>
{/if} {/if}
</BlockComponent> </BlockComponent>
</div>
</Block> </Block>

View File

@ -2,7 +2,6 @@
import { getContext } from "svelte" import { getContext } from "svelte"
import Block from "components/Block.svelte" import Block from "components/Block.svelte"
import BlockComponent from "components/BlockComponent.svelte" import BlockComponent from "components/BlockComponent.svelte"
import { Heading } from "@budibase/bbui"
import { makePropSafe as safe } from "@budibase/string-templates" import { makePropSafe as safe } from "@budibase/string-templates"
import { enrichSearchColumns, enrichFilter } from "utils/blocks.js" import { enrichSearchColumns, enrichFilter } from "utils/blocks.js"
@ -29,9 +28,7 @@
export let titleButtonURL export let titleButtonURL
export let titleButtonPeek export let titleButtonPeek
const { fetchDatasourceSchema, styleable } = getContext("sdk") const { fetchDatasourceSchema } = getContext("sdk")
const context = getContext("context")
const component = getContext("component")
let formId let formId
let dataProviderId let dataProviderId
@ -64,145 +61,116 @@
{#if schemaLoaded} {#if schemaLoaded}
<Block> <Block>
<div class={size} use:styleable={$component.styles}> <BlockComponent
<BlockComponent type="form"
type="form" bind:id={formId}
bind:id={formId} props={{
props={{ dataSource, disableValidation: true, editAutoColumns: true }} dataSource,
> disableValidation: true,
{#if title || enrichedSearchColumns?.length || showTitleButton} editAutoColumns: true,
<div class="header" class:mobile={$context.device.mobile}> size,
<div class="title"> }}
<Heading>{title || ""}</Heading> >
</div> {#if title || enrichedSearchColumns?.length || showTitleButton}
<div class="controls">
{#if enrichedSearchColumns?.length}
<div
class="search"
style="--cols:{enrichedSearchColumns?.length}"
>
{#each enrichedSearchColumns as column}
<BlockComponent
type={column.componentType}
props={{
field: column.name,
placeholder: column.name,
text: column.name,
autoWidth: true,
}}
/>
{/each}
</div>
{/if}
{#if showTitleButton}
<BlockComponent
type="button"
props={{
onClick: titleButtonAction,
text: titleButtonText,
type: "cta",
}}
/>
{/if}
</div>
</div>
{/if}
<BlockComponent <BlockComponent
type="dataprovider" type="container"
bind:id={dataProviderId}
props={{ props={{
dataSource, direction: "row",
filter: enrichedFilter, hAlign: "stretch",
sortColumn, vAlign: "middle",
sortOrder, gap: "M",
paginate, wrap: true,
limit: rowCount,
}} }}
styles={{
normal: {
"margin-bottom": "20px",
},
}}
order={0}
> >
<BlockComponent <BlockComponent
type="table" type="heading"
context="table"
props={{ props={{
dataProvider: `{{ literal ${safe(dataProviderId)} }}`, text: title,
columns: tableColumns,
showAutoColumns,
rowCount,
quiet,
compact,
allowSelectRows,
size,
linkRows,
linkURL,
linkColumn,
linkPeek,
}} }}
order={0}
/> />
<BlockComponent
type="container"
props={{
direction: "row",
hAlign: "left",
vAlign: "center",
gap: "M",
wrap: true,
}}
order={1}
>
{#if enrichedSearchColumns?.length}
{#each enrichedSearchColumns as column, idx}
<BlockComponent
type={column.componentType}
props={{
field: column.name,
placeholder: column.name,
text: column.name,
autoWidth: true,
}}
styles={{
normal: {
width: "192px",
},
}}
order={idx}
/>
{/each}
{/if}
{#if showTitleButton}
<BlockComponent
type="button"
props={{
onClick: titleButtonAction,
text: titleButtonText,
type: "cta",
}}
order={enrichedSearchColumns?.length ?? 0}
/>
{/if}
</BlockComponent>
</BlockComponent> </BlockComponent>
{/if}
<BlockComponent
type="dataprovider"
bind:id={dataProviderId}
props={{
dataSource,
filter: enrichedFilter,
sortColumn,
sortOrder,
paginate,
limit: rowCount,
}}
order={1}
>
<BlockComponent
type="table"
context="table"
props={{
dataProvider: `{{ literal ${safe(dataProviderId)} }}`,
columns: tableColumns,
showAutoColumns,
rowCount,
quiet,
compact,
allowSelectRows,
size,
linkRows,
linkURL,
linkColumn,
linkPeek,
}}
/>
</BlockComponent> </BlockComponent>
</div> </BlockComponent>
</Block> </Block>
{/if} {/if}
<style>
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
gap: 20px;
margin-bottom: 20px;
}
.title {
overflow: hidden;
}
.title :global(.spectrum-Heading) {
flex: 1 1 auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.controls {
flex: 0 1 auto;
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
gap: 20px;
}
.controls :global(.spectrum-InputGroup .spectrum-InputGroup-input) {
width: 100%;
}
.search {
flex: 0 1 auto;
gap: 10px;
max-width: 100%;
display: grid;
grid-template-columns: repeat(var(--cols), minmax(120px, 200px));
}
.search :global(.spectrum-InputGroup) {
min-width: 0;
}
/* Mobile styles */
.mobile {
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
}
.mobile .controls {
flex-direction: column-reverse;
justify-content: flex-start;
align-items: stretch;
}
.mobile .search {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
position: relative;
width: 100%;
}
</style>

View File

@ -1,3 +1,4 @@
export { default as tableblock } from "./TableBlock.svelte" export { default as tableblock } from "./TableBlock.svelte"
export { default as cardsblock } from "./CardsBlock.svelte" export { default as cardsblock } from "./CardsBlock.svelte"
export { default as repeaterblock } from "./RepeaterBlock.svelte" export { default as repeaterblock } from "./RepeaterBlock.svelte"
export { default as formblock } from "./FormBlock.svelte"

View File

@ -48,36 +48,7 @@
// Fetches the form schema from this form's dataSource // Fetches the form schema from this form's dataSource
const fetchSchema = async dataSource => { const fetchSchema = async dataSource => {
if (!dataSource) { schema = (await fetchDatasourceSchema(dataSource)) || {}
schema = {}
}
// If the datasource is a query, then we instead use a schema of the query
// parameters rather than the output schema
else if (
dataSource.type === "query" &&
dataSource._id &&
actionType === "Create"
) {
try {
const query = await API.fetchQueryDefinition(dataSource._id)
let paramSchema = {}
const params = query.parameters || []
params.forEach(param => {
paramSchema[param.name] = { ...param, type: "string" }
})
schema = paramSchema
} catch (error) {
schema = {}
}
}
// For all other cases, just grab the normal schema
else {
const dataSourceSchema = await fetchDatasourceSchema(dataSource)
schema = dataSourceSchema || {}
}
if (!loaded) { if (!loaded) {
loaded = true loaded = true
} }
@ -95,7 +66,7 @@
$: initialValues = getInitialValues(actionType, dataSource, $context) $: initialValues = getInitialValues(actionType, dataSource, $context)
$: resetKey = Helpers.hashString( $: resetKey = Helpers.hashString(
JSON.stringify(initialValues) + JSON.stringify(schema) JSON.stringify(initialValues) + JSON.stringify(schema) + disabled
) )
</script> </script>

View File

@ -1,9 +1,10 @@
import ClientApp from "./components/ClientApp.svelte" import ClientApp from "./components/ClientApp.svelte"
import { import {
componentStore,
builderStore, builderStore,
appStore, appStore,
devToolsStore, devToolsStore,
blockStore,
componentStore,
environmentStore, environmentStore,
} from "./stores" } from "./stores"
import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-rollup.js" import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-rollup.js"
@ -50,6 +51,17 @@ const loadBudibase = async () => {
const enableDevTools = !get(builderStore).inBuilder && get(appStore).isDevApp const enableDevTools = !get(builderStore).inBuilder && get(appStore).isDevApp
devToolsStore.actions.setEnabled(enableDevTools) devToolsStore.actions.setEnabled(enableDevTools)
// Register handler for runtime events from the builder
window.handleBuilderRuntimeEvent = (name, payload) => {
if (!window["##BUDIBASE_IN_BUILDER##"]) {
return
}
if (name === "eject-block") {
const block = blockStore.actions.getBlock(payload)
block?.eject()
}
}
// Register any custom components // Register any custom components
if (window["##BUDIBASE_CUSTOM_COMPONENTS##"]) { if (window["##BUDIBASE_CUSTOM_COMPONENTS##"]) {
window["##BUDIBASE_CUSTOM_COMPONENTS##"].forEach(component => { window["##BUDIBASE_CUSTOM_COMPONENTS##"].forEach(component => {

View File

@ -0,0 +1,34 @@
import { get, writable } from "svelte/store"
const createBlockStore = () => {
const store = writable({})
const registerBlock = (id, instance) => {
store.update(state => ({
...state,
[id]: instance,
}))
}
const unregisterBlock = id => {
store.update(state => {
delete state[id]
return state
})
}
const getBlock = id => {
return get(store)[id]
}
return {
subscribe: store.subscribe,
actions: {
registerBlock,
unregisterBlock,
getBlock,
},
}
}
export const blockStore = createBlockStore()

View File

@ -85,6 +85,9 @@ const createBuilderStore = () => {
highlightSetting: setting => { highlightSetting: setting => {
dispatchEvent("highlight-setting", { setting }) dispatchEvent("highlight-setting", { setting })
}, },
ejectBlock: (id, definition) => {
dispatchEvent("eject-block", { id, definition })
},
updateUsedPlugin: (name, hash) => { updateUsedPlugin: (name, hash) => {
// Check if we used this plugin // Check if we used this plugin
const used = get(store)?.usedPlugins?.find(x => x.name === name) const used = get(store)?.usedPlugins?.find(x => x.name === name)

View File

@ -17,6 +17,7 @@ export { devToolsStore } from "./devTools"
export { componentStore } from "./components" export { componentStore } from "./components"
export { uploadStore } from "./uploads.js" export { uploadStore } from "./uploads.js"
export { rowSelectionStore } from "./rowSelection.js" export { rowSelectionStore } from "./rowSelection.js"
export { blockStore } from "./blocks.js"
export { environmentStore } from "./environment" export { environmentStore } from "./environment"
// Context stores are layered and duplicated, so it is not a singleton // Context stores are layered and duplicated, so it is not a singleton

View File

@ -16,7 +16,7 @@ import JSONArrayFetch from "@budibase/frontend-core/src/fetch/JSONArrayFetch.js"
*/ */
export const fetchDatasourceSchema = async ( export const fetchDatasourceSchema = async (
datasource, datasource,
options = { enrichRelationships: false } options = { enrichRelationships: false, formSchema: false }
) => { ) => {
const handler = { const handler = {
table: TableFetch, table: TableFetch,
@ -34,7 +34,17 @@ export const fetchDatasourceSchema = async (
// Get the datasource definition and then schema // Get the datasource definition and then schema
const definition = await instance.getDefinition(datasource) const definition = await instance.getDefinition(datasource)
let schema = instance.getSchema(datasource, definition)
// Get the normal schema as long as we aren't wanting a form schema
let schema
if (datasource?.type !== "query" || !options?.formSchema) {
schema = instance.getSchema(datasource, definition)
} else if (definition.parameters?.length) {
schema = {}
definition.parameters.forEach(param => {
schema[param.name] = { ...param, type: "string" }
})
}
if (!schema) { if (!schema) {
return null return null
} }

View File

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

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/sdk", "name": "@budibase/sdk",
"version": "2.0.24-alpha.0", "version": "2.0.24-alpha.3",
"description": "Budibase Public API SDK", "description": "Budibase Public API SDK",
"author": "Budibase", "author": "Budibase",
"license": "MPL-2.0", "license": "MPL-2.0",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "2.0.24-alpha.0", "version": "2.0.24-alpha.3",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -77,11 +77,11 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@apidevtools/swagger-parser": "10.0.3", "@apidevtools/swagger-parser": "10.0.3",
"@budibase/backend-core": "2.0.24-alpha.0", "@budibase/backend-core": "2.0.24-alpha.3",
"@budibase/client": "2.0.24-alpha.0", "@budibase/client": "2.0.24-alpha.3",
"@budibase/pro": "2.0.24-alpha.0", "@budibase/pro": "2.0.24-alpha.3",
"@budibase/string-templates": "2.0.24-alpha.0", "@budibase/string-templates": "2.0.24-alpha.3",
"@budibase/types": "2.0.24-alpha.0", "@budibase/types": "2.0.24-alpha.3",
"@bull-board/api": "3.7.0", "@bull-board/api": "3.7.0",
"@bull-board/koa": "3.9.4", "@bull-board/koa": "3.9.4",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",

View File

@ -32,7 +32,7 @@ const {
import { USERS_TABLE_SCHEMA } from "../../constants" import { USERS_TABLE_SCHEMA } from "../../constants"
import { removeAppFromUserRoles } from "../../utilities/workerRequests" import { removeAppFromUserRoles } from "../../utilities/workerRequests"
import { clientLibraryPath, stringToReadStream } from "../../utilities" import { clientLibraryPath, stringToReadStream } from "../../utilities"
import { getAllLocks } from "../../utilities/redis" import { getLocksById } from "../../utilities/redis"
import { import {
updateClientLibrary, updateClientLibrary,
backupClientLibrary, backupClientLibrary,
@ -45,11 +45,10 @@ import { cleanupAutomations } from "../../automations/utils"
import { context } from "@budibase/backend-core" import { context } from "@budibase/backend-core"
import { checkAppMetadata } from "../../automations/logging" import { checkAppMetadata } from "../../automations/logging"
import { getUniqueRows } from "../../utilities/usageQuota/rows" import { getUniqueRows } from "../../utilities/usageQuota/rows"
import { quotas } from "@budibase/pro" import { quotas, groups } from "@budibase/pro"
import { errors, events, migrations } from "@budibase/backend-core" import { errors, events, migrations } from "@budibase/backend-core"
import { App, Layout, Screen, MigrationType } from "@budibase/types" import { App, Layout, Screen, MigrationType } from "@budibase/types"
import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts" import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts"
import { groups } from "@budibase/pro"
import { enrichPluginURLs } from "../../utilities/plugins" import { enrichPluginURLs } from "../../utilities/plugins"
const URL_REGEX_SLASH = /\/|\\/g const URL_REGEX_SLASH = /\/|\\/g
@ -172,16 +171,16 @@ export const fetch = async (ctx: any) => {
const all = ctx.query && ctx.query.status === AppStatus.ALL const all = ctx.query && ctx.query.status === AppStatus.ALL
const apps = await getAllApps({ dev, all }) const apps = await getAllApps({ dev, all })
const appIds = apps
.filter((app: any) => app.status === "development")
.map((app: any) => app.appId)
// get the locks for all the dev apps // get the locks for all the dev apps
if (dev || all) { if (dev || all) {
const locks = await getAllLocks() const locks = await getLocksById(appIds)
for (let app of apps) { for (let app of apps) {
if (app.status !== "development") { const lock = locks[app.appId]
continue
}
const lock = locks.find((lock: any) => lock.appId === app.appId)
if (lock) { if (lock) {
app.lockedBy = lock.user app.lockedBy = lock
} else { } else {
// make sure its definitely not present // make sure its definitely not present
delete app.lockedBy delete app.lockedBy

View File

@ -103,7 +103,7 @@ exports.revert = async ctx => {
target: appId, target: appId,
}) })
try { try {
if (!env.isTest()) { if (!env.isCypress()) {
// in-memory db stalls on rollback // in-memory db stalls on rollback
await replication.rollback() await replication.rollback()
} }

View File

@ -56,6 +56,16 @@
return return
} }
// If this is a custom event, try and handle it
if (parsed.runtimeEvent) {
const { name, payload } = parsed
if (window.handleBuilderRuntimeEvent) {
window.handleBuilderRuntimeEvent(name, payload)
}
return
}
// Otherwise this is a full reload message
// Extract data from message // Extract data from message
const { const {
selectedComponentId, selectedComponentId,

View File

@ -1,7 +1,7 @@
jest.mock("../../../utilities/redis", () => ({ jest.mock("../../../utilities/redis", () => ({
init: jest.fn(), init: jest.fn(),
getAllLocks: () => { getLocksById: () => {
return [] return {}
}, },
doesUserHaveLock: () => { doesUserHaveLock: () => {
return true return true

View File

@ -34,12 +34,8 @@ exports.doesUserHaveLock = async (devAppId, user) => {
return expected === userId return expected === userId
} }
exports.getAllLocks = async () => { exports.getLocksById = async appIds => {
const locks = await devAppClient.scan() return await devAppClient.bulkGet(appIds)
return locks.map(lock => ({
appId: lock.key,
user: lock.value,
}))
} }
exports.updateLock = async (devAppId, user) => { exports.updateLock = async (devAppId, user) => {

View File

@ -1094,12 +1094,12 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@2.0.24-alpha.0": "@budibase/backend-core@2.0.24-alpha.3":
version "2.0.24-alpha.0" version "2.0.24-alpha.3"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.24-alpha.0.tgz#db53e562cd40aa21783f6f4d6eaf880ec8225a8f" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.24-alpha.3.tgz#49a92082d1ca6bed0eb82519a6c02d7d14ead751"
integrity sha512-o8Ooa5Ga3hcbovoO+Fjyp++TcJmt8v83Y7GfurLznjAc5lakdEe8RWKR9JV7xqenXpZ49ZwWYTSOLwxFBiJuvw== integrity sha512-Zm/ddRDMzMuXCoEXZa0CzA/B1SnpQ+yjZCNAPH7Y4yYTIKfeE/DQ6COLlUixQxVMAUN1L5+GXML+px6fRigA5w==
dependencies: dependencies:
"@budibase/types" "2.0.24-alpha.0" "@budibase/types" "2.0.24-alpha.3"
"@shopify/jest-koa-mocks" "5.0.1" "@shopify/jest-koa-mocks" "5.0.1"
"@techpass/passport-openidconnect" "0.3.2" "@techpass/passport-openidconnect" "0.3.2"
aws-sdk "2.1030.0" aws-sdk "2.1030.0"
@ -1180,13 +1180,13 @@
svelte-flatpickr "^3.2.3" svelte-flatpickr "^3.2.3"
svelte-portal "^1.0.0" svelte-portal "^1.0.0"
"@budibase/pro@2.0.24-alpha.0": "@budibase/pro@2.0.24-alpha.3":
version "2.0.24-alpha.0" version "2.0.24-alpha.3"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.24-alpha.0.tgz#c7c70f431d9d17603d965b353dbb7c4956b6f7a0" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.24-alpha.3.tgz#16605029663b07d0e3bcf0c08893878b542b6752"
integrity sha512-bp+m9mXEonvxiLo4s9Gt4vhHuVrderFB4vfCISMRcIpZTo/CPVkMTOEM7C4liZyXOiLB9hh4Y+vQY1T6ilIr3Q== integrity sha512-gBdqJzvtEAMJkhB2YTAyVYKoCANA25F1wT/K7kQtTOy5LrjDmcXah7CZoetfZQGbsKxUYEAZwJzxLSXmIxvWmQ==
dependencies: dependencies:
"@budibase/backend-core" "2.0.24-alpha.0" "@budibase/backend-core" "2.0.24-alpha.3"
"@budibase/types" "2.0.24-alpha.0" "@budibase/types" "2.0.24-alpha.3"
"@koa/router" "8.0.8" "@koa/router" "8.0.8"
joi "17.6.0" joi "17.6.0"
node-fetch "^2.6.1" node-fetch "^2.6.1"
@ -1209,10 +1209,10 @@
svelte-apexcharts "^1.0.2" svelte-apexcharts "^1.0.2"
svelte-flatpickr "^3.1.0" svelte-flatpickr "^3.1.0"
"@budibase/types@2.0.24-alpha.0": "@budibase/types@2.0.24-alpha.3":
version "2.0.24-alpha.0" version "2.0.24-alpha.3"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.24-alpha.0.tgz#d5ff01dc4a6f1d584077b1808c0a6c8cffbd1eea" resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.24-alpha.3.tgz#84eec5f991a2cfaf48d968b07a075b343f847289"
integrity sha512-eiTumTQp6d9+bizhImqK+OJ4g4b9IuUTCOdNp7K2rwu0v94xxLuQEtJUA5MKiqClx7Uu27IlybeQmP+oM99gTg== integrity sha512-f9PhtqzmqPI76ITXttuvxsvqMUJtkrDYf/4MHlI2v5ssNL9r0C/hbQEXllff3L3JqViEHWxkKFmfvfnDTV8rRQ==
"@bull-board/api@3.7.0": "@bull-board/api@3.7.0":
version "3.7.0" version "3.7.0"

View File

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

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/types", "name": "@budibase/types",
"version": "2.0.24-alpha.0", "version": "2.0.24-alpha.3",
"description": "Budibase types", "description": "Budibase types",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "2.0.24-alpha.0", "version": "2.0.24-alpha.3",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -36,10 +36,10 @@
"author": "Budibase", "author": "Budibase",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@budibase/backend-core": "2.0.24-alpha.0", "@budibase/backend-core": "2.0.24-alpha.3",
"@budibase/pro": "2.0.24-alpha.0", "@budibase/pro": "2.0.24-alpha.3",
"@budibase/string-templates": "2.0.24-alpha.0", "@budibase/string-templates": "2.0.24-alpha.3",
"@budibase/types": "2.0.24-alpha.0", "@budibase/types": "2.0.24-alpha.3",
"@koa/router": "8.0.8", "@koa/router": "8.0.8",
"@sentry/node": "6.17.7", "@sentry/node": "6.17.7",
"@techpass/passport-openidconnect": "0.3.2", "@techpass/passport-openidconnect": "0.3.2",

View File

@ -291,12 +291,12 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@2.0.24-alpha.0": "@budibase/backend-core@2.0.24-alpha.3":
version "2.0.24-alpha.0" version "2.0.24-alpha.3"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.24-alpha.0.tgz#db53e562cd40aa21783f6f4d6eaf880ec8225a8f" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.24-alpha.3.tgz#49a92082d1ca6bed0eb82519a6c02d7d14ead751"
integrity sha512-o8Ooa5Ga3hcbovoO+Fjyp++TcJmt8v83Y7GfurLznjAc5lakdEe8RWKR9JV7xqenXpZ49ZwWYTSOLwxFBiJuvw== integrity sha512-Zm/ddRDMzMuXCoEXZa0CzA/B1SnpQ+yjZCNAPH7Y4yYTIKfeE/DQ6COLlUixQxVMAUN1L5+GXML+px6fRigA5w==
dependencies: dependencies:
"@budibase/types" "2.0.24-alpha.0" "@budibase/types" "2.0.24-alpha.3"
"@shopify/jest-koa-mocks" "5.0.1" "@shopify/jest-koa-mocks" "5.0.1"
"@techpass/passport-openidconnect" "0.3.2" "@techpass/passport-openidconnect" "0.3.2"
aws-sdk "2.1030.0" aws-sdk "2.1030.0"
@ -327,21 +327,21 @@
uuid "8.3.2" uuid "8.3.2"
zlib "1.0.5" zlib "1.0.5"
"@budibase/pro@2.0.24-alpha.0": "@budibase/pro@2.0.24-alpha.3":
version "2.0.24-alpha.0" version "2.0.24-alpha.3"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.24-alpha.0.tgz#c7c70f431d9d17603d965b353dbb7c4956b6f7a0" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.24-alpha.3.tgz#16605029663b07d0e3bcf0c08893878b542b6752"
integrity sha512-bp+m9mXEonvxiLo4s9Gt4vhHuVrderFB4vfCISMRcIpZTo/CPVkMTOEM7C4liZyXOiLB9hh4Y+vQY1T6ilIr3Q== integrity sha512-gBdqJzvtEAMJkhB2YTAyVYKoCANA25F1wT/K7kQtTOy5LrjDmcXah7CZoetfZQGbsKxUYEAZwJzxLSXmIxvWmQ==
dependencies: dependencies:
"@budibase/backend-core" "2.0.24-alpha.0" "@budibase/backend-core" "2.0.24-alpha.3"
"@budibase/types" "2.0.24-alpha.0" "@budibase/types" "2.0.24-alpha.3"
"@koa/router" "8.0.8" "@koa/router" "8.0.8"
joi "17.6.0" joi "17.6.0"
node-fetch "^2.6.1" node-fetch "^2.6.1"
"@budibase/types@2.0.24-alpha.0": "@budibase/types@2.0.24-alpha.3":
version "2.0.24-alpha.0" version "2.0.24-alpha.3"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.24-alpha.0.tgz#d5ff01dc4a6f1d584077b1808c0a6c8cffbd1eea" resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.24-alpha.3.tgz#84eec5f991a2cfaf48d968b07a075b343f847289"
integrity sha512-eiTumTQp6d9+bizhImqK+OJ4g4b9IuUTCOdNp7K2rwu0v94xxLuQEtJUA5MKiqClx7Uu27IlybeQmP+oM99gTg== integrity sha512-f9PhtqzmqPI76ITXttuvxsvqMUJtkrDYf/4MHlI2v5ssNL9r0C/hbQEXllff3L3JqViEHWxkKFmfvfnDTV8rRQ==
"@cspotcode/source-map-consumer@0.8.0": "@cspotcode/source-map-consumer@0.8.0":
version "0.8.0" version "0.8.0"