Skip to content

Commit

Permalink
⚡️ Store existing isolated in global and reuse it (#1914)
Browse files Browse the repository at this point in the history
most likely improves #1913
  • Loading branch information
baptisteArno authored Dec 6, 2024
1 parent 9114ee4 commit 9d62d9d
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 12 deletions.
2 changes: 2 additions & 0 deletions packages/bot-engine/src/continueBotFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type {
SetVariableHistoryItem,
Variable,
} from "@typebot.io/variables/schemas";
import { resetVariablesGlobals } from "@typebot.io/variables/store";
import { parseButtonsReply } from "./blocks/inputs/buttons/parseButtonsReply";
import { parseDateReply } from "./blocks/inputs/date/parseDateReply";
import { formatEmail } from "./blocks/inputs/email/formatEmail";
Expand Down Expand Up @@ -67,6 +68,7 @@ export const continueBotFlow = async (
}
> => {
resetGlobals();
resetVariablesGlobals();
if (!state.currentBlockId)
return startBotFlow({
state: resetSessionState(state),
Expand Down
9 changes: 6 additions & 3 deletions packages/variables/src/codeRunners.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import ivm from "isolated-vm";
import { getOrCreateIsolate } from "./getOrCreateIsolate";
import { parseGuessedValueType } from "./parseGuessedValueType";
import type { Variable } from "./schemas";

export const createCodeRunner = ({ variables }: { variables: Variable[] }) => {
const isolate = new ivm.Isolate();
export const createInlineSyncCodeRunner = ({
variables,
}: { variables: Variable[] }) => {
const isolate = getOrCreateIsolate();
const context = isolate.createContextSync();
const jail = context.global;
jail.setSync("global", jail.derefInto());
Expand All @@ -27,7 +30,7 @@ export const createHttpReqResponseMappingRunner = (response: unknown) => {
Array.isArray(response)
)
return;
const isolate = new ivm.Isolate();
const isolate = getOrCreateIsolate();
const context = isolate.createContextSync();
const jail = context.global;
jail.setSync("global", jail.derefInto());
Expand Down
16 changes: 9 additions & 7 deletions packages/variables/src/executeFunction.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { stringifyError } from "@typebot.io/lib/stringifyError";
import { isDefined } from "@typebot.io/lib/utils";
import ivm from "isolated-vm";
import { Reference } from "isolated-vm";
import { parseTransferrableValue } from "./codeRunners";
import { extractVariablesFromText } from "./extractVariablesFromText";
import { getOrCreateIsolate } from "./getOrCreateIsolate";
import { parseGuessedValueType } from "./parseGuessedValueType";
import { parseVariables } from "./parseVariables";
import type { Variable } from "./schemas";
Expand Down Expand Up @@ -35,30 +36,31 @@ export const executeFunction = async ({
: [],
);

const updatedVariables: Record<string, any> = {};
const variableUpdates = new Map<string, unknown>();

const setVariable = (key: string, value: any) => {
updatedVariables[key] = value;
variableUpdates.set(key, value);
};

const isolate = new ivm.Isolate();
const isolate = getOrCreateIsolate();
const context = isolate.createContextSync();
const jail = context.global;
jail.setSync("global", jail.derefInto());
context.evalClosure(
"globalThis.setVariable = (...args) => $0.apply(undefined, args, { arguments: { copy: true }, promise: true, result: { copy: true, promise: true } })",
[new ivm.Reference(setVariable)],
[new Reference(setVariable)],
);
context.evalClosure(
"globalThis.fetch = (...args) => $0.apply(undefined, args, { arguments: { copy: true }, promise: true, result: { copy: true, promise: true } })",
[
new ivm.Reference(async (...args: any[]) => {
new Reference(async (...args: any[]) => {
// @ts-ignore
const response = await fetch(...args);
return response.text();
}),
],
);

args.forEach(({ id, value }) => {
jail.setSync(id, parseTransferrableValue(value));
});
Expand All @@ -76,7 +78,7 @@ export const executeFunction = async ({
const output: unknown = await run(parsedBody);
return {
output,
newVariables: Object.entries(updatedVariables)
newVariables: Array.from(variableUpdates.entries())
.map(([name, value]) => {
const existingVariable = variables.find((v) => v.name === name);
if (!existingVariable) return;
Expand Down
9 changes: 9 additions & 0 deletions packages/variables/src/getOrCreateIsolate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Isolate } from "isolated-vm";
import { variablesGlobals } from "./store";

export const getOrCreateIsolate = () => {
if (!variablesGlobals.isolate) {
variablesGlobals.isolate = new Isolate();
}
return variablesGlobals.isolate;
};
4 changes: 2 additions & 2 deletions packages/variables/src/parseVariables.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { safeStringify } from "@typebot.io/lib/safeStringify";
import { isDefined, isNotDefined, isNotEmpty } from "@typebot.io/lib/utils";
import { createCodeRunner } from "./codeRunners";
import { createInlineSyncCodeRunner } from "./codeRunners";
import type { Variable, VariableWithValue } from "./schemas";

export type ParseVariablesOptions = {
Expand Down Expand Up @@ -74,7 +74,7 @@ const evaluateInlineCode = (
) => {
try {
const body = parseVariables(variables, { fieldToParse: "id" })(code);
return createCodeRunner({ variables })(
return createInlineSyncCodeRunner({ variables })(
body.includes("return ") ? body : `return ${body}`,
);
} catch (err) {
Expand Down
10 changes: 10 additions & 0 deletions packages/variables/src/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { Isolate } from "isolated-vm";

export const variablesGlobals = {
isolate: undefined as Isolate | undefined,
};

export const resetVariablesGlobals = () => {
variablesGlobals.isolate?.dispose();
variablesGlobals.isolate = undefined;
};

0 comments on commit 9d62d9d

Please sign in to comment.