Fetch snippets from app doc when creating a new isolate
This commit is contained in:
parent
b13d2d3803
commit
10c581c3be
|
@ -1,3 +1,3 @@
|
|||
"use strict";var snippets=(()=>{var u=Object.create;var i=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var m=Object.getPrototypeOf,x=Object.prototype.hasOwnProperty;var l=(e,n)=>()=>(n||e((n={exports:{}}).exports,n),n.exports),W=(e,n)=>{for(var r in n)i(e,r,{get:n[r],enumerable:!0})},o=(e,n,r,p)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of d(n))!x.call(e,t)&&t!==r&&i(e,t,{get:()=>n[t],enumerable:!(p=c(n,t))||p.enumerable});return e};var g=(e,n,r)=>(r=e!=null?u(m(e)):{},o(n||!e||!e.__esModule?i(r,"default",{value:e,enumerable:!0}):r,e)),v=e=>o(i({},"__esModule",{value:!0}),e);var a=l((_,f)=>{f.exports.iifeWrapper=e=>`(function(){
|
||||
"use strict";var snippets=(()=>{var u=Object.create;var p=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var m=Object.getPrototypeOf,x=Object.prototype.hasOwnProperty;var l=(e,n)=>()=>(n||e((n={exports:{}}).exports,n),n.exports),W=(e,n)=>{for(var i in n)p(e,i,{get:n[i],enumerable:!0})},o=(e,n,i,t)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of d(n))!x.call(e,r)&&r!==i&&p(e,r,{get:()=>n[r],enumerable:!(t=c(n,r))||t.enumerable});return e};var g=(e,n,i)=>(i=e!=null?u(m(e)):{},o(n||!e||!e.__esModule?p(i,"default",{value:e,enumerable:!0}):i,e)),v=e=>o(p({},"__esModule",{value:!0}),e);var a=l((P,f)=>{f.exports.iifeWrapper=e=>`(function(){
|
||||
${e}
|
||||
})();`});var y={};W(y,{default:()=>w});var s=g(a()),w=new Proxy({},{get:function(e,n){let r=($("snippets")||[]).find(p=>p.name===n);return[eval][0]((0,s.iifeWrapper)(r.code))}});return v(y);})();
|
||||
})();`});var y={};W(y,{default:()=>w});var s=g(a()),w=new Proxy({},{get:function(e,n){let i=(snippetDefinitions||[]).find(t=>t.name===n);return[eval][0]((0,s.iifeWrapper)(i.code))}});return v(y);})();
|
||||
|
|
|
@ -6,14 +6,13 @@ export default new Proxy(
|
|||
{},
|
||||
{
|
||||
get: function (_, name) {
|
||||
// Get snippet definitions from global context, get the correct snippet
|
||||
// then eval the JS. This will error if the snippet doesn't exist, but
|
||||
// that's intended.
|
||||
// Snippet definitions are injected to the isolate global scope before
|
||||
// this bundle is loaded, so we can access it from there.
|
||||
// https://esbuild.github.io/content-types/#direct-eval for info on why
|
||||
// eval is being called this way.
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line no-undef
|
||||
const snippet = ($("snippets") || []).find(x => x.name === name)
|
||||
const snippet = (snippetDefinitions || []).find(x => x.name === name)
|
||||
return [eval][0](iifeWrapper(snippet.code))
|
||||
},
|
||||
}
|
||||
|
|
|
@ -8,29 +8,48 @@ import {
|
|||
import { context, logging } from "@budibase/backend-core"
|
||||
import tracer from "dd-trace"
|
||||
import { IsolatedVM } from "./vm"
|
||||
import { App, DocumentType, Snippet, VM } from "@budibase/types"
|
||||
|
||||
async function getIsolate(ctx: any): Promise<VM> {
|
||||
// Reuse the existing isolate if one exists
|
||||
if (ctx?.vm) {
|
||||
return ctx.vm
|
||||
}
|
||||
|
||||
// Get snippets to build into new isolate, if inside app context
|
||||
let snippets: Snippet[] | undefined
|
||||
const db = context.getAppDB()
|
||||
if (db) {
|
||||
console.log("READ APP METADATA")
|
||||
const app = await db.get<App>(DocumentType.APP_METADATA)
|
||||
snippets = app.snippets
|
||||
}
|
||||
|
||||
// Build a new isolate
|
||||
return new IsolatedVM({
|
||||
memoryLimit: env.JS_RUNNER_MEMORY_LIMIT,
|
||||
invocationTimeout: env.JS_PER_INVOCATION_TIMEOUT_MS,
|
||||
isolateAccumulatedTimeout: env.JS_PER_REQUEST_TIMEOUT_MS,
|
||||
})
|
||||
.withHelpers()
|
||||
.withSnippets(snippets)
|
||||
}
|
||||
|
||||
export function init() {
|
||||
setJSRunner((js: string, ctx: Record<string, any>) => {
|
||||
return tracer.trace("runJS", {}, span => {
|
||||
return tracer.trace("runJS", {}, async span => {
|
||||
try {
|
||||
// Reuse an existing isolate from context, or make a new one
|
||||
const bbCtx = context.getCurrentContext()
|
||||
|
||||
const vm = bbCtx?.vm
|
||||
? bbCtx.vm
|
||||
: new IsolatedVM({
|
||||
memoryLimit: env.JS_RUNNER_MEMORY_LIMIT,
|
||||
invocationTimeout: env.JS_PER_INVOCATION_TIMEOUT_MS,
|
||||
isolateAccumulatedTimeout: env.JS_PER_REQUEST_TIMEOUT_MS,
|
||||
})
|
||||
.withHelpers()
|
||||
.withSnippets()
|
||||
|
||||
const vm = await getIsolate(bbCtx)
|
||||
if (bbCtx) {
|
||||
// If we have a context, we want to persist it to reuse the isolate
|
||||
bbCtx.vm = vm
|
||||
}
|
||||
|
||||
// Strip helpers (an array) and snippets (a proxy isntance) as these
|
||||
// will not survive the isolated-vm barrier
|
||||
const { helpers, snippets, ...rest } = ctx
|
||||
return vm.withContext(rest, () => vm.execute(js))
|
||||
return vm.withContext(rest, () => vm!.execute(js))
|
||||
} catch (error: any) {
|
||||
if (error.message === "Script execution timed out.") {
|
||||
throw new JsErrorTimeout()
|
||||
|
|
|
@ -6,7 +6,7 @@ import crypto from "crypto"
|
|||
import querystring from "querystring"
|
||||
|
||||
import { BundleType, loadBundle } from "../bundles"
|
||||
import { VM } from "@budibase/types"
|
||||
import { Snippet, VM } from "@budibase/types"
|
||||
import { iifeWrapper } from "@budibase/string-templates"
|
||||
import environment from "../../environment"
|
||||
|
||||
|
@ -98,11 +98,13 @@ export class IsolatedVM implements VM {
|
|||
return this
|
||||
}
|
||||
|
||||
withSnippets() {
|
||||
withSnippets(snippets?: Snippet[]) {
|
||||
const snippetsSource = loadBundle(BundleType.SNIPPETS)
|
||||
const script = this.isolate.compileScriptSync(
|
||||
`${snippetsSource};snippets=snippets.default;`
|
||||
)
|
||||
const script = this.isolate.compileScriptSync(`
|
||||
const snippetDefinitions = ${JSON.stringify(snippets || [])};
|
||||
${snippetsSource};
|
||||
snippets = snippets.default;
|
||||
`)
|
||||
script.runSync(this.vm, { timeout: this.invocationTimeout, release: false })
|
||||
new Promise(() => {
|
||||
script.release()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { User, Document, Plugin } from "../"
|
||||
import { User, Document, Plugin, Snippet } from "../"
|
||||
import { SocketSession } from "../../sdk"
|
||||
|
||||
export type AppMetadataErrors = { [key: string]: string[] }
|
||||
|
@ -26,6 +26,7 @@ export interface App extends Document {
|
|||
automations?: AutomationSettings
|
||||
usedPlugins?: Plugin[]
|
||||
upgradableVersion?: string
|
||||
snippets?: Snippet[]
|
||||
}
|
||||
|
||||
export interface AppInstance {
|
||||
|
|
|
@ -14,3 +14,4 @@ export * from "./backup"
|
|||
export * from "./webhook"
|
||||
export * from "./links"
|
||||
export * from "./component"
|
||||
export * from "./snippet"
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export interface Snippet {
|
||||
name: string
|
||||
code: string
|
||||
}
|
Loading…
Reference in New Issue