Bug - BUDI-6068 filters do not work for google sheets (#9886)

* Add data-utils with filters

* Create data-utils

* Add data-utils to compiled code

* Reuse constants

* Fix tests

* Rename package to shared-core

* Namespace export shared-core

* Rely on rollup to bundle shared-core

* Revert "Rely on rollup to bundle shared-core"

This reverts commit e8b5a2bb9a.

* Updating version and removing private.

* Update version

* Increment versions.

* Implement sort

* Enabling sort

* v2.3.21-alpha.2

* Fix build

* Improve readability

* Move deepGet to shared helper

* Better type usage

* Fix types

* Configure types

* Fix vite refs

* Add dep

* Fixing depencencies on client

* Add missing dev command

* Fix loading issues

* Update versions to latest

* Multiple es6 and commonjs configs

* Config

* Use local packages on rollup for client

* Change shared-core and types entry points

---------

Co-authored-by: mike12345567 <me@michaeldrury.co.uk>
This commit is contained in:
Adria Navarro 2023-03-09 08:50:26 +00:00 committed by mike12345567
parent d20724cec9
commit 63afd34eb2
35 changed files with 962 additions and 228 deletions

View File

@ -38,7 +38,8 @@
],
"dependencies": {
"@adobe/spectrum-css-workflow-icons": "1.2.1",
"@budibase/string-templates": "^2.4.8",
"@budibase/shared-core": "2.4.8",
"@budibase/string-templates": "2.4.8",
"@spectrum-css/accordion": "3.0.24",
"@spectrum-css/actionbutton": "1.0.1",
"@spectrum-css/actiongroup": "1.0.1",

View File

@ -1,3 +1,6 @@
import { helpers } from "@budibase/shared-core"
export const deepGet = helpers.deepGet
/**
* Generates a DOM safe UUID.
* Starting with a letter is important to make it DOM safe.
@ -41,30 +44,6 @@ export const hashString = string => {
return hash.toString()
}
/**
* Gets a key within an object. The key supports dot syntax for retrieving deep
* fields - e.g. "a.b.c".
* Exact matches of keys with dots in them take precedence over nested keys of
* the same path - e.g. getting "a.b" from { "a.b": "foo", a: { b: "bar" } }
* will return "foo" over "bar".
* @param obj the object
* @param key the key
* @return {*|null} the value or null if a value was not found for this key
*/
export const deepGet = (obj, key) => {
if (!obj || !key) {
return null
}
if (Object.prototype.hasOwnProperty.call(obj, key)) {
return obj[key]
}
const split = key.split(".")
for (let i = 0; i < split.length; i++) {
obj = obj?.[split[i]]
}
return obj
}
/**
* Sets a key within an object. The key supports dot syntax for retrieving deep
* fields - e.g. "a.b.c".

View File

@ -58,10 +58,11 @@
}
},
"dependencies": {
"@budibase/bbui": "^2.4.8",
"@budibase/client": "^2.4.8",
"@budibase/frontend-core": "^2.4.8",
"@budibase/string-templates": "^2.4.8",
"@budibase/bbui": "2.4.8",
"@budibase/client": "2.4.8",
"@budibase/frontend-core": "2.4.8",
"@budibase/shared-core": "2.4.8",
"@budibase/string-templates": "2.4.8",
"@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1",

View File

@ -19,9 +19,11 @@
"dev:builder": "rollup -cw"
},
"dependencies": {
"@budibase/bbui": "^2.4.8",
"@budibase/frontend-core": "^2.4.8",
"@budibase/string-templates": "^2.4.8",
"@budibase/bbui": "2.4.8",
"@budibase/frontend-core": "2.4.8",
"@budibase/shared-core": "2.4.8",
"@budibase/string-templates": "2.4.8",
"@budibase/types": "2.4.8",
"@spectrum-css/button": "^3.0.3",
"@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3",

View File

@ -20,6 +20,19 @@ const ignoredWarnings = [
"a11y-click-events-have-key-events",
]
const devPaths = production
? []
: [
{
find: "@budibase/shared-core",
replacement: path.resolve("../shared-core/dist/mjs/src/index"),
},
{
find: "@budibase/types",
replacement: path.resolve("../types/dist/mjs/index"),
},
]
export default {
input: "src/index.js",
output: [
@ -69,6 +82,7 @@ export default {
find: "sdk",
replacement: path.resolve("./src/sdk"),
},
...devPaths,
],
}),
svelte({

View File

@ -6,7 +6,8 @@
"license": "MPL-2.0",
"svelte": "src/index.js",
"dependencies": {
"@budibase/bbui": "^2.4.8",
"@budibase/bbui": "2.4.8",
"@budibase/shared-core": "2.4.8",
"lodash": "^4.17.21",
"svelte": "^3.46.2"
}

View File

@ -1,56 +1,7 @@
/**
* Operator options for lucene queries
*/
export const OperatorOptions = {
Equals: {
value: "equal",
label: "Equals",
},
NotEquals: {
value: "notEqual",
label: "Not equals",
},
Empty: {
value: "empty",
label: "Is empty",
},
NotEmpty: {
value: "notEmpty",
label: "Is not empty",
},
StartsWith: {
value: "string",
label: "Starts with",
},
Like: {
value: "fuzzy",
label: "Like",
},
MoreThan: {
value: "rangeLow",
label: "More than or equal to",
},
LessThan: {
value: "rangeHigh",
label: "Less than or equal to",
},
Contains: {
value: "contains",
label: "Contains",
},
NotContains: {
value: "notContains",
label: "Does not contain",
},
In: {
value: "oneOf",
label: "Is in",
},
ContainsAny: {
value: "containsAny",
label: "Has any",
},
}
export { OperatorOptions, SqlNumberTypeRangeMap } from "@budibase/shared-core"
// Cookie names
export const Cookies = {
@ -127,27 +78,6 @@ export const Roles = {
PUBLIC: "PUBLIC",
BUILDER: "BUILDER",
}
/**
* Maximum minimum range for SQL number values
*/
export const SqlNumberTypeRangeMap = {
integer: {
max: 2147483647,
min: -2147483648,
},
int: {
max: 2147483647,
min: -2147483648,
},
smallint: {
max: 32767,
min: -32768,
},
mediumint: {
max: 8388607,
min: -8388608,
},
}
export const Themes = [
{

View File

@ -1,13 +1,11 @@
import { writable, derived, get } from "svelte/store"
import { cloneDeep } from "lodash/fp"
import {
buildLuceneQuery,
luceneLimit,
runLuceneQuery,
luceneSort,
} from "../utils/lucene"
import { LuceneUtils } from "../utils"
import { convertJSONSchemaToTableSchema } from "../utils/json"
const { buildLuceneQuery, luceneLimit, runLuceneQuery, luceneSort } =
LuceneUtils
/**
* Parent class which handles the implementation of fetching data from an
* internal table or datasource plus.

View File

@ -1,4 +1,4 @@
export * as LuceneUtils from "./lucene"
export { dataFilters as LuceneUtils } from "@budibase/shared-core"
export * as JSONUtils from "./json"
export * as CookieUtils from "./cookies"
export * as RoleUtils from "./roles"

View File

@ -14,6 +14,7 @@ const baseConfig: Config.InitialProjectOptions = {
moduleNameMapper: {
"@budibase/backend-core/(.*)": "<rootDir>/../backend-core/$1",
"@budibase/backend-core": "<rootDir>/../backend-core/src",
"@budibase/shared-core": "<rootDir>/../shared-core/src",
"@budibase/types": "<rootDir>/../types/src",
},
}

View File

@ -43,11 +43,12 @@
"license": "GPL-3.0",
"dependencies": {
"@apidevtools/swagger-parser": "10.0.3",
"@budibase/backend-core": "^2.4.8",
"@budibase/client": "^2.4.8",
"@budibase/backend-core": "2.4.8",
"@budibase/client": "2.4.8",
"@budibase/pro": "2.4.8",
"@budibase/string-templates": "^2.4.8",
"@budibase/types": "^2.4.8",
"@budibase/shared-core": "2.4.8",
"@budibase/string-templates": "2.4.8",
"@budibase/types": "2.4.8",
"@bull-board/api": "3.7.0",
"@bull-board/koa": "3.9.4",
"@elastic/elasticsearch": "7.10.0",

View File

@ -11,6 +11,8 @@ import {
Row,
Table,
RelationshipTypes,
FieldType,
SortType,
} from "@budibase/types"
import {
breakRowIdField,
@ -749,8 +751,16 @@ export class ExternalRequest {
)
//if the sort column is a formula, remove it
for (let sortColumn of Object.keys(sort || {})) {
if (table.schema[sortColumn]?.type === "formula") {
delete sort?.[sortColumn]
if (!sort?.[sortColumn]) {
continue
}
switch (table.schema[sortColumn]?.type) {
case FieldType.FORMULA:
delete sort?.[sortColumn]
break
case FieldType.NUMBER:
sort[sortColumn].type = SortType.number
break
}
}
filters = buildFilters(id, filters || {}, table)

View File

@ -8,7 +8,6 @@ import {
breakRowIdField,
} from "../../../integrations/utils"
import { ExternalRequest, RunConfig } from "./ExternalRequest"
import { context } from "@budibase/backend-core"
import * as exporters from "../view/exporters"
import { apiFileReturn } from "../../../utilities/fileSystem"
import {
@ -19,6 +18,7 @@ import {
Table,
Datasource,
IncludeRelationship,
SortJson,
} from "@budibase/types"
import sdk from "../../../sdk"
@ -142,14 +142,14 @@ export async function search(ctx: BBContext) {
limit: limit,
}
}
let sort
let sort: SortJson | undefined
if (params.sort) {
const direction =
params.sortOrder === "descending"
? SortDirection.DESCENDING
: SortDirection.ASCENDING
sort = {
[params.sort]: direction,
[params.sort]: { direction },
}
}
try {

View File

@ -317,7 +317,8 @@ class InternalBuilder {
const table = json.meta?.table
if (sort) {
for (let [key, value] of Object.entries(sort)) {
const direction = value === SortDirection.ASCENDING ? "asc" : "desc"
const direction =
value.direction === SortDirection.ASCENDING ? "asc" : "desc"
query = query.orderBy(`${table?.name}.${key}`, direction)
}
} else if (this.client === SqlClient.MS_SQL && paginate?.limit) {

View File

@ -2,8 +2,11 @@ import {
DatasourceFieldType,
DatasourcePlus,
Integration,
PaginationJson,
QueryJson,
QueryType,
SearchFilters,
SortJson,
Table,
TableSchema,
} from "@budibase/types"
@ -13,6 +16,7 @@ import { DataSourceOperation, FieldTypes } from "../constants"
import { GoogleSpreadsheet } from "google-spreadsheet"
import fetch from "node-fetch"
import { configs, HTTPError } from "@budibase/backend-core"
import { dataFilters } from "@budibase/shared-core"
interface GoogleSheetsConfig {
spreadsheetId: string
@ -237,7 +241,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
const handlers = {
[DataSourceOperation.CREATE]: () =>
this.create({ sheet, row: json.body }),
[DataSourceOperation.READ]: () => this.read({ sheet }),
[DataSourceOperation.READ]: () => this.read({ ...json, sheet }),
[DataSourceOperation.UPDATE]: () =>
this.update({
// exclude the header row and zero index
@ -345,18 +349,40 @@ class GoogleSheetsIntegration implements DatasourcePlus {
}
}
async read(query: { sheet: string }) {
async read(query: {
sheet: string
filters?: SearchFilters
sort?: SortJson
paginate?: PaginationJson
}) {
try {
await this.connect()
const sheet = this.client.sheetsByTitle[query.sheet]
const rows = await sheet.getRows()
const filtered = dataFilters.runLuceneQuery(rows, query.filters)
const headerValues = sheet.headerValues
const response = []
for (let row of rows) {
let response = []
for (let row of filtered) {
response.push(
this.buildRowObject(headerValues, row._rawData, row._rowNumber)
)
}
if (query.sort) {
if (Object.keys(query.sort).length !== 1) {
console.warn("Googlesheets does not support multiple sorting", {
sortInfo: query.sort,
})
}
const [sortField, sortInfo] = Object.entries(query.sort)[0]
response = dataFilters.luceneSort(
response,
sortField,
sortInfo.direction,
sortInfo.type
)
}
return response
} catch (err) {
console.error("Error reading from google sheets", err)

View File

@ -9,6 +9,7 @@
"@budibase/types": ["../types/src"],
"@budibase/backend-core": ["../backend-core/src"],
"@budibase/backend-core/*": ["../backend-core/*"],
"@budibase/shared-core": ["../shared-core/src"],
"@budibase/pro": ["../../../budibase-pro/packages/pro/src"]
}
},
@ -19,15 +20,9 @@
"references": [
{ "path": "../types" },
{ "path": "../backend-core" },
{ "path": "../shared-core" },
{ "path": "../../../budibase-pro/packages/pro" }
],
"include": [
"src/**/*",
"specs",
"package.json"
],
"exclude": [
"node_modules",
"dist"
]
"include": ["src/**/*", "specs", "package.json"],
"exclude": ["node_modules", "dist"]
}

2
packages/shared-core/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules/
dist/

View File

@ -0,0 +1,30 @@
{
"name": "@budibase/shared-core",
"version": "2.4.8",
"description": "Shared data utils",
"main": "dist/cjs/src/index.js",
"types": "dist/mjs/src/index.d.ts",
"exports": {
".": {
"import": "./dist/mjs/src/index.js",
"require": "./dist/cjs/src/index.js"
},
"./package.json": "./dist/mjs/package.json"
},
"author": "Budibase",
"license": "GPL-3.0",
"scripts": {
"prebuild": "rimraf dist/",
"build": "tsc -p tsconfig.build.json && tsc -p tsconfig-cjs.build.json",
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
"dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\""
},
"dependencies": {
"@budibase/types": "2.4.5-alpha.0"
},
"devDependencies": {
"concurrently": "^7.6.0",
"typescript": "4.7.3",
"rimraf": "3.0.2"
}
}

View File

@ -0,0 +1,69 @@
export const OperatorOptions = {
Equals: {
value: "equal",
label: "Equals",
},
NotEquals: {
value: "notEqual",
label: "Not equals",
},
Empty: {
value: "empty",
label: "Is empty",
},
NotEmpty: {
value: "notEmpty",
label: "Is not empty",
},
StartsWith: {
value: "string",
label: "Starts with",
},
Like: {
value: "fuzzy",
label: "Like",
},
MoreThan: {
value: "rangeLow",
label: "More than or equal to",
},
LessThan: {
value: "rangeHigh",
label: "Less than or equal to",
},
Contains: {
value: "contains",
label: "Contains",
},
NotContains: {
value: "notContains",
label: "Does not contain",
},
In: {
value: "oneOf",
label: "Is in",
},
ContainsAny: {
value: "containsAny",
label: "Has any",
},
}
export const SqlNumberTypeRangeMap = {
integer: {
max: 2147483647,
min: -2147483648,
},
int: {
max: 2147483647,
min: -2147483648,
},
smallint: {
max: 32767,
min: -32768,
},
mediumint: {
max: 8388607,
min: -8388608,
},
}

View File

@ -1,5 +1,6 @@
import { Helpers } from "@budibase/bbui"
import { OperatorOptions, SqlNumberTypeRangeMap } from "../constants"
import { Datasource, FieldType, SortDirection, SortType } from "@budibase/types"
import { OperatorOptions, SqlNumberTypeRangeMap } from "./constants"
import { deepGet } from "./helpers"
const HBS_REGEX = /{{([^{].*?)}}/g
@ -7,7 +8,11 @@ const HBS_REGEX = /{{([^{].*?)}}/g
* Returns the valid operator options for a certain data type
* @param type the data type
*/
export const getValidOperatorsForType = (type, field, datasource) => {
export const getValidOperatorsForType = (
type: FieldType,
field: string,
datasource: Datasource & { tableId: any } // TODO: is this table id ever populated?
) => {
const Op = OperatorOptions
const stringOps = [
Op.Equals,
@ -27,7 +32,10 @@ export const getValidOperatorsForType = (type, field, datasource) => {
Op.NotEmpty,
Op.In,
]
let ops = []
let ops: {
value: string
label: string
}[] = []
if (type === "string") {
ops = stringOps
} else if (type === "number") {
@ -70,13 +78,13 @@ export const NoEmptyFilterStrings = [
OperatorOptions.NotEquals.value,
OperatorOptions.Contains.value,
OperatorOptions.NotContains.value,
]
] as (keyof QueryFields)[]
/**
* Removes any fields that contain empty strings that would cause inconsistent
* behaviour with how backend tables are filtered (no value means no filter).
*/
const cleanupQuery = query => {
const cleanupQuery = (query: Query) => {
if (!query) {
return query
}
@ -84,9 +92,10 @@ const cleanupQuery = query => {
if (!query[filterField]) {
continue
}
for (let [key, value] of Object.entries(query[filterField])) {
for (let [key, value] of Object.entries(query[filterField]!)) {
if (value == null || value === "") {
delete query[filterField][key]
delete query[filterField]![key]
}
}
}
@ -96,7 +105,7 @@ const cleanupQuery = query => {
/**
* Removes a numeric prefix on field names designed to give fields uniqueness
*/
const removeKeyNumbering = key => {
const removeKeyNumbering = (key: string) => {
if (typeof key === "string" && key.match(/\d[0-9]*:/g) != null) {
const parts = key.split(":")
parts.shift()
@ -106,12 +115,66 @@ const removeKeyNumbering = key => {
}
}
type Filter = {
operator: keyof Query
field: string
type: any
value: any
externalType: keyof typeof SqlNumberTypeRangeMap
}
type Query = QueryFields & QueryConfig
type QueryFields = {
string?: {
[key: string]: string
}
fuzzy?: {
[key: string]: string
}
range?: {
[key: string]: {
high: number | string
low: number | string
}
}
equal?: {
[key: string]: any
}
notEqual?: {
[key: string]: any
}
empty?: {
[key: string]: any
}
notEmpty?: {
[key: string]: any
}
oneOf?: {
[key: string]: any[]
}
contains?: {
[key: string]: any[]
}
notContains?: {
[key: string]: any[]
}
containsAny?: {
[key: string]: any[]
}
}
type QueryConfig = {
allOr?: boolean
}
type QueryFieldsType = keyof QueryFields
/**
* Builds a lucene JSON query from the filter structure generated in the builder
* @param filter the builder filter structure
*/
export const buildLuceneQuery = filter => {
let query = {
export const buildLuceneQuery = (filter: Filter[]) => {
let query: Query = {
string: {},
fuzzy: {},
range: {},
@ -128,7 +191,7 @@ export const buildLuceneQuery = filter => {
filter.forEach(expression => {
let { operator, field, type, value, externalType } = expression
const isHbs =
typeof value === "string" && value.match(HBS_REGEX)?.length > 0
typeof value === "string" && (value.match(HBS_REGEX) || []).length > 0
// Parse all values into correct types
if (operator === "allOr") {
query.allOr = true
@ -167,7 +230,7 @@ export const buildLuceneQuery = filter => {
) {
value = value.split(",")
}
if (operator.startsWith("range")) {
if (operator.startsWith("range") && query.range) {
const minint =
SqlNumberTypeRangeMap[externalType]?.min || Number.MIN_SAFE_INTEGER
const maxint =
@ -178,9 +241,13 @@ export const buildLuceneQuery = filter => {
high: type === "number" ? maxint : "9999-00-00T00:00:00.000Z",
}
}
if (operator === "rangeLow" && value != null && value !== "") {
if ((operator as any) === "rangeLow" && value != null && value !== "") {
query.range[field].low = value
} else if (operator === "rangeHigh" && value != null && value !== "") {
} else if (
(operator as any) === "rangeHigh" &&
value != null &&
value !== ""
) {
query.range[field].high = value
}
} else if (query[operator]) {
@ -189,14 +256,18 @@ export const buildLuceneQuery = filter => {
// "equals false" needs to be "not equals true"
// "not equals false" needs to be "equals true"
if (operator === "equal" && value === false) {
query.notEqual = query.notEqual || {}
query.notEqual[field] = true
} else if (operator === "notEqual" && value === false) {
query.equal = query.equal || {}
query.equal[field] = true
} else {
query[operator][field] = value
query[operator] = query[operator] || {}
query[operator]![field] = value
}
} else {
query[operator][field] = value
query[operator] = query[operator] || {}
query[operator]![field] = value
}
}
})
@ -209,7 +280,7 @@ export const buildLuceneQuery = filter => {
* @param docs the data
* @param query the JSON lucene query
*/
export const runLuceneQuery = (docs, query) => {
export const runLuceneQuery = (docs: any[], query?: Query) => {
if (!docs || !Array.isArray(docs)) {
return []
}
@ -221,87 +292,110 @@ export const runLuceneQuery = (docs, query) => {
query = cleanupQuery(query)
// Iterates over a set of filters and evaluates a fail function against a doc
const match = (type, failFn) => doc => {
const filters = Object.entries(query[type] || {})
for (let i = 0; i < filters.length; i++) {
const [key, testValue] = filters[i]
const docValue = Helpers.deepGet(doc, removeKeyNumbering(key))
if (failFn(docValue, testValue)) {
return false
const match =
(
type: QueryFieldsType,
failFn: (docValue: any, testValue: any) => boolean
) =>
(doc: any) => {
const filters = Object.entries(query![type] || {})
for (let i = 0; i < filters.length; i++) {
const [key, testValue] = filters[i]
const docValue = deepGet(doc, removeKeyNumbering(key))
if (failFn(docValue, testValue)) {
return false
}
}
return true
}
return true
}
// Process a string match (fails if the value does not start with the string)
const stringMatch = match("string", (docValue, testValue) => {
const stringMatch = match("string", (docValue: string, testValue: string) => {
return (
!docValue || !docValue?.toLowerCase().startsWith(testValue?.toLowerCase())
)
})
// Process a fuzzy match (treat the same as starts with when running locally)
const fuzzyMatch = match("fuzzy", (docValue, testValue) => {
const fuzzyMatch = match("fuzzy", (docValue: string, testValue: string) => {
return (
!docValue || !docValue?.toLowerCase().startsWith(testValue?.toLowerCase())
)
})
// Process a range match
const rangeMatch = match("range", (docValue, testValue) => {
return (
docValue == null ||
docValue === "" ||
docValue < testValue.low ||
docValue > testValue.high
)
})
const rangeMatch = match(
"range",
(
docValue: string | number | null,
testValue: { low: number; high: number }
) => {
return (
docValue == null ||
docValue === "" ||
docValue < testValue.low ||
docValue > testValue.high
)
}
)
// Process an equal match (fails if the value is different)
const equalMatch = match("equal", (docValue, testValue) => {
return testValue != null && testValue !== "" && docValue !== testValue
})
const equalMatch = match(
"equal",
(docValue: any, testValue: string | null) => {
return testValue != null && testValue !== "" && docValue !== testValue
}
)
// Process a not-equal match (fails if the value is the same)
const notEqualMatch = match("notEqual", (docValue, testValue) => {
return testValue != null && testValue !== "" && docValue === testValue
})
const notEqualMatch = match(
"notEqual",
(docValue: any, testValue: string | null) => {
return testValue != null && testValue !== "" && docValue === testValue
}
)
// Process an empty match (fails if the value is not empty)
const emptyMatch = match("empty", docValue => {
const emptyMatch = match("empty", (docValue: string | null) => {
return docValue != null && docValue !== ""
})
// Process a not-empty match (fails is the value is empty)
const notEmptyMatch = match("notEmpty", docValue => {
const notEmptyMatch = match("notEmpty", (docValue: string | null) => {
return docValue == null || docValue === ""
})
// Process an includes match (fails if the value is not included)
const oneOf = match("oneOf", (docValue, testValue) => {
const oneOf = match("oneOf", (docValue: any, testValue: any) => {
if (typeof testValue === "string") {
testValue = testValue.split(",")
if (typeof docValue === "number") {
testValue = testValue.map(item => parseFloat(item))
testValue = testValue.map((item: string) => parseFloat(item))
}
}
return !testValue?.includes(docValue)
})
const containsAny = match("containsAny", (docValue, testValue) => {
const containsAny = match("containsAny", (docValue: any, testValue: any) => {
return !docValue?.includes(...testValue)
})
const contains = match("contains", (docValue, testValue) => {
return !testValue?.every(item => docValue?.includes(item))
})
const contains = match(
"contains",
(docValue: string | any[], testValue: any[]) => {
return !testValue?.every((item: any) => docValue?.includes(item))
}
)
const notContains = match("notContains", (docValue, testValue) => {
return testValue?.every(item => docValue?.includes(item))
})
const notContains = match(
"notContains",
(docValue: string | any[], testValue: any[]) => {
return testValue?.every((item: any) => docValue?.includes(item))
}
)
// Match a document against all criteria
const docMatch = doc => {
const docMatch = (doc: any) => {
return (
stringMatch(doc) &&
fuzzyMatch(doc) &&
@ -329,20 +423,28 @@ export const runLuceneQuery = (docs, query) => {
* @param sortOrder the sort order ("ascending" or "descending")
* @param sortType the type of sort ("string" or "number")
*/
export const luceneSort = (docs, sort, sortOrder, sortType = "string") => {
export const luceneSort = (
docs: any[],
sort: string,
sortOrder: SortDirection,
sortType = SortType.STRING
) => {
if (!sort || !sortOrder || !sortType) {
return docs
}
const parse = sortType === "string" ? x => `${x}` : x => parseFloat(x)
return docs.slice().sort((a, b) => {
const colA = parse(a[sort])
const colB = parse(b[sort])
if (sortOrder === "Descending") {
return colA > colB ? -1 : 1
} else {
return colA > colB ? 1 : -1
}
})
const parse =
sortType === "string" ? (x: any) => `${x}` : (x: string) => parseFloat(x)
return docs
.slice()
.sort((a: { [x: string]: any }, b: { [x: string]: any }) => {
const colA = parse(a[sort])
const colB = parse(b[sort])
if (sortOrder.toLowerCase() === "descending") {
return colA > colB ? -1 : 1
} else {
return colA > colB ? 1 : -1
}
})
}
/**
@ -351,7 +453,7 @@ export const luceneSort = (docs, sort, sortOrder, sortType = "string") => {
* @param docs the data
* @param limit the number of docs to limit to
*/
export const luceneLimit = (docs, limit) => {
export const luceneLimit = (docs: any[], limit: string) => {
const numLimit = parseFloat(limit)
if (isNaN(numLimit)) {
return docs

View File

@ -0,0 +1,23 @@
/**
* Gets a key within an object. The key supports dot syntax for retrieving deep
* fields - e.g. "a.b.c".
* Exact matches of keys with dots in them take precedence over nested keys of
* the same path - e.g. getting "a.b" from { "a.b": "foo", a: { b: "bar" } }
* will return "foo" over "bar".
* @param obj the object
* @param key the key
* @return {*|null} the value or null if a value was not found for this key
*/
export const deepGet = (obj: { [x: string]: any }, key: string) => {
if (!obj || !key) {
return null
}
if (Object.prototype.hasOwnProperty.call(obj, key)) {
return obj[key]
}
const split = key.split(".")
for (let i = 0; i < split.length; i++) {
obj = obj?.[split[i]]
}
return obj
}

View File

@ -0,0 +1,4 @@
export * from "./constants"
export * as dataFilters from "./filters"
export * as helpers from "./helpers"
export * as utils from "./utils"

View File

@ -0,0 +1,6 @@
export function unreachable(
value: never,
message = `No such case in exhaustive switch: ${value}`
) {
throw new Error(message)
}

View File

@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "es6",
"moduleResolution": "node",
"lib": ["es2020"],
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"incremental": true,
"sourceMap": true,
"declaration": true,
"types": ["node"],
"outDir": "dist",
"skipLibCheck": true
},
"include": ["**/*.js", "**/*.ts", "package.json"],
"exclude": [
"node_modules",
"dist",
"**/*.spec.ts",
"**/*.spec.js",
"__mocks__"
]
}

View File

@ -0,0 +1,8 @@
{
"extends": "./tsconfig-base.build.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "dist/cjs",
"target": "es2015"
}
}

View File

@ -0,0 +1,8 @@
{
"extends": "./tsconfig-base.build.json",
"compilerOptions": {
"module": "esnext",
"outDir": "dist/mjs",
"target": "esnext"
}
}

View File

@ -0,0 +1,11 @@
{
"extends": "./tsconfig.build.json",
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"paths": {
"@budibase/types": ["../types/src"]
}
},
"references": [{ "path": "../types" }]
}

View File

@ -0,0 +1,277 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@budibase/types@2.4.5-alpha.0":
version "2.4.5-alpha.0"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.5-alpha.0.tgz#70fea09b5e471fe8fa6a760a1a2dd0dd74caac3a"
integrity sha512-tVFM9XnKwcCOo7nw6v7C8ZsK9hQLQBv3kHDn7/MFWnDMFCj72pUdtP/iFrAKr2c3tE84lkkWJfNHIolMSktHZA==
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
dependencies:
color-convert "^2.0.1"
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
chalk@^4.1.0:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
cliui@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.1"
wrap-ansi "^7.0.0"
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
concurrently@^7.6.0:
version "7.6.0"
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-7.6.0.tgz#531a6f5f30cf616f355a4afb8f8fcb2bba65a49a"
integrity sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw==
dependencies:
chalk "^4.1.0"
date-fns "^2.29.1"
lodash "^4.17.21"
rxjs "^7.0.0"
shell-quote "^1.7.3"
spawn-command "^0.0.2-1"
supports-color "^8.1.0"
tree-kill "^1.2.2"
yargs "^17.3.1"
date-fns@^2.29.1:
version "2.29.3"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
glob@^7.1.3:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.1.1"
once "^1.3.0"
path-is-absolute "^1.0.0"
has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
minimatch@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
dependencies:
wrappy "1"
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
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==
dependencies:
glob "^7.1.3"
rxjs@^7.0.0:
version "7.8.0"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4"
integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==
dependencies:
tslib "^2.1.0"
shell-quote@^1.7.3:
version "1.8.0"
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.0.tgz#20d078d0eaf71d54f43bd2ba14a1b5b9bfa5c8ba"
integrity sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==
spawn-command@^0.0.2-1:
version "0.0.2-1"
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0"
integrity sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
supports-color@^8.1.0:
version "8.1.1"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
dependencies:
has-flag "^4.0.0"
tree-kill@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
tslib@^2.1.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
typescript@4.7.3:
version "4.7.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d"
integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
yargs-parser@^21.1.1:
version "21.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
yargs@^17.3.1:
version "17.7.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967"
integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==
dependencies:
cliui "^8.0.1"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.3"
y18n "^5.0.5"
yargs-parser "^21.1.1"

View File

@ -2,14 +2,21 @@
"name": "@budibase/types",
"version": "2.4.8",
"description": "Budibase types",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"main": "dist/cjs/index.js",
"types": "dist/mjs/index.d.ts",
"exports": {
".": {
"import": "./dist/mjs/index.js",
"require": "./dist/cjs/index.js"
}
},
"author": "Budibase",
"license": "GPL-3.0",
"scripts": {
"prebuild": "rimraf dist/",
"build": "tsc -p tsconfig.build.json",
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput"
"build": "tsc -p tsconfig.build.json && tsc -p tsconfig-cjs.build.json",
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
"dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\""
},
"jest": {},
"devDependencies": {
@ -18,6 +25,7 @@
"@types/koa": "2.13.4",
"@types/node": "14.18.20",
"@types/pouchdb": "6.4.0",
"concurrently": "^7.6.0",
"koa-body": "4.2.0",
"rimraf": "3.0.2",
"typescript": "4.7.3"

View File

@ -1,5 +1,6 @@
import { Operation, SortDirection } from "./datasources"
import { Row, Table } from "../documents"
import { SortType } from "../api"
export interface SearchFilters {
allOr?: boolean
@ -42,7 +43,10 @@ export interface SearchFilters {
}
export interface SortJson {
[key: string]: SortDirection
[key: string]: {
direction: SortDirection
type?: SortType
}
}
export interface PaginationJson {

View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"lib": ["es2020"],
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"incremental": true,
"sourceMap": true,
"declaration": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.spec.js"]
}

View File

@ -0,0 +1,8 @@
{
"extends": "./tsconfig-base.build.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "dist/cjs",
"target": "es2015"
}
}

View File

@ -1,24 +1,8 @@
{
"extends": "./tsconfig-base.build.json",
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"lib": ["es2020"],
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"incremental": true,
"sourceMap": true,
"declaration": true,
"outDir": "dist"
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist",
"**/*.spec.ts",
"**/*.spec.js"
]
"moduleResolution": "node",
"outDir": "dist/mjs"
}
}

View File

@ -337,6 +337,18 @@ agent-base@^6.0.2:
dependencies:
debug "4"
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
dependencies:
color-convert "^2.0.1"
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@ -377,6 +389,23 @@ call-bind@^1.0.0:
function-bind "^1.1.1"
get-intrinsic "^1.0.2"
chalk@^4.1.0:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
cliui@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.1"
wrap-ansi "^7.0.0"
co-body@^5.1.1:
version "5.2.0"
resolved "https://registry.yarnpkg.com/co-body/-/co-body-5.2.0.tgz#5a0a658c46029131e0e3a306f67647302f71c124"
@ -387,6 +416,18 @@ co-body@^5.1.1:
raw-body "^2.2.0"
type-is "^1.6.14"
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
@ -399,6 +440,26 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
concurrently@^7.6.0:
version "7.6.0"
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-7.6.0.tgz#531a6f5f30cf616f355a4afb8f8fcb2bba65a49a"
integrity sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw==
dependencies:
chalk "^4.1.0"
date-fns "^2.29.1"
lodash "^4.17.21"
rxjs "^7.0.0"
shell-quote "^1.7.3"
spawn-command "^0.0.2-1"
supports-color "^8.1.0"
tree-kill "^1.2.2"
yargs "^17.3.1"
date-fns@^2.29.1:
version "2.29.3"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
debug@4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
@ -416,6 +477,16 @@ depd@2.0.0:
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
follow-redirects@^1.15.0:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
@ -445,6 +516,11 @@ function-bind@^1.1.1:
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-intrinsic@^1.0.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385"
@ -466,6 +542,11 @@ glob@^7.1.3:
once "^1.3.0"
path-is-absolute "^1.0.0"
has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
has-symbols@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
@ -521,6 +602,11 @@ inherits@2, inherits@2.0.4:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
json5@*:
version "2.2.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
@ -535,6 +621,11 @@ koa-body@4.2.0:
co-body "^5.1.1"
formidable "^1.1.1"
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@ -623,6 +714,11 @@ raw-body@^2.2.0:
iconv-lite "0.4.24"
unpipe "1.0.0"
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
@ -635,6 +731,13 @@ rimraf@3.0.2:
dependencies:
glob "^7.1.3"
rxjs@^7.0.0:
version "7.8.0"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4"
integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==
dependencies:
tslib "^2.1.0"
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
@ -645,6 +748,11 @@ setprototypeof@1.2.0:
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
shell-quote@^1.7.3:
version "1.8.0"
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.0.tgz#20d078d0eaf71d54f43bd2ba14a1b5b9bfa5c8ba"
integrity sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==
side-channel@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
@ -654,11 +762,46 @@ side-channel@^1.0.4:
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
spawn-command@^0.0.2-1:
version "0.0.2-1"
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0"
integrity sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
supports-color@^8.1.0:
version "8.1.1"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
dependencies:
has-flag "^4.0.0"
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
@ -674,6 +817,16 @@ tough-cookie@^4.1.2:
universalify "^0.2.0"
url-parse "^1.5.3"
tree-kill@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
tslib@^2.1.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
type-is@^1.6.14:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
@ -705,7 +858,39 @@ url-parse@^1.5.3:
querystringify "^2.1.1"
requires-port "^1.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
yargs-parser@^21.1.1:
version "21.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
yargs@^17.3.1:
version "17.7.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967"
integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==
dependencies:
cliui "^8.0.1"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.3"
y18n "^5.0.5"
yargs-parser "^21.1.1"

View File

@ -28,6 +28,12 @@ yarn unlink
yarn link
cd -
echo "Linking shared-core"
cd packages/shared-core
yarn unlink
yarn link
cd -
if [ -d "../budibase-pro" ]; then
cd ../budibase-pro
echo "Bootstrapping budibase-pro"