From 095eb0fe3a1cf4e93d2ac8edbb6e98e75da81286 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 14 Jan 2025 18:27:05 +0000 Subject: [PATCH 01/32] First iteration on introducing Typescript to the binding panel experience. --- packages/builder/package.json | 2 +- .../common/CodeEditor/{index.js => index.ts} | 204 ++++++++++------ .../common/bindings/BindingPanel.svelte | 86 ++++--- .../bindings/EvaluationSidePanel.svelte | 4 +- packages/frontend-core/src/utils/index.ts | 1 + .../frontend-core/src/utils/jsonFormatter.ts | 74 ++++++ packages/types/src/ui/bindings/binding.ts | 17 ++ packages/types/src/ui/bindings/helper.ts | 4 + packages/types/src/ui/bindings/index.ts | 2 + packages/types/src/ui/components/index.ts | 1 + packages/types/src/ui/components/sidepanel.ts | 5 + packages/types/src/ui/index.ts | 2 + yarn.lock | 230 +++++++++++++----- 13 files changed, 463 insertions(+), 169 deletions(-) rename packages/builder/src/components/common/CodeEditor/{index.js => index.ts} (59%) create mode 100644 packages/frontend-core/src/utils/jsonFormatter.ts create mode 100644 packages/types/src/ui/bindings/binding.ts create mode 100644 packages/types/src/ui/bindings/helper.ts create mode 100644 packages/types/src/ui/bindings/index.ts create mode 100644 packages/types/src/ui/components/index.ts create mode 100644 packages/types/src/ui/components/sidepanel.ts diff --git a/packages/builder/package.json b/packages/builder/package.json index 71d1c32008..6fcf72c5fb 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -74,7 +74,6 @@ "dayjs": "^1.10.8", "downloadjs": "1.4.7", "fast-json-patch": "^3.1.1", - "json-format-highlight": "^1.0.4", "lodash": "4.17.21", "posthog-js": "^1.118.0", "remixicon": "2.5.0", @@ -94,6 +93,7 @@ "@sveltejs/vite-plugin-svelte": "1.4.0", "@testing-library/jest-dom": "6.4.2", "@testing-library/svelte": "^4.1.0", + "@types/sanitize-html": "^2.13.0", "@types/shortid": "^2.2.0", "babel-jest": "^29.6.2", "identity-obj-proxy": "^3.0.0", diff --git a/packages/builder/src/components/common/CodeEditor/index.js b/packages/builder/src/components/common/CodeEditor/index.ts similarity index 59% rename from packages/builder/src/components/common/CodeEditor/index.js rename to packages/builder/src/components/common/CodeEditor/index.ts index a04a140ad6..0063cfc789 100644 --- a/packages/builder/src/components/common/CodeEditor/index.js +++ b/packages/builder/src/components/common/CodeEditor/index.ts @@ -1,6 +1,13 @@ import { getManifest } from "@budibase/string-templates" import sanitizeHtml from "sanitize-html" import { groupBy } from "lodash" +import { + BindingCompletion, + BindingMode, + Helper, + Snippet, +} from "@budibase/types" +import { CompletionContext } from "@codemirror/autocomplete" export const EditorModes = { JS: { @@ -26,7 +33,7 @@ export const SECTIONS = { }, } -export const buildHelperInfoNode = (completion, helper) => { +export const buildHelperInfoNode = (completion: any, helper: Helper) => { const ele = document.createElement("div") ele.classList.add("info-bubble") @@ -46,7 +53,7 @@ export const buildHelperInfoNode = (completion, helper) => { return ele } -const toSpectrumIcon = name => { +const toSpectrumIcon = (name: string) => { return ` { ` } -export const buildSectionHeader = (type, sectionName, icon, rank) => { +export const buildSectionHeader = ( + type: string, + sectionName: string, + icon: string, + rank: number +) => { const ele = document.createElement("div") ele.classList.add("info-section") if (type) { @@ -72,43 +84,52 @@ export const buildSectionHeader = (type, sectionName, icon, rank) => { } } -export const helpersToCompletion = (helpers, mode) => { +export const helpersToCompletion = ( + helpers: Record, + mode: { name: "javascript" | "handlebars" } +) => { const { type, name: sectionName, icon } = SECTIONS.HB_HELPER const helperSection = buildSectionHeader(type, sectionName, icon, 99) - return Object.keys(helpers).reduce((acc, key) => { - let helper = helpers[key] - acc.push({ - label: key, - info: completion => { + return Object.keys(helpers).flatMap(helperName => { + let helper = helpers[helperName] + return { + label: helperName, + info: (completion: BindingCompletion) => { return buildHelperInfoNode(completion, helper) }, type: "helper", section: helperSection, detail: "Function", - apply: (view, completion, from, to) => { - insertBinding(view, from, to, key, mode) + apply: ( + view: any, + completion: BindingCompletion, + from: number, + to: number + ) => { + insertBinding(view, from, to, helperName, mode) }, - }) - return acc - }, []) + } + }) } -export const getHelperCompletions = mode => { - const manifest = getManifest() - return Object.keys(manifest).reduce((acc, key) => { - acc = acc || [] - return [...acc, ...helpersToCompletion(manifest[key], mode)] - }, []) +export const getHelperCompletions = (mode: { + name: "javascript" | "handlebars" +}) => { + // TODO: manifest needs to be properly typed + const manifest: any = getManifest() + return Object.keys(manifest).flatMap(key => { + return helpersToCompletion(manifest[key], mode) + }) } -export const snippetAutoComplete = snippets => { - return function myCompletions(context) { +export const snippetAutoComplete = (snippets: Snippet[]) => { + return function myCompletions(context: CompletionContext) { if (!snippets?.length) { return null } const word = context.matchBefore(/\w*/) - if (word.from == word.to && !context.explicit) { + if (!word || (word.from == word.to && !context.explicit)) { return null } return { @@ -117,7 +138,12 @@ export const snippetAutoComplete = snippets => { label: `snippets.${snippet.name}`, type: "text", simple: true, - apply: (view, completion, from, to) => { + apply: ( + view: any, + completion: BindingCompletion, + from: number, + to: number + ) => { insertSnippet(view, from, to, completion.label) }, })), @@ -125,7 +151,7 @@ export const snippetAutoComplete = snippets => { } } -const bindingFilter = (options, query) => { +const bindingFilter = (options: BindingCompletion[], query: string) => { return options.filter(completion => { const section_parsed = completion.section.name.toLowerCase() const label_parsed = completion.label.toLowerCase() @@ -138,8 +164,8 @@ const bindingFilter = (options, query) => { }) } -export const hbAutocomplete = baseCompletions => { - async function coreCompletion(context) { +export const hbAutocomplete = (baseCompletions: BindingCompletion[]) => { + async function coreCompletion(context: CompletionContext) { let bindingStart = context.matchBefore(EditorModes.Handlebars.match) let options = baseCompletions || [] @@ -149,6 +175,9 @@ export const hbAutocomplete = baseCompletions => { } // Accommodate spaces const match = bindingStart.text.match(/{{[\s]*/) + if (!match) { + return null + } const query = bindingStart.text.replace(match[0], "") let filtered = bindingFilter(options, query) @@ -162,14 +191,17 @@ export const hbAutocomplete = baseCompletions => { return coreCompletion } -export const jsAutocomplete = baseCompletions => { - async function coreCompletion(context) { +export const jsAutocomplete = (baseCompletions: BindingCompletion[]) => { + async function coreCompletion(context: CompletionContext) { let jsBinding = context.matchBefore(/\$\("[\s\w]*/) let options = baseCompletions || [] if (jsBinding) { // Accommodate spaces const match = jsBinding.text.match(/\$\("[\s]*/) + if (!match) { + return null + } const query = jsBinding.text.replace(match[0], "") let filtered = bindingFilter(options, query) return { @@ -185,7 +217,10 @@ export const jsAutocomplete = baseCompletions => { return coreCompletion } -export const buildBindingInfoNode = (completion, binding) => { +export const buildBindingInfoNode = ( + completion: BindingCompletion, + binding: any +) => { if (!binding.valueHTML || binding.value == null) { return null } @@ -196,7 +231,12 @@ export const buildBindingInfoNode = (completion, binding) => { } // Readdress these methods. They shouldn't be used -export const hbInsert = (value, from, to, text) => { +export const hbInsert = ( + value: string, + from: number, + to: number, + text: string +) => { let parsedInsert = "" const left = from ? value.substring(0, from) : "" @@ -212,11 +252,14 @@ export const hbInsert = (value, from, to, text) => { } export function jsInsert( - value, - from, - to, - text, - { helper, disableWrapping } = {} + value: string, + from: number, + to: number, + text: string, + { + helper, + disableWrapping, + }: { helper?: boolean; disableWrapping?: boolean } = {} ) { let parsedInsert = "" @@ -236,7 +279,13 @@ export function jsInsert( } // Autocomplete apply behaviour -export const insertBinding = (view, from, to, text, mode) => { +export const insertBinding = ( + view: any, + from: number, + to: number, + text: string, + mode: { name: "javascript" | "handlebars" } +) => { let parsedInsert if (mode.name == "javascript") { @@ -270,7 +319,12 @@ export const insertBinding = (view, from, to, text, mode) => { }) } -export const insertSnippet = (view, from, to, text) => { +export const insertSnippet = ( + view: any, + from: number, + to: number, + text: string +) => { let cursorPos = from + text.length view.dispatch({ changes: { @@ -284,9 +338,13 @@ export const insertSnippet = (view, from, to, text) => { }) } -export const bindingsToCompletions = (bindings, mode) => { +// TODO: typing in this function isn't great +export const bindingsToCompletions = ( + bindings: any, + mode: { name: "javascript" | "handlebars" } +) => { const bindingByCategory = groupBy(bindings, "category") - const categoryMeta = bindings?.reduce((acc, ele) => { + const categoryMeta = bindings?.reduce((acc: any, ele: any) => { acc[ele.category] = acc[ele.category] || {} if (ele.icon) { @@ -298,36 +356,46 @@ export const bindingsToCompletions = (bindings, mode) => { return acc }, {}) - const completions = Object.keys(bindingByCategory).reduce((comps, catKey) => { - const { icon, rank } = categoryMeta[catKey] || {} + const completions = Object.keys(bindingByCategory).reduce( + (comps: any, catKey: string) => { + const { icon, rank } = categoryMeta[catKey] || {} - const bindindSectionHeader = buildSectionHeader( - bindingByCategory.type, - catKey, - icon || "", - typeof rank == "number" ? rank : 1 - ) + const bindingSectionHeader = buildSectionHeader( + // @ts-ignore something wrong with this - logically this should be dictionary + bindingByCategory.type, + catKey, + icon || "", + typeof rank == "number" ? rank : 1 + ) - return [ - ...comps, - ...bindingByCategory[catKey].reduce((acc, binding) => { - let displayType = binding.fieldSchema?.type || binding.display?.type - acc.push({ - label: binding.display?.name || binding.readableBinding || "NO NAME", - info: completion => { - return buildBindingInfoNode(completion, binding) - }, - type: "binding", - detail: displayType, - section: bindindSectionHeader, - apply: (view, completion, from, to) => { - insertBinding(view, from, to, binding.readableBinding, mode) - }, - }) - return acc - }, []), - ] - }, []) + return [ + ...comps, + ...bindingByCategory[catKey].reduce((acc, binding) => { + let displayType = binding.fieldSchema?.type || binding.display?.type + acc.push({ + label: + binding.display?.name || binding.readableBinding || "NO NAME", + info: (completion: BindingCompletion) => { + return buildBindingInfoNode(completion, binding) + }, + type: "binding", + detail: displayType, + section: bindingSectionHeader, + apply: ( + view: any, + completion: BindingCompletion, + from: number, + to: number + ) => { + insertBinding(view, from, to, binding.readableBinding, mode) + }, + }) + return acc + }, []), + ] + }, + [] + ) return completions } diff --git a/packages/builder/src/components/common/bindings/BindingPanel.svelte b/packages/builder/src/components/common/bindings/BindingPanel.svelte index 4d1de22a57..29ddb6a1d0 100644 --- a/packages/builder/src/components/common/bindings/BindingPanel.svelte +++ b/packages/builder/src/components/common/bindings/BindingPanel.svelte @@ -1,4 +1,4 @@ - diff --git a/packages/builder/src/components/common/CodeEditor/index.ts b/packages/builder/src/components/common/CodeEditor/index.ts index 0063cfc789..d2cbe8a23e 100644 --- a/packages/builder/src/components/common/CodeEditor/index.ts +++ b/packages/builder/src/components/common/CodeEditor/index.ts @@ -4,12 +4,13 @@ import { groupBy } from "lodash" import { BindingCompletion, BindingMode, + EditorModesMap, Helper, Snippet, } from "@budibase/types" import { CompletionContext } from "@codemirror/autocomplete" -export const EditorModes = { +export const EditorModes: EditorModesMap = { JS: { name: "javascript", json: false, diff --git a/packages/builder/src/components/common/bindings/BindingPanel.svelte b/packages/builder/src/components/common/bindings/BindingPanel.svelte index 29ddb6a1d0..6b9119f433 100644 --- a/packages/builder/src/components/common/bindings/BindingPanel.svelte +++ b/packages/builder/src/components/common/bindings/BindingPanel.svelte @@ -37,7 +37,9 @@ SidePanel, BindingCompletion, Snippet, + Helper, } from "@budibase/types" + import { CompletionContext } from "@codemirror/autocomplete" const dispatch = createEventDispatcher() @@ -53,8 +55,8 @@ export let placeholder = null export let showTabBar = true - let mode: BindingMode - let sidePanel: SidePanel + let mode: BindingMode | null + let sidePanel: SidePanel | null let initialValueJS = value?.startsWith?.("{{ js ") let jsValue = initialValueJS ? value : null let hbsValue = initialValueJS ? null : value @@ -109,19 +111,19 @@ snippets: Snippet[] | null, useSnippets?: boolean ) => { - const completions = [ + const completions: ((_: CompletionContext) => any)[] = [ jsAutocomplete([ ...bindingCompletions, ...getHelperCompletions(EditorModes.JS), ]), ] - if (useSnippets) { + if (useSnippets && snippets) { completions.push(snippetAutoComplete(snippets)) } return completions } - const getModeOptions = (allowHBS, allowJS) => { + const getModeOptions = (allowHBS: boolean, allowJS: boolean) => { let options = [] if (allowHBS) { options.push(BindingMode.Text) @@ -132,7 +134,12 @@ return options } - const getSidePanelOptions = (bindings, context, useSnippets, mode) => { + const getSidePanelOptions = ( + bindings: EnrichedBinding[], + context: any, + useSnippets: boolean, + mode: BindingMode | null + ) => { let options = [] if (bindings?.length) { options.push(SidePanel.Bindings) @@ -146,32 +153,39 @@ return options } - const debouncedEval = Utils.debounce((expression, context, snippets) => { - try { - expressionError = null - expressionResult = processStringSync( - expression || "", - { - ...context, - snippets, - }, - { - noThrow: false, - } - ) - } catch (err) { - expressionResult = null - expressionError = err - } - evaluating = false - }, 260) + const debouncedEval = Utils.debounce( + (expression: string | null, context: any, snippets: Snippet[]) => { + try { + expressionError = null + expressionResult = processStringSync( + expression || "", + { + ...context, + snippets, + }, + { + noThrow: false, + } + ) + } catch (err: any) { + expressionResult = null + expressionError = err + } + evaluating = false + }, + 260 + ) - const requestEval = (expression, context, snippets) => { + const requestEval = ( + expression: string | null, + context: any, + snippets: Snippet[] | null + ) => { evaluating = true debouncedEval(expression, context, snippets) } - const highlightJSON = json => { + const highlightJSON = (json: object | string) => { return JsonFormatter.format(json, { keyColor: "#e06c75", numberColor: "#e5c07b", @@ -182,7 +196,11 @@ }) } - const enrichBindings = (bindings, context, snippets) => { + const enrichBindings = ( + bindings: EnrichedBinding[], + context: any, + snippets: Snippet[] | null + ) => { // Create a single big array to enrich in one go const bindingStrings = bindings.map(binding => { if (binding.runtimeBinding.startsWith('trim "')) { @@ -193,17 +211,18 @@ return `{{ literal ${binding.runtimeBinding} }}` } }) - const bindingEvauations = processObjectSync(bindingStrings, { + const bindingEvaluations = processObjectSync(bindingStrings, { ...context, snippets, }) // Enrich bindings with evaluations and highlighted HTML return bindings.map((binding, idx) => { - if (!context) { + if (!context || typeof bindingEvaluations !== "object") { return binding } - const value = JSON.stringify(bindingEvauations[idx], null, 2) + const evalObj: Record = bindingEvaluations + const value = JSON.stringify(evalObj[idx], null, 2) return { ...binding, value, @@ -212,29 +231,38 @@ }) } - const updateValue = val => { + const updateValue = (val: any) => { const runtimeExpression = readableToRuntimeBinding(enrichedBindings, val) dispatch("change", val) requestEval(runtimeExpression, context, snippets) } - const onSelectHelper = (helper, js) => { - bindingHelpers.onSelectHelper(js ? jsValue : hbsValue, helper, { js }) + const onSelectHelper = (helper: Helper, js?: boolean) => { + bindingHelpers.onSelectHelper(js ? jsValue : hbsValue, helper, { + js, + dontDecode: undefined, + }) } - const onSelectBinding = (binding, { forceJS } = {}) => { + const onSelectBinding = ( + binding: EnrichedBinding, + { forceJS }: { forceJS?: boolean } = {} + ) => { const js = usingJS || forceJS - bindingHelpers.onSelectBinding(js ? jsValue : hbsValue, binding, { js }) + bindingHelpers.onSelectBinding(js ? jsValue : hbsValue, binding, { + js, + dontDecode: undefined, + }) } - const changeMode = newMode => { + const changeMode = (newMode: BindingMode) => { if (targetMode || newMode === mode) { return } // Get the raw editor value to see if we are abandoning changes let rawValue = editorValue - if (mode === BindingMode.JavaScript) { + if (mode === BindingMode.JavaScript && rawValue) { rawValue = decodeJSBinding(rawValue) } @@ -253,16 +281,16 @@ targetMode = null } - const changeSidePanel = newSidePanel => { + const changeSidePanel = (newSidePanel: SidePanel) => { sidePanel = newSidePanel === sidePanel ? null : newSidePanel } - const onChangeHBSValue = e => { + const onChangeHBSValue = (e: { detail: string }) => { hbsValue = e.detail updateValue(hbsValue) } - const onChangeJSValue = e => { + const onChangeJSValue = (e: { detail: string }) => { jsValue = encodeJSBinding(e.detail) if (!e.detail?.trim()) { // Don't bother saving empty values as JS diff --git a/packages/frontend-core/src/utils/utils.js b/packages/frontend-core/src/utils/utils.js index 55603b0129..f0635fbeac 100644 --- a/packages/frontend-core/src/utils/utils.js +++ b/packages/frontend-core/src/utils/utils.js @@ -43,7 +43,7 @@ export const sequential = fn => { * invocations is enforced. * @param callback an async function to run * @param minDelay the minimum delay between invocations - * @returns {Promise} a debounced version of the callback + * @returns a debounced version of the callback */ export const debounce = (callback, minDelay = 1000) => { let timeout diff --git a/packages/types/src/ui/components/codeEditor.ts b/packages/types/src/ui/components/codeEditor.ts new file mode 100644 index 0000000000..8ea885b667 --- /dev/null +++ b/packages/types/src/ui/components/codeEditor.ts @@ -0,0 +1,19 @@ +type EditorMode = + | { + key: "JS" + name: "javascript" + json: boolean + match: RegExp + } + | { + key: "Handlebars" + name: "handlebars" + base: "text/html" + match: RegExp + } + | { + key: "Text" + name: "text/html" + } + +export type EditorModesMap = { [M in EditorMode as M["key"]]: Omit } diff --git a/packages/types/src/ui/components/index.ts b/packages/types/src/ui/components/index.ts index 6638d36b32..8dc1638f8c 100644 --- a/packages/types/src/ui/components/index.ts +++ b/packages/types/src/ui/components/index.ts @@ -1 +1,2 @@ export * from "./sidepanel" +export * from "./codeEditor" From 0b909b434a51f9b2336c0bdb20e1d2dd29683271 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 15 Jan 2025 14:34:36 +0000 Subject: [PATCH 03/32] Work on binding drawer typing. --- packages/bbui/src/Label/Label.svelte | 2 +- .../common/CodeEditor/CodeEditor.svelte | 67 ++++++++++++------- .../common/bindings/BindingPanel.svelte | 39 ++++++----- packages/types/src/ui/bindings/binding.ts | 9 +++ .../types/src/ui/components/codeEditor.ts | 43 +++++++----- 5 files changed, 95 insertions(+), 65 deletions(-) diff --git a/packages/bbui/src/Label/Label.svelte b/packages/bbui/src/Label/Label.svelte index 71b0967d99..41e1ccf794 100644 --- a/packages/bbui/src/Label/Label.svelte +++ b/packages/bbui/src/Label/Label.svelte @@ -4,7 +4,7 @@ export let size = "M" export let tooltip = "" - export let muted + export let muted = undefined diff --git a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte index f94d26603d..1fa2eb3fae 100644 --- a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte +++ b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte @@ -1,4 +1,4 @@ - From ef3ac8883e7725df4bbb861a6e4b41e721af54ad Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 15 Jan 2025 15:18:11 +0000 Subject: [PATCH 05/32] Small TS improvement - make sure result/error is typed correctly. --- .../src/components/common/bindings/BindingPanel.svelte | 10 +++++----- .../common/bindings/EvaluationSidePanel.svelte | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/builder/src/components/common/bindings/BindingPanel.svelte b/packages/builder/src/components/common/bindings/BindingPanel.svelte index 04dc92f920..7c863f4beb 100644 --- a/packages/builder/src/components/common/bindings/BindingPanel.svelte +++ b/packages/builder/src/components/common/bindings/BindingPanel.svelte @@ -31,7 +31,7 @@ import { capitalise } from "@/helpers" import { Utils, JsonFormatter } from "@budibase/frontend-core" import { licensing } from "@/stores/portal" - import { BindingMode, EditorMode, SidePanel } from "@budibase/types" + import { BindingMode, SidePanel } from "@budibase/types" import type { EnrichedBinding, BindingCompletion, @@ -64,8 +64,8 @@ let getCaretPosition: CaretPositionFn | undefined let insertAtPos: InsertAtPositionFn | undefined let targetMode: BindingMode | null = null - let expressionResult: string | undefined | null - let expressionError: string | undefined | null + let expressionResult: string | undefined + let expressionError: string | undefined let evaluating = false $: useSnippets = allowSnippets && !$licensing.isFreePlan @@ -155,7 +155,7 @@ const debouncedEval = Utils.debounce( (expression: string | null, context: any, snippets: Snippet[]) => { try { - expressionError = null + expressionError = undefined expressionResult = processStringSync( expression || "", { @@ -167,7 +167,7 @@ } ) } catch (err: any) { - expressionResult = null + expressionResult = undefined expressionError = err } evaluating = false diff --git a/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte b/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte index 10b3f85176..91d986f0f6 100644 --- a/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte +++ b/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte @@ -5,8 +5,8 @@ import { fade } from "svelte/transition" import { UserScriptError } from "@budibase/string-templates" - export let expressionResult - export let expressionError + export let expressionResult: string | undefined = undefined + export let expressionError: string | undefined = undefined export let evaluating = false export let expression: string | null = null From fa3c5f1466328aea62f88e644058e81d25d05cd0 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 15 Jan 2025 15:25:55 +0000 Subject: [PATCH 06/32] copyToClipboard typing improvement. --- packages/bbui/src/helpers.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bbui/src/helpers.d.ts b/packages/bbui/src/helpers.d.ts index bffb75d07f..d4e2a45186 100644 --- a/packages/bbui/src/helpers.d.ts +++ b/packages/bbui/src/helpers.d.ts @@ -1,4 +1,4 @@ declare module "./helpers" { export const cloneDeep: (obj: T) => T - export const copyToClipboard: (value: any) => any + export const copyToClipboard: (value: any) => Promise } From 13915e848894bdc8e1173ad254a18abc92c6fe36 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 15 Jan 2025 15:34:17 +0000 Subject: [PATCH 07/32] Update yarn.lock --- yarn.lock | 187 +++++++++++++++--------------------------------------- 1 file changed, 50 insertions(+), 137 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8996e11ae1..453dc45128 100644 --- a/yarn.lock +++ b/yarn.lock @@ -810,7 +810,7 @@ "@azure/abort-controller" "^2.0.0" tslib "^2.6.2" -"@azure/identity@^4.2.1": +"@azure/identity@4.2.1", "@azure/identity@^4.2.1": version "4.2.1" resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-4.2.1.tgz#22b366201e989b7b41c0e1690e103bd579c31e4c" integrity sha512-U8hsyC9YPcEIzoaObJlRDvp7KiF0MGS7xcWbyJSVvXRkC/HXo1f0oYeBYmEvVgRfacw7GHf6D6yAoh9JHz6A5Q== @@ -2130,6 +2130,28 @@ pouchdb-promise "^6.0.4" through2 "^2.0.0" +"@budibase/pro@npm:@budibase/pro@latest": + version "3.2.44" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.2.44.tgz#90367bb2167aafd8c809e000a57d349e5dc4bb78" + integrity sha512-Zv2PBVUZUS6/psOpIRIDlW3jrOHWWPhpQXzCk00kIQJaqjkdcvuTXSedQ70u537sQmLu8JsSWbui9MdfF8ksVw== + dependencies: + "@anthropic-ai/sdk" "^0.27.3" + "@budibase/backend-core" "*" + "@budibase/shared-core" "*" + "@budibase/string-templates" "*" + "@budibase/types" "*" + "@koa/router" "13.1.0" + bull "4.10.1" + dd-trace "5.26.0" + joi "17.6.0" + jsonwebtoken "9.0.2" + lru-cache "^7.14.1" + memorystream "^0.3.1" + node-fetch "2.6.7" + openai "4.59.0" + scim-patch "^0.8.1" + scim2-parse-filter "^0.2.8" + "@bull-board/api@5.10.2": version "5.10.2" resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-5.10.2.tgz#ae8ff6918b23897bf879a6ead3683f964374c4b3" @@ -7020,23 +7042,7 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== -axios@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.3.tgz#8274250dada2edf53814ed7db644b9c2866c1e35" - integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA== - dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - -axios@^0.21.1: - version "0.21.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== - dependencies: - follow-redirects "^1.14.0" - -axios@^1.0.0, axios@^1.1.3, axios@^1.4.0, axios@^1.6.2, axios@^1.6.8: +axios@1.1.3, axios@1.7.7, axios@^0.21.1, axios@^1.0.0, axios@^1.1.3, axios@^1.4.0, axios@^1.6.2, axios@^1.6.8: version "1.7.7" resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== @@ -10654,7 +10660,7 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -follow-redirects@^1.14.0, follow-redirects@^1.15.0, follow-redirects@^1.15.6: +follow-redirects@^1.15.6: version "1.15.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== @@ -11269,22 +11275,10 @@ global@~4.4.0: min-document "^2.19.0" process "^0.11.10" -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^13.19.0: - version "13.24.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" - integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== - dependencies: - type-fest "^0.20.2" - -globals@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" - integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== +globals@15.13.0, globals@^11.1.0, globals@^13.19.0, globals@^14.0.0: + version "15.13.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.13.0.tgz#bbec719d69aafef188ecd67954aae76a696010fc" + integrity sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g== globalthis@^1.0.1, globalthis@^1.0.4: version "1.0.4" @@ -11694,12 +11688,7 @@ http-assert@^1.3.0: deep-equal "~1.0.1" http-errors "~1.8.0" -http-cache-semantics@3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" - integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== - -http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: +http-cache-semantics@3.8.1, http-cache-semantics@4.1.1, http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== @@ -12150,11 +12139,6 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - is-buffer@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" @@ -12594,6 +12578,11 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +isobject@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" + integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== + isolated-vm@^4.7.2: version "4.7.2" resolved "https://registry.yarnpkg.com/isolated-vm/-/isolated-vm-4.7.2.tgz#5670d5cce1d92004f9b825bec5b0b11fc7501b65" @@ -13473,14 +13462,7 @@ kill-port@^1.6.1: get-them-args "1.3.2" shell-exec "1.0.2" -kind-of@^3.0.2, kind-of@^3.1.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== - dependencies: - is-buffer "^1.1.5" - -kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: +kind-of@6.0.3, kind-of@^3.0.2, kind-of@^3.1.0, kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -14885,7 +14867,7 @@ msgpackr-extract@^3.0.2: "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" -msgpackr@^1.5.2: +msgpackr@1.10.1, msgpackr@^1.5.2: version "1.10.1" resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.10.1.tgz#51953bb4ce4f3494f0c4af3f484f01cfbb306555" integrity sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ== @@ -15078,27 +15060,13 @@ node-domexception@1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@2.6.7, node-fetch@2.6.9, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.6.9: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" -node-fetch@2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" - integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== - dependencies: - whatwg-url "^5.0.0" - -node-fetch@^2.6.9: - version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" - integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== - dependencies: - whatwg-url "^5.0.0" - node-forge@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" @@ -16122,15 +16090,7 @@ passport-strategy@1.x.x, passport-strategy@^1.0.0: resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA== -passport@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/passport/-/passport-0.4.1.tgz#941446a21cb92fc688d97a0861c38ce9f738f270" - integrity sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg== - dependencies: - passport-strategy "1.x.x" - pause "0.0.1" - -passport@^0.6.0: +passport@0.6.0, passport@^0.4.0, passport@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/passport/-/passport-0.6.0.tgz#e869579fab465b5c0b291e841e6cc95c005fac9d" integrity sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug== @@ -17115,13 +17075,6 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== -psl@^1.1.28: - version "1.15.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6" - integrity sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w== - dependencies: - punycode "^2.3.1" - psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -17155,11 +17108,6 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -punycode@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - pupa@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" @@ -17991,11 +17939,6 @@ sax@1.2.1: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA== -sax@>=0.1.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" - integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== - sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -18068,28 +18011,13 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.6.0, semver@^5.7.1: - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - -semver@7.5.3, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3: +"semver@2 || 3 || 4 || 5", semver@7.5.3, semver@^5.6.0, semver@^5.7.1, semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2: version "7.5.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^7.5.4, semver@^7.6.0, semver@^7.6.2: - version "7.6.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== - seq-queue@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" @@ -19553,7 +19481,7 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -"tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@^4.0.0, tough-cookie@^4.1.2: +tough-cookie@4.1.3, "tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@^4.0.0, tough-cookie@^4.1.2, tough-cookie@~2.5.0: version "4.1.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== @@ -19563,14 +19491,6 @@ touch@^3.1.0: universalify "^0.2.0" url-parse "^1.5.3" -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" @@ -20038,6 +19958,14 @@ unpipe@1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== +unset-value@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-2.0.1.tgz#57bed0c22d26f28d69acde5df9a11b77c74d2df3" + integrity sha512-2hvrBfjUE00PkqN+q0XP6yRAOGrR06uSiUoIQGZkc7GxvQ9H7v8quUPNtZjMg4uux69i8HWpIjLPUKwCuRGyNg== + dependencies: + has-value "^2.0.2" + isobject "^4.0.0" + untildify@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" @@ -20741,14 +20669,7 @@ 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 sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g== -xml2js@0.1.x: - version "0.1.14" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.1.14.tgz#5274e67f5a64c5f92974cd85139e0332adc6b90c" - integrity sha512-pbdws4PPPNc1HPluSUKamY4GWMk592K7qwcj6BExbVOhhubub8+pMda/ql68b6L3luZs/OGjGSB5goV7SnmgnA== - dependencies: - sax ">=0.1.1" - -xml2js@0.6.2: +xml2js@0.1.x, xml2js@0.6.2, xml2js@^0.5.0: version "0.6.2" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== @@ -20756,14 +20677,6 @@ xml2js@0.6.2: sax ">=0.6.0" xmlbuilder "~11.0.0" -xml2js@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" - integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - xmlbuilder@~11.0.0: version "11.0.1" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" From 80a67dccc06fa673654133a58d664a97f5824492 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 15 Jan 2025 15:40:18 +0000 Subject: [PATCH 08/32] Typing fixes. --- .../common/bindings/EvaluationSidePanel.svelte | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte b/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte index 91d986f0f6..6504f5f15b 100644 --- a/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte +++ b/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte @@ -5,7 +5,8 @@ import { fade } from "svelte/transition" import { UserScriptError } from "@budibase/string-templates" - export let expressionResult: string | undefined = undefined + export let expressionResult: string | { result: string } | undefined = + undefined export let expressionError: string | undefined = undefined export let evaluating = false export let expression: string | null = null @@ -13,7 +14,11 @@ $: error = expressionError != null $: empty = expression == null || expression?.trim() === "" $: success = !error && !empty - $: highlightedResult = highlight(expressionResult) + $: highlightedResult = highlight( + expressionResult && typeof expressionResult === "object" + ? expressionResult.result + : expressionResult + ) const formatError = (err: any) => { if (err.code === UserScriptError.code) { @@ -22,7 +27,7 @@ return err.toString() } - const highlight = (json: string | null) => { + const highlight = (json?: string | null) => { if (json == null) { return "" } @@ -47,7 +52,10 @@ } const copy = () => { - let clipboardVal = expressionResult.result + let clipboardVal = + expressionResult && typeof expressionResult === "object" + ? expressionResult.result + : expressionResult if (typeof clipboardVal === "object") { clipboardVal = JSON.stringify(clipboardVal, null, 2) } From 1dfedc1ee1d7f5996ca2933441732d1c6d7989eb Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 15 Jan 2025 15:43:15 +0000 Subject: [PATCH 09/32] Linting. --- packages/builder/src/components/common/CodeEditor/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/components/common/CodeEditor/index.ts b/packages/builder/src/components/common/CodeEditor/index.ts index d2cbe8a23e..0c974e0bf4 100644 --- a/packages/builder/src/components/common/CodeEditor/index.ts +++ b/packages/builder/src/components/common/CodeEditor/index.ts @@ -3,7 +3,6 @@ import sanitizeHtml from "sanitize-html" import { groupBy } from "lodash" import { BindingCompletion, - BindingMode, EditorModesMap, Helper, Snippet, From afef51166457cb2785c7bdcf3e5e8d92c2525141 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 15 Jan 2025 16:38:34 +0000 Subject: [PATCH 10/32] Removing .result from evaluation side panel. --- .../common/bindings/EvaluationSidePanel.svelte | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte b/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte index 6504f5f15b..e48b68aef8 100644 --- a/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte +++ b/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte @@ -5,7 +5,8 @@ import { fade } from "svelte/transition" import { UserScriptError } from "@budibase/string-templates" - export let expressionResult: string | { result: string } | undefined = + // this can be essentially any primitive response from the JS function + export let expressionResult: string | boolean | object | number | undefined = undefined export let expressionError: string | undefined = undefined export let evaluating = false @@ -14,11 +15,7 @@ $: error = expressionError != null $: empty = expression == null || expression?.trim() === "" $: success = !error && !empty - $: highlightedResult = highlight( - expressionResult && typeof expressionResult === "object" - ? expressionResult.result - : expressionResult - ) + $: highlightedResult = highlight(expressionResult) const formatError = (err: any) => { if (err.code === UserScriptError.code) { @@ -27,7 +24,7 @@ return err.toString() } - const highlight = (json?: string | null) => { + const highlight = (json?: any | null) => { if (json == null) { return "" } @@ -38,7 +35,7 @@ jsonString = JSON.stringify(JSON.parse(json), null, 2) } catch (err) { // Ignore - jsonString = "" + jsonString = json } return JsonFormatter.format(jsonString, { @@ -52,10 +49,7 @@ } const copy = () => { - let clipboardVal = - expressionResult && typeof expressionResult === "object" - ? expressionResult.result - : expressionResult + let clipboardVal = expressionResult if (typeof clipboardVal === "object") { clipboardVal = JSON.stringify(clipboardVal, null, 2) } From 84f1a95a8bf872a183e2a17b3c90ebe2312d326c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 15 Jan 2025 17:05:32 +0000 Subject: [PATCH 11/32] Implementing JSONValue type for passing binding results around. --- .../common/bindings/EvaluationSidePanel.svelte | 13 ++++++------- packages/frontend-core/src/utils/jsonFormatter.ts | 15 ++++++--------- packages/types/src/core/common.ts | 7 +++++++ packages/types/src/core/index.ts | 1 + 4 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 packages/types/src/core/common.ts diff --git a/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte b/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte index e48b68aef8..c8bf5529ad 100644 --- a/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte +++ b/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte @@ -4,10 +4,10 @@ import { Helpers } from "@budibase/bbui" import { fade } from "svelte/transition" import { UserScriptError } from "@budibase/string-templates" + import type { JSONValue } from "@budibase/types" // this can be essentially any primitive response from the JS function - export let expressionResult: string | boolean | object | number | undefined = - undefined + export let expressionResult: JSONValue | undefined = undefined export let expressionError: string | undefined = undefined export let evaluating = false export let expression: string | null = null @@ -24,21 +24,20 @@ return err.toString() } + // json can be any primitive type const highlight = (json?: any | null) => { if (json == null) { return "" } // Attempt to parse and then stringify, in case this is valid result - let jsonString: string try { - jsonString = JSON.stringify(JSON.parse(json), null, 2) + json = JSON.stringify(JSON.parse(json), null, 2) } catch (err) { - // Ignore - jsonString = json + // couldn't parse/stringify, just treat it as the raw input } - return JsonFormatter.format(jsonString, { + return JsonFormatter.format(json, { keyColor: "#e06c75", numberColor: "#e5c07b", stringColor: "#98c379", diff --git a/packages/frontend-core/src/utils/jsonFormatter.ts b/packages/frontend-core/src/utils/jsonFormatter.ts index 14a8989fd2..ba9f8fc748 100644 --- a/packages/frontend-core/src/utils/jsonFormatter.ts +++ b/packages/frontend-core/src/utils/jsonFormatter.ts @@ -1,3 +1,5 @@ +import { JSONValue } from "@budibase/types" + export type ColorsOptions = { keyColor?: string numberColor?: string @@ -32,15 +34,10 @@ function escapeHtml(html: string) { }) } -export function format(json: string | object, colorOptions = {}) { +export function format(json: JSONValue, colorOptions: ColorsOptions = {}) { const valueType = typeof json - if (valueType !== "string") { - json = JSON.stringify(json, null, 2) || valueType - } - let jsonString: string = - valueType !== "string" - ? JSON.stringify(json, null, 2) || valueType - : (json as string) + let jsonString = + typeof json === "string" ? json : JSON.stringify(json, null, 2) || valueType let colors = Object.assign({}, defaultColors, colorOptions) jsonString = jsonString .replace(/&/g, "&") @@ -48,7 +45,7 @@ export function format(json: string | object, colorOptions = {}) { .replace(/>/g, ">") return jsonString.replace( /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+]?\d+)?)/g, - match => { + (match: string) => { let color = colors.numberColor let style = "" if (/^"/.test(match)) { diff --git a/packages/types/src/core/common.ts b/packages/types/src/core/common.ts new file mode 100644 index 0000000000..c61fc3255d --- /dev/null +++ b/packages/types/src/core/common.ts @@ -0,0 +1,7 @@ +export type JSONValue = + | string + | number + | boolean + | null + | { [key: string]: JSONValue } + | JSONValue[] diff --git a/packages/types/src/core/index.ts b/packages/types/src/core/index.ts index 73cc7d35e0..ba9c1b907c 100644 --- a/packages/types/src/core/index.ts +++ b/packages/types/src/core/index.ts @@ -1,2 +1,3 @@ export * from "./installation" export * from "./events" +export * from "./common" From b3ccca051efd6be94d7ad448e76c2103367bca07 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 15 Jan 2025 17:12:17 +0000 Subject: [PATCH 12/32] Fixing build issue. --- .../builder/src/components/common/bindings/BindingPanel.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/components/common/bindings/BindingPanel.svelte b/packages/builder/src/components/common/bindings/BindingPanel.svelte index 7c863f4beb..98df69bc06 100644 --- a/packages/builder/src/components/common/bindings/BindingPanel.svelte +++ b/packages/builder/src/components/common/bindings/BindingPanel.svelte @@ -39,6 +39,7 @@ Helper, CaretPositionFn, InsertAtPositionFn, + JSONValue, } from "@budibase/types" import type { CompletionContext } from "@codemirror/autocomplete" @@ -184,7 +185,7 @@ debouncedEval(expression, context, snippets) } - const highlightJSON = (json: object | string) => { + const highlightJSON = (json: JSONValue) => { return JsonFormatter.format(json, { keyColor: "#e06c75", numberColor: "#e5c07b", From 9c7fc41913b12da2d4a08283b65e58e0ef3971a5 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 16 Jan 2025 09:41:51 +0000 Subject: [PATCH 13/32] Convert last few .js files in backend-core to .ts --- .../backend-core/src/db/couch/connections.ts | 4 ++-- .../db/tests/{pouch.spec.js => pouch.spec.ts} | 3 ++- .../users/{users.spec.js => users.spec.ts} | 21 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) rename packages/backend-core/src/db/tests/{pouch.spec.js => pouch.spec.ts} (98%) rename packages/backend-core/tests/core/users/{users.spec.js => users.spec.ts} (67%) diff --git a/packages/backend-core/src/db/couch/connections.ts b/packages/backend-core/src/db/couch/connections.ts index 5c7d7ec81d..9692b095a8 100644 --- a/packages/backend-core/src/db/couch/connections.ts +++ b/packages/backend-core/src/db/couch/connections.ts @@ -1,6 +1,6 @@ import env from "../../environment" -export const getCouchInfo = (connection?: string) => { +export const getCouchInfo = (connection?: string | null) => { // clean out any auth credentials const urlInfo = getUrlInfo(connection) let username @@ -45,7 +45,7 @@ export const getCouchInfo = (connection?: string) => { } } -export const getUrlInfo = (url = env.COUCH_DB_URL) => { +export const getUrlInfo = (url: string | null = env.COUCH_DB_URL) => { let cleanUrl, username, password, host if (url) { // Ensure the URL starts with a protocol diff --git a/packages/backend-core/src/db/tests/pouch.spec.js b/packages/backend-core/src/db/tests/pouch.spec.ts similarity index 98% rename from packages/backend-core/src/db/tests/pouch.spec.js rename to packages/backend-core/src/db/tests/pouch.spec.ts index f0abc82240..21632cff88 100644 --- a/packages/backend-core/src/db/tests/pouch.spec.js +++ b/packages/backend-core/src/db/tests/pouch.spec.ts @@ -1,5 +1,6 @@ require("../../../tests") -const getUrlInfo = require("../couch").getUrlInfo + +import { getUrlInfo } from "../couch" describe("pouch", () => { describe("Couch DB URL parsing", () => { diff --git a/packages/backend-core/tests/core/users/users.spec.js b/packages/backend-core/tests/core/users/users.spec.ts similarity index 67% rename from packages/backend-core/tests/core/users/users.spec.js rename to packages/backend-core/tests/core/users/users.spec.ts index dde0d87fb7..b14f553266 100644 --- a/packages/backend-core/tests/core/users/users.spec.js +++ b/packages/backend-core/tests/core/users/users.spec.ts @@ -1,17 +1,17 @@ -const _ = require("lodash/fp") -const { structures } = require("../../../tests") +import { range } from "lodash/fp" +import { structures } from "../.." jest.mock("../../../src/context") jest.mock("../../../src/db") -const context = require("../../../src/context") -const db = require("../../../src/db") +import * as context from "../../../src/context" +import * as db from "../../../src/db" -const { getCreatorCount } = require("../../../src/users/users") +import { getCreatorCount } from "../../../src/users/users" describe("Users", () => { - let getGlobalDBMock - let paginationMock + let getGlobalDBMock: jest.SpyInstance + let paginationMock: jest.SpyInstance beforeEach(() => { jest.resetAllMocks() @@ -22,11 +22,10 @@ describe("Users", () => { jest.spyOn(db, "getGlobalUserParams") }) - it("Retrieves the number of creators", async () => { - const getUsers = (offset, limit, creators = false) => { - const range = _.range(offset, limit) + it("retrieves the number of creators", async () => { + const getUsers = (offset: number, limit: number, creators = false) => { const opts = creators ? { builder: { global: true } } : undefined - return range.map(() => structures.users.user(opts)) + return range(offset, limit).map(() => structures.users.user(opts)) } const page1Data = getUsers(0, 8) const page2Data = getUsers(8, 12, true) From 5709c3b836982df012261b443190d2966c5e626b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 16 Jan 2025 10:01:51 +0000 Subject: [PATCH 14/32] Convert curl.spec.js --- .../tests/curl/{curl.spec.js => curl.spec.ts} | 45 ++++++++----------- 1 file changed, 19 insertions(+), 26 deletions(-) rename packages/server/src/api/controllers/query/import/sources/tests/curl/{curl.spec.js => curl.spec.ts} (69%) diff --git a/packages/server/src/api/controllers/query/import/sources/tests/curl/curl.spec.js b/packages/server/src/api/controllers/query/import/sources/tests/curl/curl.spec.ts similarity index 69% rename from packages/server/src/api/controllers/query/import/sources/tests/curl/curl.spec.js rename to packages/server/src/api/controllers/query/import/sources/tests/curl/curl.spec.ts index 7ea2f8932b..34777b0951 100644 --- a/packages/server/src/api/controllers/query/import/sources/tests/curl/curl.spec.js +++ b/packages/server/src/api/controllers/query/import/sources/tests/curl/curl.spec.ts @@ -1,34 +1,24 @@ -const { Curl } = require("../../curl") -const fs = require("fs") -const path = require("path") +import { Curl } from "../../curl" +import { readFileSync } from "fs" +import { join } from "path" -const getData = file => { - return fs.readFileSync(path.join(__dirname, `./data/${file}.txt`), "utf8") +const getData = (file: string) => { + return readFileSync(join(__dirname, `./data/${file}.txt`), "utf8") } describe("Curl Import", () => { - let curl + let curl: Curl beforeEach(() => { curl = new Curl() }) it("validates unsupported data", async () => { - let data - let supported - - // JSON - data = "{}" - supported = await curl.isSupported(data) - expect(supported).toBe(false) - - // Empty - data = "" - supported = await curl.isSupported(data) - expect(supported).toBe(false) + expect(await curl.isSupported("{}")).toBe(false) + expect(await curl.isSupported("")).toBe(false) }) - const init = async file => { + const init = async (file: string) => { await curl.isSupported(getData(file)) } @@ -39,14 +29,14 @@ describe("Curl Import", () => { }) describe("Returns queries", () => { - const getQueries = async file => { + const getQueries = async (file: string) => { await init(file) - const queries = await curl.getQueries() + const queries = await curl.getQueries("fake_datasource_id") expect(queries.length).toBe(1) return queries } - const testVerb = async (file, verb) => { + const testVerb = async (file: string, verb: string) => { const queries = await getQueries(file) expect(queries[0].queryVerb).toBe(verb) } @@ -59,7 +49,7 @@ describe("Curl Import", () => { await testVerb("patch", "patch") }) - const testPath = async (file, urlPath) => { + const testPath = async (file: string, urlPath: string) => { const queries = await getQueries(file) expect(queries[0].fields.path).toBe(urlPath) } @@ -69,7 +59,10 @@ describe("Curl Import", () => { await testPath("path", "http://example.com/paths/abc") }) - const testHeaders = async (file, headers) => { + const testHeaders = async ( + file: string, + headers: Record + ) => { const queries = await getQueries(file) expect(queries[0].fields.headers).toStrictEqual(headers) } @@ -82,7 +75,7 @@ describe("Curl Import", () => { }) }) - const testQuery = async (file, queryString) => { + const testQuery = async (file: string, queryString: string) => { const queries = await getQueries(file) expect(queries[0].fields.queryString).toBe(queryString) } @@ -91,7 +84,7 @@ describe("Curl Import", () => { await testQuery("query", "q1=v1&q1=v2") }) - const testBody = async (file, body) => { + const testBody = async (file: string, body?: Record) => { const queries = await getQueries(file) expect(queries[0].fields.requestBody).toStrictEqual( JSON.stringify(body, null, 2) From aa2bf74a7d9f3b901d3de04ac6952883a2fadd08 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 16 Jan 2025 10:29:48 +0000 Subject: [PATCH 15/32] Convert openapi2.spec.js --- .../sources/tests/openapi2/openapi2.spec.js | 243 ------------------ .../sources/tests/openapi2/openapi2.spec.ts | 135 ++++++++++ 2 files changed, 135 insertions(+), 243 deletions(-) delete mode 100644 packages/server/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.js create mode 100644 packages/server/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.ts diff --git a/packages/server/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.js b/packages/server/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.js deleted file mode 100644 index b61991f7d6..0000000000 --- a/packages/server/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.js +++ /dev/null @@ -1,243 +0,0 @@ -const { OpenAPI2 } = require("../../openapi2") -const fs = require("fs") -const path = require("path") - -const getData = (file, extension) => { - return fs.readFileSync( - path.join(__dirname, `./data/${file}/${file}.${extension}`), - "utf8" - ) -} - -describe("OpenAPI2 Import", () => { - let openapi2 - - beforeEach(() => { - openapi2 = new OpenAPI2() - }) - - it("validates unsupported data", async () => { - let data - let supported - - // non json / yaml - data = "curl http://example.com" - supported = await openapi2.isSupported(data) - expect(supported).toBe(false) - - // Empty - data = "" - supported = await openapi2.isSupported(data) - expect(supported).toBe(false) - }) - - const init = async (file, extension) => { - await openapi2.isSupported(getData(file, extension)) - } - - const runTests = async (filename, test, assertions) => { - for (let extension of ["json", "yaml"]) { - await test(filename, extension, assertions) - } - } - - const testImportInfo = async (file, extension) => { - await init(file, extension) - const info = await openapi2.getInfo() - expect(info.name).toBe("Swagger Petstore") - } - - it("returns import info", async () => { - await runTests("petstore", testImportInfo) - }) - - describe("Returns queries", () => { - const indexQueries = queries => { - return queries.reduce((acc, query) => { - acc[query.name] = query - return acc - }, {}) - } - - const getQueries = async (file, extension) => { - await init(file, extension) - const queries = await openapi2.getQueries() - expect(queries.length).toBe(6) - return indexQueries(queries) - } - - const testVerb = async (file, extension, assertions) => { - const queries = await getQueries(file, extension) - for (let [operationId, method] of Object.entries(assertions)) { - expect(queries[operationId].queryVerb).toBe(method) - } - } - - it("populates verb", async () => { - const assertions = { - createEntity: "create", - getEntities: "read", - getEntity: "read", - updateEntity: "update", - patchEntity: "patch", - deleteEntity: "delete", - } - await runTests("crud", testVerb, assertions) - }) - - const testPath = async (file, extension, assertions) => { - const queries = await getQueries(file, extension) - for (let [operationId, urlPath] of Object.entries(assertions)) { - expect(queries[operationId].fields.path).toBe(urlPath) - } - } - - it("populates path", async () => { - const assertions = { - createEntity: "http://example.com/entities", - getEntities: "http://example.com/entities", - getEntity: "http://example.com/entities/{{entityId}}", - updateEntity: "http://example.com/entities/{{entityId}}", - patchEntity: "http://example.com/entities/{{entityId}}", - deleteEntity: "http://example.com/entities/{{entityId}}", - } - await runTests("crud", testPath, assertions) - }) - - const testHeaders = async (file, extension, assertions) => { - const queries = await getQueries(file, extension) - for (let [operationId, headers] of Object.entries(assertions)) { - expect(queries[operationId].fields.headers).toStrictEqual(headers) - } - } - - const contentTypeHeader = { - "Content-Type": "application/json", - } - - it("populates headers", async () => { - const assertions = { - createEntity: { - ...contentTypeHeader, - }, - getEntities: {}, - getEntity: {}, - updateEntity: { - ...contentTypeHeader, - }, - patchEntity: { - ...contentTypeHeader, - }, - deleteEntity: { - "x-api-key": "{{x-api-key}}", - }, - } - - await runTests("crud", testHeaders, assertions) - }) - - const testQuery = async (file, extension, assertions) => { - const queries = await getQueries(file, extension) - for (let [operationId, queryString] of Object.entries(assertions)) { - expect(queries[operationId].fields.queryString).toStrictEqual( - queryString - ) - } - } - - it("populates query", async () => { - const assertions = { - createEntity: "", - getEntities: "page={{page}}&size={{size}}", - getEntity: "", - updateEntity: "", - patchEntity: "", - deleteEntity: "", - } - await runTests("crud", testQuery, assertions) - }) - - const testParameters = async (file, extension, assertions) => { - const queries = await getQueries(file, extension) - for (let [operationId, parameters] of Object.entries(assertions)) { - expect(queries[operationId].parameters).toStrictEqual(parameters) - } - } - - it("populates parameters", async () => { - const assertions = { - createEntity: [], - getEntities: [ - { - name: "page", - default: "", - }, - { - name: "size", - default: "", - }, - ], - getEntity: [ - { - name: "entityId", - default: "", - }, - ], - updateEntity: [ - { - name: "entityId", - default: "", - }, - ], - patchEntity: [ - { - name: "entityId", - default: "", - }, - ], - deleteEntity: [ - { - name: "entityId", - default: "", - }, - { - name: "x-api-key", - default: "", - }, - ], - } - await runTests("crud", testParameters, assertions) - }) - - const testBody = async (file, extension, assertions) => { - const queries = await getQueries(file, extension) - for (let [operationId, body] of Object.entries(assertions)) { - expect(queries[operationId].fields.requestBody).toStrictEqual( - JSON.stringify(body, null, 2) - ) - } - } - it("populates body", async () => { - const assertions = { - createEntity: { - name: "name", - type: "type", - }, - getEntities: undefined, - getEntity: undefined, - updateEntity: { - id: 1, - name: "name", - type: "type", - }, - patchEntity: { - id: 1, - name: "name", - type: "type", - }, - deleteEntity: undefined, - } - await runTests("crud", testBody, assertions) - }) - }) -}) diff --git a/packages/server/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.ts b/packages/server/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.ts new file mode 100644 index 0000000000..c16b04beb7 --- /dev/null +++ b/packages/server/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.ts @@ -0,0 +1,135 @@ +import { OpenAPI2 } from "../../openapi2" +import { readFileSync } from "fs" +import { join } from "path" +import { groupBy, mapValues } from "lodash" +import { Query } from "@budibase/types" + +const getData = (file: string, extension: string) => { + return readFileSync( + join(__dirname, `./data/${file}/${file}.${extension}`), + "utf8" + ) +} + +describe("OpenAPI2 Import", () => { + let openapi2: OpenAPI2 + + beforeEach(() => { + openapi2 = new OpenAPI2() + }) + + it("validates unsupported data", async () => { + expect(await openapi2.isSupported("curl http://example.com")).toBe(false) + expect(await openapi2.isSupported("")).toBe(false) + }) + + describe.each(["json", "yaml"])("%s", extension => { + describe("petstore", () => { + beforeEach(async () => { + await openapi2.isSupported(getData("petstore", extension)) + }) + + it("returns import info", async () => { + const { name } = await openapi2.getInfo() + expect(name).toBe("Swagger Petstore") + }) + }) + + describe("crud", () => { + let queries: Record + beforeEach(async () => { + await openapi2.isSupported(getData("crud", extension)) + + const raw = await openapi2.getQueries("fake_datasource_id") + queries = mapValues(groupBy(raw, "name"), group => group[0]) + }) + + it("should have 6 queries", () => { + expect(Object.keys(queries).length).toBe(6) + }) + + it.each([ + ["createEntity", "create"], + ["getEntities", "read"], + ["getEntity", "read"], + ["updateEntity", "update"], + ["patchEntity", "patch"], + ["deleteEntity", "delete"], + ])("should have correct verb for %s", (operationId, method) => { + expect(queries[operationId].queryVerb).toBe(method) + }) + + it.each([ + ["createEntity", "http://example.com/entities"], + ["getEntities", "http://example.com/entities"], + ["getEntity", "http://example.com/entities/{{entityId}}"], + ["updateEntity", "http://example.com/entities/{{entityId}}"], + ["patchEntity", "http://example.com/entities/{{entityId}}"], + ["deleteEntity", "http://example.com/entities/{{entityId}}"], + ])("should have correct path for %s", (operationId, urlPath) => { + expect(queries[operationId].fields.path).toBe(urlPath) + }) + + it.each([ + ["createEntity", { "Content-Type": "application/json" }], + ["getEntities", {}], + ["getEntity", {}], + ["updateEntity", { "Content-Type": "application/json" }], + ["patchEntity", { "Content-Type": "application/json" }], + ["deleteEntity", { "x-api-key": "{{x-api-key}}" }], + ])(`should have correct headers for %s`, (operationId, headers) => { + expect(queries[operationId].fields.headers).toStrictEqual(headers) + }) + + it.each([ + ["createEntity", ""], + ["getEntities", "page={{page}}&size={{size}}"], + ["getEntity", ""], + ["updateEntity", ""], + ["patchEntity", ""], + ["deleteEntity", ""], + ])( + `should have correct query string for %s`, + (operationId, queryString) => { + expect(queries[operationId].fields.queryString).toBe(queryString) + } + ) + + it.each([ + ["createEntity", []], + [ + "getEntities", + [ + { name: "page", default: "" }, + { name: "size", default: "" }, + ], + ], + ["getEntity", [{ name: "entityId", default: "" }]], + ["updateEntity", [{ name: "entityId", default: "" }]], + ["patchEntity", [{ name: "entityId", default: "" }]], + [ + "deleteEntity", + [ + { name: "entityId", default: "" }, + { name: "x-api-key", default: "" }, + ], + ], + ])(`should have correct parameters for %s`, (operationId, parameters) => { + expect(queries[operationId].parameters).toStrictEqual(parameters) + }) + + it.each([ + ["createEntity", { name: "name", type: "type" }], + ["getEntities", undefined], + ["getEntity", undefined], + ["updateEntity", { id: 1, name: "name", type: "type" }], + ["patchEntity", { id: 1, name: "name", type: "type" }], + ["deleteEntity", undefined], + ])(`should have correct body for %s`, (operationId, body) => { + expect(queries[operationId].fields.requestBody).toBe( + JSON.stringify(body, null, 2) + ) + }) + }) + }) +}) From dd96f4ef8edce236332048f5934ff31418a6a25d Mon Sep 17 00:00:00 2001 From: Michael Drury Date: Thu, 16 Jan 2025 11:45:45 +0000 Subject: [PATCH 16/32] PR comments. --- .../common/CodeEditor/CodeEditor.svelte | 16 ++++++++-------- .../frontend-core/src/utils/jsonFormatter.ts | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte index 1fa2eb3fae..bc88f0f981 100644 --- a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte +++ b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte @@ -60,8 +60,8 @@ const dispatch = createEventDispatcher() - let textarea: HTMLDivElement | undefined - let editor: EditorView | undefined + let textarea: HTMLDivElement + let editor: EditorView let mounted = false let isEditorInitialised = false let queuedRefresh = false @@ -73,7 +73,7 @@ $: { if (autofocus && isEditorInitialised) { - editor?.focus() + editor.focus() } } @@ -90,7 +90,7 @@ isDark = !currentTheme.includes("light") // Issue theme compartment update - editor?.dispatch({ + editor.dispatch({ effects: themeConfig.reconfigure([...(isDark ? [oneDark] : [])]), }) } @@ -119,7 +119,7 @@ (editor.state.doc.toString() !== value || queuedRefresh) ) { editor.dispatch({ - changes: { from: 0, to: editor.state.doc.length, insert: value! }, + changes: { from: 0, to: editor.state.doc.length, insert: value }, }) queuedRefresh = false } @@ -127,7 +127,7 @@ // Export a function to expose caret position export const getCaretPosition = () => { - const selection_range = editor?.state.selection.ranges[0] + const selection_range = editor.state.selection.ranges[0] return { start: selection_range?.from, end: selection_range?.to, @@ -142,7 +142,7 @@ }) => { // Updating the value inside. // Retain focus - editor?.dispatch({ + editor.dispatch({ changes: { from: opts.start || editor.state.doc.length, to: opts.end || editor.state.doc.length, @@ -323,7 +323,7 @@ keymap.of(buildKeymap()), EditorView.domEventHandlers({ blur: () => { - dispatch("blur", editor?.state.doc.toString()) + dispatch("blur", editor.state.doc.toString()) }, }), EditorView.updateListener.of(v => { diff --git a/packages/frontend-core/src/utils/jsonFormatter.ts b/packages/frontend-core/src/utils/jsonFormatter.ts index ba9f8fc748..de17426109 100644 --- a/packages/frontend-core/src/utils/jsonFormatter.ts +++ b/packages/frontend-core/src/utils/jsonFormatter.ts @@ -18,7 +18,7 @@ const defaultColors: ColorsOptions = { nullColor: "cornflowerblue", } -const entityMap: Record = { +const entityMap = { "&": "&", "<": "<", ">": ">", From 3f10d9d8edb383462aaefcf968fa383e27fb5f5b Mon Sep 17 00:00:00 2001 From: Michael Drury Date: Thu, 16 Jan 2025 12:19:36 +0000 Subject: [PATCH 17/32] Linting. --- packages/types/src/ui/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/types/src/ui/index.ts b/packages/types/src/ui/index.ts index 306c4d8fe2..907f4ec0b5 100644 --- a/packages/types/src/ui/index.ts +++ b/packages/types/src/ui/index.ts @@ -2,4 +2,3 @@ export * from "./stores" export * from "./bindings" export * from "./components" export * from "./dataFetch" - From 7c8bef3da76532d4636248cce716f94dd0b38c89 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 16 Jan 2025 14:10:00 +0000 Subject: [PATCH 18/32] fix column names with spaces for relationship columns --- packages/backend-core/src/sql/sql.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index 5a6907faa0..eb103d3e2e 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -1173,7 +1173,13 @@ class InternalBuilder { } if (this.isAggregateField(key)) { - query = query.orderBy(key, direction, nulls) + // query = query.orderBy(this.quotedIdentifier(key), direction, nulls) + // query = query.orderBy(this.rawQuotedIdentifier(key), direction, nulls) + query = query.orderByRaw(`?? ??`, [ + this.rawQuotedIdentifier(key), + this.knex.raw(direction), + this.knex.raw(nulls as string), + ]) } else { let composite = `${aliased}.${key}` if (this.client === SqlClient.ORACLE) { @@ -1183,7 +1189,11 @@ class InternalBuilder { this.knex.raw(nulls as string), ]) } else { - query = query.orderBy(composite, direction, nulls) + query = query.orderByRaw(`?? ??`, [ + this.rawQuotedIdentifier(composite), + this.knex.raw(direction), + ]) + // query = query.orderBy(this.quotedIdentifier(key), direction, nulls) } } } @@ -1344,14 +1354,16 @@ class InternalBuilder { // add the correlation to the overall query subQuery = subQuery.where( - correlatedTo, + this.rawQuotedIdentifier(correlatedTo), "=", this.rawQuotedIdentifier(correlatedFrom) ) const standardWrap = (select: Knex.Raw): Knex.QueryBuilder => { subQuery = subQuery - .select(relationshipFields) + .select( + relationshipFields.map(field => this.rawQuotedIdentifier(field)) + ) .limit(getRelationshipLimit()) // @ts-ignore - the from alias syntax isn't in Knex typing return knex.select(select).from({ From df2f6cf257b4a1c9013b8be8683157daed5208e9 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 16 Jan 2025 14:13:48 +0000 Subject: [PATCH 19/32] re-added nulls --- packages/backend-core/src/sql/sql.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index eb103d3e2e..04b1309534 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -1167,15 +1167,14 @@ class InternalBuilder { let nulls: "first" | "last" | undefined = undefined if ( this.client === SqlClient.POSTGRES || - this.client === SqlClient.ORACLE + this.client === SqlClient.ORACLE || + this.client === SqlClient.SQL_LITE ) { nulls = value.direction === SortOrder.ASCENDING ? "first" : "last" } if (this.isAggregateField(key)) { - // query = query.orderBy(this.quotedIdentifier(key), direction, nulls) - // query = query.orderBy(this.rawQuotedIdentifier(key), direction, nulls) - query = query.orderByRaw(`?? ??`, [ + query = query.orderByRaw(`?? ?? nulls ??`, [ this.rawQuotedIdentifier(key), this.knex.raw(direction), this.knex.raw(nulls as string), @@ -1189,11 +1188,11 @@ class InternalBuilder { this.knex.raw(nulls as string), ]) } else { - query = query.orderByRaw(`?? ??`, [ + query = query.orderByRaw(`?? ?? nulls ??`, [ this.rawQuotedIdentifier(composite), this.knex.raw(direction), + this.knex.raw(nulls as string) ]) - // query = query.orderBy(this.quotedIdentifier(key), direction, nulls) } } } From c077f7e84cf5e77ba447e7824e5f8483f2756f73 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 16 Jan 2025 14:20:26 +0000 Subject: [PATCH 20/32] lint --- packages/backend-core/src/sql/sql.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index 04b1309534..6dc8963323 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -1191,7 +1191,7 @@ class InternalBuilder { query = query.orderByRaw(`?? ?? nulls ??`, [ this.rawQuotedIdentifier(composite), this.knex.raw(direction), - this.knex.raw(nulls as string) + this.knex.raw(nulls as string), ]) } } From 3bd56bdca7be821b81d13fca1059b9fb206a6e6c Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 16 Jan 2025 14:29:49 +0000 Subject: [PATCH 21/32] remove nulls for raw and rely on defaults --- packages/backend-core/src/sql/sql.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index 6dc8963323..fb7cfdec1b 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -1167,17 +1167,15 @@ class InternalBuilder { let nulls: "first" | "last" | undefined = undefined if ( this.client === SqlClient.POSTGRES || - this.client === SqlClient.ORACLE || - this.client === SqlClient.SQL_LITE + this.client === SqlClient.ORACLE ) { nulls = value.direction === SortOrder.ASCENDING ? "first" : "last" } if (this.isAggregateField(key)) { - query = query.orderByRaw(`?? ?? nulls ??`, [ + query = query.orderByRaw(`?? ??`, [ this.rawQuotedIdentifier(key), this.knex.raw(direction), - this.knex.raw(nulls as string), ]) } else { let composite = `${aliased}.${key}` @@ -1188,10 +1186,9 @@ class InternalBuilder { this.knex.raw(nulls as string), ]) } else { - query = query.orderByRaw(`?? ?? nulls ??`, [ + query = query.orderByRaw(`?? ??`, [ this.rawQuotedIdentifier(composite), this.knex.raw(direction), - this.knex.raw(nulls as string), ]) } } From e4f7fa7fadc7ebf94ff21b2cc68186349745e210 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 16 Jan 2025 15:07:32 +0000 Subject: [PATCH 22/32] more consistent null handling --- packages/backend-core/src/sql/sql.ts | 17 +++----- .../server/src/api/routes/tests/row.spec.ts | 43 +++++++++++++++++++ 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index fb7cfdec1b..b91a331740 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -1162,20 +1162,14 @@ class InternalBuilder { const direction = value.direction === SortOrder.ASCENDING ? "asc" : "desc" - // TODO: figure out a way to remove this conditional, not relying on - // the defaults of each datastore. - let nulls: "first" | "last" | undefined = undefined - if ( - this.client === SqlClient.POSTGRES || - this.client === SqlClient.ORACLE - ) { - nulls = value.direction === SortOrder.ASCENDING ? "first" : "last" - } + let nulls: "first" | "last" = + value.direction === SortOrder.ASCENDING ? "first" : "last" if (this.isAggregateField(key)) { - query = query.orderByRaw(`?? ??`, [ + query = query.orderByRaw(`?? ?? nulls ??`, [ this.rawQuotedIdentifier(key), this.knex.raw(direction), + this.knex.raw(nulls as string), ]) } else { let composite = `${aliased}.${key}` @@ -1186,9 +1180,10 @@ class InternalBuilder { this.knex.raw(nulls as string), ]) } else { - query = query.orderByRaw(`?? ??`, [ + query = query.orderByRaw(`?? ?? nulls ??`, [ this.rawQuotedIdentifier(composite), this.knex.raw(direction), + this.knex.raw(nulls as string), ]) } } diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index db5fcbaebb..101b9928f4 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -3650,6 +3650,49 @@ if (descriptions.length) { }) }) + describe("Fields with spaces", () => { + let table: Table + let otherTable: Table + let relatedRow: Row, mainRow: Row + + beforeAll(async () => { + otherTable = await config.api.table.save(defaultTable()) + table = await config.api.table.save( + saveTableRequest({ + schema: { + links: { + name: "links", + fieldName: "links", + type: FieldType.LINK, + tableId: otherTable._id!, + relationshipType: RelationshipType.ONE_TO_MANY, + }, + "nameWithSpace ": { + name: "nameWithSpace ", + type: FieldType.STRING, + }, + }, + }) + ) + relatedRow = await config.api.row.save(otherTable._id!, { + name: generator.word(), + description: generator.paragraph(), + }) + mainRow = await config.api.row.save(table._id!, { + "nameWithSpace ": generator.word(), + tableId: table._id!, + links: [relatedRow._id], + }) + }) + + it("Successfully returns rows that have spaces in their field names", async () => { + const { rows } = await config.api.row.search(table._id!) + expect(rows.length).toBe(1) + const row = rows[0] + expect(row["nameWithSpace "]).toBeDefined() + }) + }) + if (!isInternal && !isOracle) { describe("bigint ids", () => { let table1: Table, table2: Table From c69e604eaf6d91189d778be6463b79426fb07f2c Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 16 Jan 2025 15:17:40 +0000 Subject: [PATCH 23/32] lint --- .../server/src/api/routes/tests/row.spec.ts | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 101b9928f4..576f0bb663 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -3650,48 +3650,50 @@ if (descriptions.length) { }) }) - describe("Fields with spaces", () => { - let table: Table - let otherTable: Table - let relatedRow: Row, mainRow: Row + if (isInternal || isMSSQL) { + describe("Fields with spaces", () => { + let table: Table + let otherTable: Table + let relatedRow: Row - beforeAll(async () => { - otherTable = await config.api.table.save(defaultTable()) - table = await config.api.table.save( - saveTableRequest({ - schema: { - links: { - name: "links", - fieldName: "links", - type: FieldType.LINK, - tableId: otherTable._id!, - relationshipType: RelationshipType.ONE_TO_MANY, + beforeAll(async () => { + otherTable = await config.api.table.save(defaultTable()) + table = await config.api.table.save( + saveTableRequest({ + schema: { + links: { + name: "links", + fieldName: "links", + type: FieldType.LINK, + tableId: otherTable._id!, + relationshipType: RelationshipType.ONE_TO_MANY, + }, + "nameWithSpace ": { + name: "nameWithSpace ", + type: FieldType.STRING, + }, }, - "nameWithSpace ": { - name: "nameWithSpace ", - type: FieldType.STRING, - }, - }, + }) + ) + relatedRow = await config.api.row.save(otherTable._id!, { + name: generator.word(), + description: generator.paragraph(), + }) + await config.api.row.save(table._id!, { + "nameWithSpace ": generator.word(), + tableId: table._id!, + links: [relatedRow._id], }) - ) - relatedRow = await config.api.row.save(otherTable._id!, { - name: generator.word(), - description: generator.paragraph(), }) - mainRow = await config.api.row.save(table._id!, { - "nameWithSpace ": generator.word(), - tableId: table._id!, - links: [relatedRow._id], - }) - }) - it("Successfully returns rows that have spaces in their field names", async () => { - const { rows } = await config.api.row.search(table._id!) - expect(rows.length).toBe(1) - const row = rows[0] - expect(row["nameWithSpace "]).toBeDefined() + it("Successfully returns rows that have spaces in their field names", async () => { + const { rows } = await config.api.row.search(table._id!) + expect(rows.length).toBe(1) + const row = rows[0] + expect(row["nameWithSpace "]).toBeDefined() + }) }) - }) + } if (!isInternal && !isOracle) { describe("bigint ids", () => { From a142c0cd2a0f835709e9a3c7aef8ae4175cdca9f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 16 Jan 2025 15:54:32 +0000 Subject: [PATCH 24/32] Fixing build issue. --- packages/frontend-core/src/utils/jsonFormatter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-core/src/utils/jsonFormatter.ts b/packages/frontend-core/src/utils/jsonFormatter.ts index de17426109..9f9af3a3c0 100644 --- a/packages/frontend-core/src/utils/jsonFormatter.ts +++ b/packages/frontend-core/src/utils/jsonFormatter.ts @@ -30,7 +30,7 @@ const entityMap = { function escapeHtml(html: string) { return String(html).replace(/[&<>"'`=]/g, function (s) { - return entityMap[s] + return entityMap[s as keyof typeof entityMap] }) } From d610317ef55c9a371779433c870bbd84d7511bbb Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 16 Jan 2025 16:41:00 +0000 Subject: [PATCH 25/32] fix tests --- packages/backend-core/src/sql/sql.ts | 43 ++++++++++--------- .../src/integrations/microsoftSqlServer.ts | 1 + 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index b91a331740..334f1efdd4 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -1162,31 +1162,32 @@ class InternalBuilder { const direction = value.direction === SortOrder.ASCENDING ? "asc" : "desc" - let nulls: "first" | "last" = - value.direction === SortOrder.ASCENDING ? "first" : "last" + // TODO: figure out a way to remove this conditional, not relying on + // the defaults of each datastore. + let nulls: "first" | "last" | undefined = undefined + if ( + this.client === SqlClient.POSTGRES || + this.client === SqlClient.ORACLE + ) { + nulls = value.direction === SortOrder.ASCENDING ? "first" : "last" + } + + const composite = `${aliased}.${key}` + let identifier if (this.isAggregateField(key)) { - query = query.orderByRaw(`?? ?? nulls ??`, [ - this.rawQuotedIdentifier(key), - this.knex.raw(direction), - this.knex.raw(nulls as string), - ]) + identifier = this.rawQuotedIdentifier(key) + } else if (this.client === SqlClient.ORACLE) { + identifier = this.convertClobs(composite) } else { - let composite = `${aliased}.${key}` - if (this.client === SqlClient.ORACLE) { - query = query.orderByRaw(`?? ?? nulls ??`, [ - this.convertClobs(composite), - this.knex.raw(direction), - this.knex.raw(nulls as string), - ]) - } else { - query = query.orderByRaw(`?? ?? nulls ??`, [ - this.rawQuotedIdentifier(composite), - this.knex.raw(direction), - this.knex.raw(nulls as string), - ]) - } + identifier = this.rawQuotedIdentifier(composite) } + + query = query.orderByRaw(`?? ?? ${nulls ? "nulls ??" : ""}`, [ + identifier, + this.knex.raw(direction), + ...(nulls ? [this.knex.raw(nulls as string)] : []), + ]) } } diff --git a/packages/server/src/integrations/microsoftSqlServer.ts b/packages/server/src/integrations/microsoftSqlServer.ts index 477813239b..8548d57f15 100644 --- a/packages/server/src/integrations/microsoftSqlServer.ts +++ b/packages/server/src/integrations/microsoftSqlServer.ts @@ -276,6 +276,7 @@ class SqlServerIntegration extends Sql implements DatasourcePlus { encrypt, enableArithAbort: true, requestTimeout: env.QUERY_THREAD_TIMEOUT, + connectTimeout: env.QUERY_THREAD_TIMEOUT, }, } if (encrypt) { From 709b3fb317ed50515ec73ad391167888674a0ced Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Thu, 16 Jan 2025 17:05:35 +0000 Subject: [PATCH 26/32] Bump version to 3.2.45 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 8d7179dbae..c02b221ec8 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "3.2.44", + "version": "3.2.45", "npmClient": "yarn", "concurrency": 20, "command": { From 65ef56e416cf8828041e9b44d0d2ce50f55e504d Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 17 Jan 2025 11:02:08 +0000 Subject: [PATCH 27/32] Add basic TS config for BBUI --- packages/bbui/package.json | 5 +++-- packages/bbui/src/helpers.d.ts | 4 ---- packages/bbui/src/{index.js => index.ts} | 0 packages/bbui/svelte.config.js | 7 +++++++ packages/bbui/tsconfig.json | 11 +++++++++++ packages/bbui/vite.config.js | 2 +- 6 files changed, 22 insertions(+), 7 deletions(-) delete mode 100644 packages/bbui/src/helpers.d.ts rename packages/bbui/src/{index.js => index.ts} (100%) create mode 100644 packages/bbui/svelte.config.js create mode 100644 packages/bbui/tsconfig.json diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 89f72bc46d..2caad20bf6 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -3,7 +3,7 @@ "description": "A UI solution used in the different Budibase projects.", "version": "0.0.0", "license": "MPL-2.0", - "svelte": "src/index.js", + "svelte": "src/index.ts", "module": "dist/bbui.mjs", "exports": { ".": { @@ -14,7 +14,8 @@ "./spectrum-icons-vite.js": "./src/spectrum-icons-vite.js" }, "scripts": { - "build": "vite build" + "build": "vite build", + "dev": "vite build --watch --mode=dev" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "1.4.0", diff --git a/packages/bbui/src/helpers.d.ts b/packages/bbui/src/helpers.d.ts deleted file mode 100644 index d4e2a45186..0000000000 --- a/packages/bbui/src/helpers.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module "./helpers" { - export const cloneDeep: (obj: T) => T - export const copyToClipboard: (value: any) => Promise -} diff --git a/packages/bbui/src/index.js b/packages/bbui/src/index.ts similarity index 100% rename from packages/bbui/src/index.js rename to packages/bbui/src/index.ts diff --git a/packages/bbui/svelte.config.js b/packages/bbui/svelte.config.js new file mode 100644 index 0000000000..7d908c15d5 --- /dev/null +++ b/packages/bbui/svelte.config.js @@ -0,0 +1,7 @@ +const { vitePreprocess } = require("@sveltejs/vite-plugin-svelte") + +const config = { + preprocess: vitePreprocess(), +} + +module.exports = config diff --git a/packages/bbui/tsconfig.json b/packages/bbui/tsconfig.json new file mode 100644 index 0000000000..0b529c199a --- /dev/null +++ b/packages/bbui/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "allowJs": true, + "outDir": "./dist", + "lib": ["ESNext"], + "baseUrl": ".", + }, + "include": ["./src/**/*"], + "exclude": ["node_modules", "**/*.json", "**/*.spec.ts", "**/*.spec.js"] +} \ No newline at end of file diff --git a/packages/bbui/vite.config.js b/packages/bbui/vite.config.js index bf0f9fc26d..bccca20e43 100644 --- a/packages/bbui/vite.config.js +++ b/packages/bbui/vite.config.js @@ -9,7 +9,7 @@ export default defineConfig(({ mode }) => { build: { sourcemap: !isProduction, lib: { - entry: "src/index.js", + entry: "src/index.ts", formats: ["es"], }, }, From 50a8caddb9743bd5668fddafb2737610e8d073eb Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 17 Jan 2025 11:15:52 +0000 Subject: [PATCH 28/32] Type BBUI helpers --- packages/bbui/src/{helpers.js => helpers.ts} | 45 ++++++++++---------- packages/bbui/tsconfig.json | 8 ++++ 2 files changed, 31 insertions(+), 22 deletions(-) rename packages/bbui/src/{helpers.js => helpers.ts} (87%) diff --git a/packages/bbui/src/helpers.js b/packages/bbui/src/helpers.ts similarity index 87% rename from packages/bbui/src/helpers.js rename to packages/bbui/src/helpers.ts index 246587af44..dfb5015a8d 100644 --- a/packages/bbui/src/helpers.js +++ b/packages/bbui/src/helpers.ts @@ -6,9 +6,8 @@ export const deepGet = helpers.deepGet /** * Generates a DOM safe UUID. * Starting with a letter is important to make it DOM safe. - * @return {string} a random DOM safe UUID */ -export function uuid() { +export function uuid(): string { return "cxxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, c => { const r = (Math.random() * 16) | 0 const v = c === "x" ? r : (r & 0x3) | 0x8 @@ -18,12 +17,10 @@ export function uuid() { /** * Capitalises a string - * @param string the string to capitalise - * @return {string} the capitalised string */ -export const capitalise = string => { +export const capitalise = (string?: string | null): string => { if (!string) { - return string + return "" } return string.substring(0, 1).toUpperCase() + string.substring(1) } @@ -33,7 +30,7 @@ export const capitalise = string => { * @param string the string to compute a hash of * @return {string} the hash string */ -export const hashString = string => { +export const hashString = (string?: string | null): string => { if (!string) { return "0" } @@ -54,11 +51,12 @@ export const hashString = string => { * will override the value "foo" rather than "bar". * If a deep path is specified and the parent keys don't exist then these will * be created. - * @param obj the object - * @param key the key - * @param value the value */ -export const deepSet = (obj, key, value) => { +export const deepSet = ( + obj: Record | null, + key: string | null, + value: any +): void => { if (!obj || !key) { return } @@ -84,7 +82,7 @@ export const deepSet = (obj, key, value) => { * Deeply clones an object. Functions are not supported. * @param obj the object to clone */ -export const cloneDeep = obj => { +export const cloneDeep = (obj: T): T => { if (!obj) { return obj } @@ -95,7 +93,7 @@ export const cloneDeep = obj => { * Copies a value to the clipboard * @param value the value to copy */ -export const copyToClipboard = value => { +export const copyToClipboard = (value: string): Promise => { return new Promise(res => { if (navigator.clipboard && window.isSecureContext) { // Try using the clipboard API first @@ -117,9 +115,12 @@ export const copyToClipboard = value => { }) } -// Parsed a date value. This is usually an ISO string, but can be a +// Parse a date value. This is usually an ISO string, but can be a // bunch of different formats and shapes depending on schema flags. -export const parseDate = (value, { enableTime = true }) => { +export const parseDate = ( + value: string | dayjs.Dayjs | null, + { enableTime = true } +): dayjs.Dayjs | null => { // If empty then invalid if (!value) { return null @@ -128,7 +129,7 @@ export const parseDate = (value, { enableTime = true }) => { // Certain string values need transformed if (typeof value === "string") { // Check for time only values - if (!isNaN(new Date(`0-${value}`))) { + if (!isNaN(new Date(`0-${value}`).valueOf())) { value = `0-${value}` } @@ -153,9 +154,9 @@ export const parseDate = (value, { enableTime = true }) => { // Stringifies a dayjs object to create an ISO string that respects the various // schema flags export const stringifyDate = ( - value, + value: null | dayjs.Dayjs, { enableTime = true, timeOnly = false, ignoreTimezones = false } = {} -) => { +): string | null => { if (!value) { return null } @@ -192,7 +193,7 @@ export const stringifyDate = ( } // Determine the dayjs-compatible format of the browser's default locale -const getPatternForPart = part => { +const getPatternForPart = (part: Intl.DateTimeFormatPart): string => { switch (part.type) { case "day": return "D".repeat(part.value.length) @@ -214,9 +215,9 @@ const localeDateFormat = new Intl.DateTimeFormat() // Formats a dayjs date according to schema flags export const getDateDisplayValue = ( - value, + value: dayjs.Dayjs | null, { enableTime = true, timeOnly = false } = {} -) => { +): string => { if (!value?.isValid()) { return "" } @@ -229,7 +230,7 @@ export const getDateDisplayValue = ( } } -export const hexToRGBA = (color, opacity) => { +export const hexToRGBA = (color: string, opacity: number): string => { if (color.includes("#")) { color = color.replace("#", "") } diff --git a/packages/bbui/tsconfig.json b/packages/bbui/tsconfig.json index 0b529c199a..2fe17da42e 100644 --- a/packages/bbui/tsconfig.json +++ b/packages/bbui/tsconfig.json @@ -5,6 +5,14 @@ "outDir": "./dist", "lib": ["ESNext"], "baseUrl": ".", + "paths": { + "@budibase/*": [ + "../*/src/index.ts", + "../*/src/index.js", + "../*", + "../../node_modules/@budibase/*" + ] + } }, "include": ["./src/**/*"], "exclude": ["node_modules", "**/*.json", "**/*.spec.ts", "**/*.spec.js"] From 3fa7a239326b5bdc33067b500cb40612e9c2b6e7 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 17 Jan 2025 11:23:16 +0000 Subject: [PATCH 29/32] Update helpers --- packages/bbui/src/helpers.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/bbui/src/helpers.ts b/packages/bbui/src/helpers.ts index dfb5015a8d..330f381d53 100644 --- a/packages/bbui/src/helpers.ts +++ b/packages/bbui/src/helpers.ts @@ -27,8 +27,6 @@ export const capitalise = (string?: string | null): string => { /** * Computes a short hash of a string - * @param string the string to compute a hash of - * @return {string} the hash string */ export const hashString = (string?: string | null): string => { if (!string) { @@ -80,7 +78,6 @@ export const deepSet = ( /** * Deeply clones an object. Functions are not supported. - * @param obj the object to clone */ export const cloneDeep = (obj: T): T => { if (!obj) { @@ -91,9 +88,8 @@ export const cloneDeep = (obj: T): T => { /** * Copies a value to the clipboard - * @param value the value to copy */ -export const copyToClipboard = (value: string): Promise => { +export const copyToClipboard = (value: any): Promise => { return new Promise(res => { if (navigator.clipboard && window.isSecureContext) { // Try using the clipboard API first From ffd2d7bd18032cee8d2458f175582a20e6145f6d Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 17 Jan 2025 11:28:21 +0000 Subject: [PATCH 30/32] Type heading as example TS svelte component --- packages/bbui/src/Typography/Heading.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/bbui/src/Typography/Heading.svelte b/packages/bbui/src/Typography/Heading.svelte index 90d53fb208..f48d5d958e 100644 --- a/packages/bbui/src/Typography/Heading.svelte +++ b/packages/bbui/src/Typography/Heading.svelte @@ -2,10 +2,10 @@ import "@spectrum-css/typography/dist/index-vars.css" // Sizes - export let size = "M" - export let textAlign = undefined - export let noPadding = false - export let weight = "default" // light, heavy, default + export let size: "XS" | "S" | "M" | "L" = "M" + export let textAlign: string | undefined = undefined + export let noPadding: boolean = false + export let weight: "light" | "heavy" | "default" = "default"

Date: Fri, 17 Jan 2025 14:15:40 +0000 Subject: [PATCH 31/32] lint --- .../components/automation/SetupPanel/CronBuilder.svelte | 5 ++--- .../backend/DataTable/modals/CreateEditColumn.svelte | 5 +++-- .../data/table/[tableId]/[viewId]/index.svelte | 4 ++-- .../app/[application]/data/table/[tableId]/index.svelte | 5 +++-- .../src/pages/builder/portal/settings/ai/index.svelte | 5 ++--- .../builder/src/pages/builder/portal/settings/index.svelte | 7 +------ 6 files changed, 13 insertions(+), 18 deletions(-) diff --git a/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte b/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte index f04c5454ea..1490baa602 100644 --- a/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte +++ b/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte @@ -9,7 +9,7 @@ } from "@budibase/bbui" import { onMount, createEventDispatcher } from "svelte" import { flags } from "@/stores/builder" - import { featureFlags, licensing } from "@/stores/portal" + import { licensing } from "@/stores/portal" import { API } from "@/api" import MagicWand from "../../../../assets/MagicWand.svelte" @@ -27,8 +27,7 @@ let loadingAICronExpression = false $: aiEnabled = - ($featureFlags.AI_CUSTOM_CONFIGS && $licensing.customAIConfigsEnabled) || - ($featureFlags.BUDIBASE_AI && $licensing.budibaseAIEnabled) + $licensing.customAIConfigsEnabled || $licensing.budibaseAIEnabled $: { if (cronExpression) { try { diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 55ac8474a6..4af1dcc0ee 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -26,7 +26,7 @@ import { createEventDispatcher, getContext, onMount } from "svelte" import { cloneDeep } from "lodash/fp" import { tables, datasources } from "@/stores/builder" - import { featureFlags } from "@/stores/portal" + import { licensing } from "@/stores/portal" import { TableNames, UNEDITABLE_USER_FIELDS } from "@/constants" import { FIELDS, @@ -100,7 +100,8 @@ let optionsValid = true $: rowGoldenSample = RowUtils.generateGoldenSample($rows) - $: aiEnabled = $featureFlags.BUDIBASE_AI || $featureFlags.AI_CUSTOM_CONFIGS + $: aiEnabled = + $licensing.customAIConfigsEnabled || $licensing.budibaseAiEnabled $: if (primaryDisplay) { editableColumn.constraints.presence = { allowEmpty: false } } diff --git a/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/[viewId]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/[viewId]/index.svelte index 9ac7ccd715..4d42c0d5d6 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/[viewId]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/[viewId]/index.svelte @@ -1,6 +1,6 @@ From 4d90751988811729d296a3888a153a41661da39d Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 17 Jan 2025 14:30:17 +0000 Subject: [PATCH 32/32] Bump version to 3.2.46 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index c02b221ec8..d033c24518 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "3.2.45", + "version": "3.2.46", "npmClient": "yarn", "concurrency": 20, "command": {