Don't break the fact that processStringSync returns a string.
This commit is contained in:
parent
fdbe633b02
commit
df242cc2ad
|
@ -66,6 +66,7 @@
|
||||||
let insertAtPos
|
let insertAtPos
|
||||||
let targetMode = null
|
let targetMode = null
|
||||||
let expressionResult
|
let expressionResult
|
||||||
|
let expressionError
|
||||||
let evaluating = false
|
let evaluating = false
|
||||||
|
|
||||||
$: useSnippets = allowSnippets && !$licensing.isFreePlan
|
$: useSnippets = allowSnippets && !$licensing.isFreePlan
|
||||||
|
@ -142,10 +143,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const debouncedEval = Utils.debounce((expression, context, snippets) => {
|
const debouncedEval = Utils.debounce((expression, context, snippets) => {
|
||||||
|
try {
|
||||||
|
expressionError = null
|
||||||
expressionResult = processStringSync(expression || "", {
|
expressionResult = processStringSync(expression || "", {
|
||||||
...context,
|
...context,
|
||||||
snippets,
|
snippets,
|
||||||
})
|
})
|
||||||
|
} catch (err) {
|
||||||
|
expressionResult = null
|
||||||
|
expressionError = err
|
||||||
|
}
|
||||||
evaluating = false
|
evaluating = false
|
||||||
}, 260)
|
}, 260)
|
||||||
|
|
||||||
|
@ -370,6 +377,7 @@
|
||||||
{:else if sidePanel === SidePanels.Evaluation}
|
{:else if sidePanel === SidePanels.Evaluation}
|
||||||
<EvaluationSidePanel
|
<EvaluationSidePanel
|
||||||
{expressionResult}
|
{expressionResult}
|
||||||
|
{expressionError}
|
||||||
{evaluating}
|
{evaluating}
|
||||||
expression={editorValue}
|
expression={editorValue}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -5,32 +5,35 @@
|
||||||
import { fade } from "svelte/transition"
|
import { fade } from "svelte/transition"
|
||||||
|
|
||||||
export let expressionResult
|
export let expressionResult
|
||||||
|
export let expressionError
|
||||||
export let evaluating = false
|
export let evaluating = false
|
||||||
export let expression = null
|
export let expression = null
|
||||||
|
|
||||||
$: error = expressionResult && expressionResult.error != null
|
$: error = expressionError != null
|
||||||
$: empty = expression == null || expression?.trim() === ""
|
$: empty = expression == null || expression?.trim() === ""
|
||||||
$: success = !error && !empty
|
$: success = !error && !empty
|
||||||
$: highlightedResult = highlight(expressionResult)
|
$: highlightedResult = highlight(expressionResult)
|
||||||
|
|
||||||
const highlight = result => {
|
const formatError = err => {
|
||||||
if (result == null) {
|
if (err.code === "USER_SCRIPT_ERROR") {
|
||||||
|
return err.userScriptError.toString()
|
||||||
|
}
|
||||||
|
return err.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
const highlight = json => {
|
||||||
|
if (json == null) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
let str
|
|
||||||
if (result.error) {
|
|
||||||
str = result.error.toString()
|
|
||||||
} else {
|
|
||||||
// Attempt to parse and then stringify, in case this is valid result
|
// Attempt to parse and then stringify, in case this is valid result
|
||||||
try {
|
try {
|
||||||
str = JSON.stringify(JSON.parse(result.result), null, 2)
|
json = JSON.stringify(JSON.parse(json), null, 2)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return formatHighlight(str, {
|
return formatHighlight(json, {
|
||||||
keyColor: "#e06c75",
|
keyColor: "#e06c75",
|
||||||
numberColor: "#e5c07b",
|
numberColor: "#e5c07b",
|
||||||
stringColor: "#98c379",
|
stringColor: "#98c379",
|
||||||
|
@ -80,6 +83,8 @@
|
||||||
<div class="body">
|
<div class="body">
|
||||||
{#if empty}
|
{#if empty}
|
||||||
Your expression will be evaluated here
|
Your expression will be evaluated here
|
||||||
|
{:else if error}
|
||||||
|
{formatError(expressionError)}
|
||||||
{:else}
|
{:else}
|
||||||
<!-- eslint-disable-next-line svelte/no-at-html-tags-->
|
<!-- eslint-disable-next-line svelte/no-at-html-tags-->
|
||||||
{@html highlightedResult}
|
{@html highlightedResult}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { iifeWrapper } from "@budibase/string-templates"
|
||||||
import environment from "../../environment"
|
import environment from "../../environment"
|
||||||
|
|
||||||
class ExecutionTimeoutError extends Error {
|
class ExecutionTimeoutError extends Error {
|
||||||
|
code = "ERR_SCRIPT_EXECUTION_TIMEOUT"
|
||||||
constructor(message: string) {
|
constructor(message: string) {
|
||||||
super(message)
|
super(message)
|
||||||
this.name = "ExecutionTimeoutError"
|
this.name = "ExecutionTimeoutError"
|
||||||
|
@ -18,6 +19,7 @@ class ExecutionTimeoutError extends Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserScriptError extends Error {
|
class UserScriptError extends Error {
|
||||||
|
code = "USER_SCRIPT_ERROR"
|
||||||
constructor(readonly userScriptError: Error) {
|
constructor(readonly userScriptError: Error) {
|
||||||
super(
|
super(
|
||||||
`error while running user-supplied JavaScript: ${userScriptError.message}`,
|
`error while running user-supplied JavaScript: ${userScriptError.message}`,
|
||||||
|
|
|
@ -81,7 +81,14 @@ export async function processFormulas<T extends Row | Row[]>(
|
||||||
...row,
|
...row,
|
||||||
[column]: tracer.trace("processStringSync", {}, span => {
|
[column]: tracer.trace("processStringSync", {}, span => {
|
||||||
span?.addTags({ table_id: table._id, column, static: isStatic })
|
span?.addTags({ table_id: table._id, column, static: isStatic })
|
||||||
|
try {
|
||||||
return processStringSync(formula, context)
|
return processStringSync(formula, context)
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err.code === "USER_SCRIPT_ERROR") {
|
||||||
|
return err.userScriptError.toString()
|
||||||
|
}
|
||||||
|
throw err
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
export class JsErrorTimeout extends Error {
|
export class JsErrorTimeout extends Error {
|
||||||
code = "ERR_SCRIPT_EXECUTION_TIMEOUT"
|
code = "ERR_SCRIPT_EXECUTION_TIMEOUT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class UserScriptError extends Error {
|
||||||
|
code = "USER_SCRIPT_ERROR"
|
||||||
|
|
||||||
|
constructor(readonly userScriptError: Error) {
|
||||||
|
super(
|
||||||
|
`error while running user-supplied JavaScript: ${userScriptError.message}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -100,8 +100,8 @@ export function processJS(handlebars: string, context: any) {
|
||||||
if (error.name === "ExecutionTimeoutError") {
|
if (error.name === "ExecutionTimeoutError") {
|
||||||
return "Request JS execution limit hit"
|
return "Request JS execution limit hit"
|
||||||
}
|
}
|
||||||
if ("userScriptError" in error) {
|
if (error.code === "USER_SCRIPT_ERROR") {
|
||||||
return error.userScriptError.toString()
|
throw error
|
||||||
}
|
}
|
||||||
return "Error while executing JS"
|
return "Error while executing JS"
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { removeJSRunner, setJSRunner } from "./helpers/javascript"
|
||||||
|
|
||||||
import manifest from "./manifest.json"
|
import manifest from "./manifest.json"
|
||||||
import { ProcessOptions } from "./types"
|
import { ProcessOptions } from "./types"
|
||||||
|
import { UserScriptError } from "./errors"
|
||||||
|
|
||||||
export { helpersToRemoveForJs, getJsHelperList } from "./helpers/list"
|
export { helpersToRemoveForJs, getJsHelperList } from "./helpers/list"
|
||||||
export { FIND_ANY_HBS_REGEX } from "./utilities"
|
export { FIND_ANY_HBS_REGEX } from "./utilities"
|
||||||
|
@ -230,6 +231,9 @@ export function processStringSync(
|
||||||
return process(string)
|
return process(string)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
if (err.code === "USER_SCRIPT_ERROR") {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -448,7 +452,7 @@ export function convertToJS(hbs: string) {
|
||||||
return `${varBlock}${js}`
|
return `${varBlock}${js}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export { JsErrorTimeout } from "./errors"
|
export { JsErrorTimeout, UserScriptError } from "./errors"
|
||||||
|
|
||||||
export function defaultJSSetup() {
|
export function defaultJSSetup() {
|
||||||
if (!isBackendService()) {
|
if (!isBackendService()) {
|
||||||
|
@ -473,13 +477,17 @@ export function defaultJSSetup() {
|
||||||
try {
|
try {
|
||||||
result.result = ${js};
|
result.result = ${js};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
result.error = e.toString();
|
result.error = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
result;
|
result;
|
||||||
`
|
`
|
||||||
|
|
||||||
return runInNewContext(js, context, { timeout: 1000 })
|
const result = runInNewContext(js, context, { timeout: 1000 })
|
||||||
|
if (result.error) {
|
||||||
|
throw new UserScriptError(result.error)
|
||||||
|
}
|
||||||
|
return result.result
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
removeJSRunner()
|
removeJSRunner()
|
||||||
|
|
Loading…
Reference in New Issue