Merge branch 'develop' of github.com:Budibase/budibase into custom-form-validation

This commit is contained in:
Andrew Kingston 2021-08-11 15:09:04 +01:00
commit d1bc289f4b
53 changed files with 328 additions and 160 deletions

View File

@ -5,7 +5,7 @@ version: "3"
services:
app-service:
restart: always
image: budibase/apps
image: budibase.docker.scarf.sh/budibase/apps
container_name: bbapps
ports:
- "${APP_PORT}:4002"
@ -33,7 +33,7 @@ services:
worker-service:
restart: always
image: budibase/worker
image: budibase.docker.scarf.sh/budibase/worker
container_name: bbworker
ports:
- "${WORKER_PORT}:4003"

View File

@ -1,5 +1,5 @@
{
"version": "0.9.99-alpha.4",
"version": "0.9.105-alpha.3",
"npmClient": "yarn",
"packages": [
"packages/*"

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/auth",
"version": "0.9.99-alpha.4",
"version": "0.9.105-alpha.3",
"description": "Authentication middlewares for budibase builder and apps",
"main": "src/index.js",
"author": "Budibase",

View File

@ -4,6 +4,8 @@ const { DEFAULT_TENANT_ID } = require("../constants")
const env = require("../environment")
const { StaticDatabases, SEPARATOR } = require("./constants")
const { getTenantId } = require("../tenancy")
const fetch = require("node-fetch")
const { getCouch } = require("./index")
const UNICODE_MAX = "\ufff0"
@ -156,6 +158,23 @@ exports.getDeployedAppID = appId => {
return appId
}
/**
* if in production this will use the CouchDB _all_dbs call to retrieve a list of databases. If testing
* when using Pouch it will use the pouchdb-all-dbs package.
*/
exports.getAllDbs = async () => {
// specifically for testing we use the pouch package for this
if (env.isTest()) {
return getCouch().allDbs()
}
const response = await fetch(`${env.COUCH_DB_URL}/_all_dbs`)
if (response.status === 200) {
return response.json()
} else {
throw "Cannot connect to CouchDB instance"
}
}
/**
* Lots of different points in the system need to find the full list of apps, this will
* enumerate the entire CouchDB cluster and get the list of databases (every app).
@ -163,13 +182,13 @@ exports.getDeployedAppID = appId => {
* different users/companies apps as there is no security around it - all apps are returned.
* @return {Promise<object[]>} returns the app information document stored in each app database.
*/
exports.getAllApps = async (CouchDB, { dev, all } = {}) => {
exports.getAllApps = async (CouchDB, { dev, all, idsOnly } = {}) => {
let tenantId = getTenantId()
if (!env.MULTI_TENANCY && !tenantId) {
tenantId = DEFAULT_TENANT_ID
}
let allDbs = await CouchDB.allDbs()
const appDbNames = allDbs.filter(dbName => {
let dbs = await exports.getAllDbs()
const appDbNames = dbs.filter(dbName => {
const split = dbName.split(SEPARATOR)
// it is an app, check the tenantId
if (split[0] === DocumentTypes.APP) {
@ -183,6 +202,9 @@ exports.getAllApps = async (CouchDB, { dev, all } = {}) => {
}
return false
})
if (idsOnly) {
return appDbNames
}
const appPromises = appDbNames.map(db =>
// skip setup otherwise databases could be re-created
new CouchDB(db, { skip_setup: true }).get(DocumentTypes.APP_METADATA)

View File

@ -1,5 +1,4 @@
const PouchDB = require("pouchdb")
const allDbs = require("pouchdb-all-dbs")
const env = require("../../../../environment")
let POUCH_DB_DEFAULTS
@ -15,6 +14,4 @@ if (env.isTest()) {
const Pouch = PouchDB.defaults(POUCH_DB_DEFAULTS)
allDbs(Pouch)
module.exports = Pouch

View File

@ -1,7 +1,7 @@
{
"name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.",
"version": "0.9.99-alpha.4",
"version": "0.9.105-alpha.3",
"license": "AGPL-3.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",

View File

@ -9,6 +9,7 @@
export let text = null
export let disabled = false
export let error = null
export let size = "M"
const dispatch = createEventDispatcher()
const onChange = e => {
@ -18,5 +19,5 @@
</script>
<Field {label} {labelPosition} {error}>
<Checkbox {error} {disabled} {text} {value} on:change={onChange} />
<Checkbox {error} {disabled} {text} {value} {size} on:change={onChange} />
</Field>

View File

@ -8,6 +8,7 @@
export let id = null
export let text = null
export let disabled = false
export let size = null
const dispatch = createEventDispatcher()
const onChange = event => {
@ -16,7 +17,7 @@
</script>
<label
class="spectrum-Checkbox spectrum-Checkbox--sizeM spectrum-Checkbox--emphasized"
class="spectrum-Checkbox spectrum-Checkbox--size{size} spectrum-Checkbox--emphasized"
class:is-invalid={!!error}
>
<input

View File

@ -30,6 +30,7 @@
on:click={onClick}
class:is-selected={$selected.title === title}
class="spectrum-Tabs-item"
class:emphasized={$selected.title === title && $selected.emphasized}
tabindex="0"
>
{#if icon}
@ -49,3 +50,9 @@
<slot />
</Portal>
{/if}
<style>
.emphasized {
color: var(--spectrum-global-color-blue-600);
}
</style>

View File

@ -6,9 +6,11 @@
export let selected
export let vertical = false
export let noPadding = false
export let quiet = false
export let emphasized = false
let _id = id()
const tab = writable({ title: selected, id: _id })
const tab = writable({ title: selected, id: _id, emphasized })
setContext("tab", tab)
let container
@ -56,7 +58,9 @@
<div
bind:this={container}
class="selected-border spectrum-Tabs spectrum-Tabs--{vertical
class:quiet
class="selected-border spectrum-Tabs {quiet &&
'spectrum-Tabs--quiet'} spectrum-Tabs--{vertical
? 'vertical'
: 'horizontal'}"
>
@ -64,7 +68,8 @@
{#if $tab.info}
<div
class="spectrum-Tabs-selectionIndicator indicator-transition"
style="width: {width}; height: {height}; left: {left}; top: {top};"
style="{emphasized &&
'background-color: var(--spectrum-global-color-blue-400)'}; width: {width}; height: {height}; left: {left}; top: {top};"
/>
{/if}
</div>
@ -75,6 +80,10 @@
/>
<style>
.quiet {
border-bottom: none !important;
}
.spectrum-Tabs {
padding-left: var(--spacing-xl);
padding-right: var(--spacing-xl);

View File

@ -7,5 +7,15 @@ context("Screen Tests", () => {
it("Should successfully create a screen", () => {
cy.createScreen("Test Screen", "/test")
cy.get(".nav-items-container").within(() => {
cy.contains("/test").should("exist")
})
})
it("Should update the url", () => {
cy.createScreen("Test Screen", "test with spaces")
cy.get(".nav-items-container").within(() => {
cy.contains("/test-with-spaces").should("exist")
})
})
})

View File

@ -160,7 +160,4 @@ Cypress.Commands.add("createScreen", (screenName, route) => {
cy.get("input").eq(1).type(route)
cy.get(".spectrum-Button--cta").click()
})
cy.get(".nav-items-container").within(() => {
cy.contains(route).should("exist")
})
})

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
"version": "0.9.99-alpha.4",
"version": "0.9.105-alpha.3",
"license": "AGPL-3.0",
"private": true,
"scripts": {
@ -65,10 +65,10 @@
}
},
"dependencies": {
"@budibase/bbui": "^0.9.99-alpha.4",
"@budibase/client": "^0.9.99-alpha.4",
"@budibase/colorpicker": "^1.1.2",
"@budibase/string-templates": "^0.9.99-alpha.4",
"@budibase/bbui": "^0.9.105-alpha.3",
"@budibase/client": "^0.9.105-alpha.3",
"@budibase/colorpicker": "1.1.2",
"@budibase/string-templates": "^0.9.105-alpha.3",
"@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",

View File

@ -2,6 +2,7 @@
import { Select, Label, notifications, ModalContent } from "@budibase/bbui"
import { tables, views } from "stores/backend"
import analytics from "analytics"
import { FIELDS } from "constants/backend"
const CALCULATIONS = [
{
@ -25,13 +26,16 @@
)
$: fields =
viewTable &&
Object.keys(viewTable.schema).filter(
field =>
view.calculation === "count" ||
// don't want to perform calculations based on auto ID
(viewTable.schema[field].type === "number" &&
!viewTable.schema[field].autocolumn)
)
Object.keys(viewTable.schema).filter(fieldName => {
const field = viewTable.schema[fieldName]
return (
field.type !== FIELDS.FORMULA.type &&
field.type !== FIELDS.LINK.type &&
(view.calculation === "count" ||
// don't want to perform calculations based on auto ID
(field.type === "number" && !field.autocolumn))
)
})
function saveView() {
views.save(view)

View File

@ -18,10 +18,12 @@
let exportFormat = FORMATS[0].key
async function exportView() {
const filename = `export.${exportFormat}`
download(
`/api/views/export?view=${encodeURIComponent(
view
)}&format=${exportFormat}`
)}&format=${exportFormat}`,
filename
)
}
</script>

View File

@ -11,7 +11,11 @@
$: fields =
viewTable &&
Object.entries(viewTable.schema)
.filter(entry => entry[1].type !== FIELDS.LINK.type)
.filter(
entry =>
entry[1].type !== FIELDS.LINK.type &&
entry[1].type !== FIELDS.FORMULA.type
)
.map(([key]) => key)
function saveView() {

View File

@ -84,6 +84,7 @@
if (!event.detail.startsWith("/")) {
route = "/" + event.detail
}
route = route.replaceAll(" ", "-")
}
</script>

View File

@ -11,7 +11,11 @@
export let componentInstance
export let bindings
function setAssetProps(name, value) {
function setAssetProps(name, value, parser) {
if (parser && typeof parser === "function") {
value = parser(value)
}
const selectedAsset = get(currentAsset)
store.update(state => {
if (
@ -29,7 +33,12 @@
const screenSettings = [
// { key: "description", label: "Description", control: Input },
{ key: "routing.route", label: "Route", control: Input },
{
key: "routing.route",
label: "Route",
control: Input,
parser: val => val.replaceAll(" ", "-"),
},
{ key: "routing.roleId", label: "Access", control: RoleSelect },
{ key: "layoutId", label: "Layout", control: LayoutSelect },
]
@ -44,7 +53,7 @@
label={def.label}
key={def.key}
value={deepGet($currentAsset, def.key)}
onChange={val => setAssetProps(def.key, val)}
on:change={event => setAssetProps(def.key, event.detail, def.parser)}
{bindings}
/>
{/each}

View File

@ -31,8 +31,8 @@
// Data to send off
let rating
let improvements
let comment
let improvements = ""
let comment = ""
function selectNumber(n) {
rating = n
@ -106,11 +106,7 @@
<Detail size="S">STEP 2 OF 3</Detail>
<ButtonGroup>
<Button secondary on:click={() => (step -= 1)}>Previous</Button>
<Button
disabled={!improvements}
primary
on:click={() => (step += 1)}>Next</Button
>
<Button primary on:click={() => (step += 1)}>Next</Button>
</ButtonGroup>
</div>
{:else}
@ -121,9 +117,7 @@
<Detail size="S">STEP 3 OF 3</Detail>
<ButtonGroup>
<Button secondary on:click={() => (step -= 1)}>Previous</Button>
<Button disabled={!comment} cta on:click={submitFeedback}
>Complete</Button
>
<Button cta on:click={submitFeedback}>Complete</Button>
</ButtonGroup>
</div>
{/if}

View File

@ -9,6 +9,7 @@
Checkbox,
} from "@budibase/bbui"
import { store, automationStore, hostingStore } from "builderStore"
import { admin } from "stores/portal"
import { string, mixed, object } from "yup"
import api, { get, post } from "builderStore/api"
import analytics from "analytics"
@ -102,6 +103,8 @@
if (applicationPkg.ok) {
await store.actions.initialise(pkg)
await automationStore.actions.fetch()
// update checklist - incase first app
await admin.init()
} else {
throw new Error(pkg)
}

View File

@ -199,6 +199,9 @@
delete datasource.entities[toTable.name].schema[originalToName]
}
// store the original names so it won't cause an error
originalToName = toRelationship.name
originalFromName = fromRelationship.name
await save()
await tables.fetch()
}

View File

@ -6,7 +6,7 @@
import OktaLogo from "assets/okta-logo.png"
import OneLoginLogo from "assets/onelogin-logo.png"
import { oidc, organisation } from "stores/portal"
import { oidc, organisation, auth } from "stores/portal"
import { onMount } from "svelte"
$: show = $organisation.oidc
@ -31,7 +31,10 @@
{#if show}
<ActionButton
on:click={() =>
window.open(`/api/global/auth/oidc/configs/${$oidc.uuid}`, "_blank")}
window.open(
`/api/global/auth/${$auth.tenantId}/oidc/configs/${$oidc.uuid}`,
"_blank"
)}
>
<div class="inner">
<img {src} alt="oidc icon" />

View File

@ -18,7 +18,7 @@
import api, { del } from "builderStore/api"
import analytics from "analytics"
import { onMount } from "svelte"
import { apps, auth } from "stores/portal"
import { apps, auth, admin } from "stores/portal"
import download from "downloadjs"
import { goto } from "@roxi/routify"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
@ -159,6 +159,8 @@
throw json.message
}
await apps.load()
// get checklist, just in case that was the last app
await admin.init()
notifications.success("App deleted successfully")
} catch (err) {
notifications.error(`Error deleting app: ${err}`)

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/cli",
"version": "0.9.99-alpha.4",
"version": "0.9.105-alpha.3",
"description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js",
"bin": {

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/client",
"version": "0.9.99-alpha.4",
"version": "0.9.105-alpha.3",
"license": "MPL-2.0",
"module": "dist/budibase-client.js",
"main": "dist/budibase-client.js",
@ -18,9 +18,9 @@
"dev:builder": "rollup -cw"
},
"dependencies": {
"@budibase/bbui": "^0.9.99-alpha.4",
"@budibase/standard-components": "^0.9.99-alpha.4",
"@budibase/string-templates": "^0.9.99-alpha.4",
"@budibase/bbui": "^0.9.105-alpha.3",
"@budibase/standard-components": "^0.9.105-alpha.3",
"@budibase/string-templates": "^0.9.105-alpha.3",
"regexparam": "^1.3.0",
"shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5"

View File

@ -1,7 +1,7 @@
{
"name": "@budibase/server",
"email": "hi@budibase.com",
"version": "0.9.99-alpha.4",
"version": "0.9.105-alpha.3",
"description": "Budibase Web Server",
"main": "src/index.js",
"repository": {
@ -62,9 +62,9 @@
"author": "Budibase",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@budibase/auth": "^0.9.99-alpha.4",
"@budibase/client": "^0.9.99-alpha.4",
"@budibase/string-templates": "^0.9.99-alpha.4",
"@budibase/auth": "^0.9.105-alpha.3",
"@budibase/client": "^0.9.105-alpha.3",
"@budibase/string-templates": "^0.9.105-alpha.3",
"@elastic/elasticsearch": "7.10.0",
"@koa/router": "8.0.0",
"@sendgrid/mail": "7.1.1",
@ -117,7 +117,7 @@
"devDependencies": {
"@babel/core": "^7.14.3",
"@babel/preset-env": "^7.14.4",
"@budibase/standard-components": "^0.9.99-alpha.4",
"@budibase/standard-components": "^0.9.105-alpha.3",
"@jest/test-sequencer": "^24.8.0",
"@types/bull": "^3.15.1",
"@types/jest": "^26.0.23",

View File

@ -7,6 +7,7 @@
const CouchDB = require("../src/db")
const { DocumentTypes } = require("../src/db/utils")
const { getAllDbs } = require("@budibase/auth/db")
const appName = process.argv[2].toLowerCase()
const remoteUrl = process.argv[3]
@ -14,8 +15,8 @@ const remoteUrl = process.argv[3]
console.log(`Replicating from ${appName} to ${remoteUrl}/${appName}`)
const run = async () => {
const allDbs = await CouchDB.allDbs()
const appDbNames = allDbs.filter(dbName => dbName.startsWith("inst_app"))
const dbs = await getAllDbs()
const appDbNames = dbs.filter(dbName => dbName.startsWith("inst_app"))
let apps = []
for (let dbName of appDbNames) {
const db = new CouchDB(dbName)

View File

@ -25,10 +25,10 @@ interface ManyRelationship {
interface RunConfig {
id: string
row: Row
filters: SearchFilters
sort: SortJson
paginate: PaginationJson
row: Row
}
module External {
@ -89,8 +89,9 @@ module External {
// build id array
let idParts = []
for (let field of primary) {
if (row[field]) {
idParts.push(row[field])
const fieldValue = row[`${table.name}.${field}`]
if (fieldValue) {
idParts.push(fieldValue)
}
}
if (idParts.length === 0) {
@ -115,7 +116,11 @@ module External {
const thisRow: { [key: string]: any } = {}
// filter the row down to what is actually the row (not joined)
for (let fieldName of Object.keys(table.schema)) {
thisRow[fieldName] = row[fieldName]
const value = row[`${table.name}.${fieldName}`]
// all responses include "select col as table.col" so that overlaps are handled
if (value) {
thisRow[fieldName] = value
}
}
thisRow._id = generateIdForRow(row, table)
thisRow.tableId = table._id
@ -191,7 +196,7 @@ module External {
const isUpdate = !field.through
const thisKey: string = isUpdate ? "id" : linkTablePrimary
// @ts-ignore
const otherKey: string = isUpdate ? field.foreignKey : tablePrimary
const otherKey: string = isUpdate ? field.fieldName : tablePrimary
row[key].map((relationship: any) => {
// we don't really support composite keys for relationships, this is why [0] is used
manyRelationships.push({
@ -359,7 +364,7 @@ module External {
}
}
if (cache[fullKey] == null) {
cache[fullKey] = await makeExternalQuery(this.appId, {
const response = await makeExternalQuery(this.appId, {
endpoint: getEndpoint(tableId, DataSourceOperation.READ),
filters: {
equal: {
@ -367,8 +372,12 @@ module External {
},
},
})
// this is the response from knex if no rows found
if (!response[0].read) {
cache[fullKey] = response
}
}
return { rows: cache[fullKey], table }
return { rows: cache[fullKey] || [], table }
}
/**
@ -418,12 +427,16 @@ module External {
const { tableName } = breakExternalTableId(tableId)
const table = this.tables[tableName]
for (let row of rows) {
promises.push(
makeExternalQuery(this.appId, {
endpoint: getEndpoint(tableId, DataSourceOperation.DELETE),
filters: buildFilters(generateIdForRow(row, table), {}, table),
})
)
const filters = buildFilters(generateIdForRow(row, table), {}, table)
// safety check, if there are no filters on deletion bad things happen
if (Object.keys(filters).length !== 0) {
promises.push(
makeExternalQuery(this.appId, {
endpoint: getEndpoint(tableId, DataSourceOperation.DELETE),
filters,
})
)
}
}
}
await Promise.all(promises)
@ -442,7 +455,7 @@ module External {
.filter(
column =>
column[1].type !== FieldTypes.LINK &&
!existing.find((field: string) => field.includes(column[0]))
!existing.find((field: string) => field === column[0])
)
.map(column => `${table.name}.${column[0]}`)
}

View File

@ -3,7 +3,11 @@ exports.csv = function (headers, rows) {
for (let row of rows) {
csv = `${csv}\n${headers
.map(header => `"${row[header]}"`.trim())
.map(header => {
let val = row[header]
val = typeof val === "object" ? JSON.stringify(val) : val
return `"${val}"`.trim()
})
.join(",")}`
}
return csv

View File

@ -82,7 +82,7 @@ describe("/datasources", () => {
entityId: "users",
},
resource: {
fields: ["name", "age"],
fields: ["users.name", "users.age"],
},
filters: {
string: {
@ -94,7 +94,7 @@ describe("/datasources", () => {
.expect(200)
// this is mock data, can't test it
expect(res.body).toBeDefined()
expect(pg.queryMock).toHaveBeenCalledWith(`select "name", "age" from "users" where "users"."name" like $1 limit $2`, ["John%", 5000])
expect(pg.queryMock).toHaveBeenCalledWith(`select "users"."name" as "users.name", "users"."age" as "users.age" from "users" where "users"."name" like $1 limit $2`, ["John%", 5000])
})
})

View File

@ -27,6 +27,7 @@ module.exports.definition = {
requestMethod: "POST",
url: "http://",
requestBody: "{}",
headers: "{}",
},
schema: {
inputs: {
@ -45,6 +46,11 @@ module.exports.definition = {
title: "JSON Body",
customType: "wide",
},
headers: {
type: "string",
title: "Headers",
customType: "wide",
},
},
required: ["requestMethod", "url"],
},
@ -65,7 +71,7 @@ module.exports.definition = {
}
module.exports.run = async function ({ inputs }) {
let { requestMethod, url, requestBody } = inputs
let { requestMethod, url, requestBody, headers } = inputs
if (!url.startsWith("http")) {
url = `http://${url}`
}
@ -84,6 +90,15 @@ module.exports.run = async function ({ inputs }) {
request.headers = {
"Content-Type": "application/json",
}
if (headers && headers.length !== 0) {
try {
const customHeaders = JSON.parse(headers)
request.headers = { ...request.headers, ...customHeaders }
} catch (err) {
console.error(err)
}
}
}
try {

View File

@ -24,21 +24,7 @@ if (env.isTest()) {
const Pouch = PouchDB.defaults(POUCH_DB_DEFAULTS)
// have to still have pouch alldbs for testing
allDbs(Pouch)
// replicate your local levelDB pouch to a running HTTP compliant couch or pouchdb server.
/* istanbul ignore next */
// eslint-disable-next-line no-unused-vars
function replicateLocal() {
Pouch.allDbs().then(dbs => {
for (let db of dbs) {
new Pouch(db).sync(
new PouchDB(`http://127.0.0.1:5984/${db}`, { live: true })
)
}
})
}
// replicateLocal()
module.exports = Pouch

View File

@ -322,7 +322,7 @@ class LinkController {
// remove schema from other table
let linkedTable = await this._db.get(field.tableId)
delete linkedTable.schema[field.fieldName]
this._db.put(linkedTable)
await this._db.put(linkedTable)
}
/**

View File

@ -151,7 +151,9 @@ function buildRead(knex: Knex, json: QueryJson, limit: number): KnexQuery {
}
// handle select
if (resource.fields && resource.fields.length > 0) {
query = query.select(resource.fields)
// select the resources as the format "table.columnName" - this is what is provided
// by the resource builder further up
query = query.select(resource.fields.map(field => `${field} as ${field}`))
} else {
query = query.select("*")
}

View File

@ -31,8 +31,8 @@ const INTEGRATIONS = {
[SourceNames.MONGODB]: mongodb.integration,
[SourceNames.ELASTICSEARCH]: elasticsearch.integration,
[SourceNames.COUCHDB]: couchdb.integration,
[SourceNames.SQL_SERVER]: s3.integration,
[SourceNames.S3]: sqlServer.integration,
[SourceNames.SQL_SERVER]: sqlServer.integration,
[SourceNames.S3]: s3.integration,
[SourceNames.AIRTABLE]: airtable.integration,
[SourceNames.MYSQL]: mysql.integration,
[SourceNames.ARANGODB]: arangodb.integration,

View File

@ -242,7 +242,7 @@ module MySQLModule {
const input = this._query(json, { disableReturning: true })
let row
// need to manage returning, a feature mySQL can't do
if (operation === "awdawd") {
if (operation === operation.DELETE) {
row = this.getReturningRow(json)
}
const results = await internalQuery(this.client, input, false)

View File

@ -62,12 +62,13 @@ describe("SQL query builder", () => {
})
it("should test a read with specific columns", () => {
const nameProp = `${TABLE_NAME}.name`, ageProp = `${TABLE_NAME}.age`
const query = sql._query(generateReadJson({
fields: ["name", "age"]
fields: [nameProp, ageProp]
}))
expect(query).toEqual({
bindings: [limit],
sql: `select "name", "age" from "${TABLE_NAME}" limit $1`
sql: `select "${TABLE_NAME}"."name" as "${nameProp}", "${TABLE_NAME}"."age" as "${ageProp}" from "${TABLE_NAME}" limit $1`
})
})

View File

@ -40,8 +40,13 @@ export function breakRowIdField(_id: string): any[] {
// have to replace on the way back as we swapped out the double quotes
// when encoding, but JSON can't handle the single quotes
const decoded: string = decodeURIComponent(_id).replace(/'/g, '"')
const parsed = JSON.parse(decoded)
return Array.isArray(parsed) ? parsed : [parsed]
try {
const parsed = JSON.parse(decoded)
return Array.isArray(parsed) ? parsed : [parsed]
} catch (err) {
// wasn't json - likely was handlebars for a many to many
return [_id]
}
}
export function convertType(type: string, map: { [key: string]: any }) {

View File

@ -10,7 +10,7 @@ const CouchDB = require("../db")
module.exports = async (ctx, next) => {
// try to get the appID from the request
const requestAppId = getAppId(ctx)
let requestAppId = getAppId(ctx)
// get app cookie if it exists
let appCookie = null
try {
@ -29,6 +29,8 @@ module.exports = async (ctx, next) => {
clearCookie(ctx, Cookies.CurrentApp)
return next()
}
// if the request app ID wasn't set, update it with the cookie
requestAppId = requestAppId || appId
}
let appId,

View File

@ -1,5 +1,5 @@
const env = require("../environment")
const { OBJ_STORE_DIRECTORY, ObjectStoreBuckets } = require("../constants")
const { OBJ_STORE_DIRECTORY } = require("../constants")
const { sanitizeKey } = require("@budibase/auth/src/objectStore")
const BB_CDN = "https://cdn.app.budi.live/assets"
@ -52,6 +52,6 @@ exports.clientLibraryPath = appId => {
exports.attachmentsRelativeURL = attachmentKey => {
return exports.checkSlashesInUrl(
`/${ObjectStoreBuckets.APPS}/${attachmentKey}`
`${exports.objectStoreUrl()}/${attachmentKey}`
)
}

View File

@ -184,7 +184,13 @@ exports.inputProcessing = (user = {}, table, row) => {
}
continue
}
clonedRow[key] = exports.coerce(value, field.type)
// specific case to delete formula values if they get saved
// type coercion cannot completely remove the field, so have to do it here
if (field.type === FieldTypes.FORMULA) {
delete clonedRow[key]
} else {
clonedRow[key] = exports.coerce(value, field.type)
}
}
// handle auto columns - this returns an object like {table, row}
return processAutoColumn(user, copiedTable, clonedRow)

View File

@ -1146,11 +1146,12 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/auth@^0.9.79-alpha.4":
version "0.9.79"
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.79.tgz#416271ffc55e84116550469656bf151a7734a90f"
integrity sha512-ENh099tYeUfVExsAeoxwMh2ODioKQGPteK9LJiU5hMdM4Oi7pyImu287BgKpTIheB+WtadT4e21VpPaJ62APEw==
"@budibase/auth@^0.9.102":
version "0.9.102"
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.102.tgz#86bb653686a311858b6ab6a048c6a4746bd5ced4"
integrity sha512-Wov1vWAkWQn2P/gVapMBDdGKE1OwbpyD+3LV9E1HqZ3AASWZztqAFfnUaF6kKsHeem8S69uZ3vBU6uuyn9Sp+w==
dependencies:
"@techpass/passport-openidconnect" "^0.3.0"
aws-sdk "^2.901.0"
bcryptjs "^2.4.3"
ioredis "^4.27.1"
@ -1167,10 +1168,10 @@
uuid "^8.3.2"
zlib "^1.0.5"
"@budibase/bbui@^0.9.79":
version "0.9.79"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.79.tgz#c033ba0af41cb584d2657a8353f9887328f6633f"
integrity sha512-XxUJSPGd2FZDFdbNOeMUXohhID5h3DVq9XyKTe6WhYax4m2da/2WTENJ16UFvmfA+yxLN1qSDeweq9vw2zCahQ==
"@budibase/bbui@^0.9.102":
version "0.9.102"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.102.tgz#e27a1763086651c6a9832a81b3695c10b0d52079"
integrity sha512-DKtfEoUdDm7aApfMHQi53TB18Qj4+c/CYWTyWP8T7+G/h4pCDdYEsjokn1MT2pbouPzECcjelw2w3k78Ac3N2Q==
dependencies:
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
"@spectrum-css/actionbutton" "^1.0.1"
@ -1215,14 +1216,14 @@
svelte-flatpickr "^3.1.0"
svelte-portal "^1.0.0"
"@budibase/client@^0.9.79-alpha.4":
version "0.9.79"
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.79.tgz#d1c8d51e9121f81902cfb31d3b685c8061f272a2"
integrity sha512-//Yqm5Qki6BmBe5W2Tz8GONdkFjdD1jkIU7pcLYKqdZJWEQIrX6T/xNvYvZVhw7Dx5bwSZRjFwzm7jLoiyHBIA==
"@budibase/client@^0.9.102":
version "0.9.102"
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.102.tgz#4f1d5f84671182ecb65c1d09f691584dbd687fd3"
integrity sha512-Uu7DzFOKlIKGF4IO24kS8ANiN5rf7i90KRqLfPDh1I8vdEj5L9sYQYyyI9YwUJ48keuoGfqHHlcH48dPJU+cxQ==
dependencies:
"@budibase/bbui" "^0.9.79"
"@budibase/standard-components" "^0.9.79"
"@budibase/string-templates" "^0.9.79"
"@budibase/bbui" "^0.9.102"
"@budibase/standard-components" "^0.9.102"
"@budibase/string-templates" "^0.9.102"
regexparam "^1.3.0"
shortid "^2.2.15"
svelte-spa-router "^3.0.5"
@ -1255,24 +1256,26 @@
to-gfm-code-block "^0.1.1"
year "^0.2.1"
"@budibase/standard-components@^0.9.79", "@budibase/standard-components@^0.9.79-alpha.4":
version "0.9.79"
resolved "https://registry.yarnpkg.com/@budibase/standard-components/-/standard-components-0.9.79.tgz#24206642e0cdc655ea3a99ed5e9402ec4f6b3ba8"
integrity sha512-ZWhmBZ1iG+CjGMEvT/jtugMMgA1n88UYcOfP3BSP2P3eA16DubyU9hH9OyJHbGPzDHLoBF6vuS/5ZPZCkOKppw==
"@budibase/standard-components@^0.9.102":
version "0.9.102"
resolved "https://registry.yarnpkg.com/@budibase/standard-components/-/standard-components-0.9.102.tgz#4158eb5e2c46a403f2968cfa6a47751f402e776f"
integrity sha512-N8PcMzktBThMOb+LLBUdVi4WI2LxhEgWEXQr6tD0wKigxL734wWxRf2QWskFSJb/YvJoSP74jR9LF5P0zt1oTw==
dependencies:
"@budibase/bbui" "^0.9.79"
"@budibase/bbui" "^0.9.102"
"@spectrum-css/card" "^3.0.3"
"@spectrum-css/link" "^3.1.3"
"@spectrum-css/page" "^3.0.1"
"@spectrum-css/typography" "^3.0.2"
"@spectrum-css/vars" "^3.0.1"
apexcharts "^3.22.1"
dayjs "^1.10.5"
svelte-apexcharts "^1.0.2"
svelte-flatpickr "^3.1.0"
"@budibase/string-templates@^0.9.79", "@budibase/string-templates@^0.9.79-alpha.4":
version "0.9.79"
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.79.tgz#bb75a7433a7cfda1fc488283f35e47879b799fcc"
integrity sha512-hkAne5mx7mj8+osXFt45VwgLKSa94uQOGOb4R8uv9WNzvk4RzcjBfRzJxggv29FUemItrAeZpSh+Um6yugFI+w==
"@budibase/string-templates@^0.9.102":
version "0.9.102"
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.102.tgz#5beb1dac696781b404020d5ab6bb91cbe4dcd10a"
integrity sha512-cDDh5tyGYtA2sr8VDaRMLp9Ff+icXKNZ5BredePfES5du3GOWjFeVe0h1WFsR12Yq1dH1WWKR1XKc9CNwoQ8YA==
dependencies:
"@budibase/handlebars-helpers" "^0.11.4"
dayjs "^1.10.4"
@ -2111,6 +2114,11 @@
resolved "https://registry.yarnpkg.com/@spectrum-css/buttongroup/-/buttongroup-3.0.3.tgz#719d868845ac9d2c4f939c1b9f6044507902d5aa"
integrity sha512-eXl8U4QWMWXqyTu654FdQdEGnmczgOYlpIFSHyCMVjhtPqZp2xwnLFiGh6LKw+bLio6eeOZ0L+vpk1GcoYqgkw==
"@spectrum-css/card@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/card/-/card-3.0.3.tgz#56b2e2da6b80c1583228baa279de7407383bfb6b"
integrity sha512-+oKLUI2a0QmQP9EzySeq/G4FpUkkdaDNbuEbqCj2IkPMc/2v/nwzsPhh1fj2UIghGAiiUwXfPpzax1e8fyhQUg==
"@spectrum-css/checkbox@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/checkbox/-/checkbox-3.0.3.tgz#8577067fc8b97e4609f92bd242364937a533a7bb"
@ -2270,7 +2278,7 @@
resolved "https://registry.yarnpkg.com/@spectrum-css/treeview/-/treeview-3.0.3.tgz#aeda5175158b9f8d7529cb2b394428eb2a428046"
integrity sha512-D5gGzZC/KtRArdx86Mesc9+99W9nTbUOeyYGqoJoAfJSOttoT6Tk5CrDvlCmAqjKf5rajemAkGri1ChqvUIwkw==
"@spectrum-css/typography@^3.0.1":
"@spectrum-css/typography@^3.0.1", "@spectrum-css/typography@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/typography/-/typography-3.0.2.tgz#ea3ca0a60e18064527819d48c8c4364cab4fcd38"
integrity sha512-5ZOLmQe0edzsDMyhghUd4hBb5uxGsFrxzf+WasfcUw9klSfTsRZ09n1BsaaWbgrLjlMQ+EEHS46v5VNo0Ms2CA==
@ -2292,6 +2300,17 @@
dependencies:
defer-to-connect "^1.0.1"
"@techpass/passport-openidconnect@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@techpass/passport-openidconnect/-/passport-openidconnect-0.3.0.tgz#a60b2bbf3f262649a5a02d5d186219944acc3010"
integrity sha512-bVsPwl66s7J7GHxTPlW/RJYhZol9SshNznQsx83OOh9G+JWFGoeWxh+xbX+FTdJNoUvGIGbJnpWPY2wC6NOHPw==
dependencies:
base64url "^3.0.1"
oauth "^0.9.15"
passport-strategy "^1.0.0"
request "^2.88.0"
webfinger "^0.4.2"
"@tootallnate/once@1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
@ -3286,7 +3305,7 @@ base64-js@^1.0.2, base64-js@^1.3.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
base64url@3.x.x:
base64url@3.x.x, base64url@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d"
integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==
@ -8707,7 +8726,7 @@ oauth-sign@~0.9.0:
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
oauth@0.9.x:
oauth@0.9.x, oauth@^0.9.15:
version "0.9.15"
resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1"
integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE=
@ -10081,7 +10100,7 @@ request-promise-native@^1.0.5:
stealthy-require "^1.1.1"
tough-cookie "^2.3.3"
"request@>= 2.52.0", request@^2.72.0, request@^2.74.0, request@^2.87.0:
"request@>= 2.52.0", request@^2.72.0, request@^2.74.0, request@^2.87.0, request@^2.88.0:
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
@ -10205,7 +10224,7 @@ rimraf@2.6.3:
dependencies:
glob "^7.1.3"
rimraf@^3.0.0:
rimraf@^3.0.0, rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
@ -10290,7 +10309,7 @@ sax@1.2.1:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o=
sax@>=0.6.0, sax@^1.2.4:
sax@>=0.1.1, sax@>=0.6.0, sax@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
@ -10745,6 +10764,11 @@ stealthy-require@^1.1.1:
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
step@0.0.x:
version "0.0.6"
resolved "https://registry.yarnpkg.com/step/-/step-0.0.6.tgz#143e7849a5d7d3f4a088fe29af94915216eeede2"
integrity sha1-FD54SaXX0/SgiP4pr5SRUhbu7eI=
strict-uri-encode@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
@ -11786,6 +11810,14 @@ walker@^1.0.7, walker@~1.0.5:
dependencies:
makeerror "1.0.x"
webfinger@^0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/webfinger/-/webfinger-0.4.2.tgz#3477a6d97799461896039fcffc650b73468ee76d"
integrity sha1-NHem2XeZRhiWA5/P/GULc0aO520=
dependencies:
step "0.0.x"
xml2js "0.1.x"
webidl-conversions@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
@ -11992,6 +12024,13 @@ xml-parse-from-string@^1.0.0:
resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28"
integrity sha1-qQKekp09vN7RafPG4oI42VpdWig=
xml2js@0.1.x:
version "0.1.14"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.1.14.tgz#5274e67f5a64c5f92974cd85139e0332adc6b90c"
integrity sha1-UnTmf1pkxfkpdM2FE54DMq3GuQw=
dependencies:
sax ">=0.1.1"
xml2js@0.4.19:
version "0.4.19"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"

View File

@ -1888,7 +1888,6 @@
"booleanfield": {
"name": "Checkbox",
"icon": "Checkmark",
"styles": ["size"],
"illegalChildren": ["section"],
"settings": [
{
@ -1906,6 +1905,30 @@
"label": "Text",
"key": "text"
},
{
"type": "select",
"label": "Size",
"key": "size",
"options": [
{
"label": "Small",
"value": "S"
},
{
"label": "Medium",
"value": "M"
},
{
"label": "Large",
"value": "L"
},
{
"label": "Extra large",
"value": "XL"
}
],
"defaultValue": "M"
},
{
"type": "boolean",
"label": "Disabled",

View File

@ -29,11 +29,11 @@
"keywords": [
"svelte"
],
"version": "0.9.99-alpha.4",
"version": "0.9.105-alpha.3",
"license": "MIT",
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
"dependencies": {
"@budibase/bbui": "^0.9.99-alpha.4",
"@budibase/bbui": "^0.9.105-alpha.3",
"@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3",
"@spectrum-css/link": "^3.1.3",

View File

@ -6,6 +6,7 @@
export let label
export let text
export let disabled = false
export let size
export let validation
let fieldState
@ -28,6 +29,7 @@
disabled={$fieldState.disabled}
error={$fieldState.error}
id={$fieldState.fieldId}
{size}
on:change={e => fieldApi.setValue(e.detail)}
{text}
/>

View File

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

View File

@ -126,6 +126,7 @@ module.exports.isValid = string => {
"object",
"array",
"cannot read property",
"undefined",
]
// this is a portion of a specific string always output by handlebars in the case of a syntax error
const invalidCases = [`expecting '`]

View File

@ -1,7 +1,7 @@
{
"name": "@budibase/worker",
"email": "hi@budibase.com",
"version": "0.9.99-alpha.4",
"version": "0.9.105-alpha.3",
"description": "Budibase background service",
"main": "src/index.js",
"repository": {
@ -23,8 +23,8 @@
"author": "Budibase",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@budibase/auth": "^0.9.99-alpha.4",
"@budibase/string-templates": "^0.9.99-alpha.4",
"@budibase/auth": "^0.9.105-alpha.3",
"@budibase/string-templates": "^0.9.105-alpha.3",
"@koa/router": "^8.0.0",
"@techpass/passport-openidconnect": "^0.3.0",
"aws-sdk": "^2.811.0",

View File

@ -4,14 +4,9 @@ const CouchDB = require("../../db")
const URL_REGEX_SLASH = /\/|\\/g
exports.getApps = async ctx => {
const tenantId = ctx.user.tenantId
const apps = await getAllApps(CouchDB, { tenantId })
const apps = await getAllApps(CouchDB, { dev: true })
const body = {}
for (let app of apps) {
if (app.status !== "fulfilled") {
continue
}
app = app.value
let url = app.url || encodeURI(`${app.name}`)
url = `/${url.replace(URL_REGEX_SLASH, "")}`

View File

@ -223,7 +223,7 @@ exports.configChecklist = async function (ctx) {
// TODO: Watch get started video
// Apps exist
const apps = await getAllApps(CouchDB)
const apps = await getAllApps(CouchDB, { idsOnly: true })
// They have set up SMTP
const smtpConfig = await getScopedFullConfig(db, {

View File

@ -30,7 +30,9 @@ function buildResetUpdateValidation() {
}
function updateTenant(ctx, next) {
updateTenantId(ctx.params.tenantId)
if (ctx.params) {
updateTenantId(ctx.params.tenantId)
}
return next()
}

View File

@ -19,6 +19,7 @@ if (env.isTest()) {
const Pouch = PouchDB.defaults(POUCH_DB_DEFAULTS)
// have to still have pouch alldbs for testing
allDbs(Pouch)
module.exports = Pouch

View File

@ -287,11 +287,12 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/auth@^0.9.79-alpha.4":
version "0.9.79"
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.79.tgz#416271ffc55e84116550469656bf151a7734a90f"
integrity sha512-ENh099tYeUfVExsAeoxwMh2ODioKQGPteK9LJiU5hMdM4Oi7pyImu287BgKpTIheB+WtadT4e21VpPaJ62APEw==
"@budibase/auth@^0.9.102":
version "0.9.102"
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.102.tgz#86bb653686a311858b6ab6a048c6a4746bd5ced4"
integrity sha512-Wov1vWAkWQn2P/gVapMBDdGKE1OwbpyD+3LV9E1HqZ3AASWZztqAFfnUaF6kKsHeem8S69uZ3vBU6uuyn9Sp+w==
dependencies:
"@techpass/passport-openidconnect" "^0.3.0"
aws-sdk "^2.901.0"
bcryptjs "^2.4.3"
ioredis "^4.27.1"
@ -336,10 +337,10 @@
to-gfm-code-block "^0.1.1"
year "^0.2.1"
"@budibase/string-templates@^0.9.79-alpha.4":
version "0.9.79"
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.79.tgz#bb75a7433a7cfda1fc488283f35e47879b799fcc"
integrity sha512-hkAne5mx7mj8+osXFt45VwgLKSa94uQOGOb4R8uv9WNzvk4RzcjBfRzJxggv29FUemItrAeZpSh+Um6yugFI+w==
"@budibase/string-templates@^0.9.102":
version "0.9.102"
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.102.tgz#5beb1dac696781b404020d5ab6bb91cbe4dcd10a"
integrity sha512-cDDh5tyGYtA2sr8VDaRMLp9Ff+icXKNZ5BredePfES5du3GOWjFeVe0h1WFsR12Yq1dH1WWKR1XKc9CNwoQ8YA==
dependencies:
"@budibase/handlebars-helpers" "^0.11.4"
dayjs "^1.10.4"